igraph/0000755000176000001440000000000012325555115011560 5ustar ripleyusersigraph/inst/0000755000176000001440000000000012265557231012541 5ustar ripleyusersigraph/inst/my_html_library.tcl0000644000176000001440000000622712240234657016446 0ustar ripleyusers # IGraph R package # Copyright (C) 2009-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### proc render_real {win href} { global tkigraph_help_root set Url $tkigraph_help_root/$href $win configure -state normal HMreset_win $win HMparse_html [get_html $Url] "HMrender $win" $win tag add indented 1.0 insert $win tag configure indented -lmargin1 20 -lmargin2 20 $win configure -state disabled update } proc render {win href} { global tkigraph_help_history tkigraph_help_history_pos global browser_button browser_url if { [ regexp ^http:// "$href" ] } { set browser_url $href $browser_button invoke return } lappend tkigraph_help_history($win) $href incr tkigraph_help_history_pos($win) render_real $win $href } proc start_history {win} { global tkigraph_help_history tkigraph_help_history_pos set tkigraph_help_history($win) [ list ] set tkigraph_help_history_pos($win) -1 } proc render_back {win} { global tkigraph_help_history tkigraph_help_history_pos if { $tkigraph_help_history_pos($win) > 0 } { set pos [ incr tkigraph_help_history_pos($win) -1 ] render_real $win [ lindex $tkigraph_help_history($win) $pos ] } } proc render_forw {win} { global tkigraph_help_history tkigraph_help_history_pos if { [ expr $tkigraph_help_history_pos($win) + 1 ] < [ llength $tkigraph_help_history($win) ] } { set pos [ incr tkigraph_help_history_pos($win) ] render_real $win [ lindex $tkigraph_help_history($win) $pos ] } } proc HMlink_callback {win href} { render $win $href } proc get_html {file} { global tkigraph_help_root if {[catch {set fd [open $file]} msg]} { return " Bad file $file

Error reading $file

$msg


Go home " } set result [read $fd] close $fd return $result } proc HMset_image {win handle src} { global tkigraph_help_root set image $tkigraph_help_root/$src update if {[string first " $image " " [image names] "] >= 0} { HMgot_image $handle $image } else { set type photo if {[file extension $image] == ".bmp"} {set type bitmap} catch {image create $type $image -file $image} image HMgot_image $handle $image } } igraph/inst/html_library.tcl0000644000176000001440000011751012240234657015737 0ustar ripleyusers# Simple HTML display library by Stephen Uhler (stephen.uhler@sun.com) # Copyright (c) 1995 by Sun Microsystems # Version 0.3 Fri Sep 1 10:47:17 PDT 1995 # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # To use this package, create a text widget (say, .text) # and set a variable full of html, (say $html), and issue: # HMinit_win .text # HMparse_html $html "HMrender .text" # You also need to supply the routine: # proc HMlink_callback {win href} { ...} # win: The name of the text widget # href The name of the link # which will be called anytime the user "clicks" on a link. # The supplied version just prints the link to stdout. # In addition, if you wish to use embedded images, you will need to write # proc HMset_image {handle src} # handle an arbitrary handle (not really) # src The name of the image # Which calls # HMgot_image $handle $image # with the TK image. # # To return a "used" text widget to its initialized state, call: # HMreset_win .text # See "sample.tcl" for sample usage ################################################################## ############################################ # mapping of html tags to text tag properties # properties beginning with "T" map directly to text tags # These are Defined in HTML 2.0 array set HMtag_map { b {weight bold} blockquote {style i indent 1 Trindent rindent} bq {style i indent 1 Trindent rindent} cite {style i} code {family courier} dfn {style i} dir {indent 1} dl {indent 1} em {style i} h1 {size 24 weight bold} h2 {size 22} h3 {size 20} h4 {size 18} h5 {size 16} h6 {style i} i {style i} kbd {family courier weight bold} menu {indent 1} ol {indent 1} pre {fill 0 family courier Tnowrap nowrap} samp {family courier} strong {weight bold} tt {family courier} u {Tunderline underline} ul {indent 1} var {style i} } # These are in common(?) use, but not defined in html2.0 array set HMtag_map { center {Tcenter center} strike {Tstrike strike} u {Tunderline underline} } # initial values set HMtag_map(hmstart) { family times weight medium style r size 14 Tcenter "" Tlink "" Tnowrap "" Tunderline "" list list fill 1 indent "" counter 0 adjust 0 } # html tags that insert white space array set HMinsert_map { blockquote "\n\n" /blockquote "\n" br "\n" dd "\n" /dd "\n" dl "\n" /dl "\n" dt "\n" form "\n" /form "\n" h1 "\n\n" /h1 "\n" h2 "\n\n" /h2 "\n" h3 "\n\n" /h3 "\n" h4 "\n" /h4 "\n" h5 "\n" /h5 "\n" h6 "\n" /h6 "\n" li "\n" /dir "\n" /ul "\n" /ol "\n" /menu "\n" p "\n\n" pre "\n" /pre "\n" } # tags that are list elements, that support "compact" rendering array set HMlist_elements { ol 1 ul 1 menu 1 dl 1 dir 1 } ############################################ # initialize the window and stack state proc HMinit_win {win} { upvar #0 HM$win var HMinit_state $win $win tag configure underline -underline 1 $win tag configure center -justify center $win tag configure nowrap -wrap none $win tag configure rindent -rmargin $var(S_tab)c $win tag configure strike -overstrike 1 $win tag configure mark -foreground red ;# list markers $win tag configure list -spacing1 3p -spacing3 3p ;# regular lists $win tag configure compact -spacing1 0p ;# compact lists $win tag configure link -borderwidth 2 -foreground blue ;# hypertext links HMset_indent $win $var(S_tab) $win configure -wrap word # configure the text insertion point $win mark set $var(S_insert) 1.0 # for horizontal rules $win tag configure thin -font [HMx_font times 2 medium r] $win tag configure hr -relief sunken -borderwidth 2 -wrap none \ -tabs [winfo width $win] bind $win { %W tag configure hr -tabs %w %W tag configure last -spacing3 %h } # generic link enter callback $win tag bind link <1> "HMlink_hit $win %x %y" } # set the indent spacing (in cm) for lists # TK uses a "weird" tabbing model that causes \t to insert a single # space if the current line position is past the tab setting proc HMset_indent {win cm} { set tabs [expr $cm / 2.0] $win configure -tabs ${tabs}c foreach i {1 2 3 4 5 6 7 8 9} { set tab [expr $i * $cm] $win tag configure indent$i -lmargin1 ${tab}c -lmargin2 ${tab}c \ -tabs "[expr $tab + $tabs]c [expr $tab + 2*$tabs]c" } } # reset the state of window - get ready for the next page # remove all but the font tags, and remove all form state proc HMreset_win {win} { upvar #0 HM$win var regsub -all { +[^L ][^ ]*} " [$win tag names] " {} tags catch "$win tag delete $tags" eval $win mark unset [$win mark names] $win delete 0.0 end $win tag configure hr -tabs [winfo width $win] # configure the text insertion point $win mark set $var(S_insert) 1.0 # remove form state. If any check/radio buttons still exists, # their variables will be magically re-created, and never get # cleaned up. catch unset [info globals HM$win.form*] HMinit_state $win return HM$win } # initialize the window's state array # Parameters beginning with S_ are NOT reset # adjust_size: global font size adjuster # unknown: character to use for unknown entities # tab: tab stop (in cm) # stop: enabled to stop processing # update: how many tags between update calls # tags: number of tags processed so far # symbols: Symbols to use on un-ordered lists proc HMinit_state {win} { upvar #0 HM$win var array set tmp [array get var S_*] catch {unset var} array set var { stop 0 tags 0 fill 0 list list S_adjust_size 0 S_tab 1.0 S_unknown \xb7 S_update 10 S_symbols O*=+-o\xd7\xb0>:\xb7 S_insert Insert } array set var [array get tmp] } # alter the parameters of the text state # this allows an application to over-ride the default settings # it is called as: HMset_state -param value -param value ... array set HMparam_map { -update S_update -tab S_tab -unknown S_unknown -stop S_stop -size S_adjust_size -symbols S_symbols -insert S_insert } proc HMset_state {win args} { upvar #0 HM$win var global HMparam_map set bad 0 if {[catch {array set params $args}]} {return 0} foreach i [array names params] { incr bad [catch {set var($HMparam_map($i)) $params($i)}] } return [expr $bad == 0] } ############################################ # manage the display of html # HMrender gets called for every html tag # win: The name of the text widget to render into # tag: The html tag (in arbitrary case) # not: a "/" or the empty string # param: The un-interpreted parameter list # text: The plain text until the next html tag proc HMrender {win tag not param text} { upvar #0 HM$win var if {$var(stop)} return global HMtag_map HMinsert_map HMlist_elements set tag [string tolower $tag] set text [HMmap_esc $text] # manage compact rendering of lists if {[info exists HMlist_elements($tag)]} { set list "list [expr {[HMextract_param $param compact] ? "compact" : "list"}]" } else { set list "" } # Allow text to be diverted to a different window (for tables) # this is not currently used if {[info exists var(divert)]} { set win $var(divert) upvar #0 HM$win var } # adjust (push or pop) tag state catch {HMstack $win $not "$HMtag_map($tag) $list"} # insert white space (with current font) # adding white space can get a bit tricky. This isn't quite right set bad [catch {$win insert $var(S_insert) $HMinsert_map($not$tag) "space $var(font)"}] if {!$bad && [lindex $var(fill) end]} { set text [string trimleft $text] } # to fill or not to fill if {[lindex $var(fill) end]} { set text [HMzap_white $text] } # generic mark hook catch {HMmark $not$tag $win $param text} err # do any special tag processing catch {HMtag_$not$tag $win $param text} msg # add the text with proper tags set tags [HMcurrent_tags $win] $win insert $var(S_insert) $text $tags # We need to do an update every so often to insure interactive response. # This can cause us to re-enter the event loop, and cause recursive # invocations of HMrender, so we need to be careful. if {!([incr var(tags)] % $var(S_update))} { update } } # html tags requiring special processing # Procs of the form HMtag_ or HMtag_ get called just before # the text for this tag is displayed. These procs are called inside a # "catch" so it is OK to fail. # win: The name of the text widget to render into # param: The un-interpreted parameter list # text: A pass-by-reference name of the plain text until the next html tag # Tag commands may change this to affect what text will be inserted # next. # A pair of pseudo tags are added automatically as the 1st and last html # tags in the document. The default is and . # Append enough blank space at the end of the text widget while # rendering so HMgoto can place the target near the top of the page, # then remove the extra space when done rendering. proc HMtag_hmstart {win param text} { upvar #0 HM$win var $win mark gravity $var(S_insert) left $win insert end "\n " last $win mark gravity $var(S_insert) right } proc HMtag_/hmstart {win param text} { $win delete last.first end } # put the document title in the window banner, and remove the title text # from the document proc HMtag_title {win param text} { upvar $text data wm title [winfo toplevel $win] $data set data "" } proc HMtag_hr {win param text} { upvar #0 HM$win var $win insert $var(S_insert) "\n" space "\n" thin "\t" "thin hr" "\n" thin } # list element tags proc HMtag_ol {win param text} { upvar #0 HM$win var set var(count$var(level)) 0 } proc HMtag_ul {win param text} { upvar #0 HM$win var catch {unset var(count$var(level))} } proc HMtag_menu {win param text} { upvar #0 HM$win var set var(menu) -> set var(compact) 1 } proc HMtag_/menu {win param text} { upvar #0 HM$win var catch {unset var(menu)} catch {unset var(compact)} } proc HMtag_dt {win param text} { upvar #0 HM$win var upvar $text data set level $var(level) incr level -1 $win insert $var(S_insert) "$data" \ "hi [lindex $var(list) end] indent$level $var(font)" set data {} } proc HMtag_li {win param text} { upvar #0 HM$win var set level $var(level) incr level -1 set x [string index $var(S_symbols)+-+-+-+-" $level] catch {set x [incr var(count$level)]} catch {set x $var(menu)} $win insert $var(S_insert) \t$x\t "mark [lindex $var(list) end] indent$level $var(font)" } # Manage hypertext "anchor" links. A link can be either a source (href) # a destination (name) or both. If its a source, register it via a callback, # and set its default behavior. If its a destination, check to see if we need # to go there now, as a result of a previous HMgoto request. If so, schedule # it to happen with the closing tag, so we can highlight the text up to # the . proc HMtag_a {win param text} { upvar #0 HM$win var # a source if {[HMextract_param $param href]} { set var(Tref) [list L:$href] HMstack $win "" "Tlink link" HMlink_setup $win $href } # a destination if {[HMextract_param $param name]} { set var(Tname) [list N:$name] HMstack $win "" "Tanchor anchor" $win mark set N:$name "$var(S_insert) - 1 chars" $win mark gravity N:$name left if {[info exists var(goto)] && $var(goto) == $name} { unset var(goto) set var(going) $name } } } # The application should call here with the fragment name # to cause the display to go to this spot. # If the target exists, go there (and do the callback), # otherwise schedule the goto to happen when we see the reference. proc HMgoto {win where {callback HMwent_to}} { upvar #0 HM$win var if {[regexp N:$where [$win mark names]]} { $win see N:$where update eval $callback $win [list $where] return 1 } else { set var(goto) $where return 0 } } # We actually got to the spot, so highlight it! # This should/could be replaced by the application # We'll flash it orange a couple of times. proc HMwent_to {win where {count 0} {color orange}} { upvar #0 HM$win var if {$count > 5} return catch {$win tag configure N:$where -foreground $color} update after 200 [list HMwent_to $win $where [incr count] \ [expr {$color=="orange" ? "" : "orange"}]] } proc HMtag_/a {win param text} { upvar #0 HM$win var if {[info exists var(Tref)]} { unset var(Tref) HMstack $win / "Tlink link" } # goto this link, then invoke the call-back. if {[info exists var(going)]} { $win yview N:$var(going) update HMwent_to $win $var(going) unset var(going) } if {[info exists var(Tname)]} { unset var(Tname) HMstack $win / "Tanchor anchor" } } # Inline Images # This interface is subject to change # Most of the work is getting around a limitation of TK that prevents # setting the size of a label to a widthxheight in pixels # # Images have the following parameters: # align: top,middle,bottom # alt: alternate text # ismap: A clickable image map # src: The URL link # Netscape supports (and so do we) # width: A width hint (in pixels) # height: A height hint (in pixels) # border: The size of the window border proc HMtag_img {win param text} { upvar #0 HM$win var # get alignment array set align_map {top top middle center bottom bottom} set align bottom ;# The spec isn't clear what the default should be HMextract_param $param align catch {set align $align_map([string tolower $align])} # get alternate text set alt "" HMextract_param $param alt set alt [HMmap_esc $alt] # get the border width set border 1 HMextract_param $param border # see if we have an image size hint # If so, make a frame the "hint" size to put the label in # otherwise just make the label set item $win.$var(tags) # catch {destroy $item} if {[HMextract_param $param width] && [HMextract_param $param height]} { frame $item -width $width -height $height pack propagate $item 0 set label $item.label label $label pack $label -expand 1 -fill both } else { set label $item label $label } $label configure -relief ridge -fg orange -text $alt catch {$label configure -bd $border} $win window create $var(S_insert) -align $align -window $item -pady 2 -padx 2 # add in all the current tags (this is overkill) set tags [HMcurrent_tags $win] foreach tag $tags { $win tag add $tag $item } # set imagemap callbacks if {[HMextract_param $param ismap]} { # regsub -all {[^L]*L:([^ ]*).*} $tags {\1} link set link [lindex $tags [lsearch -glob $tags L:*]] regsub L: $link {} link global HMevents regsub -all {%} $link {%%} link2 foreach i [array names HMevents] { bind $label <$i> "catch \{%W configure $HMevents($i)\}" } bind $label <1> "+HMlink_callback $win $link2?%x,%y" } # now callback to the application set src "" HMextract_param $param src HMset_image $win $label $src return $label ;# used by the forms package for input_image types } # The app needs to supply one of these proc HMset_image {win handle src} { HMgot_image $handle "can't get\n$src" } # When the image is available, the application should call back here. # If we have the image, put it in the label, otherwise display the error # message. If we don't get a callback, the "alt" text remains. # if we have a clickable image, arrange for a callback proc HMgot_image {win image_error} { # if we're in a frame turn on geometry propogation if {[winfo name $win] == "label"} { pack propagate [winfo parent $win] 1 } if {[catch {$win configure -image $image_error}]} { $win configure -image {} $win configure -text $image_error } } # Sample hypertext link callback routine - should be replaced by app # This proc is called once for each tag. # Applications can overwrite this procedure, as required, or # replace the HMevents array # win: The name of the text widget to render into # href: The HREF link for this tag. array set HMevents { Enter {-borderwidth 2 -relief raised } Leave {-borderwidth 2 -relief flat } 1 {-borderwidth 2 -relief sunken} ButtonRelease-1 {-borderwidth 2 -relief raised} } # We need to escape any %'s in the href tag name so the bind command # doesn't try to substitute them. proc HMlink_setup {win href} { global HMevents regsub -all {%} $href {%%} href2 foreach i [array names HMevents] { eval {$win tag bind L:$href <$i>} \ \{$win tag configure \{L:$href2\} $HMevents($i)\} } } # generic link-hit callback # This gets called upon button hits on hypertext links # Applications are expected to supply ther own HMlink_callback routine # win: The name of the text widget to render into # x,y: The cursor position at the "click" proc HMlink_hit {win x y} { set tags [$win tag names @$x,$y] set link [lindex $tags [lsearch -glob $tags L:*]] # regsub -all {[^L]*L:([^ ]*).*} $tags {\1} link regsub L: $link {} link HMlink_callback $win $link } # replace this! # win: The name of the text widget to render into # href: The HREF link for this tag. proc HMlink_callback {win href} { puts "Got hit on $win, link $href" } # extract a value from parameter list (this needs a re-do) # returns "1" if the keyword is found, "0" otherwise # param: A parameter list. It should alredy have been processed to # remove any entity references # key: The parameter name # val: The variable to put the value into (use key as default) proc HMextract_param {param key {val ""}} { if {$val == ""} { upvar $key result } else { upvar $val result } set ws " \n\r" # look for name=value combinations. Either (') or (") are valid delimeters if { [regsub -nocase [format {.*%s[%s]*=[%s]*"([^"]*).*} $key $ws $ws] $param {\1} value] || [regsub -nocase [format {.*%s[%s]*=[%s]*'([^']*).*} $key $ws $ws] $param {\1} value] || [regsub -nocase [format {.*%s[%s]*=[%s]*([^%s]+).*} $key $ws $ws $ws] $param {\1} value] } { set result $value return 1 } # now look for valueless names # I should strip out name=value pairs, so we don't end up with "name" # inside the "value" part of some other key word - some day set bad \[^a-zA-Z\]+ if {[regexp -nocase "$bad$key$bad" -$param-]} { return 1 } else { return 0 } } # These next two routines manage the display state of the page. # Push or pop tags to/from stack. # Each orthogonal text property has its own stack, stored as a list. # The current (most recent) tag is the last item on the list. # Push is {} for pushing and {/} for popping proc HMstack {win push list} { upvar #0 HM$win var array set tags $list if {$push == ""} { foreach tag [array names tags] { lappend var($tag) $tags($tag) } } else { foreach tag [array names tags] { # set cnt [regsub { *[^ ]+$} $var($tag) {} var($tag)] set var($tag) [lreplace $var($tag) end end] } } } # extract set of current text tags # tags starting with T map directly to text tags, all others are # handled specially. There is an application callback, HMset_font # to allow the application to do font error handling proc HMcurrent_tags {win} { upvar #0 HM$win var set font font foreach i {family size weight style} { set $i [lindex $var($i) end] append font :[set $i] } set xfont [HMx_font $family $size $weight $style $var(S_adjust_size)] HMset_font $win $font $xfont set indent [llength $var(indent)] incr indent -1 lappend tags $font indent$indent foreach tag [array names var T*] { lappend tags [lindex $var($tag) end] ;# test } set var(font) $font set var(xfont) [$win tag cget $font -font] set var(level) $indent return $tags } # allow the application to do do better font management # by overriding this procedure proc HMset_font {win tag font} { catch {$win tag configure $tag -font $font} msg } # generate an X font name proc HMx_font {family size weight style {adjust_size 0}} { catch {incr size $adjust_size} return "-*-$family-$weight-$style-normal-*-*-${size}0-*-*-*-*-*-*" } # Optimize HMrender (hee hee) # This is experimental proc HMoptimize {} { regsub -all "\n\[ \]*#\[^\n\]*" [info body HMrender] {} body regsub -all ";\[ \]*#\[^\n]*" $body {} body regsub -all "\n\n+" $body \n body proc HMrender {win tag not param text} $body } ############################################ # Turn HTML into TCL commands # html A string containing an html document # cmd A command to run for each html tag found # start The name of the dummy html start/stop tags proc HMparse_html {html {cmd HMtest_parse} {start hmstart}} { regsub -all \{ $html {\&ob;} html regsub -all \} $html {\&cb;} html set w " \t\r\n" ;# white space proc HMcl x {return "\[$x\]"} set exp <(/?)([HMcl ^$w>]+)[HMcl $w]*([HMcl ^>]*)> set sub "\}\n$cmd {\\2} {\\1} {\\3} \{" regsub -all $exp $html $sub html eval "$cmd {$start} {} {} \{ $html \}" eval "$cmd {$start} / {} {}" } proc HMtest_parse {command tag slash text_after_tag} { puts "==> $command $tag $slash $text_after_tag" } # Convert multiple white space into a single space proc HMzap_white {data} { regsub -all "\[ \t\r\n\]+" $data " " data return $data } # find HTML escape characters of the form &xxx; proc HMmap_esc {text} { if {![regexp & $text]} {return $text} regsub -all {([][$\\])} $text {\\\1} new regsub -all {&#([0-9][0-9]?[0-9]?);?} \ $new {[format %c [scan \1 %d tmp;set tmp]]} new regsub -all {&([a-zA-Z]+);?} $new {[HMdo_map \1]} new return [subst $new] } # convert an HTML escape sequence into character proc HMdo_map {text {unknown ?}} { global HMesc_map set result $unknown catch {set result $HMesc_map($text)} return $result } # table of escape characters (ISO latin-1 esc's are in a different table) array set HMesc_map { lt < gt > amp & quot \" copy \xa9 reg \xae ob \x7b cb \x7d nbsp \xa0 } ############################################################# # ISO Latin-1 escape codes array set HMesc_map { nbsp \xa0 iexcl \xa1 cent \xa2 pound \xa3 curren \xa4 yen \xa5 brvbar \xa6 sect \xa7 uml \xa8 copy \xa9 ordf \xaa laquo \xab not \xac shy \xad reg \xae hibar \xaf deg \xb0 plusmn \xb1 sup2 \xb2 sup3 \xb3 acute \xb4 micro \xb5 para \xb6 middot \xb7 cedil \xb8 sup1 \xb9 ordm \xba raquo \xbb frac14 \xbc frac12 \xbd frac34 \xbe iquest \xbf Agrave \xc0 Aacute \xc1 Acirc \xc2 Atilde \xc3 Auml \xc4 Aring \xc5 AElig \xc6 Ccedil \xc7 Egrave \xc8 Eacute \xc9 Ecirc \xca Euml \xcb Igrave \xcc Iacute \xcd Icirc \xce Iuml \xcf ETH \xd0 Ntilde \xd1 Ograve \xd2 Oacute \xd3 Ocirc \xd4 Otilde \xd5 Ouml \xd6 times \xd7 Oslash \xd8 Ugrave \xd9 Uacute \xda Ucirc \xdb Uuml \xdc Yacute \xdd THORN \xde szlig \xdf agrave \xe0 aacute \xe1 acirc \xe2 atilde \xe3 auml \xe4 aring \xe5 aelig \xe6 ccedil \xe7 egrave \xe8 eacute \xe9 ecirc \xea euml \xeb igrave \xec iacute \xed icirc \xee iuml \xef eth \xf0 ntilde \xf1 ograve \xf2 oacute \xf3 ocirc \xf4 otilde \xf5 ouml \xf6 divide \xf7 oslash \xf8 ugrave \xf9 uacute \xfa ucirc \xfb uuml \xfc yacute \xfd thorn \xfe yuml \xff } ########################################################## # html forms management commands # As each form element is located, it is created and rendered. Additional # state is stored in a form specific global variable to be processed at # the end of the form, including the "reset" and "submit" options. # Remember, there can be multiple forms existing on multiple pages. When # HTML tables are added, a single form could be spread out over multiple # text widgets, which makes it impractical to hang the form state off the # HM$win structure. We don't need to check for the existance of required # parameters, we just "fail" and get caught in HMrender # This causes line breaks to be preserved in the inital values # of text areas array set HMtag_map { textarea {fill 0} } ########################################################## # html isindex tag. Although not strictly forms, they're close enough # to be in this file # is-index forms # make a frame with a label, entry, and submit button proc HMtag_isindex {win param text} { upvar #0 HM$win var set item $win.$var(tags) if {[winfo exists $item]} { destroy $item } frame $item -relief ridge -bd 3 set prompt "Enter search keywords here" HMextract_param $param prompt label $item.label -text [HMmap_esc $prompt] -font $var(xfont) entry $item.entry bind $item.entry "$item.submit invoke" button $item.submit -text search -font $var(xfont) -command \ [format {HMsubmit_index %s {%s} [HMmap_reply [%s get]]} \ $win $param $item.entry] pack $item.label -side top pack $item.entry $item.submit -side left # insert window into text widget $win insert $var(S_insert) \n isindex HMwin_install $win $item $win insert $var(S_insert) \n isindex bind $item {focus %W.entry} } # This is called when the isindex form is submitted. # The default version calls HMlink_callback. Isindex tags should either # be deprecated, or fully supported (e.g. they need an href parameter) proc HMsubmit_index {win param text} { HMlink_callback $win ?$text } # initialize form state. All of the state for this form is kept # in a global array whose name is stored in the form_id field of # the main window array. # Parameters: ACTION, METHOD, ENCTYPE proc HMtag_form {win param text} { upvar #0 HM$win var # create a global array for the form set id HM$win.form$var(tags) upvar #0 $id form # missing /form tag, simulate it if {[info exists var(form_id)]} { puts "Missing end-form tag !!!! $var(form_id)" HMtag_/form $win {} {} } catch {unset form} set var(form_id) $id set form(param) $param ;# form initial parameter list set form(reset) "" ;# command to reset the form set form(reset_button) "" ;# list of all reset buttons set form(submit) "" ;# command to submit the form set form(submit_button) "" ;# list of all submit buttons } # Where we're done try to get all of the state into the widgets so # we can free up the form structure here. Unfortunately, we can't! proc HMtag_/form {win param text} { upvar #0 HM$win var upvar #0 $var(form_id) form # make submit button entries for all radio buttons foreach name [array names form radio_*] { regsub radio_ $name {} name lappend form(submit) [list $name \$form(radio_$name)] } # process the reset button(s) foreach item $form(reset_button) { $item configure -command $form(reset) } # no submit button - add one if {$form(submit_button) == ""} { HMinput_submit $win {} } # process the "submit" command(s) # each submit button could have its own name,value pair foreach item $form(submit_button) { set submit $form(submit) catch {lappend submit $form(submit_$item)} $item configure -command \ [list HMsubmit_button $win $var(form_id) $form(param) \ $submit] } # unset all unused fields here unset form(reset) form(submit) form(reset_button) form(submit_button) unset var(form_id) } ################################################################### # handle form input items # each item type is handled in a separate procedure # Each "type" procedure needs to: # - create the window # - initialize it # - add the "submit" and "reset" commands onto the proper Q's # "submit" is subst'd # "reset" is eval'd proc HMtag_input {win param text} { upvar #0 HM$win var set type text ;# the default HMextract_param $param type set type [string tolower $type] if {[catch {HMinput_$type $win $param} err]} { puts stderr $err } } # input type=text # parameters NAME (reqd), MAXLENGTH, SIZE, VALUE proc HMinput_text {win param {show {}}} { upvar #0 HM$win var upvar #0 $var(form_id) form # make the entry HMextract_param $param name ;# required set item $win.input_text,$var(tags) set size 20; HMextract_param $param size set maxlength 0; HMextract_param $param maxlength entry $item -width $size -show $show # set the initial value set value ""; HMextract_param $param value $item insert 0 $value # insert the entry HMwin_install $win $item # set the "reset" and "submit" commands append form(reset) ";$item delete 0 end;$item insert 0 [list $value]" lappend form(submit) [list $name "\[$item get]"] # handle the maximum length (broken - no way to cleanup bindtags state) if {$maxlength} { bindtags $item "[bindtags $item] max$maxlength" bind max$maxlength "%W delete $maxlength end" } } # password fields - same as text, only don't show data # parameters NAME (reqd), MAXLENGTH, SIZE, VALUE proc HMinput_password {win param} { HMinput_text $win $param * } # checkbuttons are missing a "get" option, so we must use a global # variable to store the value. # Parameters NAME, VALUE, (reqd), CHECKED proc HMinput_checkbox {win param} { upvar #0 HM$win var upvar #0 $var(form_id) form HMextract_param $param name HMextract_param $param value # Set the global variable, don't use the "form" alias as it is not # defined in the global scope of the button set variable $var(form_id)(check_$var(tags)) set item $win.input_checkbutton,$var(tags) checkbutton $item -variable $variable -off {} -on $value -text " " if {[HMextract_param $param checked]} { $item select append form(reset) ";$item select" } else { append form(reset) ";$item deselect" } HMwin_install $win $item lappend form(submit) [list $name \$form(check_$var(tags))] } # radio buttons. These are like check buttons, but only one can be selected proc HMinput_radio {win param} { upvar #0 HM$win var upvar #0 $var(form_id) form HMextract_param $param name HMextract_param $param value set first [expr ![info exists form(radio_$name)]] set variable $var(form_id)(radio_$name) set variable $var(form_id)(radio_$name) set item $win.input_radiobutton,$var(tags) radiobutton $item -variable $variable -value $value -text " " HMwin_install $win $item if {$first || [HMextract_param $param checked]} { $item select append form(reset) ";$item select" } else { append form(reset) ";$item deselect" } # do the "submit" actions in /form so we only end up with 1 per button grouping # contributing to the submission } # hidden fields, just append to the "submit" data # params: NAME, VALUE (reqd) proc HMinput_hidden {win param} { upvar #0 HM$win var upvar #0 $var(form_id) form HMextract_param $param name HMextract_param $param value lappend form(submit) [list $name $value] } # handle input images. The spec isn't very clear on these, so I'm not # sure its quite right # Use std image tag, only set up our own callbacks # (e.g. make sure ismap isn't set) # params: NAME, SRC (reqd) ALIGN proc HMinput_image {win param} { upvar #0 HM$win var upvar #0 $var(form_id) form HMextract_param $param name set name ;# barf if no name is specified set item [HMtag_img $win $param {}] $item configure -relief raised -bd 2 -bg blue # make a dummy "submit" button, and invoke it to send the form. # We have to get the %x,%y in the value somehow, so calculate it during # binding, and save it in the form array for later processing set submit $win.dummy_submit,$var(tags) if {[winfo exists $submit]} { destroy $submit } button $submit -takefocus 0;# this never gets mapped! lappend form(submit_button) $submit set form(submit_$submit) [list $name $name.\$form(X).\$form(Y)] $item configure -takefocus 1 bind $item "catch \{$win see $item\}" bind $item <1> "$item configure -relief sunken" bind $item " set $var(form_id)(X) 0 set $var(form_id)(Y) 0 $submit invoke " bind $item " set $var(form_id)(X) %x set $var(form_id)(Y) %y $item configure -relief raised $submit invoke " } # Set up the reset button. Wait for the /form to attach # the -command option. There could be more that 1 reset button # params VALUE proc HMinput_reset {win param} { upvar #0 HM$win var upvar #0 $var(form_id) form set value reset HMextract_param $param value set item $win.input_reset,$var(tags) button $item -text [HMmap_esc $value] HMwin_install $win $item lappend form(reset_button) $item } # Set up the submit button. Wait for the /form to attach # the -command option. There could be more that 1 submit button # params: NAME, VALUE proc HMinput_submit {win param} { upvar #0 HM$win var upvar #0 $var(form_id) form HMextract_param $param name set value submit HMextract_param $param value set item $win.input_submit,$var(tags) button $item -text [HMmap_esc $value] -fg blue HMwin_install $win $item lappend form(submit_button) $item # need to tie the "name=value" to this button # save the pair and do it when we finish the submit button catch {set form(submit_$item) [list $name $value]} } ######################################################################### # selection items # They all go into a list box. We don't what to do with the listbox until # we know how many items end up in it. Gather up the data for the "options" # and finish up in the /select tag # params: NAME (reqd), MULTIPLE, SIZE proc HMtag_select {win param text} { upvar #0 HM$win var upvar #0 $var(form_id) form HMextract_param $param name set size 5; HMextract_param $param size set form(select_size) $size set form(select_name) $name set form(select_values) "" ;# list of values to submit if {[HMextract_param $param multiple]} { set mode multiple } else { set mode single } set item $win.select,$var(tags) frame $item set form(select_frame) $item listbox $item.list -selectmode $mode -width 0 -exportselection 0 HMwin_install $win $item } # select options # The values returned in the query may be different from those # displayed in the listbox, so we need to keep a separate list of # query values. # form(select_default) - contains the default query value # form(select_frame) - name of the listbox's containing frame # form(select_values) - list of query values # params: VALUE, SELECTED proc HMtag_option {win param text} { upvar #0 HM$win var upvar #0 $var(form_id) form upvar $text data set frame $form(select_frame) # set default option (or options) if {[HMextract_param $param selected]} { lappend form(select_default) [$form(select_frame).list size] } set value [string trimright $data " \n"] $frame.list insert end $value HMextract_param $param value lappend form(select_values) $value set data "" } # do most of the work here! # if SIZE>1, make the listbox. Otherwise make a "drop-down" # listbox with a label in it # If the # of items > size, add a scroll bar # This should probably be broken up into callbacks to make it # easier to override the "look". proc HMtag_/select {win param text} { upvar #0 HM$win var upvar #0 $var(form_id) form set frame $form(select_frame) set size $form(select_size) set items [$frame.list size] # set the defaults and reset button append form(reset) ";$frame.list selection clear 0 $items" if {[info exists form(select_default)]} { foreach i $form(select_default) { $frame.list selection set $i append form(reset) ";$frame.list selection set $i" } } else { $frame.list selection set 0 append form(reset) ";$frame.list selection set 0" } # set up the submit button. This is the general case. For single # selections we could be smarter for {set i 0} {$i < $size} {incr i} { set value [format {[expr {[%s selection includes %s] ? {%s} : {}}]} \ $frame.list $i [lindex $form(select_values) $i]] lappend form(submit) [list $form(select_name) $value] } # show the listbox - no scroll bar if {$size > 1 && $items <= $size} { $frame.list configure -height $items pack $frame.list # Listbox with scrollbar } elseif {$size > 1} { scrollbar $frame.scroll -command "$frame.list yview" \ -orient v -takefocus 0 $frame.list configure -height $size \ -yscrollcommand "$frame.scroll set" pack $frame.list $frame.scroll -side right -fill y # This is a joke! } else { scrollbar $frame.scroll -command "$frame.list yview" \ -orient h -takefocus 0 $frame.list configure -height 1 \ -yscrollcommand "$frame.scroll set" pack $frame.list $frame.scroll -side top -fill x } # cleanup foreach i [array names form select_*] { unset form($i) } } # do a text area (multi-line text) # params: COLS, NAME, ROWS (all reqd, but default rows and cols anyway) proc HMtag_textarea {win param text} { upvar #0 HM$win var upvar #0 $var(form_id) form upvar $text data set rows 5; HMextract_param $param rows set cols 30; HMextract_param $param cols HMextract_param $param name set item $win.textarea,$var(tags) frame $item text $item.text -width $cols -height $rows -wrap none \ -yscrollcommand "$item.scroll set" -padx 3 -pady 3 scrollbar $item.scroll -command "$item.text yview" -orient v $item.text insert 1.0 $data HMwin_install $win $item pack $item.text $item.scroll -side right -fill y lappend form(submit) [list $name "\[$item.text get 0.0 end]"] append form(reset) ";$item.text delete 1.0 end; \ $item.text insert 1.0 [list $data]" set data "" } # procedure to install windows into the text widget # - win: name of the text widget # - item: name of widget to install proc HMwin_install {win item} { upvar #0 HM$win var $win window create $var(S_insert) -window $item -align bottom $win tag add indent$var(level) $item set focus [expr {[winfo class $item] != "Frame"}] $item configure -takefocus $focus bind $item "$win see $item" } ##################################################################### # Assemble and submit the query # each list element in "stuff" is a name/value pair # - The names are the NAME parameters of the various fields # - The values get run through "subst" to extract the values # - We do the user callback with the list of name value pairs proc HMsubmit_button {win form_id param stuff} { upvar #0 HM$win var upvar #0 $form_id form set query "" foreach pair $stuff { set value [subst [lindex $pair 1]] if {$value != ""} { set item [lindex $pair 0] lappend query $item $value } } # this is the user callback. HMsubmit_form $win $param $query } # sample user callback for form submission # should be replaced by the application # Sample version generates a string suitable for http proc HMsubmit_form {win param query} { set result "" set sep "" foreach i $query { append result $sep [HMmap_reply $i] if {$sep != "="} {set sep =} {set sep &} } puts $result } # do x-www-urlencoded character mapping # The spec says: "non-alphanumeric characters are replaced by '%HH'" set HMalphanumeric a-zA-Z0-9 ;# definition of alphanumeric character class for {set i 1} {$i <= 256} {incr i} { set c [format %c $i] if {![string match \[$HMalphanumeric\] $c]} { set HMform_map($c) %[format %.2x $i] } } # These are handled specially array set HMform_map { " " + \n %0d%0a } # 1 leave alphanumerics characters alone # 2 Convert every other character to an array lookup # 3 Escape constructs that are "special" to the tcl parser # 4 "subst" the result, doing all the array substitutions proc HMmap_reply {string} { global HMform_map HMalphanumeric regsub -all \[^$HMalphanumeric\] $string {$HMform_map(&)} string regsub -all \n $string {\\n} string regsub -all \t $string {\\t} string regsub -all {[][{})\\]\)} $string {\\&} string return [subst $string] } # convert a x-www-urlencoded string int a a list of name/value pairs # 1 convert a=b&c=d... to {a} {b} {c} {d}... # 2, convert + to " " # 3, convert %xx to char equiv proc HMcgiDecode {data} { set data [split $data "&="] foreach i $data { lappend result [cgiMap $i] } return $result } proc HMcgiMap {data} { regsub -all {\+} $data " " data if {[regexp % $data]} { regsub -all {([][$\\])} $data {\\\1} data regsub -all {%([0-9a-fA-F][0-9a-fA-F])} $data {[format %c 0x\1]} data return [subst $data] } else { return $data } } # There is a bug in the tcl library focus routines that prevents focus # from every reaching an un-viewable window. Use our *own* # version of the library routine, until the bug is fixed, make sure we # over-ride the library version, and not the otherway around auto_load tkFocusOK proc tkFocusOK w { set code [catch {$w cget -takefocus} value] if {($code == 0) && ($value != "")} { if {$value == 0} { return 0 } elseif {$value == 1} { return 1 } else { set value [uplevel #0 $value $w] if {$value != ""} { return $value } } } set code [catch {$w cget -state} value] if {($code == 0) && ($value == "disabled")} { return 0 } regexp Key|Focus "[bind $w] [bind [winfo class $w]]" } igraph/inst/CITATION0000644000176000001440000000120412251656216013671 0ustar ripleyuserscitHeader("To cite 'igraph' in publications use:") citEntry(entry="article", title="The igraph software package for complex network research", author=personList(as.person("Gabor Csardi"), as.person("Tamas Nepusz")), journal="InterJournal", volume="Complex Systems", pages="1695", year="2006", url="http://igraph.org", textVersion=paste("Csardi G, Nepusz T: The igraph software package for ", "complex network research, InterJournal, ", "Complex Systems 1695. 2006. ", "http://igraph.org", sep="")) igraph/inst/tests/0000755000176000001440000000000012325365704013702 5ustar ripleyusersigraph/inst/tests/test_graph.density.R0000644000176000001440000000057512251656216017651 0ustar ripleyusers context("graph.density") test_that("graph.density works", { library(igraph) g <- erdos.renyi.game(50, 4/50) gd <- graph.density(g) gd2 <- ecount(g) / vcount(g) / (vcount(g)-1) * 2 expect_that(gd, equals(gd2)) #### g <- erdos.renyi.game(50, 4/50, dir=TRUE) gd <- graph.density(g) gd2 <- ecount(g) / vcount(g) / (vcount(g)-1) expect_that(gd, equals(gd2)) }) igraph/inst/tests/test_alpha.centrality.R0000644000176000001440000000417512251656216020334 0ustar ripleyusers context("alpha.centrality") test_that("dense alpha.centrality works", { library(igraph) g.1 <- graph( c(1,3,2,3,3,4,4,5) ) ac1 <- alpha.centrality(g.1, sparse=FALSE) expect_that(ac1, equals(c(1, 1, 3, 4, 5))) g.2 <- graph( c(2,1,3,1,4,1,5,1) ) ac2 <- alpha.centrality(g.2, sparse=FALSE) expect_that(ac2, equals(c(5,1,1,1,1))) g.3 <- graph( c(1,2,2,3,3,4,4,1,5,1) ) ac3 <- alpha.centrality(g.3, alpha=0.5, sparse=FALSE) expect_that(ac3, equals(c(76, 68, 64, 62, 30)/30)) }) test_that("sparse alpha.centrality works", { if (require(Matrix, quietly=TRUE)) { library(igraph) g.1 <- graph( c(1,3,2,3,3,4,4,5) ) ac1 <- alpha.centrality(g.1, sparse=TRUE) expect_that(ac1, equals(c(1, 1, 3, 4, 5))) g.2 <- graph( c(2,1,3,1,4,1,5,1) ) ac2 <- alpha.centrality(g.2, sparse=TRUE) expect_that(ac2, equals(c(5,1,1,1,1))) g.3 <- graph( c(1,2,2,3,3,4,4,1,5,1) ) ac3 <- alpha.centrality(g.3, alpha=0.5, sparse=TRUE) expect_that(ac3, equals(c(76, 68, 64, 62, 30)/30)) } }) ############################## ## weighted version test_that("weighted dense alpha.centrality works", { library(igraph) star <- graph.star(10) E(star)$weight <- sample(ecount(star)) ac1 <- alpha.centrality(star, sparse=FALSE) expect_that(ac1, equals(c(46, 1, 1, 1, 1, 1, 1, 1, 1, 1))) ac2 <- alpha.centrality(star, weights="weight", sparse=FALSE) expect_that(ac2, equals(c(46, 1, 1, 1, 1, 1, 1, 1, 1, 1))) ac3 <- alpha.centrality(star, weights=NA, sparse=FALSE) expect_that(ac3, equals(c(vcount(star), 1, 1, 1, 1, 1, 1, 1, 1, 1))) }) test_that("weighted sparse alpha.centrality works", { if (require("Matrix", quietly=TRUE)) { library(igraph) star <- graph.star(10) E(star)$weight <- sample(ecount(star)) ac1 <- alpha.centrality(star, sparse=TRUE) expect_that(ac1, equals(c(46, 1, 1, 1, 1, 1, 1, 1, 1, 1))) ac2 <- alpha.centrality(star, weights="weight", sparse=TRUE) expect_that(ac2, equals(c(46, 1, 1, 1, 1, 1, 1, 1, 1, 1))) ac3 <- alpha.centrality(star, weights=NA, sparse=TRUE) expect_that(ac3, equals(c(vcount(star), 1, 1, 1, 1, 1, 1, 1, 1, 1))) } }) igraph/inst/tests/test_dominator.tree.R0000644000176000001440000000154012251656216020015 0ustar ripleyusers context("dominator.tree") test_that("dominator.tree works", { library(igraph) g <- graph.formula(R-+A:B:C, A-+D, B-+A:D:E, C-+F:G, D-+L, E-+H, F-+I, G-+I:J, H-+E:K, I-+K, J-+I, K-+I:R, L-+H) dtree <- dominator.tree(g, root="R") dtree$dom <- V(g)$name[ dtree$dom ] dtree$leftout <- V(g)$name[ dtree$leftout ] expect_that(dtree$dom, equals(c("R", "R", "R", "R", "R", "C", "C", "D", "R", "R", "G", "R"))) expect_that(dtree$leftout, equals(character())) expect_that(get.edgelist(dtree$domtree), equals(structure(c("R", "R", "R", "R", "R", "C", "C", "D", "R", "R", "G", "R", "A", "B", "C", "D", "E", "F", "G", "L", "H", "I", "J", "K"), .Dim = c(12L, 2L)))) }) igraph/inst/tests/test_girth.R0000644000176000001440000000063012251656216016177 0ustar ripleyusers context("girth") test_that("girth works", { library(igraph) ## No circle in a tree g <- graph.tree(1000, 3) gi <- girth(g) expect_that(gi$girth, equals(0)) expect_that(gi$circle, equals(numeric())) ## The worst case running time is for a ring g <- graph.ring(100) gi <- girth(g) expect_that(gi$girth, equals(100)) expect_that(sort(diff(gi$circle)), equals(c(-99, rep(1, 98)))) }) igraph/inst/tests/test_edge.connectivity.R0000644000176000001440000000255412251656216020512 0ustar ripleyusers context("edge.connectivity") test_that("edge.connectivity works", { library(igraph) gc <- function(graph) { clu <- clusters(graph) induced.subgraph(graph, which(clu$membership==which.max(clu$csize))) } g <- gc(erdos.renyi.game(30, 8/30)) ec <- edge.connectivity(g) ecST <- Inf for (j in 1:(vcount(g)-1)) { for (k in (j+1):vcount(g)) { ec2 <- edge.connectivity(g, source=j, target=k) if (ec2 < ecST) { ecST <- ec2 } } } expect_that(ec, equals(ecST)) #### kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) ec1 <- edge.connectivity(kite, source="Heather", target="Andre") ec2 <- edge.connectivity(kite, source="Garth", target="Andre") ec3 <- edge.connectivity(kite, source="Garth", target="Ike") expect_that(ec1, equals(2)) expect_that(ec2, equals(4)) expect_that(ec3, equals(1)) }) igraph/inst/tests/test_get.shortest.paths.R0000644000176000001440000000170112251656216020631 0ustar ripleyusers context("get.shortest.paths") test_that("get.shortest.paths works", { library(igraph) edges <- matrix(c("s", "a", 2, "s", "b", 4, "a", "t", 4, "b", "t", 2, "a", "1", 1, "a", "2", 1, "a", "3", 2, "1", "b", 1, "2", "b", 2, "3", "b", 1), byrow=TRUE, ncol=3, dimnames=list(NULL, c("from", "to", "weight"))) edges <- as.data.frame(edges) edges[[3]] <- as.numeric(as.character(edges[[3]])) g <- graph.data.frame(as.data.frame(edges)) all1 <- get.all.shortest.paths(g, "s", "t", weights=NA)$res all2 <- get.all.shortest.paths(g, "s", "t")$res s1 <- get.shortest.paths(g, "s", "t", weights=NA) s2 <- get.shortest.paths(g, "s", "t") expect_that(s1$vpath %in% all1, is_true()) expect_that(s2$vpath %in% all2, is_true()) }) igraph/inst/tests/test_fartherst.nodes.R0000644000176000001440000000154112251656216020175 0ustar ripleyusers context("farthest.nodes") test_that("farthest.nodes works", { library(igraph) kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) fn <- farthest.nodes(kite) expect_that(fn, equals(c(1,10,4))) expect_that(shortest.paths(kite, v=fn[1], to=fn[2])[1], equals(fn[3])) expect_that(diameter(kite), equals(fn[3])) }) igraph/inst/tests/test_callbacks.R0000644000176000001440000000046412325263644017007 0ustar ripleyusers context("igraph callbacks in R") test_that("igraph calls from callbacks are not allowed", { library(igraph) f <- function(graph, data, extra) { vcount(graph) } expect_that(graph.bfs(graph.ring(10), root=1, callback=f), throws_error("igraph callbacks cannot call igraph functions")) }) igraph/inst/tests/test_are.connected.R0000644000176000001440000000125212251656216017573 0ustar ripleyusers context("are.connected") test_that("are.connected works", { library(igraph) g <- graph.formula( A-B-C, B-D ) expect_that(are.connected(g, "A", "B"), is_true()) expect_that(are.connected(g, "B", "A"), is_true()) expect_that(are.connected(g, "A", "D"), is_false()) g2 <- graph( c(1,2, 2,3, 3,4), dir=FALSE ) expect_that(are.connected(g2, 1,2), is_true()) expect_that(are.connected(g2, 3,2), is_true()) expect_that(are.connected(g2, 4,1), is_false()) g3 <- graph.formula( A-+B-+C, B-+D ) expect_that(are.connected(g3, "A", "C"), is_false()) expect_that(are.connected(g3, "A", "B"), is_true()) expect_that(are.connected(g3, "B", "A"), is_false()) }) igraph/inst/tests/test_articulation.points.R0000644000176000001440000000051012251656216021070 0ustar ripleyusers context("articulation.points") test_that("articulation.points works", { library(igraph) g <- graph.full(5) + graph.full(5) clu <- clusters(g)$membership g <- add.edges(g, c(match(1,clu), match(2,clu)) ) ap <- articulation.points(g) deg <- degree(g) expect_that(sort(which(deg==max(deg))), equals(sort(ap))) }) igraph/inst/tests/test_operators.R0000644000176000001440000000222712251656216017104 0ustar ripleyusers context("operators") test_that("operators work", { library(igraph) o <- function(x) x[order(x[,1], x[,2]),] g1 <- graph.ring(10) g2 <- graph.star(11, center=11, mode="undirected") gu <- graph.union(g1, g2) expect_that(vcount(gu), equals(11)) expect_that(ecount(gu), equals(20)) expect_that(o(rbind(get.edgelist(g1), get.edgelist(g2))), equals(o(get.edgelist(gu)))) gdu <- graph.disjoint.union(g1, g2) expect_that(o(get.edgelist(gdu)), equals(o(rbind(get.edgelist(g1), get.edgelist(g2)+vcount(g1))))) #### expect_that(graph.isomorphic(graph.difference(gu, g1), g2), is_true()) #### expect_that(graph.isomorphic(graph.intersection(gu, g2), g2), is_true()) expect_that(graph.isomorphic(graph.intersection(gu, g1, keep.all.vertices=FALSE), g1),is_true()) #### expect_that(graph.complementer(graph.complementer(g2)), equals(g2)) #### gc <- graph.compose(gu, g1) expect_that(vcount(gc), equals(11)) expect_that(ecount(gc), equals(60)) expect_that(diameter(gc), equals(2)) }) igraph/inst/tests/test_communities.R0000644000176000001440000000510612325262335017416 0ustar ripleyusers context("communities") test_that("community detection functions work", { library(igraph) set.seed(42) F <- list("edge.betweenness.community", "fastgreedy.community", "label.propagation.community", "leading.eigenvector.community", "multilevel.community", "optimal.community", "spinglass.community", "walktrap.community") karate <- graph.famous("Zachary") for (f in F) { f <- get(f) comm <- f(karate) expect_that(modularity(comm), equals(modularity(karate, membership(comm)))) cc <- communities(comm) expect_that(all(!duplicated(unlist(cc))), is_true()) expect_that(all(unlist(cc) <= vcount(karate) & unlist(cc) >= 1), is_true()) expect_that(length(comm), equals(max(membership(comm)))) } fc <- fastgreedy.community(karate) m1 <- modularity(karate, cutat(fc, no=1)) m2 <- modularity(karate, cutat(fc, no=2)) m3 <- modularity(karate, cutat(fc, no=3)) m4 <- modularity(karate, cutat(fc, no=4)) expect_that(m1, equals(0)) expect_that(m2, equals(0.3717948718)) expect_that(m3, equals(0.3806706114)) expect_that(m4, equals(0.3759861933)) cr <- crossing(fc, karate) expect_that(cr, equals(c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) )) }) test_that("creating communities objects works", { library(igraph) set.seed(42) karate <- graph.famous("Zachary") membership <- sample(1:2, vcount(karate), replace=TRUE) mod <- modularity(karate, membership) comm <- create.communities(algorithm="random", membership=membership, mod=mod, foo="bar") print(comm) expect_that(membership(comm), equals(membership)) expect_that(modularity(comm), equals(mod)) expect_that(algorithm(comm), equals("random")) expect_that(comm$foo, equals("bar")) }) igraph/inst/tests/test_operators4.R0000644000176000001440000002376712251656216017204 0ustar ripleyusers context("operators on named graphs") test_that("disjoint union works for named graphs", { library(igraph) g1 <- g2 <- graph.ring(10) g1$foo <- "bar" V(g1)$name <- letters[ 1:10] V(g2)$name <- letters[11:20] E(g1)$weight <- 1:10 E(g2)$weight <- 10:1 V(g1)$a1 <- 1:10 V(g2)$a2 <- 11:20 E(g1)$b1 <- 1:10 E(g2)$b2 <- 11:20 g <- graph.disjoint.union(g1, g2) expect_that(sort(list.graph.attributes(g)), equals(c("circular_1", "circular_2", "foo", "mutual_1", "mutual_2", "name_1", "name_2"))) expect_that(sort(list.vertex.attributes(g)), equals(c("a1", "a2", "name"))) expect_that(sort(list.edge.attributes(g)), equals(c("b1", "b2", "weight"))) expect_that(V(g)$name, equals(letters[1:20])) expect_that(V(g)$a1, equals(c(1:10, rep(NA, 10)))) expect_that(V(g)$a2, equals(c(rep(NA, 10), 11:20))) expect_that(E(g)$weight, equals(c(1:10, 10:1))) expect_that(E(g)$b1, equals(c(1:10, rep(NA, 10)))) expect_that(E(g)$b2, equals(c(rep(NA, 10), 11:20))) }) test_that("disjoint union gives warning for non-unique vertex names", { library(igraph) g1 <- graph.ring(5); V(g1)$name <- letters[1:5] g2 <- graph.ring(5); V(g2)$name <- letters[5:9] expect_that(graph.disjoint.union(g1, g2), gives_warning("Duplicate vertex names in disjoint union")) }) test_that("union of unnamed graphs works", { library(igraph) g1 <- graph.ring(10) g2 <- graph.ring(13) g1$foo <- "bar" E(g1)$weight <- 1:10 E(g2)$weight <- 13:1 V(g1)$a1 <- 1:10 V(g2)$a2 <- 11:23 E(g1)$b1 <- letters[1:10] E(g2)$b2 <- letters[11:23] g <- graph.union(g1, g2) expect_that(sort(list.graph.attributes(g)), equals(c("circular_1", "circular_2", "foo", "mutual_1", "mutual_2", "name_1", "name_2"))) expect_that(sort(list.vertex.attributes(g)), equals(c("a1", "a2"))) expect_that(sort(list.edge.attributes(g)), equals(c("b1", "b2", "weight_1", "weight_2"))) df1 <- get.data.frame(g) df1 <- df1[ order(df1$from, df1$to), c(1,2,3,5,4,6)] df2 <- merge(get.data.frame(g1), get.data.frame(g2), by=c("from", "to"), all=TRUE) rownames(df1) <- seq_len(nrow(df1)) colnames(df2) <- c("from", "to", "weight_1", "b1", "weight_2", "b2") expect_that(df1, equals(df2)) }) test_that("union of named graphs works", { library(igraph) g1 <- graph.ring(10) g2 <- graph.ring(13) V(g1)$name <- letters[seq_len(vcount(g1))] V(g2)$name <- letters[seq_len(vcount(g2))] g1$foo <- "bar" E(g1)$weight <- 1:10 E(g2)$weight <- 13:1 V(g1)$a1 <- 1:10 V(g2)$a2 <- 11:23 E(g1)$b1 <- letters[1:10] E(g2)$b2 <- letters[11:23] g <- graph.union(g1, g2) expect_that(sort(list.graph.attributes(g)), equals(c("circular_1", "circular_2", "foo", "mutual_1", "mutual_2", "name_1", "name_2"))) expect_that(sort(list.vertex.attributes(g)), equals(c("a1", "a2", "name"))) expect_that(sort(list.edge.attributes(g)), equals(c("b1", "b2", "weight_1", "weight_2"))) df1 <- get.data.frame(g, what="both") g.v <- read.table(stringsAsFactors=FALSE, textConnection(" a1 a2 name a 1 11 a b 2 12 b c 3 13 c d 4 14 d e 5 15 e f 6 16 f g 7 17 g h 8 18 h i 9 19 i j 10 20 j k NA 21 k l NA 22 l m NA 23 m ")) expect_that(df1$vertices, equals(g.v)) g.e <- read.table(stringsAsFactors=FALSE, textConnection(" from to weight_1 weight_2 b1 b2 1 l m NA 2 NA v 2 k l NA 3 NA u 3 j k NA 4 NA t 4 i j 9 5 i s 5 h i 8 6 h r 6 g h 7 7 g q 7 f g 6 8 f p 8 e f 5 9 e o 9 d e 4 10 d n 10 c d 3 11 c m 11 b c 2 12 b l 12 a m NA 1 NA w 13 a j 10 NA j NA 14 a b 1 13 a k ")) rownames(df1$edges) <- rownames(df1$edges) expect_that(df1$edges, equals(g.e)) }) test_that("intersection of named graphs works", { library(igraph) g1 <- graph.ring(10) g2 <- graph.ring(13) V(g1)$name <- letters[V(g1)] V(g2)$name <- letters[V(g2)] g1$foo <- "bar" E(g1)$weight <- 1:10 E(g2)$weight <- 13:1 V(g1)$a1 <- 1:10 V(g2)$a2 <- 11:23 E(g1)$b1 <- letters[1:10] E(g2)$b2 <- letters[11:23] g <- graph.intersection(g1, g2, keep.all.vertices=FALSE) expect_that(sort(list.graph.attributes(g)), equals(c("circular_1", "circular_2", "foo", "mutual_1", "mutual_2", "name_1", "name_2"))) expect_that(sort(list.vertex.attributes(g)), equals(c("a1", "a2", "name"))) expect_that(sort(list.edge.attributes(g)), equals(c("b1", "b2", "weight_1", "weight_2"))) df1 <- get.data.frame(g, what="both") g.e <- read.table(stringsAsFactors=FALSE, textConnection(" from to weight_1 weight_2 b1 b2 1 i j 9 5 i s 2 h i 8 6 h r 3 g h 7 7 g q 4 f g 6 8 f p 5 e f 5 9 e o 6 d e 4 10 d n 7 c d 3 11 c m 8 b c 2 12 b l 9 a b 1 13 a k ")) rownames(df1$edges) <- rownames(df1$edges) expect_that(df1$edges, equals(g.e)) g.v <- read.table(stringsAsFactors=FALSE, textConnection(" a1 a2 name a 1 11 a b 2 12 b c 3 13 c d 4 14 d e 5 15 e f 6 16 f g 7 17 g h 8 18 h i 9 19 i j 10 20 j ")) expect_that(df1$vertices, equals(g.v)) gg <- graph.intersection(g1, g2, keep.all.vertices=TRUE) df2 <- get.data.frame(gg, what="both") rownames(df2$edges) <- rownames(df2$edges) expect_that(df2$edges, equals(g.e)) gg.v <- read.table(stringsAsFactors=FALSE, textConnection(" a1 a2 name a 1 11 a b 2 12 b c 3 13 c d 4 14 d e 5 15 e f 6 16 f g 7 17 g h 8 18 h i 9 19 i j 10 20 j k NA 21 k l NA 22 l m NA 23 m ")) expect_that(df2$vertices, equals(gg.v)) }) test_that("difference of named graphs works", { library(igraph) g1 <- graph.ring(10) g2 <- graph.star(11, center=11, mode="undirected") V(g1)$name <- letters[1:10] V(g2)$name <- letters[1:11] g <- g1 %u% g2 sg <- graph.ring(4) V(sg)$name <- letters[c(1,2,3,11)] df1 <- get.data.frame(g - sg, what="both") t1.e <- read.table(stringsAsFactors=FALSE, textConnection(" from to 1 a j 2 b k 3 c d 4 j k 5 i k 6 h k 7 g k 8 f k 9 e k 10 d k 11 d e 12 e f 13 f g 14 g h 15 h i 16 i j ")) rownames(df1$edges) <- rownames(df1$edges) expect_that(df1$edges, equals(t1.e)) expect_that(df1$vertices, equals(data.frame(row.names=letters[1:11], name=letters[1:11], stringsAsFactors=FALSE))) gg <- sg - g expect_that(ecount(gg), equals(0)) expect_that(V(gg)$name, equals(letters[c(1:3,11)])) }) test_that("compose works for named graphs", { library(igraph) g1 <- graph.formula( A-B:D:E, B-C:D, C-D, D-E ) g2 <- graph.formula( A-B-E-A ) V(g1)$bar1 <- seq_len(vcount(g1)) V(g2)$bar2 <- seq_len(vcount(g2)) V(g1)$foo <- letters[seq_len(vcount(g1))] V(g2)$foo <- letters[seq_len(vcount(g2))] E(g1)$bar1 <- seq_len(ecount(g1)) E(g2)$bar2 <- seq_len(ecount(g2)) E(g1)$foo <- letters[seq_len(ecount(g1))] E(g2)$foo <- letters[seq_len(ecount(g2))] g <- graph.compose(g1, g2) df <- get.data.frame(g, what="both") df.v <- read.table(stringsAsFactors=FALSE, textConnection(" bar1 foo_1 foo_2 bar2 name A 1 a a 1 A B 2 b b 2 B D 3 c NA NA D E 4 d c 3 E C 5 e NA NA C ")) expect_that(df$vertices, equals(df.v)) df.e <- read.table(stringsAsFactors=FALSE, textConnection(" from to bar1 foo_1 foo_2 bar2 1 A B 3 c c 3 2 A A 3 c b 2 3 A E 1 a c 3 4 A A 1 a a 1 5 B E 1 a b 2 6 B B 1 a a 1 7 B D 6 f c 3 8 A D 6 f b 2 9 D E 4 d c 3 10 A D 4 d a 1 11 D E 2 b b 2 12 B D 2 b a 1 13 E E 3 c b 2 14 B E 3 c a 1 15 E C 5 e c 3 16 A C 5 e a 1 ")) rownames(df$edges) <- rownames(df$edges) expect_that(df$edges, equals(df.e)) }) test_that("intersection of non-named graphs keeps attributes properly", { library(igraph) set.seed(42) g <- erdos.renyi.game(10, 1/2) g2 <- erdos.renyi.game(10, 1/2) E(g)$weight <- sample(ecount(g)) E(g2)$weight <- sample(ecount(g2)) gi <- graph.intersection(g, g2) rn <- function(D) { rownames(D) <- paste(D[,1], D[,2], sep="-") D } df <- rn(get.data.frame(g)) df2 <- rn(get.data.frame(g2)) dfi <- rn(get.data.frame(gi)) expect_that(df[rownames(dfi), ], is_equivalent_to(dfi[, 1:3])) expect_that(df2[rownames(dfi), ], is_equivalent_to(dfi[, c(1,2,4)])) }) test_that("union of non-named graphs keeps attributes properly", { library(igraph) set.seed(42) g <- erdos.renyi.game(10, 1/2) g2 <- erdos.renyi.game(10, 1/2) E(g)$weight <- sample(ecount(g)) E(g2)$weight <- sample(ecount(g2)) gu <- graph.union(g, g2) rn <- function(D) { rownames(D) <- paste(D[,1], D[,2], sep="-") D } df <- rn(get.data.frame(g)) df2 <- rn(get.data.frame(g2)) dfu <- rn(get.data.frame(gu)) expect_that(dfu[rownames(df), 1:3], is_equivalent_to(df)) expect_that(dfu[rownames(df2), c(1,2,4)], is_equivalent_to(df2)) expect_that(dfu[!rownames(dfu) %in% rownames(df), 3], equals(rep(NA_real_, ecount(gu)-ecount(g)))) expect_that(dfu[!rownames(dfu) %in% rownames(df2), 4], equals(rep(NA_real_, ecount(gu)-ecount(g2)))) }) igraph/inst/tests/test_bug-1073800-clique.R0000644000176000001440000000036712251656216017746 0ustar ripleyusers context("Bug 1073800") test_that("Largest cliques is correct", { library(igraph) adj <- matrix(1, nrow=11, ncol=11) - diag(11) g <- graph.adjacency(adj) lc <- suppressWarnings(largest.cliques(g)) expect_that(lc, equals(list(1:11))) }) igraph/inst/tests/test_attributes.R0000644000176000001440000000527012251656216017255 0ustar ripleyusers context("attributes") test_that("assigning and querying attributes work", { library(igraph) ## Create a small ring graph, assign attributes ring <- graph.formula( A-B-C-D-E-F-G-A ) E(ring)$weight <- seq_len(ecount(ring)) ## Query attributes expect_that(V(ring)$name, equals(LETTERS[seq_len(vcount(ring))])) expect_that(E(ring)$weight, equals(seq_len(ecount(ring)))) }) test_that("brackering works", { library(igraph) g <- graph(c(1,2, 1,3, 3,4)) g <- set.vertex.attribute(g, name="weight", value=1:vcount(g)) g <- set.edge.attribute(g, name="weight", value=1:ecount(g)) g <- set.graph.attribute(g, name="name", "foo") graph2 <- set.vertex.attribute(g, name="weight", value=rep(1, vcount(g))) graph2 <- set.edge.attribute(g, name="weight", value=rep(1, ecount(g))) graph2 <- set.graph.attribute(g, name="name", "foobar") expect_that(get.vertex.attribute(g, name="weight"), equals(1:4)) expect_that(get.edge.attribute(g, name="weight"), equals(1:3)) expect_that(get.graph.attribute(g, name="name"), equals("foo")) }) test_that("brackering works with a function", { library(igraph) library(testthat) g <- graph(c(1,2, 1,3, 3,4)) g <- set.vertex.attribute(g, name="weight", value=1:vcount(g)) g <- set.edge.attribute(g, name="weight", value=1:ecount(g)) g <- set.graph.attribute(g, name="name", "foo") run.test <- function(graph) { graph2 <- set.vertex.attribute(graph, name="weight", value=rep(1, vcount(graph))) graph2 <- set.edge.attribute(graph, name="weight", value=rep(1, ecount(graph))) graph2 <- set.graph.attribute(graph, name="name", "foobar") } g2 <- run.test(g) expect_that(get.vertex.attribute(g, name="weight"), equals(1:4)) expect_that(get.edge.attribute(g, name="weight"), equals(1:3)) expect_that(get.graph.attribute(g, name="name"), equals("foo")) }) test_that("brackering works with shortcuts", { library(igraph) g <- graph(c(1,2, 1,3, 3,4)) g <- set.vertex.attribute(g, name="weight", value=1:vcount(g)) g <- set.edge.attribute(g, name="weight", value=1:ecount(g)) g <- set.graph.attribute(g, name="name", "foo") run.test <- function(graph) { V(graph)$weight <- rep(1, vcount(graph)) E(graph)$weight <- rep(1, ecount(graph)) graph$name <- "foobar" } g2 <- run.test(g) expect_that(get.vertex.attribute(g, name="weight"), equals(1:4)) expect_that(get.edge.attribute(g, name="weight"), equals(1:3)) expect_that(get.graph.attribute(g, name="name"), equals("foo")) }) ## TODO: subsetting igraph/inst/tests/test_sdf.R0000644000176000001440000000246512251656216015646 0ustar ripleyusers context("sdf") test_that("sdf works", { library(igraph) sdf <- igraph:::sdf(id=1:10, color="black") expect_that(as.data.frame(sdf), equals(data.frame(id=1:10, color="black"))) ## access expect_that(sdf[1,"id"], equals(1)) expect_that(sdf[1:4, "id"], equals(1:4)) expect_that(sdf[, "id"], equals(1:10)) expect_that(sdf[1, "color"], equals("black")) expect_that(sdf[1:4, "color"], equals(rep("black", 4))) expect_that(sdf[, "color"], equals(rep("black", 10))) ## set sdf2 <- sdf sdf2[5, "id"] <- 100 expect_that(as.data.frame(sdf2), equals(data.frame(id=c(1:4,100,6:10), color="black"))) sdf2 <- sdf sdf2[, "id"] <- 0 expect_that(as.data.frame(sdf2), equals(data.frame(id=rep(0,10), color="black"))) sdf2 <- sdf sdf2[2:10, "id"] <- 1 expect_that(as.data.frame(sdf2), equals(data.frame(id=rep(1,10), color="black"))) sdf2 <- sdf sdf2[, "color"] <- "white" expect_that(as.data.frame(sdf2), equals(data.frame(id=1:10, color="white"))) sdf2 <- sdf sdf2[5:6, "color"] <- "white" expect_that(as.data.frame(sdf2), equals(data.frame(id=1:10, color=c(rep("black", 4), rep("white", 2), rep("black", 4))))) }) igraph/inst/tests/test_forestfire.R0000644000176000001440000000136212251656216017235 0ustar ripleyusers context("forest.fire.game") test_that("forest.fire.game works", { library(igraph) set.seed(42) pars <- list(sparse=c(0.35, 0.2/0.35), densifying=c(0.37, 0.32/0.37), dense=c(0.38, 0.38/0.37)) N <- 5000 G <- lapply(pars, function(x) forest.fire.game(N, fw=x[1], bw=x[2])) xv <- log(2:N) co <- sapply(G, function(x) { yv <- log(cumsum(degree(x, mode="out"))[-1]) coef(lm( yv ~ xv ))[2] }) expect_that(co, equals(structure(c(1.06045500245466, 1.22800967143684, 1.96234121488344), .Names = c("sparse.xv", "densifying.xv", "dense.xv")))) }) igraph/inst/tests/test_neighborhood.R0000644000176000001440000000154212325365704017535 0ustar ripleyusers context("neighborhood") test_that("neighborhood works", { library(igraph) neig <- function(graph, order, vertices) { sp <- shortest.paths(graph) v <- unique(unlist(lapply(vertices, function(x) { w <- which(sp[x,] <= order) }))) induced.subgraph(graph, c(v,vertices)) } g <- erdos.renyi.game(50, 5/50) v <- sample(vcount(g), 1) g1 <- graph.neighborhood(g, 2, v)[[1]] g2 <- neig(g, 2, v) expect_that(graph.isomorphic(g1, g2), is_true()) ######### nei <- function(graph, order, vertices) { sp <- shortest.paths(graph) v <- unique(unlist(lapply(vertices, function(x) { w <- which(sp[x,] <= order) }))) v } v1 <- neighborhood(g, 2, v)[[1]] v2 <- nei(g, 2, v) expect_that(sort(v1), equals(sort(v2))) ######### s <- neighborhood.size(g, 2, v)[[1]] expect_that(s, equals(length(v1))) }) igraph/inst/tests/test_decompose.graph.R0000644000176000001440000000240212251656216020137 0ustar ripleyusers context("decompose.graph") test_that("decompose.graph works", { library(igraph) g <- erdos.renyi.game(1000, 1/1500) G <- decompose.graph(g) clu <- clusters(g) Gsizes <- sapply(G, vcount) expect_that(sort(clu$csize), equals(sort(Gsizes))) }) test_that("decompose.graph works for many components", { library(igraph) g <- graph.empty(50001) tmp <- decompose.graph(g) expect_that(1, equals(1)) }) test_that("decompose.graph works for many components and attributes", { library(igraph) g <- graph.empty(50001) V(g)$name <- 1:vcount(g) tmp <- decompose.graph(g) expect_that(1, equals(1)) }) test_that("decompose.graph keeps attributes", { library(igraph) g <- graph.ring(10) + graph.ring(5) V(g)$name <- letters[1:(10+5)] E(g)$name <- apply(get.edgelist(g), 1, paste, collapse="-") d <- decompose.graph(g) d <- d[order(sapply(d, vcount))] expect_that(length(d), equals(2)) expect_that(sapply(d, vcount), equals(c(5,10))) expect_that(V(d[[1]])$name, equals(letters[1:5+10])) expect_that(V(d[[2]])$name, equals(letters[1:10])) e1 <- apply(get.edgelist(d[[1]]), 1, paste, collapse="-") e2 <- apply(get.edgelist(d[[2]]), 1, paste, collapse="-") expect_that(E(d[[1]])$name, equals(e1)) expect_that(E(d[[2]])$name, equals(e2)) }) igraph/inst/tests/test_layout.mds.R0000644000176000001440000000142312251656216017162 0ustar ripleyusers context("layout.mds") test_that("layout.mds works", { library(igraph) ## A tree g <- graph.tree(10, 2, "undirected") mymds <- function(g) { sp <- shortest.paths(g) sp <- sp * sp sp <- sp - rowMeans(sp) - rep(rowMeans(sp), each=nrow(sp)) + mean(sp) sp <- sp / -2 ei <- eigen(sp) va <- sqrt(abs(ei$values[1:2])) ei$vectors[,1:2] * rep(va, each=nrow(sp)) } expect_that(mymds(g), equals(layout.mds(g))) ## plot(g, layout=ll) ## A graph with multiple components, just test that it runs set.seed(42) g <- graph.ring(10) + graph.ring(3) expect_that(ncol(layout.mds(g)), equals(2)) ## Small stress test for (i in 1:10) { g <- erdos.renyi.game(100, 2/100) l <- layout.mds(g) expect_that(ncol(l), equals(2)) } }) igraph/inst/tests/test_arpack.R0000644000176000001440000000540212251656216016325 0ustar ripleyusers context("arpack") test_that("arpack works for identity matrix", { library(igraph) f <- function(x, extra=NULL) x res <- arpack(f, options=list(n=10, nev=2, ncv=4), sym=TRUE) expect_that(res$values, equals(c(1,1))) }) test_that("arpack works on the Laplacian of a star", { library(igraph) f <- function(x, extra=NULL) { y <- x y[1] <- (length(x)-1)*x[1] - sum(x[-1]) for (i in 2:length(x)) { y[i] <- x[i] - x[1] } y } r1 <- arpack(f, options=list(n=10, nev=1, ncv=3), sym=TRUE) r2 <- eigen(graph.laplacian(graph.star(10, mode="undirected"))) correctSign <- function(x) { if (x[1]<0) { -x } else { x } } expect_that(r1$values, equals(r2$values[1])) expect_that(correctSign(r1$vectors), equals(correctSign(r2$vectors[,1]))) }) #### # Complex case test_that("arpack works for non-symmetric matrices", { library(igraph) A <- structure(c(-6, -6, 7, 6, 1, -9, -3, 2, -9, -7, 0, 1, -7, 8, -7, 10, 0, 0, 1, 1, 10, 0, 8, -4, -4, -5, 8, 9, -6, 9, 3, 8, 6, -1, 9, -9, -6, -3, -1, -7, 8, -4, -4, 10, 0, 5, -2, 0, 7, 10, 1, 4, -8, 3, 5, 3, -7, -9, 10, -1, -4, -7, -1, 7, 5, -5, 1, -4, 9, -2, 10, 1, -7, 7, 6, 7, -3, 0, 9, -5, -8, 1, -3, -3, -8, -7, -8, 10, 8, 7, 0, 6, -7, -8, 10, 10, 1, 0, -2, 6), .Dim = c(10L, 10L)) f <- function(x, extra=NULL) A %*% x res <- arpack(f, options=list(n=10, nev=3, ncv=7), sym=FALSE) ## This is needed because they might return a different complex conjugate expect_that(abs(res$values/eigen(A)$values[1:3]), equals(c(1,1,1))) expect_that((res$values[1] * res$vectors[,1]) / (A %*% res$vectors[,1]), equals(cbind(rep(1+0i, nrow(A))))) expect_that((res$values[2] * res$vectors[,2]) / (A %*% res$vectors[,2]), equals(cbind(rep(1+0i, nrow(A))))) expect_that(abs((res$values[3] * res$vectors[,3]) / (A %*% res$vectors[,3])), equals(cbind(rep(1, nrow(A))))) f <- function(x, extra=NULL) A %*% x res <- arpack(f, options=list(n=10, nev=4, ncv=9), sym=FALSE) ## This is needed because they might return a different complex conjugate expect_that(abs(res$values/eigen(A)$values[1:4]), equals(rep(1, 4))) expect_that((res$values[1] * res$vectors[,1]) / (A %*% res$vectors[,1]), equals(cbind(rep(1+0i, nrow(A))))) expect_that((res$values[2] * res$vectors[,2]) / (A %*% res$vectors[,2]), equals(cbind(rep(1+0i, nrow(A))))) expect_that(abs((res$values[3] * res$vectors[,3]) / (A %*% res$vectors[,3])), equals(cbind(rep(1, nrow(A))))) expect_that(abs((res$values[4] * res$vectors[,4]) / (A %*% res$vectors[,4])), equals(cbind(rep(1, nrow(A))))) }) #### # TODO: further tests for typically hard cases igraph/inst/tests/test_igraph.options.R0000644000176000001440000000037712251656216020036 0ustar ripleyusers context("igraph.options") test_that("igraph.options works", { library(igraph) igraph.options(verbose=TRUE) expect_that(getIgraphOpt("verbose"), is_true()) igraph.options(verbose=FALSE) expect_that(getIgraphOpt("verbose"), is_false()) }) igraph/inst/tests/dyad.census.R0000644000176000001440000000122612251656216016245 0ustar ripleyusers context("dyad.census") test_that("dyad.census works", { library(igraph) g1 <- graph.ring(10) expect_that(dc1 <- dyad.census(g1), gives_warning("undirected")) expect_that(dc1, equals(list(mut=10, asym=0, null=35))) g2 <- graph.ring(10, directed=TRUE, mutual=TRUE) dc2 <- dyad.census(g2) expect_that(dc2, equals(list(mut=10, asym=0, null=35))) g3 <- graph.ring(10, directed=TRUE, mutual=FALSE) dc3 <- dyad.census(g3) expect_that(dc3, equals(list(mut=0, asym=10, null=35))) g4 <- graph.empty(2000000) expect_that(dc4 <- dyad.census(g4), gives_warning("Integer overflow")) expect_that(dc4, equals(list(mut=0, asym=0, null=0))) }) igraph/inst/tests/test_edge.betweenness.community.R0000644000176000001440000000201012251656216022324 0ustar ripleyusers context("edge.betweenness.community") test_that("edge.betweenness.community works", { library(igraph) g <- graph.famous("Zachary") ebc <- edge.betweenness.community(g) expect_that(max(ebc$modularity), equals(modularity(g, ebc$membership))) expect_that(membership(ebc), equals(c(1, 1, 2, 1, 3, 3, 3, 1, 4, 5, 3, 1, 1, 1, 4, 4, 3, 1, 4, 1, 4, 1, 4, 4, 2, 2, 4, 2, 2, 4, 4, 2, 4, 4))) expect_that(length(ebc), equals(5)) expect_that(as.numeric(sizes(ebc)), equals(c(10, 6, 5, 12, 1))) d <- as.dendrogram(ebc) expect_that(print(d), prints_text("2 branches.*34 members.*height 33")) expect_that(print(d[[1]]), prints_text("2 branches.*15 members.*height 31")) expect_that(print(d[[2]]), prints_text("2 branches.*19 members.*height 32")) m2 <- cutat(ebc, no=3) expect_that(modularity(g, m2), equals(ebc$modularity[length(ebc$modularity)-2])) }) igraph/inst/tests/test_bonpow.R0000644000176000001440000000543612251656216016377 0ustar ripleyusers context("Bonacich's power centrality") test_that("Power centrality works", { library(igraph) ## Generate some test data from Bonacich, 1987: fig1 <- graph.formula( A -+ B -+ C:D ) fig1.bp <- lapply(seq(0, 0.8, by=0.2), function(x) round(bonpow(fig1, exponent=x), 2)) expect_that(fig1.bp, equals(list(c(A=0.89, B=1.79, C=0, D=0), c(A=1.15, B=1.64, C=0, D=0), c(A=1.34, B=1.49, C=0, D=0), c(A=1.48, B=1.35, C=0, D=0), c(A=1.59, B=1.22, C=0, D=0)))) g.c <- graph( c(1,2,1,3,2,4,3,5), dir=FALSE) bp.c <- lapply(seq(-.5, .5, by=0.1), function(x) round(bonpow(g.c, exponent=x), 2)[c(1,2,4)]) expect_that(bp.c, equals(list(c(0.00, 1.58, 0.00), c(0.73, 1.45, 0.36), c(0.97, 1.34, 0.49), c(1.09, 1.27, 0.54), c(1.15, 1.23, 0.58), c(1.20, 1.20, 0.60), c(1.22, 1.17, 0.61), c(1.25, 1.16, 0.62), c(1.26, 1.14, 0.63), c(1.27, 1.13, 0.64), c(1.28, 1.12, 0.64)))) g.d <- graph( c(1,2,1,3,1,4,2,5,3,6,4,7), dir=FALSE) bp.d <- lapply(seq(-.4, .4, by=0.1), function(x) round(bonpow(g.d, exponent=x), 2)[c(1,2,5)]) expect_that(bp.d, equals(list(c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54), c(1.62, 1.08, 0.54)))) g.e <- graph( c(1,2,1,3,1,4,2,5,2,6,3,7,3,8,4,9,4,10), dir=FALSE) bp.e <- lapply(seq(-.4, .4, by=0.1), function(x) round(bonpow(g.e, exponent=x), 2)[c(1,2,5)]) expect_that(bp.e, equals(list(c(-1.00, 1.67, -0.33), c(0.36, 1.81, 0.12), c( 1.00, 1.67, 0.33), c(1.30, 1.55, 0.43), c( 1.46, 1.46, 0.49), c(1.57, 1.40, 0.52), c( 1.63, 1.36, 0.54), c(1.68, 1.33, 0.56), c( 1.72, 1.30, 0.57)))) g.f <- graph( c(1,2,1,3,1,4,2,5,2,6,2,7,3,8,3,9,3,10,4,11,4,12,4,13), dir=FALSE) bp.f <- lapply(seq(-.4, .4, by=0.1), function(x) round(bonpow(g.f, exponent=x), 2)[c(1,2,5)]) expect_that(bp.f, equals(list(c(-1.72, 1.53, -0.57), c(-0.55, 2.03, -0.18), c( 0.44, 2.05, 0.15), c( 1.01, 1.91, 0.34), c( 1.33, 1.78, 0.44), c( 1.52, 1.67, 0.51), c( 1.65, 1.59, 0.55), c( 1.74, 1.53, 0.58), c( 1.80, 1.48, 0.60)))) }) igraph/inst/tests/test_graphlets.R0000644000176000001440000001365012251656216017061 0ustar ripleyusers context("Graphlets") test_that("Getting subcliques works", { library(igraph) set.seed(42*42) g <- erdos.renyi.game(10, 4/10) E(g)$weight <- as.double(sample(1:10, ecount(g), replace=TRUE)) ids <- 1:vcount(g) cl <- maximal.cliques(g) cl <- lapply(cl, "-", 1)[c(9, 2, 3, 10, 5, 7, 6, 1, 4, 8)] res <- .Call("R_igraph_subclique_next", g, E(g)$weight, ids, cl, PACKAGE="igraph") for (i in seq_along(res$graphs)) { V(res$graphs[[i]])$name <- res$ids[[i]] E(res$graphs[[i]])$weight <- res$weights[[i]] } expect_that(res$thr, equals(c(7,2,5,3,1,7,3,2,4,7))) expect_that(res$next_thr, equals(c(Inf, 4, 8, 8, Inf, 9, 5, 3, 5, Inf))) expect_that(res$weights, equals(list(numeric(), c(4,8), c(8,8), c(9,8), numeric(), c(9,9), c(7,7,9,10,5), c(7,3,4,5), c(5,7,5,9,10), numeric()))) expect_that(res$ids, equals(list(integer(), c(5,9,10), c(1,9,10), c(1,8,9), integer(), c(3,7,6), c(3,6,5,4), c(3,5,10,2), c(2,5,3,4), integer()))) expect_that(sapply(res$graphs, vcount), equals(sapply(res$ids, length))) expect_that(sapply(res$graphs, ecount), equals(sapply(res$weights, length))) }) sortgl <- function(x) { cl <- lapply(x$cliques, sort) n <- sapply(cl, length) list(cliques=cl[order(n)], thresholds=x$thresholds[order(n)]) } test_that("Graphlets work for some simple graphs", { library(igraph) g <- graph.full(5) E(g)$weight <- 1 gl <- graphlets.candidate.basis(g) expect_that(names(gl), equals(c("cliques", "thresholds"))) expect_that(length(gl$cliques), equals(1)) expect_that(sort(gl$cliques[[1]]), equals(1:vcount(g))) expect_that(gl$thresholds, equals(1)) g2 <- graph.full(5) E(g2)$weight <- 1 E(g2)[1%--%2]$weight <- 2 gl2 <- sortgl(graphlets.candidate.basis(g2)) expect_that(gl2, equals(list(cliques=list(1:2, 1:5), thresholds=c(2,1)))) }) test_that("Graphlets filtering works", { library(igraph) gt <- data.frame(from =c("A", "A", "B", "B", "B", "C", "C", "D"), to =c("B", "C", "C", "D", "E", "D", "E", "E"), weight=c( 8 , 8 , 8 , 5 , 5 , 5 , 5 , 5 )) g <- graph.data.frame(gt, directed=FALSE, vertices=data.frame(LETTERS[1:5])) gl <- sortgl(graphlets.candidate.basis(g)) expect_that(gl$cliques, equals(list(1:3, 2:5))) expect_that(gl$thresholds, equals(c(8, 5))) }) ## Naive version of graphlets threshold.net <- function(graph, level) { N <- vcount(graph) graph.t <- delete.edges(graph, which(E(graph)$weight < level)) clqt <- maximal.cliques(graph.t) clqt <- lapply(clqt, sort) clqt[order(sapply(clqt, length), decreasing=TRUE)] } graphlets.old <- function(graph) { if (!is.weighted(graph)) { stop("Graph not weighted") } if (min(E(graph)$weight) <= 0 || !is.finite(E(graph)$weight)) { stop("Edge weights must be non-negative and finite") } ## Do all thresholds cl <- lapply(sort(unique(E(graph)$weight)), function(w) { threshold.net(graph, w) }) ## Put the cliques in one long list clv <- unlist(cl, recursive=FALSE) ## Sort the vertices within the cliques cls <- lapply(clv, sort) ## Delete duplicate cliques clu <- unique(cls) ## Delete cliques that consist of single vertices clf <- clu[sapply(clu, length) != 1] clf } test_that("Graphlets work for a bigger graph", { library(igraph) set.seed(42) g <- graph.famous("zachary") E(g)$weight <- sample(1:5, ecount(g), replace=TRUE) gl <- graphlets.candidate.basis(g) gl2 <- graphlets.old(g) glo <- sort(sapply(gl$cliques, paste, collapse="-")) gl2o <- sort(sapply(gl2, paste, collapse="-")) expect_that(glo, equals(gl2o)) }) graphlets.project.old <- function(graph, cliques, iter, Mu=NULL) { if (!is.weighted(graph)) { stop("Graph not weighted") } if (min(E(graph)$weight) <= 0 || !is.finite(E(graph)$weight)) { stop("Edge weights must be non-negative and finite") } if (length(iter) != 1 || !is.numeric(iter) || !is.finite(iter) || iter != as.integer(iter)) { stop("`iter' must be a non-negative finite integer scalar") } clf <- cliques ## Create vertex-clique list first vcl <- vector(length=vcount(graph), mode="list") for (i in 1:length(clf)) { for (j in clf[[i]]) { vcl[[j]] <- c(vcl[[j]], i) } } ## Create edge-clique list from this, it is useful to have the edge list ## of the graph at hand el <- get.edgelist(graph, names=FALSE) ecl <- vector(length=ecount(graph), mode="list") for (i in 1:ecount(graph)) { edge <- el[i,] ecl[[i]] <- intersect(vcl[[edge[1]]], vcl[[edge[2]]]) } ## We will also need a clique-edge list, the edges in the cliques system.time({ cel <- vector(length=length(clf), mode="list") for (i in 1:length(ecl)) { for (j in ecl[[i]]) { cel[[j]] <- c(cel[[j]], i) } } }) ## OK, we are ready to do the projection now if (is.null(Mu)) { Mu <- rep(1, length(clf)) } origw <- E(graph)$weight w <- numeric(length(ecl)) a <- sapply(clf, function(x) length(x) * (length(x) + 1) / 2) for (i in 1:iter) { for (j in 1:length(ecl)) { w[j] <- sum(Mu[ ecl[[j]] ]) } for (j in 1:length(clf)) { Mu[j] <- Mu[j] * sum(origw[cel[[j]]] / (w[cel[[j]]] + .0001)) / a[j] } } ## Sort the cliques according to their weights Smb <- sort(Mu, decreasing=TRUE, index=TRUE) list(cliques=clf[Smb$ix], Mu=Mu[Smb$ix]) } test_that("Graphlet projection works", { library(igraph) D1 <- matrix(0, 5, 5) D2 <- matrix(0, 5, 5) D3 <- matrix(0, 5, 5) D1[1:3, 1:3] <- 2 D2[3:5, 3:5] <- 3 D3[2:5, 2:5] <- 1 g <- graph.adjacency(D1 + D2 + D3, mode="undirected", weighted=TRUE) g <- simplify(g) gl <- graphlets.candidate.basis(g) glp <- graphlets(g) glp2 <- graphlets.project.old(g, cliques=gl$cliques, iter=1000) expect_that(glp, equals(glp2)) }) igraph/inst/tests/test_graph.subisomorphic.vf2.R0000644000176000001440000000113712251656216021547 0ustar ripleyusers context("graph.subisomorphic.vf2") test_that("graph.subisomorphic.vf2 works", { library(igraph) set.seed(42) g1 <- erdos.renyi.game(20,6/20) g2 <- erdos.renyi.game(20,6/20) g <- g1 %du% g2 ig1 <- graph.subisomorphic.vf2(g, g1) ig2 <- graph.subisomorphic.vf2(g, g2) expect_that(ig1$iso, is_true()) expect_that(ig1$map12, equals(c(1:vcount(g1), rep(0, vcount(g2))))) expect_that(ig1$map21, equals(1:vcount(g1))) expect_that(ig2$iso, is_true()) expect_that(ig2$map12, equals(c(rep(0, vcount(g1)), 1:vcount(g2)))) expect_that(ig2$map21, equals(1:vcount(g2) + vcount(g1))) }) igraph/inst/tests/test_largest.cliques.R0000644000176000001440000000052512251656216020172 0ustar ripleyusers context("largest.cliques") test_that("largest.cliques works", { library(igraph) g <- erdos.renyi.game(50,20/50) lc <- largest.cliques(g) ## TODO: this only checks that these are cliques expect_that(unique(sapply(lc, function(x) graph.density(induced.subgraph(g, x)))), equals(1)) }) igraph/inst/tests/test_average.path.length.R0000644000176000001440000000165012251656216020712 0ustar ripleyusers context("average.path.length") test_that("average.path.length works", { library(igraph) apl <- function(graph) { sp <- shortest.paths(graph, mode="out") if (is.directed(graph)) { diag(sp) <- NA } else { sp[lower.tri(sp, diag=TRUE)] <- NA } sp[sp=="Inf"] <- NA mean(sp, na.rm=TRUE) } giant.component <- function(graph, mode="weak") { clu <- clusters(graph, mode=mode) induced.subgraph(graph, which(clu$membership==which.max(clu$csize))) } g <- giant.component(erdos.renyi.game(100, 3/100)) expect_that(apl(g), equals(average.path.length(g))) g <- giant.component(erdos.renyi.game(100, 6/100, dir=TRUE), mode="strong") expect_that(apl(g), equals(average.path.length(g))) g <- erdos.renyi.game(100, 2/100) expect_that(apl(g), equals(average.path.length(g))) g <- erdos.renyi.game(100, 4/100, dir=TRUE) expect_that(apl(g), equals(average.path.length(g))) }) igraph/inst/tests/test_is.bipartite.R0000644000176000001440000000067112251656216017464 0ustar ripleyusers context("is.bipartite") test_that("is.bipartite works", { library(igraph) I <- matrix(sample(0:1, 35, replace=TRUE, prob=c(3,1)), nc=5) g <- graph.incidence(I) expect_that(bipartite.mapping(g)$res, is_true()) set.seed(42) I <- matrix(sample(0:1, 35, replace=TRUE, prob=c(3,1)), nc=5) g <- graph.incidence(I) expect_that(bipartite.mapping(g), equals(list(res=TRUE, type=c(rep(FALSE, 7), rep(TRUE, 5))))) }) igraph/inst/tests/test_iterators.R0000644000176000001440000000221512251656216017077 0ustar ripleyusers context("iterators") test_that("iterators work", { library(igraph) ## Create a small ring graph, assign attributes ring <- graph.formula( A-B-C-D-E-F-G-A ) E(ring)$weight <- seq_len(ecount(ring)) ## Selection based on attributes expect_that(sort(E(ring)[ weight < 4 ]$weight), equals(1:3)) expect_that(V(ring)[ c("A", "C") ]$name, equals(c("A", "C"))) ## TODO: %--%, %->%, other special functions }) test_that("complex attributes work", { library(igraph) g <- graph.ring(10) foo <- lapply(1:vcount(g), seq, from=1) V(g)$foo <- foo V(g)$foo[[5]][1:3] <- 0 expect_that(V(g)[(1:10)[-5]]$foo, equals(foo[-5])) expect_that(V(g)[[5]]$foo, equals(c(0,0,0,4,5))) expect_that(V(g)[5]$foo, equals(list(c(0,0,0,4,5)))) V(g)$foo <- foo V(g)[[5]]$foo[1:3] <- 0 expect_that(V(g)[(1:10)[-5]]$foo, equals(foo[-5])) expect_that(V(g)[[5]]$foo, equals(c(0,0,0,4,5))) expect_that(V(g)[5]$foo, equals(list(c(0,0,0,4,5)))) V(g)$foo <- foo V(g)[5]$foo[[1]][1:3] <- 0 expect_that(V(g)[(1:10)[-5]]$foo, equals(foo[-5])) expect_that(V(g)[[5]]$foo, equals(c(0,0,0,4,5))) expect_that(V(g)[5]$foo, equals(list(c(0,0,0,4,5)))) }) igraph/inst/tests/test_graph.atlas.R0000644000176000001440000000065012251656216017270 0ustar ripleyusers context("graph.atlas") test_that("graph.atlas works", { library(igraph) g124 <- graph.atlas(124) expect_that(graph.isomorphic(g124, graph(c(1,2,2,3,3,4,4,5,1,5,1,3,2,6), directed=FALSE)), is_true()) g234 <- graph.atlas(234) expect_that(graph.isomorphic(g234, graph(c(1,6,2,6,3,6,4,6,5,6), n=7, directed=FALSE)), is_true()) }) igraph/inst/tests/test_bug-1019624.R0000644000176000001440000000041712251656216016466 0ustar ripleyusers context("Bug 1019624") test_that("weighted graph.adjacency works on integer matrices", { library(igraph) data <- matrix(c(0,0,0,2, 0,0,0,0, 0,0,0,2, 0,1,0,0), 4) g <- graph.adjacency(data, weighted=TRUE) expect_that(as.matrix(g[]), is_equivalent_to(data)) }) igraph/inst/tests/test_graph.data.frame.R0000644000176000001440000000236512257563227020200 0ustar ripleyusers context("graph.data.frame") test_that("graph.data.frame works", { library(igraph) ; igraph.options(print.full=TRUE) actors <- data.frame(name=c("Alice", "Bob", "Cecil", "David", "Esmeralda"), age=c(48,33,45,34,21), gender=c("F","M","F","M","F"), stringsAsFactors=FALSE) relations <- data.frame(from=c("Bob", "Cecil", "Cecil", "David", "David", "Esmeralda"), to=c("Alice", "Bob", "Alice", "Alice", "Bob", "Alice"), same.dept=c(FALSE,FALSE,TRUE,FALSE,FALSE,TRUE), friendship=c(4,5,5,2,1,1), advice=c(4,5,5,4,2,3), stringsAsFactors=FALSE) g <- graph.data.frame(relations, directed=TRUE, vertices=actors) df <- get.data.frame(g, what="both") expect_that(df$vertices, is_equivalent_to(actors)) expect_that(df$edges, equals(relations)) }) test_that("graph.data.frame works on matrices", { library(igraph) el <- cbind(1:5,5:1,weight=1:5) g <- graph.data.frame(el) g <- remove.vertex.attribute(g, "name") el2 <- get.data.frame(g) expect_that(as.data.frame(el), is_equivalent_to(el2)) }) igraph/inst/tests/test_minimal.st.separators.R0000644000176000001440000000043712251656216021324 0ustar ripleyusers context("minimal.st.separators") test_that("minimal.st.separators works", { library(igraph) g <- graph.famous("Zachary") msts <- minimal.st.separators(g) is <- sapply(msts, is.separator, graph=g) expect_that(unique(is), equals(TRUE)) ## TODO: check that it is minimal }) igraph/inst/tests/test_evcent.R0000644000176000001440000000305612251656216016353 0ustar ripleyusers context("evcent") test_that("evcent works", { library(igraph) kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) evc <- round(evcent(kite)$vector, 3) expect_that(evc, equals(structure(c(0.732, 0.732, 0.594, 1, 0.827, 0.594, 0.827, 0.407, 0.1, 0.023), .Names = c("Andre", "Beverly", "Carol", "Diane", "Fernando", "Ed", "Garth", "Heather", "Ike", "Jane")))) ## Eigenvector-centrality, small stress-test is.principal <- function(M, lambda, eps=1e-12) { abs(eigen(M)$values[1] - lambda) < eps } is.ev <- function(M, v, lambda, eps=1e-12) { max(abs(M %*% v - lambda * v)) < eps } is.good <- function(M, v, lambda, eps=1e-12) { is.principal(M, lambda, eps) && is.ev(M, v, lambda, eps) } for (i in 1:1000) { G <- erdos.renyi.game(10, sample(1:20, 1), type="gnm") ev <- evcent(G) expect_that(is.good(get.adjacency(G, sparse=FALSE), ev$vector, ev$value), is_true()) } }) igraph/inst/tests/test_delete.vertices.R0000644000176000001440000000041512251656216020150 0ustar ripleyusers context("delete.vertices") test_that("delete.vertices works", { library(igraph) g <- graph.formula(A:B:C - D:E:F, D-E-F) g2 <- delete.vertices(g, "A") g3 <- delete.vertices(g, match("A", V(g)$name)) expect_that(graph.isomorphic(g2, g3), is_true()) }) igraph/inst/tests/test_optimal.community.R0000644000176000001440000000165512271600260020550 0ustar ripleyusers context("optimal.community") test_that("optimal.community works", { library(igraph) g <- graph.famous("Zachary") oc <- optimal.community(g) expect_that(membership(oc), equals(c(1, 1, 1, 1, 2, 2, 2, 1, 3, 3, 2, 1, 1, 1, 3, 3, 2, 1, 3, 1, 3, 1, 3, 4, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3) )) expect_that(modularity(g, oc$membership), equals(oc$modularity)) expect_that(length(oc), equals(4)) expect_that(sizes(oc), equals(structure(c(11L, 5L, 12L, 6L), .Dim=4L, .Dimnames=structure(list(`Community sizes`=c("1", "2", "3", "4")), .Names="Community sizes"), class="table") )) }) test_that("weighted optimal.community works", { library(igraph) set.seed(42) g <- graph.full(5) + graph.ring(5) E(g)$weight <- sample(1:2, ecount(g), replace=TRUE) oc <- optimal.community(g) expect_that(modularity(oc), equals(0.4032)) }) igraph/inst/tests/test_assortativity.R0000644000176000001440000000324012251656216020007 0ustar ripleyusers context("assortativity") test_that("assortativity works", { library(igraph) g <- read.graph(f <- gzfile("celegansneural.gml.gz"), format="gml") assR <- function(graph) { indeg <- degree(graph, mode="in") outdeg <- degree(graph, mode="out") el <- get.edgelist(graph, names=FALSE) J <- outdeg[el[,1]]-1 K <- indeg[el[,2]]-1 num <- sum(J*K) - sum(J)*sum(K)/ecount(graph) den1 <- sum(J*J) - sum(J)^2/ecount(graph) den2 <- sum(K*K) - sum(K)^2/ecount(graph) num / sqrt(den1) / sqrt(den2) } asd <- assortativity.degree(g) as <- assortativity(g, degree(g, mode="out"), degree(g, mode="in")) as2 <- assR(g) expect_that(asd, equals(as)) expect_that(asd, equals(as2)) asu <- assortativity.degree(simplify(as.undirected(g, mode="collapse"))) expect_that(asu, equals(-0.16319921031570466807)) p <- read.graph(f <- gzfile("power.gml.gz"), format="gml") p.asd <- assortativity.degree(p) p.as <- assortativity(p, degree(p)) p.as2 <- assR(as.directed(p, mode="mutual")) expect_that(p.asd, equals(p.as)) expect_that(p.asd, equals(p.as2)) }) test_that("nominal assortativity works", { library(igraph) o <- read.graph(f <- gzfile("football.gml.gz"), format="gml") o <- simplify(o) an <- assortativity.nominal(o, V(o)$value+1) el <- get.edgelist(o, names=FALSE) etm <- matrix(0, nr=max(V(o)$value)+1, nc=max(V(o)$value)+1) for (e in 1:nrow(el)) { t1 <- V(o)$value[ el[e,1] ]+1 t2 <- V(o)$value[ el[e,2] ]+1 etm[t1, t2] <- etm[t1, t2] + 1 etm[t2, t1] <- etm[t2, t1] + 1 } etm <- etm/sum(etm) an2 <- ( sum(diag(etm))-sum(etm %*% etm) ) / ( 1-sum(etm %*% etm) ) expect_that(an, equals(an2)) }) igraph/inst/tests/test_graph.de.bruijn.R0000644000176000001440000000054412251656216020046 0ustar ripleyusers context("graph.de.bruijn") test_that("graph.de.bruijn works", { library(igraph) g <- graph.de.bruijn(2,1) g2 <- graph.de.bruijn(2,2) g3 <- line.graph(g) expect_that(graph.isomorphic(g3, graph(c(1,1,3,1,1,2,3,2,2,3, 4,3,2,4,4,4))), is_true()) expect_that(graph.isomorphic(g2, g3), is_true()) }) igraph/inst/tests/test_neighbors.R0000644000176000001440000000040512251656216017042 0ustar ripleyusers context("neighbors") test_that("neighbors works", { library(igraph) g <- erdos.renyi.game(100, 20/100) al <- get.adjlist(g, mode="all") for (i in 1:length(al)) { n <- neighbors(g, i, mode="out") expect_that(sort(n), equals(al[[i]])) } }) igraph/inst/tests/test_authority.score.R0000644000176000001440000000320512251656216020225 0ustar ripleyusers context("authority.score") test_that("authority score works", { library(igraph) ashs <- function(graph, as=TRUE) { mscale <- function(x) { if (sd(x)!=0) { x <- scale(x) } if (x[1] < 0) { x <- -x } x } A <- get.adjacency(graph, sparse=FALSE) if (as) { s1 <- eigen(t(A) %*% A)$vectors[,1] s2 <- authority.score(graph)$vector } else { s1 <- eigen(A %*% t(A))$vectors[,1] s2 <- hub.score(graph)$vector } expect_that(mscale(s1), is_equivalent_to(mscale(s2))) } g1 <- ba.game(100, m=10) ashs(g1) ashs(g1, as=FALSE) g2 <- erdos.renyi.game(100, 2/100) ashs(g2) ashs(g2, as=FALSE) }) test_that("authority scores of a ring are all one", { library(igraph) g3 <- graph.ring(100) expect_that(authority.score(g3)$vector, equals(rep(1, vcount(g3)))) expect_that(hub.score(g3)$vector, equals(rep(1, vcount(g3)))) }) test_that("authority.score survives stress test", { library(igraph) library(Matrix) set.seed(42) is.principal <- function(M, lambda) { expect_that(eigen(M)$values[1], equals(lambda)) } is.ev <- function(M, v, lambda) { expect_that(as.vector(M %*% v), equals(lambda * v)) } is.good <- function(M, v, lambda) { is.principal(M, lambda) is.ev(M, v, lambda) } for (i in 1:100) { G <- erdos.renyi.game(10, sample(1:20, 1), type="gnm") as <- authority.score(G) M <- get.adjacency(G) is.good(t(M) %*% M, as$vector, as$value) } for (i in 1:100) { G <- erdos.renyi.game(10, sample(1:20, 1), type="gnm") hs <- hub.score(G) M <- get.adjacency(G) is.good(M %*% t(M), hs$vector, hs$value) } }) igraph/inst/tests/test_edgenames.R0000644000176000001440000000264412251656216017021 0ustar ripleyusers context("edge names") test_that("edge names work", { library(igraph) ## named edges igraph.options(print.edge.attributes = TRUE) g <- graph.ring(10) E(g)$name <- letters[1:ecount(g)] g2 <- delete.edges(g, c("b", "d", "e")) expect_that(get.edgelist(g2), equals(structure(c(1, 3, 6, 7, 8, 9, 1, 2, 4, 7, 8, 9, 10, 10), .Dim = c(7L, 2L)))) ## named vertices g <- graph.ring(10) V(g)$name <- letters[1:vcount(g)] g3 <- delete.edges(g, c("a|b", "f|g", "c|b")) expect_that(get.edgelist(g3), equals(structure(c("c", "d", "e", "g", "h", "i", "a", "d", "e", "f", "h", "i", "j", "j"), .Dim = c(7L, 2L)))) ## no names at all, but select edges based on vertices g <- graph.ring(10) g4 <- delete.edges(g, c("1|2", "8|7", "1|10")) expect_that(get.edgelist(g4), equals(structure(c(2, 3, 4, 5, 6, 8, 9, 3, 4, 5, 6, 7, 9, 10), .Dim = c(7L, 2L)))) ## mix edge names and vertex names g <- graph.ring(10) V(g)$name <- letters[1:vcount(g)] E(g)$name <- LETTERS[1:ecount(g)] g5 <- delete.edges(g, c("a|b", "F", "j|i")) expect_that(get.edgelist(g5), equals(structure(c("b", "c", "d", "e", "g", "h", "a", "c", "d", "e", "f", "h", "i", "j"), .Dim = c(7L, 2L)))) }) igraph/inst/tests/test_bug-1073705-indexing.R0000644000176000001440000000122112251656216020263 0ustar ripleyusers context("Bug 1073705") test_that("Weighted indexing does not remove edges", { library(igraph) g <- graph.ring(10) g[1, 2, attr="weight"] <- 0 expect_that("weight" %in% list.edge.attributes(g), is_true()) expect_that(E(g)$weight, equals(c(0, rep(NA, 9)))) el <- get.edgelist(g) g[from=el[,1], to=el[,2], attr="sim"] <- rep(0:1, length=ecount(g)) expect_that("sim" %in% list.edge.attributes(g), is_true()) expect_that(E(g)$sim, equals(rep(0:1, 5))) V(g)$name <- letters[seq_len(vcount(g))] el <- get.edgelist(g) g[from=el[,1], to=el[,2], attr="sim"] <- rep(1:0, length=ecount(g)) expect_that(E(g)$sim, equals(rep(1:0, 5))) }) igraph/inst/tests/test_pajek.R0000644000176000001440000000116512251656216016160 0ustar ripleyusers context("Pajek file format") test_that("writing Pajek files works", { library(igraph) g <- graph.ring(9) V(g)$color <- c("red", "green", "yellow") tc <- rawConnection(raw(0), "w") write.graph(g, format="pajek", file=tc) out <- rawToChar(rawConnectionValue(tc)) close(tc) expect_that(out, equals("*Vertices 9\r\n1 \"1\" ic \"red\"\r\n2 \"2\" ic \"green\"\r\n3 \"3\" ic \"yellow\"\r\n4 \"4\" ic \"red\"\r\n5 \"5\" ic \"green\"\r\n6 \"6\" ic \"yellow\"\r\n7 \"7\" ic \"red\"\r\n8 \"8\" ic \"green\"\r\n9 \"9\" ic \"yellow\"\r\n*Edges\r\n1 2\r\n2 3\r\n3 4\r\n4 5\r\n5 6\r\n6 7\r\n7 8\r\n8 9\r\n1 9\r\n")) }) igraph/inst/tests/test_independent.vertex.sets.R0000644000176000001440000000052412251656216021652 0ustar ripleyusers context("independent.vertex.sets") test_that("independent.vetex.sets works", { library(igraph) g <- erdos.renyi.game(50, 0.8) ivs <- independent.vertex.sets(g, min=independence.number(g)) ec <- sapply(seq_along(ivs), function(x) ecount(induced.subgraph(g, ivs[[x]]))) expect_that(unique(ec), equals(0)) }) igraph/inst/tests/test_bug-1033045.R0000644000176000001440000000043412251656216016456 0ustar ripleyusers context("Bug 1033045") test_that("Minimal s-t separators work", { library(igraph) g <- graph.formula(a -- 1:3 -- 5 -- 2:4 -- b, 1 -- 2, 3 -- 4) stsep <- minimal.st.separators(g) ims <- sapply(stsep, is.minimal.separator, graph=g) expect_that(ims, equals(rep(TRUE, 9))) }) igraph/inst/tests/test_graph.maxflow.R0000644000176000001440000000100112271600260017616 0ustar ripleyusers context("graph.maxflow") test_that("graph.maxflow works", { library(igraph) E <- rbind( c(1,3,3), c(3,4,1), c(4,2,2), c(1,5,1), c(5,6,2), c(6,2,10)) colnames(E) <- c("from", "to", "capacity") g1 <- graph.data.frame(as.data.frame(E)) fl <- graph.maxflow(g1, source="1", target="2") expect_that(fl$value, equals(2)) expect_that(fl$flow, equals(rep(1, 6))) expect_that(sort(fl$cut), equals(c(2,4))) expect_that(sort(fl$partition1), equals(1:2)) expect_that(sort(fl$partition2), equals(3:6)) }) igraph/inst/tests/test_multilevel.community.R0000644000176000001440000000136212251656216021272 0ustar ripleyusers context("multilevel.community") test_that("multilevel.community works", { library(igraph) g <- graph.famous("Zachary") mc <- multilevel.community(g) expect_that(membership(mc), equals(c(2, 2, 2, 2, 1, 1, 1, 2, 4, 2, 1, 2, 2, 2, 4, 4, 1, 2, 4, 2, 4, 2, 4, 3, 3, 3, 4, 3, 3, 4, 4, 3, 4, 4) )) expect_that(modularity(g, mc$membership), equals(max(mc$modularity))) expect_that(length(mc), equals(4)) expect_that(sizes(mc), equals(structure(c(5L, 12L, 6L, 11L), .Dim = 4L, .Dimnames = structure(list(`Community sizes` = c("1", "2", "3", "4")), .Names = "Community sizes"), class = "table") )) }) igraph/inst/tests/test_operators3.R0000644000176000001440000000200612251656216017162 0ustar ripleyusers context("infix operators") test_that("infix operators work", { library(igraph) g <- graph.ring(10) V(g)$name <- letters[1:10] E(g)$name <- LETTERS[1:10] g <- g - c("a", "b") expect_that(vcount(g), equals(8)) expect_that(ecount(g), equals(7)) expect_that(graph.isomorphic(g, graph.lattice(8)), is_true()) g <- g - edge("e|f") expect_that(graph.isomorphic(g, graph.lattice(5) + graph.lattice(3)), is_true()) g <- g - edge("H") expect_that(graph.isomorphic(g, graph.formula(a-b-c, d-e-f, g-h)), is_true()) g <- graph.ring(10) V(g)$name <- letters[1:10] g <- g - path("a", "b", "c", "d") expect_that(graph.isomorphic(g, graph.lattice(8) + 2), is_true()) expect_that(graph.isomorphic(g - V(g)[c('d', 'g')], graph.lattice(4) + graph.lattice(2) + 2), is_true()) expect_that(graph.isomorphic(g - E(g)['f' %--% 'g'], graph.lattice(5) + graph.lattice(3) + 2), is_true()) }) igraph/inst/tests/test_scg.R0000644000176000001440000000341512251656216015642 0ustar ripleyusers context("scg") ## TODO: we only test that they run, not the results test_that("SCG functions work", { library(igraph) tree <- graph.tree(10, 3, "undirected") treeM <- get.adjacency(tree, sparse=TRUE) treeM2 <- get.adjacency(tree, sparse=FALSE) args <- list(ev=1, nt=3, mtype="symmetric", algo="exact_scg", semproj=TRUE, epairs=TRUE) do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) args[["ev"]] <- 3 do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) args[["ev"]] <- c(1,3) do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) ############################### args <- list(ev=1, nt=2, mtype="stochastic", algo="exact_scg", semproj=TRUE, epairs=TRUE, stat.prob=TRUE) do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) args[["ev"]] <- 3 do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) args[["ev"]] <- c(1,3) do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) ############################### args <- list(ev=1, nt=2, mtype="laplacian", algo="exact_scg", semproj=TRUE, epairs=TRUE) do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) args[["ev"]] <- 3 do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) args[["ev"]] <- c(1,3) do.call(scg, c(list(tree), args)) do.call(scg, c(list(treeM), args)) do.call(scg, c(list(treeM2), args)) }) igraph/inst/tests/test_as.directed.R0000644000176000001440000000212412251656216017247 0ustar ripleyusers context("as.directed") test_that("as.directed works", { library(igraph) g <- erdos.renyi.game(100, 2/100) g2 <- as.directed(g, mode="mutual") g3 <- as.directed(g, mode="arbitrary") expect_that(degree(g), equals(degree(g3))) expect_that(degree(g), equals(degree(g2) / 2)) expect_that(graph.isomorphic(g, as.undirected(g2)), is_true()) expect_that(graph.isomorphic(g, as.undirected(g3)), is_true()) }) test_that("as.directed keeps attributes", { library(igraph) g <- graph.formula( A-B-C, D-A, E ) g$name <- "Small graph" g2 <- as.directed(g, mode="mutual") g3 <- as.directed(g, mode="arbitrary") expect_that(g2$name, equals(g$name)) expect_that(V(g2)$name, equals(V(g)$name)) expect_that(g3$name, equals(g$name)) expect_that(V(g3)$name, equals(V(g)$name)) E(g)$weight <- seq_len(ecount(g)) g4 <- as.directed(g, "mutual") ; df4 <- get.data.frame(g4) g5 <- as.directed(g, "arbitrary") ; df5 <- get.data.frame(g5) expect_that(df4[order(df4[,1], df4[,2]),]$weight, equals(c(1,2,1,3,3,2))) expect_that(df5[order(df5[,1], df5[,2]),]$weight, equals(1:3)) }) igraph/inst/tests/test_closeness.R0000644000176000001440000000367212257605255017074 0ustar ripleyusers context("closeness") test_that("closeness works", { library(igraph) kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) clo <- closeness(kite) * (vcount(kite)-1) expect_that(round(sort(clo, decreasing=TRUE), 3), equals(c(Fernando=0.643, Garth=0.643, Diane=0.600, Heather=0.600, Andre=0.529, Beverly=0.529, Carol=0.500, Ed=0.500, Ike=0.429, Jane=0.310))) clo2 <- closeness(kite, normalized=TRUE) expect_that(clo, equals(clo2)) }) ## TODO: weighted closeness test_that("closeness centralization works", { library(igraph) kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) c1 <- closeness(kite, normalized=TRUE) c2 <- centralization.closeness(kite) expect_that(unname(c1), equals(c2$res)) expect_that(c2$centralization, equals(0.270374931581828)) expect_that(c2$theoretical_max, equals(4.23529411764706)) }) igraph/inst/tests/test_walktrap.community.R0000644000176000001440000000224312251656216020734 0ustar ripleyusers context("walktrap.community") test_that("walktrap.community works", { library(igraph) g <- graph.famous("Zachary") set.seed(42) wc <- walktrap.community(g) expect_that(modularity(g, membership(wc)), equals(modularity(wc))) expect_that(membership(wc), equals(c(1, 1, 2, 1, 5, 5, 5, 1, 2, 2, 5, 1, 1, 2, 3, 3, 5, 1, 3, 1, 3, 1, 3, 4, 4, 4, 3, 4, 2, 3, 2, 2, 3, 3))) expect_that(length(wc), equals(5)) expect_that(sizes(wc), equals(structure(c(9L, 7L, 9L, 4L, 5L), .Dim=5L, .Dimnames = structure(list(`Community sizes` = c("1", "2", "3", "4", "5")), .Names = "Community sizes"), class = "table"))) d <- as.dendrogram(wc) expect_that(print(d), prints_text("2 branches.*34 members.*height 33")) expect_that(print(d[[1]]), prints_text("2 branches.*20 members.*height 31")) expect_that(print(d[[2]]), prints_text("2 branches.*14 members.*height 32")) m2 <- cutat(wc, no=3) expect_that(modularity(g, m2), equals(wc$modularity[length(wc$modularity)-2], tolerance=1e-7)) }) igraph/inst/tests/test_graphNEL.R0000644000176000001440000000161212251656216016523 0ustar ripleyusers context("graphNEL conversion") test_that("graphNEL conversion works", { library(igraph) library(graph, warn.conflicts=FALSE) g <- erdos.renyi.game(100, 5/100) N <- igraph.to.graphNEL(g) g2 <- igraph.from.graphNEL(N) gi <- graph.isomorphic.vf2(g, g2) expect_that(gi$iso, is_true()) expect_that(gi$map12, equals(1:vcount(g))) expect_that(gi$map21, equals(1:vcount(g))) ## Attributes V(g)$name <- as.character(vcount(g):1) E(g)$weight <- sample(1:10, ecount(g), replace=TRUE) g$name <- "Foobar" N <- igraph.to.graphNEL(g) g2 <- igraph.from.graphNEL(N) expect_that(graph.isomorphic(g, g2), is_true()) expect_that(V(g)$name, equals(V(g2)$name)) A <- get.adjacency(g, attr="weight", sparse=FALSE) A2 <- get.adjacency(g2, attr="weight", sparse=FALSE) expect_that(A, equals(A)) expect_that(g$name, equals(g2$name)) suppressWarnings(unloadNamespace("graph")) }) igraph/inst/tests/test_label.propagation.community.R0000644000176000001440000000153312251656216022511 0ustar ripleyusers context("label.propagation.community") test_that("label.probagation.community works", { library(igraph) g <- graph.famous("Zachary") set.seed(42) lpc <- label.propagation.community(g) expect_that(lpc$modularity, equals(modularity(g, lpc$membership))) expect_that(membership(lpc), equals(c(1, 1, 2, 1, 3, 3, 3, 1, 2, 2, 3, 1, 1, 1, 2, 2, 3, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2))) expect_that(length(lpc), equals(3)) expect_that(sizes(lpc), equals(structure(c(10L, 19L, 5L), .Dim = 3L, .Dimnames = structure(list(`Community sizes` = c("1", "2", "3")), .Names = "Community sizes"), class = "table"))) }) igraph/inst/tests/test_fastgreedy.community.R0000644000176000001440000000176212251656216021251 0ustar ripleyusers context("fastgreedy.community") test_that("fastgreedy.community works", { library(igraph) set.seed(42) g <- graph.famous("Zachary") fc <- fastgreedy.community(g) expect_that(modularity(g, fc$membership), equals(max(fc$modularity))) expect_that(membership(fc), equals(c(1, 3, 3, 3, 1, 1, 1, 3, 2, 3, 1, 1, 3, 3, 2, 2, 1, 3, 2, 1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2))) expect_that(length(fc), equals(3)) expect_that(as.numeric(sizes(fc)), equals(c(8, 17, 9))) d <- as.dendrogram(fc) expect_that(print(d), prints_text("2 branches.*34 members.*height 33")) expect_that(print(d[[1]]), prints_text("2 branches.*17 members.*height 32")) expect_that(print(d[[2]]), prints_text("2 branches.*17 members.*height 30")) m2 <- cutat(fc, no=3) expect_that(modularity(g, m2), equals(fc$modularity[length(fc$modularity)-2])) }) igraph/inst/tests/test_graph.adhesion.R0000644000176000001440000000324712251656216017763 0ustar ripleyusers context("graph.adhesion") test_that("graph.adhesion works", { library(igraph) g <- graph.famous("Zachary") expect_that(graph.adhesion(g), equals(1)) expect_that(graph.cohesion(g), equals(1)) kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) expect_that(graph.adhesion(kite), equals(1)) expect_that(graph.cohesion(kite), equals(1)) camp <- graph.formula(Harry:Steve:Don:Bert - Harry:Steve:Don:Bert, Pam:Brazey:Carol:Pat - Pam:Brazey:Carol:Pat, Holly - Carol:Pat:Pam:Jennie:Bill, Bill - Pauline:Michael:Lee:Holly, Pauline - Bill:Jennie:Ann, Jennie - Holly:Michael:Lee:Ann:Pauline, Michael - Bill:Jennie:Ann:Lee:John, Ann - Michael:Jennie:Pauline, Lee - Michael:Bill:Jennie, Gery - Pat:Steve:Russ:John, Russ - Steve:Bert:Gery:John, John - Gery:Russ:Michael) expect_that(graph.adhesion(camp), equals(2)) expect_that(graph.cohesion(camp), equals(2)) }) igraph/inst/tests/test_graph.formula.R0000644000176000001440000000051312325263533017625 0ustar ripleyusers context("graph.formula") test_that("simplify argument works", { library(igraph) g1 <- graph.formula(1-1, 1-2, 1-2) g2 <- graph.formula(1-1, 1-2, 1-2, simplify=FALSE) expect_that(vcount(g1), equals(2)) expect_that(ecount(g1), equals(1)) expect_that(vcount(g2), equals(2)) expect_that(ecount(g2), equals(3)) }) igraph/inst/tests/test_minimum.size.separators.R0000644000176000001440000000174012251656216021673 0ustar ripleyusers context("minimum.size.separators") test_that("minimum.size.separators works", { library(igraph) camp <- graph.formula(Harry:Steve:Don:Bert - Harry:Steve:Don:Bert, Pam:Brazey:Carol:Pat - Pam:Brazey:Carol:Pat, Holly - Carol:Pat:Pam:Jennie:Bill, Bill - Pauline:Michael:Lee:Holly, Pauline - Bill:Jennie:Ann, Jennie - Holly:Michael:Lee:Ann:Pauline, Michael - Bill:Jennie:Ann:Lee:John, Ann - Michael:Jennie:Pauline, Lee - Michael:Bill:Jennie, Gery - Pat:Steve:Russ:John, Russ - Steve:Bert:Gery:John, John - Gery:Russ:Michael) camp <- simplify(camp) sep <- lapply(minimum.size.separators(camp), function(x) V(camp)[x]) expect_that(all(sapply(sep, is.minimal.separator, graph=camp)), is_true()) }) igraph/inst/tests/test_graph.isoclass.R0000644000176000001440000000072312251656216020005 0ustar ripleyusers context("graph.isoclass") test_that("graph.isoclass works", { library(igraph) g1 <- graph.isocreate(3, 10) g2 <- graph.isocreate(3, 11) expect_that(graph.isoclass(g1), equals(10)) expect_that(graph.isoclass(g2), equals(11)) g1 <- add.vertices(g1, 3) expect_that(graph.isoclass.subgraph(g1, 1:3), equals(10)) expect_that(graph.isoclass.subgraph(g1 %du% g2, 1:3), equals(10)) expect_that(graph.isoclass.subgraph(g1 %du% g2, 7:9), equals(11)) }) igraph/inst/tests/test_indexing.R0000644000176000001440000001752212251656216016677 0ustar ripleyusers context("Indexing") mm <- function(...) { v <- as.numeric(as.vector(list(...))) matrix(v, nrow=sqrt(length(v))) } am <- function(x) { x <- as.matrix(x) dimnames(x) <- NULL x } library(igraph) library(Matrix, quietly=TRUE, warn.conflicts=FALSE) g <- graph.tree(20) test_that("[ indexing works", { ## Are these vertices connected? expect_that(g[1,2], equals(1)) expect_that(am(g[c(1,1,7), c(2,3,14)]), equals(mm(1,1,0, 1,1,0, 0,0,1))) expect_that(am(g[c(1,1,7), c(5,3,12)]), equals(mm(0,0,0, 1,1,0 ,0,0,0))) expect_that(am(g[c(1,1,1,1), c(2,3,2,2)]), equals(matrix(1, 4, 4))) expect_that(am(g[c(8,17), c(17,8)]), equals(mm(1,0, 0,0))) }) V(g)$name <- letters[1:vcount(g)] test_that("[ indexing works with symbolic names", { ## The same with symbolic names expect_that(g['a','b'], equals(1)) expect_that(am(g[c('a','a','g'), c('b','c','n')]), equals(mm(1,1,0, 1,1,0, 0,0,1))) expect_that(am(g[c('a','a','g'), c('e','c','l')]), equals(mm(0,0,0, 1,1,0, 0,0,0))) expect_that(am(g[c('a','a','a','a'), c('b','c','b','b')]), equals(matrix(1, 4, 4))) expect_that(am(g[c('h','q'), c('q','h')]), equals(mm(1,0, 0,0))) }) test_that("[ indexing works with logical vectors", { ## Logical vectors lres <- structure(c(0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), .Dim = c(2L, 20L), .Dimnames = list(c("b", "c"), c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"))) expect_that(g[degree(g,mode="in")==0,2], equals(1)) expect_that(as.matrix(g[2:3,TRUE]), equals(lres)) }) test_that("[ indexing works with negative indices", { ## Negative indices nres <- structure(c(0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), .Dim = c(2L, 19L), .Dimnames=list(c("b", "c"), c("b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"))) expect_that(as.matrix(g[2:3,-1]), equals(nres)) }) el <- get.edgelist(g, names=FALSE) E(g)$weight <- el[,1] * el[,2] test_that("[ indexing works with weighted graphs", { ## Weighted graphs expect_that(g[1,2], equals(2)) expect_that(am(g[c(1,1,7), c(2,3,14)]), equals(mm(2,2,0, 3,3,0, 0,0,98))) expect_that(am(g[c(1,1,7), c(5,3,12)]), equals(mm(0,0,0, 3,3,0, 0,0,0))) expect_that(am(g[c(1,1,1,1), c(2,3,2,2)]), equals(mm(2,2,2,2, 3,3,3,3, 2,2,2,2, 2,2,2,2))) expect_that(am(g[c(8,17), c(17,8)]), equals(mm(136,0, 0,0))) }) test_that("[ indexing works with weighted graphs and symbolic names", { ## Weighted graph, with symbolic names expect_that(g['a','b'], equals(2)) expect_that(am(g[c('a','a','g'), c('b','c','n')]), equals(mm(2,2,0, 3,3,0, 0,0,98))) expect_that(am(g[c('a','a','g'), c('e','c','l')]), equals(mm(0,0,0, 3,3,0, 0,0,0))) expect_that(am(g[c('a','a','a','a'), c('b','c','b','b')]), equals(mm(2,2,2,2, 3,3,3,3, 2,2,2,2, 2,2,2,2))) expect_that(am(g[c('h','q'), c('q','h')]), equals(mm(136,0, 0,0))) }) ################################################################ test_that("[[ indexing works", { ## Adjacent vertices expect_that(g[[1, ]], equals(list(a=2:3))) expect_that(g[[, 2]], equals(list(b=1))) expect_that(g[[, 2, directed=FALSE]], equals(list(b=c(1,4,5)))) expect_that(g[[2, directed=FALSE]], equals(list(b=c(1,4,5)))) expect_that(g[[1:3, ]], equals(list(a=2:3, b=4:5, c=6:7))) expect_that(g[[, 1:3]], equals(list(a=numeric(), b=1, c=1))) }) test_that("[[ indexing works with symbolic names", { ## Same with vertex names expect_that(g[['a', ]], equals(list(a=2:3))) expect_that(g[[, 'b']], equals(list(b=1))) expect_that(g[[, 'b', directed=FALSE]], equals(list(b=c(1,4,5)))) expect_that(g[['b', directed=FALSE]], equals(list(b=c(1,4,5)))) expect_that(g[[letters[1:3],]], equals(list(a=2:3, b=4:5, c=6:7))) expect_that(g[[, letters[1:3]]], equals(list(a=numeric(), b=1, c=1))) }) test_that("[[ indexing works with logical vectors", { ## Logical vectors expect_that(g[[degree(g,mode="in")==0,]], equals(list(a=2:3))) }) test_that("[[ indexing works with filtering on both ends", { ## Filtering on both ends expect_that(g[[1:10, 1:10]], equals(list(a=2:3, b=4:5, c=6:7, d=8:9, e=10, f=numeric(), g=numeric(), h=numeric(), i=numeric(), j=numeric()))) }) ################################################################ test_that("[ can query edge ids", { ## Query edge ids expect_that(g[1,2, edges=TRUE], equals(1)) expect_that(am(g[c(1,1,7), c(2,3,14), edges=TRUE]), equals(mm(1,1,0, 2,2,0, 0,0,13))) expect_that(am(g[c(1,1,7), c(5,3,12), edges=TRUE]), equals(mm(0,0,0, 2,2,0, 0,0,0))) expect_that(am(g[c(1,1,1,1), c(2,3,2,2), edges=TRUE]), equals(mm(1,1,1,1, 2,2,2,2, 1,1,1,1, 1,1,1,1))) expect_that(am(g[c(8,17), c(17,8), edges=TRUE]), equals(mm(16,0, 0,0))) }) test_that("[ can query edge ids with symbolic names", { ## The same with symbolic names expect_that(g['a','b', edges=TRUE], equals(1)) expect_that(am(g[c('a','a','g'), c('b','c','n'), edges=TRUE]), equals(mm(1,1,0, 2,2,0, 0,0,13))) expect_that(am(g[c('a','a','g'), c('e','c','l'), edges=TRUE]), equals(mm(0,0,0, 2,2,0, 0,0,0))) expect_that(am(g[c('a','a','a','a'), c('b','c','b','b'), edges=TRUE]), equals(mm(1,1,1,1, 2,2,2,2, 1,1,1,1, 1,1,1,1))) expect_that(am(g[c('h','q'), c('q','h'), edges=TRUE]), equals(mm(16,0 ,0,0))) }) ################################################################ test_that("[[ can query incident edges", { ## Incident edges of vertices expect_that(g[[1, , edges=TRUE]], equals(list(a=1:2))) expect_that(g[[, 2, edges=TRUE]], equals(list(b=1))) expect_that(g[[, 2, directed=FALSE, edges=TRUE]], equals(list(b=c(3,4,1)))) expect_that(g[[2, directed=FALSE, edges=TRUE]], equals(list(b=c(3,4,1)))) expect_that(g[[1:3, , edges=TRUE]], equals(list(a=1:2, b=3:4, c=5:6))) expect_that(g[[, 1:3, edges=TRUE]], equals(list(a=numeric(), b=1, c=2))) }) test_that("[[ queries edges with vertex names", { ## Same with vertex names expect_that(g[['a', , edges=TRUE]], equals(list(a=1:2))) expect_that(g[[, 'b', edges=TRUE]], equals(list(b=1))) expect_that(g[[, 'b', directed=FALSE, edges=TRUE]], equals(list(b=c(3,4,1)))) expect_that(g[['b', directed=FALSE, edges=TRUE]], equals(list(b=c(3,4,1)))) expect_that(g[[letters[1:3],, edges=TRUE]], equals(list(a=1:2, b=3:4, c=5:6))) expect_that(g[[, letters[1:3], edges=TRUE]], equals(list(a=numeric(), b=1, c=2))) ## Filtering on both ends expect_that(g[[1:10, 1:10, edges=TRUE]], equals(list(1:2, 3:4, 5:6, 7:8, 9, numeric(), numeric(), numeric(), numeric(), numeric()))) }) ################################################################# test_that("[ handles from and to properly", { ## from & to g <- graph.tree(20) expect_that(g[from=c(1,2,2,3), to=c(3,4,8,7)], equals(c(1,1,0,1))) V(g)$name <- letters[1:20] expect_that(g[from=c("a","b","b","c"), to=c("c","d","h","g")], equals(c(1,1,0,1))) E(g)$weight <- (1:ecount(g)) ^ 2 expect_that(g[from=c("a","b","b","c"), to=c("c","d","h","g")], equals(c(4,9,0,36))) expect_that(g[from=c("a","b","b","c"), to=c("c","d","h","g"), edges=TRUE], equals(c(2,3,0,6))) }) igraph/inst/tests/test_delete.edges.R0000644000176000001440000000063112251656216017413 0ustar ripleyusers context("delete.edges") test_that("delete.edges works", { library(igraph) g <- graph.formula(A:B:C - D:E:F, D-E-F) g2 <- delete.edges(g, E(g, P=c("D", "E"))) expect_that(as.matrix(g2[]), is_equivalent_to(cbind(c(0,0,0,1,1,1), c(0,0,0,1,1,1), c(0,0,0,1,1,1), c(1,1,1,0,0,0), c(1,1,1,0,0,1), c(1,1,1,0,1,0)))) }) igraph/inst/tests/test_ba.game.R0000644000176000001440000000401312251656216016353 0ustar ripleyusers context("ba.game") test_that("ba.game works", { library(igraph) g <- ba.game(100, m=2) expect_that(ecount(g), equals(197)) expect_that(vcount(g), equals(100)) expect_that(is.simple(g), is_true()) g2 <- ba.game(100, m=2, algorithm="psumtree-multiple") expect_that(ecount(g2), equals(198)) expect_that(vcount(g2), equals(100)) expect_that(is.simple(g2), is_false()) g3 <- ba.game(100, m=2, algorithm="bag") expect_that(ecount(g3), equals(198)) expect_that(vcount(g3), equals(100)) expect_that(is.simple(g3), is_false()) }) test_that("ba.game can start from a graph", { library(igraph) set.seed(1234) g4 <- ba.game(10, m=1, algorithm="bag", start.graph=graph.empty(5)) expect_that(ecount(g4), equals(5)) expect_that(vcount(g4), equals(10)) expect_that(degree(g4), equals(c(2,0,0,0,1,2,1,1,2,1))) g6 <- ba.game(10, m=1, algorithm="bag", start.graph=graph.star(10)) expect_that(graph.isomorphic(g6, graph.star(10)), is_true()) g7 <- ba.game(10, m=3, algorithm="psumtree-multiple", start.graph=graph.empty(5)) expect_that(degree(g7, mode="out"), equals(c(0,0,0,0,0, 3,3,3,3,3))) g8 <- ba.game(10, m=3, algorithm="psumtree-multiple", start.graph=graph.star(5)) expect_that(degree(g8, mode="out"), equals(c(0,1,1,1,1, 3,3,3,3,3))) expect_that(graph.isomorphic(induced.subgraph(g8, 1:5), graph.star(5)), is_true()) g9 <- ba.game(10, m=3, algorithm="psumtree-multiple", start.graph=graph.star(10)) expect_that(graph.isomorphic(g9, graph.star(10)), is_true()) g10 <- ba.game(10, m=3, start.graph=graph.empty(5)) expect_that(degree(g10, mode="out"), equals(c(0,0,0,0,0, 3,3,3,3,3))) g11 <- ba.game(10, m=3, start.graph=graph.star(5)) expect_that(degree(g11, mode="out"), equals(c(0,1,1,1,1, 3,3,3,3,3))) expect_that(graph.isomorphic(induced.subgraph(g11, 1:5), graph.star(5)), is_true()) g12 <- ba.game(10, m=3, start.graph=graph.star(10)) expect_that(graph.isomorphic(g12, graph.star(10)), is_true()) }) igraph/inst/tests/test_motifs.R0000644000176000001440000000611212251656216016364 0ustar ripleyusers context("motifs") test_that("motif finding works", { library(igraph) set.seed(123) b <- erdos.renyi.game(10000, 4/10000, directed=TRUE) mno <- graph.motifs.no(b) mno0 <- graph.motifs.no(b, cut.prob=c(1/3, 0, 0)) mno1 <- graph.motifs.no(b, cut.prob=c(0, 0, 1/3)) mno2 <- graph.motifs.no(b, cut.prob=c(0, 1/3, 0)) expect_that(c(mno0/mno, mno1/mno, mno2/mno), equals(c(0.654821903845065, 0.666289144345659, 0.668393831285275))) mno3 <- graph.motifs.no(b, cut.prob=c(0, 1/3, 1/3)) mno4 <- graph.motifs.no(b, cut.prob=c(1/3, 0, 1/3)) mno5 <- graph.motifs.no(b, cut.prob=c(1/3, 1/3, 0)) expect_that(c(mno3/mno, mno4/mno, mno5/mno), equals(c(0.443959957465819, 0.441952797125797, 0.446004870037941) )) ###################### set.seed(123) b <- erdos.renyi.game(10000, 4/10000, directed=TRUE) m <- graph.motifs(b) m0 <- graph.motifs(b, cut.prob=c(1/3, 0, 0)) m1 <- graph.motifs(b, cut.prob=c(0, 1/3, 0)) m2 <- graph.motifs(b, cut.prob=c(0, 0, 1/3)) expect_that(m0/m, equals(c(NA, NA, 0.653972107372707, NA, 0.653993015279859, 0.612244897959184, 0.657514670174019, 0.63013698630137, NaN, 0.538461538461538, NaN, 0.565217391304348, NaN, NaN, NaN, NaN))) expect_that(m1/m, equals(c(NA, NA, 0.669562138856225, NA, 0.66808158454082, 0.73469387755102, 0.670819000404694, 0.657534246575342, NaN, 0.769230769230769, NaN, 0.739130434782609, NaN, NaN, NaN, NaN) )) expect_that(m2/m, equals(c(NA, NA, 0.666451718949538, NA, 0.665291458452201, 0.591836734693878, 0.666683528935654, 0.671232876712329, NaN, 0.753846153846154, NaN, 0.565217391304348, NaN, NaN, NaN, NaN) )) m3 <- graph.motifs(b, cut.prob=c(0, 1/3, 1/3)) m4 <- graph.motifs(b, cut.prob=c(1/3, 1/3, 0)) m5 <- graph.motifs(b, cut.prob=c(1/3, 1/3, 0)) expect_that(m3/m, equals(c(NA, NA, 0.445611905574732, NA, 0.442789875290769, 0.448979591836735, 0.444695973290166, 0.424657534246575, NaN, 0.369230769230769, NaN, 0.608695652173913, NaN, NaN, NaN, NaN))) expect_that(m4/m, equals(c(NA, NA, 0.439251981944392, NA, 0.439284975327761, 0.73469387755102, 0.445088021044112, 0.465753424657534, NaN, 0.630769230769231, NaN, 0.565217391304348, NaN, NaN, NaN, NaN) )) expect_that(m5/m, equals(c(NA, NA, 0.439985332979302, NA, 0.440288166730411, 0.346938775510204, 0.44159753136382, 0.452054794520548, NaN, 0.323076923076923, NaN, 0.347826086956522, NaN, NaN, NaN, NaN) )) }) igraph/inst/tests/test_graph.knn.R0000644000176000001440000000260412251656216016753 0ustar ripleyusers context("graph.knn") test_that("graph.knn works", { library(igraph) set.seed(42) ## Some trivial ones g <- graph.ring(10) expect_that(graph.knn(g), equals(list(knn=rep(2,10), knnk=c(NaN, 2)))) g2 <- graph.star(10) expect_that(graph.knn(g2), equals(list(knn=c(1, rep(9,9)), knnk=c(9, rep(NaN, 7), 1)))) ## A scale-free one, try to plot 'knnk' g3 <- simplify(ba.game(1000, m=5)) r3 <- graph.knn(g3) expect_that(r3$knn[43], equals(46)) expect_that(r3$knn[1000], equals(192.4)) expect_that(r3$knnk[100], equals(18.78)) expect_that(length(r3$knnk), equals(359)) ## A random graph g4 <- random.graph.game(1000, p=5/1000) r4 <- graph.knn(g4) expect_that(r4$knn[1000], equals(20/3)) expect_that(length(r4$knnk), equals(15)) expect_that(r4$knnk[12], equals(19/3)) ## A weighted graph g5 <- graph.star(10) E(g5)$weight <- seq(ecount(g5)) r5 <- graph.knn(g5) expect_that(r5, equals(structure(list(knn = c(1, 45, 22.5, 15, 11.25, 9, 7.5, 6.42857142857143, 5.625, 5), knnk = c(14.1448412698413, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 1)), .Names = c("knn", "knnk")) )) }) igraph/inst/tests/test_constraint.R0000644000176000001440000000232612251656216017252 0ustar ripleyusers context("constraint") test_that("constraint works", { library(igraph) constraint.orig <- function(graph, nodes=V(graph), attr=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } idx <- degree(graph) != 0 A <- get.adjacency(graph, attr=attr, sparse=FALSE) A <- A[idx, idx] n <- sum(idx) one <- c(rep(1,n)) CZ <- A + t(A) cs <- CZ %*% one # degree of vertices ics <- 1/cs CS <- ics %*% t(one) # 1/degree of vertices P <- CZ * CS #intermediate result: proportionate tie strengths PSQ <- P%*%P #sum paths of length two P.bi <- as.numeric(P>0) #exclude paths to non-contacts (& reflexive): PC <- (P + (PSQ*P.bi))^2 #dyadic constraint ci <- PC %*% one #overall constraint dim(ci) <- NULL ci2 <- numeric(vcount(graph)) ci2[idx] <- ci ci2[!idx] <- NaN ci2[nodes] } karate <- graph.famous("Zachary") c1 <- constraint(karate) c2 <- constraint.orig(karate) expect_that(c1, equals(c2)) set.seed(42) E(karate)$weight <- sample(1:10, replace=TRUE, ecount(karate)) wc1 <- constraint(karate) wc2 <- constraint.orig(karate, attr="weight") expect_that(wc1, equals(wc2)) }) igraph/inst/tests/test_degree.R0000644000176000001440000000160412251656216016317 0ustar ripleyusers context("degree") test_that("degree works", { library(igraph) g <- erdos.renyi.game(100, 1/100) d <- degree(g) el <- get.edgelist(g) expect_that(as.numeric(table(el)), equals(d[d!=0])) expect_that(degree(g) / (vcount(g)-1), equals(degree(g, normalized=TRUE))) g2 <- erdos.renyi.game(100, 2/100, dir=TRUE) din <- degree(g2, mode="in") dout <- degree(g2, mode="out") el2 <- get.edgelist(g2) expect_that(as.numeric(table(el2[,1])), equals(dout[dout!=0])) expect_that(as.numeric(table(el2[,2])), equals(din[din!=0])) expect_that(degree(g2, mode="in") / (vcount(g2)-1), equals(degree(g2, mode="in", normalized=TRUE))) expect_that(degree(g2, mode="out") / (vcount(g2)-1), equals(degree(g2, mode="out", normalized=TRUE))) expect_that(degree(g2, mode="all") / (vcount(g2)-1), equals(degree(g2, mode="all", normalized=TRUE))) }) igraph/inst/tests/test_graph.complementer.R0000644000176000001440000000036512251656216020661 0ustar ripleyusers context("graph.complementer") test_that("graph.complementer works", { library(igraph) g <- erdos.renyi.game(50, 3/50) g2 <- graph.complementer(g) g3 <- graph.complementer(g2) expect_that(graph.isomorphic(g, g3), is_true()) }) igraph/inst/tests/test_clusters.R0000644000176000001440000000111212251656216016722 0ustar ripleyusers context("clusters") test_that("clusters works", { library(igraph) set.seed(42) gc <- function(graph) { cl <- clusters(graph) induced.subgraph(graph, which(cl$membership==which.max(cl$csize))) } rg <- function(n) { gc(erdos.renyi.game(n, 1/n)) } G <- lapply(1:30, function(x) rg(sample(100, 1))) Gsize <- sapply(G, vcount) allg <- graph.disjoint.union(G) clu <- clusters(allg) expect_that(as.numeric(table(clu$membership)), equals(clu$csize)) expect_that(sort(clu$csize), equals(sort(Gsize))) expect_that(clu$no, equals(length(G))) }) igraph/inst/tests/test_graph.subisomorphic.lad.R0000644000176000001440000000506312251656216021614 0ustar ripleyusers context("graph.subisomorphic.lad") test_that("graph.subisomorphic.lad works", { library(igraph) pattern <- graph.formula(1:2:3:4:5, 1 - 2:5, 2 - 1:5:3, 3 - 2:4, 4 - 3:5, 5 - 4:2:1) target <- graph.formula(1:2:3:4:5:6:7:8:9, 1 - 2:5:7, 2 - 1:5:3, 3 - 2:4, 4 - 3:5:6:8:9, 5 - 1:2:4:6:7, 6 - 7:5:4:9, 7 - 1:5:6, 8 - 4:9, 9 - 6:4:8) domains <- list(`1` = c(1,3,9), `2` = c(5,6,7,8), `3` = c(2,4,6,7,8,9), `4` = c(1,3,9), `5` = c(2,4,8,9)) i1 <- graph.subisomorphic.lad(pattern, target, all.maps=TRUE) i2 <- graph.subisomorphic.lad(pattern, target, induced=TRUE, all.maps=TRUE) i3 <- graph.subisomorphic.lad(pattern, target, domains=domains, all.maps=TRUE) expect_that(i1$iso, is_true()) expect_that(i2, equals( structure(list(iso = TRUE, map = structure(c(1, 2, 3, 4, 5), .Names = c("1", "2", "3", "4", "5")), maps = list(structure(c(1, 2, 3, 4, 5), .Names = c("1", "2", "3", "4", "5")), structure(c(6, 4, 3, 2, 5), .Names = c("6", "4", "3", "2", "5")), structure(c(6, 5, 2, 3, 4), .Names = c("6", "5", "2", "3", "4")), structure(c(1, 5, 4, 3, 2), .Names = c("1", "5", "4", "3", "2")))), .Names = c("iso", "map", "maps")) )) expect_that(i3, equals( structure(list(iso = TRUE, map = structure(c(1, 5, 4, 3, 2), .Names = c("1", "5", "4", "3", "2")), maps = list(structure(c(1, 5, 4, 3, 2), .Names = c("1", "5", "4", "3", "2")))), .Names = c("iso", "map", "maps")) )) }) test_that("LAD stress test", { library(igraph) set.seed(42) N <- 100 for (i in 1:N) { target <- erdos.renyi.game(20, .5) pn <- sample(4:18, 1) pattern <- induced.subgraph(target, sample(vcount(target), pn)) iso <- graph.subisomorphic.lad(pattern, target, induced=TRUE, all.maps=FALSE) expect_that(iso$iso, is_true()) } set.seed(42) for (i in 1:N) { target <- erdos.renyi.game(20, 1/20) pn <- sample(5:18, 1) pattern <- erdos.renyi.game(pn, .6) iso <- graph.subisomorphic.lad(pattern, target, induced=TRUE, all.maps=FALSE) expect_that(iso$iso, is_false()) } }) igraph/inst/tests/test_graph.adjacency.R0000644000176000001440000001320012252344615020076 0ustar ripleyusers context("graph.adjancency") test_that("graph.adjacency works", { library(igraph) M1 <- rbind(c(0,0,1,1), c(1,0,0,0), c(0,1,0,1), c(1,0,0,1)) g1 <- graph.adjacency(M1) el1 <- get.edgelist(g1) expect_that(el1[order(el1[,1], el1[,2]),], equals(structure(c(1, 1, 2, 3, 3, 4, 4, 3, 4, 1, 2, 4, 1, 4), .Dim = c(7L, 2L)))) M2 <- rbind(c(0,1,1,1), c(1,0,0,0), c(1,0,0,1), c(1,0,1,0)) g2 <- graph.adjacency(M2, mode="undirected") el2 <- get.edgelist(g2) expect_that(el2[order(el2[,1], el2[,2]),], equals(structure(c(1, 1, 1, 3, 2, 3, 4, 4), .Dim = c(4L, 2L)))) M3 <- rbind(c(0,1,1,2), c(1,0,0,0), c(1,0,0,0), c(1,0,1,0)) g3 <- graph.adjacency(M3, mode="min") el3 <- get.edgelist(g3) expect_that(el3[order(el3[,1], el3[,2]),], equals(structure(c(1, 1, 1, 2, 3, 4), .Dim=c(3L, 2L)))) M4 <- rbind(c(0,1,1,2), c(1,0,0,0), c(1,0,0,0), c(1,0,1,0)) g4 <- graph.adjacency(M4, mode="max") el4 <- get.edgelist(g4) expect_that(el4[order(el4[,1], el4[,2]),], equals(structure(c(1, 1, 1, 1, 3, 2, 3, 4, 4, 4), .Dim=c(5L, 2L)))) M5 <- rbind(c(0,1,1,2), c(1,0,0,0), c(1,0,0,0), c(1,0,1,0)) g5 <- graph.adjacency(M5, mode="upper") el5 <- get.edgelist(g5) expect_that(el5[order(el5[,1], el5[,2]),], equals(structure(c(1, 1, 1, 1, 2, 3, 4, 4), .Dim=c(4L, 2L)))) M6 <- rbind(c(0,1,1,2), c(1,0,0,0), c(1,0,0,0), c(1,0,1,0)) g6 <- graph.adjacency(M6, mode="lower") el6 <- get.edgelist(g6) expect_that(el6[order(el6[,1], el6[,2]),], equals(structure(c(1, 1, 1, 3, 2, 3, 4, 4), .Dim=c(4L, 2L)))) M7 <- rbind(c(0,1,1,2), c(1,0,0,0), c(1,0,0,0), c(1,0,1,0)) g7 <- graph.adjacency(M7, mode="plus") el7 <- get.edgelist(g7) expect_that(el7[order(el7[,1], el7[,2]),], equals(structure(c(1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 3, 3, 4, 4, 4, 4), .Dim = c(8L, 2L)))) M8 <- rbind(c(0,1,1,0.5), c(1,0,0,0), c(1,0,0,0), c(1,0,2,0)) g8 <- graph.adjacency(M8, mode="directed", weighted=TRUE) el8 <- cbind(get.edgelist(g8), E(g8)$weight) expect_that(el8[order(el8[,1], el8[,2]),], equals(structure(c(1, 1, 1, 2, 3, 4, 4, 2, 3, 4, 1, 1, 1, 3, 1, 1, 0.5, 1, 1, 1, 2), .Dim = c(7L, 3L)))) M9 <- rbind(c(0,1,1,3), c(1,0,0,0), c(1,0,0,2), c(3,0,2,0)) g9 <- graph.adjacency(M9, mode="undirected", weighted=TRUE) el9 <- cbind(get.edgelist(g9), E(g9)$weight) expect_that(el9[order(el9[,1], el9[,2]),], equals(structure(c(1, 1, 1, 3, 2, 3, 4, 4, 1, 1, 3, 2), .Dim = c(4L, 3L)))) M10 <- rbind(c(0,1,1,0.5), c(1,0,0,0), c(1,0,0,0), c(1,0,2,0)) g10 <- graph.adjacency(M10, mode="max", weighted=TRUE) el10 <- cbind(get.edgelist(g10), E(g10)$weight) expect_that(el10[order(el10[,1], el10[,2]),], equals(structure(c(1, 1, 1, 3, 2, 3, 4, 4, 1, 1, 1, 2), .Dim = c(4L, 3L)))) M11 <- rbind(c(0,1,1,0.5), c(1,0,0,0), c(1,0,0,0), c(1,0,2,0)) g11 <- graph.adjacency(M11, mode="min", weighted=TRUE) el11 <- cbind(get.edgelist(g11), E(g11)$weight) expect_that(el11[order(el11[,1], el11[,2]),], equals(structure(c(1, 1, 1, 2, 3, 4, 1, 1, 0.5), .Dim = c(3L, 3L)))) M12 <- rbind(c(0,1,1,0.5), c(1,0,0,0), c(1,0,0,0), c(1,0,2,0)) g12 <- graph.adjacency(M12, mode="lower", weighted=TRUE) el12 <- cbind(get.edgelist(g12), E(g12)$weight) expect_that(el12[order(el12[,1], el12[,2]),], equals(structure(c(1, 1, 1, 3, 2, 3, 4, 4, 1, 1, 1, 2), .Dim = c(4L, 3L)))) M13 <- rbind(c(0,1,1,0.5), c(1,0,0,0), c(1,0,0,0), c(1,0,2,0)) g13 <- graph.adjacency(M13, mode="upper", weighted=TRUE) el13 <- cbind(get.edgelist(g13), E(g13)$weight) expect_that(el13[order(el13[,1], el13[,2]),], equals(structure(c(1, 1, 1, 2, 3, 4, 1, 1, 0.5), .Dim = c(3L, 3L)))) M14 <- rbind(c(0,1,1,0.5), c(1,0,0,0), c(1,0,0,0), c(1,0,2,0)) g14 <- graph.adjacency(M14, mode="plus", weighted=TRUE) el14 <- cbind(get.edgelist(g14), E(g14)$weight) expect_that(el14[order(el14[,1], el14[,2]),], equals(structure(c(1, 1, 1, 3, 2, 3, 4, 4, 2, 2, 1.5, 2), .Dim = c(4L, 3L)))) }) test_that("graph.adjacency 2 edge bug is fixed", { library(Matrix) library(igraph) A <- Matrix(0, 10, 10, sparse=TRUE) A[3,5] <- A[5,3] <- 1 g <- graph.adjacency(A, mode="undirected") expect_that(g[], equals(A)) }) test_that("graph.adjacenct empty graph bug is fixed", { library(Matrix) library(igraph) A <- Matrix(0, 10, 10, sparse=TRUE) g <- graph.adjacency(A, mode="undirected") expect_that(as.matrix(g[]), equals(as.matrix(A))) }) test_that("bug #554 is fixed", { library(igraph) library(Matrix) M <- Matrix(0, 5, 5) M[1,2] <- M[2,1] <- M[3,4] <- M[4,3] <- 1 g <- graph.adjacency(M, mode="undirected", weighted=TRUE) expect_that(g[], equals(M)) }) igraph/inst/tests/test_betweenness.R0000644000176000001440000000517712251656216017417 0ustar ripleyusers context("betweenness") test_that("betweenness works for kite graph", { library(igraph) kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) nf <- (vcount(kite)-1) * (vcount(kite)-2) /2 bet <- structure(betweenness(kite) / nf, names=V(kite)$name) bet <- round(sort(bet, decreasing=TRUE), 3) expect_that(bet, equals(structure(c(0.389, 0.231, 0.231, 0.222, 0.102, 0.023, 0.023, 0.000, 0.000, 0.000), names=c("Heather", "Fernando", "Garth", "Ike", "Diane", "Andre", "Beverly", "Carol", "Ed", "Jane")))) bet2 <- structure(betweenness(kite, normalized=TRUE), names=V(kite)$name) bet2 <- round(sort(bet2, decreasing=TRUE), 3) expect_that(bet2, equals(bet)) }) test_that("weighted betweenness works", { library(igraph) nontriv <- graph( c(0,19,0,16,0,20,1,19,2,5,3,7,3,8, 4,15,4,11,5,8,5,19,6,7,6,10,6,8, 6,9,7,20,9,10,9,20,10,19, 11,12,11,20,12,15,13,15, 14,18,14,16,14,17,15,16,17,18)+1, dir=FALSE ) E(nontriv)$weight <- c(0.5249, 1, 0.1934, 0.6274, 0.5249, 0.0029, 0.3831, 0.05, 0.6274, 0.3831, 0.5249, 0.0587, 0.0579, 0.0562, 0.0562, 0.1934, 0.6274, 0.6274, 0.6274, 0.0418, 0.6274, 0.3511, 0.3511, 0.1486, 1, 1, 0.0711, 0.2409) nontrivRes <- c(20,0,0,0,0,19,80,85,32,0,10, 75,70,0,36,81,60,0,19,19,86) bet <- betweenness(nontriv) expect_that(bet, equals(nontrivRes)) }) test_that("normalization works well", { library(igraph) g1 <- graph.formula( 0 +-+ 1 +-+ 2 ) b11 <- betweenness(g1, normalized=TRUE, directed=FALSE) expect_that(b11, equals(c('0'=0, '1'=1, '2'=0))) b12 <- betweenness(g1, normalized=TRUE, directed=TRUE) expect_that(b12, equals(c('0'=0, '1'=1, '2'=0))) g2 <- graph.formula( 0 --- 1 --- 2 ) b2 <- betweenness(g2, normalized=TRUE) expect_that(b2, equals(c('0'=0, '1'=1, '2'=0))) }) igraph/inst/tests/test_as.undirected.R0000644000176000001440000000134512251656216017616 0ustar ripleyusers context("as.undirected") test_that("as.undirected keeps attributes", { library(igraph) g <- graph.formula(A+-+B, A--+C, C+-+D) g$name <- "Tiny graph" E(g)$weight <- seq_len(ecount(g)) g2 <- as.undirected(g, mode="collapse") ; df2 <- get.data.frame(g2) g3 <- as.undirected(g, mode="each") ; df3 <- get.data.frame(g3) g4 <- as.undirected(g, mode="mutual") ; df4 <- get.data.frame(g4) expect_that(g2$name, equals(g$name)) expect_that(g3$name, equals(g$name)) expect_that(g4$name, equals(g$name)) expect_that(df2[order(df2[,1], df2[,2]),]$weight, equals(c(4,2,9))) expect_that(df3[order(df3[,1], df3[,2]),]$weight, equals(c(1,3,2,4,5))) expect_that(df4[order(df4[,1], df4[,2]),]$weight, equals(c(4,9))) }) igraph/inst/tests/test_bipartite.random.game.R0000644000176000001440000000362212251656216021240 0ustar ripleyusers context("bipartite.random.game") test_that("bipartite.random.game works", { library(igraph) set.seed(42) g1 <- bipartite.random.game(10, 5, type="gnp", p=.1) expect_that(g1$name, equals("Bipartite Gnp random graph")) expect_that(vcount(g1), equals(15)) expect_that(ecount(g1), equals(7)) expect_that(bipartite.mapping(g1)$res, is_true()) expect_that(is.directed(g1), is_false()) g2 <- bipartite.random.game(10, 5, type="gnp", p=.1, directed=TRUE) expect_that(vcount(g2), equals(15)) expect_that(ecount(g2), equals(6)) expect_that(bipartite.mapping(g2)$res, is_true()) expect_that(is.directed(g2), is_true()) expect_that(str(g2), prints_text("5->11")); g3 <- bipartite.random.game(10, 5, type="gnp", p=.1, directed=TRUE, mode="in") expect_that(str(g3), prints_text("11->3")); g4 <- bipartite.random.game(10, 5, type="gnm", m=8) expect_that(vcount(g4), equals(15)) expect_that(ecount(g4), equals(8)) expect_that(bipartite.mapping(g4)$res, is_true()) expect_that(is.directed(g4), is_false()) g5 <- bipartite.random.game(10, 5, type="gnm", m=8, directed=TRUE) expect_that(vcount(g5), equals(15)) expect_that(ecount(g5), equals(8)) expect_that(bipartite.mapping(g5)$res, is_true()) expect_that(is.directed(g5), is_true()) expect_that(str(g5), prints_text("5->12")) g6 <- bipartite.random.game(10, 5, type="gnm", m=8, directed=TRUE, mode="in") expect_that(vcount(g6), equals(15)) expect_that(ecount(g6), equals(8)) expect_that(bipartite.mapping(g6)$res, is_true()) expect_that(is.directed(g6), is_true()) expect_that(str(g6), prints_text("12->10")) ##### g7 <- bipartite.random.game(10, 5, type="gnp", p=0.9999, directed=TRUE, mode="all") expect_that(ecount(g7), equals(100)) g8 <- bipartite.random.game(10, 5, type="gnm", m=99, directed=TRUE, mode="all") expect_that(ecount(g8), equals(99)) }) igraph/inst/tests/test_count.multiple.R0000644000176000001440000000237112251656216020050 0ustar ripleyusers context("count.multiple") test_that("count.multiple works", { library(igraph) set.seed(42) g <- barabasi.game(10, m=3, algorithm="bag") im <- is.multiple(g) cm <- count.multiple(g) expect_that(im, equals(c(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE))) expect_that(cm, equals(c(3, 3, 3, 3, 3, 3, 1, 1, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2))) expect_that(count.multiple(simplify(g)), equals(rep(1, ecount(simplify(g))))) ## Direction of the edge is important expect_that(is.multiple(graph( c(1,2, 2,1) )), equals(c(FALSE, FALSE))) expect_that(is.multiple(graph( c(1,2, 2,1), dir=FALSE )), equals(c(FALSE, TRUE))) ## Remove multiple edges but keep multiplicity g <- barabasi.game(10, m=3, algorithm="bag") E(g)$weight <- 1 g <- simplify(g) expect_that(any(is.multiple(g)), is_false()) expect_that(E(g)$weight, equals(c(3, 2, 1, 2, 1, 3, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1))) }) igraph/inst/tests/test_dyad.census.R0000644000176000001440000000072412251656216017306 0ustar ripleyusers context("dyad.census") test_that("dyad.census works", { library(igraph) ce <- simplify(read.graph(gzfile("celegansneural.gml.gz"), format="gml")) dc <- dyad.census(ce) expect_that(dc, equals(list(mut=197, asym=1951, null=41808))) expect_that(sum(is.mutual(ce)), equals(dc$mut * 2)) expect_that(ecount(as.undirected(ce, mode="collapse")) - dc$mut, equals(dc$asym)) expect_that(sum(unlist(dc)), equals(vcount(ce) * (vcount(ce)-1) / 2)) }) igraph/inst/tests/test_modularity_matrix.R0000644000176000001440000000065712251656216020650 0ustar ripleyusers context("mod.matrix") test_that("mod.matrix works", { library(igraph) kar <- graph.famous("zachary") fc <- fastgreedy.community(kar) m1 <- modularity(kar, membership(fc)) m2 <- modularity(kar, membership(fc), weights=rep(1, ecount(kar))) expect_that(m1, equals(m2)) B1 <- mod.matrix(kar, membership(fc)) B2 <- mod.matrix(kar, membership(fc), weights=rep(1, ecount(kar))) expect_that(B1, equals(B2)) }) igraph/inst/tests/test_graph.bipartite.R0000644000176000001440000000074212251656216020151 0ustar ripleyusers context("graph.bipartite") test_that("graph.bipartite works", { library(igraph) I <- matrix(sample(0:1, 35, replace=TRUE, prob=c(3,1)), nc=5) g <- graph.incidence(I) edges <- unlist(sapply(seq_len(nrow(I)), function(x) { w <- which(I[x,] != 0) + nrow(I) if (length(w)!=0) { as.vector(rbind(x, w)) } else { numeric() } })) g2 <- graph.bipartite(seq_len(nrow(I)+ncol(I)) > nrow(I), edges) I2 <- get.incidence(g2) expect_that(I2, is_equivalent_to(I)) }) igraph/inst/tests/test_edge.betweenness.R0000644000176000001440000000226312251656216020313 0ustar ripleyusers context("edge.betweenness") test_that("edge.betweenness works", { library(igraph) kite <- graph.formula(Andre - Beverly:Carol:Diane:Fernando, Beverly - Andre:Diane:Ed:Garth, Carol - Andre:Diane:Fernando, Diane - Andre:Beverly:Carol:Ed:Fernando:Garth, Ed - Beverly:Diane:Garth, Fernando - Andre:Carol:Diane:Garth:Heather, Garth - Beverly:Diane:Ed:Fernando:Heather, Heather - Fernando:Garth:Ike, Ike - Heather:Jane, Jane - Ike) bet <- betweenness(kite) ebet <- edge.betweenness(kite) bet2 <- sapply(1:vcount(kite), function(x) { ae <- E(kite)[ adj(x) ] (sum(ebet[ae])-vcount(kite)+1) / 2 }) expect_that(unname(bet), equals(bet2)) #### Weighted E(kite)$weight <- sample(1:10, ecount(kite), replace=TRUE) bet <- betweenness(kite) ebet <- edge.betweenness(kite) bet2 <- sapply(1:vcount(kite), function(x) { ae <- E(kite)[ adj(x) ] (sum(ebet[ae])-vcount(kite)+1) / 2 }) expect_that(unname(bet), equals(bet2)) }) igraph/inst/tests/test_graph.mincut.R0000644000176000001440000000056512271600260017456 0ustar ripleyusers context("graph.mincut") test_that("graph.mincut works", { library(igraph) g2 <- graph( c(1,2,2,3,3,4, 1,6,6,5,5,4, 4,1) ) E(g2)$capacity <- c(3,1,2, 10,1,3, 2) mc <- graph.mincut(g2, value.only=FALSE) expect_that(mc$value, equals(1)) expect_that(mc$cut, equals(2)) expect_that(mc$partition1, equals(2)) expect_that(mc$partition2, equals(c(1,3:6))) }) igraph/inst/tests/celegansneural.gml.gz0000644000176000001440000002715712263023733020020 0ustar ripleyusers‹xó“Lcelegansneural.gml­É®-ÇqEçúŠ}Á©¬Þ3Ãc{䙡->P‚eÉ Eû÷ý,èRµ÷aì»2 œu³‰ÌÈè2ëŸ~üúÝŸÿôã—_ÿów?þÇ—ùú¿ÿùÝ¿üé_þõw?}ùÇŸ~ø²._–ñûý¯ûËx½Ž_ÿꇿû¯ßýêß~õåË÷¿ÿñëoÿüõû/Ë·ÿøãŸ¾ÿúíÿÿÿ¿|ùý÷_^ù—?|÷ï_ÿðå×˯¿ý×o~áWËóW{ù³ñüÙ9ªŸ­ò³³úÙ&?»ªŸíÏŸ•mÏ_ݯêg§ü¬ük—ÌÚ^öíÖßÝåüª–µü¡Hâ¨ÿ Š¢¬Èâªÿžã®ÿžH£†ˆc+—À¢ò(¦ò¨ÿœÈcÔëXÄq—Ã"{+'Òõßi,¡ƒ›þp/(òXÖú/ŠDÂïD"õˆE"G¹ß†îòw«n£üHd)7檩U•Hd”ã]u”+pUy„†ýa=bȲÖQD~'"Y_åjÝD&åÜD$µ¶7‰”ýÛt“¼Ê¥ºm:º‡"”r¦7É«î¡j­Rmmª¶^åêßTo•s¸‹DêS$R/­]D²•‹‰¬u÷Tk•‚Ûu“”ó·‹@Fý;Ý"¥Üv‘ÇV÷O•V­e‘G­¥ÈZ®ƒCR[ ‡n‘­þ¡HdÔ‰„¿§[¤\1‡J¤^Z‡ˆdÔ3£J«ü{§ž#¥DN‘ÈVÛ‹*‘zOÉZ.ÖS7IhZwÉ«nZözÌ*”W¹OÕ[k¹NÝ(µX.K­ª/=ß_¥îºD0ký;Õ]åP.õFêß™ÁU7,R9ë©Q©¥VºT*µ:¼L*ådß*•z÷ݪ¿jwH…RöðÖÍR›·n–ZsÞ*–zºoõLʳâV±ÔžÓ-b¹ê?(R9Ë‘|3¤‡¥ô¾YFÒÅú<ûfÊ<¹‡?©‚©æåe’©}²—ž-å†ù¶Ùu<åT~S]ÚÍRÚß´¦Œ<4.â9ê¶Õ•¿ê¿¨®üR{™ËbÇ~¹¿–ÅlãZ>êÐïa<"ž:Ô±¨O_ë©Eú³^mêÖŸa*E8WÝ´:öWÝ´zö[=juí—Úƒ]Ô¹¿ÂE4Wh[C_µW¼¨w¿…°‘Èf¯7Ž:ø{ÅÀê?©>þ]ÆœüÚZÔÍ¿ky¯vâ„ÆuãÔ»V]ý=tR7Nø‹ªÖîze¨¯…¶uçÔÂ1g? 5BY7­ÿRxõøÐI‘ÍQ‹[=þQ[6‹:ýa]¨Ó„ѨKSù–ÍüÌuµøq­Îw3 êí©gSýÿ%œyuèaÙMBõ:²(@í‚-µ…¼h$`Ôvåb±€ -P;C‹†Fm /‡–ëùÔÀö˜Å‚µ¡QQÛó‹Å‚B²È@8ý,4P;Ê‹F05<0êØç¢‚å¬Ç®!‚²#F¦ ‚JÔ(ÁG¿† FXÉ(ǪF F9\4T0‚AzYP­–‘ F0=4Z0‚¤ñ‚¥Žô/1¯Ðº:§Á4Ô Ál „Uw™ŒjrYºþ¥FÖ:L³Ü¶BîNeÜY Œ:®¾hü`ÔñÀÅuP¹MFáoZh'üMõ„Âõ8ª—ñ²ŒZø¥™ u²QãK×H¨3íãe"*Ç>4°Ô÷°HB]©04’0êPÿÐHÂUçx5’PñÊêäã°PB}l %ÔJiX(¡Þc±dthÝ$Tª…±˜„êòbCh]¤µ»<3êê¿©…d4,ÜS·nÕuôjhHaÔ ¡aõ7¬b Žtañ¸zgjTaÔ!Ÿ1,ß~©‡Q}Ä «åKXõa44°°ÔæÊð ‚zDYµC<4´0êøÐØÂ¨sÖ µã>4¸0êckhta©Ã Cà K]°46«ð¨çÓ á—ªëjÃfl&£z>-Ä4Øf†w-÷Ít]˜%ÕuuÚaxmA-# 2ÔEt u¹ØÐ ÃJ¨vÛGá—*£P|dA†:ã9<ÈPkE 2ÔiÙaA†:X?v«[«GdA†:d4,ÈP‡Œ†êÈþ° CíÀ 2Ԏ̰ Cíê 2ŒÚðduToX!ì# 2Ô6ú° CªµSÕŽá° C]Ý54È0‚9-TËȃ õ|Z!xdÁ“± C]2<ÈP·îA†Zšd禂ǥA†NC 2kMƒ #èäËʪ굤A† e-Èô¼êàÒРõF´dÞ™ê€À¸­1Ãjõt°ë4ȰþÍ®ûúýûåÿé§ûõ¯5þþîǾþù¯¥üÿóÝ~úú—GÐx@ƒBk§¥­ÓÒÞiéx@…Îtuºw? -@‹÷›Rj¡¨]B]O ¯¼­»ÓųÓÃDô_ÛšàèÞÒ>¶¦ñÜ`[CÖüÙ¡>’†ÓØsdi öT7ÇèÂ2ì¹öÓôvô°³7%W«µÑ™‘«7²¥ÕÅ­7T9*v¼zØsõóõx.Žá)'»!®NíæsUG¶ÑYÞ+3*Åžƒ‹J^±£7'8„¨Ø‰>Åž %ê?ÅžàëDµX\—–”ÄΗq= »l‚]x¯63®Êa}"öµÏÆ¢¼;±¯ Ù<‘{5´ØGm ŸsŠ=…ÅhS±A³ä†Ñ,¹axý+†‰bxo+Ö[[»¡’’Xxöϲ“ÑÐSLŒ_ÞÜŽ]HNÅ.el‰¢`ܳÖ$ÖΊáMW'¢ä,ÙÂpæÒ€ Åð2Ñg2 @0 ÕÄK–z{`ñ®Ly¡8^f°¼ÝÎÑÕzù¢'ªq¸þO¹‹.Åð7 _¾SŒjØaW“é7/0ÁhªH©«76l †o¬*†o9)†Ç&±Q~?¶Ž°Å{…ŠÑ×ÐÀ ¾ ·õZ³8ÞÞµ¡å­å©øöµ–VbÝ%¿Gª­ÑDþPû ®Uägaõªai+F¯¡†‰Ž ŸpŠÑ ”u_#U ?Z VAŠáK«ŠaõªXs%ã]ªN•:íñj­‚ ÞßjÃâM ß;ŠÑë2†ÉÇ›ãnûªv¾|§ãPÃ^1ºœ•Âu%ÖGÙᜓÃ*©/Å6ªšmJ¨eƒ£ipõ>Ö² .Ϥ`TW*…+«“‹3ñÒªö‘Ʀ­5ªô;¨¡˜„­âæV±áª›z~FÏÃh ·ax¿ÕŽÎ„zk'² ËR‹ ñ¢Ôr?|“Z[£F½bøÎ‰uËM° ëuòÂG‡á`WgÍc'Ã*HM¬ëõñ¤.„b+ÞnV"Eí%V µÏOS­?ÀUPÆqÓ°ç}+†ï­µ÷ŸªÐ[y8±jþ¢‡­Ôm_{IÃð'ŽXk4þ±©ÝKÃè–S  ¬5z·Ã0ºS·ÒîMóo~L@-QzÂYkø1ÚÆ›hîèÃh~uE1¬(·úvFœbÔîUL+9⤇+96³òðƒ-á.çNy{Í~®½~òݪ~³ÅÊ×{“‰7‚“w{Ù“(u¾V¬ùš—+¶V©¼R£×.]W [Q›>¥B+UõŸª Òˆ…wG¢zÖ*/¼ z頭ΙÄÖÔŽ¥õŸŠá#¤ÎFð>nø± u“¬9Á"×|YA9.µ¶éÖ©kÃx¹ÕV›öQÁ†¤B|÷¨õl¾RøhT Û¿ÖGZäe¤×… £ÆÉ^»-­ŸÚëÄGÚß{/§°×¾U¼¬³+=¶§ö/Í"íµqÏ[×uë5†MfÅð*é…}múéÚê™x6x»ikT»îz¥ËZmµ•Ž¥°'`Ö$ŠÑP†b8ƯŽåíµ‰r]ßÅÇvâµUßMŒì]1TL¼Ód•(†+嬓Xm &n_²°­“´àǦëmšØ{}5.N‰:bx•†/ÅïêâЊ«½W¨ö:õƒ{í¾%z×[3xw«¯B_êØÍ5jµÆÕk(y‹òV'«eí%M†ÚTbKA;‰[+ü‹‡°PøåeÅd•ðÖp®Ê†öœ‘¸ÔŸ¥Ï(†Íuƒéë†aO¥çt›Ø°¡ ØÕœê«[kXq)Ö[ÉV\Šá½­Äþ=5ˆg½°á½›«ÜÕyëÍF¥N,9m //’^'¥–/®ÁpuV[YkØèÒ)¡oà(&>~´0´5~æëàX4͵5ìÓ–‰òm3z»tÅG¾ÆB{›ôÀ®‘ª„Þ.Å¥øGýª'ÇðKõ!îßú–¸)¶Q ¶y­5,Áðg­Qçи½ë§5ž”ÂåAŠí4ÿ©˜(×ø‘ibi×¥¬ñéZ¥xô‚š†Q›Ë üF#H‡Æ_x™(Gï¡(FJá  mñ)hÀ°;#XÞ í5wô€?µjXSØ UŽzÂJ=ç?j®:dÎgßG´ÁÏØ*¡†¹M >»µ—½Áñ勤{;{Іá¥Ü+9Wl£†ùQÎ)~GG?|„ÏŽòƒNq‘…ëmhô~¦bÜR(?û¨Ùñnk~ Jãó4¨fÄf‰¶FýY½¯ª®›9êÜÃÄ”àMªSқɫr­8ï™(ǽmŽzëŠaÇHÃEËÊa¹ u÷ã•ÕG/Eåè­“1°6¯ !9†¿Ü¦"xP)‚d3Ù›\i#ÃÚÜ– ÞujeÃVPZ‰‡wýù&ÞÚ‰e} ·Ö‹}FŸ#S ±ãrÓ°&>ëM N‰Ök —sYkX+¨¸©Asjõ$lL)©KN¦aø{õÇ¢8†]ZÅô]˜‰æè£ØF-JŰgƒ£E—§Fzi•¢Ùuè*Q ÇtÑ^kZ%ŠáSÑ0Z]bŽ)†ë Mnô ¶ÖzØÚ[É88£Vʶ¸ðgQµ5êã(vâÖìF'žÍZP¹Ùå¾Vcúpi”·qô›K6¸æ¤àçÎ^dY1j«)…ß±TL‚xQàõ3!qU¶ž 9ë@cìc/§VAÃÃʤþEœH-Îía|&5ÒÕ¢øf«Kz£ê­G•P?×Ï;‰¿‰vª—OÝnÅð§l£·•ÂÙðþÑï~ÓÊD›H<ÿöÑjlÝ=îz5¹¥É•åiV”¢s©u„•ÂúκHµ²õ‘Þâ1ŒnKÓo= 'åÕb8¦£ÞÞÖZÃùOÅzt\jÌàë ÎQWÌ9ZÐâõœÃ{Õ8ê}+'³’ŽbÅ®æèZ3«5j¬5n4î ††qØd0û>>|òG]@ç¨Ýlœœ)Äs©S€§SwWlH±C\ÓµžŠ/µ¸¹"3+2ã°"3ouãðæ3+²ðÑ·¨Éêï·M4'ø¸ÊÔcjQšL9®Éì3lÍÅÉ5™k2ã°&3k2›¬ÉšµÊ œõöðøZÕJá´ªu?e 6¬µÞ”,/¼…´—X?ÔµX³“ØUÓº‰Þ:Á­«¸4#Þ¢š3³Æp%ºrØz¸Ë–R¸T)z]D)þž–st-+·àBå°o®¶7¬—Ôœ²I¡:ÏäÝÛBwœÍdk,ø£ ÎÑ-gSÙ[–¸„Ý0zÀ¦²×IÕ2¬·º°Õ¦6o ˜RŬØs"ã¢Ô <=õ£Rxöµ‹x‰è|Кb¸HÀ†F mÃzÓ?›q·BêJQÐ(oе³ÝwÃzV9‰ü§ü½{ÊØÔ¶ÊúÔ†sø<ÐÒ¬gmµ`ݧ‹cúż¦õÞ^ÒõÃtÓ8Ô»¾ƒGgžÌPí#z„ŽN_†¢%)†áóǾ£{Ö91ÿÒ”¾ ‘º3o 5§?¦ÿÄ#T ß1/ÁðQäÜsO$×8j¿vÓ#Ìg?ÆárÀŸƒk±×`—;i`øc—ÿ¼°é3ÊâÊ|±Ã=“ä¥8ˆÃ3o =Ê ä{ÉÚ£GàØ›|ƒü­=¾)”Â…™™yqÙ[äÙÓxiÆÄÀ1qúZÔ²ÇñÅmW†h”ÚAlø¾µØß„u kB€¸BícQþ¬¸¹æÖ«J4tfÜ"iÕ,Áú–SÞ…·Á®ÒÈ›Ï]˜JÞ@®Úô=&ú¾­7¸ðcÔ@¾ÊÛcŸ˜À:Âf?Årž˜\¬ö61|# 8á7Õµ”y€ÊÑ2$㸠lµ›|‰*ˆ+¼£Ížò›Šž4Dò¡X~^£ü04ð©gòÙd÷åøíËß|Û—Wí² õöÆ,ZÅ7“‚,…Qé[iÒ°fkj<åÚúÜâ[{|ëÖHȶ…É[øÆã6BøÜAžRñ‡˜ÄõeÒ¬žaø‹Ùoý<{ òwä ä¡'{?Ÿ+ ë(W†e@v¢£¸DóêûÅ5·¡ƒ8n¥àÀT×z‡7¯Ç‚ ĵ,kdþ¨WpÂ?·›=ÅV¬bÜ51ÊaÏfE"²ýÔ:é¼%t>¹9ê]å‹Ô$OQ%g7! ž|3o{ «7gt¥Eo¢ ·?ÄÏAì…*xðí¤ôiÖÕ8cYš5>±aá­§\³Y [zÞU¾ÜB¡e–¾UZbÁ@îÞ)Èûÿ0_gó~Šq£T9®ƒëלf8¾¬=™—)’/¶–O®Ú¬A>©V¿Ç…o 7K-¦‡ýtoû£v;ŠNbGÝ9\%â$A9Ø•"~S{Y4ùâǾųäÎîÀw)ßÀî~ÄŸ¶z¹>ÖìÇÄ ``»E~Èåh ßÈåU‡O€Þ­ï脉c#ä NÁ‰À@~ȹâ0°+މÀ.LðÀ@~ØUô³ù.iL]9žÜs0°»&»ûqâ0«cË)÷¸ #·N g?5¤†ó”–Þ¬nìubk° å{¸›Sö¹-¦àÉCMZÀ¯ˆøù޲›c¼ùgsƒø°ñ³ÅXÈi /òqîMA¾‡G}').kËpXVRvF´ÄŒÔ9i“g' |aݯ VÄŠM±ÎžO´7Q£ã o¸ø±FUqö­AlM)Èó!Þ ÎÚšÁ™ãxGív 6¥ l·¨;8ÏõŸ5Þ$ßúvYÛ >;|k‘/%–î]zàDQ‘‚zÊrè\ŒÖS\½h-Nè ë*—¿€Ý‡þ£uk=Ýšë¦)‹‰+Ö^sFüÊÛ”r•ª¹é g ?ùl¶Ç‹ Ä _çžC,[6påûBÓ¶<ªé vl ä&¸¾ ƒgFoa¯Ö89¾c ÅÆÇÝoñ}q”X1¾íÃR3ãÃ/¾;ˆŸ%ò%ƒ?ð÷Ö"¾èkàÝ\ÝËќձóƒÍ*=p­­Ë±»rðûo ޾ØÇq"ŸTn¼ë=J³3Ç^”Ãï¾ °)Ãqt§ôh®Ó?Küâ mq œƒ8+5¬Bˆ«F«gÁêÆ’Ò|¡Zå?mlbâafYèƒþ=l"²(Ü͆~·[íšÅÐÒÀrÂg×{U<¶¤ßWî57ñHÒ¨ó;K^3õkc9Œ]'†²Fl&†|fè+øo 8³]x½Ææ”ÇyV ñãh †aØ%]ëŒÂ ‡…oýäù·2 Ä’W ?þkþ‚s8Pãó‚m6ŸK-ê±l’Àg¶q8üaSŠÛªÁ}, Ë à-¡±ÊAúxyÑÊ:ÊNpJF9l[?¹ÒnbÏë…:®c”Ã÷S•Ó=?Ñ /ò¹®0ŸKÚSüð”q]Qp]a¾Öì2?í©2.Dk‘oCkñ)ýôT_Pµñ·}¼¹Æ°ùñk`SŒƒ¿mécäK\@ü„eµtŽB([æ!ê}Q. áx˜r¸ßÆÇ·¾^¦ìÍ ÷ÑW»¾‡=mù}*XŒy{ÜЫ/6抱+j $ºf¦†ßOT»“å%ÃŒ+'ãøádWðø"µË‚]PîQÎŒ‘'9 œ½=Ç=ýÔÄ‹ÛmïÄ“ªñG®£ê«{ýä9 垊-†ÊÃõì&† “Ín´q¡{£Í[lJpð[BÖâÅ­„p£-Û%*Eî“ØƒŠ½©Áu›Þ÷òL]4;:áªì»êbLDtßã4µqݵö⦉)Òîb›ðdmÙ4¥?x)Íj©¾õà žÙŠ*_ðÌ#ì¾ài ìÄ,~a³A>£á¥ÊÜQË:=Ošìã‡oãLuõÙbv,™Ã->ËñÝo 7‡Ã%˜©ÉÁ›x³üÊŒrÜʼS\7†ñ’ù3&ÛR0§œÄCTŒ¿Bæ V7Û¨::ƒqõf ÷ÚÄ‘ù o#¶¦Äõ þ–œ·ˆMiù%o¬m¬Á7vWw3 ä×¹±uc-ò̬·ˆÏbk‘þ òÊVkÇÛ ä:»:îjªªÁ#vò›※‚ö57òÜØÝ"~1Gø äŸ_ðùn´1òýoQ~\’ã Ø©yZCà=š œ°8öî »Õô Nä?­Å›ÿr-g ·µœºÙàÄ•&yŠh³X8.Âw«ceçIµ°Ÿ4™Û´X ßö\—G÷,[ˆŒDçØÀ IÚ»2Ý®Nh}¬iBXW¹è¾Öã³Ê—¹}á¸ÙÕ ›á“j1ŽçÜdkEc NMZ‹Žª8ˆ3w ò|nøï~åDA®¨,Þ„ªÖщÝo`s„úFÄ”0º]å.ðÞ'î4+ØÝÄœÛ-Їׂ§Â­Á oEq¸Ù@¾mˆ<ùn-Þ8n¨ ßRÞ vª}RqˆËçï ùWŽ”›XàŠåŸVpâV{b»EÆø”Z¸A†½F#¾‹ë€Ãm†]ñƒ¯T±±á >Â<úo fF²<êBÍO¦U#ü†º¼¦TÁ™®ÚEs¾öº«Y©˜Ø{wGÝd+¯ïRå¾ݾ†w¾³@κ¯yn|gwáIÚ<È«îk¤‚ÇĪ»ºƒ4'€k³å¹¸ë®Æ×þ»J†eùW=ÆÍ9#e3GA*¨aù˜’?Ôhá[RÁ‰ÏÐY‹ª°òìX“ žÚNúdvBÒ2RÍ[Xª¦Ë³c$®5çIÚ4ûdZÍP†«‚ƒ?Úlà1±è¬IlDµ…õÉ옅Å×ÎÖW>;JØ?c÷s‡Ÿø–rÒÍt•¿âè]]z-š-ì#XØ1UvÔö'-ª}rãÓ@nÐø\:yˆ_r4påf€‚"Ž™å0Ÿ“ÇsäPû-¹Å³~m%ºfÊá(»5·à¤®ƒ8²ç V6ÂH¾ðà-bµzªË¢EöyvÔM’焎‰6þ¢¾üÁq%ŸWuÌpNÀÜšòÐoçUn…–ø(WpâÕYq8ÈÀç¤Æ‹‡6Äg?c€E9¹•±qøÓm6>þ¶—ƒØYQ?S­>ݬ›¼ØÅ@š7ÉóÇc½EülƒüñXoßʳ1òÇc­Eþx¬ïC~¾Y©tSŽ3šßºÚyÍ’Iƒ_?PðÆFªrü _6Í]<ñz¬ƒÍ…:ñz¬ƒø’³ƒ¸4ßAœ`;»o蜖}ä Nƒ8âÝäãÔ^ræ6JH惸âäžjÔˆ_VPCcYþ)lÄÁ™¹±òZ\ fàÄ7IÁ™“ªpÍ QΛpÂa°q½“ƒ|S…PÜLW/~ô¬ƒxù%Ù3-ç¾0™»Ú­¶eÍeA†ÌsÞÈw=9ù b#× IjÒúâÓ²Ýyz¬E\ÕqÖá¦5võzÕ³§õ²œ5vp½Å&8±v. H¬:ÑB*ؘ³eŒq8ˆ½g&'¤¬óä˜×m2gºjV 6X®:'›‡hÏb)jzôE'ךãñ4ñ[N òr`k)Ú@n(È=Ná^v‰PâÛy+†ÏÎ䮑¼èÀAQõQò%Wwf¦Aµ!òîuÔ`nÑ|.ò)·™öøEyPÁ‰¹«érXOùk^ÖS8²%Ý1ŸbÌZ<¸YÅÕŽCŒo_–Šn©|¼ÈÅòâyˆÖ"vÇ®®ßp©E½c_õ2·¯ñù+îVk“¿Ìe FÆA|þÈߨñ1bõï]Åž‘üÀqë*#öþÔë`Qx‹ÝÉ8ä-vA^ZëâÀ–£Í*ÿ¾–µ¸v·óÆ@þæƒØ<¶Éá8¡‘•äOÐ.Ûã·eÔ¹²2«ÇàSG[ÕÁçˆIuyPÅAœÆ7PK«'ä¡F@´¬œY8Às>À‘—ª=ôಠEçx^ã/U[s‡|cØ&pÂÙÀæq‚bw›ø ŠƒØ¹½ëU^2½ÕM¹&ÚÜ ³ˆöü¬EþBÂ]~b"Ë0|b"¯‹ÁàSñ3·ÖU~GÞ@ÉŠN€¼?py¹…¹f„Áßcrñ7× 7ÀtfzÃ+PŽ™øå*Ñ@®u>ùS×_L¬/fP«!x¾÷¶Â ~üÚçE¸c¢]•Dq^nÝÂÛ‚¯8n£àDÜæ®ÃoYö€;÷/äOFYƒ<|YÚ@j¿M'uØ”}˜7° bbq[J ÛUü pñWW\úÍŽÞ4xò²þ…Å›`8‰lœ.Ò‰i–ÌÛÃõn>/8—ð6BZ'aàMíÞàÄv²!òCF{JƒQÞ`wjf6~¨!žY7ö¥e„èÕ,÷n‹£»õñëÇoâàZJAìs¿‘Ÿù¶9ÚKîÙâÄ®{hfßÜgÓÜ®Z3Àœ͘u¸}†Vùq¨ÖÁ ÿY3_|…Û‡ˆºCœð„CYþÔÜðnwhÖÌÀ -. žƒ&h“ž×©r<>¤—¤ù*µ+Ëü@Õi ˜qü9ù¿8º]=¹…ªYATÔ9åvŸp8_úa!Làú%pn€Ûín€—NfÚ›0À튄–-8a€Û5nn€kƒ|Û‡+5ùt²¼5×À&Å®ø'ìhíªTVMLêŒm`sKÍØÑ&ne(8aG7_ }[ãí%Ç­ŒºTbf7NØÑ–òæsc• Ü^°› ª*^œ\ù1lݾâKuã¥ùY²Žg#ì*V¯›q>åøé¦ÃãN›M'-ô‰áA"áΦüÎf{¸´î£èïgsÏèbiy¼d¤7é ÄU@Îá£t©Óùñâ‹"Þ ¾ õ>µLºabàÀwÌ <±o¹hží93yjF=5Yˆ£žš‰eçönЇõ…bÍÖ>¼·ip¢ÖÁÀ Ÿ½ÖU^vb-òÃÉÇÈ÷¡|± ȃX¶hºSƒ ª?ŠË?@Üž¥>øaa ®vPP>491À‰5£A.‰:^žûiµûØÒ[BØ;ÏKˆ{sná¶åÒ BÛÔàâ]ïéľ!áΤ†EtÕ““׸½bÙlq¢úÓºÊ#n>98á Në;Èm”úêÆ‡_¢pW×Å„Ígïûàà§‚ünŠ·ˆï²È¿îôQ6òK~)Ç&Îo±ö7» 6ÂïDñ©á]ÅRËßä7†úú|j4¸Ào| s—&øÂ—!~~Þ¸‰ø g¬›Ü®qÉsÝf ¶ù¼«ø 6× ½«ÿl0¯6õMñ»`£ö¡'8½ó11Àš×Η›|›×ŽsB NˆZÆ÷…9Ãx‘ª‹ÙknB“Z0ƒK°QMöÔBsf&v“}<–¯˜¿ˆ_ò¹üp2°½º›]1…MþÜ2«ùe[{³rÝÁî"Çù»·h¦>® 2[4â¾¼Gwv¸å ¾æd}Ý›-Ž‹ëF“×ýÖ"_¬–³åºÃòÒÍÅ:á{ûäð“Ê@œG5úïm•ãª"¹½©/,>æ¸ð"FoÁx༦‚ú-­¬9¬‚ùÙ` L8a:¤×²<ìF=×ݪéM]E>9!ç“'ÇÒS;ŸíêÆuN¨¸Í`]Æ{º«Ópàž*ȵên©ì†íuø=sK³£uÔ>s]ëxW똟ââ3UA™šxÜ(÷±öH ÜHqnã&6×Ì(Z<†&÷Ïc³Z\3O0oâ`2féÈ'Çì°&'†_VÄvÍŽ¯Ód1δÈç¦[zc ~Yþ¡f'oûð ·¨¬ô†omQ–j> Äâ8¬ž»þG¤ŽKÕ¸ÛpòÍa ¿.k äëgZäU»G¨fɦ˜’Õ^ÜÛÊ@oä¾Ö†ê'}ÝÚMv Î#„cóô¤plîk©ú¤¯G»Éð}ÅÜäY7™§Ç@ît(9Ó×€Ï-†·<5tpàìú¦@œ×3r_»5§†ø{u ê óì¸òÙ Z22øÖÜê¾æA*(a€Ot]î«z>(Ô4@^! 0NývÛçžOS®¸üÃAlðžµRþdŒê·بWPÃd¹« Øà=keþÉ5`ÍKÄö§‚¼ðÛœXãÖӥ٢Ꜽ¬¯øì3œ­¹Eu•Ä€ˆ§¹üyÕ³>Ísšü²C; WȰyJß@ž™¿jó!.ò6g/Xò©±º%. sêðSÞ">sÄ’uµ;«<óxi¶ƒ)8Q3n /!p+G›cœÙÅvÇÈóòÍ¡ /tuÛŽ¾äÚ+§Ùℵj 7ìŠqæñ•È… IäBÀ"rµM•¹`à$înÖ+Üͺƒ;8ã‘ áÃÈ5ÚmõxØ\¸?½‚ÿ›_ýN‚ß 5igraph/inst/tests/football.gml.gz0000644000176000001440000000706712263023733016630 0ustar ripleyusers‹ ðÁDfootball.gml[oÛÆ…ßó+„<œgQE²oŽÛ´iã$hÒ}˜XŒDX&RJêþúCÇvªo$./(^ÖìÙ³g;͈=o˰kÚÉÓ‹Ð^M^•_®C=iêÉÛ°›üºßNf³É4ýa>û!YNfÓéòé“u>mžüïÉd²ªÚòrW®&ÓþêfUöÜþûɤºûw“É6|(·“§ÏÚj½ ×ÿmöõúé×ÿð9l÷å$ëÿþ¯ƒ“ÃÁÏ·M[­ÂÛ]Ø•‡ƒ§ƒg‡ƒ_4_Âá ÙÀ ùá ßBÝ…îhÂùÀØÅáØÞ„åßÕeã¬3=ù®ü;tïÊË3çòp䛲®´Ziv8òm³ßmʶ>ÛêcÓÖŒ•ˆÈEœµÕ?M}¼=Cƒ Ìê«rÝdynzr8öJ@©WM»Û|µ÷áØdˆQÉìhpo³ÛmU7D,‡$Ì$|)»]/ÅÙ² {7ð¢ºÜTëP[Óƒhï«î²©»Ê›L{Ó\WæÑÎöö\l14Žìºjo‰àÕŸU»®z6ÇÇ)ò ÕYÿg¸޶3ðêó—gΘP÷'Èý± g+f NwuÚ¿ òú|eïyh›žþ#¼2xô,l·GC‡¶v&݇ks@¥gýY-=H‰‚ƒ»]SŸ7Ûm¹.FÍÁ¨Û3ûÀIktÂÉ¿ô¶^ÿܖ屟²Ú;å-c!HvJí¡MžƒhÏö?†mc) †½½iÃå¾óì ^—õ® ÛI‡Æƒ\?—M¿Ú#·1¸ÞüÄäcó${³oW{+ˆ/À®ž–MVÉ©‘vü]Ì8¼®û°ºÜï[/@¬ŸÂè8¶XÄüÐáèÅÐhìÇý•å¸ Øó¶ìêcs z‘èõzSÜcë—fë¬u‚T¿W—žª)8õb6R0ê}è6½¯ŠTŠg)èt—w;,NÁ¤‡,ö¢ÜmšUÕí¼õ‚L¿õ‡×¡` ½©v»®ÏlÖV–‘.ã÷—W7N$KA£—;ê>W},rèæGc«P§FƒZ'Ç_4uÛxùÃ̺¨z§Ñ5;«.[&®«ÛCäìÔÜú3Ô«²ýPmw޽—sN»ZmËwå­Þ]y"‡\8(vª`\wJºîö¯OŸª£É—²]”ן6œzˆ/KpíUù90’e ¢½nûºÎrË‚Þо™3ðë«78†,•c.½²£>ƒÝ0Èè½Býk3&3Í¢†Ã‡6tWÁš8=2Ô×ü]?2ëÛ©°,ÍæÃ.Ô›våp$·þ 2vÛ!+¨õõ§­•_æàÖ«ðþz(ÓÉÎöwèÎþsálQõvmùc¸.­IçG“þ´}ºÆbUV½¾ÚöñÿÚbUÎ6V³-WÖ ÊI¤ý6ÔV@ËÁ¢öQ0Ï£I;ϹåtR‡5°“E Ñmá~ÄÜ¡m-À¥³öúÆ1RÁD½ª/û°vÖ /@¤³ª}Þ´—VyP€D¿ïwë²íœcV€C÷e˜³ŸHô-;±ÃdqœYÝŽ>†›rg ‚¤Š‹C§§.BÛmÂvku»¦QJuWCÙµE2=ΫNÍC»•L£æB×5û¶rÖœLA®ómyÝ1MìÂN£¸w›˜¼ ÝŸåšæî³ ®ÊžØ%Ë…áÙA´õê–&ž­YÞ{V;[H¦'ò*;î%Ó˜d7½Ÿ]YKf³ý+­Ï7m__UÁ*DvÝÇ]2$ìºß÷dŸUímz®=èÉî;ŸVšM÷_—PU'#E¹Zÿ;òö\–÷X»Ð®ËÝ7ûžÎ3\¸ÓCàÂÎpéꘗ˜3,¹ TæP™çžü÷ÈT!±Û‰šP_¨ÚžJÔ%6Ý’Å÷!õô \"­O¨\j« &Ëé,l¤Ü{D*×”ûPp?QÇiò'êˆÎ@Ô™²¡r~ åô3w§ˆôeΔ£'T.‰gOm¡rM@*F)ÙG¨ÜPž}i(žR©*ϾBâð)'A¤<&TjŠs:“Få‘–ôËìõ ?!E"çTo*"õÎgæCµTFiuTç8Ôse¹ï*MTð ŠØ„*ÎícM¤\š\…Ê­‚[Ñögž"×ï'*Tr…i´:„Ê  ³”¶¢³ô¡s©ÀÒ7€ïY •©B$U. Np.)`{V"5±9¿<¬t­Ò¬„ÊUÑ ûRõq¡’Ø~:G˜K]•ªR¨?¿ö¬„* ,†T&RM¿@¼Z¨Ã²°óêH¨œùšª£º°û"DJú/­å¢­¥ª ÁÒ¨”*ue¾.>€J·Fèdh Ò~ "‚ÊÝb–»ÅŒAžUB% ÙK“ºúý Be¼XØ•P$Tº &ÒV ØRDÁ…ä c«d‹ÝR$R{kÎo#ç’„J©,Å$­Ø®”gЂ‘TeÖ”ñJ6žY¶ÉÖ3Ë6Å«Hª²@ÊæªÔÕ®©\‰”¼Ž Ò o6R/ PuXRøëT.Ê/ •¼Ž ê FPÉkF)Õ¯F#¨\ƒ›<-”êCeÖ’" ¤Rjê[P¹*Ùâ—é_§~9œ2ªèš"¤Òv5J¤L#¨ÜVB%Y²¤R.ŸU£4*§—R‹ï„¦jýK^*Z*o •NˆPµ*"ʬ‘ªò˜PuZ U\]2 ËùULÔ Xú=aBeE¨t—TêÊF¯”Ê‚TR9ƒÜWVÄr ¨€•þr9÷¥2k‘K¨<„~õ¾D ^Jj*çg"0b~Iûª?j#õqeÊ"-e7ЉÔTaÆàCõ¦2»’¶¢®rYÈC–ò´ªiÍìF:!¾ •GÐAèBzA¿#Ah*7–R¥]™ If1’ ¨¬r •Uf$Õ‡ú«’1¡’,ö ."5¯ õç×öç ‚2jÆüNÑŠP™5ER­•^ˆPéÛ"äãA* x•!k’Á-ó3¼ªè’ùi[æg£‘T_W™ f~6šñ…”•ûÊ´Qn–ÝêÊüV¡²•!¿“ ô›R„ÊØB¨ ™ßêÊ`eR;Œ„úP郘 Ê]¥P~ùšU~ÒFèRn/œ¤`‚)¹â_ceHÚ2¹Y~O,ósÑHª´+uõ¡z ¨«ÕÁ Y£>ƒþíX$ÕFÊô.šß‡J¶2g”ª2i”¦òSáHª¯«Þ;k%r9ÂVþúåV±-êCu~I©rü÷Ÿ„Êv¤€ôWÌÛ4çs¥¡òÞP¹¬ÜýB¨\•çÍïCe"@¨âUÎWõŠ­„f*¸*Ûí‘T¹­ÔUÚŠ øPMûw ¹ŸŠªw€Y«T• ¶¯€Lps?mÎý¼=G2˜K]™6K.¾S™âævŠK¤>.~6ÙJ²Å®F,ʾt&RÖø„ÊjœP1 ÕÞ‚ºÊýg5â[@&¹ßì%T{¿Äˆ¤Êƒíw› Õ[à× 9sa*o•%¡²p!Tû+B¥±¨«ÜX¾€“^9®ÜXfã’„LÇ}t ¢®¾:ú×9/-Å|ÜFê—™»¤¥JŒÈÜY»(haÿ¾›Hù+t¦øjQ„Ê_¬û%£5ùR¥ T–Hª´TH©¬\|©ÒcGÐ Hcñ¹¨¯€ o‘T¹±üA´¤ ëyü/ª-À6¾ÜX¶ñ¥±ü—2Ô×Uæ-…ýKK"µÃ`EæK•íB%±9¿JEÕäü>TWÿÁ,¡²-E¨ C„Ê¡z[Yfް€/Uï«ÿ+›e†lΪý0¥JbSª\–ÿV‡PyéA¨Ìò Í}¨LÆ ¿$"TëÊòMË¿!*ü[Be™) IÈ¢PêÊBËFêHH¡r³X>J—I©òh±z“‘I!måßûªéÊBOúwJ•à,*)@¤tC\•ÜWÖyr_YgI[±$ô¡¹´«· H ø÷Y„êtPùf„±>Öøp`ä礈•á8Âêd;F¬üJ±òIr„]Ž0™|@ë0¼ò\S–gòsÄÊ|+ÂÚ ¼ˆó¡2猗¦Z¬Â¹š94Ù¬Ì|>MúÍë½ðo#¬tå¶Ð¦#äÊpvd« ‹Ë©Ö—ß!Xöî"¬l4FX,c}µÛ±¿’Ae5ôð•[þ÷×"ì ½küMˆ¶/?G¨«¿–H¬LÞ#¬ì¸ÄúŽ0™„²Ú½ã‘ ~ä°ùåq,W›—W¡ú`Ú_·ˆm¦Ùëßâá£Îß•5j„-´yíïëFÐG>~q„\ù >ÆŽ€êĥ؉G>Öà:ó=¥ÖØG"<+öGB&•}$ !ôq³[Gk¡‚æƒý½ÊªÙ`¿­Œ ¤ÿ~›#ÂÊ÷µ±\M2ÿÙj„•¯Q¾ïÕþ{Ð+NV›Ì¿WŽÅ>]#°>þ=t„}$ùŸØ’”üعÎâ’]bå-s„Õ1>ÂJ+›DXù,#–+#a¤ï¹òI¯Äël–X‰}dml<è½°?¦/MúdD«&RׇÞyM×¼#°:^$#ÚÄzÛˆ£Ã¬¼R±š¾l}húrm#l¦“¾H®æ[šÄj×Çkp­¯ÿm‹«{ÑÿrBóí—Xy¹aå=`l3-×÷cõG½m`¾5¡Ä#ñÍÿ-h,w„Ñɸ¶1FÓ›½W¿ó=ù?og@ðã‚igraph/inst/tests/test_is.chordal.R0000644000176000001440000000212212251656216017106 0ustar ripleyusers context("is.chordal") test_that("is.chordal works", { library(igraph) ## The examples from the Tarjan-Yannakakis paper g1 <- graph.formula(A-B:C:I, B-A:C:D, C-A:B:E:H, D-B:E:F, E-C:D:F:H, F-D:E:G, G-F:H, H-C:E:G:I, I-A:H) mc <- maximum.cardinality.search(g1) expect_that(mc, equals(list(alpha=c(9,4,6,8,3,5,7,2,1), alpham1=c(9,8,5,2,6,3,7,4,1)))) ic <- is.chordal(g1, fillin=TRUE) expect_that(ic$chordal, equals(FALSE)) expect_that(unique(sort(ic$fillin)), equals(c(1,2,5,6,7,8))) expect_that(ic$newgraph, equals(NULL)) g2 <- graph.formula(A-B:E, B-A:E:F:D, C-E:D:G, D-B:F:E:C:G, E-A:B:C:D:F, F-B:D:E, G-C:D:H:I, H-G:I:J, I-G:H:J, J-H:I) mc2 <- maximum.cardinality.search(g2) expect_that(mc2, equals(list(alpha=c(10,8,9,6,7,5,4,2,3,1), alpham1=c(10,8,9,7,6,4,5,2,3,1)))) ic2 <- is.chordal(g2, fillin=TRUE) expect_that(ic2, equals(list(chordal=TRUE, fillin=numeric(), newgraph=NULL))) }) igraph/inst/tests/test_watts.strogatz.game.R0000644000176000001440000000044412251656216021013 0ustar ripleyusers context("watts.strogatz.game") test_that("watts.strogatz.game works", { library(igraph) for (i in 1:50) { p <- runif(1) d <- sample(1:3, 1) nei <- sample(2:5, 1) g <- watts.strogatz.game(d, 10, nei, p, loops=FALSE) expect_that(any(is.loop(g)), is_false()) } }) igraph/inst/tests/test_transitivity.R0000644000176000001440000000132312251656216017633 0ustar ripleyusers context("transitivity") test_that("transitivity works", { library(igraph) set.seed(42) g <- erdos.renyi.game(100, p=10/100) t1 <- transitivity(g, type="global") expect_that(t1, equals(0.10483870967741935887)) t2 <- transitivity(g, type="average") expect_that(t2, equals(0.10159943848720931481)) t3 <- transitivity(g, type="local", vids=V(g)) t33 <- transitivity(g, type="local") est3 <- structure(c(0, 0.06667, 0.1028, 0.1016, 0.1333, 0.2222), .Names = c("Min.", "1st Qu.", "Median", "Mean", "3rd Qu.", "Max."), class = c("summaryDefault", "table")) expect_that(summary(t3), equals(est3)) expect_that(summary(t33), equals(est3)) }) igraph/inst/tests/test_graph.edgelist.R0000644000176000001440000000120012251656216017754 0ustar ripleyusers context("graph.edgelist") test_that("graph.edgelist works", { library(igraph) g <- erdos.renyi.game(50, 5/50) el <- get.edgelist(g) g2 <- graph.edgelist(el, dir=FALSE) expect_that(graph.isomorphic(g, g2), is_true()) #### g <- erdos.renyi.game(50, 5/50, dir=TRUE) el <- get.edgelist(g) g2 <- graph.edgelist(el, dir=TRUE) expect_that(graph.isomorphic(g, g2), is_true()) #### g <- erdos.renyi.game(26, 5/26, dir=TRUE) el <- get.edgelist(g) n <- letters[1:26] names(n) <- 1:26 mode(el) <- "character" el[] <- n[el] g2 <- graph.edgelist(el, dir=TRUE) expect_that(graph.isomorphic(g, g2), is_true()) }) igraph/inst/tests/test_sir.R0000644000176000001440000000063712271600260015654 0ustar ripleyusers context("SIR epidemics model on a network") test_that("SIR works", { set.seed(42) library(digest) library(igraph) g <- erdos.renyi.game(50, 50, type="gnm") res <- sir(g, beta=5, gamma=1, no.sim=10) if (.Machine$sizeof.pointer == 4) { expect_that(digest(res), equals("b73a8ad03b832b3543f2f03d07330398")) } else { expect_that(digest(res), equals("bc42d0cbe0bb3321e83979c0432f9cea")) } }) igraph/inst/tests/test_get.adjacency.R0000644000176000001440000000125312251656216017563 0ustar ripleyusers context("get.adjacency") test_that("get.adjacency works", { library(igraph) g <- erdos.renyi.game(50, 1/50) A <- get.adjacency(g, sparse=FALSE) g2 <- graph.adjacency(A, mode="undirected") expect_that(graph.isomorphic(g, g2), is_true()) ### A <- get.adjacency(g, sparse=TRUE) g2 <- graph.adjacency(A, mode="undirected") expect_that(graph.isomorphic(g, g2), is_true()) ### g <- erdos.renyi.game(50, 2/50, directed=TRUE) A <- get.adjacency(g, sparse=FALSE) g2 <- graph.adjacency(A) expect_that(graph.isomorphic(g, g2), is_true()) ### A <- get.adjacency(g, sparse=TRUE) g2 <- graph.adjacency(A) expect_that(graph.isomorphic(g, g2), is_true()) }) igraph/inst/tests/test_graph.kautz.R0000644000176000001440000000141612251656216017323 0ustar ripleyusers context("graph.kautz") test_that("graph.kautz works", { library(igraph) g <- graph.kautz(2,3) expect_that(g$name, equals("Kautz graph 2-3")) expect_that(g$m, equals(2)) expect_that(g$n, equals(3)) el <- get.edgelist(g) el <- el[order(el[,1], el[,2]),] expect_that(el, equals( structure(c(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1, 2, 3, 4, 5, 6, 7, 8, 17, 18, 19, 20, 21, 22, 23, 24, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), .Dim = c(48L, 2L)) )) }) igraph/inst/tests/test_cliques.R0000644000176000001440000000132412251656216016530 0ustar ripleyusers context("cliques") test_that("cliques works", { library(igraph) set.seed(42) check.clique <- function(graph, vids) { s <- induced.subgraph(graph, vids) ecount(s) == vcount(s) * (vcount(s)-1) / 2 } g <- erdos.renyi.game(100, 0.3) expect_that(clique.number(g), equals(6)) cl <- sapply(cliques(g, min=6), check.clique, graph=g) lcl <- sapply(largest.cliques(g), check.clique, graph=g) expect_that(cl, equals(lcl)) expect_that(cl, equals(rep(TRUE, 17))) expect_that(lcl, equals(rep(TRUE, 17))) ## To have a bit less maximal cliques, about 100-200 usually g <- erdos.renyi.game(100, 0.03) expect_that(all(sapply(maximal.cliques(g), check.clique, graph=g)), is_true()) }) igraph/inst/tests/test_indexing2.R0000644000176000001440000000563212251656216016760 0ustar ripleyusers context("Assignments via indexing") library(igraph) am <- function(x) { x <- as.matrix(x) dimnames(x) <- NULL x } test_that("[ can add and delete edges", { g <- graph.empty(10) ; A <- matrix(0, 10, 10) A[1,2] <- g[1,2] <- TRUE expect_that(am(g[]), equals(A)) A[2,1] <- g[2,1] <- TRUE expect_that(am(g[]), equals(A)) g[2,1] <- NULL ; A[2,1] <- 0 expect_that(am(g[]), equals(A)) A[1,2] <- g[1,2] <- FALSE expect_that(am(g[]), equals(A)) g <- graph.empty(10) ; A <- matrix(0, 10, 10) A[-1,1] <- g[-1,1] <- 1 expect_that(am(g[]), equals(A)) }) test_that("[ can set weights and delete weighted edges", { g <- graph.empty(10) ; A <- matrix(0, 10, 10) g <- set.edge.attribute(g, "weight", c(), 1) A[1,2] <- g[1,2] <- 1 expect_that(am(g[]), equals(A)) A[2,1] <- g[2,1] <- 2 expect_that(am(g[]), equals(A)) A[1,2] <- g[1,2] <- 3 expect_that(am(g[]), equals(A)) A[1:2,2:3] <- g[1:2,2:3] <- -1 expect_that(am(g[]), equals(A)) g[1,2] <- NULL ; A[1,2] <- 0 expect_that(am(g[]), equals(A)) }) test_that("[ can add edges and ste weights via vertex names", { g <- graph.empty(10) ; A <- matrix(0, 10, 10) V(g)$name <- letters[1:vcount(g)] rownames(A) <- colnames(A) <- letters[1:vcount(g)] A['a', 'b'] <- g['a','b'] <- TRUE A['b', 'c'] <- g['b','c'] <- TRUE expect_that(am(g[]), equals(am(A))) A[c('a','f'), c('f','a')] <- g[c('a','f'),c('f','a')] <- TRUE expect_that(am(g[]), equals(am(A))) A[A==1] <- NA A[c('a','c','h'), c('a', 'b', 'c')] <- g[c('a','c','h'), c('a','b','c'), attr="weight"] <- 3 expect_that(am(g[]), equals(am(A))) }) test_that("[ and the from-to notation", { g <- graph.empty(10) ; A <- matrix(0, 10, 10) V(g)$name <- letters[1:vcount(g)] rownames(A) <- colnames(A) <- letters[1:vcount(g)] g[from=c('a','c','h'), to=c('a','b','c')] <- 1 A['a','a'] <- A['c','b'] <- A['h','c'] <- 1 expect_that(g[from=c('a','c','h','d'), to=c('a','b','c','e')], equals(c(1,1,1,0))) expect_that(am(g[]), equals(am(A))) g[from=c('a','c','h','a'), to=c('a','a','a','e'), attr="weight"] <- 3 A[A!=0] <- NA ; A['a','a'] <- A['c','a'] <- A['h','a'] <- A['a','e'] <- 3 expect_that(g[from=c('a','c','h','a','c','c'), to=c('a','a','a','e','f','b')], equals(c(3,3,3,3,0,NA))) expect_that(am(g[]), equals(am(A))) }) test_that("[ and from-to with multiple values", { g <- graph.empty(10) ; A <- matrix(0, 10, 10) V(g)$name <- letters[1:vcount(g)] rownames(A) <- colnames(A) <- letters[1:vcount(g)] g[from=c('a','c','h'), to=c('a','b','c')] <- 1 A['a','a'] <- A['c','b'] <- A['h','c'] <- 1 g[from=c('a','c','h','a'), to=c('a','a','a','e'), attr="weight"] <- 5:8 A[A!=0] <- NA ; A['a','a'] <- 5 ; A['c','a'] <- 6 ; A['h','a'] <- 7 A['a','e'] <- 8 expect_that(g[from=c('a','c','h','a','c','c'), to=c('a','a','a','e','f','b')], equals(c(5:8,0,NA))) expect_that(am(g[]), equals(am(A))) }) igraph/inst/tests/test_graph.compose.R0000644000176000001440000000054112251656216017630 0ustar ripleyusers context("graph.compose") test_that("graph.compose works", { library(igraph) g1 <- erdos.renyi.game(50, 3/50, directed=TRUE) gi <- graph( rep(1:vcount(g1), each=2), dir=TRUE ) g2 <- graph.compose(g1, gi) g3 <- graph.compose(gi, g1) expect_that(graph.isomorphic(g1, g2), is_true()) expect_that(graph.isomorphic(g1, g3), is_true()) }) igraph/inst/tests/test_graph.bfs.R0000644000176000001440000000121512272210517016725 0ustar ripleyusers context("BFS") test_that("BFS works from multiple root vertices", { library(igraph) g <- graph.ring(10) %du% graph.ring(10) expect_that(graph.bfs(g, 1)$order, equals(c(1,2,10,3,9,4,8,5,7,6,11,12,20,13,19,14,18,15,17,16))) expect_that(graph.bfs(g, 1, unreachable=FALSE)$order, equals(c(1,2,10,3,9,4,8,5,7,6,rep(NaN, 10)))) expect_that(graph.bfs(g,c(1, 12), unreachable=FALSE)$order, equals(c(1,2,10,3,9,4,8,5,7,6,12,11,13,20,14,19,15,18,16,17))) expect_that(graph.bfs(g,c(12, 1, 15), unreachable=FALSE)$order, equals(c(12,11,13,20,14,19,15,18,16,17,1,2,10,3,9,4,8,5,7,6))) }) igraph/inst/tests/test_get.adjlist.R0000644000176000001440000000203412251656216017272 0ustar ripleyusers context("get.adjlist") test_that("get.adjist works", { library(igraph) g <- erdos.renyi.game(50, 2/50) al <- get.adjlist(g) g2 <- graph.adjlist(al, mode="all") expect_that(graph.isomorphic(g, g2), is_true()) expect_that(graph.isomorphic.vf2(g, g2, vertex.color1=1:vcount(g), vertex.color2=1:vcount(g2))$iso, is_true()) #### el <- get.adjedgelist(g) for (i in 1:vcount(g)) { a <- as.numeric(E(g)[adj(i)]) expect_that(length(a), equals(length(el[[i]]))) expect_that(sort(el[[i]]), equals(sort(a))) } g <- erdos.renyi.game(50, 4/50, directed=TRUE) el1 <- get.adjedgelist(g, mode="out") el2 <- get.adjedgelist(g, mode="in") for (i in 1:vcount(g)) { a <- as.numeric(E(g)[from(i)]) expect_that(length(a), equals(length(el1[[i]]))) expect_that(sort(el1[[i]]), equals(sort(a))) } for (i in 1:vcount(g)) { a <- as.numeric(E(g)[to(i)]) expect_that(length(a), equals(length(el2[[i]]))) expect_that(sort(el2[[i]]), equals(sort(a))) } }) igraph/inst/tests/test_bug-1032819.R0000644000176000001440000000043012251656216016462 0ustar ripleyusers context("Bug 1032819") test_that("VF2 isomorphism considers colors", { library(igraph) g <- graph.full(3) path <- graph.ring(3, circular=F) V(g)$color <- c(1,1,2) V(path)$color <- c(1,2,1) n <- graph.count.subisomorphisms.vf2(g, path) expect_that(n, equals(2)) }) igraph/inst/tests/test_diameter.R0000644000176000001440000000176012251656216016661 0ustar ripleyusers context("diameter") test_that("diameter works", { library(igraph) gc <- function(graph) { clu <- clusters(graph) induced.subgraph(graph, which(clu$membership==which.max(clu$csize))) } #### Undirected g <- gc(erdos.renyi.game(30, 3/30)) sp <- shortest.paths(g) expect_that(max(sp), equals(diameter(g))) g <- gc(erdos.renyi.game(100, 1/100)) sp <- shortest.paths(g) sp[sp==Inf] <- NA expect_that(max(sp, na.rm=TRUE), equals(diameter(g))) #### Directed g <- erdos.renyi.game(30, 3/30, dir=TRUE) sp <- shortest.paths(g, mode="out") sp[sp==Inf] <- NA expect_that(max(sp, na.rm=TRUE), equals(diameter(g, unconnected=TRUE))) #### Weighted E(g)$weight <- sample(1:10, ecount(g), replace=TRUE) sp <- shortest.paths(g, mode="out") sp[sp==Inf] <- NA expect_that(max(sp, na.rm=TRUE), equals(diameter(g, unconnected=TRUE))) #### Bug #680538 g <- graph.tree(30, mode="undirected") E(g)$weight <- 2 expect_that(diameter(g, unconnected=FALSE), equals(16)) }) igraph/inst/tests/test_biconnected.components.R0000644000176000001440000000136412251656216021530 0ustar ripleyusers context("biconnected.components") test_that("biconnected.components works", { library(igraph) g <- graph.full(5) + graph.full(5) clu <- clusters(g)$membership g <- add.edges(g, c(match(1,clu), match(2,clu)) ) sortlist <- function(list) { list <- lapply(list, sort) list[order(sapply(list, paste, collapse="x"))] } bc <- biconnected.components(g) expect_that(bc$no, equals(3)) expect_that(sortlist(bc$tree_edges), equals(list(c(11,15,18,20), c(1,5,8,10), 21))) expect_that(sortlist(bc$component_edges), equals(list(11:20, 1:10, 21))) expect_that(sortlist(bc$components), equals(list(1:5, c(1,6), 6:10))) expect_that(sort(bc$articulation_points), equals(c(1,6))) }) igraph/inst/tests/test_contract.vertices.R0000644000176000001440000000137012251656216020524 0ustar ripleyusers context("contract.vertices") test_that("contract.vertices works", { library(igraph) set.seed(42) g <- graph.ring(10) g$name <- "Ring" V(g)$name <- letters[1:vcount(g)] E(g)$weight <- sample(ecount(g)) g2 <- contract.vertices(g, rep(1:5, each=2), vertex.attr.comb=toString) ## graph and edge attributes are kept, vertex attributes are ## combined using the 'toString' function. expect_that(g2$name, equals(g$name)) expect_that(V(g2)$name, equals(c("a, b", "c, d", "e, f", "g, h", "i, j"))) expect_that(as.matrix(g2[]), is_equivalent_to(cbind(c(10,9,0,0,7), c(9,3,6,0,0), c(0,6,4,8,0), c(0,0,8,5,1), c(7,0,0,1,2)))) }) igraph/inst/tests/test_graph.coreness.R0000644000176000001440000000035412251656216020006 0ustar ripleyusers context("graph.coreness") test_that("graph.coreness works", { library(igraph) g <- graph.ring(10) g <- add.edges(g, c(1,2, 2,3, 1,3)) gc <- graph.coreness(g) expect_that(gc, equals(c(3,3,3,2,2,2,2,2,2,2))) }) igraph/inst/tests/test_print.R0000644000176000001440000000305212271600260016205 0ustar ripleyusers context("print.igraph") test_that("print.igraph works", { library(igraph) igraph.options(print.full=TRUE) options(width=76) g <- graph.ring(5) expect_that(summary(g), prints_text("attr:.* name[ ]*[(]g/c[)]")) expect_that(g, prints_text("attr:.* name[ ]*[(]g/c[)]")) expect_that(g, prints_text("1--2")) V(g)$name <- letters[1:vcount(g)] expect_that(summary(g), prints_text("name[ ]*[(]v/c[)]")) expect_that(g, prints_text("a--b")) set.seed(42) E(g)$weight <- sample(ecount(g)) expect_that(summary(g), prints_text("weight[\n ]*[(]e/n[)]")) g$name <- "A ring" expect_that(summary(g), prints_text("A ring")) expect_that(print(g, v=T), prints_text("vertex attributes")) expect_that(print(g, e=T), prints_text("edges [(]vertex names[)] and")) set.seed(42) g2 <- erdos.renyi.game(13, p=0.6, directed=TRUE) expect_that(g2, prints_text("1 ->")) g3 <- erdos.renyi.game(20, p=0.8) expect_that(g3, prints_text("1 --")) g4 <- graph.star(100) expect_that(g4, prints_text("2->1")) g5 <- graph.star(100, mode="out") expect_that(g5, prints_text("1->")) g6 <- ba.game(100, m=6, directed=FALSE) expect_that(g6, prints_text(" ")) kite <- graph.empty(directed=FALSE) + LETTERS[1:10] kite <- kite + edges('A','B','A','C','A','D','A','F', 'B','D','B','E','B','G', 'C','D','C','F', 'D','E','D','F','D','G', 'E','G', 'F','G','F','H', 'G','H', 'H','I','I','J') expect_that(kite, prints_text("A -- ")) igraph.options(print.full=FALSE) }) igraph/inst/tests/test_sbm.game.R0000644000176000001440000000174712251656216016565 0ustar ripleyusers context("Stochastic block models") test_that("Generating stochastic block models works", { library(igraph) pm <- matrix(1, nrow=2, ncol=2) bs <- c(4,6) g1 <- sbm.game(10, pref.matrix=pm, block.sizes=bs, directed=FALSE, loops=FALSE) expect_that(graph.isomorphic(g1, graph.full(10, directed=FALSE, loops=FALSE)), is_true()) g2 <- sbm.game(10, pref.matrix=pm, block.sizes=bs, directed=FALSE, loops=TRUE) g2x <- graph.full(10, directed=FALSE, loops=TRUE) expect_that(g2[sparse=FALSE], equals(g2x[sparse=FALSE])) g3 <- sbm.game(10, pref.matrix=pm, block.sizes=bs, directed=TRUE, loops=FALSE) g3x <- graph.full(10, directed=TRUE, loops=FALSE) expect_that(g3[sparse=FALSE], equals(g3x[sparse=FALSE])) g4 <- sbm.game(10, pref.matrix=pm, block.sizes=bs, directed=TRUE, loops=TRUE) g4x <- graph.full(10, directed=TRUE, loops=TRUE) expect_that(g4[sparse=FALSE], equals(g4x[sparse=FALSE])) }) igraph/inst/tests/test_get.edgelist.R0000644000176000001440000000035312251656216017442 0ustar ripleyusers context("get.edgelist") test_that("get.edgelist works", { library(igraph) g <- erdos.renyi.game(100, 3/100) e <- get.edgelist(g) g2 <- graph(t(e), n=vcount(g), dir=FALSE) expect_that(graph.isomorphic(g, g2), is_true()) }) igraph/inst/tests/test_psumtree.R0000644000176000001440000000105312266024104016715 0ustar ripleyusers context("Prefix sum tree") test_that("Prefix sum tree works", { library(igraph) set.seed(42) mysample <- function(x, size, prob=NULL) { if (!is.null(prob)) { prob <- as.numeric(prob) } .Call("R_igraph_psumtree_draw", as.integer(x), as.integer(size), prob, PACKAGE="igraph") } S <- mysample(100, 10000) expect_that(range(table(S)), equals(c(69, 129))) S2 <- mysample(100, 10000, rep(1:2, each=50)) expect_that(range(table(S2)[1:50]), equals(c(45, 85))) expect_that(range(table(S2)[51:100]), equals(c(103, 160))) }) igraph/inst/tests/test_maximal_cliques.R0000644000176000001440000000664712251656216020255 0ustar ripleyusers context("Maximal cliques") mysort <- function(x) { xl <- sapply(x, length) x <- lapply(x, sort) xc <- sapply(x, paste, collapse="-") x[order(xl, xc)] } bk4 <- function(graph, min=0, max=Inf) { Gamma <- function(v) { neighbors(graph, v) } bkpivot <- function(PX, R) { P <- if (PX$PE >= PX$PS) { PX$PX[PX$PS:PX$PE] } else { numeric() } X <- if (PX$XE >= PX$XS) { PX$PX[PX$XS:PX$XE] } else { numeric() } if (length(P) == 0 && length(X) == 0) { if (length(R) >= min && length(R) <= max) { list(R) } else { list() } } else if (length(P) != 0) { psize <- sapply(c(P, X), function(u) length(intersect(P, Gamma(u)))) u <- c(P, X)[which.max(psize)] pres <- list() for (v in setdiff(P, Gamma(u))) { p0 <- if (PX$PS > 1) { PX$PX[1:(PX$PS-1)] } else { numeric() } p1 <- setdiff(P, Gamma(v)) p2 <- intersect(P, Gamma(v)) x1 <- intersect(X, Gamma(v)) x2 <- setdiff(X, Gamma(v)) x0 <- if (PX$XE < length(PX$PX)) { PX$PX[(PX$XE+1):length(PX$PX)] } else { numeric() } newPX <- list(PX=c(p0, p1, p2, x1, x2, x0), PS=length(p0) + length(p1) + 1, PE=length(p0) + length(p1) + length(p2), XS=length(p0) + length(p1) + length(p2) + 1, XE=length(p0) + length(p1) + length(p2) + length(x1)) pres <- c(pres, bkpivot(newPX, c(R, v))) vpos <- which(PX$PX==v) tmp <- PX$PX[PX$PE] PX$PX[PX$PE] <- v PX$PX[vpos] <- tmp PX$PE <- PX$PE - 1 PX$XS <- PX$XS - 1 P <- if (PX$PE >= PX$PS) { PX$PX[PX$PS:PX$PE] } else { numeric() } X <- if (PX$XE >= PX$XS) { PX$PX[PX$XS:PX$XE] } else { numeric() } if (any(duplicated(PX$PX))) { stop("foo2") } } pres } } res <- list() cord <- order(graph.coreness(graph)) for (v in seq_along(cord)) { if (v != length(cord)) { P <- intersect(Gamma(cord[v]), cord[(v+1):length(cord)]) } else { P <- numeric() } if (v != 1) { X <- intersect(Gamma(cord[v]), cord[1:(v-1)]) } else { X <- numeric() } PX <- list(PX=c(P, X), PS=1, PE=length(P), XS=length(P)+1, XE=length(P)+length(X)) res <- c(res, bkpivot(PX, cord[v])) } res } ################################################################# test_that("Maximal cliques work", { library(igraph) set.seed(42) G <- erdos.renyi.game(1000, 1000, type="gnm") cli <- graph.full(10) for (i in 1:10) { G <- permute.vertices(G, sample(vcount(G))) G <- G %u% cli } G <- simplify(G) cl1 <- mysort(bk4(G, min=3)) cl2 <- mysort(maximal.cliques(G, min=3)) expect_that(cl1, is_identical_to(cl2)) }) test_that("Maximal cliques work for subsets", { library(igraph) set.seed(42) G <- erdos.renyi.game(100, .5) cl1 <- mysort(maximal.cliques(G, min=8)) c1 <- maximal.cliques(G, min=8, subset=1:13) c2 <- maximal.cliques(G, min=8, subset=14:100) cl2 <- mysort(c(c1, c2)) expect_that(cl1, is_identical_to(cl2)) }) test_that("Counting maximal cliques works", { library(igraph) set.seed(42) G <- erdos.renyi.game(100, .5) cl1 <- maximal.cliques.count(G, min=8) c1 <- maximal.cliques.count(G, min=8, subset=1:13) c2 <- maximal.cliques.count(G, min=8, subset=14:100) cl2 <- c1+c2 expect_that(cl1, is_identical_to(cl2)) }) igraph/inst/tests/test_add.vertices.R0000644000176000001440000000124612251656216017441 0ustar ripleyusers context("add.vertices") test_that("add.vertices works", { library(igraph) g <- graph.formula(A-B-C-D-E) g2 <- add.vertices(g, (nv <- 4)) expect_that(vcount(g2), equals(vcount(g) + nv)) expect_that(ecount(g2), equals(ecount(g))) expect_that(get.edgelist(g2), equals(get.edgelist(g))) }) test_that("add.vertices handles attributes properly", { library(igraph) g <- graph.formula(A-B-C-D-E) g3 <- add.vertices(g, (nv <- 3), attr=list(name=(names <- c("F","G","H")), weight=weights <- 1:3)) expect_that(V(g3)$name, equals(c(V(g)$name, names))) expect_that(V(g3)$weight, equals(c(rep(NA, vcount(g)), weights))) }) igraph/inst/tests/power.gml.gz0000644000176000001440000013143612263023733016160 0ustar ripleyusers‹l÷Dpower.gml¥ýË®6M²œ‰Íy…¾‚îž'Í¥‘fDsƒÝÔ¶HèöµÄúÀbZÔcö'7@€øjÙÎ?ßÈ8X¸{Äóù×ùÿå?ÿëßþ—ÿëø×ÿçßþoÿòÿýý‡ÿãoÿùÿøÛÿýû¯û?ÿ×ÿô·^[ýêßÿW«ŸŸóùwÿé_ÿÃÿûûwÿ÷·¿ýÇÿý_ÿåý/ÿòÿöóûÿã?ÿÇùýÿþÿÿûßþö¿ÿýû÷ÿä_ð¿üï ÿûÀÿ~Àÿ~Âÿ~Áÿ~ÃÿþÐïÂL¿xÑO^ô›ýèE¿zÑÏ^ô»ýðE¿¼è—~kúåE¿¼è—ýò¢_^ôË‹~yÑ/oúåM¿¼±›Ó/oúåM¿¼é—7ýò¦_Þôˇ~ùÐ/úåƒ#œ~ùÐ/úåC¿|è—ýòƒ~ùA¿ü _~Ð/?pr£_~Ð/?è—ôËúå'ýò“~ùI¿ü¤_~Ò/?q^§_~Ò/?é—ŸôË/úåýò‹~ùE¿ü¢_~Ñ/¿pI£_~Ñ/¿è—ßôËoúå7ýò›~ùM¿ü¦_~Ó/¿q5§_~Ó/è—?ôËúåýò‡~ùC¿ü¡_þÐ/ÐȰ“A+óƒ^æÍ̺™´3?èg~ÐÐü £ùAKóƒm`ì¶:vtléØÓ±©cWǶ}ÝBc·ÐÙ-´v ½ÝBs·ÐÝ-´w ýÝBƒ·Ðá-´x =ÞB“·Ðå-´y }ÞB£·Ðé-´z ½ÞB³·Ðí-´{ ýÞB÷Ðñ-´| =ßBÓ·Ðõ-´} }ßBã·Ðù-´~ ½ßBó·Ðý-´ ýßB¸Ð.´€ =àB¸Ð.´ }àB#¸Ð .´‚ ½àB3¸Ð .´ƒ ýàBC¸Ð.´„ =áBS¸Ð.´… }áBc¸Ð.´† ½áBs¸Ð.´‡ ýáBƒ¸Ð!.´ˆ =âB“¸Ð%.´‰ }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±8þÇ@Ä6à 9 Èa@Žr }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸXè }b¡O,ô‰…>±Ð'úÄBŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±9cÌ)cΛ¤1¶§9ỏcÎsê}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'6úÄFŸØè}b£Olô‰>±Ñ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>q¸Æ‹ ¹ÊË M!¶Wr©!×r±!úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8è}â Oô‰ƒ>qÐ'úÄAŸ8èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰ŸJác)|.…¦ðÉs4Û€§ðé>ž‚>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñ@Ÿx O<Ð'èô‰úÄ}â>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'ž|Ž™2óIf>ÊÌg™ù0³9ÍŒmÀç™ù@3úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žèOô‰'úÄ}â‰>ñDŸx¢O<Ñ'žè/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸxñÍ7|õ ß}×ßðí7|ý ßc.ÀÁ6à+pÐ'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â…>ñBŸx¡O¼Ð'^è/ô‰úÄ }â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O¼Ñ'Þèoô‰7úÄ›ïJäËù¶D¾.‘ïKä ùÆD¾2ÑÜ™ˆm€>ñFŸx£O¼Ñ'Þèoô‰7úÄ}â>ñFŸx£O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>èô‰úÄ}âƒ>ñAŸø O|Ð'>|»6_¯Í÷kóÛ|Ã6_±Íwló%Û|˶¹fÛܳÍm››¶ÍUÛæ®msÙ¶¹mÛ\·mîÛ6nóÛ?|åöß¹ý×nÿð­Û?|íöß»ýÃoÿðÍÛ?|õöß½ý×oÿðíÛ?|ýöß¿ýÃpÿð Ü?|÷ßÁý×pÿð-Ü?| ÷ßÃýÃqÿðMÜ?|÷ßÅý×qÿðmÜ?|÷ßÇýÃrÿðÜ?|%÷ßÉý×rÿð­Ü?|-÷ßËýÃsÿðÍÜ?|5÷ßÍý×sÿðíÜ?|=÷ßÏýÃtÿð Ý?|E÷ßÑý×tÿð-Ý?|M÷ßÓýÃuÿðMÝ?|U÷ßÕý×uÿðmÝ?|]÷ß×ýÃvÿðÝ?|e÷ßÙý×vÿð­Ý?|m÷ßÛýÃwÿðÍÝ?|u÷ßÝý×wÿðíÝ?|}÷ßßýÃxÿð Þ?|…÷ßáý×xÿð-Þ?|÷ßãýÃyÿðMÞ?|•÷{Q}1Ôƒ}1Ü~1äƒ~1ìqô‡áÖ0C€1À1C1ö¢cH0cX0ch0cx0cˆ0 c˜0 c¨0 c¸0 cÈ0 cØ0cè0cø0c1c1c(1c81cH1cX1ch1cx1cˆ1c˜1c¨1c¸1cÈ1cØ1cè1cø1 c2!c2"c(2#c82$cH2%cX2&ch2'cx2(cˆ2)c˜2*c¨2+Ã\™Å`™Åd™Åh™Ål™Åp™Åt™Åx™Å|™Å€™Å„™Åˆ™ÅŒ™Å™Å”™Å˜™Åœ™Å ™Å¤™Å¨™Å¬™Å°™Å´™Å¸™Å¼™ÅÀ™ÅÄ™ÅÈ™ÅÌ™ÅЙÅÔ™ÅØ™ÅÜ™Åà™Åä™Åè™Åì™Åð™Åô™Åø™Åü™ÅšÅšÅšÅ šÅšÅšÅšÅšÅ šÅ$šÅ(šÅ,šÅ0šÅ4šÅ8šÅ<šÅ@šÅDšÅHšÅLšÅPšÅTšÅXšÅ\šÅ`šÅdšÅhšÅlšÅpšÅtšÅxšÅ|šÅ€šÅ„šÅˆšÅŒšÅšÅ”šÅ˜šÅœšÅ šÅ¤šÅ¨šÅ¬šÅ°šÅ´šÅ¸šÅ¼šÅÀšÅÄšÅÈšÅÌšÅКÅÔšÅØšÅÜšÅàšÅäšÅèšÅìšÅðšÅôšÅøšÅüšÅ›Å›Å›Å ›Å›Å›Å›Å›Å ›Å$›Å(›Å,›Å0›Å4›Å8›Å<›Å@›ÅD›ÅH›ÅL›ÅP›ÅT›ÅX›Å\›Å`›Åd›Åh›Ål›Åp›Åt›Åx›Å|›Å€›Å„›Åˆ›ÅŒ›Å›Å”›Å˜›Åœ›Å ›Å¤›Å¨›Å¬›Å°›Å´›Å¸›Å¼›ÅÀ›ÅÄ›ÅÈ›ÅÌ›ÅЛÅÔ›ÅØ›ÅÜ›Åà›Åä›Åè›Åì›Åð›Åô›Åø›Åü›ÅœÅœÅœÅ œÅœÅœÅœÅœÅ œÅ$œÅ(œÅ,œÅ0œÅ4œÅ8œÅ<œÅ@œÅDœÅHœÅLœÅPœÅTœÅXœÅ\œÅ`œÅdœÅhœÅlœÅpœÅtœÅxœÅ|œÅ€œÅ„œÅˆœÅŒœÅœÅ”œÅ˜œÅœœÅ œÅ¤œÅ¨œÅ¬œÅ°œÅ´œÅ¸œÅ¼œÅÀœÅÄœÅÈœÅÌœÅМÅÔœÅØœÅÜœÅàœÅäœÅèœÅìœÅðœÅôœÅøœÅüœÅÅÅÅ ÅÅÅÅÅ Å$Å(Å,Å0Å4Å8Å<Å@ÅDÅHÅLÅPÅTÅXÅ\Å`ÅdÅhÅlÅpÅtÅxÅ|ŀńňŌÅŔŘŜŠŤŨŬŰŴŸżÅÀÅÄÅÈÅÌÅÐÅÔÅØÅÜÅàÅäÅèÅìÅðÅôÅøÅüŞŞŞŠžÅžÅžÅžÅžÅ žÅ$žÅ(žÅ,žÅ0žÅ4žÅ8žÅ<žÅ@žÅDžÅHžÅLžÅPžÅTžÅXžÅ\žÅ`žÅdžÅhžÅlžÅpžÅtžÅxžÅ|žÅ€žÅ„žÅˆžÅŒžÅžÅ”žÅ˜žÅœžÅ žÅ¤žÅ¨žÅ¬žÅ°žÅ´žÅ¸žÅ¼žÅÀžÅÄžÅÈžÅÌžÅОÅÔžÅØžÅÜžÅàžÅäžÅèžÅìžÅðžÅôžÅøžÅüžÅŸÅŸÅŸÅ ŸÅŸÅŸÅŸÅŸÅ ŸÅ$ŸÅ(ŸÅ,ŸÅ0ŸÅ4ŸÅ8ŸÅ<ŸÅ@ŸÅDŸÅHŸÅLŸÅPŸÅTŸÅXŸÅ\ŸÅ`ŸÅdŸÅhŸÅlŸÅpŸÅtŸÅxŸÅ|ŸÅ€ŸÅ„ŸÅˆŸÅŒŸÅŸÅ”ŸÅ˜ŸÅœŸÅ ŸÅ¤ŸÅ¨ŸÅ¬ŸÅ°ŸÅ´ŸÅ¸ŸÅ¼ŸÅÀŸÅÄŸÅÈŸÅÌŸÅПÅÔŸÅØŸÅÜŸÅàŸÅäŸÅèŸÅìŸÅðŸÅôŸÅøŸÅüŸÅ Å Å Å  Å Å Å Å Å  Å$ Å( Å, Å0 Å4 Å8 Å< Å@ ÅD ÅH ÅL ÅP ÅT ÅX Å\ Å` Åd Åh Ål Åp Åt Åx Å| Å€ Å„ Åˆ ÅŒ Å Å” Å˜ Åœ Å  Å¤ Å¨ Å¬ Å° Å´ Å¸ Å¼ ÅÀ ÅÄ ÅÈ ÅÌ ÅРÅÔ ÅØ ÅÜ Åà Åä Åè Åì Åð Åô Åø Åü Å¡Å¡Å¡Å ¡Å¡Å¡Å¡Å¡Å ¡Å$¡Å(¡Å,¡Å0¡Å4¡Å8¡Å<¡Å@¡ÅD¡ÅH¡ÅL¡ÅP¡ÅT¡ÅX¡Å\¡Å`¡Åd¡Åh¡Ål¡Åp¡Åt¡Åx¡Å|¡Å€¡Å„¡Åˆ¡ÅŒ¡Å¡Å”¡Å˜¡Åœ¡Å ¡Å¤¡Å¨¡Å¬¡Å°¡Å´¡Å¸¡Å¼¡ÅÀ¡ÅÄ¡ÅÈ¡ÅÌ¡ÅСÅÔ¡ÅØ¡ÅÜ¡Åà¡Åä¡Åè¡Åì¡Åð¡Åô¡Åø¡Åü¡Å¢Å¢Å¢Å ¢Å¢Å¢Å¢Å¢Å ¢Å$¢Å(¢Å,¢Å0¢Å4¢Å8¢Å<¢Å@¢ÅD¢ÅH¢ÅL¢ÅP¢ÅT¢ÅX¢Å\¢Å`¢Åd¢Åh¢Ål¢Åp¢Åt¢Åx¢Å|¢Å€¢Å„¢Åˆ¢ÅŒ¢Å¢Å”¢Å˜¢Åœ¢Å ¢Å¤¢Å¨¢Å¬¢Å°¢Å´¢Å¸¢Å¼¢ÅÀ¢ÅÄ¢ÅÈ¢ÅÌ¢ÅТÅÔ¢ÅØ¢ÅÜ¢Åà¢Åä¢Åè¢Åì¢Åð¢Åô¢Åø¢Åü¢Å£Å£Å£Å £Å£Å£Å£Å£Å £Å$£Å(£Å,£Å0£Å4£Å8£Å<£Å@£ÅD£ÅH£ÅL£ÅP£ÅT£ÅX£Å\£Å`£Åd£Åh£Ål£Åp£Åt£Åx£Å|£Å€£Å„£Åˆ£ÅŒ£Å£Å”£Å˜£Åœ£Å £Å¤£Å¨£Å¬£Å°£Å´£Å¸£Å¼£ÅÀ£ÅÄ£ÅÈ£ÅÌ£ÅУÅÔ£ÅØ£ÅÜ£Åà£Åä£Åè£Åì£Åð£Åô£Åø£Åü£Å¤Å¤Å¤Å ¤Å¤Å¤Å¤Å¤Å ¤Å$¤Å(¤Å,¤Å0¤Å4¤Å8¤Å<¤Å@¤ÅD¤ÅH¤ÅL¤ÅP¤ÅT¤ÅX¤Å\¤Å`¤Åd¤Åh¤Ål¤Åp¤Åt¤Åx¤Å|¤Å€¤Å„¤Åˆ¤ÅŒ¤Å¤Å”¤Å˜¤Åœ¤Å ¤Å¤¤Å¨¤Å¬¤Å°¤Å´¤Å¸¤Å¼¤ÅÀ¤ÅĤÅȤÅ̤ÅФÅÔ¤ÅØ¤ÅܤÅà¤Åä¤Åè¤Åì¤Åð¤Åô¤Åø¤Åü¤Å¥Å¥Å¥Å ¥Å¥Å¥Å¥Å¥Å ¥Å$¥Å(¥Å,¥Å0¥Å4¥Å8¥Å<¥Å@¥ÅD¥ÅH¥ÅL¥ÅP¥ÅT¥ÅX¥Å\¥Å`¥Åd¥Åh¥Ål¥Åp¥Åt¥Åx¥Å|¥Å€¥Å„¥Åˆ¥ÅŒ¥Å¥Å”¥Å˜¥Åœ¥Å ¥Å¤¥Å¨¥Å¬¥Å°¥Å´¥Å¸¥Å¼¥ÅÀ¥ÅÄ¥ÅÈ¥ÅÌ¥ÅÐ¥ÅÔ¥ÅØ¥ÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥bîR1w©˜»TÌ]*æ.s—йKÅÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥fîR3w©™»ÔÌ]jæ.5s—š¹KÍÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]æ. s—†¹KÃÜ¥aîÒ0wi˜»4Ì]šp—þå?þ§üéÿóŸÿë¿þ¯ÿò·û¿ýã¿ü‡ýOÿò_þvþUáe„Ïÿ(¼ð¿àÿ®|œ²ÿGåñ—•ÿ}´ÿS鼤í¤Ç_—ž/é8éõ×¥¯F]®UëÕªË5k=ùúÕXíZ _Õö©¯Æj÷aûÕXízjß]úüeéÛ›ýwéßÄýSé_×·t\c‰Ô5–HÝ ïî⦊ÿ¶UûïÒr-p¼†a¹Ÿu¼¾V¹Ÿõ–ÚOp¼>ìáÌùjà ˜óÕVùê-í^õ|OÁnh‰Ô5«H]³žïAð×¥§ëX"u_àz}Ó-."uŸàzõÁË-×kʲ})Çþª× ¼\[]¯¶ºÜSïW\î]ïW¼]¼ß˦ëƒ÷kξìSÏ¿Ü÷{hÿeåázë[z»~u?Yú¼¾Àí>Öóš/.׬ÏÛ¸f}KË5ë[:ý7\DjŸúYîÃ>ïUó¯K­¿}ÞË‹ýZï>h[àÝ mwygû³Þý՚ן·yö>û­µNûçÕeí÷wÉ_[äþŽù«Ï}}^;Äÿ'ùGóº®ðwZÉ?žk ÷Ï_Ÿ’×{#cç9ÑÞ^ûú‡íë5ÙÛÕnÉ&Éï’dïã·)²ûùñÍöÞÿ,ß÷ñûCûß÷ÖÚç¾÷V¶_ŠÔŽz}»eõßÁ/ÿt¶Ã¿µvNí?ˆEE\þÉÒƒþºÖOkïíëã?Ç{«ë·ð"¶+ì&vkÁ&¶¿¯ÿúN^´åµï}œýuo­]¿Wøvï¿Ýõ­÷&=¼ï{4ÛÑñÖ.¿~©Øþºy} ?FG6à>dõ—íÆ"þòÊÿÀýsñ«WØ£h—5këd ~Ïš6ðw$Ò_n‹·¿µnÞ“Ûÿ"ïåÑF†Ö|¾µÞÙÌýá×ɪëÛXľ }Xv/ù¥ºm:¤oúp}xcYLýƒe1µ_OÄ6¶÷w¾Õ?¢%vè‰ÖNCoí?[ÿ\üŽû¿G“ïÈÇùå-þzLV´Ÿœ^ùþòäçÓÏàÆôŽå|õ7?BÞÚåûÐùîC6îùwþØ_~òùáç½# v~{kmLy½ã”—}‡·vÙPé&¶úzoæý[¼ž7ßÚåwÝ×{Þ´Ÿî­µ±µu}™6EìÇÒõŽkø7þëa3ÑþƒáúÏů.ï—›KvX¾S¼‡‡íòoí²ÿ¿SìþêÇ{O„?¾k¾Åa4=Äïhz¿G“ýÔoíò‘û¥µüÖÞžû0ð_û裊íä}¿z§Ý@ú"ö[‹û½Nû‰SÄÞE¾Ó §P·¬éþ÷IWöï®lGŸh½=•$†"Ï—n/bïž÷nÏïDDlÓêç<þïìà[ìçûwžÄÇ%ÿãW3ÛZ€õNÁøiù9¿*{Lž׽ãGÞPµÄ|Ó½ÅmCB-Á&ß‘Dì;’D¦Â“ßb?Eüé5ü€±°oqÛÚÓMüåÉ6öÖKL ïHïëO-¿Ï#”M/¨ØXÛ$‡ˆÃ€U±=¿,bÜo /Úý ˆÛÆ"{}0Tý>Ñ6þÖ¹´Ib· svɆ×vÑ·8½³ŒÛÎõaS£b›~qÛ8R—t$/–MºoçwG²õx-a@»©i‰ìÙú6· gu¿gQGqÛz±–œÝþobß²kóO÷åŸ,]Ôög{K,Ç@Û‘ä¶[âÒý¥‹ŠØ†}EÜ6`®b…\\ÄïSû~2Ÿ/» =½òáÉmÑ´œñEóö£ˆØ[ˆäà{tÛ˜L Øâ¶Uý>g’ž||yò{ Ú<”ˆÛÆÌ[‚Õ6&³‰mç‹Û;s¹·$¼³ÔÆû×x‹mªßööãˆØ/m*¶ýYÄÞªâd|;YßGH û·ŸŸ%Ýàç 9œÞYn6ú"ïüÅÆ‹Ø†³Zνسˆý>ž>÷ù!À®b+±ßK¦Æ;™·¸íe?­i/boãEìm¼Š?¼F{{yJ|ÿƇÜDŸÇÿ¼ØžHl9Üžü!²Ý’kóãûz¥ÔŸe+íû³dñ>ˆÛ öõóá^_6é"¶78õ%Ö~îK†•ýÜ—,@þ>¹/kŠœ™ñ±‚÷ÅOíûÆõ!E¢b¿{‹Û&m7±oºw¯ó;úûËAR«¾éÞâö#Eò°Aüî6Úï4¥î{åöÆwF³íU1ý>KÑ~#ýÎ;¶Í;öûÄCû9ô}0!=ù5ïû7~›)›îëwŠ2‰ß­ìŸwÖ1‰Ÿ¿.žwº/‰åû¹Ùh´ÀÞ^\ù.š›˜wêllêl$ÁÞù}Y”M5Ì;g5ö(áüÈG±­ñNC…Ö±]äçñ{ͼ3I,šmGZÒ¶ëK-i ·Rmbç5Tlcü#‘xÿ¹%^î»hÉG±_Ä6?‰·®ˆSkˆØîwG²e³óÛûs$okdDú³ˆm•ô´ŒnÛ*¶]TŠ}­å™wö ‰eʵ]TÄ6à4ýe~îóËk¼Å6Ä2/¿};Ë5¾#=_Þù-¶÷›©ØFâçQ±—ÜdË‹D<þ:ëw<ù![¶‰m;?2Rl¯{‡“Çæv7±mgû¬€|;«ØŽÁçKGz‡ª}xê9¿4Æ[ì#{‚Sýè-Íü^¬B׸¿¼Æ;MêãooñØÐåñ¾¾æøq=ôx_sبÂ&vÒñ>©pØ®CiAü¾z×›z½m:»ic»¾±‰Ý*qÈáåzJÛtïxòaSÇûž‰ÃF#wˆ8‰ß7‘Ú½ÿñ®¿><ŒëÈ=ì>ìx‡[ÏîR±²\Ã`Ç`Ë´M§bÛtïØìaC—Ç»þ:ŒÁwPô°{Çãçmœÿ¬/Ãêø=mà÷”X®ÿoñi’§~í:x¾+xýj%âÓÆÌO‰Û)Wħ=tJà×N¹">m¥ÀùŽåž6ù¾Ã³§µ=›Ø~Á–v¶M'bk¨Î–Îoû†ˆÃ±E%<ë»è[¦¯w,÷´„óq=íF씸¨Ÿrß¡ÎÓ^î{¾’§ †¨ØîÚ6±Vï°áik—N‰ªYãz¾£j§Ý.©Ø†ÁUlK/7±·9±I.Û¨Ú)±/{2RÅ~z‹ƒGR±oçwG²QbÛÄœŠm°ZÅ6è´‰}ӽͶÿ(çÏñ{Mñí,b{Úþ”p–-tñi‹ ÏS̶ÿo±½òTä¥í¢§ø:Û‘D쬈ýÒ&b¡:%èä}Ý[|Úºãóí9-*á¼¾ÌÏoñi+Ïw´ç´èÏw´'‰¿Øû1x}ù(oñé÷ƒ·„Yì;¿Åa±ïu·ô ;ˆØÏ"¶åuç;—ÄýE||ù²Û¹NÄ~¤bÿ¹ß+¬·j"¯!b»Þ_lüýeF± ”Ä7lkˆØæ€Tl¯*8™mGzd9¶íü|éÏïPçé÷°ïPçé·Òìýk¼ÅÞ#½C§-z<ßÑËÓ^‰wý|Øâ]´Ó—ˆý2¡b;E|ÙªÎMìŸ,¹ ÷Q.‰¸Ú¦Š-£ãúù`¿Þ7ˆ_6[zý|°j*Eæ:ßtχw~Çr/kã¯wxö´™t_¶¨æ’X®ÿ"¶¾NÄ—Ÿ¯wà÷²ßëø½là÷Zœ¹Š- FÄ—_øµ“¹ˆO›¦ñeCp›Øö:‰?Ûš_~ʱl_ï’æËF{. V‡v~¯ƒ¡éÄ1Úþ¬bßÎb/¿¼³õ¢*é%¾ìM›Ø·†¸ÿeûïßù-Ÿû-¶iø—­3¿Þµñ©eúò­ñ{ô.¤¿lTíêªK²Ö‹ø²)’ëõ¸ìaÛÉ%Y?ɼŗŸ Tì›NbŒþ£Èçöïü^­|Gz§H.?}½‹Ò/q½Þ)’ËÖPmbûßù”$~»•¾ÞYËæ€®wÍvhºwŠ$,ô›Ø”ûº¿mÍö&öŸûþòŸ÷“í|KûyCÄ·‹Þ·;ú[bæA,¾Î¿†ˆý|‹mޤoø×±íÏ"ïüšŸo›¸—ÌÏþ5î÷“íTð®¿m‰Ñ-ÑxëÌE|ÛÐý]2™Û¦{Gão›ŠR±Í.Ýõ¥?‹ØÖöÜ5_ž<_žüîH6–{ëÕ%¾5ÞÉ¥ßïðìm¯¹õÎûoñm#®÷;ˆzÛrwÛˆëÝç—'¿Å6ˆz¿o#¹mdû~י߶Îü~ÇEo߸ß7{Ü6Ôy´³ýïPgèHïPçí§Ü÷½·=“~¿ã¢·ÝðÞï°ámãÏ›Ø~ÛìÒ}|é¢"¶ñMl?÷»À;ŒA{÷%bo¶ßw,Ü6ˆz_¦¯w\4¼Æ;Ôzˆý;«Ø6ÝûrƒÛ;s‡(S®r}yþŸ‡wžƒØ·³ôgÿdéÏvtŸ²LøÖxÏu~ ¾k¶“øù ¾¾tþw°:Ì¢W}™E¯/³è;J|ÛÜñ}ÉÚíßYV+ÿäûËk¼¿ ßß_¾à-ó†)"öïü.–¾m¼î¾ûËk¼Å~Ÿ"bfyG‰SÓ½Å>œu˼á¿à[lCʛط†¸/ÿ×ñýEü¥??2ÉØv~¾¬ƒ"¶µ—÷#Ó—íü"ö!‹G:¿)"öcðùÒŸEls*öªç‹½±MSª8¼ó—]›ˆmœû'¿‡•°"O~A¿Ax‡î};??ÖÛµ[Å6fþHRÀ6ÝóS_^ãÃj¥b{"ãù‘Í£ëü*¶âù™/Oþ0ŸŸó‹X– ß‘î/âçƒxý|xg‰™ÛXÁ³dd;ÿ;–ûØÂãMìfÑMl;ÿ;Jœ^CĶשطƇ­ÇS_¾ „”­ |ÞAÔÇ$7±m:Û/ø.µM¯!bÿEl¿ ~­½ÜÄÎɨØV,ˆø±‡†6±ÿ(ï^gì›ØÿÀ·ØÏüoñ§w~l”øy×?ÿ}³ü—Å_žüØ)·Å?Ûø‡Öø’xÞÜÆÇÖ??ïÐ}¿{ Y<ïhüc£ñÏû"—Ç^Šò¼C÷ Ý?ïÐýcCpÏ;tÿØØ×óÝ?¶üìy‡î¿h¾«”»|Þ…ÇIüngo!DlýóóŽó?Þ^¾£ñ¡#ÒEý;¿ÛÙÖ}=‡´³ç·Ø†,žs}xçSú³Kö¯ñî϶áy‡g[Äû¼#®-â}Þ×0ɨؾÆ;<ºè%ýÙÎ"¶EbÏ%ßþ@ûv±_»ßåÁa ¾#®©5äì¿Ó¾®-Ü{ÞaÑÇÆœž[šÙÎå*¶Í|Ë„äßùÝÌöZˆç5 Cð5|l]Û#QCÿàçÃßqÀÇVö<ïÐÞc1>|ÛDl«rUlãSÏ#s¨ít"ö÷ùb DìCN‚úòóÑ;L¶|}¤‡Ú%SÄöôàóÀ¥ò˜rýüÈÄï>á¯úÝ£í]¿êW—þómäï®gæ¯úø7¨ífEÕ~ˆ‘‹u3䦶9©MmWŒMmï½SõŸŸmä×7ùýMþ|’/Ùù_*j›ÝØÔöž _µ¸!ßEm‹¨UýçÍŒ¼>½¸¬ áÅ[^ʼn%F <üÃB¢êÜUÄÍ…VS^E†~ø>2àlDô÷Ï2àlLô÷Ï2‚lUîú)q'þ]Tíg-QÛ@’ª¿§_²Tn¯øýs}k–ûïMm3G¿êùôìùôl1AA-ËŠŸ>Em#»ªþc1>ÈCG¹IÿþYÆ­0þýóÛiW¡êÐæââ‹ëòé.vûáù¯Ðâ-Ñ·‰¨Ãª¢obë£å2¯„6ÑÕ3<\& þý³,ˆöʸß?ËØÿ¤¶›Ú—ûU‹»µa¤ß?ZâDmƒ¿ê÷.(L,ýÍ€¶Œ ôžOòùù&—Î,ÎÈ^È7ãHW “œ„òƒMq[Áø©<ýPÝjùùBåé—ªá í¨kKø©"†¦.ü(µmj!øUߟÞä­sÑ|ïoj[ï¥ê8¨Ü†ÀWÁO[JQÛPõ®öm(jvRuìä*|“ûO´ÉûKÍO¢‡,èa}_ø<Ñ[œ\Ρ^!4b}{ñ÷ì6Ï’ Á Q‡Ä!ómx¶ìA¾<;í´%{öN‡Nü¡ÁE-Iކ œ¨CPîP“¦ ™ÊÃK”ZQ&Ð4U¨<ô[•‡©Bä6õ¿~N™+lÂè÷ÏõM.ã"lsNi÷°ß¾d S×%öÏ&7ÿ,=ì¢6¹ïb—|¦j¿åé¶¶ë÷Ï×7¹´{°º›Ü¦çç›\{¤w•‡wßä¾ÝUnÓq¿–á–G6Á×=Ç7¹v‚Ð2"Y«G;ôw¿ù^ï Úï¿m»¯wNì÷ßvh/Éè„õzIFgùµfiFÇ÷™%É‹å×à%Áñe«–ÿüj÷¶÷U¨:=•/3^b^¶÷÷Ïï{{¹Ä¦ökðz‡Çž,üUKðÛé%1É$—@Óò&yµvÞðtí¼þ§ªÜ–Fþþùþ&>É%´¼ß\£³€›<¼Œ|¦0iŒ´Œ÷a»Ü&Ù?.ùZ‡,4~±Äç/[Ìôûgõ2v)Pùò±uªËó_U íò1„uJ'ó†Êm•ÐïŸuÅösû).ÏÛ•§>£r_R±ÉÃZpÊÄá£e›Ü–ýþùþ$—Áò¸]î[Få~ß±.é‘¶–ê÷ÏÇ7¹ôŸÑTùòQ°u©) O}¦òÛQÿù¹Ìa¾å+…éý]¿9~_°Þœ¿ïæÛåVá[ýV_åFåáÝe?¶ìÁ‘_¹lü¶Yå+Lyºcò5*_a†|dËv²ÁZ¡·?:ôüWÕ Ö7ùò¡óMî·oõ#À5ú•¿¾j¥»7?…ír;°K6{å7{õ>§›åºŽÙ±§òò³LéÞЦZ:‡ù¯$[Éò[É’Zµò)Ë’‹ }ô¡ÞûÔñ»7QÿþÛw-ù =¦>E ª>YÈÒm­¯o©êo/#ÖÝ/’›ÜOî»<¼û{‚,‹®Ò­jx‘ûU²Þ»ò;ô^9ëÃ&õ.¸ñW“ªÚß–ü«–QꃻÜϽ*÷Õbdl…ý&/Ÿs+‰U”O-—ʇJ¢ å'»Ü7äèÜä2iøaih#4¤Æ*|Îh“û’‹ÒbŸŒPùòiš’úàÙTž¾êHŸñ1ÈzÇY&L£[ß!ª~ìjdü,£r¿©-‰ù”lìr?Hˆ¨|ªÞéÚ4CJþµì}0«Nݾû¯¤rŸWyùà@I†4-‘Pªô2-ó)ø«òòu½»<üT‘ûNUô}Fäå« J‚2ž.¹ã ¾ýšorùªa¨ªÜ—É”†|Âà»´Ïø)òòuo%1Ÿ(—¯V&‰ù¤íÕ-Àjýý³¬ª¡ÝE^ÁŠß+ö-#AŸJ/#ò°ëØä¾Ý%¤T>4W#*{¯\§Tß#UVIÂWØ×jÞ^<ù+ X‚8i‰Wyè2’ƒ_>“¢òòÁ³–ˆRo©¼|uBkŒÈ—@ª¼ýœ×r7þTé2¾ r“û°¢ÊÛ§1[§ú)RåaÑÞäéÝOywÛß[£g>ŽÓ?šm¶ʃQy{³Ôr79U^~YÝåa4é:éåWìðt=_ëƒý>_ûçÿÜȵ.ϦMîûûú´sVyù½í&÷‰õÖ#¹ÞŸôûíŸÿs#×Á>Ó§²¦–bŸöíÖ¨¨ß6µDEÓ˨܇OZœ:"½ƒPyù³–›ÜoWUÞéé—ÈÃÓïor¡…¯ªÂËå˜cšßUîó¨¼}„£õX¤wKÝšðËAë^FãôáéZXúééåÏô¨¼ÃàÓú6yJK¸}¸µÍçTžf ûª–MÖ&‘wMbŽrM~û¯:ŸÂ‹*o¿‡k­ž ÃCäíƒÀ›<˜Z 1ûdÊS×R¾0ø4Æö£ûþÐ2²j‡,u…aGÙzÒÌü´œï ÁŸ>´û‰ãP§~ªöw?®Ûz~/˜+‘§å@åÁGÞ2-ù¬à&A Íc„0šÈÛ§=Z*iË×ÛµdIBº¿õܤ¿M¨µN75¤¬|!†-ç&;Ä–¶ ÿLîü¤§r_§ÛZˆðt‘‡v´!ÃO¹OÙ´U Ï*ï°£”4IûTÙHÞ#t‚Mîg‚Mî÷ó£þ=<]¼˜wØ*Ÿ„áúøj˜Ñ”¯sÛäÞí(õËê&÷74©¼“\>ßÝçGã-A.CÕ6Øä~ŠTùø„Ðhõµßò©¼|m4Kâ缑,I()M{ø4µÊC…ôhÃWϨ¼CCê©b?½orÜßä~5Pyûr•Ï’ìòð2ߺ؛ϔ®°˜íÀuèb¼{èÀ*÷OßÊä}ˈ<ø“Ñ„_VUÞ>V¤òñQ…MÆj}²À*ŸÐßÙ©oê”±-Ùӻȃ-Íeùx¡ÊC4r“ûÏhêËD¦t ú€ÊóÈ'x%É6M°3’> U¦›Üç²T_FÖš0öTîÏ?¨||Rpô@CªšœòÇgUêUÞ>’:zµC«"ù~•‡Z´ÑÄZ0šX Bc¤yF3e¾²^åã3e#‰µòG=FÏnø8í&˪ÈǧFóp>¢§ò s¤ž ñï]þéé!œ>šå Û&‘I^SꀻD§òñ7ŸŒ¤K;õwM…†ü”ÿTy¸h4] ¶Êý­*OÃC’±+Ìï‡&|'Øäáݵ&´Œžëï.›a|Pùë&¹Ûøôùöt™–RŸ‘U;Ds6yhH«þ’]ž®—ùU[ä¡&j¶k‡BCª£Iä>9¥òP7ªòäõÈZˆjÎ<øHÍ™ë¦9ó°ˆ|ÂL rŸ3ÍȧwêÎÆm½î)5¤ž /#ÓRpKr!S¨žQyÚ{ˆ|ÂFh“û™@"údì&^LO†Ð©ö'4¤^S^F§¥Ðîzyïbr÷Ô„ŸªwO}“wˆ\VðC[ä†RMŠœæÒ¿ow=¢éo¾9s9Áþè™K_´­òP•‡á!g.S\Whú:i•w؉|BþHNtN˜Rß':0‰½ !¢Zf÷°ÔhDhF½Q,L×Ûd~™Üä¾Çˆ|ÂÀ–¢Œ •‡¡'5ÉEª<µŒÊûëBöIž¶pz³vx‹<\ ±ÉC$UåþžÎÑ[èÂt­Õ-ÁÍlòO/jRG¯(÷5Ø*ïÕ+ñ<}`äÒºp°Ê'ô½ã.8‚Gcn~4é•x!¬+òpÇÎ|»AO團Þhžž. YÈÅêáko:­LÈö•¯û9´ÇOK*w ?º'³ÓÒ&÷g›)ž qÚCŠgB–OåËOKÇVkc;°ÊËÏ*%*—ÛÁñð™>Õ|¨»zh¡?¢òCÜäaÒÓkXýVEå±e4ßïW>•‡XëŠ|ÑÇ¡uEA®uE¡eTîCQ*q±c»BÖ&‘|úø£ÊþóPp³OÛzAmX䥤äv_JJRùᚇÖ*‡-òöa…C½‡,òÃÛ ò!‘CŸ>¶¯ò¦>4ߦT•‡uUËÂy¨…ð?U©DþÄÚ!YíÃNz‰lXÊ4 žž.«‡J% X²«Gp©’]r‡w—¯êS<‡dW+4¤f(ý‘†CRŽG0W’rtêE–þz£S™4~zÊÝ‘‡?qJ€ñôCû”`pK§†ÑünøT¾©·û§ˆû(?ýýñÔËñ¼e>u7ìËÙOÙ Ÿþäø)ÕE;eGy†U[6q§O5lrŸÂ?õ*2ŧÊOP?ån±Ó—³ŸŠ–õgxNÙQž~«²ÉC–=_ê3"?ýnø”=ßéÃhçÆ[õí.ò4a‹üôvÿT>«Ôª<7œŠöðip•'‡½•Sú†Ô[¨¼Ý?oý„½É}ˈü .U6 Gè3"?}Øø” ÆpÄMåg˜°e{{§§U€>M¢ò#8=‘Ÿa–²¾Ã"©ü ^ìÑå ¼ŒXˆ0aë^;ɵ×·»ì†Ï0¿«ÜoU®ŸOû¦Mî.—Öºùb•>–zýh(ÊÎÀ—lüÃÊwiy™wz—Ô\~Zº~>yàK/[ò1½K !(}iÝ’¿ÏëRh„Ÿ#/©[:ýbs-}ÛÀ v±¹¼à ï®úäg.-æñ‡C®RÛé?“È/_ûsi1Oèï²ñ¿BCêU8~~Wùé Ê¥q‚ôôOû¦K bB¡Ê¿¾$¬púŒÿµÄø [I aðµ.”¾e´Ž#LK­C;üT‰¥úÜÊÛÛÎK/| ³˜fø»>. Zœþ°ë./ó–_Iþîb—ßk_ómS¹7ä—T+œ¡KµÂ寨Øåáe4úãR£?þdÀ&󻢴¤•þ ˜Ê¯ôSõH‘—š“÷í.7Zœ¾¨ä’:ŽÓ{àM:°Þh–2\ù(Ä%wN\Á‹IÇåSkס3AxwYl|!Òuª1ôƒïÔÝAëe¾e´0#´Ì©1l?<ôþ_árIÔíý]ïÜk“éNŸÍºô ¾ßš«üòAºëRÇáß]ë8|æ’cïgzºìÊÂXÝä~Ósì¡‹é9ö°®JQI@R\RTNø«ü No“û>£dâ°ò][ò‹¼È/þ4é‹Â/©p¹|Ôí’ —#|&‘‡¤ã¥1a)ù†Ç&O×û}»¿#µá²ÔKB©—?¥pi-OØh(55ŒÈƒ“Pjˆ^^JõüKƒa‡(Gž“cùÜÏóÍ`«E©<ÔÜZ3è*<Ò[J ÇŸÛä¡‹)ÖÁÛ÷[ }Jþ–LC¸ÃùÖ3̾¦Då§‹©üòi[òáµ[‹/S'Ð#Ïá§J–ÄgU~øò™[k5}Fs“§§K‡gîí@ux™ß½Û¿µVÓïnoÉÁ„ ó­g˜}9Á&÷ÉÛ»Ô‡ŸªûÏðîZX^F/ˆ ?UäažÙXÙþeD~ùƒŽ÷V êß]k5}(õ–”MØlrì¼5æ¥-!äçÈí`oxw‰YøðÌ­9˜?qˆüöU¬wkÌ"¼Ì[~{Ë|Kæ ŽCä·ßšßzÕÛ}•‡xË- ¡p§í­Ÿˆ»%î´Pùác·¢µ}õÁ-žP9qK}ï™~ªlâ¾=ýž@«‡ƒ¹Òt“/ؽGS”ßä¾üý>¾yÉNÝ>¤wKºéÇÇÓUÐ|·f§|äUåWpK’Ì å3÷¡M?‹I2+Ê?EÝT*…nI•þ^“[¯ ÷Q·[r_áÞªûÔB ßî[Źÿªzw¶§ßЧÃC+Î}ÞîVšuˆÏˆüJí.aã0‹éýÓÁ¥êj_#{Ë‘çÓG;U~ùšðûÒ<†w•c(7Dß>(}Kf-Ê?Ý­ò´jKfíô•B·ä¾Ž`®4÷º˜ÈÃ}÷–* ?U×ÕПþ·¦Ê |ëæÙ¿»fÖBŸ‘dÖ<Á–ûòcUr_¡HDåw˜ôôçà°5Wâ’ÍJ‡dOï®^ì“ü )é©´ÒäWØÙè}¿aðé9_±rËÁ€ ÖMÏ„ …^ Âõz=p0W›ÜU‘ß>=ukz*5¤F;ýXݲYáÝUî;°Êý¹™MžÞ]&ìÔ Ô؆|$=uùB‹G¡ã~«¢òÇ÷÷gã‚ÛϤòÓé%wû™à‘ôÔå7q›ÜG!IOýøó*5TÏv:$|&é‘~g£òËg@ÉfMê3z¢ÈN*«ö³´¿ûv—|Óé—ág»À÷“¼}€ñ‘ôTù¤£Ê/e~$›*ýU~û$û#—}„ è&OŸIëC'ÈU˜ T†‡&¿Â”º4¬à'lÍ•y‡­òñe$¿Æ[7•ß¡ë5(>ϧòà vyxwY=ÂÚ¤—‡¡-òÛžz$7a4‰üÃCoN-#Û[ŸÒ|$W.YQy8Fùè­)Þþ¨ôªòÇç½b×× ¨<>ý=<ÂÎæ‘,âã“ìÏv[Møªoù㯾|$q÷„¡­ò`Fí~xºä=|ÔMå!kþŒöß›ÜwIÜ]>rõhâ.Ø}ÍÄùü#¹²p'‹ÊÏ0ÏèI®0a‹<Üýôl dÿSE.UùÚ]ä·¿hð‘<_HO©üöQˆ]îÛ]Ó‚a‹(ò;LKŠaó»r•ƒÃþv™Ó&÷ÑN•ßÁKŽ2 ¿T~…(„޳쫢6y’oòÁÑGRš·v>z-³O:ªüòµÌÏ©æÊ÷ÈSǪ÷Swþ«JÂ4DÈŇÝÈã Ó°6º5ÿ$/sŠ<4¤Æ–‚\k™ý ¬ÞðUõ0Ÿ¸Sùã‹×ö†ùýÒ ¨_†5¥ìÏvX0üTõ3¾ÏH4ÜÞ±ÉC' h8ò¬òÇW+<’}|9壇S˼å¿àñQFnè‘"?»KÂôö·3=š0 !@=ý—Þ]'½ —þöÚÛ=máé‚@ò•*BXAŽÿˆ…Ê`÷%(,^¼ve’0}|õå#Ð'ä›4Âõ"¿}¹Í£w¢ûSk*¿}ᣇ CÔÃ…Á ˆü Qfɯ†+‹T_æøö2 K™d@Ã…¦*¿BŒC¦i‘×ëß=Bå—¯uSyJ5ˆü r•Ûù½~$Ùë÷«ÿDîÚ}“ûíí¯\ïÑwí¾Éï$ÿÂ4ÚäþÜÁ¯\¯Øý$¿Ò»ËBièýÊõßÐî2VÓÓµà1üT=ä_.ú•«¹òý}»—пŒÈ¿=ý÷å|»K*ù²b“ß¶vÿW®—Sú¡­r»lòÇ®«¿rÖüʵ‡ŸªõÀ¡ÝõоŸ ô”¦Ýkoò ƒORÉ>‚²É;µûk4ýù?7rYùl~u“ûCn›ÜÇ êG¯¾´å6›ü¶{>•ÿy9#—¼aorOîù•Ëà C[SÉ6ƱËmøW®•£¾¨ÜC•ÿþ;4¤l³ÂðØäá«Ê6+,ò"ìYó_¹Üò&½ÒáñQæ†Ãgyò‘šJöv)¨«­+Ÿïï’¨ö'Låb®l-³Êÿü׌\C¯áe4ô~ªÒo|¹¯¤û•ëý¾GJ^Û‡6¹?bñ+WäWxº^ z¤„Ã)‡iãÓuÏçûŒÞjóM*ÿó_3rµ¾!ßòØß%'?öv›M~…þ.GuýÍ ›ü¶©µ_¹œZ³±¥_¹,ea4n³ü¤÷–ÿ1,F.%‰¶LåŸþçåŒ\Çjè32¡‹Inø¶ñ÷ú‘ÜðÍ€Öä†'8½K\j˜ 4•œž®÷èû†¼d¡ £I¯©µ)ÍM~Ø4ø¯\3qáÝņuõ’€‹-ðý•ËÊúŒ¦ ±%•‡ø»È}Ö¼~$•œúÌ->2½Œtà}ç†ÿ¬U]n À~ÿ||{•ûþ®÷ÔS«‰ê`P¶Trø©zåKhÈ/g7ùmë ~åšÍ -£ŽÃ·ûóm–̳/¼Ûäþ Ø¯ü[Ôìmzé‘6¯ý+?¥eÂËHœ ,šì Á"=Ì•ÊCÄð‘MØ€J*9¾»fžÃËÈð!@‘§á¡yí°5W[ú©º‰³C{é1co;׬–¢øOäöÝw¹m™%ùÕÝ_?ŸÆêÒt¬_ŠüwÒ /#ýÝ/ÛÜ÷ÈMn ‘6¹?¦ðûg=7R¼XêZËlg1‘WØ"®¥1=ÿU57ìCÞkiÔÍ·Œ¤cÇ;l•_>(ò??ÝÈ%äíg1‘ÿ)1r­ ŸIvÃ~Ò[’ìõg„~åb®RCJ¾) >97<>7¬òÛ§Á—žNO×;\>=ý±·$ª<9½%¹á ¶ÈC…‹ÊÇWE-M%‡‰CrÃa‹(ò?K›‘ë)ÿ™ôTrè‘%cÕžú•ëBž.¦Öžüú•ë!æÐ ôTCËÊg,mòå À–Þ>ìÃ9*¿ÃÊ'©äøtÚŸävi,×#Ò¡eôˆt˜R%•<¡e47~jK÷{•¯°Øh²×ïÊD^ÉÏH²÷ò¥BK1ûd€ÈSEåËHý•Ë®,l&Z}dø©"OXN …XäñÝõÑðîϧw×ãÝá«jæÙÔU¾|žoI¢º|èUå·= WK2Ïþ`¤Ê+ >É<džÔhgø©ŸÂõ"Ó’œd¿íud*ÿ]ÊÂO¹½åüŸÈýÄ!Y󴮪<øwIƒ/Ÿ;Xz¬> íùÎYš5¶ž“3|ÿôô?/gäŸJýUžv‡¦üй¿ØäŸÈ½1ã¿¶ü¤§w¨ûTÃ’:޽y…ÏÒ+ÂÖü”­yè*O/#¥qa7,òÛ—ÛˆüO|ÒÈ¥Å×vŠüO8ÓÈ5›ž~~“¹u“‡£\ë”°±¯1\×Ï7¹„sÂOy(XRöáO²«üO÷_–¯©•²Ps¥ò#,Û á§Þòî¾Ï\ºE ïþm'u+-DÞÁvJDzGÓÿ,Ç»CM®ÇÿDþç;¹LKaŽÜ®\-#l»¯7„-â-³XØkßßâ3·æùÂÓ5‚Ú]×~&²ø™4$âûû#A {hÿŸÈ}ˈ|…–‘”pÄBäŠÙŒ\¯OÇ>Ó&í.òÔôX/zðÅkKonðÇÐTîa¿rå"†—‘¼G˜ôM:úøÑðî²™‘ÚGV>ï°ëG†‡? WR˜áñ¿–˜žß —ÂýhùŸ—3òùöSGžnÛ½Þ¥áòƒ’“é¡Ä°–6{¿Gv(ØÝå¾ßåí7ˆ¢þÓN]îw%ÙÕò!‹*yu;òDýñáÞÍÈç›ü¹J’ä+žh—‡—Q¹oÈ–ÞîׂzçÛ/ðÕߺ@k³[?P’´‹¿T¾’ßÕìrß0’ó(¼Fg0ÿSUîOà×HðÁ–]î_Fâ×Q.¤wס äýM~ŠÜU r¥,a(`Ùå¾q—ûŸªr+‰Z•wJuÊððyá’PN’K('Êå3ùD]Ú2~&`Kù:ˆ]nír½£'¿ÿö/#!…òùšz‡ÚGCDWx•ûüú&÷[ÕMîã¢u‹ÍøROX‚%tRÞç—œ˜)öÙåŸî»£°)¶¢$t?’ÈÖæ–YÃoöJilÈŽ¿|Y}=Ú}ý»«ÜWÔ£îÄwßGíFx~}ŸQy›Ü~¦–jùÝ[ÿèÀ¶ïÞº•ô¦ýÞ^þ+µî$Ó»è`²c¯t0}”Û>ÐïmmHïµTø—¯Çh)Ùý±uÓì§ÊÛoš[÷Ø>,ÓïMóá'÷Ö-vú¥ê“|g_º§ O—æ ›ÜÇ {élm'È^bM|RJåíoˆêÒÉÝ¿L©÷=l“ÛuLå&÷O侨<ôH‰œ„b–àF”Ï7¹öÈðî*÷CUj»Ã¬òöõD­™0´EÞ>ÒiiöÙåŸîjëŒêŸÞ2<¼ok ä„þ®r_~´ÉýI8•§N —…½dkÊgj6¹¯€hY…áÑ:aû±*1«ö!®íþ«Jám¨…êwÌÊŸÌñŸÿ–‘K3ú}DKéj…#ò ÜäaÍ–ÂØø2ßáQëRzŒ/‡k‰v˜P5Ra‘·åõ;°x‡…éÐEÕw©þlŸ4Úåá—ê4ž®]&4û·.£ò°K¹¿þW®‹jxwM_„§K‡ ¾M®¶*Ykø7l÷TîoŽh Ðzx[µh;ô÷MÊ}@¡¥ 1ÄTÞ>°¸ÉýùÙM:°”DF¹¦ B»ëâ: ¾0_Ÿ:øü ¯QzÛä!x¢Aý°¨<ø‘{Sµ¦ ÂîSåa­yá ‰°·yZVUîóF­éŸØä!$&òö•›Üç/ZîfK‘·¯¿ïK&Ž0š4³“ZFæ™Ôî2Ï„}¹ÈÃÁûÖ4SÚ"ƒïþöSe&H@f‚0šn«ÁBhF-ìo«~ÂÖ,–/³Úä¡Ý·™*m‘·¯iØä!J«ò°·Õœ¯öØäaJy¨GÞä!8£ò0qH†/ÊoòO%*ƒO·*¡¿‹<„ODÞ!\¨éÆó¸u3~ªÊCŸÑàÌ7¹¯ÔÞåaxˆÜ§¨[n ‰X•§eXåaÒÓ¬pøLš· ¡N‘wØ<¿k©CÅ¥¨cì]åþnð–{;dƒ6ùǧûö|›•TüÉ£=¿’©<ìƒÄüPyû¡~tì…–yHÆ?ÏùHî>ÊÅýøÍðhªß÷w•O;Zž.£ÉOb£¥>p¥ò­$Hï®…¶Ïlr?-©¼}íß&÷ÇHYCˆnr¿3¹1¡mr¿SÙä¾´EåíÏ`Ìϧèû&÷³•pø§KMÆx;3Z“ácÒ*Îm´(#½Ì§¢áy×p„æ]Âqyg5Zðá‹K7¹«<œô© Žv´>Ä1•Oê¯ÁqùDªö~`ÞªCTÞþ‹]~¨ÚßðMEîýÀhÝŒßÔ¨>4ïÛÁ~ÿíÛq“û¡$¹õñ¡–]Vèî£ïîû¯ð}éHªtÂ|*éÆpàs$Ý8ÁBnòðti™`eTîõ#ùÃpUÙHÊn|n$kú»Êý5É#ÁîÀ2‰^‡»%wyxúõíé*÷O—øò„%ûÖ† r™Å|df$´ß]'`?<$ø7þ4ÀHx.õ÷[GÓ7¹¯^ …)æÑ†ô£Iåa$á´”I¼b‚áAxú!Ûì€õQùá3|Ç6¤}w•¾Ðæ½g¸{ì-ÓáË"Ù~ÛqȦ&ÊµÏØ|ˆy?|Œëòëè=ÄÓFù§•o“‡N òÃïS6yxwñ´ã‹ 7¹Oª¥ÃÃÿT)¿Ëð¡ŽÙO©‡H‡Ëf©xQ8ê“1ªŽ ßìR4œ>ê¶YõvãÐ9éÓÓ¹f“ûó!%ÌÁë«<9+Ù—¡»«<µŒÈ}\é8t;^F>ô#ò ×´[ ¼ŒÈƒ÷9Ôˆ}zúá#‡† ÂÐVyêbº%óKÍ¡3ÿLR­=¾LìÐðIÚ[´Åw`•‡Xå¾ÔJåáJ¶M&Žw¥yè1o±¯Êq¸#û8uÎ"ÃÔ§R AJÁ&]]CP¡ÉUPNhÈK÷KáetIõr paò•׿;X…í•Èßä>™rh€+x•û‚áMzäm ?U»XhHÙÑøÜË&÷u›£rĹ4•Þ]náÝemòY,•‡Ü¤ÊO¿jŸ’l ±ñMî«rvyhH‘{C®òØî*ÿÔ2vu. ÂÓURvþe¤Vðð'ãÏÒ(”™-QäýíÝ[ZÆ÷™oiX•ǧ«u )r¿_UyÚ*÷NOågèï›<|U Ey¹ä„ƒ?µ4Ì­],<]:Ï2œ[ 9Èå3Ù:Ï„Ÿª—ðt™g©òôôOu›ÜïÊNAçž±pŽúÿ™TîƒEç|ÊÙš/÷ee*¥Y§$ÀÏ0-iFÛ—Qª<¾Ìùí§JÃCrÔ§¿‚n—|ºŸÅ$¥}ú„ù.÷‹Í¡;Èeå ‹ü¡+ŸwM܆åàø?5ùé«ONÍ7†E^sv>¶tÊýMÉBÈÅ@!bxJòë øüPWùékÇwyhéÀÁ^Ÿ*uÏKÛ=<]Œ¡½ž—FPÂËÌ·§ÛÙˆüð7B«üô'\Ïwž/” ‰ú÷á¾ÿjÙ{˜€µì=XM"&¹|¥ÐݵJ>½»ÊýW’TÙz˜Õûš¢SR6gøª[ÆÿTÉcœ¾–à”;BIÆ©ï£Ó²÷0çILúô1éëGWût•‡Í¥5øéé‰ò~üÒ|?ͨüôx€KƒÌ~»zI9 ¾ëGû{hé~ð]?šöø(-ó–_¾.ãÒã~&PùéÇê%ÑËPª{éé„$×…Ïw1^ú•ìÚ"€¡!µìýÛÓ}aÑ.·“žÊãW•ìëÐ. 饖‘é×Fû‹<>]†~âЀazíÀv¡Tùå÷ý—ÄÑœ«>T~ù Û%ç6þ÷Òs>½ºÉÃB)Ç<ÒÓUî Ê%ÀËg.‰^¾âÒC$>5¬ò°¾ôˆ¹O"^Ò»‚… Û,Ä&ÿøtÿS5èÖU‘_Þ¿_£uHáéëÛÓ¥ÏøÃk;Ó^æÓ˜Kbt—Ñ]t»|òö’ðÌ勯C§¥ —\ªDsN­¿´ØÜ_o¦òÓ§=.-6÷¥*?ýÎFå—¿7ëÚªÇ}ÓHT’k9Èeúû© ¥ï‘*K™È¯àRO 2ûÁ'ò4‹‰<~UMóùþ.A·+ØÎS‡ÇG¹_ä%Fws%1ºëòcUkñþIŠë£\ó¥¾\:-¹8ì0Gª<¬«[ÔÍ7¤ÈS»ßj®ÂˬÓÓ}õðuk}€oH­ô÷•—„/T麵G†§ÿ–§_ÁJ0ÅgDž<ÊC¿5äzæ+t1Yµ}9ð%Õò¡bEå!UvI,õ¡(¥†½‡È¯àg´žÝ'7¹Ô^©M!‘§ÐV>“D ƒ¹R¹¿ªHå—/©½¤j;ÍÀ”¾Â2üJ§Í°†°ƒûÙäö£ÞR…^}—ÛÁ´Ë­ƒ¸5žîcE÷vI•n aûYi—‡Ÿúîa·_Éî­¬Úvw•‡ü-ñôøt™¯C»ë­¡> ¬òp8DåWøªRk|¥§Ê}ÝZÞëžÊCÚc“{KpëE ~5¸õ.¡Ðß5Z·F¼Ã<#ïÛG拾˜oHI‡iIä·ßKÜÂö»Û]D¼o“¾%„_FåáeF^&<]mß D~{£wk<Ý‹¼KC©áe4ž^Fåáe¤¿‡Á÷aK ê?Wèù»ÜþÀÄý.y||yøo»xGðf§ÆwùWRùí½ûýŽìÇw9¿½Ë)ïâ»—V‡ùQå~Û¡òÛŸ†»µÎ8ÌšekªÊ} ÒÝŸB3›üãÓý®\å·¿0çÖ I°>*÷›•ß¾FúÖ¢g´º5¡L¡Èo¿+¿µèÙïÊ7y0´"¿ýáŠ[ïK/£w”„—ÑR±Ðîš® OסÝuÕ rÝHø.¦÷x%ù»GÞ¾*n“§§Ë¢ê¤*¿“\{dx™ù&×…/´Œ®MA®)>ßßUîë§6y˜Ež¦T­÷¥b*¿}‘ÿ-ùÆÛ×ÜÝ’L/#òô2’@¼Ãrpª‰ /Sß^F¦Ô`P4ßV‘§¥ìü¶ïØä¡eĺ·té¤ç'ìMîßõ-„¶ÉÃÓuŽ OWyxºô_WkÕ~ØJ¨<½»t‚(’ôäíãÑ·¤'o`¾/M ‡§k°Åw`•‡°^ækT DnI ž! -ò;¬šo )òÛg3w¹ÿªšÍ ñ ‘‡ÊƒMž~ª ¾oO¿Ã„­Ç%|u–ÊÃA×[OW$¹ ¾Ôexø\é­ÉÏôtݯ†á!rŸâSyêÀš+ “ÞvîÄw`Í•úLÖ­éÉaùíO(Üšžô%Ø»Üw`½ö),"¿BòHÎÌÜ! !éÉ#,6›Ü÷H½öé«Ü~¦Go‰òs¤Êo_“ñHÂññ‰€GŽ~Ò{äˆÍí3à&}:þÑ{œ’\{¤íbfÓÓõ¦¥ðS?Ý´¤òpm“û™@å·¦?š,MOW¹ÿ©’ý||(êÑ‹™üb³ËíÄñh²Ô‡¼ŸíxPx™°}åÑË|´^å©Ïˆüò7<ÛAá3‰Ü›Z•‡½Ç£X—Ô2"÷«‡ÊcC¾åìnruyô¬’þlr©}JýLÚ «üöwe<š¹Mï.vß{1•?am’ÌíãC€dnãWUyøª:šÂÓ¯oOÿ6#ÙÛ',ÚŽõ¯¤c£\/¹÷Sª^Y\ê;{›.ÉÛÇ'M¯†Á$òËWߪýñ•B¦†ý]¼*OÿÄWSy(…xô(ª?-±Éܧ‰ç0ç©ÜŸßyô «¯CÚå¾e$OýøÄÄsêðð?UäOhHÅ…˜…È“s“Lòü¸ÈŸ·{ô «ëªü }FåéÝõ ßßE~ùT™ÊïàOômzºØÈ`Åôª»ÔÅ´Ð?]þá2éç[Â_åḄÊï0«Ü×”ó+ÿæÛåö,ê.·e“û+~å¯ÑôûïðUïoO¿åéáݵª/|U‘‡>£GäíÎf“ß¡e¤H$õH=€ºØÒèOxºÌÖºír»ÝåaÒ¹7†¿rÚééŠ -ó’ÿþ;<]oEû$?~ÜB©ò߇v×[ÑBŸQyhw•‡þ.ž 5¤Èí‰úMî/¯Ûä·­ø•9¯ô+×ûýËHùŒÏ‚ÿÊ¿”ÏüÊ5œä²+³5T¿rM"ú.¦WØhç¯üËA›Üþ•ËW }¦tãïåR—áãb¿rµû_äþkF.Û[{íÓ¯\¦¥0ÏhmƒÝÞþʥݭ!ßäOX¶ër}Ëè…¶¡Gª<ôw‘û­ù&O3Á[þÛR~¬n—ñ†w×ÀnxÙØÒŸß?ë%jáeô6i?<ô0¸Ýk«ü÷ß¡‹}¹÷i“?öÒ •ÿþ;üT±ÌÁëÙñà°˜ö|oy|)nð%W¿r¡Ý¥žà Û[ÍøÛ3n¿rÝ•…w׳ã.Æñ+§>Ó–Ârqøþ®8ß¾e>%ªåzSª÷M¨ö.¿rYµíÕÖ¿rÙø‡eX’½Opš½ »ƒSK'¼üýý?÷?õ’ÅÆ&2~åý–‡YLå!N°ÉÃOý6¿«<„¢$µæo¨ý•+~ÎO*;JI~-uû•k…‹º¤§îðîšž à–.¾ê­~&¼Œìõ3Á­]Ìw½ÙÖé©<>½Õ8¸¥û[–Ò ù-kSØ•é%Èaï!瀯àßo±?¶²èW®{CˈŸ ÓÒ£ ¥º&¿lzêW®¨Žðt‰„U[ÓSaã¯ü¹0¥j6ËÞVó+·d‹7¹ÏÙüÊe{º˜&¿lN~“û£Y¿r=ò ÙCå·‘?ßž®£ÉþÔ¥÷ÛúÈ_¹noí<³Éo;Ïlr]šüò»áMîr“ÛJÿ_¹,e¶øWþ¥Ò“ûJºMþø¥lI¾É_c¸Éý­X*ÿýLáe4r^Fvò>¥¹$ùuùˆá’ôTØ{ˆüÏÖ…åšÍòÁÑ% $ãË.÷©µ¥Gžý¾i“ûÊ/{àW®¸ ?¨ÜžÞå©!e¬z/¶äöé°5W¹?"ô+ÿR’¨ò?› #×ó×ßä©‹½ÃhËÖÔþþY¢Ì~›µ$ÅóMþç¿Ær½9ÛÇ –d„ÒÄ¡'¤ÓÓ%Œ–²’¥Ì\ÖvsvÏ7¹,a¡”RÈLìr?-•vmaõ¯üËe*ÿ]ùü´¤òÔgîûÿùÊäá3©<ôÈç[Ë<ßZæù·¼Ì ã5àâ{ä&÷Ÿé-ÿÓ¡\)ø–Qyð‘"÷G@åÝ÷_UÉûííÒsïŸäþkF.ûU{+–ÊS,ui:6òÖ|Shw‘ûèÊ—Ïk¯-Ùë­›${}‘ì¯üË)ž_¹"…—‘.Zf$ÀèSkk¤‹Ù£-¿rqza–ÕžöµÉCiiÂ4=]^xºô™0akÂ4½Œ,6ö`ä¯üS=°Èãrpè&î“ÜWJÿÊ×7¹–õù¡­Ç»ÃÖ\ÎkûC»Ü'aTžvòzQ¸/xÜä>(½ô𸎮ã›KÝä~&ÐÃã¾Üf“‡E^òÚ+u½ƒ(4¤'¹Þ÷I~…øÐøŒºž×³Ø[þghäÒ߃K•¼ö¦¥S·YáÝ5Nà{¤¤ÁŸ`;OéÁþHÖü¶7üÊeã:°3~|UÔ’“½!Ù»äì­ÿÊõòa?ψ<”$.Íɇ‡^bz"Õh"ÿScû—åË—õ-9ìqŽ¿r½$ÑÏz˜ÖÄ,=L›ž®È¬ ?v6*¶žŽ .õ–ýjX†¥ü EjEîï˜û•+[ÒÏÛ¡N¿®Þº û.¦9ù°Øˆ|…ÅFåa¡”Œ¿lò3t±[v”aw På0¿«<øw)?XÁvêaZ_%¢r'ºÊÿíG“–„YLä)þ.åOðï†s‚\2 Áv>2¿‡Á§õamÒŒÈÞêùU_­ ò'Ì‘›<4¤¸¥ÐßE¾B*YÓ†ŠÊS'øÂØä—?¦PR­²æ*ÁQ•‡hgéA`oPJ‹|(ª´üÀ¯*«Ç&÷³XéÉ^@¯´¸ÁÃÚîs/£µù—ƒï›<'—¦õ›‰Ò_ˆTZ থRý<£ò[Rùãצ’ŒÿéK´j}Ú‰<ílT¾ü¹ƒÒó«¾ °”7í'=•{ÖÈ&Os¤wõ;ùÒ#£aðIN~ÂLð–ÿù¯¹^lâ?“¯ÃðÐSš>=%ò¹Úå¾Ï”Fj?ÉÓXÕKÎ}H¤$…ŽH—òO'ªK/9Oré‘~¯­rOQü•‹ŸYþe4Ùë}¤Êƒ1,Í ‡X²·áÈhiö6 >ÉÞ>ÚYzɹÏk—žŽM/óŽvVpzQxpz ‘Nr _†Ÿ*ɯ0¿ËUÞ§¸”ž½Mr=îêß]Ò±G°8ÈeZ Xä—e/üÊeGf1½Û§Öj$>V>•‡.¦ÙÛ° ëùUp)e§§kÂÔ>Í€†µéIÏçÊJ¦‡¯¾ùŸiÊÈ%ºŒá[þÇW¹’LÂÓÅ2O Ð38lÍ€ú£\¥÷a'¹N©A®Û¬ ×9ÒËõtl’‹ô§ÖJŽ»†€zIZ0™ÚítlxwIÂø´`ɽÌi7¬×8‡±ºÝËì{¤ä(ËG÷7yÚ*÷)üÚ®qí.!À0¥ê5ÎahŸß "¯Ð ô^æ`;ås(¼Ûåþ3é­Ï¡eôædŸoùŸ ©‘Ky™Ï”ÞmœäbPÂhÒÛ‡CÞ䟞jPDþçÆ˜¿,/_S—Ú?q¼åŽF®—á„–ùÂÖÙäÉ¥^2Vƒ1ÔËŠÃgÒtlEéið0Ü2š|zªä4øéŠüOÞÏÈeê³**÷·±ª<´«<Ôîr?q¨<¥õæä°Ò£éÁÏh¢:ä=D^!–ª'Ù}Ui*9˜«-ó¾êóíéz'ú;ÙûçåŒ\v6a#¤ˆê0é=pñç(Kr”6ÏLz~#ÔKZÆOK*ooúÈøý·ýª-·S–/·ÙåáeF^ÆŽÕ–wí›–vûhg¿£Ì¿ÿrí3áed&_UN µGîòðtí‘þ3µ4¤?lµÉýêÑïÐkl‘·0îrÿUUîC¯ÝÚßíÄÑïHm~•‡–yû±*÷éµ?·Éýå~»Üw`9|Ò>¦×rŸ^ê‘rÄ¢}H¤%ؾög“‡§ߦ%•ûv:¿‡—‘†ôY•Mî ïúÐ Û÷•ûc -õï±!EîïéSÖU_à»É}<²¥º¾½AÙä¾:§ÏúötM~9PyzºÎb¡ed¬ú‹Å[¢?íO:¶Äg¢\žîëÅZʇ£\ê Âêñ|Ú®|A®_Õ¾ûHØøÈÕüèr`_Fåã÷«ó#?Õ§G,󸔿.÷ï¾d«â7 *íÜåv¬nr?ÏÒ†´C{ÄÚ#õ3íã‘Sº³ñí¾Ùý —Ÿê#(#v¼ã±ûŸåv&˜údPT>>>3R˜‘¾ªÚ}_¶ËýOÝä~h·ö÷Oï>¡Ï´®«áeDm„‚\­[hH‘ûxä´z1ÿSGƒ~hΑAþÞL˜–twàëGª:,e›Ü7¤T+LX›d«’ä‡Î‘¾eÙfù‡Ê'Ì‘Ûf·ŒÕwÎÈî`üî`Žû“\ý{X(ÅaÏéê;¯ýçÿÜÈu7쇇È='®Gý{zº¬|¾Ìi“§Ÿ*£Éï=6yz™Å‚ÓÓ½‡¿½l—û¼ÉýÐùøíí.ÿöt¿ÍÝf/&òñÛ¬Ñ]YxºÈãÓ5|éÇê¥q1ßÅTîSšsé®,¼»ÎA.C;Ì×§øÌ\ê߃ü['Ø6ϾHöv|qòÜÚî~&¸53ž®r?Våúìñ1½Mî¤Î­s¤ï"Ÿ£9;Á\m1ÿ2’ç D±yäÝÓÓµ¿û¡ý¨ãð_õÑ$LxºÌÀ>:’EŒ?Uå¾Ïlr;øŽŸŸ//³ËmÛåáe´ÏØŸºËÃ˨<¼ŒC¿+Ûä~>~ÔG¹&íXUy@÷;üª}H(êð¶óDõágà]nùC]‡_W7yzºLØ>ãȵ¶í‹JŽ-êäÏ'¹DÝ94Œæ¡F £I䇋FóŽãûRÇ—õmò0ø$'?~¯}hLÏ'LU>¾Ðô@W¸©äÈÕøä×!ið 8¶¼¶Ÿ–Z ŠoÈVÇῪÈfë˜O[•cK²û–Ñ@—¥è ·}lr_Duh +½ûõI~èêáªÊúzh`÷“<>]JAƒãxmñãú>Ì¡óÁÈCBn‡ß¬[þÞT‘‡C”Ç¡ÕwÇ㓽>$ ö)*GcMÈû”Ê&÷Õì‡æïýyQ•‡K!ŽóSæKåÉûhì2,Ù» ë˜Èãg¹‡ =RC>–sh02ؼSÝIèÀº/÷æýÔÁž®Å3þér Óc ¹…§k,Ç&•û»; ¹¥§Ï·§ë¢êÛ]"tGp´*÷ÇCzQ®µ6~¬Š<œ>4þ¶d*ŽVåÁûHt1>ýSÌø³!´±Ý:Az*¡ÅÇw¬ÐÏ_†Lmr}x´ZÍðhÙ‰…}žÈ¿ë!—âþŠš]îç:9'j"U>þ8ú!áÖŸ_£³a6z4¸äZÿà{úVßæÛ]BÅiz‘ØïÖGýƒ§±_oN ˆúEãüQ·i»˜ÊÂÙåáÝeBòqSøjGz÷·<ð¦N‰pž>Âyþ|ÚÒž?Ò RËh€ÈNէܶ‚,*O ©Çˆ|úXå㫉N‰ýž>S~ê!%ŸxQy@ŸorO:—ÆÜÃÓ?Õ*mòôt-Æí.£É_zj ÚŸ~Qùá«+N‰[ÆÁ)Õ¢Q®EŽvå;µ¸Ô‡6Tb¿§Ö¢†™@äáÖ¯Sh¡m“‡†,@…Ÿ*á_éz–,>Á{J MK*÷±_•>ÎrjzÁ;ŽMîs§¤BdYåã·©›<µŒ,e>*®òÃNέ~Ù/Zì‹UnÉ8µJ7|U•û ç)©‘ö{SªtÏÔ2}Ó$Ú?¹µ¿,?}øé”4Jئnò0ãµ&ÇüÓ¥¼øôgOeµß4Z^ìÙ*÷í’u 5·§f]R˨<¼Ì·¯ºÉÃÓß_õôñÓSR@ã¯j85§ãÏ*ª<Ü_pê9Ë0[«<8ZÉ¥M“–uû½­ÊÐßUî/>9·3¢¾ÏˆüðAÅóÐܱŸ8Tî·ñ»<|UÙN¦†Ôˆ~h æ†>#Ûɰ·Õã°ah‹<”Ÿ’NKûrÍùã*?à ¬ù±0qˆüô·RœšNóIx•‡s *¸îSSRÁ·‰<Üc{žj€ý)ò#Ìï§îÃOÕ|pøªºØ„§‹<|U)I?ÂÎYKÒÃWùÏ©é´à—UúŒ¦ÓÂB¹ÉûϷ§«<<ýS‚WåÂ'—Æ.ýÄ¡rÕÚyéà /£r?ø¶£¡et÷>Ó§ 8T8§4ð·Wž’ üûÿ¹‘¯or=ºîýŒÈOìÔl`X=DÈW§$'¬|*÷Ù£Srgˆ¶è¹„åùéï/8oÝ€ú,Y»4ÏhÖ.D¹$¯v&¹Îïþ§Š<\†¦ò3Lz[ÎU‘Áa W«Ó˨ýù(-#Ø—ý†Çƒ\gàð2"O?UÂ9a#´e O— ÛWWœzÚÄŸÝRùéSæ×¦ÌíÓUj?¯-¹žþ©²ôÒ{Rü<£ò¹äøH¨ßå¶Ï\’‹ 53*?½ãPùåGÓ¥gYüʷɽݿ$1|ùÌ×¥‰a¿”©üð‹¼Ê/Šº~tãž.r?é©<Ð0/Í#‡vW¹/X»$Ó\ªÊÃeõ—^–ãe*Oןô–FÝ‚\ψ~“û­Ê¥™^á&÷)óKéH!ÄùMî·ˆ*?ý^û’LïZFS·Þ lò0qˆ<\Q½Ëý”ªrŸvÞä~w òË”k»í)´Œì›|1õ%Iípõ”ÊŸžº4©íw6»ÜwàÒ™ <]ÊÔ2"«‡ÈOŸ ¸4ežÞ]sà¡Ïh `ø©êC'øtk¨Ê»ä’;¼Â…‘—ôUH›<ÌÀ’2!•Ÿah˵Y¡8r“ûsA—m ¸5bèûŒmLnIròár¨]~ªž ñ‡È/_yé9Ë`D~újÇ«uGéW‘‡ó)—ú NOå¾ÀPåã3q—7¾¢D姯‹»¶«áézìÞ÷H©´ÑÎKŽÃþ.©K+-|‘Þ.?Uw”áé2š|ôÒ{߾ɯ°zH ÿò™‰KròQ.3A0W’J¾Âª-ÉÞ+,e’½½B|f“û) ¯Ð#%¿zùó‡—$L£\Û=üÔW»§)ò­œ·ëÿ\>,ê?ÿ-#—oä«i/É8^>Þ}]߯†äÕ®Œ”DY”ë¸-#ò°§²®à”$ñuùÄ×&÷AÝëÖäòQC¸õ£zÛ¦ò°.©< %I|]¾€j—|zø©Ò!Ãn“‡>#25¤ÊýP•,\šUvd’´KøÑ•#<]å~ÕSy˜7T¶¶N‘᧪<<]†‡?ÈwIZíò—žlò°#{tþ(÷ý}“‡N £éÛËø¤Ý%Y¸4ÏlòÐÅž//sKZíò‰ÞûG]^xº.6ö§Þ?:<ìO½%ñuû îý£‹Í'y|ú%òÐ2ò™üÄqËɼۧHnIbÜ>I}/5KÖBÜ’¸ý¡¬[bï·ß|Þõí§nòðtíb¾e$Â|ûÄ]:Gúþ.òÛ_ÂrKèòöñ[‚‹·)Ü­ éß}“ûw—háí³Î·„ÿnŸQ¹%žwûó8·Äó‚u»%„RÝ’›<|U•[ƒ¢ò4-©Üïo ÐÝ>ÈuK-Êuõð/#A®ò4øæÛ”ªrŸ<ºGß TžRä>tyº%?ˆüöñë{¾-*÷ÙÏûÐ.æ¿êñió¬òÛò[Ψó­á¿`Ý4ü—ÞýÛ)‡fÒb£rž¹5é÷«*¿ýI’[B—·»nr¿ç»Oíþ§žºò…§kŦS=°M*÷±¥MúŒÊýô>uÒ ï.ò`PTuo_;sŸº+ O—uÕgûo¼†}Ó&÷óŒÈo×½¯os¤„oŸýÜäÁþ\º®†§K'ðA‹[ÎzܾdûÖ s˜°5ÈüM~ûÇ-Aæ;ì(/º…–ÑeØÏrxãöÇZn J§m–¥ï0<4(æw‰ÔÞ>úsߺ™ðŸI#µ¡Ü:q„—¹¥Þ·z1/ô«~“‡—yÔîû–‘Xjêï ³Ø£ ¥.e¾‹ipÔC%vyxw +|“‡­ù&_UäþÖ§M–•ûÃ׻ܦçÛÐ~Ô2‡†Ô½vxw‘ûHíóó)ú³É}èu—‡§Ëüî··›Ü盞µ¶Ý7¹OÂ<eö²lòô2"÷=RåJ?òö)•¼ËíðØä»¼Éý2¼Ë¿½»OO=z¬%=ý-|)å#ÇZÂq“§véÏmr¿oz~t ?UcØáeÄ ø…r“ûšÏRƒâª&aübó, 0†§‹Ü‰<šâIO×±ê'=•‡þ¾t] Oy˜–4=å-³Êã˨ܯM*÷{Gse~k®òÇ'¿vyhM„wÿ6VUæ•û«îTþøèÏ£IG f“û#÷LS»ÉÓÓ5àòI‚£f@ÓÓ?%LUþøÒ£ùU5Ó&XNðr &MK£«‡ï’5Â<#ÉÞ',6’½}üÁ“GÒ±×?’_}BÀe“ûU[RšéÝå$É–•ûƒ´ä(Ÿ xNmH¿È«Ü‡^w¹ÿL’E||Òq“ûsd£\‡¶ÿ©—~Õðt•û.&‰»4š.ýªáÝUÚ]ä>é¸Ë¿½L˜%‹˜Rå>Å󼳈áÚËçÖ‰ÀϨ*a9ªòO{ë¼á¿’ÊC(U2šOþ¼3šá`·¨óÿõvÉ!>Á‰=:mø5ûѯäßýÑyÀ7»ÊCxtUõ½]åÁŽ?:)…§[³UníÉüüè<ðMnú¿òWŸùónäj \'ø•ËD`³M¿rébvô+×>ä:²Ýgú•_Ò2®ÏüþYúŒ«ÿDîºØüHô=Êå3ÙUu—ÛmЯ\¿j«q /#r;Våò™lhô÷Ï÷[n·óS_VU•ÇÑ´É}CªÜÞF÷+×å <ýÕ ê²õ-¿òþ7ÈÃFåWþš 걡Ñ_¹ÌvåSùoCúYìüórF.‹Mê3Ú#ÃˈÜNüÊÕtúáñ–ç,r›ÿÜåöâŽùiþÝ5¼fà–•ÏÖˆìr¼üýs}{ºN©~Õ~ËcV¹u©¿r]†ÃO÷ÓmÌm—ÛrÇ]nsY¿~ íj·RùŸm‘‘ËL`ƒõ»Ü&4åê8BŸÑ•/Œ&qvS¦ò8´UnóŸÿDƪLið©Ü¿Ì¨Ãö`4˜žþmâP¹=;õûg™gì5ô¿îorÝj‘ÿþÛ÷™ÑÑäû»ÊmõûïŸe›‰ÑÁ÷M†öèàócutöÃCå©Ï¨Ü¿ŒÆÇm^âW.=ÒÆ­~ÿ,=Ò†ÿ‰<<]£¾ÏÒmÐâ÷Ϻ³ O×þî¿ê!]Ìž:ø•ë„^FäÁ-âÅÂô\ßä² í~J Ó’¦1„}J öGŽZÅwWs^Fw6þ«žÇ·w×)54¤ÈCôçÔh§ŸRå˜Øc«RUþç¿fä2é…EþÒþäÒßC»oßî—ôÈèºÄ2ÛXêïŸoòó›\¾ª­ñÞåöÔÊÿí;Á¥ëªŸ–néa§ò`;7¹_WUnÓv»ãGözg–ŸPEýç¿eäïeÒ&(ÕÒm±îØ rIžaùðøz‡öÓ'z«ýÍЪþ}ßè%VÆÇÒU¾üFx•¶zø¥"O?Uåþ›J,}ÙÓmÿDîÇÝ&]à=µßÄ«Üß;=KÂ×ËG¡–DŒ—½‚ê÷χÈý:£qQ[ú+×±Þýþö2*÷/#´eoþßåá«nòO÷ŸitöøÎóàÝ_µ¬y>ø·ËýØÓx›B©<þÒWˆco´C†Ÿú|’2_‡v¨ió}@å>³ºÉý®ciðÏïáUž,¡Ê}ðoßÖ•ûýØ&÷u9K‚iSy˜€UîƒP»Ü>‰D¦)òø6:²¸$²˜ä×·Uå¡Ýß‘Åò`"•Ç–QyØ^ºóӌʃ¥•¨¨,ª:í%UnK@åÚÃ˨_O—é:ìlßQÎ;˜B‰‰¦‘ªòôKEî´»ÜO· ŸLY·t0{Æõ÷ÏÒ|LtÝê®ýO½¥„Øœ9—¯ZR—þcoMÚäi¤ª<5äýíetþ _UäaÓðoä<ºJúϤò0‡©Ügv6y03›ÜÏy›<¼Œ`ÿ™Tž~ªàðî"‹°ÊCÞäßž‚ËJ-æGå>Ý¡r$v“?!t-†B9›ÜÎbõ£æÝ>½4á×øMî§Ô’tGØ—or_زɽµª]›BËÈàó¦s“ûütméŽorŸ,ßä©edmòe“{©òò¥ú‘•ÏO©*+_ifÇÇCJ2;ËçjêÙ9B;.{vÞØä¾¬w“‡î®‰£Ô0º7ð_Iäå^i¢)|T•{³TK·Âá§êØ íþ–Wúª¥õ#{éPý$OcOú¤>#cÏû“Mþñé>0ªòò«p-IYÆÊã)òòºÚ2‚¾Gª<¬Â"¯°Ö¨Üïn«4Àá¿jiøÏ÷H‘Wp¥[ð2Ÿâ×%‡ Bì½J-°%C;}&‘‡5^S«©etôr9w¢´¥©Õ$ÿ.ÍĦw—NàkrJ·:$n£üSjµZ^>Z×䟊›jô3ùX3±>½£ò -³%nÃÓµ¸)<ýS:¾$·Zyóûçû›\ǪowÍOúIiÑ×dÔ¡ƒÏ÷wM ú}ßF“fáÒÓuŽ ri÷Ð#%­–zä©«¶ÿ©’øŠòw»—O°oòÐ2*–ù›J‘ÿ¾LhÈO¡¥:Å—ª™¯`ݶ̗w©©/Žº.µnA®æ*Èû›|¾ÉorI’—½§èŸÈ}'ØäÞ H¡yHN•¤lB!OÝjȃ¼¿Éu¯亮ú™@3<>_·.¾èÍCI®¡¥ð™th{ù£¡%?<2´ýá„’"ùòyŒ’ú¯ÊÓ»‹ÜŸh ºÅ¦òOï~}zw‰Ñµq´ÄèÚ$éCûŒÿª‡þÔor2d—û†Ty°£‹ï.rŸÞ徿K0ýT‰¦.¦ò0V7¹ï‘ïøbù°nK¡|ûjóMîK[‹±ÙUîƒóÛ¹ÉC;Jÿõ¥ -áÅø2*/#r²Øå~á“Xgû(pK¬3ÊuŠô_Uå>ßO¹Ï©ìòo/>“Ê})ZKX7M3*ÓÌ&ï®3Á·wO ©òo/ãë[ZÞíO¢nò0Gª< ¾M:°Èý™™–`}²À*³˜Ê-*âW®¾0´»ÈS»‹<µ»Èöÿ’å láT„Êê½ÉýO•4Fš#U¦¥Mž®fÉÕMîg‚MîÛ]åaÒÛäŸ>“.”ßZÆ'§úÖÕÃw1•§>óm~¿uþ&O-£sä·§û”cßߢ3*O?Uä!ˆvëÎel6¹ŸgæG—áoro6¹ÿL›ÜOØ›ÜY­âH ©rûUw¹íÀ›Ü÷÷]n—áMîûû.í®r»òmr_é¦òñå£Å6Þnr¿6ÍîlBŸÑMhÈO±¥]ò“'Øä~囟OYÁMž^Fs_ßä~'¿É}Àe~tm _õÓÚ´ÉÓXUù§—‰£IW¾ðUuåó«‡h…Í&£Iåa9ùøªÔYºò…§kÞÃÕ¥›8?øTÆêÒ`QxwÍ„–ù”tÜä¡orß#Ežúû&ŸiDÞ]X„¯¤pxÝeù9oé>(4ŒÊÃ˨Üû•ûÊ&÷5Û³¾­›<üÔo‹ÇÒÅ#¼ŒæzCË|›¯Už.ƒã$€}ÕHàø “GÀ‰a‚|¦•ã}H·ýõ±#gt'¬brF׃—åïš8_e*âAPµúŽå}ö%ó->R‡}_׿ŒìÖÂr¾É}‡•Cß,à&÷ï.Õ­Vh9R>!nó®?½}DvÞ'Ê0BÕᓊÚ'ÄD}û:U‡©YÔ¾$WÔéæøÑCùa‰;´+~iò?=ù¯ËƒŸÛä¡o©<4ûk½õgtDüç¿Åòwu°?‹-â05¿Åá–v}4oºêû®¨ò6z×@ßÁ@Ÿºè‡vQ¹ÿž*÷'F*¬ãç×)7ÈuÊ rr½\j'™¤zpžþ]°×¡ö\äîÐ)©_)0ÂòK¡¹w`‚\J»&„/¤øjB~PJŒ&$6yxwýªßäé§Êg 1¬GfÇ`]¥þc‚•lû#(iÜñi­Cò²ã7Ó»<<]»˜m•þNœC’f‡?~H®çð‡Âw6¦ÊïÒDþÛí„}hÄÏbÇ;£Ñá4„ÈÛÝ¿Œæü.æpóáÁo‡D&eGÓ!¡ÆÃß pÈiãÃÂCb‡‡rÚøð‘õCø¦N ·ð~gh„Ï_>qh„ÏO‡áü¤wH®¼UùŸáò×å>¬v¼ãdñeF[Æ?]‚GéeFûLx‘ûàç¡áŸN<äÈîáצ]Þ]§TßgäØëø{òT~øÊÇC.¾;B'8´øwª­ñðC.¾ ög“û’ºãÐ8¼»È}ÜîÐÍ¡§®Úþ3©ÜokÝÙ„Iï½³I^ì­J¸£û8µG†w¹?ÝyÈÊǪŽ÷ ɧ!9òøSÇû´[•߀Šü÷ß¾‹m›8ß"?‚[ºt±ñ}F6qGú©2ÏøpÈ&÷ÿMîƒDÇ­Öͯ²çKÖí}y\‡ ‘ǵIä‡/1:äMü©Òg|Xù-âá/j?äŒK|ºÌÁ‹É—ñ·Nªüð1ÔC®¦K¦VåéÝUî'=¹ø.Äg6¹B²×Nv_åað=:-ù†”#Q.› _•¯òÇRGwe~ÒS¹Ïænâü<£rºó”(DØÉŸ["Èeó›8• ±ËÃ˼ÖÕ§çÆÅl»«üô3Áù£=ÒNK*?|ðüÑi;ð)”Óï(OqøÈ¹tÏçÛ]å>‚¢òÓG®Î¥}&ÈuïaǪÊS»oÑ;¿oro;Ï¥],4¤Ô?ÉÃVåÔP”ßøŸRÆv†iI#W~mÚåþ3i +´û&÷£Iãb>U¶Ëû«1ôŸIîø‹r5†áÝßòÓ¥O¹ðôëêù-b¨òÃߘy äðþý”‚ÃÓ—3ˆß¤òòÞä¡!ß¡Vûh§ÈãñÔ`‘ï3"?}5Ú©Ù,ÏPÛäa›¥É¯à°E(–*?}²w“ûSZ§ a)ù铽›ÜÄœšçó¹ƒóÔè¾ïï[ž/|U‰\ùrúóÔ±Þ]_˜åžÕdP$‹8>Ïw^jjý´¤YÄpÑ,¢/zVùâ‘*ûUM:ús7*?}mò¹š††”†¶ÈÏôU_ý½Ãùz‘Ç`‘V½†yæú…ùf`MdžÈÕ&÷í~ë6Ë¿»ÊÃðP¹Ï•š0 Sê­ÃÃ7¤&LËOK*»a‘Ÿa±Qy˜°UHšì “žÈϰ5ßráHƒO²·§?W¹ÉôôhLÏ•‡œÍ£^,<}}{º _ˆ¤ò3éÞ¹á6Lä¿ÿöcu“‡§k”9¼»îB'>²ˆ›Ü¶û¥¹a¿®nro÷Uv—´{yiæÙ&•‡à%åò¡¨Då§ß7]’Ò¼|*ù’¤ãå£û—d/ŸZÛä>†}IÒñJ?Uåáݵ¢ËŽ&•§—‘´àå7 —¦Ö¼…ØäþœÍ¥™8¿UQyt©üòЫ¾uÉ•]~ÒÛå¾GªÜÛŸKre—_(w¹]Ê6yú©*]L;pxw•ûw—ÄÝåùKn"¹|!Ò./£¡Wß2­“^x÷C^Æ•û Ý%y¾Ë¯Ú»<üTá§Jó™‰]~ªXŸ;Pùå³·›ÜoU.I:^>ÐuIZðò†|“û$ÌõÎóU ‡^šô¡Kò|é3Iž/-ò*O ©«vx•û,gU.ŸìÝåá3I÷áœMî7 ×·¤£È_ÆI:¦Áwèüî;ÊÃXUyh•ûÄÝ&÷•E—äùR{çùâgÒ´`jw«Á ¨üãÓ}žï:t) _UצÐ#eð…‰Cîá¸|&îz'¿:€R¯-µæŸ¾%|ùå£?שñwÿUUîÏ7][F(¼Œžâ òOòKr6?fÿ—å‡O²_z”+lUD~ù(ó%)žË‡//Iñ\¾ÒâÒƒb¾Ô_å§Oöîrßß%Î{\—-ÂOÕI/4¤NKA.aãà°%Mrù¼Ç%‰Œí¼$3BÞ—œ+»|éÄ%çÊΰñד_áÝoýL~¬ª<¬M*÷å×&ÒËÈÚú»åòQæK[Û©!oŸoRùå‹J.9=u…wt7ìÛ]å> ~=úU}W¹¨_P/ó©Êûztm /£Œá§jŸ ï®)Ûo ¨ûIïÖy’:Ïwë 1~¬ÞB2ˆr½¤Âö÷[C%ù§w·‡ Öí–b’\Ï7ùµé–KÁ2ßzÉ¡»õHQ’:ãKt?”±Þr¤èöSê-GŠBòëÖC?að©ÜĨ<´«üöÁÑ[Ž…ú÷[R —ÏóÝš;ð%¸*¿}2àÖÜ¿0ìÖC?ÞßzŠÇ‡ÑTÒ$›<½»”ø}“Ê/oÈï÷)žn¿ºåšñpÜUän/¾õZr_²rKÈûö·oßïwµÏªˆüÏË}ûyf´eüÓUƪD™£¼¿ÉGä¾¼ãÀnayì3£íž~Ë»‡Ï¤rß#%RÛ>h!òØg«aÛä~x¼C€Uþ°•Èÿíç÷wÄð sžÄï0£ÒìéÕ¥Ù}Ií.÷}@ Ôo_N°Ëý²ºÉýÿŽ/vùs·F/}ªì~Wœw¨g¿%yû(ÚýGþ.5¡eôìIx÷OŒ*¿}†g—‡Ï¤Çäý$&ò4_«è¦ò;ìƒ.íbáézÏrxw‘ûÐ’Èã¯ò0‹]ÚßýÐV¹/èº% |‡ÍÁ­®3<}}{™×r°ÒÃÅ£†uò],ß·%ò8¨ÜR¸oÂÓE|žÊÃjð¾­Ï0²7¹ŸfäÞµo/óg¤¹ÌÁ£ÞºðùUåaì©Üߌvß2Íøcb"ÏO×½Aøª"sÞ&÷X ýÓ¼¡ò0_?:ÍøÕàщÃw‚w^Âøñûñ×å>­¶ËC»¼æõvÂ’"¹}Vâ~4zärݺ?!ò8Cª<Ì*!IÀÄŸªC/|U‘ûÀè# ˜°2mr¿sÞåáéï%ûÇ·ŒÈÿ¸•¿,üÝdƒnŸóx~tU ?UåáéºÙ±ªòÇ'KIÅ–Q¹äšn_ç¦òøt‡„§?Ÿ~êÒØ?}©Wò@äO3<’šJƒoé”j§¥GSS¡‹Ijêñ¥òÏÒv÷-£É£Ð’8³É}tq—ûXòd·¦ïòðS%­–Þý5ø.¿?õŸÿ˜‘ËPõf鑜×ÊÀóòyßt’‡¢þý÷GyxõãÛÓ¯o¿Tåþû·~QßwUîýì#GTR˼¨„<¿¨ÿüÇþý¿û÷ÿîÿØœ×Y:´igraph/inst/tests/test_graph.adjlist.R0000644000176000001440000000063412251656216017620 0ustar ripleyusers context("graph.adjlist") test_that("graph.adjlist works", { library(igraph) g <- erdos.renyi.game(100, 3/100) al <- get.adjlist(g) g2 <- graph.adjlist(al, mode="all") expect_that(graph.isomorphic(g, g2), is_true()) ## g <- erdos.renyi.game(100, 3/100, dir=TRUE) al <- get.adjlist(g, mode="out") g2 <- graph.adjlist(al, mode="out") expect_that(graph.isomorphic(g, g2), is_true()) }) igraph/inst/tests/test_leading.eigenvector.community.R0000644000176000001440000000411412302762354023020 0ustar ripleyusers context("leading.eigenvector.community") test_that("leading.eigenvector.community works", { library(igraph) ## Check-test f <- function(membership, community, value, vector, multiplier, extra) { M <- sapply(1:length(vector), function(x) { v <- rep(0, length(vector)) v[x] <- 1 multiplier(v) }) ev <- eigen(M) ret <- 0 expect_that(ev$values[1], equals(value)) if (sign(ev$vectors[1,1]) != sign(vector[1])) { ev$vectors <- -ev$vectors } expect_that(ev$vectors[,1], equals(vector)) } g <- graph.famous("Zachary") lc <- leading.eigenvector.community(g, callback=f) expect_that(lc$modularity, equals(modularity(g, lc$membership))) expect_that(membership(lc), equals(c(1, 3, 3, 3, 1, 1, 1, 3, 2, 2, 1, 1, 3, 3, 2, 2, 1, 3, 2, 3, 2, 3, 2, 4, 4, 4, 2, 4, 4, 2, 2, 4, 2, 2))) expect_that(length(lc), equals(4)) expect_that(sizes(lc), equals(structure(c(7L, 12L, 9L, 6L), .Dim = 4L, .Dimnames = structure(list(`Community sizes` = c("1", "2", "3", "4")), .Names = "Community sizes"), class = "table"))) ## Check that the modularity matrix is correct f <- function(membership, community, value, vector, multiplier, extra) { M <- sapply(1:length(vector), function(x) { v <- rep(0, length(vector)) v[x] <- 1 multiplier(v) }) myc <- membership==community B <- A[myc,myc] - (deg[myc] %*% t(deg[myc]))/2/ec BG <- B-diag(rowSums(B)) expect_that(M, equals(BG)) } g <- graph.famous("Zachary") A <- get.adjacency(g, sparse=FALSE) ec <- ecount(g) deg <- degree(g) lc <- leading.eigenvector.community(g, callback=f) ## Stress-test for (i in 1:100) { g <- erdos.renyi.game(20, sample(5:40, 1), type="gnm") lec1 <- leading.eigenvector.community(g) lec2 <- leading.eigenvector.community(g) expect_that(membership(lec1), equals(membership(lec2))) } }) igraph/inst/tests/test_hrg.R0000644000176000001440000000046512251656216015650 0ustar ripleyusers context("Hierarchical random graphs") test_that("Starting from state works (#225)", { library(igraph) set.seed(42) g <- erdos.renyi.game(10, p=1/2) + erdos.renyi.game(10, p=1/2) hrg <- hrg.fit(g) hrg2 <- hrg.fit(g, hrg=hrg, start=TRUE, steps=1) expect_that(hrg2, is_equivalent_to(hrg)) }) igraph/inst/tests/test_graph.eigen.R0000644000176000001440000000130412251656216017250 0ustar ripleyusers context("Eigenproblems") test_that("graph.eigen works for symmetric matrices", { library(igraph) set.seed(42) std <- function(x) { x <- zapsmall(x) apply(x, 2, function(col) { if (any(col < 0) && col[which(col != 0)[1]] < 0) { -col } else { col } }) } g <- erdos.renyi.game(50, 5/50) e0 <- eigen(get.adjacency(g, sparse=FALSE)) e1 <- graph.eigen(g, which=list(howmany=4, pos="LA")) expect_that(e0$values[1:4], equals(e1$values)) expect_that(std(e0$vectors[,1:4]), equals(std(e1$vectors))) e2 <- graph.eigen(g, which=list(howmany=4, pos="SA")) expect_that(e0$values[50:47], equals(e2$values)) expect_that(std(e0$vectors[,50:47]), equals(std(e2$vectors))) }) igraph/inst/tests/test_bipartite.projection.R0000644000176000001440000000673212325263075021230 0ustar ripleyusers context("bipartite.projection") test_that("bipartite.projection works", { library(igraph) set.seed(42) g <- graph.full.bipartite(10,5) proj <- bipartite.projection(g) expect_that(graph.isomorphic(proj[[1]], graph.full(10)), is_true()) expect_that(graph.isomorphic(proj[[2]], graph.full(5)), is_true()) M <- matrix(0, nr=5, nc=3) rownames(M) <- c("Alice", "Bob", "Cecil", "Dan", "Ethel") colnames(M) <- c("Party", "Skiing", "Badminton") M[] <- sample(0:1, length(M), replace=TRUE) M g2 <- graph.incidence(M) expect_that(as.matrix(g2[1:5,6:8]), equals(M)) expect_that(as.matrix(g2[1:5,1:5]), is_equivalent_to(matrix(0, 5, 5))) expect_that(as.matrix(g2[6:8,6:8]), is_equivalent_to(matrix(0, 3, 3))) g2$name <- "Event network" proj2 <- bipartite.projection(g2) expect_that(as.matrix(proj2[[1]][]), is_equivalent_to(cbind(c(0,2,0,2,2), c(2,0,1,2,2), c(0,1,0,0,0), c(2,2,0,0,2), c(2,2,0,2,0)))) expect_that(as.matrix(proj2[[2]][]), is_equivalent_to(cbind(c(0,4,1), c(4,0,1), c(1,1,0)))) bs <- bipartite.projection.size(g2) expect_that(bs$vcount1, equals(vcount(proj2[[1]]))) expect_that(bs$ecount1, equals(ecount(proj2[[1]]))) expect_that(bs$vcount2, equals(vcount(proj2[[2]]))) expect_that(bs$ecount2, equals(ecount(proj2[[2]]))) }) test_that("bipartite.projection can calculate only one projection", { library(igraph) set.seed(42) g <- bipartite.random.game(5, 10, p=.3) proj <- bipartite.projection(g) proj1 <- bipartite.projection(g, which="false") proj2 <- bipartite.projection(g, which="true") expect_that(graph.isomorphic(proj$proj1, proj1), is_true()) expect_that(graph.isomorphic(proj$proj2, proj2), is_true()) expect_that(vertex.attributes(proj$proj1), equals(vertex.attributes(proj1))) expect_that(vertex.attributes(proj$proj2), equals(vertex.attributes(proj2))) expect_that(edge.attributes(proj$proj1), equals(edge.attributes(proj1))) expect_that(edge.attributes(proj$proj2), equals(edge.attributes(proj2))) }) test_that("bipartite.projection removes 'type' attribute if requested", { library(igraph) g <- graph.full.bipartite(10,5) proj <- bipartite.projection(g) proj1 <- bipartite.projection(g, which="true") proj2 <- bipartite.projection(g, which="false") proj3 <- bipartite.projection(g, remove.type=FALSE) proj4 <- bipartite.projection(g, which="true", remove.type=FALSE) proj5 <- bipartite.projection(g, which="false", remove.type=FALSE) expect_that("type" %in% list.vertex.attributes(proj[[1]]), is_false()) expect_that("type" %in% list.vertex.attributes(proj[[2]]), is_false()) expect_that("type" %in% list.vertex.attributes(proj1), is_false()) expect_that("type" %in% list.vertex.attributes(proj2), is_false()) expect_that("type" %in% list.vertex.attributes(proj3[[1]]), is_true()) expect_that("type" %in% list.vertex.attributes(proj3[[2]]), is_true()) expect_that("type" %in% list.vertex.attributes(proj4), is_true()) expect_that("type" %in% list.vertex.attributes(proj5), is_true()) }) test_that("bipartite.projection breaks for non-bipartite graphs (#543)", { library(igraph) g <- graph.formula(A-0, B-1, A-1, 0-1) V(g)$type <- V(g)$name %in% LETTERS expect_that(bipartite.projection.size(g), throws_error("Non-bipartite edge found in bipartite projection")) expect_that(bipartite.projection(g), throws_error("Non-bipartite edge found in bipartite projection")) }) igraph/inst/tests/test_get.edge.R0000644000176000001440000000041512251656216016545 0ustar ripleyusers context("get.edge") test_that("get.edge works", { library(igraph) g <- erdos.renyi.game(100, 3/100) edges <- unlist(lapply(seq_len(ecount(g)), get.edge, graph=g)) g2 <- graph(edges, dir=FALSE, n=vcount(g)) expect_that(graph.isomorphic(g, g2), is_true()) }) igraph/inst/tests/test_degree.sequence.game.R0000644000176000001440000000202612251656216021035 0ustar ripleyusers context("degree.sequence.game") test_that("degree.sequence.game works", { library(igraph) gc <- function(graph) { clu <- clusters(graph) induced.subgraph(graph, which(clu$membership==which.max(clu$csize))) } g <- gc(erdos.renyi.game(1000, 2/1000)) nG <- degree.sequence.game(degree(g), method="simple") expect_that(degree(nG), equals(degree(g))) nG <- degree.sequence.game(degree(g), method="vl") expect_that(degree(nG), equals(degree(g))) expect_that(is.connected(nG), is_true()) expect_that(is.simple(nG), is_true()) ##### g <- erdos.renyi.game(1000, 1/1000) nG <- degree.sequence.game(degree(g), method="simple") expect_that(degree(nG), equals(degree(g))) g2 <- erdos.renyi.game(1000, 2/1000, dir=TRUE) nG2 <- degree.sequence.game(degree(g, mode="out"), degree(g, mode="in"), method="simple") expect_that(degree(nG, mode="out"), equals(degree(g, mode="out"))) expect_that(degree(nG, mode="in"), equals(degree(g, mode="in"))) }) igraph/inst/tests/test_get.diameter.R0000644000176000001440000000101712251656216017432 0ustar ripleyusers context("get.diameter") test_that("get.diameter works", { library(igraph) g <- graph.ring(10) E(g)$weight <- sample(seq_len(ecount(g))) d <- diameter(g) gd <- get.diameter(g) sp <- shortest.paths(g) expect_that(d, equals(max(sp))) expect_that(sp[ gd[1], gd[length(gd)] ], equals(d)) d <- diameter(g, weights=NA) gd <- get.diameter(g, weights=NA) sp <- shortest.paths(g, weights=NA) expect_that(d, equals(max(sp))) length(gd) == d + 1 expect_that(sp[ gd[1], gd[length(gd)] ], equals(d)) }) igraph/inst/tests/test_get.incidence.R0000644000176000001440000000107612251656216017566 0ustar ripleyusers context("get.incidence") test_that("get.incidence works", { library(igraph) ## Dense I <- matrix(sample(0:1, 35, replace=TRUE, prob=c(3,1)), nc=5) g <- graph.incidence(I) I2 <- get.incidence(g) expect_that(I, is_equivalent_to(I2)) expect_that(rownames(I2), equals(as.character(1:7))) expect_that(colnames(I2), equals(as.character(8:12))) ## Sparse I3 <- get.incidence(g, sparse=TRUE) expect_that(as.matrix(I3), is_equivalent_to(I)) expect_that(rownames(I3), equals(as.character(1:7))) expect_that(colnames(I3), equals(as.character(8:12))) }) igraph/inst/tests/test_layout.merge.R0000644000176000001440000000076212251656216017503 0ustar ripleyusers context("layout.merge") test_that("layout.merge works", { library(igraph) set.seed(42) g <- list(graph.ring(10), graph.ring(5)) l <- lapply(g, layout.mds) l lm <- layout.merge(g, l) expect_that(is.matrix(lm), is_true()) expect_that(ncol(lm), equals(2)) expect_that(nrow(lm), equals(sum(sapply(g, vcount)))) ########## ## Stress test for (i in 1:10) { g <- erdos.renyi.game(100, 2/100) l <- layout.mds(g) expect_that(dim(l), equals(c(vcount(g), 2))) } }) igraph/inst/tests/test_add.edges.R0000644000176000001440000000313112251656216016677 0ustar ripleyusers context("add.edges") test_that("add.edges keeps edge id order", { library(igraph) g <- graph.empty(10) g2 <- add.edges(g, (edges <- c(1,2, 2,3, 3,4, 1,6, 1,7, 9,10)) ) expect_that(ecount(g2), equals(length(edges)/2)) expect_that(get.edge.ids(g2, edges), equals(seq_len(length(edges)/2))) }) test_that("add.edges adds attributes", { library(igraph) g <- graph.empty(10) g3 <- add.edges(g, (edges <- c(1,5, 2,6, 3,10, 4,5)), attr=list(weight=(weights <- c(1,2,1,-1))) ) expect_that(ecount(g3), equals(length(edges)/2)) expect_that(get.edge.ids(g3, edges), equals(seq_len(length(edges)/2))) expect_that(E(g3)$weight, equals(weights)) }) test_that("add.edges unknown attributes to NA", { library(igraph) g <- graph.empty(10) g2 <- add.edges(g, (edges <- c(1,2, 2,3, 3,4, 1,6, 1,7, 9,10)) ) g4 <- add.edges(g2, c(1,4, 4,6, 7,1), attr=list(weight=c(-1,1,-2.5))) expect_that(all(is.na(E(g4)$weight[seq_len(length(edges)/2)])), is_true()) }) test_that("add.edges appends attributes properly", { library(igraph) g <- graph.empty(10) g3 <- add.edges(g, (edges1 <- c(1,5, 2,6, 3,10, 4,5)), attr=list(weight=(weights1 <- c(1,2,1,-1))) ) g5 <- add.edges(g3, (edges2 <- c(10,9, 10,10, 1,1)), attr=list(weight=(weights2 <- c(100,100,100))) ) expect_that(E(g5)$weight, equals(c(weights1, weights2))) }) test_that("add.edges signals error for zero vertex ids", { library(igraph) g <- graph.full(5) %du% graph.full(5) %du% graph.full(5) expect_that(add.edges(g, c(0,5, 0,10, 5,10)), throws_error("Invalid vertex id")) }) igraph/inst/tests/test_all.st.cuts.R0000644000176000001440000000225112251656216017235 0ustar ripleyusers context("all.st.cuts") test_that("all.st.cuts works", { library(igraph) g <- graph.formula( a -+ b -+ c -+ d -+ e ) cc <- stCuts(g, source="a", target="e") expect_that(cc$cuts, equals(list(1,2,3,4))) expect_that(cc$partition1s, equals(list(1, 1:2, 1:3, 1:4))) g2 <- graph.formula( s -+ a:b -+ t, a -+ 1:2:3 -+ b ) cc <- stCuts(g2, source="s", target="t") expect_that(cc$cuts, equals(list(c(1,2), c(1,7), c(2,3,4,5,6), c(2,3,4,5,10), c(2,3,4,6,9), c(2,3,4,9,10), c(2,3,5,6,8), c(2,3,5,8,10), c(2,3,6,8,9), c(2,3,8,9,10), c(3,7)))) expect_that(cc$partition1s, equals(list(1, c(1,3), c(1,2), c(1,2,7), c(1,2,6), c(1,2,6,7), c(1,2,5), c(1,2,5,7), c(1,2,5,6), c(1,2,5,6,7), c(1,2,5,6,7,3)))) g3 <- graph.formula( s -+ a:b -+ t, a -+ 1:2:3:4:5 -+ b ) cc <- stMincuts(g2, source="s", target="t") expect_that(cc$value, equals(2)) expect_that(cc$cuts, equals(list(c(1,2), c(1,7), c(3,7)))) expect_that(cc$partition1s, equals(list(1, c(1,3), c(1,3,2,7,6,5)))) }) igraph/inst/tests/test_unfold.tree.R0000644000176000001440000000060112251656216017305 0ustar ripleyusers context("unfold.tree") test_that("unfold.tree works", { library(igraph) g <- graph.tree(7, 2) g <- add.edges(g, c(2,7, 1,4)) g2 <- unfold.tree(g, roots=1) expect_that(graph.isomorphic(g2$tree, graph(c(1,2, 1,3, 2,8, 2,5, 3,6, 3,9, 2,7, 1,4))), is_true()) expect_that(g2$vertex_index, equals(c(1,2,3,4,5,6,7,4,7))) }) igraph/inst/tests/test_canonical.permutation.R0000644000176000001440000000115012251656216021355 0ustar ripleyusers context("canonical.permutation") test_that("canonical.permutation works", { library(igraph) g1 <- erdos.renyi.game(10, 20, type="gnm") cp1 <- canonical.permutation(g1) cf1 <- permute.vertices(g1, cp1$labeling) ## Do the same with a random permutation of it g2 <- permute.vertices(g1, sample(vcount(g1))) cp2 <- canonical.permutation(g2) cf2 <- permute.vertices(g2, cp2$labeling) ## Check that they are the same el1 <- get.edgelist(cf1) el2 <- get.edgelist(cf2) el1 <- el1[ order(el1[,1], el1[,2]), ] el2 <- el2[ order(el2[,1], el2[,2]), ] expect_that(el1, equals(el2)) }) igraph/inst/tests/test_largest.independent.vertex.sets.R0000644000176000001440000000072212251656216023312 0ustar ripleyusers context("largest.independent.vertex.sets") test_that("largest.independent.vertex.sets works", { library(igraph) g <- erdos.renyi.game(50, 0.8) livs <- largest.independent.vertex.sets(g) expect_that(unique(sapply(livs, length)), equals(independence.number(g))) ec <- sapply(seq_along(livs), function(x) ecount(induced.subgraph(g, livs[[x]]))) expect_that(unique(ec), equals(0)) ## TODO: check that they are largest }) igraph/inst/tests/test_get.all.shortest.paths.R0000644000176000001440000000241012251656216021376 0ustar ripleyusers context("get.all.shortest.paths") test_that("get.all.shortest.paths works", { library(igraph) edges <- matrix(c("s", "a", 2, "s", "b", 4, "a", "t", 4, "b", "t", 2, "a", "1", 1, "a", "2", 1, "a", "3", 2, "1", "b", 1, "2", "b", 2, "3", "b", 1), byrow=TRUE, ncol=3, dimnames=list(NULL, c("from", "to", "weight"))) edges <- as.data.frame(edges) edges[[3]] <- as.numeric(as.character(edges[[3]])) g <- graph.data.frame(as.data.frame(edges)) sortlist <- function(list) { list <- lapply(list, sort) list[order(sapply(list, paste, collapse="!"))] } sp1 <- get.all.shortest.paths(g, "s", "t", weights=NA) expect_that(sortlist(sp1$res), equals(list(c(1, 2, 7), c(1, 3, 7)))) expect_that(sp1$nrgeo, equals(c(1,1,1,1,1,1,2))) sp2 <- get.all.shortest.paths(g, "s", "t") expect_that(sortlist(sp2$res), equals(list(c(1, 2, 3, 4, 7), c(1, 2, 7), c(1, 3, 7)))) expect_that(sp2$nrgeo, equals(c(1,1,2,1,1,1,3))) ## TODO ## E(g)$weight <- E(g)$weight - 1 ## get.all.shortest.paths(g, "s", "t") }) igraph/inst/AUTHORS0000644000176000001440000001115712263024035013603 0ustar ripleyusers igraph authors, in alphabetical order: -------------------------------------- Patrick R. Amestoy AMD library Adelchi Azzalini igraph.options based on the sm package Tamas Badics GLPK Gregory Benison Minimum cut calculation Adrian Bowman igraph.options based on the sm package Keith Briggs Parts from the Very Nauty Graph Library Geometric random graphs Girth Various patches and bug fixes Jeroen Bruggeman spinglass community detection Burt's constraints Juergen Buchmueller Big number math implementation Carter T. Butts Some layout algorithms from the SNA R package bonpow function in the SNA R package Some R manual pages, from the SNA R package Aaron Clauset Hierarchical random graphs J.T. Conklin logbl function Topher Cooper GSL random number generators (not used in R) Gabor Csardi Most of igraph Trevor Croft simpleraytracer Peter DalGaard zeroin root finder Timothy A Davis CXSPARSE: a Concise Sparse Matrix package - Extended AMD library Sparse matrix column ordering Laurent Deniau Bits of the error handling system Ulrich Drepper logbl function Iain S. Duff AMD library GLPK S.I. Feldman f2c David Firth Display data frame in Tk, from relimp package P. Foggia VF2 graph isomorphism algorithm John Fox R: suppressing X11 warnings Alan George GLPK John Gilbert Sparse matrix column ordering D.Goldfarb GLPK Brian Gough GSL random number generators (not used in R) Tom Gregorovic Multilevel community detection M.Grigoriadis GLPK Oscar Gustafsson GLPK Paul Hsieh pstdint.h Ross Ihaka Some random number generators (not used in R) Tommi Junttila BLISS graph isomorphism library Petteri Kaski BLISS graph isomorphism library Oleg Keselyov zeroin root finder Darwin Klingman GLPK Donald E. Knuth GLPK Stefan I. Larimore Sparse matrix column ordering Yusin Lee GLPK Richard Lehoucq ARPACK Rene Locher R arrow drawing function, from IDPmisc package J.C. Nash BFGS optimizer Joseph W-H Liu GLPK Makoto Matsumoto GSL random number generators (not used in R) Vincent Matossian Graph laplacian igraph_neighborhood_graphs Line graphs Peter McMahan Cohesive blocking Andrew Makhorin GLPK David Morton de Lachapelle Spectral coarse graining Laurence Muller Fixes for compilation on MS Visual Studio Fionn Murtagh Order a hierarchical clustering Emmanuel Navarro infomap community detection Various fixes and patches Tamas Nepusz Most of igraph Esmond Ng Sparse matrix column ordering Kevin O'Neill Maximal independent vertex sets Takuji Nishimura GSL random number generators (not used in R) Jim Orlin GLPK Patric Ostergard GLPK Elliot Paquette psumtree data type Pascal Pons walktrap community detection Joerg Reichardt spinglass community detection Marc Rieffel GSL random number generators (not used in R) B.D. Ripley igraph.options based on the sm package BFGS optimizer Various bug fixes Martin Rosvall infomap community detection Andreas Ruckstuhl R arrow drawing function, from IDPmisc package Heinrich Schuchardt GLPK J.K. Reid GLPK C. Sansone VF2 graph isomorphism algorithm Michael Schmuhl The graphopt layout generator Christine Solnon LAD graph isomorphism library Danny Sorensen ARPACK James Theiler GSL random number generators (not used in R) Samuel Thiriot Interconnected islands graph generator Vincent A. Traag spinglass community detection Magnus Torfason R operators that work by name Minh Van Nguyen Microscopic update rules Various test cases Many documentation and other fixes M. Vento VF2 graph isomorphism algorithm Fabien Viger gengraph graph generator Phuong Vu ARPACK P.J. Weinberger f2c Garrett A. Wollman qsort B.N. Wylie DrL layout generator Chao Yang ARPACK Institutional copyright owners: ------------------------------- Free Software Foundation, Inc Code generated by bison Sandia Corporation DrL layout generator The R Development Core Team Some random number generators (not used in R) R: as.dendrogram from stats package The Regents of the University of California qsort Xerox PARC Sparse matrix column ordering Other contributors ------------------ Neal Becker Patches to compile with gcc 4.4 Richard Bowman R patches Alex Chen Patch to compile on Intel compilers Daniel Cordeiro Patches Tom Gregorovic Bug fixes Mayank Lahiri Forest fire game fix John Lapeyre Patches Christopher Lu Various fixes and patches André Panisson R patches Bob Pap Bug fixes Keith Ponting R package bug fixes Martin J Reed Bug fixes Elena Tea Russo Bug fixes KennyTM Bug fixes Jordi Torrents Patches Matthew Walker Various patches Kai Willadsen Arrow size support in Python igraph/inst/html_library.license.terms0000644000176000001440000000314512240234657017726 0ustar ripleyusersSun Microsystems, Inc. The following terms apply to all files associated with the software unless explicitly disclaimed in individual files. The authors hereby grant permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. RESTRICTED RIGHTS: Use, duplication or disclosure by the government is subject to the restrictions as set forth in subparagraph (c) (1) (ii) of the Rights in Technical Data and Computer Software Clause as DFARS 252.227-7013 and FAR 52.227-19. igraph/inst/igraph2.gif0000644000176000001440000000312712240234657014564 0ustar ripleyusersGIF89a@@÷ï î õõóóôòñ íëçåâäÞ%%Ý('Ú+*Ü)(Ø.-Ï<;Ó43Ô65Ð:9á!!¾UU¹[[­nnµcc²gg°ii±ii§wv«rqªsr¤{{£||¢ÊBAÉFEÈGFÆJIÅJJÀQQÀRRÀSSëÞìßëÞýîýîûíúìûíüíõç õæ÷é ùëùë øé øê ÿðÿðïáîáïâíáóæòäðãßÓ-ÝÒ/×Í7ÙÎ5ÒÇ>ÔÊ;×Í8ÓÉ<ÓÉ=ÛÐ1ÛÐ2ã×'çÛ!æÚ"äØ&èÛ âÖ(â×(àÔ*áÕ*àÔ+¿·X¾¶Yº³^»´]¼´\¼µ\¼´]¿¸X£ž¤Ÿ~¶¯d±«k²«j³­h³¬i³­i´­h±«l¹²aº³`¬§r¬§sª¤u«¦t©¤w¦¡z¦¢z¤ }Ç¿MƽNƾNúRûSļQºTÀ¸WÎÅCÌÂFÌÃFÎÄDËÁHÊÁIËÂHÑÈ@Ÿƒƒž„„—›ˆˆ™ŒŒ™šŒŒ˜ŽŽŸ›„Ÿš…Ÿ›…™‡—•›—Šš–‹˜•™•™–Œš–Œ˜”Ž˜•Žœ˜ˆœ™ˆ”‘‘•‘‘––‘‘•’‘–“–’‘–“‘”’’•’’•“’”““•““–’’–“’–““¡€€¡¢€¢ž€¡œ‚!ù´,@@þi H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jܨѕLJ]rÅq¡+E"S. ‰’<–H–48²&¥ŸdtE&‡ŸEbÎ$èñT'&/faÒIä'„®üœJHçPrpøˆ1õÆW”vž*2õ'‘Ci¤4ä'W·/nZxJŒ²>’Ìåèñ Þ²a>tõæíV9§ÒÒ9èïÔ%‚ Šô$Œ3hÓŽtâøg’‘PCž­ø”¢1cô‰Ls­â"!ùăÆ,°IodMp¤+/[ËÆðjâŽ5y ëê Y®E0+_¾·¢G.T^jð“wžgÈþ¤<~P÷E†‚û 2}tQ‘æ“¿xJWÃiæK‰RfH«ÓQ䊭ÆÐhîÕÄß»]bØOiT·J#1R|Ém41¼%…+O)Ÿ"Xà #€•Ón”xq†Üt“~™æÊàã>Á)ßm”ƒ‹å¤È~®d€Púø"Z´—oD)²A”\ ðÀ'ñ)¦ÒH‹Ð%—CVYÒ§Xp&—”æk"1‚â›Qf€Òkƒ¹ž\V &l¾IèHåü‰¨`HaM4ŠFÉÈh aHÆexäÝD©P)¨X¦B$Rþ£þø€o8ªåŠ%d©C§†fz "±úˆBrŠ˜–%!þtħ¥´@°|”\J)¥ú—rItÒ–£NЪ­"{Ê´ÕFɋ͎fB¥ rSh#1Q. s6ä# J'õ*R4çC„‘JzÒ o:BM+ydoÑàGqòwþ ŠäTkŸ®Lâ \q¤uÖ¤e(p §S½îW(X/æ"ˆ4 &Z¿Ó‚úŸ„;ƒÈ–DaHqZ!A’K˜q#u1óI—"iäÐa‡GÄÔ_ÍRÃæÊ,<üå’];HgždV¶B#eÑÙõ®£"Pt6‡±rûÚG7²>Ð` ×y¯õmE-7ˆH1Dç-¢wu¼qF';î(›†[¾\åšwîùç ‡.úè¤Ó;igraph/inst/tkigraph_help/0000755000176000001440000000000012240234657015357 5ustar ripleyusersigraph/inst/tkigraph_help/index.html0000644000176000001440000001333412240234657017360 0ustar ripleyusers tkigraph online help

The tkigraph manual

tkigraph is a basic Graphical User Interface (GUI) to some igraph functions.

What is tkigraph?

tkigraph is a simple Graphical User Interface to the igraph R package. R is a general purpose programming language and environment, used mostly but not exclusively for statistical analysis. igraph is an extension package to R. tkigraph lets you use some basic features of igraph via a GUI, instead of typing in R commands.

Installing and starting tkigraph

Well, if you are reading these lines, then you probably already know how to install and start tkigraph. If not, here is how to do it.

First, install the GNU R software package. It can be downloaded from the R website, but first check your system, because it might be already installed. You can also ask your system administrator to install it for you.

Second, you need to install the igraph extension package. First, start R by clicking on its icon in Windows, or by typing "R" into a terminal and pressing ENTER on Linux. Now type in

	install.package("igraph")
      
and press ENTER. After choosing an appropriate mirror site, R downloads and installs the igraph package.

Third, you need to load the igraph package and start tkigraph. This can be done by typing

	library(igraph)
	tkigraph()
      
(in two separate lines, pressing ENTER after each line) into your R session. You should see a new windows appear, it looks like the one on the picture below.

The tkigraph window

The main window of tkigraph look like this:

Main tkigraph window

Almost all the window is occupied by the list of graphs in the workspace. Unlike on the picture for you this is initially empty. Every graph has a number, in the # column, a name that is not necessarily unique, you can change the name of the graph to whatever you like. In the last three columns you can see the number of vertices and edges in the graph, and whether it is directed or not.

In the leftmost column there is a checkbox for every graph, you can select one or more graphs using this and then perform operations on them. Some operations require exactly one graph to be selected, others work happily on many graphs as well. You will always get an error message if not the appropriate number of graphs were selected for an operation.

The tkigraph menus

Creating new graphs or performing operations on them can be done by selecting entries from the main menu. Let us discuss briefly what the various menus are good for.

Graph menu

The Graph menu lets you create and delete graphs, show them in an edge list format, calculate some basic properties for them. Moreover all file-related operations are here a well.

Draw menu

In this menu you can draw your graphs using various layouts, possibly also interactively. There are two entries in the menu. The first one (Simple) tries to do the plotting automatically; first it chooses an appropriate layout for the graph and then tries to guess the graphical parameters to make the plot look good. Finally it creates a non-interactive plot.

The advanced plotting lets you choose various graphical parameters, and you also have the possibility to create an interactive plot.

Centrality menu

Lets you calculate various degree centrality measures, plus edge betweenness. The results are always shown in a table that can be sorted according to all of its columns and the data can also be exported into a text file.

Distances menu

Various measures related to path lengths in the network are included in this menu.

Subgraphs menu

This menu contains three slightly related entries. Components are maximal connected subgraphs of a graph. Communities are natural modules in the graph, a module is a subgraph that has more edges within the module than between the module and the rest of the graph. (Loosely speaking.) In the 'Communities' menu you can run the Spinglass algorithm by J Reichardt and S Bornholdt. Cohesion measures how difficult it is to disconnect a graph by removing vertices from it. The last menu entry calculates cohesion for all components in the selected graph.

Motifs menu

Motifs are small subgraphs with a given structure. The first menu entry in this menu just plots all possible motifs of a given size in a directed or directed graphs. The second menu entry finds all the different motifs in the selected graph and plots all the different motifs annotated with the number of motifs of that kind found in the graph. It also plots a histogram for the various motifs.

Help menu

This is what you are reading right now.

Quit

Not really a menu, just a button. Lets you quit from tkigraph.

igraph/inst/tkigraph_help/communities.html0000644000176000001440000000013612240234657020601 0ustar ripleyusers Community structure detection

Bla-bla-bla

igraph/inst/tkigraph_help/style.css0000644000176000001440000002111312240234657017227 0ustar ripleyusers body { font: medium/150% "Lucida Grande", sans-serif; margin: 0; padding: 0 0 10px; color: #333; background: #fff; } a img { border: 0; } h1 { color: #fff; margin: 0; height: 40px; line-height: 40px; text-shadow: 0px 1px 2px #000; background: #1872ce url(images/header_blue.png) repeat-x; border-top: 1px solid #1872ce; border-bottom: 1px solid #1c477f; font-size: large; padding-left: 10px; } h2 { font-size: 1.5em; text-indent: -40px; } h2.th { font-size: 1.5em; text-indent: 0px; } h3 { font-size: 1em; text-indent: -20px; } h4 { font-size: 0.8em; } body.error h1 { background: #d70000; border-bottom: 1px solid #7f0000; } hr { color: #888; background-color: #888; height: 1px; width: 100%; border: 0; } code { font-size: 1.2em; } img.float_right { float: right } img.float_left { float: left } pre.condensed { font-size: 0.8em; line-height: 1.5em; } .igraphlogo { float: right; padding-left: 40px; padding-right: 40px; padding-top:30px; } div.main { max-width:900px; padding-left: 50px; padding-bottom: 50px; margin-right: 0; } .more { text-align: right; margin-top: -1em; } .back { text-align: left; } ul.no-bullet { list-style-type: none; padding: 0; margin: 0; } ul.no-bullet li { padding: 0; margin: 0; } li.download { line-height: 1em; padding-bottom: 10px !important; } li.download .name { font-weight: bold; padding-left: 20px; } li.download span.comment { font-size: 0.8em; color: #888; } li.download div.comment { font-size: 0.8em; padding: 4px 0px 0px 20px; } p.comment { font-size: 0.8em; color: #888; } div.image_caption { font-size: 0.8em; color: #888; text-align: center; } li.download-c { background: url(images/icon_c.png) no-repeat 0px 0px; } li.download-sf { background: url(images/icon_sf.png) no-repeat 0px 0px; } li.download-r { background: url(images/icon_r.png) no-repeat 0px 0px; } li.download-python { background: url(images/icon_python.png) no-repeat 0px 0px; } li.download-ruby { background: url(images/icon_ruby.png) no-repeat 0px 0px; } li.download-doc { background: url(images/icon_documentation.png) no-repeat 0px 0px; } li.download-wiki { background: url(images/icon_wiki.png) no-repeat 0px 0px; } ul.download-links { list-style-type: none; padding: 2px 0 0 20px; margin: 0; font-size: 0.8em; } ul.download-links li { padding: 0px 10px 0px 0px; margin: 0; padding-bottom: 5px !important; } ul.download-links li.download-source { background: url(images/icon_source.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-sf { background: url(images/icon_sf.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-windows { background: url(images/icon_windows.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-debian { background: url(images/icon_debian.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-osx { background: url(images/icon_osx.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-html { background: url(images/icon_html.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-external { background: url(images/icon_links.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-wiki { background: url(images/icon_wiki.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-pdf { background: url(images/icon_pdf.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-html { background: url(images/icon_html.png) no-repeat 0px 0px; padding-left: 18px; } ul.download-links li.download-info { background: url(images/icon_info.png) no-repeat 0px 0px; padding-left: 18px; } a { color: #00f; text-decoration: none } a:visited { color: #00c } a:hover { color: #00f; text-decoration: underline } h1 a, h1 a:visited, h1 a:hover { color: #fff; text-decoration: none } h2 a, h2 a:visited, h2 a:hover { color: #000; text-decoration: none } h3 a, h3 a:visited, h3 a:hover { color: #000; text-decoration: none } #sourceforge_logo { float: right; padding: 3px 15px 0px 0px; } /* Menu items */ ul.menu { list-style-type: none; padding: 0; margin: 0; } ul.menu-upper { list-style-type: none; padding: 0px 0px 0px 15px; margin: 0; margin-top: 10px; width: 95%; border-bottom: 1px solid; text-align: left; } ul.menu li { padding: 0px 10px 10px 20px; margin: 0px; } ul.menu-upper li { padding: 8px 10px 4px 25px; font-size: 0.8em; border: solid; border-width: 1px 1px 1px 1px; margin: 0px 0px 0px 0px; display: inline; } ul.menu-upper li:hover { border-top: solid 2px #0000ff; border-left: solid 2px #0000ff; border-right: solid 2px #0000ff; } ul li.item-introduction { background: #dadaff url(images/icon_info.png) no-repeat 5px 6px; } ul li.item-download { background: #dadaff url(images/icon_download.png) no-repeat 5px 6px; } ul li.item-news { background: #dadaff url(images/icon_news.png) no-repeat 5px 6px; } ul li.item-documentation { background: #dadaff url(images/icon_documentation.png) no-repeat 5px 6px; } ul li.item-wiki { background: #dadaff url(images/icon_wiki.png) no-repeat 5px 6px; } ul li.item-screenshots { background: #dadaff url(images/icon_screenshots.png) no-repeat 5px 6px; } ul li.item-community { background: #dadaff url(images/icon_community.png) no-repeat 5px 6px; } ul li.item-bug { background: #dadaff url(images/icon_bug.png) no-repeat 5px 6px; } ul li.item-links { background: #dadaff url(images/icon_links.png) no-repeat 5px 6px; } ul li.item-license { background: #dadaff url(images/icon_license.png) no-repeat 5px 6px; } body#index li#n-index { background: #ffffff url(images/icon_info.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } body#news li#n-news { background: #ffffff url(images/icon_news.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } body#download li#n-download{ background: #ffffff url(images/icon_download.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } body#documentation li#n-documentation{ background: #ffffff url(images/icon_documentation.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } body#screenshots li#n-screenshots{ background: #ffffff url(images/icon_screenshots.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } body#support li#n-support{ background: #ffffff url(images/icon_community.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } body#bugs li#n-bugs{ background: #ffffff url(images/icon_bug.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } body#license li#n-license { background: #ffffff url(images/icon_license.png) no-repeat 5px 6px; border-bottom: 1px solid #ffffff; } /* Forms */ label { display: block; float: left; width: 130px; font-weight: bold; } label.normal { color: #444; } div.explanation { border-left: 130px solid white; font-size: 0.8em; color: #888; } .programlisting { background: #eeeeff; border: solid 1px #4444ff; padding: 0.5em; } p.news { padding: 0px 40px 0px; } h4.news { padding: 0px 40px 0px; } ul.newslist { padding: 0px 60px 0px; } table.intro td { vertical-align: top; width: 33.3%; padding: 20px; } div.feed { width: 100%; padding: 20px; } div.feedburnerFeedBlock p.feedTitle { font-size: 1em; } div.feedburnerFeedBlock a { color: black; } div.feedburnerFeedBlock ul a { color: #00f; text-decoration: none } div.feedburnerFeedBlock { font-size: 1em; } div.feedburnerFeedBlock ul { list-style-type: none; } div.feedburnerFeedBlock ul li { margin-top: 10px; } div.feedburnerFeedBlock p.date { font-style: italic; font-size: 0.8em; margin: 5px; } div.feedburnerFeedBlock p.date:before { content: "("; } div.feedburnerFeedBlock p.date:after { content: ")"; } div.feedburnerFeedBlock span, span+p {display:inline} div.feed span.headline { font-size: 1.25em; } #creditfooter { float: right; } div.feedburnerFeedBlock table { table-layout: fixed; width: 100%; } div.quick-menu { position:fixed; bottom:0; width: 100%; } div.quick-menu ul.menu-bottom { padding: 5px; border-top: 1px solid #0000a0; text-align: center; background: #dadaff; } ul.menu-bottom { list-style-type: none; padding: 0px 0px 0px 15px; margin: 0; } ul.menu-bottom li { padding: 8px 10px 10px 25px; font-size: 0.8em; margin: 0px; display: inline; } div.copyright { border-top: 1px solid #8e8e8e; text-align: center; color: #8e8e8e; padding-top: 10px; padding-bottom: 50px; }igraph/inst/tkigraph_help/tkigraph-main.gif0000644000176000001440000003451312240234657020607 0ustar ripleyusersGIF89a*çÿ      "#%"'(&*,)-.,0205748:7=>cÅ d„jI ,¨°r¸õëØ³kßν»wÅÂþK0€`y®oæÒ§oæ©Ã¾ËŸO¿¾ýûøG‡WCÂTàŽzëUrÁŸie„à¶5øànRt° I!dÎ×amR ü°à‰¡ƒŒ©Hdµ!£Œœ"L',Ѐ «4Sˆ P0F)‚0ãŒVŒ°Á#Xqä“PF)å”TVie”e¼ðN=BˆÑ4ˆÐÁ˜2°q%•YnÙ%•`ŠI&ʨaœ3±+þD3Ã4Œi¡™g¶‘&—^B9èšr†ð§ Še£^y¨£G6èg&l© Zj%3,ã.?ljꩨJ©ä.¤*å’þ°ÆºA”cŒÉTȸ„­#Œ!%AolÖÛÌ).40€cœ˳E ¬.<¸ ƒ´Üvëí·à†+î¸Ý ç ´ ã?ì8Óä‚k®¼èªË®3;t*œ¡/¼Á–Q„<÷hñEÈôã?à¼ïó–ûoÀZúã aL,±š?nÄÒ¦¹îÂט±…Ç ‡KÆòÄsÅÉÇ,óÌàîºîKÐüí#÷ìÏÞ.‘‰?Ú¨@*hãO&9{ ¬´Ä+µ'ôc6(¡.wÉË^5ò[WѨàKòLVÇ!0ýñoÌÏ !`˜Àb-ÐØÌæ#È®}­8þ€ŠIÎrö’jjÁ8 æ:(ÊsôÇ1vP7ÜB%þ¸>˜!ƒŠõ"²U?ßá^Ô@zéxW&c°N-´ós„:¹ô/Ò!å(˜ê}PøAž€§”TO€\5 pƒZLvöħ>ùYQ; Ó¡ÈÇ&ôÕ<#x‚…v·†Æã¶ôÇNß‘ô´ Uí:Ïäm) ã°ä…,ÊJèIÏ3ÈCLó¹Ï0Ôr%xÛxªR–ºthUkÖ˜ó®x á¬A4aúÃ#È«’Y@Â~iÎd¦ÑDH=8Ö±Ô¬æ5³‰Í>VÝô‡Æ ¢prಠ ­hGKÚÒšö´ ¨ôOŠfÑ¡z˜þ¶§¥vAKûËŠ[ƒý˱ªmíj9¦Úßêá¦ûÂÂ#ÆÈCX0Á Ãn{Àµ¥a sp¿f;¦ÚÞ–cÀee¤á %¤Óºo96‡ð"‘¼7˜†y𺓮<ÖËNtòü¸¤‹Îžéc]Ðw;P[Äà÷àÄJ`‚ˆ ¡ºú¸nvá ›€ ¯jGLâ›ø±#P†5T°.XC#8ñh9àÏ>–Æ6-kûé”¶±—-@)ªiÉRÖ²­–?š¡4Cƒ.±”§lbÕª³¿úÒ—ôP†XÎðZ²ÇÖЩïrÉÂ[ÖCþŠe(rɉYn"p9 [0”  ­P£mÃ<æ2‡Y[Vm¼üG0Ëù¸¬ d>REŸÕÃÐoV°-SQ]°¢¡¨¢P(f2wRËöÇQ·pÙ‰zuz8è²)g($ër‡&V E{Zvx«c­ /Ä®< ²°‡MÚZ€Æ5A¯HËf;»ÙÌ®ñeq<Úâ(­™Dûi Ò‡nw[ÈØ·¸œMDÀÛ}P‘:z¦ŽÍ¢ûÝðŽ·¼çMïzÛûݪU¡úˆP\5áÁgÅ ŠÅüN—µ¬º=‡#ª‚ß §óÈ@W3+¼Û‡À)Ñ?äÀ VhËþâ„+kA¿ Åpº]}©ÓrwóÅû Z,ÃøàRuï‘?}À ø9ÔQE>äƒu­~O~pßôÓë¼Ü2¿\à?3 Và…Wàc½:·.ˇ­wýë=ƒ-qyï¶»ýíp‡7²9`od÷ŒîòÆñ³qo;¸À™ß¸ à]ðyDˆG<¸ÅnrcsH<â© kaKT¼æ7ÏùÎ{þó ýæýð0 š AäáZ.íàåЇ"NP×Bõ¤2Hx¾‡ÄÇáô÷Hý깄„€öb´(ïÈI. ÏÅ„hØ€¤·@þ¹Ò–¾T•}ò[/„Þ ‚ôj C}ýACÈÍ3qWàp<°‡Cô }G­Ø4n ‡;e_LÀ{è÷húT 8@ˆ7€§v1á7{µ'QP rõvBPCþÐ}.%8 ¥o'z"8‚$X‚›7ÍÐ #zv72ày-xwœgžÀL.ÀYçž`G…ðƒ?h‘` Œ‡ „ Hˆ„±`„@ø: lÛâ„TX…Vx…X˜…Z¸…V8€åGQà ÌÅÁà¼W`ðjÄj~´0>¤Z»§&{à„`(†ëB†d7´0†t†?˜W07€ öGPPþzØH³`„pN0 jÄFújØ3ä7‡…à…„@9ì'„ðˆj„J’0X°k®Çkg§T]P‡ˆddªõz G¢Ðº‚‰@äÝn†køGBðŒ°jB´ijò‰¸F¦°Ǩ0B¤[ò`F€\˜Ú¸ÜX…m lm°…—r)X8Žãh…~ç *0?8.à .`WHˆPõ( z  ãæI¸ TðpöXy™ ¹ ÙY~ôW~@<¦ó„‘Y`°=t‡6&‰¹$)‘&ié&0’‡€,9þ’%Y‡àÑHà&%’’‡ð¥Ó’„P/ù“]@(‰y)ô7“ö8“5Ù9”dÒŠhøN)Ép¸ÐöØ“?)“Gy’ôw`ÐDPM —bnÀ‘ •sX}‚)oé”X@”…@—câ–9 *º–9˜„Y˜†y˜ˆéz`zP‹Ù˜ IŽ0™“¹c€ Pà ÍpM™ °`”Yš¦yš¨™šª¹š¬Ùš®ùš°Yš˜{›¶éš‡€•D@·Ù›¾ù›ÀœÂyšOI‹0œÈ™œÊ¹œ¶I‘ðœÐùœ…°p ` Ùþ©…Þùàžâ9žäYžæyžè)ž‰ ^†žîùžðŸò9ŸôYŸöyŸø™Ÿú¹ŸéI•🠕P*  o   º  Ú ú ¡:¡Z ‘†P¡º¡Ú¡ú¡ ¢":¢$Z¢&z¢ÿ韖°¢,Ú¢.ú¢0£2:£4Z£6z£8š£:º£<Ú£>ú£@¤B:¤DZ¤Fz¤HZ£•àƒ™Ð¤Nú¤P¥R:¥TZ¥Vz¥Xš¥Zº¥\Ú¥^ú¥`¦b:¦dZ¦fz¦hš¦jz¥…ÐX‚P MÚ r:§tZ§vz§xš§zº§|Ú§~ú§€¨‚:¨þ„Z¨†z¨ˆš¨Šº¨ŒÚ¨Žú¨tÚ¤• zÐX†q©˜š©šº©œÚ©žú©bQ© r?¦zª¨šªªºª¬Úª®úª°«²:«´Z«¶z«¸š«ºº«¼Ú«¾*«"ª¤ªEdÆz¬Èš¬Êº¬ÌÚ¬Îú¬Ð­Ò:­ÔZ­Öz­Øš­Úº­ÜÚ­Þú¬j¬UªßZ®æz®èš®êº®ìÚ®îú®ðš@áªÂJ®ñz¯øš¯úº¯üÚ¯þú¯Å2¯éQ¯Ä °{°›° »° Û­k›ÅÚ°[±{±›±æú°k«± ²";²$«°;®[²*»²,Ûþ².›­';ªöú²4[³6{³%³Ã*±éj±€³ÏЍ` B{®EK²>k®I{¯E‹ 0c`´C«®Gk±:;³Dæ àV@ Ôº´ÐÊð_«®‹ðžð¬0µÌZ´z Ö·ÖJ¶f‹¶`k uk¬x»¯¥ð{û¶È*¶ÞЏÔJ¶¹ 5«p¬ë·v˰¸} 5…°ÈZµÓ*  v‹¬;¹ {µ)Kdg@u« ¥`¿@Ðð¬ŠÛ¬  Ð • PMµë°ÐÂ@ ˆ°p+·R¼D湨+­¹»»½û»Éú¼ñþª °Ê  €@‡û³ÏʼÌz»Ð»:P,¸€+¹Ê[,𘫹œ{¬ž­gP“Ð ¸`Ð Ëj½ «ºl ¹pÃÅbÃ8|¾€µÅ]̬®œ@0»ÖÐ.–ÁV°pÁþÅ‚ @{UÄ'ŒÄ),ÁlÁ¬Á¼ZÌÅ틬  óë*‘ 0`?Ä`,´ €¶0Éq̬ ,¿k|Ã9L´%ÜȀ̬\RÓ  ÖpÇÅ¢ÇKìŤLÄ©+®2»º T tŒÀ¼‹`·z0þk@ Ô <Âð ÊUœ@ @üÌÅÍ~K͸0Ô° pÖ€ зÊÊÌu\,ÔÐ(¸ €|Øüà<¹`ÎÖ€ÎÓì¿L<´» @ Ö PÉÇúÊR3δ| `·»° w+·MÍÿТL ÐÍþ ”ÏûÜÏÍÑ-ÃÙ ¥[,ÀœÇ{l@ Ä. µ§0‡\ÐÐ,Í"mÍØ¬ÍÌÍÞ,Ê/m »ˆàC«¶ÀÉЀ:@ ¨0C6¹EKðÕSíÊ4¼¹ÖÀÒÍÀÏþ\´JÍ{¼¬öœ@ð»7-Ì´üÑ+Àk¬‘@R3`l†[M PÂP,žl¸#p¹á,5àbˆm¬‹}·«Pð¶°ÂÍ: ‚]M_Ý·VpçbNæTý QËÉˆÐæÅÅý=ÖðëèuŽ»VÐÑ»0œûæJÎäp­è-Nä^,w½ç8=Ìý}Ìôв\MÍ0º\Þ"¾±@¶ÐÂ(> Åb Íè1¾Û"LÂiþëÁn ð¶m@ ° –põ‹¬|ÀËÞ ´´Nоì¾×.·Ìâ+ãW¼×Å" p¹ëìåîÈ›Üô\dÞ^MðNÙÏêP,°îÓl Þ­ïRc…¾âäíÃ^ìÇnï ÔïTm  v{ßù],û½éCûßRcñÊnþ j ÞÍ[ÂÄn ÆÞ¬¹¼Ëµ_,/ë~°~¬c@ÄÛ ¨€¿i~ à¿€è:ÍÓÐðjžì˽Â` ×éâÅ"ôDè¶0 ðbÀÎÎÚº‚ÀÓ‹°&­þk %üâ1Þ»ôCo E_åX<´jŸ@ÀzŽ»ºûôQO˱€ ÔÐ Àßso TïöFökÍ(¨Ò‰OöÖ`ö}ÿ÷Ϭ¸°¸ ¸ P,¿¹P9½ÓÊ{ÔBMÔROømÿö¼Ó=­ôOù€Ïß®ìϸ0µZKàÕ`m ƒïßð+û“_òj=öe÷­ô‡Ì¬cP•þ ¿ÀþÛùf ú²þû´>°¶þ×ÇŠ[ÛµVP»bË À(ÞÇüȈ»e{¶áŸúS\þço¸“^,0Њlë¶Rm1`€ƒLÖ Ä²fMdž„‚ hcaB "p`àÉT;:„˜°; MžD™X–#*)³2bÅ‹%&\˜ׄ2$ÜeA Aš6q¦L) Ã,ìJˆjƒ T$4`eÁ Ͳ2 @!Ãן-bÔvlY°ƒ6p*TˆPXeÍ„Þ4|‘£5 ‡ñêÝ‹’A“… U:°àb¹dÍFþNÙ§ËOµrõ*ócbЭ]¿†mR9ÚæôT¨ §¶9±aô\øpâÅ]ç ÌøræÍ‹[°ò{¢sêÕƒ7.ˆ€-ë݋Ϯ};÷îÞÕ±{GŸ¾ùˆ£êÝ¿Oy §§¸¤ÃÇßü|qAzòÿ·<ÚÄÓ­¶ò¨Û@ÕSfì[0BæÔ0€‰-A 7éàf(%!bp…m„ƒ›™²²_m2“l #Ζp€áŠÆwoã’µc ¥úž&]tÉÉ€ÅG^'=0Ë€ ˆÁ…lQ‚%¬¡¦ ÙeEœÿ½yœÙÒ ½3xŸöØbOIl×ȶÆv“ ‚G bµáí\^qþ¢Uä‚~ƒc_p°\ ‹;‰:b0†-VÀ€5P1€»Yá\—û\gR`ψΠ¦Â™  7 aF:ÿ%D_áLÛ—7Q!Í$ÂHK €ÄP6*dß­¡ ,n~@â2‘ Îc¨Û"°2" (œa ¦€Bà‰Ó¡Û`g6+ìÂŽ›Óß¶x(»„4 :œ™Žï…@‹UFÖ¨€™‡Æ­9:ØX,¶“l1!]L­qŠ0²}j4‰ &à‚›Õ/… ìM’GÐð¯‡nÔÝ™B"m_)Á6B ¨ðR´þ°1Nš¥Zl &¿X7j0@f±EG<¹?,8€_L™Æ9ªmjÛæßF bX‚nªi‹5ÞPÖ²bFDZUS%5ÀYÙ¯±Ägw¤IR6Cw&8*¼gAEŠŸNt¢ötèÂÚÖP¬u´riLATÒ•–®¦z<¨ÌnÚJ(zx„„§8e:Ôr©°ŠLÉQí¥F¦©Ü —?\T˜Õªÿ )z²êÓžvç§× üá)²–Õ¬gEëÆŠÖµžµ­f•êÁzUºÖ5‚qä\%dþ ¾öÕ¯l`;XÂÖ°‡Elb»XÆ6Ö±…ldkWêàubz%‚µYÎvÖ³Ÿmheõ ÊŽjª{mjU»ZÖ¶–µ¤-í¾N›Y×ÖÖ¶·ÅmnSÛØo¶O³SW¡¢YݦêÍ(nr•»ÜYñ¶·ýíW+Œ†!‘!î."€¼ä‚T †mË\ò–×¼þp®kª €ë&DÀ¼h÷Æw¾è‰€B˜$¿ûíoz( =‘à4cI)Kñà©‹eöNŽM÷5‹¨Ú;\¨ƒzP‡:pñ‹ï†×UôŠc5Þó¶ØÅ¶Mok0¬áþ“h`ÊXêbcë=Ð@€  qŠ ÈB&ò,«³ ¤® (ÉÙÒÖŒ˜4h ÊPF'Ná ¥† 6œÍ6*.• °YIXh€-Hu;DÀ§"† pPaÎu¾³›á,g@ hà1gK4@@ó‹%=éÍÆØ5b6 4°eklà(šæ´§Ó³ |ækÖ µ©Á–;( #˜€JT)08]j…UåLc×ð8ÀpÁ›R·T¨à'°èd Òþ á_ ÀÏ…v6©íe3;۰œ-`Žp4 ”FwºyeéÖþôÚnüŒ ðîR7kÞèAE½­a ˆPßüvÏ(@€\È%Í0À:1´G·Y¬âõ˜QB\lŒ`6ÓF‰SµaÚ»§ÚJ•kàã§âx¡qA*\,`Χ‚ Ô=sš7W8î&µIỜ7Äw?>€ ¨ [AzÑÕsM'Öä†È!.4 H à —«Þ¾¼ëظ›Ã¦ ‡& qRUâ úÊYl*TD T:(9rË~ö´ZîÐÀœåÞÔÜïW»AãnxÛ›Þñ¾7zVïe Ö¨f<ÿž‘u¼#J~1¿a=¯Zï%Ä».ñ“þ×T«(Ù¡QjêAvGUÈGwR­¾õþxý\AªX¸öþà;à…/|ÁGÆÝš–&>-€ä;-à9<É ŸžÊólÖ(q¸ìð‰Þ5ÔhÆ4ÐŒ»‘ÙÚ°6è X€T@©„€E»Qír7U´§]mÚûcþõµ›sP è¿à¾¤¹âK ð?òKˆ  €f$“@ l2õ@…_†%0Óà@d8ô¨>oš²˜À…%ÀhÀ è7Ï»,ÐÓµT 5«ªá0sØ€,* øR)… €LðX p¸¿T=»€·ß+Â#LBµ+þ„šP4½#TÀ.¤4D d ¬/˜¯2¼/ïPã‘îHˆ5Ì7t숢©  «À œ0²†V©ÓÛ¬ XXY;/DD¿Ãç \ìÄ^Þ¨ l0DüKÄLT·EdĽpD¤-`±„(€(…ÃDMTEIãÄNL‰O,"ÔZÅY¤Å^iEW< X´¥Ìú†^ôÅ_Æ`Æa$Æb4ÆcDÆdTÆedÆftÆg„Æh”FfÄEáÐE «ÆlÔF#¹F?ÜÆoGéF‘¬r4ÇsDÇtTÇudÇv¬plqÜH¬Åz´GX¹Ål”GY¼Ç~ôÇÀþƒÇFä¾ü Ç4È{ÌÇjÜÇ ®@–%¸¶\9DRQ#¯‰<ÈKH\\Hæ³Öت0€(¿‰#€Œ#• e¨ˆt;p?×›IÜ;),O06_éI‰LÅŒ<¯„  Š°NI’üäx¼ ¡: ‚©š‘ɉЀ|Ž”.®sRÀb8(‰¯ë7 lP€pIZÉVù@±oP€¨KøAŒ,6‹ä•Ÿ´ãK¡$¯T†pax˜±$K³ü €€§,„ðbh ©$´¢¨„fÈ p€…‚ ¯|¸| þaP€JØ âr5hR+Hh 87ÛÄMÝ$ex€=ë3Ѓ (¹R¡¯Sè€íñ‡Sð½h´GS±ŠT³`3m¸0 T¡3 XH#îôNµÌÎJê„ p=ãäI—¼ÍÜÜM:³3Ø›?B#ÌÂdð“PMÖÄH €%xÊÀÿ0Ä™khHÊf@oôDîsÔ (ŸÖ@¤Ž‚ômu â:-›eÄÔ%HÏZ‚µã?æ,<~.=Æ8Þxˆ´äE¶Å€Œ B†CfäIŽ_GžÐ@ÝEHœÆMæäNöäOåPåQFK† H6åTVeï@åUvåWV ,dw¤åZ¶å[Æå\vGXneõdJfÜäÞêåôøå`FæÖæØ*fô8æd†fÑZæÒJ]µrÈ\¹ØVÁ €i«­¸Ô­ÁŒæÑâå ^]¨pJ_CÖí€î’•lfXÜçÜçqþÆGßX¯ «/ù2‰~>]ëø/þò/ý"h#020³‘`0ƒ°Ò4g°| É|Jóó012…x^• aåÄ•g‹•zÞ,’f•{ÆgW¹ÅÛ0Ë1ƒióŽ#²"3º »i%£&s2(ëŸo¢2ú€+Ë2N“hLÆF¤èÖÐ.æ03#iî´!?´8{UóüNSé€ËUk Ï;Ójµ<¿îÜj%´6{3¬.4;OòôàÎk‹Oß,´H¨‰%І   cX£c>аlô¾”8÷9´¼ë;‰ì;õärUG•×þð_`E?õc¿™t€%¨mû?°?ÝîëííuüëÞƒ/xïbù£?ßó ¦á%ÞꆲßåÝß{öƒMtðæ¿‰ ø?€,À”ôn_•[tÀñ»› ¬ÀœÀ˜·ïüÀ¬°y¯:”2pJÁlAþ–àÓÞºUÖ6IäA,Ï ¨êæ¥B%$ø!îáÎp'.ª/â®züÄs#DB©çóß=á¦kÜ$aŽ?ôD÷?'„¯z+ÄÂE3ö”WùÝò 12„/3üç¾èê€Ã64‰Á—Cõày;´<¬Š«x÷>ôD ^Žg†_B¼Ä»Ç•i¦¬föþÊÏÏIø°ÄWQiÌgÍ·+ÎïÏ'ÌQ,ÅS¼|Ó¯Ô¯+Õ·Ö—ýÜ?Ú§+ÛDRþàþá'þâ'år&zXVþåçYfþç‡~”ðý7Öåê·þëÇþìgGäGõ¥VÜ×ýðçý«š~çÿðÏýñ·ªòoŽóGÓW¢ªæ†|ch¾XÆæ5é_)}DŒÿ¡æšÓS¡‚‚æÔXkè°¡'R|hÑ"\thÉŸ ˆ”ÈàEŠˆM\™ò¥Ä] ˆK¢K˜:w¦ÌÉó'Љ X j´(Å%TŒ2My YÓ¨R§R¥ø­£E+ lxþ¨ì‚€c~ ;¶aY±X×^̸‘,Xµhãžµ–¶.Û¼ Ö°‡‚0 Á¯^‹ D¨aLj;[£Ð¦æµ çž¿ø¦Ó§ÏêèQ§×/œ,Sz®ê/µm E‘æöÌ[¢Ò¨µ>Ímü¸í«yUÂÐÕ¡† ÊV (Õ0úôê×¥S·N¹£eÌÍ s×n {÷íÙ½×Ë€Àˆ¿­)P©Y. Æ·_<°qBAv‘d:QFz}D,ÒÁ. ‘ Ѐ-þ¨À x²a‡žøC…, EÍ0œD zèÏvDà€?ÍH0€þHDŒ\0ÂRþTxa†.z²ÊIÖ”‰&¢x@$|-¡ÍP …b(Ñn³IÔÉ&e€“25Þ˜£D1ÎèOQÊ<à#/ñgjÒ8ä–0ª1AÀ“¦%  ‹ÈzhJÊéÅÁsÖ@#À) m£JJ飑Z3i{m%øP¦—Vª)§¡nJi§y1 Ã¿X˜5ÍPˆCÍ 0 eþtP€áúP0x€d¢™Dâ X±¬ H„Š9þxBÀŸ¨Íö‹áø£iÁsÀ¸TÛ’—dà= ¼á0­Òƒ€#þü2@ÔZ‹ím^J¤N•PÄ­þ·àÂh9Ø 5êL Ç´Õ^ûg—aCÀg àÂþ´ûn¼±©[ÛPí.›‹AN4o½÷Ö¹nÄûþ9²?X‘&Âá4 #¢=÷¬h^Œ:„ ü©5D´Ò!ªCklCM[stÒE;õÒP³ÅÀ"pðj`¥hítª+€aìK”uâ .T€ Vù3ƘXcKß"¦tb¿èJÔŒ¸*6#,ÚÈ6Ñ7ù³‹©àÂ.K”ÊŽ#¤„¸â”ã"7ûSÈ/^±JÊœ2BÔt‚Á噷茖³IÌ.L„Aè—{^sÂSDþø«ûƒË„ÁKDð>kÐl ÝÐ*Æ€5á_¾ø B¾×ÖÌ]÷ÝèŸo¾úäÓoÍúíc6.äk$`D`m i›€ÞÆ“TQ >Ñ. `ã©ãæ€äüQ‰ @X¶&b  @ᨄF&@‘pX@……KTü LÄ@E&¢ƒ m°ƒ|¡DÆ?¥d„%ôVqü¡zipLä W×%@…„ J±„Jt@2¤¡Dl£‡:Lž%²CN¤‡Q£D 16ú£ *ØsÓ½µ|k[³Öù4ý5¤~¼ÓÎHEþ’Ûy4+³ñ§!i3 cxå¶È,°SpàEôˆÑ°ƒç Qj˜„^Â90=˜¬¢“£#æ4ǹåΪd¥?àʉ¸âá‚I,—Ä%J¤cúe+‡'MŠdÐF,6P”\þŽ\"ž&²äun"½ô&4ƒ¹º¸B"±¨^í˜ÇyNeXéã£ðf `Júäg¥þ‰*G~2 –ì§@ ºÏ:²!D™#+Z5ÄVxI Û4™@NNF/Í@Ä/ˆ¡°Ç"z󇀉¸€\þD´Õ=©iQD Ìò[vÀ=°a’à”þŽí²ƒ?ˆA€]°Ì^ø‚WL§÷âZû€ 4tÊSo2³‰P•éi7k `Bê(ÀØ¥¤*õªI=‹o yMua ÖuNÀê@b(OzÖ(ö´5š± 4ƒ Éš x²’¥ìc#;Ù’vê£!ie5‹YkXv³™½,g É}`>c°~,ЀJæê€õ•;še ²®€2ùãK@8`u¥82áçFW0à­‰˜c@4.êJ÷­6@¤ ¯] ®sr™ë\èf¢ @~y7ëbW»þ\e¢3“»ÜæJó7i’–)Kb&ôª÷­E!$pq¾Dìu/™_ |ñ[Øïä°ÙFàÀ² .,v1XZ¬?ÞÀ·À‹dŒcÛ%Æmèj!€ùào+Èbúc[Çlô"?i¨GHŒ¨ ,BÊVf •‰CÆ+s™'&†2˜Ã,æ1c%£LÆ-™¿sÒ.GEÕê6Ø,gx½yqÊ[ç¬ç/§¹Ï~þ3F—Ü«…:/kÖ3P,q€, ˆî²¢íh-?zÏ…¾4¦3í3úWš¶Æ¡+-êQ“ºÔ@áó§S­jLþç¶„^5¨¿!ëYÓºÖ¶þ¾5®s­ë]óº×¾þ5°ƒ-ìa»ØÆ>v°a­ìe+9“g~5³£-íiS»ÚÖ¾6¶¡ÆéǘºÛÞþ6¸Ã-îq#ÓÙƒºÓ­îu³»Ýî~7¼ã-ïyÓ»Þö¾7¾ó­ï}ó»ßþþ7À.ð¯[Ð !8®ð…3¼á8Ä#.ñ‰«Ûà¡8Æ3®ñs¼ãÿ8ÈÓmñ„¼ä&?9ÊS®ò•|äæ`9Ìc.ó™Ó¼æ w¹Ís®óó¼ç1ǹσ.ô¡½èºÑ“®ô¥3]èHo:Ô£.õ©{üéT¿:Ö³®u[}ë^ÿ:ØÃÞõ°“½ìfWúØÏ®öµ³}æioþ;Üã.wŽ¿}îv¿;Þ^÷¼ó½ï~§÷Þÿ.øÁ>ð„?<âçnøÄ3¾ñe_¼ã#/ù«C~ò–¿|Ò+ùÍs~çšï<èCÏòÏ‹¾ô¦ÿ8éO¯úÕK<õ¬=ìõnîƒÇ¾ö¶oýì/~ûÝó>á®ï=ðƒ/òÜ“\øÆ?>à‰ÿrä3¿ùW¾ó£ýßK¿ú¡§¾õ³yìk¿û‘ç¾÷Ãxð‹¿ü'¿ùÓwô«¿ýqg¿ûã¯vøË¿þb‡¾ýó?~üë¿ÿçç¿ÿàú àû &àü! 6àýµÚ³9 >N Rý] Â\n ¢\~ ‚\þŽ  n\ ž  N\ ®  :\ ¾  ú^Π ‚` Þ ’`î ¢`þ ²`¡Â`¡Ò žÛ>aÄÅ Na3H!>¡^¡f¡!v¡~!ú Ž¡–¡Ú ¦¡ ®!º ¾¡ Æ¡š Ö¡Þ!z î¡ö¡Z ¢"!6 !b&¢" #6b>"$öŸ$NbþU¢%Ö&fbüm"'¶Ÿ'~bú…¢(–)–bø"*vŸ*®böµ¢+V,Æâô%!-þà,Þ"óå¢./ö¢ðý"0Ÿ0#ï£1Þ2&cí-#þ3ž3>#ëE£4ª5V£é]#6Šž6n#èu£7r8†ãöÙ"9ò¡9žã¦£: ";¶c!¾#<"¢<Îã"Ö£=:">æc$î#?R¢?þã%¤@j"Ad'$B‚¢B.ä(6¤Cš"DFd*N$E²¢E^ä+*ŸBt¤G~$H†¤HŽ$I–¤Iž$J¦¤J®$K¶¤K¾$LƤLÎ$MÖ¤MÞ¤I"Nî$Oö¤Oþ$P¥P%Q¥Q’¤N¥R.%S6¥S>%TF¥TŠdcœÁT^%Vf¥Vn%Wv¥PžÁAl@èY–¥Yž%Z¦¥Z®%[¶¥[¾%\Æ¥\Î%]Ö¥]ÞI%^æ¥^î%_ö¥_þ%`&[¶ÁTÀ?b&¦b.&c6¦c>&dF¦dN&eV¦e^&ff¦fn&gv¦g~&h†¦hŽ&iRæ?;igraph/inst/benchmarks/0000755000176000001440000000000012325365704014655 5ustar ripleyusersigraph/inst/benchmarks/time_sir.R0000644000176000001440000000040412266024104016577 0ustar ripleyusers time_group("SIR epidemics models on networks") time_that("SIR is fast", replications=10, init = { library(igraph); set.seed(42) }, reinit = { g <- erdos.renyi.game(40, 40, type="gnm") }, { sir(g, beta=5, gamma=1, no.sim=100) }) igraph/inst/benchmarks/time_call.R0000644000176000001440000000103512325263644016730 0ustar ripleyusers time_group(".Call from R") time_that("Redefining .Call does not have much overhead #1", replications=10, init = { library(igraph) ; g <- graph.ring(100) }, { for (i in 1:20000) { base::.Call("R_igraph_vcount", g, PACKAGE = "igraph") } }) time_that("Redefining .Call does not have much overhead #1", replications=10, init = { library(igraph) ; g <- graph.ring(100) }, { for (i in 1:20000) { igraph:::.Call("R_igraph_vcount", g, PACKAGE = "igraph") } }) igraph/inst/igraph.gif0000644000176000001440000000376112240234657014506 0ustar ripleyusersGIF89a––öî õòð çéçäÞ&%Ý('Ú,+×0/Î=<Ó65Ð:9à!!Ì@?¿TT·__»ZZ­nn¶bb°ii­po§wv¨ut£}|ÊDCÅKKÀQQêÝüîõçöè øê ÿðîáíàòåÞÓ.×Í7ÙÎ4ÒÇ>ÔÊ;ÜÑ0ã×&åÙ#èÛ áÖ)¼µ[¿¸X£Ÿ~¯©oµ¯f²«k·°d¹²a¬¦s®¨p¦¡z¨£xǾMÈ¿LºTÎÄDÊÁIÐÇ@ž……—š‹‹žš†—”™–Œœ™ˆ•’’¡€€¡œ!ùM,––þ€M‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌJÏÐÍÁÐÔÔÒ•ÕÕœÙÜJ×ÝÜ™Ü;4IÙߌáë—Ô3'"#ò$?ÚéˆëùØÐ?òÿò\0±v¯P¾ƒÞ"Q«°á §ȉ-Rü˜Ná³$ 6ìñ^·&DŽp¡C¸g=TQ2]6&dþâÒ4:ÿíˆV0ÛŽAå±è¹ˆZʤ1ˆš¤†„DR€BÐ5¢f5é ©ß²­¸ÚІ½­ÏP\ø¬(µþþÈþƒx¶é³IAÔ”VM…܆-÷â{v¤DP‚—eCúWÏƨåˆ+r)ØkÕ˜4H÷²"j:ºüäˆg¾’7ÿ3¡í3$=`ˆøPEŽºa©ÙP-&îÏÜ0µù–÷¿Õ”8‘Zã0’+ïÆ|P¶¼ëAND½ºAjx7`{Ún[!a,EkŸåÑ7É&£±òç_æO¶==´±d}PÃ{ðíW ;Bs„ZI}•tÀ8 BÎdóC<2‘p®%t`6 Ç]6Fñg2t§Ÿ‡ÄT³Ã | ‚ @œßwÝðPƒ 1Ø€„²8!4=P¶šY7þ‚‚’ô· 5~É„¤„#.é¤Ç@CCP&„(¤yVîCå/ÔäTI²dzÓ} ŒzFª]šþ¹¦’têMWù6f“Vv4_ž¹@³[ƒWv(“*ñ\R/$Úæ¢‚ãTRYýéBDÜ™å]A}À‘¦ r£A ½Tðè¬ ˜F´ç$ª‘ èÄÝÄ¥:ƒ rÉFéœ'=•ƒöÈSŸ+R0D »Bc¢¸G„ÖéDrÁCOùÕL©¡}œf6£ÈŒZ‚FP ¥8Ñjh/(Á¨GÙ,¹ˆàœ*])랃Ƭ %­†=“b|ÆT¦Õ09“rÑfÊ´Ž58)@sЕfƒfI úTÓ£šQ A ¨<>Ÿ’ô¨Ý8‚ dЃÜÀ«_µê-­º b²u˜º|k.…)×¶¦µ®xÍ«^÷Ê×¾úõ¯€ ¬`KØÂö°ˆM¬bËØÆ:V¦;igraph/configure.in0000644000176000001440000000657212263024035014074 0ustar ripleyusersAC_INIT(igraph, @VERSION@, csardi.gabor@gmail.com) AC_CONFIG_SRCDIR(src/rinterface.c) AC_CONFIG_HEADERS(src/config.h) : ${R_HOME=`R RHOME`} if test -z "${R_HOME}"; then echo "could not determine R_HOME" exit 1 fi CC=`"${R_HOME}/bin/R" CMD config CC` CXX=`"${R_HOME}/bin/R" CMD config CXX` FC=`"${R_HOME}/bin/R" CMD config FC` CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS` CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXXFLAGS` CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` FCFLAGS=`"${R_HOME}/bin/R" CMD config FCFLAGS` FLIBS=`"${R_HOME}/bin/R" CMD config FLIBS` AC_LANG(C) AC_PROG_CC # Fortran compiler, we need to check if it is the GNU compiler AC_PROG_FC if test "x$ac_cv_fc_compiler_gnu" == xyes; then AC_DEFINE([HAVE_GFORTRAN], [1], [Define to 1 if using the GNU fortran compiler]) fi # Tricky check for C++ compiler, because Autoconf has a weird bug: # http://lists.gnu.org/archive/html/autoconf/2006-03/msg00067.html AC_PROG_CXX AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include const char hw[] = "Hello, World\n";]], [[std::cout << hw;]])], [AC_PROG_CXXCPP cxx_error=no], [AC_MSG_ERROR([no C++ compiler found or it cannot create executables])]) AC_LANG_POP([C++]) LIBS_SAVE=$LIBS LIBS="$LIBS -lm" AC_CHECK_FUNCS([rintf finite expm1 rint log2 logbl snprintf log1p round fmin stpcpy]) AC_CHECK_DECL([stpcpy], [AC_DEFINE([HAVE_STPCPY_SIGNATURE], [1], [Define to 1 if the stpcpy function has a signature])]) LIBS=$LIBS_SAVE AC_CHECK_HEADER([sys/times.h], [AC_DEFINE([HAVE_TIMES_H], [1], [Define to 1 if you have the sys/times.h header])]) graphml_support=yes AC_ARG_ENABLE(graphml, AC_HELP_STRING([--disable-graphml], [Disable support for GraphML format]), [graphml_support=$enableval], [graphml_support=yes]) HAVE_LIBXML=0 if test $graphml_support = yes; then AC_PATH_PROG([XML2CONFIG], [xml2-config], [none]) if test "$XML2CONFIG" = "none"; then graphml_support=no else XML2_LIBS=`$XML2CONFIG --libs` XML2_CFLAGS=`$XML2CONFIG --cflags` AC_CHECK_LIB([xml2], [xmlSAXUserParseFile], [ OLDCFLAGS=${CFLAGS} OLDCPPFLAGS=${CPPFLAGS} CFLAGS=${XML2_CFLAGS} CPPFLAGS=${XML2_CFLAGS} AC_CHECK_HEADER([libxml/parser.h], [ HAVE_LIBXML=1 AC_DEFINE([HAVE_LIBXML], [1], [Define to 1 if you have the libxml2 libraries installed]) CFLAGS="${OLDCFLAGS} ${XML2_CFLAGS}" CPPFLAGS="${OLDCFLAGS} ${XML2_CFLAGS}" AC_SUBST(XML2_LIBS) AC_SUBST(XML2_CFLAGS) ], [ graphml_support=no CFLAGS=${OLDCFLAGS} CPPFLAGS=${OLDCPPFLAGS} ]) ], [ graphml_support=no ]) fi fi AC_SUBST(HAVE_LIBXML) AC_LANG_PUSH([C++]) HAVE_GMP=0 GMP_LIBS="" gmp_support=no AC_ARG_ENABLE(gmp, AC_HELP_STRING([--disable-gmp], [Compile without the GMP library])) if test "x$enable_gmp" != "xno"; then AC_CHECK_LIB([gmp], [__gmpz_add], [ AC_CHECK_HEADER([gmp.h], [ HAVE_GMP=1 AC_DEFINE([HAVE_GMP], [1], [Define to 1 if you have the GMP library]) gmp_support=yes GMP_LIBS="-lgmp" ]) ]) fi AC_SUBST(HAVE_GMP) AC_SUBST(GMP_LIBS) AC_LANG_POP([C++]) glpk_support=yes AC_DEFINE([HAVE_GLPK], [1], [Define to 1 if you have the GLPK library]) HAVE_GLPK=1 GLPK_LIBS="" AC_SUBST(HAVE_GLPK) AC_SUBST(GLPK_LIBS) AC_DEFINE(IGRAPH_THREAD_LOCAL, [], [We don't care about thread-local storage in R]) AC_CONFIG_FILES([src/Makevars]) AC_OUTPUT igraph/src/0000755000176000001440000000000012325527071012347 5ustar ripleyusersigraph/src/NetDataTypes.h0000644000176000001440000005411212325527072015071 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Jörg Reichardt The original copyright notice follows here */ /*************************************************************************** NetDataTypes.h - description ------------------- begin : Mon Oct 6 2003 copyright : (C) 2003 by Joerg Reichardt email : reichardt@mitte ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef NETDATATYPES_H #define NETDATATYPES_H #include //########################################################################################### struct HUGE_INDEX { unsigned int field_index; unsigned long in_field_index; }; template class HugeArray { private: unsigned long int size; unsigned int highest_field_index; unsigned long max_bit_left; unsigned long max_index; DATA *data; DATA *fields[32]; public: HUGE_INDEX get_huge_index(unsigned long); DATA &Set(unsigned long); DATA Get(unsigned long); HugeArray(void); ~HugeArray(void); DATA &operator[](unsigned long); unsigned long Size(void) {return max_index;} } ; //############################################################################################### template class DLList; template class DL_Indexed_List; template class ClusterList; template class DLList_Iter; template class DLItem { friend class DLList ; friend class DL_Indexed_List; friend class DLList_Iter; private: L_DATA item; unsigned long index; DLItem *previous; DLItem *next; DLItem(L_DATA i, unsigned long ind); DLItem(L_DATA i, unsigned long ind, DLItem *p, DLItem *n); ~DLItem(); public: void del() { delete item; } }; template class DLList { friend class DLList_Iter; protected: DLItem *head; DLItem *tail; unsigned long number_of_items; DLItem *pInsert(L_DATA, DLItem*); L_DATA pDelete(DLItem*); public: DLList(void); ~DLList(); unsigned long Size(void) { return number_of_items; } int Insert(L_DATA, unsigned long); int Delete(unsigned long); int fDelete(L_DATA); L_DATA Push(L_DATA); L_DATA Pop(void); L_DATA Get(unsigned long); int Enqueue(L_DATA); L_DATA Dequeue(void); unsigned long Is_In_List(L_DATA); void delete_items(); }; template class DL_Indexed_List : virtual public DLList { friend class DLList_Iter; private: DLItem *pInsert(L_DATA, DLItem*); L_DATA pDelete(DLItem*); HugeArray*> array; unsigned long last_index; public: DL_Indexed_List(void); ~DL_Indexed_List(); L_DATA Push(L_DATA); L_DATA Pop(void); L_DATA Get(unsigned long); }; //##################################################################################################### template class DLList_Iter { private: DLList *list; DLItem *current; bool end_reached; public: DLList_Iter(void); ~DLList_Iter() {end_reached=true;}; L_DATA Next(void); L_DATA Previous(void); L_DATA First(DLList *l); L_DATA Last(DLList *l); bool End(void) {return end_reached;} DLItem *Get_Current(void) {return current;} L_DATA Get_Current_Item(void) {return current->item;} void Set_Current(DLItem *c) {current=c;} void Set_Status(bool s) {end_reached=s;} bool Swap(DLList_Iter); //swapt die beiden Elemente, wenn sie in der gleichen Liste stehen!! }; //##################################################################################################### struct RGBcolor { unsigned int red; unsigned int green; unsigned int blue; char pajek_c[20]; }; //------------------------------------------------------------------------------- class NLink; class NNode { friend class NLink; private : unsigned long index; unsigned long cluster_index; unsigned long marker, affiliations; unsigned long *state_history; unsigned int max_states; long distance; double clustering; double weight; double affinity; // double old_weight; DLList *neighbours; //list with pointers to neighbours DLList *n_links; DLList *global_link_list; char name[255]; RGBcolor color; public : NNode(unsigned long, unsigned long, DLList*, char*, int); ~NNode(); unsigned long Get_Index(void) { return(index); } unsigned long Get_ClusterIndex(void) { return(cluster_index);} unsigned long Get_Marker(void) { return marker;} void Set_Marker(unsigned long m) {marker=m;} unsigned long Get_Affiliations(void) { return affiliations;} void Set_Affiliations(unsigned long m) {affiliations=m;} void Set_ClusterIndex(unsigned long ci) { cluster_index=ci; return;} void Set_Index(unsigned long i) {index=i; return;} unsigned long Get_Degree(void) { return(neighbours->Size());} char *Get_Name(void) {return name;} void Set_Name(char* n) {strcpy(name,n);} double Get_Links_Among_Neigbours(void); double Get_Clustering(void); double Get_Weight(void) {return weight;} double Get_Affinity(void) {return affinity;} unsigned long *Get_StateHistory(void) {return state_history;} void Add_StateHistory(unsigned int q); // double Get_OldWeight(void) {return old_weight;} void Set_Weight(double w) {weight=w;} void Set_Affinity(double w) {affinity=w;} // void Set_OldWeight(double w) {old_weight=w;} long Get_Distance(void) {return distance;} void Set_Distance(long d) {distance=d;} int Connect_To(NNode*, double); DLList *Get_Neighbours(void) {return neighbours;} DLList *Get_Links(void) {return n_links;} int Disconnect_From(NNode*); int Disconnect_From_All(void); bool Is_Linked_To(NNode*); RGBcolor Get_Color(void) {return color;} void Set_Color(RGBcolor c); NLink *Get_LinkToNeighbour(NNode *neighbour); }; //##################################################################################################### class NLink { friend class NNode; private : NNode *start; NNode *end; double weight; double old_weight; unsigned long index; unsigned long marker; public : NLink( NNode*, NNode*, double); ~NLink(); unsigned long Get_Start_Index(void) { return(start->Get_Index()); } unsigned long Get_End_Index(void) { return(end->Get_Index()); } NNode *Get_Start(void) {return(start);} NNode *Get_End(void) {return(end);} double Get_Weight(void) {return weight;} void Set_Weight(double w) {weight=w;} double Get_OldWeight(void) {return old_weight;} void Set_OldWeight(double w) {old_weight=w;} unsigned long Get_Marker(void) {return marker;} void Set_Marker(unsigned long m) {marker=m;} unsigned long Get_Index() {return index;} void Set_Index(unsigned long i) {index=i;} }; //##################################################################################################### template class ClusterList : public DLList { friend class DLList_Iter; private: long links_out_of_cluster; unsigned long links_inside_cluster; unsigned long frequency; double cluster_energy; DLList *candidates; long marker; public: ClusterList(void); ~ClusterList(); long Get_Links_OOC(void) {return(links_out_of_cluster);} void Set_Links_OOC(long looc) {links_out_of_cluster=looc;} unsigned long Get_Links_IC(void) {return(links_inside_cluster);} unsigned long Get_Frequency(void) {return(frequency);} void IncreaseFrequency(void) {frequency++;} void Set_Links_IC(unsigned long lic) {links_inside_cluster=lic;} double Get_Energy(void) {return (cluster_energy);} void Set_Energy(double e) {cluster_energy=e;} DLList *Get_Candidates(void) {return candidates;} bool operator<(ClusterList &b); bool operator==(ClusterList &b); long Get_Marker(void) {return marker;} void Set_Marker(long m) {marker=m;} }; //##################################################################################################### template class DL_Node_List : virtual public DL_Indexed_List { friend class DLList_Iter; private: DLItem *pInsert(NNode*, DLItem*); NNode* pDelete(DLItem*); HugeArray*> array; unsigned long last_index; public: DL_Node_List(void); ~DL_Node_List(); NNode* Push(NNode*); NNode* Pop(void); NNode* Get(unsigned long); int Delete(unsigned long); }; //##################################################################################################### struct cluster_join_move { ClusterList *c1; ClusterList *c2; double joint_energy; long joint_looc; unsigned long joint_lic; } ; struct network { DL_Indexed_List *node_list; DL_Indexed_List *link_list; DL_Indexed_List*> *cluster_list; DL_Indexed_List *moveset; unsigned long max_k; unsigned long min_k; unsigned long diameter; double av_weight; double max_weight; double min_weight; double sum_weights; double av_k; double av_bids; unsigned long max_bids; unsigned long min_bids; unsigned long sum_bids; } ; /* struct network { DLList *node_list; DLList *link_list; DLList*> *cluster_list; DLList *moveset; } ; */ template HugeArray::HugeArray(void) { max_bit_left=1<<31; //wir setzen das 31. Bit auf 1 size=2; max_index=0; highest_field_index=0; data=new DATA[2]; //ein extra Platz fuer das Nullelement data[0]=0; data[1]=0; for (int i=0; i<32; i++) fields[i]=NULL; fields[highest_field_index]=data; } template HugeArray::~HugeArray(void) { for (unsigned int i=0; i<=highest_field_index; i++) { data=fields[i]; delete [] data; } } template HUGE_INDEX HugeArray::get_huge_index(unsigned long index) { HUGE_INDEX h_index; unsigned int shift_index=0; unsigned long help_index; help_index=index; if (index<2) { h_index.field_index=0; h_index.in_field_index=index; return h_index; } // wie oft muessen wir help_index nach links shiften, damit das 31. Bit gesetzt ist?? while (!(max_bit_left & help_index)) { help_index <<= 1; shift_index++; } h_index.field_index=31-shift_index; // das hoechste besetzte Bit im Index help_index=1 << h_index.field_index; // in help_index wird das hoechste besetzte Bit von Index gesetzt h_index.in_field_index=(index ^ help_index); // index XOR help_index, womit alle bits unter dem hoechsten erhalten bleiben return h_index; } template DATA &HugeArray::Set(unsigned long int index) { HUGE_INDEX h_index; unsigned long data_size; while (size DATA HugeArray::Get(unsigned long index) { return(Set(index)); } template DATA &HugeArray::operator[](unsigned long index) { return(Set(index)); } //############################################################################### template DLItem::DLItem(L_DATA i, unsigned long ind) : item(i), index(ind), previous(0), next(0) { } template DLItem::DLItem(L_DATA i, unsigned long ind, DLItem *p, DLItem *n) : item(i), index(ind), previous(p), next(n) { } template DLItem::~DLItem() { //delete item; //eigentlich muessten wir pruefen, ob item ueberhaupt ein Pointer ist... //previous=NULL; //next=NULL; } //###################################################################################################################### template DLList::DLList(void) { head=tail=NULL; number_of_items=0; head=new DLItem(NULL,0); //fuer head und Tail gibt es das gleiche Array-Element!! Vorsicht!! tail=new DLItem(NULL,0); if ( !head || !tail ) { if (head) delete(head); if (tail) delete(tail); return; } else { head->next=tail; tail->previous=head; } } template DLList::~DLList() { DLItem *cur=head, *next; while (cur) { next=cur->next; delete(cur); cur=next; } number_of_items=0; // printf("Liste Zerstoert!\n"); } template void DLList::delete_items() { DLItem *cur, *next; cur=this->head; while (cur) { next=cur->next; cur->del(); cur=next; } this->number_of_items=0; } //privates Insert template DLItem *DLList::pInsert(L_DATA data, DLItem *pos) { DLItem *i=new DLItem(data, number_of_items+1, pos->previous, pos); if (i) { pos->previous->next=i; pos->previous=i; number_of_items++; return(i); } else return(0); } //privates delete template L_DATA DLList::pDelete(DLItem *i) { L_DATA data=i->item; i->previous->next=i->next; i->next->previous=i->previous; // array[i->index]=0; delete(i); number_of_items--; return(data); } //oeffentliches Insert template int DLList::Insert(L_DATA data, unsigned long pos) { if ((pos<0)||(pos>(number_of_items))) return(0); DLItem *cur=head; while(pos--) cur=cur->next; return(pInsert(data,cur)!=0); } //oeffentliche Delete template int DLList::Delete(unsigned long pos) { if ((pos<0)||(pos>(number_of_items))) return(0); DLItem *cur=head; while(pos--) cur=cur->next; return(pDelete(cur)!=0); } //oeffentliche Delete template int DLList::fDelete(L_DATA data) { if ((number_of_items==0) || (!data)) return(0); DLItem *cur; cur=head->next; while ((cur!=tail) && (cur->item!=data)) cur=cur->next; if (cur!=tail) return(pDelete(cur)!=0); return(0); } template L_DATA DLList::Push(L_DATA data) { DLItem *tmp; tmp=pInsert(data,tail); if (tmp) return (tmp->item); return(0); } template L_DATA DLList::Pop(void) { return(pDelete(tail->previous)); } template L_DATA DLList::Get(unsigned long pos) { if ((pos<1)||(pos>(number_of_items+1))) return(0); // return(array[pos]->item); DLItem *cur=head; while(pos--) cur=cur->next; return(cur->item); } template int DLList::Enqueue(L_DATA data) { return(pInsert(data,tail)!=0); } template L_DATA DLList::Dequeue(void) { return(pDelete(head->next)); } //gibt Index des gesuchte Listenelement zurueck, besser waere eigentlich zeiger template unsigned long DLList::Is_In_List(L_DATA data) { DLItem *cur=head, *next; unsigned long pos=0; while (cur) { next=cur->next; if (cur->item==data) return(pos) ; cur=next; pos++; } return(0); } //###################################################################################################################### template DL_Indexed_List::DL_Indexed_List(void) : DLList() { last_index=0; } template DL_Indexed_List::~DL_Indexed_List() { /* This is already done by the DLList destructor */ /* DLItem *cur, *next; */ /* cur=this->head; */ /* while (cur) */ /* { */ /* next=cur->next; */ /* delete(cur); */ /* cur=next; */ /* } */ /* this->number_of_items=0; */ // printf("Liste Zerstoert!\n"); } //privates Insert template DLItem *DL_Indexed_List::pInsert(L_DATA data, DLItem *pos) { DLItem *i=new DLItem(data, last_index, pos->previous, pos); if (i) { pos->previous->next=i; pos->previous=i; this->number_of_items++; array[last_index]=i; last_index++; return(i); } else return(0); } //privates delete template L_DATA DL_Indexed_List::pDelete(DLItem *i) { L_DATA data=i->item; i->previous->next=i->next; i->next->previous=i->previous; array[i->index]=0; last_index=i->index; delete(i); this->number_of_items--; return(data); } template L_DATA DL_Indexed_List::Push(L_DATA data) { DLItem *tmp; tmp=pInsert(data,this->tail); if (tmp) return (tmp->item); return(0); } template L_DATA DL_Indexed_List::Pop(void) { return(pDelete(this->tail->previous)); } template L_DATA DL_Indexed_List::Get(unsigned long pos) { if (pos > this->number_of_items - 1) return(0); return(array[pos]->item); } //####################################################################################### //************************************************************************************************************ template ClusterList::ClusterList(void) : DLList() { links_out_of_cluster=0; links_inside_cluster=0; frequency=1; cluster_energy=1e30; candidates=new DLList(); marker=0; } template ClusterList::~ClusterList() { while (candidates->Size()) { candidates->Pop(); } delete candidates; } template bool ClusterList::operator==(ClusterList &b) { bool found=false; L_DATA n_cur, n_cur_b; DLList_Iter a_iter,b_iter; if (this->Size()!=b.Size()) return false; n_cur=a_iter.First(this); while (!(a_iter.End())) { found=false; n_cur_b=b_iter.First(&b); while (!(b_iter.End()) && !found) { if (n_cur==n_cur_b) found=true; n_cur_b=b_iter.Next(); } if (!found) return false; n_cur=a_iter.Next(); } return(found); } //A bool ClusterList::operator<(ClusterList &b) { bool found=false; L_DATA n_cur, n_cur_b; DLList_Iter a_iter, b_iter; if (this->Size()>=b.Size()) return false; n_cur=a_iter.First(this); while (!(a_iter.End())) { found=false; n_cur_b=b_iter.First(&b); while (!(b_iter.End()) && !found) { if (n_cur==n_cur_b) found=true; n_cur_b=b_iter.Next(); } if (!found) return false; n_cur=a_iter.Next(); } return(found); } //##################################################################################### template DLList_Iter::DLList_Iter() { list=NULL; current=NULL; end_reached=true; } template L_DATA DLList_Iter::Next(void) { current=current->next; if (current==(list->tail)) end_reached=true; return(current->item); } template L_DATA DLList_Iter::Previous(void) { current=current->previous; if (current==(list->head)) end_reached=true; return(current->item); } template L_DATA DLList_Iter::First(DLList *l) { list=l; current=list->head->next; if (current==(list->tail)) end_reached=true; else end_reached=false; return(current->item); } template L_DATA DLList_Iter::Last(DLList *l) { list=l; current=list->tail->previous; if (current==(list->head)) end_reached=true; // falls die List leer ist else end_reached=false; return(current->item); } template bool DLList_Iter::Swap(DLList_Iter b) { L_DATA h; if (list!=b.list) return false; //elemeten muessen aus der gleichen List stammen if (end_reached || b.end_reached) return false; h=current->item; current->item=b.current->item; b.current->item=h; return true; } #endif igraph/src/bignum.h0000644000176000001440000001051612325527072014005 0ustar ripleyusers/***************************************************************************** * Entropy - Emerging Network To Reduce Orwellian Potency Yield * * Copyright (C) 2005 Juergen Buchmueller * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * * $Id: bignum.h,v 1.6 2005/08/11 17:57:39 pullmoll Exp $ *****************************************************************************/ #ifndef _bignum_h_ #define _bignum_h_ #include "config.h" #ifdef HAVE_STDINT_H # include #else # ifdef HAVE_SYS_INT_TYPES_H # include # else # include "pstdint.h" # endif #endif #include #include #include #ifndef NULL #define NULL 0 #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef HAVE_U64 #define HAVE_U64 1 #endif /* up to 512 limbs (512 * 32 = 16384 bits) numbers */ #define BN_MAXSIZE 512 #define LIMBBITS 32 #define LIMBMASK 0xfffffffful #define HALFMASK 0x0000fffful #define DIGMSB 0x80000000ul #define DIGLSB 0x00000001ul typedef uint32_t count_t; typedef uint16_t half_t; typedef uint32_t limb_t; #if HAVE_U64 typedef uint64_t dlimb_t; #endif /* less significant half limb */ #define LSH(d) ((half_t)(d)) /* more significant half limb */ #define MSH(d) ((limb_t)(d)>>16) /* shift left half limb */ #define SHL(d) ((limb_t)(d)<<16) /* single limb functions */ limb_t sl_div(limb_t *q, limb_t *r, limb_t u[2], limb_t v); limb_t sl_gcd(limb_t x, limb_t y); int sl_modexp(limb_t *exp, limb_t x, limb_t n, limb_t d); int sl_modinv(limb_t *inv, limb_t u, limb_t v); int sl_modmul(limb_t *a, limb_t x, limb_t y, limb_t m); int sl_mul(limb_t p[2], limb_t x, limb_t y); /* big number functions (max. MAXSIZE limbs) */ void bn_zero(limb_t a[], count_t nlimb); void bn_limb(limb_t a[], limb_t d, count_t nlimb); void bn_copy(limb_t a[], limb_t b[], count_t nlimb); count_t bn_sizeof(limb_t a[], count_t nlimb); int bn_cmp_limb(limb_t a[], limb_t b, count_t nlimb); int bn_cmp(limb_t a[], limb_t b[], count_t nlimb); /* big number to hex, decimal, binary */ const char *bn2x(limb_t a[], count_t nlimb); const char *bn2d(limb_t a[], count_t nlimb); const char *bn2f(limb_t a[], count_t alimb, limb_t b[], count_t blimb); const char *bn2b(limb_t a[], count_t nlimb); /* big number with single limb operations */ limb_t bn_add_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb); limb_t bn_sub_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb); limb_t bn_div_limb(limb_t q[], limb_t u[], limb_t v, count_t nlimb); limb_t bn_mod_limb(limb_t u[], limb_t d, count_t nlimb); limb_t bn_mul_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb); /* big number with single limb <= HALFMASK operations */ limb_t bn_div_half(limb_t q[], limb_t u[], limb_t v, count_t nlimb); limb_t bn_mod_half(limb_t a[], limb_t d, count_t nlimb); /* big number operations */ limb_t bn_add(limb_t w[], limb_t u[], limb_t v[], count_t nlimb); limb_t bn_sub(limb_t w[], limb_t u[], limb_t v[], count_t nlimb); limb_t bn_shl(limb_t a[], limb_t b[], count_t x, count_t nlimb); limb_t bn_shr(limb_t a[], limb_t b[], count_t x, count_t nlimb); int bn_mul(limb_t w[], limb_t u[], limb_t v[], count_t nlimb); int bn_div(limb_t q[], limb_t r[], limb_t u[], limb_t v[], count_t ulimb, count_t vlimb); limb_t bn_mod(limb_t r[], limb_t u[], count_t ulimb, limb_t v[], count_t vlimb); int bn_gcd(limb_t g[], limb_t x[], limb_t y[], count_t nlimb); int bn_sqrt(limb_t g[], limb_t x[], limb_t y[], count_t rlimb, count_t nlimb); int bn_modexp(limb_t y[], limb_t x[], limb_t e[], limb_t m[], count_t nlimb); int bn_modinv(limb_t inv[], limb_t u[], limb_t v[], count_t nlimb); limb_t bn_modmul(limb_t a[], limb_t x[], limb_t y[], limb_t m[], count_t nlimb); #endif /* !defined(_bignum_h_) */ igraph/src/walktrap_communities.cpp0000644000176000001440000006623312325527074017331 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Pascal Pons The original copyright notice follows here. The FSF address was fixed by Tamas Nepusz */ // File: communities.cpp //----------------------------------------------------------------------------- // Walktrap v0.2 -- Finds community structure of networks using random walks // Copyright (C) 2004-2005 Pascal Pons // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA //----------------------------------------------------------------------------- // Author : Pascal Pons // Email : pascal.pons@gmail.com // Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html // Location : Paris, France // Time : June 2005 //----------------------------------------------------------------------------- // see readme.txt for more details #include "walktrap_communities.h" #include #include #include #include #include "config.h" namespace igraph { namespace walktrap { IGRAPH_THREAD_LOCAL int Probabilities::length = 0; IGRAPH_THREAD_LOCAL Communities* Probabilities::C = 0; IGRAPH_THREAD_LOCAL float* Probabilities::tmp_vector1 = 0; IGRAPH_THREAD_LOCAL float* Probabilities::tmp_vector2 = 0; IGRAPH_THREAD_LOCAL int* Probabilities::id = 0; IGRAPH_THREAD_LOCAL int* Probabilities::vertices1 = 0; IGRAPH_THREAD_LOCAL int* Probabilities::vertices2 = 0; IGRAPH_THREAD_LOCAL int Probabilities::current_id = 0; Neighbor::Neighbor() { next_community1 = 0; previous_community1 = 0; next_community2 = 0; previous_community2 = 0; heap_index = -1; } Probabilities::~Probabilities() { C->memory_used -= memory(); if(P) delete[] P; if(vertices) delete[] vertices; } Probabilities::Probabilities(int community) { Graph* G = C->G; int nb_vertices1 = 0; int nb_vertices2 = 0; float initial_proba = 1./float(C->communities[community].size); int last = C->members[C->communities[community].last_member]; for(int m = C->communities[community].first_member; m != last; m = C->members[m]) { tmp_vector1[m] = initial_proba; vertices1[nb_vertices1++] = m; } for(int t = 0; t < length; t++) { current_id++; if(nb_vertices1 > (G->nb_vertices/2)) { nb_vertices2 = G->nb_vertices; for(int i = 0; i < G->nb_vertices; i++) tmp_vector2[i] = 0.; if(nb_vertices1 == G->nb_vertices) { for(int i = 0; i < G->nb_vertices; i++) { float proba = tmp_vector1[i]/G->vertices[i].total_weight; for(int j = 0; j < G->vertices[i].degree; j++) tmp_vector2[G->vertices[i].edges[j].neighbor] += proba*G->vertices[i].edges[j].weight; } } else { for(int i = 0; i < nb_vertices1; i++) { int v1 = vertices1[i]; float proba = tmp_vector1[v1]/G->vertices[v1].total_weight; for(int j = 0; j < G->vertices[v1].degree; j++) tmp_vector2[G->vertices[v1].edges[j].neighbor] += proba*G->vertices[v1].edges[j].weight; } } } else { nb_vertices2 = 0; for(int i = 0; i < nb_vertices1; i++) { int v1 = vertices1[i]; float proba = tmp_vector1[v1]/G->vertices[v1].total_weight; for(int j = 0; j < G->vertices[v1].degree; j++) { int v2 = G->vertices[v1].edges[j].neighbor; if(id[v2] == current_id) tmp_vector2[v2] += proba*G->vertices[v1].edges[j].weight; else { tmp_vector2[v2] = proba*G->vertices[v1].edges[j].weight; id[v2] = current_id; vertices2[nb_vertices2++] = v2; } } } } float* tmp = tmp_vector2; tmp_vector2 = tmp_vector1; tmp_vector1 = tmp; int* tmp2 = vertices2; vertices2 = vertices1; vertices1 = tmp2; nb_vertices1 = nb_vertices2; } if(nb_vertices1 > (G->nb_vertices/2)) { P = new float[G->nb_vertices]; size = G->nb_vertices; vertices = 0; if(nb_vertices1 == G->nb_vertices) { for(int i = 0; i < G->nb_vertices; i++) P[i] = tmp_vector1[i]/sqrt(G->vertices[i].total_weight); } else { for(int i = 0; i < G->nb_vertices; i++) P[i] = 0.; for(int i = 0; i < nb_vertices1; i++) P[vertices1[i]] = tmp_vector1[vertices1[i]]/sqrt(G->vertices[vertices1[i]].total_weight); } } else { P = new float[nb_vertices1]; size = nb_vertices1; vertices = new int[nb_vertices1]; int j = 0; for(int i = 0; i < G->nb_vertices; i++) { if(id[i] == current_id) { P[j] = tmp_vector1[i]/sqrt(G->vertices[i].total_weight); vertices[j] = i; j++; } } } C->memory_used += memory(); } Probabilities::Probabilities(int community1, int community2) { // The two following probability vectors must exist. // Do not call this function if it is not the case. Probabilities* P1 = C->communities[community1].P; Probabilities* P2 = C->communities[community2].P; float w1 = float(C->communities[community1].size)/float(C->communities[community1].size + C->communities[community2].size); float w2 = float(C->communities[community2].size)/float(C->communities[community1].size + C->communities[community2].size); if(P1->size == C->G->nb_vertices) { P = new float[C->G->nb_vertices]; size = C->G->nb_vertices; vertices = 0; if(P2->size == C->G->nb_vertices) { // two full vectors for(int i = 0; i < C->G->nb_vertices; i++) P[i] = P1->P[i]*w1 + P2->P[i]*w2; } else { // P1 full vector, P2 partial vector int j = 0; for(int i = 0; i < P2->size; i++) { for(; j < P2->vertices[i]; j++) P[j] = P1->P[j]*w1; P[j] = P1->P[j]*w1 + P2->P[i]*w2; j++; } for(; j < C->G->nb_vertices; j++) P[j] = P1->P[j]*w1; } } else { if(P2->size == C->G->nb_vertices) { // P1 partial vector, P2 full vector P = new float[C->G->nb_vertices]; size = C->G->nb_vertices; vertices = 0; int j = 0; for(int i = 0; i < P1->size; i++) { for(; j < P1->vertices[i]; j++) P[j] = P2->P[j]*w2; P[j] = P1->P[i]*w1 + P2->P[j]*w2; j++; } for(; j < C->G->nb_vertices; j++) P[j] = P2->P[j]*w2; } else { // two partial vectors int i = 0; int j = 0; int nb_vertices1 = 0; while((i < P1->size) && (j < P2->size)) { if(P1->vertices[i] < P2->vertices[j]) { tmp_vector1[P1->vertices[i]] = P1->P[i]*w1; vertices1[nb_vertices1++] = P1->vertices[i]; i++; continue; } if(P1->vertices[i] > P2->vertices[j]) { tmp_vector1[P2->vertices[j]] = P2->P[j]*w2; vertices1[nb_vertices1++] = P2->vertices[j]; j++; continue; } tmp_vector1[P1->vertices[i]] = P1->P[i]*w1 + P2->P[j]*w2; vertices1[nb_vertices1++] = P1->vertices[i]; i++; j++; } if(i == P1->size) { for(; j < P2->size; j++) { tmp_vector1[P2->vertices[j]] = P2->P[j]*w2; vertices1[nb_vertices1++] = P2->vertices[j]; } } else { for(; i < P1->size; i++) { tmp_vector1[P1->vertices[i]] = P1->P[i]*w1; vertices1[nb_vertices1++] = P1->vertices[i]; } } if(nb_vertices1 > (C->G->nb_vertices/2)) { P = new float[C->G->nb_vertices]; size = C->G->nb_vertices; vertices = 0; for(int i = 0; i < C->G->nb_vertices; i++) P[i] = 0.; for(int i = 0; i < nb_vertices1; i++) P[vertices1[i]] = tmp_vector1[vertices1[i]]; } else { P = new float[nb_vertices1]; size = nb_vertices1; vertices = new int[nb_vertices1]; for(int i = 0; i < nb_vertices1; i++) { vertices[i] = vertices1[i]; P[i] = tmp_vector1[vertices1[i]]; } } } } C->memory_used += memory(); } double Probabilities::compute_distance(const Probabilities* P2) const { double r = 0.; if(vertices) { if(P2->vertices) { // two partial vectors int i = 0; int j = 0; while((i < size) && (j < P2->size)) { if(vertices[i] < P2->vertices[j]) { r += P[i]*P[i]; i++; continue; } if(vertices[i] > P2->vertices[j]) { r += P2->P[j]*P2->P[j]; j++; continue; } r += (P[i] - P2->P[j])*(P[i] - P2->P[j]); i++; j++; } if(i == size) { for(; j < P2->size; j++) r += P2->P[j]*P2->P[j]; } else { for(; i < size; i++) r += P[i]*P[i]; } } else { // P1 partial vector, P2 full vector int i = 0; for(int j = 0; j < size; j++) { for(; i < vertices[j]; i++) r += P2->P[i]*P2->P[i]; r += (P[j] - P2->P[i])*(P[j] - P2->P[i]); i++; } for(; i < P2->size; i++) r += P2->P[i]*P2->P[i]; } } else { if(P2->vertices) { // P1 full vector, P2 partial vector int i = 0; for(int j = 0; j < P2->size; j++) { for(; i < P2->vertices[j]; i++) r += P[i]*P[i]; r += (P[i] - P2->P[j])*(P[i] - P2->P[j]); i++; } for(; i < size; i++) r += P[i]*P[i]; } else { // two full vectors for(int i = 0; i < size; i++) r += (P[i] - P2->P[i])*(P[i] - P2->P[i]); } } return r; } long Probabilities::memory() { if(vertices) return (sizeof(Probabilities) + long(size)*(sizeof(float) + sizeof(int))); else return (sizeof(Probabilities) + long(size)*sizeof(float)); } Community::Community() { P = 0; first_neighbor = 0; last_neighbor = 0; sub_community_of = -1; sub_communities[0] = -1; sub_communities[1] = -1; sigma = 0.; internal_weight = 0.; total_weight = 0.; } Community::~Community() { if(P) delete P; } Communities::Communities(Graph* graph, int random_walks_length, long m, igraph_matrix_t *pmerges, igraph_vector_t *pmodularity) { max_memory = m; memory_used = 0; G = graph; merges=pmerges; mergeidx=0; modularity=pmodularity; Probabilities::C = this; Probabilities::length = random_walks_length; Probabilities::tmp_vector1 = new float[G->nb_vertices]; Probabilities::tmp_vector2 = new float[G->nb_vertices]; Probabilities::id = new int[G->nb_vertices]; for(int i = 0; i < G->nb_vertices; i++) Probabilities::id[i] = 0; Probabilities::vertices1 = new int[G->nb_vertices]; Probabilities::vertices2 = new int[G->nb_vertices]; Probabilities::current_id = 0; members = new int[G->nb_vertices]; for(int i = 0; i < G->nb_vertices; i++) members[i] = -1; H = new Neighbor_heap(G->nb_edges); communities = new Community[2*G->nb_vertices]; // init the n single vertex communities if(max_memory != -1) min_delta_sigma = new Min_delta_sigma_heap(G->nb_vertices*2); else min_delta_sigma = 0; for(int i = 0; i < G->nb_vertices; i++) { communities[i].this_community = i; communities[i].first_member = i; communities[i].last_member = i; communities[i].size = 1; communities[i].sub_community_of = 0; } nb_communities = G->nb_vertices; nb_active_communities = G->nb_vertices; for(int i = 0; i < G->nb_vertices; i++) for(int j = 0; j < G->vertices[i].degree; j++) if (i < G->vertices[i].edges[j].neighbor) { communities[i].total_weight += G->vertices[i].edges[j].weight/2.; communities[G->vertices[i].edges[j].neighbor].total_weight += G->vertices[i].edges[j].weight/2.; Neighbor* N = new Neighbor; N->community1 = i; N->community2 = G->vertices[i].edges[j].neighbor; N->delta_sigma = -1./double(min(G->vertices[i].degree, G->vertices[G->vertices[i].edges[j].neighbor].degree)); N->weight = G->vertices[i].edges[j].weight; N->exact = false; add_neighbor(N); } if(max_memory != -1) { memory_used += min_delta_sigma->memory(); memory_used += 2*long(G->nb_vertices)*sizeof(Community); memory_used += long(G->nb_vertices)*(2*sizeof(float) + 3*sizeof(int)); // the static data of Probabilities class memory_used += H->memory() + long(G->nb_edges)*sizeof(Neighbor); memory_used += G->memory(); } /* int c = 0; */ Neighbor* N = H->get_first(); if (N == 0) return; /* this can happen if there are no edges */ while(!N->exact) { update_neighbor(N, compute_delta_sigma(N->community1, N->community2)); N->exact = true; N = H->get_first(); if(max_memory != -1) manage_memory(); /* TODO: this could use igraph_progress */ /* if(!silent) { */ /* c++; */ /* for(int k = (500*(c-1))/G->nb_edges + 1; k <= (500*c)/G->nb_edges; k++) { */ /* if(k % 50 == 1) {cerr.width(2); cerr << endl << k/ 5 << "% ";} */ /* cerr << "."; */ /* } */ /* } */ } } Communities::~Communities() { delete[] members; delete[] communities; delete H; if(min_delta_sigma) delete min_delta_sigma; delete[] Probabilities::tmp_vector1; delete[] Probabilities::tmp_vector2; delete[] Probabilities::id; delete[] Probabilities::vertices1; delete[] Probabilities::vertices2; } float Community::min_delta_sigma() { float r = 1.; for(Neighbor* N = first_neighbor; N != 0;) { if(N->delta_sigma < r) r = N->delta_sigma; if(N->community1 == this_community) N = N->next_community1; else N = N->next_community2; } return r; } void Community::add_neighbor(Neighbor* N) { // add a new neighbor at the end of the list if (last_neighbor) { if(last_neighbor->community1 == this_community) last_neighbor->next_community1 = N; else last_neighbor->next_community2 = N; if(N->community1 == this_community) N->previous_community1 = last_neighbor; else N->previous_community2 = last_neighbor; } else { first_neighbor = N; if(N->community1 == this_community) N->previous_community1 = 0; else N->previous_community2 = 0; } last_neighbor = N; } void Community::remove_neighbor(Neighbor* N) { // remove a neighbor from the list if (N->community1 == this_community) { if(N->next_community1) { // if (N->next_community1->community1 == this_community) N->next_community1->previous_community1 = N->previous_community1; // else // N->next_community1->previous_community2 = N->previous_community1; } else last_neighbor = N->previous_community1; if(N->previous_community1) { if (N->previous_community1->community1 == this_community) N->previous_community1->next_community1 = N->next_community1; else N->previous_community1->next_community2 = N->next_community1; } else first_neighbor = N->next_community1; } else { if(N->next_community2) { if (N->next_community2->community1 == this_community) N->next_community2->previous_community1 = N->previous_community2; else N->next_community2->previous_community2 = N->previous_community2; } else last_neighbor = N->previous_community2; if(N->previous_community2) { // if (N->previous_community2->community1 == this_community) // N->previous_community2->next_community1 = N->next_community2; // else N->previous_community2->next_community2 = N->next_community2; } else first_neighbor = N->next_community2; } } void Communities::remove_neighbor(Neighbor* N) { communities[N->community1].remove_neighbor(N); communities[N->community2].remove_neighbor(N); H->remove(N); if(max_memory !=-1) { if(N->delta_sigma == min_delta_sigma->delta_sigma[N->community1]) { min_delta_sigma->delta_sigma[N->community1] = communities[N->community1].min_delta_sigma(); if(communities[N->community1].P) min_delta_sigma->update(N->community1); } if(N->delta_sigma == min_delta_sigma->delta_sigma[N->community2]) { min_delta_sigma->delta_sigma[N->community2] = communities[N->community2].min_delta_sigma(); if(communities[N->community2].P) min_delta_sigma->update(N->community2); } } } void Communities::add_neighbor(Neighbor* N) { communities[N->community1].add_neighbor(N); communities[N->community2].add_neighbor(N); H->add(N); if(max_memory !=-1) { if(N->delta_sigma < min_delta_sigma->delta_sigma[N->community1]) { min_delta_sigma->delta_sigma[N->community1] = N->delta_sigma; if(communities[N->community1].P) min_delta_sigma->update(N->community1); } if(N->delta_sigma < min_delta_sigma->delta_sigma[N->community2]) { min_delta_sigma->delta_sigma[N->community2] = N->delta_sigma; if(communities[N->community2].P) min_delta_sigma->update(N->community2); } } } void Communities::update_neighbor(Neighbor* N, float new_delta_sigma) { if(max_memory !=-1) { if(new_delta_sigma < min_delta_sigma->delta_sigma[N->community1]) { min_delta_sigma->delta_sigma[N->community1] = new_delta_sigma; if(communities[N->community1].P) min_delta_sigma->update(N->community1); } if(new_delta_sigma < min_delta_sigma->delta_sigma[N->community2]) { min_delta_sigma->delta_sigma[N->community2] = new_delta_sigma; if(communities[N->community2].P) min_delta_sigma->update(N->community2); } float old_delta_sigma = N->delta_sigma; N->delta_sigma = new_delta_sigma; H->update(N); if(old_delta_sigma == min_delta_sigma->delta_sigma[N->community1]) { min_delta_sigma->delta_sigma[N->community1] = communities[N->community1].min_delta_sigma(); if(communities[N->community1].P) min_delta_sigma->update(N->community1); } if(old_delta_sigma == min_delta_sigma->delta_sigma[N->community2]) { min_delta_sigma->delta_sigma[N->community2] = communities[N->community2].min_delta_sigma(); if(communities[N->community2].P) min_delta_sigma->update(N->community2); } } else { N->delta_sigma = new_delta_sigma; H->update(N); } } void Communities::manage_memory() { while((memory_used > max_memory) && !min_delta_sigma->is_empty()) { int c = min_delta_sigma->get_max_community(); delete communities[c].P; communities[c].P = 0; min_delta_sigma->remove_community(c); } } void Communities::merge_communities(Neighbor* merge_N) { int c1 = merge_N->community1; int c2 = merge_N->community2; communities[nb_communities].first_member = communities[c1].first_member; // merge the communities[nb_communities].last_member = communities[c2].last_member; // two lists members[communities[c1].last_member] = communities[c2].first_member; // of members communities[nb_communities].size = communities[c1].size + communities[c2].size; communities[nb_communities].this_community = nb_communities; communities[nb_communities].sub_community_of = 0; communities[nb_communities].sub_communities[0] = c1; communities[nb_communities].sub_communities[1] = c2; communities[nb_communities].total_weight = communities[c1].total_weight + communities[c2].total_weight; communities[nb_communities].internal_weight = communities[c1].internal_weight + communities[c2].internal_weight + merge_N->weight; communities[nb_communities].sigma = communities[c1].sigma + communities[c2].sigma + merge_N->delta_sigma; communities[c1].sub_community_of = nb_communities; communities[c2].sub_community_of = nb_communities; // update the new probability vector... if(communities[c1].P && communities[c2].P) communities[nb_communities].P = new Probabilities(c1, c2); if(communities[c1].P) { delete communities[c1].P; communities[c1].P = 0; if(max_memory != -1) min_delta_sigma->remove_community(c1); } if(communities[c2].P) { delete communities[c2].P; communities[c2].P = 0; if(max_memory != -1) min_delta_sigma->remove_community(c2); } if(max_memory != -1) { min_delta_sigma->delta_sigma[c1] = -1.; // to avoid to update the min_delta_sigma for these communities min_delta_sigma->delta_sigma[c2] = -1.; // min_delta_sigma->delta_sigma[nb_communities] = -1.; } // update the new neighbors // by enumerating all the neighbors of c1 and c2 Neighbor* N1 = communities[c1].first_neighbor; Neighbor* N2 = communities[c2].first_neighbor; while(N1 && N2) { int neighbor_community1; int neighbor_community2; if (N1->community1 == c1) neighbor_community1 = N1->community2; else neighbor_community1 = N1->community1; if (N2->community1 == c2) neighbor_community2 = N2->community2; else neighbor_community2 = N2->community1; if (neighbor_community1 < neighbor_community2) { Neighbor* tmp = N1; if (N1->community1 == c1) N1 = N1->next_community1; else N1 = N1->next_community2; remove_neighbor(tmp); Neighbor* N = new Neighbor; N->weight = tmp->weight; N->community1 = neighbor_community1; N->community2 = nb_communities; N->delta_sigma = (double(communities[c1].size+communities[neighbor_community1].size)*tmp->delta_sigma + double(communities[c2].size)*merge_N->delta_sigma)/(double(communities[c1].size+communities[c2].size+communities[neighbor_community1].size));//compute_delta_sigma(neighbor_community1, nb_communities); N->exact = false; delete tmp; add_neighbor(N); } if (neighbor_community2 < neighbor_community1) { Neighbor* tmp = N2; if (N2->community1 == c2) N2 = N2->next_community1; else N2 = N2->next_community2; remove_neighbor(tmp); Neighbor* N = new Neighbor; N->weight = tmp->weight; N->community1 = neighbor_community2; N->community2 = nb_communities; N->delta_sigma = (double(communities[c1].size)*merge_N->delta_sigma + double(communities[c2].size+communities[neighbor_community2].size)*tmp->delta_sigma)/(double(communities[c1].size+communities[c2].size+communities[neighbor_community2].size));//compute_delta_sigma(neighbor_community2, nb_communities); N->exact = false; delete tmp; add_neighbor(N); } if (neighbor_community1 == neighbor_community2) { Neighbor* tmp1 = N1; Neighbor* tmp2 = N2; bool exact = N1->exact && N2->exact; if (N1->community1 == c1) N1 = N1->next_community1; else N1 = N1->next_community2; if (N2->community1 == c2) N2 = N2->next_community1; else N2 = N2->next_community2; remove_neighbor(tmp1); remove_neighbor(tmp2); Neighbor* N = new Neighbor; N->weight = tmp1->weight + tmp2->weight; N->community1 = neighbor_community1; N->community2 = nb_communities; N->delta_sigma = (double(communities[c1].size+communities[neighbor_community1].size)*tmp1->delta_sigma + double(communities[c2].size+communities[neighbor_community1].size)*tmp2->delta_sigma - double(communities[neighbor_community1].size)*merge_N->delta_sigma)/(double(communities[c1].size+communities[c2].size+communities[neighbor_community1].size)); N->exact = exact; delete tmp1; delete tmp2; add_neighbor(N); } } if(!N1) { while(N2) { // double delta_sigma2 = N2->delta_sigma; int neighbor_community; if (N2->community1 == c2) neighbor_community = N2->community2; else neighbor_community = N2->community1; Neighbor* tmp = N2; if (N2->community1 == c2) N2 = N2->next_community1; else N2 = N2->next_community2; remove_neighbor(tmp); Neighbor* N = new Neighbor; N->weight = tmp->weight; N->community1 = neighbor_community; N->community2 = nb_communities; N->delta_sigma = (double(communities[c1].size)*merge_N->delta_sigma + double(communities[c2].size+communities[neighbor_community].size)*tmp->delta_sigma)/(double(communities[c1].size+communities[c2].size+communities[neighbor_community].size));//compute_delta_sigma(neighbor_community, nb_communities); N->exact = false; delete tmp; add_neighbor(N); } } if(!N2) { while(N1) { // double delta_sigma1 = N1->delta_sigma; int neighbor_community; if (N1->community1 == c1) neighbor_community = N1->community2; else neighbor_community = N1->community1; Neighbor* tmp = N1; if (N1->community1 == c1) N1 = N1->next_community1; else N1 = N1->next_community2; remove_neighbor(tmp); Neighbor* N = new Neighbor; N->weight = tmp->weight; N->community1 = neighbor_community; N->community2 = nb_communities; N->delta_sigma = (double(communities[c1].size+communities[neighbor_community].size)*tmp->delta_sigma + double(communities[c2].size)*merge_N->delta_sigma)/(double(communities[c1].size+communities[c2].size+communities[neighbor_community].size));//compute_delta_sigma(neighbor_community, nb_communities); N->exact = false; delete tmp; add_neighbor(N); } } if(max_memory != -1) { min_delta_sigma->delta_sigma[nb_communities] = communities[nb_communities].min_delta_sigma(); min_delta_sigma->update(nb_communities); } nb_communities++; nb_active_communities--; } double Communities::merge_nearest_communities() { Neighbor* N = H->get_first(); while(!N->exact) { update_neighbor(N, compute_delta_sigma(N->community1, N->community2)); N->exact = true; N = H->get_first(); if(max_memory != -1) manage_memory(); } double d = N->delta_sigma; remove_neighbor(N); merge_communities(N); if(max_memory != -1) manage_memory(); if (merges) { MATRIX(*merges, mergeidx, 0)=N->community1; MATRIX(*merges, mergeidx, 1)=N->community2; mergeidx++; } if (modularity) { float Q = 0.; for(int i = 0; i < nb_communities; i++) { if(communities[i].sub_community_of == 0) { Q += (communities[i].internal_weight - communities[i].total_weight*communities[i].total_weight/G->total_weight)/G->total_weight; } } VECTOR(*modularity)[mergeidx]=Q; } delete N; /* This could use igraph_progress */ /* if(!silent) { */ /* for(int k = (500*(G->nb_vertices - nb_active_communities - 1))/(G->nb_vertices-1) + 1; k <= (500*(G->nb_vertices - nb_active_communities))/(G->nb_vertices-1); k++) { */ /* if(k % 50 == 1) {cerr.width(2); cerr << endl << k/ 5 << "% ";} */ /* cerr << "."; */ /* } */ /* } */ return d; } double Communities::compute_delta_sigma(int community1, int community2) { if(!communities[community1].P) { communities[community1].P = new Probabilities(community1); if(max_memory != -1) min_delta_sigma->update(community1); } if(!communities[community2].P) { communities[community2].P = new Probabilities(community2); if(max_memory != -1) min_delta_sigma->update(community2); } return communities[community1].P->compute_distance(communities[community2].P)*double(communities[community1].size)*double(communities[community2].size)/double(communities[community1].size + communities[community2].size); } } } /* end of namespaces */ igraph/src/bliss_partition.hh0000644000176000001440000000645512325372072016106 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_PARTITION_HH #define BLISS_PARTITION_HH namespace igraph { class Cell; class Partition; } #include #include "bliss_kstack.hh" #include "bliss_kqueue.hh" #include "bliss_heap.hh" #include "bliss_orbit.hh" #include "bliss_graph.hh" namespace igraph { class Cell { public: /* Index of the first element of the cell in the Partition::elements array */ unsigned int first; unsigned int length; unsigned int max_ival; unsigned int max_ival_count; bool in_neighbour_heap; bool in_splitting_queue; Cell *next; Cell **prev_next_ptr; Cell *next_nonsingleton; Cell *prev_nonsingleton; unsigned int split_level; }; class Partition { public: AbstractGraph *graph; /* Used during equitable partition refinement */ KQueue splitting_queue; void add_in_splitting_queue(Cell * const cell); void clear_splitting_queue(); class RefInfo { public: unsigned int split_cell_first; int prev_nonsingleton_first; int next_nonsingleton_first; }; /* Used for unrefinement */ KStack refinement_stack; Cell *aux_split_in_two(Cell * const cell, const unsigned int first_half_size); /* The current search level */ unsigned int level; void unrefine(unsigned int dest_level, unsigned int dest_split_stack_size); void consistency_check(); public: Cell *cells; Cell *free_cells; Cell *first_cell; Cell *first_nonsingleton_cell; unsigned int *elements; unsigned int *invariant_values; /* element_to_cell_map[e] gives the cell of element e */ Cell **element_to_cell_map; /* in_pos[e] points to the elements array s.t. *in_pos[e] = e */ unsigned int **in_pos; Partition(); ~Partition(); void init(const unsigned int); bool is_discrete() const {return(free_cells == 0); } /* * Splits "cell" into [cell_1,...,cell_n] so that &cell_1 == &cell * according to the invariant_values of elements in the cell * Returns cell_n which is different from "cell" iff * the cell was actually splitted. * max_ival_info_ok indicates that the max_ival and max_ival_count fields * in cell have proper values * Clears the invariant values of elements in cell and cell's max_ival and * max_ival_count fields */ Cell *zplit_cell(Cell * const cell, const bool max_ival_info_ok); private: /* Auxiliary routines for sorting and splitting cells */ void clear_ivs(Cell * const cell); Cell *sort_and_split_cell1(Cell *cell); Cell *sort_and_split_cell255(Cell * const cell, const unsigned int max_ival); bool shellsort_cell(Cell *cell); Cell *split_cell(Cell * const cell); }; } #endif igraph/src/bliss_defs.hh0000644000176000001440000000266512325372072015015 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_DEFS_HH #define BLISS_DEFS_HH #include "config.h" #include /* Define this if you have gmp and want to have exact group sizes. * Remember to include -lgmp in LIB in Makefile. */ #if HAVE_GMP == 1 # define BLISS_USE_GMP #endif #if defined(DEBUG) #define CONSISTENCY_CHECKS #define EXPENSIVE_CONSISTENCY_CHECKS #endif //#define PRINT_SEARCH_TREE_DOT /* Force a check that the found automorphisms are valid */ #if defined(CONSISTENCY_CHECKS) #define VERIFY_AUTOMORPHISMS /* Force a check that the generated partitions are equitable */ #define VERIFY_EQUITABLEDNESS #endif #if defined(CONSISTENCY_CHECKS) #define DEBUG_ASSERT(a) assert(a) #else #define DEBUG_ASSERT(a) ; #endif #endif igraph/src/cs_house.c0000644000176000001440000000334012325527073014325 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* create a Householder reflection [v,beta,s]=house(x), overwrite x with v, * where (I-beta*v*v')*x = s*e1 and e1 = [1 0 ... 0]'. * Note that this CXSparse version is different than CSparse. See Higham, * Accuracy & Stability of Num Algorithms, 2nd ed, 2002, page 357. */ CS_ENTRY cs_house (CS_ENTRY *x, double *beta, CS_INT n) { CS_ENTRY s = 0 ; CS_INT i ; if (!x || !beta) return (-1) ; /* check inputs */ /* s = norm(x) */ for (i = 0 ; i < n ; i++) s += x [i] * CS_CONJ (x [i]) ; s = sqrt (s) ; if (s == 0) { (*beta) = 0 ; x [0] = 1 ; } else { /* s = sign(x[0]) * norm (x) ; */ if (x [0] != 0) { s *= x [0] / CS_ABS (x [0]) ; } x [0] += s ; (*beta) = 1. / CS_REAL (CS_CONJ (s) * x [0]) ; } return (-s) ; } igraph/src/colamd.c0000644000176000001440000037022612325527072013765 0ustar ripleyusers/* ========================================================================== */ /* === colamd/symamd - a sparse matrix column ordering algorithm ============ */ /* ========================================================================== */ /* COLAMD / SYMAMD colamd: an approximate minimum degree column ordering algorithm, for LU factorization of symmetric or unsymmetric matrices, QR factorization, least squares, interior point methods for linear programming problems, and other related problems. symamd: an approximate minimum degree ordering algorithm for Cholesky factorization of symmetric matrices. Purpose: Colamd computes a permutation Q such that the Cholesky factorization of (AQ)'(AQ) has less fill-in and requires fewer floating point operations than A'A. This also provides a good ordering for sparse partial pivoting methods, P(AQ) = LU, where Q is computed prior to numerical factorization, and P is computed during numerical factorization via conventional partial pivoting with row interchanges. Colamd is the column ordering method used in SuperLU, part of the ScaLAPACK library. It is also available as built-in function in MATLAB Version 6, available from MathWorks, Inc. (http://www.mathworks.com). This routine can be used in place of colmmd in MATLAB. Symamd computes a permutation P of a symmetric matrix A such that the Cholesky factorization of PAP' has less fill-in and requires fewer floating point operations than A. Symamd constructs a matrix M such that M'M has the same nonzero pattern of A, and then orders the columns of M using colmmd. The column ordering of M is then returned as the row and column ordering P of A. Authors: The authors of the code itself are Stefan I. Larimore and Timothy A. Davis (davis at cise.ufl.edu), University of Florida. The algorithm was developed in collaboration with John Gilbert, Xerox PARC, and Esmond Ng, Oak Ridge National Laboratory. Acknowledgements: This work was supported by the National Science Foundation, under grants DMS-9504974 and DMS-9803599. Copyright and License: Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. COLAMD is also available under alternate licenses, contact T. Davis for details. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Permission is hereby granted to use or copy this program under the terms of the GNU LGPL, provided that the Copyright, this License, and the Availability of the original version is retained on all copies. User documentation of any code that uses this code or any modified version of this code must cite the Copyright, this License, the Availability note, and "Used by permission." Permission to modify the code and to distribute modified code is granted, provided the Copyright, this License, and the Availability note are retained, and a notice that the code was modified is included. Availability: The colamd/symamd library is available at http://www.cise.ufl.edu/research/sparse/colamd/ This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.c file. It requires the colamd.h file. It is required by the colamdmex.c and symamdmex.c files, for the MATLAB interface to colamd and symamd. Appears as ACM Algorithm 836. See the ChangeLog file for changes since Version 1.0. References: T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, An approximate column minimum degree ordering algorithm, ACM Transactions on Mathematical Software, vol. 30, no. 3., pp. 353-376, 2004. T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, an approximate column minimum degree ordering algorithm, ACM Transactions on Mathematical Software, vol. 30, no. 3., pp. 377-380, 2004. */ /* ========================================================================== */ /* === Description of user-callable routines ================================ */ /* ========================================================================== */ /* COLAMD includes both int and UF_long versions of all its routines. The * description below is for the int version. For UF_long, all int arguments * become UF_long. UF_long is normally defined as long, except for WIN64. ---------------------------------------------------------------------------- colamd_recommended: ---------------------------------------------------------------------------- C syntax: #include "colamd.h" size_t colamd_recommended (int nnz, int n_row, int n_col) ; size_t colamd_l_recommended (UF_long nnz, UF_long n_row, UF_long n_col) ; Purpose: Returns recommended value of Alen for use by colamd. Returns 0 if any input argument is negative. The use of this routine is optional. Not needed for symamd, which dynamically allocates its own memory. Note that in v2.4 and earlier, these routines returned int or long. They now return a value of type size_t. Arguments (all input arguments): int nnz ; Number of nonzeros in the matrix A. This must be the same value as p [n_col] in the call to colamd - otherwise you will get a wrong value of the recommended memory to use. int n_row ; Number of rows in the matrix A. int n_col ; Number of columns in the matrix A. ---------------------------------------------------------------------------- colamd_set_defaults: ---------------------------------------------------------------------------- C syntax: #include "colamd.h" colamd_set_defaults (double knobs [COLAMD_KNOBS]) ; colamd_l_set_defaults (double knobs [COLAMD_KNOBS]) ; Purpose: Sets the default parameters. The use of this routine is optional. Arguments: double knobs [COLAMD_KNOBS] ; Output only. NOTE: the meaning of the dense row/col knobs has changed in v2.4 knobs [0] and knobs [1] control dense row and col detection: Colamd: rows with more than max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n_col)) entries are removed prior to ordering. Columns with more than max (16, knobs [COLAMD_DENSE_COL] * sqrt (MIN (n_row,n_col))) entries are removed prior to ordering, and placed last in the output column ordering. Symamd: uses only knobs [COLAMD_DENSE_ROW], which is knobs [0]. Rows and columns with more than max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n)) entries are removed prior to ordering, and placed last in the output ordering. COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1, respectively, in colamd.h. Default values of these two knobs are both 10. Currently, only knobs [0] and knobs [1] are used, but future versions may use more knobs. If so, they will be properly set to their defaults by the future version of colamd_set_defaults, so that the code that calls colamd will not need to change, assuming that you either use colamd_set_defaults, or pass a (double *) NULL pointer as the knobs array to colamd or symamd. knobs [2]: aggressive absorption knobs [COLAMD_AGGRESSIVE] controls whether or not to do aggressive absorption during the ordering. Default is TRUE. ---------------------------------------------------------------------------- colamd: ---------------------------------------------------------------------------- C syntax: #include "colamd.h" int colamd (int n_row, int n_col, int Alen, int *A, int *p, double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS]) ; UF_long colamd_l (UF_long n_row, UF_long n_col, UF_long Alen, UF_long *A, UF_long *p, double knobs [COLAMD_KNOBS], UF_long stats [COLAMD_STATS]) ; Purpose: Computes a column ordering (Q) of A such that P(AQ)=LU or (AQ)'AQ=LL' have less fill-in and require fewer floating point operations than factorizing the unpermuted matrix A or A'A, respectively. Returns: TRUE (1) if successful, FALSE (0) otherwise. Arguments: int n_row ; Input argument. Number of rows in the matrix A. Restriction: n_row >= 0. Colamd returns FALSE if n_row is negative. int n_col ; Input argument. Number of columns in the matrix A. Restriction: n_col >= 0. Colamd returns FALSE if n_col is negative. int Alen ; Input argument. Restriction (see note): Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col Colamd returns FALSE if these conditions are not met. Note: this restriction makes an modest assumption regarding the size of the two typedef's structures in colamd.h. We do, however, guarantee that Alen >= colamd_recommended (nnz, n_row, n_col) will be sufficient. Note: the macro version does not check for integer overflow, and thus is not recommended. Use the colamd_recommended routine instead. int A [Alen] ; Input argument, undefined on output. A is an integer array of size Alen. Alen must be at least as large as the bare minimum value given above, but this is very low, and can result in excessive run time. For best performance, we recommend that Alen be greater than or equal to colamd_recommended (nnz, n_row, n_col), which adds nnz/5 to the bare minimum value given above. On input, the row indices of the entries in column c of the matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices in a given column c need not be in ascending order, and duplicate row indices may be be present. However, colamd will work a little faster if both of these conditions are met (Colamd puts the matrix into this format, if it finds that the the conditions are not met). The matrix is 0-based. That is, rows are in the range 0 to n_row-1, and columns are in the range 0 to n_col-1. Colamd returns FALSE if any row index is out of range. The contents of A are modified during ordering, and are undefined on output. int p [n_col+1] ; Both input and output argument. p is an integer array of size n_col+1. On input, it holds the "pointers" for the column form of the matrix A. Column c of the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first entry, p [0], must be zero, and p [c] <= p [c+1] must hold for all c in the range 0 to n_col-1. The value p [n_col] is thus the total number of entries in the pattern of the matrix A. Colamd returns FALSE if these conditions are not met. On output, if colamd returns TRUE, the array p holds the column permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is the first column index in the new ordering, and p [n_col-1] is the last. That is, p [k] = j means that column j of A is the kth pivot column, in AQ, where k is in the range 0 to n_col-1 (p [0] = j means that column j of A is the first column in AQ). If colamd returns FALSE, then no permutation is returned, and p is undefined on output. double knobs [COLAMD_KNOBS] ; Input argument. See colamd_set_defaults for a description. int stats [COLAMD_STATS] ; Output argument. Statistics on the ordering, and error status. See colamd.h for related definitions. Colamd returns FALSE if stats is not present. stats [0]: number of dense or empty rows ignored. stats [1]: number of dense or empty columns ignored (and ordered last in the output permutation p) Note that a row can become "empty" if it contains only "dense" and/or "empty" columns, and similarly a column can become "empty" if it only contains "dense" and/or "empty" rows. stats [2]: number of garbage collections performed. This can be excessively high if Alen is close to the minimum required value. stats [3]: status code. < 0 is an error code. > 1 is a warning or notice. 0 OK. Each column of the input matrix contained row indices in increasing order, with no duplicates. 1 OK, but columns of input matrix were jumbled (unsorted columns or duplicate entries). Colamd had to do some extra work to sort the matrix first and remove duplicate entries, but it still was able to return a valid permutation (return value of colamd was TRUE). stats [4]: highest numbered column that is unsorted or has duplicate entries. stats [5]: last seen duplicate or unsorted row index. stats [6]: number of duplicate or unsorted row indices. -1 A is a null pointer -2 p is a null pointer -3 n_row is negative stats [4]: n_row -4 n_col is negative stats [4]: n_col -5 number of nonzeros in matrix is negative stats [4]: number of nonzeros, p [n_col] -6 p [0] is nonzero stats [4]: p [0] -7 A is too small stats [4]: required size stats [5]: actual size (Alen) -8 a column has a negative number of entries stats [4]: column with < 0 entries stats [5]: number of entries in col -9 a row index is out of bounds stats [4]: column with bad row index stats [5]: bad row index stats [6]: n_row, # of rows of matrx -10 (unused; see symamd.c) -999 (unused; see symamd.c) Future versions may return more statistics in the stats array. Example: See http://www.cise.ufl.edu/research/sparse/colamd/example.c for a complete example. To order the columns of a 5-by-4 matrix with 11 nonzero entries in the following nonzero pattern x 0 x 0 x 0 x x 0 x x 0 0 0 x x x x 0 0 with default knobs and no output statistics, do the following: #include "colamd.h" #define ALEN 100 int A [ALEN] = {0, 1, 4, 2, 4, 0, 1, 2, 3, 1, 3} ; int p [ ] = {0, 3, 5, 9, 11} ; int stats [COLAMD_STATS] ; colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; The permutation is returned in the array p, and A is destroyed. ---------------------------------------------------------------------------- symamd: ---------------------------------------------------------------------------- C syntax: #include "colamd.h" int symamd (int n, int *A, int *p, int *perm, double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS], void (*allocate) (size_t, size_t), void (*release) (void *)) ; UF_long symamd_l (UF_long n, UF_long *A, UF_long *p, UF_long *perm, double knobs [COLAMD_KNOBS], UF_long stats [COLAMD_STATS], void (*allocate) (size_t, size_t), void (*release) (void *)) ; Purpose: The symamd routine computes an ordering P of a symmetric sparse matrix A such that the Cholesky factorization PAP' = LL' remains sparse. It is based on a column ordering of a matrix M constructed so that the nonzero pattern of M'M is the same as A. The matrix A is assumed to be symmetric; only the strictly lower triangular part is accessed. You must pass your selected memory allocator (usually calloc/free or mxCalloc/mxFree) to symamd, for it to allocate memory for the temporary matrix M. Returns: TRUE (1) if successful, FALSE (0) otherwise. Arguments: int n ; Input argument. Number of rows and columns in the symmetrix matrix A. Restriction: n >= 0. Symamd returns FALSE if n is negative. int A [nnz] ; Input argument. A is an integer array of size nnz, where nnz = p [n]. The row indices of the entries in column c of the matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices in a given column c need not be in ascending order, and duplicate row indices may be present. However, symamd will run faster if the columns are in sorted order with no duplicate entries. The matrix is 0-based. That is, rows are in the range 0 to n-1, and columns are in the range 0 to n-1. Symamd returns FALSE if any row index is out of range. The contents of A are not modified. int p [n+1] ; Input argument. p is an integer array of size n+1. On input, it holds the "pointers" for the column form of the matrix A. Column c of the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first entry, p [0], must be zero, and p [c] <= p [c+1] must hold for all c in the range 0 to n-1. The value p [n] is thus the total number of entries in the pattern of the matrix A. Symamd returns FALSE if these conditions are not met. The contents of p are not modified. int perm [n+1] ; Output argument. On output, if symamd returns TRUE, the array perm holds the permutation P, where perm [0] is the first index in the new ordering, and perm [n-1] is the last. That is, perm [k] = j means that row and column j of A is the kth column in PAP', where k is in the range 0 to n-1 (perm [0] = j means that row and column j of A are the first row and column in PAP'). The array is used as a workspace during the ordering, which is why it must be of length n+1, not just n. double knobs [COLAMD_KNOBS] ; Input argument. See colamd_set_defaults for a description. int stats [COLAMD_STATS] ; Output argument. Statistics on the ordering, and error status. See colamd.h for related definitions. Symamd returns FALSE if stats is not present. stats [0]: number of dense or empty row and columns ignored (and ordered last in the output permutation perm). Note that a row/column can become "empty" if it contains only "dense" and/or "empty" columns/rows. stats [1]: (same as stats [0]) stats [2]: number of garbage collections performed. stats [3]: status code. < 0 is an error code. > 1 is a warning or notice. 0 OK. Each column of the input matrix contained row indices in increasing order, with no duplicates. 1 OK, but columns of input matrix were jumbled (unsorted columns or duplicate entries). Symamd had to do some extra work to sort the matrix first and remove duplicate entries, but it still was able to return a valid permutation (return value of symamd was TRUE). stats [4]: highest numbered column that is unsorted or has duplicate entries. stats [5]: last seen duplicate or unsorted row index. stats [6]: number of duplicate or unsorted row indices. -1 A is a null pointer -2 p is a null pointer -3 (unused, see colamd.c) -4 n is negative stats [4]: n -5 number of nonzeros in matrix is negative stats [4]: # of nonzeros (p [n]). -6 p [0] is nonzero stats [4]: p [0] -7 (unused) -8 a column has a negative number of entries stats [4]: column with < 0 entries stats [5]: number of entries in col -9 a row index is out of bounds stats [4]: column with bad row index stats [5]: bad row index stats [6]: n_row, # of rows of matrx -10 out of memory (unable to allocate temporary workspace for M or count arrays using the "allocate" routine passed into symamd). Future versions may return more statistics in the stats array. void * (*allocate) (size_t, size_t) A pointer to a function providing memory allocation. The allocated memory must be returned initialized to zero. For a C application, this argument should normally be a pointer to calloc. For a MATLAB mexFunction, the routine mxCalloc is passed instead. void (*release) (size_t, size_t) A pointer to a function that frees memory allocated by the memory allocation routine above. For a C application, this argument should normally be a pointer to free. For a MATLAB mexFunction, the routine mxFree is passed instead. ---------------------------------------------------------------------------- colamd_report: ---------------------------------------------------------------------------- C syntax: #include "colamd.h" colamd_report (int stats [COLAMD_STATS]) ; colamd_l_report (UF_long stats [COLAMD_STATS]) ; Purpose: Prints the error status and statistics recorded in the stats array on the standard error output (for a standard C routine) or on the MATLAB output (for a mexFunction). Arguments: int stats [COLAMD_STATS] ; Input only. Statistics from colamd. ---------------------------------------------------------------------------- symamd_report: ---------------------------------------------------------------------------- C syntax: #include "colamd.h" symamd_report (int stats [COLAMD_STATS]) ; symamd_l_report (UF_long stats [COLAMD_STATS]) ; Purpose: Prints the error status and statistics recorded in the stats array on the standard error output (for a standard C routine) or on the MATLAB output (for a mexFunction). Arguments: int stats [COLAMD_STATS] ; Input only. Statistics from symamd. */ /* ========================================================================== */ /* === Scaffolding code definitions ======================================== */ /* ========================================================================== */ /* Ensure that debugging is turned off: */ #ifndef NDEBUG #define NDEBUG #endif /* turn on debugging by uncommenting the following line #undef NDEBUG */ /* Our "scaffolding code" philosophy: In our opinion, well-written library code should keep its "debugging" code, and just normally have it turned off by the compiler so as not to interfere with performance. This serves several purposes: (1) assertions act as comments to the reader, telling you what the code expects at that point. All assertions will always be true (unless there really is a bug, of course). (2) leaving in the scaffolding code assists anyone who would like to modify the code, or understand the algorithm (by reading the debugging output, one can get a glimpse into what the code is doing). (3) (gasp!) for actually finding bugs. This code has been heavily tested and "should" be fully functional and bug-free ... but you never know... The code will become outrageously slow when debugging is enabled. To control the level of debugging output, set an environment variable D to 0 (little), 1 (some), 2, 3, or 4 (lots). When debugging, you should see the following message on the standard output: colamd: debug version, D = 1 (THIS WILL BE SLOW!) or a similar message for symamd. If you don't, then debugging has not been enabled. */ /* ========================================================================== */ /* === Include files ======================================================== */ /* ========================================================================== */ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #include "colamd.h" #if 0 /* by mao */ #include #include #ifdef MATLAB_MEX_FILE #include "mex.h" #include "matrix.h" #endif /* MATLAB_MEX_FILE */ #if !defined (NPRINT) || !defined (NDEBUG) #include #endif #ifndef NULL #define NULL ((void *) 0) #endif #endif /* ========================================================================== */ /* === int or UF_long ======================================================= */ /* ========================================================================== */ #if 0 /* by mao */ /* define UF_long */ #include "UFconfig.h" #endif #ifdef DLONG #define Int UF_long #define ID UF_long_id #define Int_MAX UF_long_max #define COLAMD_recommended colamd_l_recommended #define COLAMD_set_defaults colamd_l_set_defaults #define COLAMD_MAIN colamd_l #define SYMAMD_MAIN symamd_l #define COLAMD_report colamd_l_report #define SYMAMD_report symamd_l_report #else #define Int int #define ID "%d" #define Int_MAX INT_MAX #define COLAMD_recommended colamd_recommended #define COLAMD_set_defaults colamd_set_defaults #define COLAMD_MAIN colamd #define SYMAMD_MAIN symamd #define COLAMD_report colamd_report #define SYMAMD_report symamd_report #endif /* ========================================================================== */ /* === Row and Column structures ============================================ */ /* ========================================================================== */ /* User code that makes use of the colamd/symamd routines need not directly */ /* reference these structures. They are used only for colamd_recommended. */ typedef struct Colamd_Col_struct { Int start ; /* index for A of first row in this column, or DEAD */ /* if column is dead */ Int length ; /* number of rows in this column */ union { Int thickness ; /* number of original columns represented by this */ /* col, if the column is alive */ Int parent ; /* parent in parent tree super-column structure, if */ /* the column is dead */ } shared1 ; union { Int score ; /* the score used to maintain heap, if col is alive */ Int order ; /* pivot ordering of this column, if col is dead */ } shared2 ; union { Int headhash ; /* head of a hash bucket, if col is at the head of */ /* a degree list */ Int hash ; /* hash value, if col is not in a degree list */ Int prev ; /* previous column in degree list, if col is in a */ /* degree list (but not at the head of a degree list) */ } shared3 ; union { Int degree_next ; /* next column, if col is in a degree list */ Int hash_next ; /* next column, if col is in a hash list */ } shared4 ; } Colamd_Col ; typedef struct Colamd_Row_struct { Int start ; /* index for A of first col in this row */ Int length ; /* number of principal columns in this row */ union { Int degree ; /* number of principal & non-principal columns in row */ Int p ; /* used as a row pointer in init_rows_cols () */ } shared1 ; union { Int mark ; /* for computing set differences and marking dead rows*/ Int first_column ;/* first column in row (used in garbage collection) */ } shared2 ; } Colamd_Row ; /* ========================================================================== */ /* === Definitions ========================================================== */ /* ========================================================================== */ /* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */ #define PUBLIC #define PRIVATE static #define DENSE_DEGREE(alpha,n) \ ((Int) MAX (16.0, (alpha) * sqrt ((double) (n)))) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define ONES_COMPLEMENT(r) (-(r)-1) /* -------------------------------------------------------------------------- */ /* Change for version 2.1: define TRUE and FALSE only if not yet defined */ /* -------------------------------------------------------------------------- */ #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif /* -------------------------------------------------------------------------- */ #define EMPTY (-1) /* Row and column status */ #define ALIVE (0) #define DEAD (-1) /* Column status */ #define DEAD_PRINCIPAL (-1) #define DEAD_NON_PRINCIPAL (-2) /* Macros for row and column status update and checking. */ #define ROW_IS_DEAD(r) ROW_IS_MARKED_DEAD (Row[r].shared2.mark) #define ROW_IS_MARKED_DEAD(row_mark) (row_mark < ALIVE) #define ROW_IS_ALIVE(r) (Row [r].shared2.mark >= ALIVE) #define COL_IS_DEAD(c) (Col [c].start < ALIVE) #define COL_IS_ALIVE(c) (Col [c].start >= ALIVE) #define COL_IS_DEAD_PRINCIPAL(c) (Col [c].start == DEAD_PRINCIPAL) #define KILL_ROW(r) { Row [r].shared2.mark = DEAD ; } #define KILL_PRINCIPAL_COL(c) { Col [c].start = DEAD_PRINCIPAL ; } #define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; } /* ========================================================================== */ /* === Colamd reporting mechanism =========================================== */ /* ========================================================================== */ #if defined (MATLAB_MEX_FILE) || defined (MATHWORKS) /* In MATLAB, matrices are 1-based to the user, but 0-based internally */ #define INDEX(i) ((i)+1) #else /* In C, matrices are 0-based and indices are reported as such in *_report */ #define INDEX(i) (i) #endif /* All output goes through the PRINTF macro. */ #define PRINTF(params) { if (colamd_printf != NULL) (void) colamd_printf params ; } /* ========================================================================== */ /* === Prototypes of PRIVATE routines ======================================= */ /* ========================================================================== */ PRIVATE Int init_rows_cols ( Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int A [], Int p [], Int stats [COLAMD_STATS] ) ; PRIVATE void init_scoring ( Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int A [], Int head [], double knobs [COLAMD_KNOBS], Int *p_n_row2, Int *p_n_col2, Int *p_max_deg ) ; PRIVATE Int find_ordering ( Int n_row, Int n_col, Int Alen, Colamd_Row Row [], Colamd_Col Col [], Int A [], Int head [], Int n_col2, Int max_deg, Int pfree, Int aggressive ) ; PRIVATE void order_children ( Int n_col, Colamd_Col Col [], Int p [] ) ; PRIVATE void detect_super_cols ( #ifndef NDEBUG Int n_col, Colamd_Row Row [], #endif /* NDEBUG */ Colamd_Col Col [], Int A [], Int head [], Int row_start, Int row_length ) ; PRIVATE Int garbage_collection ( Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int A [], Int *pfree ) ; PRIVATE Int clear_mark ( Int tag_mark, Int max_mark, Int n_row, Colamd_Row Row [] ) ; PRIVATE void print_report ( char *method, Int stats [COLAMD_STATS] ) ; /* ========================================================================== */ /* === Debugging prototypes and definitions ================================= */ /* ========================================================================== */ #ifndef NDEBUG #if 0 /* by mao */ #include #endif /* colamd_debug is the *ONLY* global variable, and is only */ /* present when debugging */ PRIVATE Int colamd_debug = 0 ; /* debug print level */ #define DEBUG0(params) { PRINTF (params) ; } #define DEBUG1(params) { if (colamd_debug >= 1) PRINTF (params) ; } #define DEBUG2(params) { if (colamd_debug >= 2) PRINTF (params) ; } #define DEBUG3(params) { if (colamd_debug >= 3) PRINTF (params) ; } #define DEBUG4(params) { if (colamd_debug >= 4) PRINTF (params) ; } #if 0 /* by mao */ #ifdef MATLAB_MEX_FILE #define ASSERT(expression) (mxAssert ((expression), "")) #else #define ASSERT(expression) (assert (expression)) #endif /* MATLAB_MEX_FILE */ #else #define ASSERT xassert #endif PRIVATE void colamd_get_debug /* gets the debug print level from getenv */ ( char *method ) ; PRIVATE void debug_deg_lists ( Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int head [], Int min_score, Int should, Int max_deg ) ; PRIVATE void debug_mark ( Int n_row, Colamd_Row Row [], Int tag_mark, Int max_mark ) ; PRIVATE void debug_matrix ( Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int A [] ) ; PRIVATE void debug_structures ( Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int A [], Int n_col2 ) ; #else /* NDEBUG */ /* === No debugging ========================================================= */ #define DEBUG0(params) ; #define DEBUG1(params) ; #define DEBUG2(params) ; #define DEBUG3(params) ; #define DEBUG4(params) ; #define ASSERT(expression) #endif /* NDEBUG */ /* ========================================================================== */ /* === USER-CALLABLE ROUTINES: ============================================== */ /* ========================================================================== */ /* ========================================================================== */ /* === colamd_recommended =================================================== */ /* ========================================================================== */ /* The colamd_recommended routine returns the suggested size for Alen. This value has been determined to provide good balance between the number of garbage collections and the memory requirements for colamd. If any argument is negative, or if integer overflow occurs, a 0 is returned as an error condition. 2*nnz space is required for the row and column indices of the matrix. COLAMD_C (n_col) + COLAMD_R (n_row) space is required for the Col and Row arrays, respectively, which are internal to colamd (roughly 6*n_col + 4*n_row). An additional n_col space is the minimal amount of "elbow room", and nnz/5 more space is recommended for run time efficiency. Alen is approximately 2.2*nnz + 7*n_col + 4*n_row + 10. This function is not needed when using symamd. */ /* add two values of type size_t, and check for integer overflow */ static size_t t_add (size_t a, size_t b, int *ok) { (*ok) = (*ok) && ((a + b) >= MAX (a,b)) ; return ((*ok) ? (a + b) : 0) ; } /* compute a*k where k is a small integer, and check for integer overflow */ static size_t t_mult (size_t a, size_t k, int *ok) { size_t i, s = 0 ; for (i = 0 ; i < k ; i++) { s = t_add (s, a, ok) ; } return (s) ; } /* size of the Col and Row structures */ #define COLAMD_C(n_col,ok) \ ((t_mult (t_add (n_col, 1, ok), sizeof (Colamd_Col), ok) / sizeof (Int))) #define COLAMD_R(n_row,ok) \ ((t_mult (t_add (n_row, 1, ok), sizeof (Colamd_Row), ok) / sizeof (Int))) PUBLIC size_t COLAMD_recommended /* returns recommended value of Alen. */ ( /* === Parameters ======================================================= */ Int nnz, /* number of nonzeros in A */ Int n_row, /* number of rows in A */ Int n_col /* number of columns in A */ ) { size_t s, c, r ; int ok = TRUE ; if (nnz < 0 || n_row < 0 || n_col < 0) { return (0) ; } s = t_mult (nnz, 2, &ok) ; /* 2*nnz */ c = COLAMD_C (n_col, &ok) ; /* size of column structures */ r = COLAMD_R (n_row, &ok) ; /* size of row structures */ s = t_add (s, c, &ok) ; s = t_add (s, r, &ok) ; s = t_add (s, n_col, &ok) ; /* elbow room */ s = t_add (s, nnz/5, &ok) ; /* elbow room */ ok = ok && (s < Int_MAX) ; return (ok ? s : 0) ; } /* ========================================================================== */ /* === colamd_set_defaults ================================================== */ /* ========================================================================== */ /* The colamd_set_defaults routine sets the default values of the user- controllable parameters for colamd and symamd: Colamd: rows with more than max (16, knobs [0] * sqrt (n_col)) entries are removed prior to ordering. Columns with more than max (16, knobs [1] * sqrt (MIN (n_row,n_col))) entries are removed prior to ordering, and placed last in the output column ordering. Symamd: Rows and columns with more than max (16, knobs [0] * sqrt (n)) entries are removed prior to ordering, and placed last in the output ordering. knobs [0] dense row control knobs [1] dense column control knobs [2] if nonzero, do aggresive absorption knobs [3..19] unused, but future versions might use this */ PUBLIC void COLAMD_set_defaults ( /* === Parameters ======================================================= */ double knobs [COLAMD_KNOBS] /* knob array */ ) { /* === Local variables ================================================== */ Int i ; if (!knobs) { return ; /* no knobs to initialize */ } for (i = 0 ; i < COLAMD_KNOBS ; i++) { knobs [i] = 0 ; } knobs [COLAMD_DENSE_ROW] = 10 ; knobs [COLAMD_DENSE_COL] = 10 ; knobs [COLAMD_AGGRESSIVE] = TRUE ; /* default: do aggressive absorption*/ } /* ========================================================================== */ /* === symamd =============================================================== */ /* ========================================================================== */ PUBLIC Int SYMAMD_MAIN /* return TRUE if OK, FALSE otherwise */ ( /* === Parameters ======================================================= */ Int n, /* number of rows and columns of A */ Int A [], /* row indices of A */ Int p [], /* column pointers of A */ Int perm [], /* output permutation, size n+1 */ double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ Int stats [COLAMD_STATS], /* output statistics and error codes */ void * (*allocate) (size_t, size_t), /* pointer to calloc (ANSI C) or */ /* mxCalloc (for MATLAB mexFunction) */ void (*release) (void *) /* pointer to free (ANSI C) or */ /* mxFree (for MATLAB mexFunction) */ ) { /* === Local variables ================================================== */ Int *count ; /* length of each column of M, and col pointer*/ Int *mark ; /* mark array for finding duplicate entries */ Int *M ; /* row indices of matrix M */ size_t Mlen ; /* length of M */ Int n_row ; /* number of rows in M */ Int nnz ; /* number of entries in A */ Int i ; /* row index of A */ Int j ; /* column index of A */ Int k ; /* row index of M */ Int mnz ; /* number of nonzeros in M */ Int pp ; /* index into a column of A */ Int last_row ; /* last row seen in the current column */ Int length ; /* number of nonzeros in a column */ double cknobs [COLAMD_KNOBS] ; /* knobs for colamd */ double default_knobs [COLAMD_KNOBS] ; /* default knobs for colamd */ #ifndef NDEBUG colamd_get_debug ("symamd") ; #endif /* NDEBUG */ /* === Check the input arguments ======================================== */ if (!stats) { DEBUG0 (("symamd: stats not present\n")) ; return (FALSE) ; } for (i = 0 ; i < COLAMD_STATS ; i++) { stats [i] = 0 ; } stats [COLAMD_STATUS] = COLAMD_OK ; stats [COLAMD_INFO1] = -1 ; stats [COLAMD_INFO2] = -1 ; if (!A) { stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; DEBUG0 (("symamd: A not present\n")) ; return (FALSE) ; } if (!p) /* p is not present */ { stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; DEBUG0 (("symamd: p not present\n")) ; return (FALSE) ; } if (n < 0) /* n must be >= 0 */ { stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; stats [COLAMD_INFO1] = n ; DEBUG0 (("symamd: n negative %d\n", n)) ; return (FALSE) ; } nnz = p [n] ; if (nnz < 0) /* nnz must be >= 0 */ { stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; stats [COLAMD_INFO1] = nnz ; DEBUG0 (("symamd: number of entries negative %d\n", nnz)) ; return (FALSE) ; } if (p [0] != 0) { stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; stats [COLAMD_INFO1] = p [0] ; DEBUG0 (("symamd: p[0] not zero %d\n", p [0])) ; return (FALSE) ; } /* === If no knobs, set default knobs =================================== */ if (!knobs) { COLAMD_set_defaults (default_knobs) ; knobs = default_knobs ; } /* === Allocate count and mark ========================================== */ count = (Int *) ((*allocate) (n+1, sizeof (Int))) ; if (!count) { stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; DEBUG0 (("symamd: allocate count (size %d) failed\n", n+1)) ; return (FALSE) ; } mark = (Int *) ((*allocate) (n+1, sizeof (Int))) ; if (!mark) { stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; (*release) ((void *) count) ; DEBUG0 (("symamd: allocate mark (size %d) failed\n", n+1)) ; return (FALSE) ; } /* === Compute column counts of M, check if A is valid ================== */ stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ for (i = 0 ; i < n ; i++) { mark [i] = -1 ; } for (j = 0 ; j < n ; j++) { last_row = -1 ; length = p [j+1] - p [j] ; if (length < 0) { /* column pointers must be non-decreasing */ stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; stats [COLAMD_INFO1] = j ; stats [COLAMD_INFO2] = length ; (*release) ((void *) count) ; (*release) ((void *) mark) ; DEBUG0 (("symamd: col %d negative length %d\n", j, length)) ; return (FALSE) ; } for (pp = p [j] ; pp < p [j+1] ; pp++) { i = A [pp] ; if (i < 0 || i >= n) { /* row index i, in column j, is out of bounds */ stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; stats [COLAMD_INFO1] = j ; stats [COLAMD_INFO2] = i ; stats [COLAMD_INFO3] = n ; (*release) ((void *) count) ; (*release) ((void *) mark) ; DEBUG0 (("symamd: row %d col %d out of bounds\n", i, j)) ; return (FALSE) ; } if (i <= last_row || mark [i] == j) { /* row index is unsorted or repeated (or both), thus col */ /* is jumbled. This is a notice, not an error condition. */ stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; stats [COLAMD_INFO1] = j ; stats [COLAMD_INFO2] = i ; (stats [COLAMD_INFO3]) ++ ; DEBUG1 (("symamd: row %d col %d unsorted/duplicate\n", i, j)) ; } if (i > j && mark [i] != j) { /* row k of M will contain column indices i and j */ count [i]++ ; count [j]++ ; } /* mark the row as having been seen in this column */ mark [i] = j ; last_row = i ; } } /* v2.4: removed free(mark) */ /* === Compute column pointers of M ===================================== */ /* use output permutation, perm, for column pointers of M */ perm [0] = 0 ; for (j = 1 ; j <= n ; j++) { perm [j] = perm [j-1] + count [j-1] ; } for (j = 0 ; j < n ; j++) { count [j] = perm [j] ; } /* === Construct M ====================================================== */ mnz = perm [n] ; n_row = mnz / 2 ; Mlen = COLAMD_recommended (mnz, n_row, n) ; M = (Int *) ((*allocate) (Mlen, sizeof (Int))) ; DEBUG0 (("symamd: M is %d-by-%d with %d entries, Mlen = %g\n", n_row, n, mnz, (double) Mlen)) ; if (!M) { stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; (*release) ((void *) count) ; (*release) ((void *) mark) ; DEBUG0 (("symamd: allocate M (size %g) failed\n", (double) Mlen)) ; return (FALSE) ; } k = 0 ; if (stats [COLAMD_STATUS] == COLAMD_OK) { /* Matrix is OK */ for (j = 0 ; j < n ; j++) { ASSERT (p [j+1] - p [j] >= 0) ; for (pp = p [j] ; pp < p [j+1] ; pp++) { i = A [pp] ; ASSERT (i >= 0 && i < n) ; if (i > j) { /* row k of M contains column indices i and j */ M [count [i]++] = k ; M [count [j]++] = k ; k++ ; } } } } else { /* Matrix is jumbled. Do not add duplicates to M. Unsorted cols OK. */ DEBUG0 (("symamd: Duplicates in A.\n")) ; for (i = 0 ; i < n ; i++) { mark [i] = -1 ; } for (j = 0 ; j < n ; j++) { ASSERT (p [j+1] - p [j] >= 0) ; for (pp = p [j] ; pp < p [j+1] ; pp++) { i = A [pp] ; ASSERT (i >= 0 && i < n) ; if (i > j && mark [i] != j) { /* row k of M contains column indices i and j */ M [count [i]++] = k ; M [count [j]++] = k ; k++ ; mark [i] = j ; } } } /* v2.4: free(mark) moved below */ } /* count and mark no longer needed */ (*release) ((void *) count) ; (*release) ((void *) mark) ; /* v2.4: free (mark) moved here */ ASSERT (k == n_row) ; /* === Adjust the knobs for M =========================================== */ for (i = 0 ; i < COLAMD_KNOBS ; i++) { cknobs [i] = knobs [i] ; } /* there are no dense rows in M */ cknobs [COLAMD_DENSE_ROW] = -1 ; cknobs [COLAMD_DENSE_COL] = knobs [COLAMD_DENSE_ROW] ; /* === Order the columns of M =========================================== */ /* v2.4: colamd cannot fail here, so the error check is removed */ (void) COLAMD_MAIN (n_row, n, (Int) Mlen, M, perm, cknobs, stats) ; /* Note that the output permutation is now in perm */ /* === get the statistics for symamd from colamd ======================== */ /* a dense column in colamd means a dense row and col in symamd */ stats [COLAMD_DENSE_ROW] = stats [COLAMD_DENSE_COL] ; /* === Free M =========================================================== */ (*release) ((void *) M) ; DEBUG0 (("symamd: done.\n")) ; return (TRUE) ; } /* ========================================================================== */ /* === colamd =============================================================== */ /* ========================================================================== */ /* The colamd routine computes a column ordering Q of a sparse matrix A such that the LU factorization P(AQ) = LU remains sparse, where P is selected via partial pivoting. The routine can also be viewed as providing a permutation Q such that the Cholesky factorization (AQ)'(AQ) = LL' remains sparse. */ PUBLIC Int COLAMD_MAIN /* returns TRUE if successful, FALSE otherwise*/ ( /* === Parameters ======================================================= */ Int n_row, /* number of rows in A */ Int n_col, /* number of columns in A */ Int Alen, /* length of A */ Int A [], /* row indices of A */ Int p [], /* pointers to columns in A */ double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */ Int stats [COLAMD_STATS] /* output statistics and error codes */ ) { /* === Local variables ================================================== */ Int i ; /* loop index */ Int nnz ; /* nonzeros in A */ size_t Row_size ; /* size of Row [], in integers */ size_t Col_size ; /* size of Col [], in integers */ size_t need ; /* minimum required length of A */ Colamd_Row *Row ; /* pointer into A of Row [0..n_row] array */ Colamd_Col *Col ; /* pointer into A of Col [0..n_col] array */ Int n_col2 ; /* number of non-dense, non-empty columns */ Int n_row2 ; /* number of non-dense, non-empty rows */ Int ngarbage ; /* number of garbage collections performed */ Int max_deg ; /* maximum row degree */ double default_knobs [COLAMD_KNOBS] ; /* default knobs array */ Int aggressive ; /* do aggressive absorption */ int ok ; #ifndef NDEBUG colamd_get_debug ("colamd") ; #endif /* NDEBUG */ /* === Check the input arguments ======================================== */ if (!stats) { DEBUG0 (("colamd: stats not present\n")) ; return (FALSE) ; } for (i = 0 ; i < COLAMD_STATS ; i++) { stats [i] = 0 ; } stats [COLAMD_STATUS] = COLAMD_OK ; stats [COLAMD_INFO1] = -1 ; stats [COLAMD_INFO2] = -1 ; if (!A) /* A is not present */ { stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; DEBUG0 (("colamd: A not present\n")) ; return (FALSE) ; } if (!p) /* p is not present */ { stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; DEBUG0 (("colamd: p not present\n")) ; return (FALSE) ; } if (n_row < 0) /* n_row must be >= 0 */ { stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ; stats [COLAMD_INFO1] = n_row ; DEBUG0 (("colamd: nrow negative %d\n", n_row)) ; return (FALSE) ; } if (n_col < 0) /* n_col must be >= 0 */ { stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; stats [COLAMD_INFO1] = n_col ; DEBUG0 (("colamd: ncol negative %d\n", n_col)) ; return (FALSE) ; } nnz = p [n_col] ; if (nnz < 0) /* nnz must be >= 0 */ { stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; stats [COLAMD_INFO1] = nnz ; DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ; return (FALSE) ; } if (p [0] != 0) { stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; stats [COLAMD_INFO1] = p [0] ; DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ; return (FALSE) ; } /* === If no knobs, set default knobs =================================== */ if (!knobs) { COLAMD_set_defaults (default_knobs) ; knobs = default_knobs ; } aggressive = (knobs [COLAMD_AGGRESSIVE] != FALSE) ; /* === Allocate the Row and Col arrays from array A ===================== */ ok = TRUE ; Col_size = COLAMD_C (n_col, &ok) ; /* size of Col array of structs */ Row_size = COLAMD_R (n_row, &ok) ; /* size of Row array of structs */ /* need = 2*nnz + n_col + Col_size + Row_size ; */ need = t_mult (nnz, 2, &ok) ; need = t_add (need, n_col, &ok) ; need = t_add (need, Col_size, &ok) ; need = t_add (need, Row_size, &ok) ; if (!ok || need > (size_t) Alen || need > Int_MAX) { /* not enough space in array A to perform the ordering */ stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; stats [COLAMD_INFO1] = need ; stats [COLAMD_INFO2] = Alen ; DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen)); return (FALSE) ; } Alen -= Col_size + Row_size ; Col = (Colamd_Col *) &A [Alen] ; Row = (Colamd_Row *) &A [Alen + Col_size] ; /* === Construct the row and column data structures ===================== */ if (!init_rows_cols (n_row, n_col, Row, Col, A, p, stats)) { /* input matrix is invalid */ DEBUG0 (("colamd: Matrix invalid\n")) ; return (FALSE) ; } /* === Initialize scores, kill dense rows/columns ======================= */ init_scoring (n_row, n_col, Row, Col, A, p, knobs, &n_row2, &n_col2, &max_deg) ; /* === Order the supercolumns =========================================== */ ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, n_col2, max_deg, 2*nnz, aggressive) ; /* === Order the non-principal columns ================================== */ order_children (n_col, Col, p) ; /* === Return statistics in stats ======================================= */ stats [COLAMD_DENSE_ROW] = n_row - n_row2 ; stats [COLAMD_DENSE_COL] = n_col - n_col2 ; stats [COLAMD_DEFRAG_COUNT] = ngarbage ; DEBUG0 (("colamd: done.\n")) ; return (TRUE) ; } /* ========================================================================== */ /* === colamd_report ======================================================== */ /* ========================================================================== */ PUBLIC void COLAMD_report ( Int stats [COLAMD_STATS] ) { print_report ("colamd", stats) ; } /* ========================================================================== */ /* === symamd_report ======================================================== */ /* ========================================================================== */ PUBLIC void SYMAMD_report ( Int stats [COLAMD_STATS] ) { print_report ("symamd", stats) ; } /* ========================================================================== */ /* === NON-USER-CALLABLE ROUTINES: ========================================== */ /* ========================================================================== */ /* There are no user-callable routines beyond this point in the file */ /* ========================================================================== */ /* === init_rows_cols ======================================================= */ /* ========================================================================== */ /* Takes the column form of the matrix in A and creates the row form of the matrix. Also, row and column attributes are stored in the Col and Row structs. If the columns are un-sorted or contain duplicate row indices, this routine will also sort and remove duplicate row indices from the column form of the matrix. Returns FALSE if the matrix is invalid, TRUE otherwise. Not user-callable. */ PRIVATE Int init_rows_cols /* returns TRUE if OK, or FALSE otherwise */ ( /* === Parameters ======================================================= */ Int n_row, /* number of rows of A */ Int n_col, /* number of columns of A */ Colamd_Row Row [], /* of size n_row+1 */ Colamd_Col Col [], /* of size n_col+1 */ Int A [], /* row indices of A, of size Alen */ Int p [], /* pointers to columns in A, of size n_col+1 */ Int stats [COLAMD_STATS] /* colamd statistics */ ) { /* === Local variables ================================================== */ Int col ; /* a column index */ Int row ; /* a row index */ Int *cp ; /* a column pointer */ Int *cp_end ; /* a pointer to the end of a column */ Int *rp ; /* a row pointer */ Int *rp_end ; /* a pointer to the end of a row */ Int last_row ; /* previous row */ /* === Initialize columns, and check column pointers ==================== */ for (col = 0 ; col < n_col ; col++) { Col [col].start = p [col] ; Col [col].length = p [col+1] - p [col] ; if (Col [col].length < 0) { /* column pointers must be non-decreasing */ stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; stats [COLAMD_INFO1] = col ; stats [COLAMD_INFO2] = Col [col].length ; DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ; return (FALSE) ; } Col [col].shared1.thickness = 1 ; Col [col].shared2.score = 0 ; Col [col].shared3.prev = EMPTY ; Col [col].shared4.degree_next = EMPTY ; } /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ /* === Scan columns, compute row degrees, and check row indices ========= */ stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ for (row = 0 ; row < n_row ; row++) { Row [row].length = 0 ; Row [row].shared2.mark = -1 ; } for (col = 0 ; col < n_col ; col++) { last_row = -1 ; cp = &A [p [col]] ; cp_end = &A [p [col+1]] ; while (cp < cp_end) { row = *cp++ ; /* make sure row indices within range */ if (row < 0 || row >= n_row) { stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; stats [COLAMD_INFO1] = col ; stats [COLAMD_INFO2] = row ; stats [COLAMD_INFO3] = n_row ; DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ; return (FALSE) ; } if (row <= last_row || Row [row].shared2.mark == col) { /* row index are unsorted or repeated (or both), thus col */ /* is jumbled. This is a notice, not an error condition. */ stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; stats [COLAMD_INFO1] = col ; stats [COLAMD_INFO2] = row ; (stats [COLAMD_INFO3]) ++ ; DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col)); } if (Row [row].shared2.mark != col) { Row [row].length++ ; } else { /* this is a repeated entry in the column, */ /* it will be removed */ Col [col].length-- ; } /* mark the row as having been seen in this column */ Row [row].shared2.mark = col ; last_row = row ; } } /* === Compute row pointers ============================================= */ /* row form of the matrix starts directly after the column */ /* form of matrix in A */ Row [0].start = p [n_col] ; Row [0].shared1.p = Row [0].start ; Row [0].shared2.mark = -1 ; for (row = 1 ; row < n_row ; row++) { Row [row].start = Row [row-1].start + Row [row-1].length ; Row [row].shared1.p = Row [row].start ; Row [row].shared2.mark = -1 ; } /* === Create row form ================================================== */ if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) { /* if cols jumbled, watch for repeated row indices */ for (col = 0 ; col < n_col ; col++) { cp = &A [p [col]] ; cp_end = &A [p [col+1]] ; while (cp < cp_end) { row = *cp++ ; if (Row [row].shared2.mark != col) { A [(Row [row].shared1.p)++] = col ; Row [row].shared2.mark = col ; } } } } else { /* if cols not jumbled, we don't need the mark (this is faster) */ for (col = 0 ; col < n_col ; col++) { cp = &A [p [col]] ; cp_end = &A [p [col+1]] ; while (cp < cp_end) { A [(Row [*cp++].shared1.p)++] = col ; } } } /* === Clear the row marks and set row degrees ========================== */ for (row = 0 ; row < n_row ; row++) { Row [row].shared2.mark = 0 ; Row [row].shared1.degree = Row [row].length ; } /* === See if we need to re-create columns ============================== */ if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) { DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ; #ifndef NDEBUG /* make sure column lengths are correct */ for (col = 0 ; col < n_col ; col++) { p [col] = Col [col].length ; } for (row = 0 ; row < n_row ; row++) { rp = &A [Row [row].start] ; rp_end = rp + Row [row].length ; while (rp < rp_end) { p [*rp++]-- ; } } for (col = 0 ; col < n_col ; col++) { ASSERT (p [col] == 0) ; } /* now p is all zero (different than when debugging is turned off) */ #endif /* NDEBUG */ /* === Compute col pointers ========================================= */ /* col form of the matrix starts at A [0]. */ /* Note, we may have a gap between the col form and the row */ /* form if there were duplicate entries, if so, it will be */ /* removed upon the first garbage collection */ Col [0].start = 0 ; p [0] = Col [0].start ; for (col = 1 ; col < n_col ; col++) { /* note that the lengths here are for pruned columns, i.e. */ /* no duplicate row indices will exist for these columns */ Col [col].start = Col [col-1].start + Col [col-1].length ; p [col] = Col [col].start ; } /* === Re-create col form =========================================== */ for (row = 0 ; row < n_row ; row++) { rp = &A [Row [row].start] ; rp_end = rp + Row [row].length ; while (rp < rp_end) { A [(p [*rp++])++] = row ; } } } /* === Done. Matrix is not (or no longer) jumbled ====================== */ return (TRUE) ; } /* ========================================================================== */ /* === init_scoring ========================================================= */ /* ========================================================================== */ /* Kills dense or empty columns and rows, calculates an initial score for each column, and places all columns in the degree lists. Not user-callable. */ PRIVATE void init_scoring ( /* === Parameters ======================================================= */ Int n_row, /* number of rows of A */ Int n_col, /* number of columns of A */ Colamd_Row Row [], /* of size n_row+1 */ Colamd_Col Col [], /* of size n_col+1 */ Int A [], /* column form and row form of A */ Int head [], /* of size n_col+1 */ double knobs [COLAMD_KNOBS],/* parameters */ Int *p_n_row2, /* number of non-dense, non-empty rows */ Int *p_n_col2, /* number of non-dense, non-empty columns */ Int *p_max_deg /* maximum row degree */ ) { /* === Local variables ================================================== */ Int c ; /* a column index */ Int r, row ; /* a row index */ Int *cp ; /* a column pointer */ Int deg ; /* degree of a row or column */ Int *cp_end ; /* a pointer to the end of a column */ Int *new_cp ; /* new column pointer */ Int col_length ; /* length of pruned column */ Int score ; /* current column score */ Int n_col2 ; /* number of non-dense, non-empty columns */ Int n_row2 ; /* number of non-dense, non-empty rows */ Int dense_row_count ; /* remove rows with more entries than this */ Int dense_col_count ; /* remove cols with more entries than this */ Int min_score ; /* smallest column score */ Int max_deg ; /* maximum row degree */ Int next_col ; /* Used to add to degree list.*/ #ifndef NDEBUG Int debug_count ; /* debug only. */ #endif /* NDEBUG */ /* === Extract knobs ==================================================== */ /* Note: if knobs contains a NaN, this is undefined: */ if (knobs [COLAMD_DENSE_ROW] < 0) { /* only remove completely dense rows */ dense_row_count = n_col-1 ; } else { dense_row_count = DENSE_DEGREE (knobs [COLAMD_DENSE_ROW], n_col) ; } if (knobs [COLAMD_DENSE_COL] < 0) { /* only remove completely dense columns */ dense_col_count = n_row-1 ; } else { dense_col_count = DENSE_DEGREE (knobs [COLAMD_DENSE_COL], MIN (n_row, n_col)) ; } DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ; max_deg = 0 ; n_col2 = n_col ; n_row2 = n_row ; /* === Kill empty columns =============================================== */ /* Put the empty columns at the end in their natural order, so that LU */ /* factorization can proceed as far as possible. */ for (c = n_col-1 ; c >= 0 ; c--) { deg = Col [c].length ; if (deg == 0) { /* this is a empty column, kill and order it last */ Col [c].shared2.order = --n_col2 ; KILL_PRINCIPAL_COL (c) ; } } DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ; /* === Kill dense columns =============================================== */ /* Put the dense columns at the end, in their natural order */ for (c = n_col-1 ; c >= 0 ; c--) { /* skip any dead columns */ if (COL_IS_DEAD (c)) { continue ; } deg = Col [c].length ; if (deg > dense_col_count) { /* this is a dense column, kill and order it last */ Col [c].shared2.order = --n_col2 ; /* decrement the row degrees */ cp = &A [Col [c].start] ; cp_end = cp + Col [c].length ; while (cp < cp_end) { Row [*cp++].shared1.degree-- ; } KILL_PRINCIPAL_COL (c) ; } } DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ; /* === Kill dense and empty rows ======================================== */ for (r = 0 ; r < n_row ; r++) { deg = Row [r].shared1.degree ; ASSERT (deg >= 0 && deg <= n_col) ; if (deg > dense_row_count || deg == 0) { /* kill a dense or empty row */ KILL_ROW (r) ; --n_row2 ; } else { /* keep track of max degree of remaining rows */ max_deg = MAX (max_deg, deg) ; } } DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ; /* === Compute initial column scores ==================================== */ /* At this point the row degrees are accurate. They reflect the number */ /* of "live" (non-dense) columns in each row. No empty rows exist. */ /* Some "live" columns may contain only dead rows, however. These are */ /* pruned in the code below. */ /* now find the initial matlab score for each column */ for (c = n_col-1 ; c >= 0 ; c--) { /* skip dead column */ if (COL_IS_DEAD (c)) { continue ; } score = 0 ; cp = &A [Col [c].start] ; new_cp = cp ; cp_end = cp + Col [c].length ; while (cp < cp_end) { /* get a row */ row = *cp++ ; /* skip if dead */ if (ROW_IS_DEAD (row)) { continue ; } /* compact the column */ *new_cp++ = row ; /* add row's external degree */ score += Row [row].shared1.degree - 1 ; /* guard against integer overflow */ score = MIN (score, n_col) ; } /* determine pruned column length */ col_length = (Int) (new_cp - &A [Col [c].start]) ; if (col_length == 0) { /* a newly-made null column (all rows in this col are "dense" */ /* and have already been killed) */ DEBUG2 (("Newly null killed: %d\n", c)) ; Col [c].shared2.order = --n_col2 ; KILL_PRINCIPAL_COL (c) ; } else { /* set column length and set score */ ASSERT (score >= 0) ; ASSERT (score <= n_col) ; Col [c].length = col_length ; Col [c].shared2.score = score ; } } DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n", n_col-n_col2)) ; /* At this point, all empty rows and columns are dead. All live columns */ /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ /* yet). Rows may contain dead columns, but all live rows contain at */ /* least one live column. */ #ifndef NDEBUG debug_structures (n_row, n_col, Row, Col, A, n_col2) ; #endif /* NDEBUG */ /* === Initialize degree lists ========================================== */ #ifndef NDEBUG debug_count = 0 ; #endif /* NDEBUG */ /* clear the hash buckets */ for (c = 0 ; c <= n_col ; c++) { head [c] = EMPTY ; } min_score = n_col ; /* place in reverse order, so low column indices are at the front */ /* of the lists. This is to encourage natural tie-breaking */ for (c = n_col-1 ; c >= 0 ; c--) { /* only add principal columns to degree lists */ if (COL_IS_ALIVE (c)) { DEBUG4 (("place %d score %d minscore %d ncol %d\n", c, Col [c].shared2.score, min_score, n_col)) ; /* === Add columns score to DList =============================== */ score = Col [c].shared2.score ; ASSERT (min_score >= 0) ; ASSERT (min_score <= n_col) ; ASSERT (score >= 0) ; ASSERT (score <= n_col) ; ASSERT (head [score] >= EMPTY) ; /* now add this column to dList at proper score location */ next_col = head [score] ; Col [c].shared3.prev = EMPTY ; Col [c].shared4.degree_next = next_col ; /* if there already was a column with the same score, set its */ /* previous pointer to this new column */ if (next_col != EMPTY) { Col [next_col].shared3.prev = c ; } head [score] = c ; /* see if this score is less than current min */ min_score = MIN (min_score, score) ; #ifndef NDEBUG debug_count++ ; #endif /* NDEBUG */ } } #ifndef NDEBUG DEBUG1 (("colamd: Live cols %d out of %d, non-princ: %d\n", debug_count, n_col, n_col-debug_count)) ; ASSERT (debug_count == n_col2) ; debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; #endif /* NDEBUG */ /* === Return number of remaining columns, and max row degree =========== */ *p_n_col2 = n_col2 ; *p_n_row2 = n_row2 ; *p_max_deg = max_deg ; } /* ========================================================================== */ /* === find_ordering ======================================================== */ /* ========================================================================== */ /* Order the principal columns of the supercolumn form of the matrix (no supercolumns on input). Uses a minimum approximate column minimum degree ordering method. Not user-callable. */ PRIVATE Int find_ordering /* return the number of garbage collections */ ( /* === Parameters ======================================================= */ Int n_row, /* number of rows of A */ Int n_col, /* number of columns of A */ Int Alen, /* size of A, 2*nnz + n_col or larger */ Colamd_Row Row [], /* of size n_row+1 */ Colamd_Col Col [], /* of size n_col+1 */ Int A [], /* column form and row form of A */ Int head [], /* of size n_col+1 */ Int n_col2, /* Remaining columns to order */ Int max_deg, /* Maximum row degree */ Int pfree, /* index of first free slot (2*nnz on entry) */ Int aggressive ) { /* === Local variables ================================================== */ Int k ; /* current pivot ordering step */ Int pivot_col ; /* current pivot column */ Int *cp ; /* a column pointer */ Int *rp ; /* a row pointer */ Int pivot_row ; /* current pivot row */ Int *new_cp ; /* modified column pointer */ Int *new_rp ; /* modified row pointer */ Int pivot_row_start ; /* pointer to start of pivot row */ Int pivot_row_degree ; /* number of columns in pivot row */ Int pivot_row_length ; /* number of supercolumns in pivot row */ Int pivot_col_score ; /* score of pivot column */ Int needed_memory ; /* free space needed for pivot row */ Int *cp_end ; /* pointer to the end of a column */ Int *rp_end ; /* pointer to the end of a row */ Int row ; /* a row index */ Int col ; /* a column index */ Int max_score ; /* maximum possible score */ Int cur_score ; /* score of current column */ unsigned Int hash ; /* hash value for supernode detection */ Int head_column ; /* head of hash bucket */ Int first_col ; /* first column in hash bucket */ Int tag_mark ; /* marker value for mark array */ Int row_mark ; /* Row [row].shared2.mark */ Int set_difference ; /* set difference size of row with pivot row */ Int min_score ; /* smallest column score */ Int col_thickness ; /* "thickness" (no. of columns in a supercol) */ Int max_mark ; /* maximum value of tag_mark */ Int pivot_col_thickness ; /* number of columns represented by pivot col */ Int prev_col ; /* Used by Dlist operations. */ Int next_col ; /* Used by Dlist operations. */ Int ngarbage ; /* number of garbage collections performed */ #ifndef NDEBUG Int debug_d ; /* debug loop counter */ Int debug_step = 0 ; /* debug loop counter */ #endif /* NDEBUG */ /* === Initialization and clear mark ==================================== */ max_mark = INT_MAX - n_col ; /* INT_MAX defined in */ tag_mark = clear_mark (0, max_mark, n_row, Row) ; min_score = 0 ; ngarbage = 0 ; DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ; /* === Order the columns ================================================ */ for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) { #ifndef NDEBUG if (debug_step % 100 == 0) { DEBUG2 (("\n... Step k: %d out of n_col2: %d\n", k, n_col2)) ; } else { DEBUG3 (("\n----------Step k: %d out of n_col2: %d\n", k, n_col2)) ; } debug_step++ ; debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2-k, max_deg) ; debug_matrix (n_row, n_col, Row, Col, A) ; #endif /* NDEBUG */ /* === Select pivot column, and order it ============================ */ /* make sure degree list isn't empty */ ASSERT (min_score >= 0) ; ASSERT (min_score <= n_col) ; ASSERT (head [min_score] >= EMPTY) ; #ifndef NDEBUG for (debug_d = 0 ; debug_d < min_score ; debug_d++) { ASSERT (head [debug_d] == EMPTY) ; } #endif /* NDEBUG */ /* get pivot column from head of minimum degree list */ while (head [min_score] == EMPTY && min_score < n_col) { min_score++ ; } pivot_col = head [min_score] ; ASSERT (pivot_col >= 0 && pivot_col <= n_col) ; next_col = Col [pivot_col].shared4.degree_next ; head [min_score] = next_col ; if (next_col != EMPTY) { Col [next_col].shared3.prev = EMPTY ; } ASSERT (COL_IS_ALIVE (pivot_col)) ; /* remember score for defrag check */ pivot_col_score = Col [pivot_col].shared2.score ; /* the pivot column is the kth column in the pivot order */ Col [pivot_col].shared2.order = k ; /* increment order count by column thickness */ pivot_col_thickness = Col [pivot_col].shared1.thickness ; k += pivot_col_thickness ; ASSERT (pivot_col_thickness > 0) ; DEBUG3 (("Pivot col: %d thick %d\n", pivot_col, pivot_col_thickness)) ; /* === Garbage_collection, if necessary ============================= */ needed_memory = MIN (pivot_col_score, n_col - k) ; if (pfree + needed_memory >= Alen) { pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; ngarbage++ ; /* after garbage collection we will have enough */ ASSERT (pfree + needed_memory < Alen) ; /* garbage collection has wiped out the Row[].shared2.mark array */ tag_mark = clear_mark (0, max_mark, n_row, Row) ; #ifndef NDEBUG debug_matrix (n_row, n_col, Row, Col, A) ; #endif /* NDEBUG */ } /* === Compute pivot row pattern ==================================== */ /* get starting location for this new merged row */ pivot_row_start = pfree ; /* initialize new row counts to zero */ pivot_row_degree = 0 ; /* tag pivot column as having been visited so it isn't included */ /* in merged pivot row */ Col [pivot_col].shared1.thickness = -pivot_col_thickness ; /* pivot row is the union of all rows in the pivot column pattern */ cp = &A [Col [pivot_col].start] ; cp_end = cp + Col [pivot_col].length ; while (cp < cp_end) { /* get a row */ row = *cp++ ; DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ; /* skip if row is dead */ if (ROW_IS_ALIVE (row)) { rp = &A [Row [row].start] ; rp_end = rp + Row [row].length ; while (rp < rp_end) { /* get a column */ col = *rp++ ; /* add the column, if alive and untagged */ col_thickness = Col [col].shared1.thickness ; if (col_thickness > 0 && COL_IS_ALIVE (col)) { /* tag column in pivot row */ Col [col].shared1.thickness = -col_thickness ; ASSERT (pfree < Alen) ; /* place column in pivot row */ A [pfree++] = col ; pivot_row_degree += col_thickness ; } } } } /* clear tag on pivot column */ Col [pivot_col].shared1.thickness = pivot_col_thickness ; max_deg = MAX (max_deg, pivot_row_degree) ; #ifndef NDEBUG DEBUG3 (("check2\n")) ; debug_mark (n_row, Row, tag_mark, max_mark) ; #endif /* NDEBUG */ /* === Kill all rows used to construct pivot row ==================== */ /* also kill pivot row, temporarily */ cp = &A [Col [pivot_col].start] ; cp_end = cp + Col [pivot_col].length ; while (cp < cp_end) { /* may be killing an already dead row */ row = *cp++ ; DEBUG3 (("Kill row in pivot col: %d\n", row)) ; KILL_ROW (row) ; } /* === Select a row index to use as the new pivot row =============== */ pivot_row_length = pfree - pivot_row_start ; if (pivot_row_length > 0) { /* pick the "pivot" row arbitrarily (first row in col) */ pivot_row = A [Col [pivot_col].start] ; DEBUG3 (("Pivotal row is %d\n", pivot_row)) ; } else { /* there is no pivot row, since it is of zero length */ pivot_row = EMPTY ; ASSERT (pivot_row_length == 0) ; } ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ; /* === Approximate degree computation =============================== */ /* Here begins the computation of the approximate degree. The column */ /* score is the sum of the pivot row "length", plus the size of the */ /* set differences of each row in the column minus the pattern of the */ /* pivot row itself. The column ("thickness") itself is also */ /* excluded from the column score (we thus use an approximate */ /* external degree). */ /* The time taken by the following code (compute set differences, and */ /* add them up) is proportional to the size of the data structure */ /* being scanned - that is, the sum of the sizes of each column in */ /* the pivot row. Thus, the amortized time to compute a column score */ /* is proportional to the size of that column (where size, in this */ /* context, is the column "length", or the number of row indices */ /* in that column). The number of row indices in a column is */ /* monotonically non-decreasing, from the length of the original */ /* column on input to colamd. */ /* === Compute set differences ====================================== */ DEBUG3 (("** Computing set differences phase. **\n")) ; /* pivot row is currently dead - it will be revived later. */ DEBUG3 (("Pivot row: ")) ; /* for each column in pivot row */ rp = &A [pivot_row_start] ; rp_end = rp + pivot_row_length ; while (rp < rp_end) { col = *rp++ ; ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; DEBUG3 (("Col: %d\n", col)) ; /* clear tags used to construct pivot row pattern */ col_thickness = -Col [col].shared1.thickness ; ASSERT (col_thickness > 0) ; Col [col].shared1.thickness = col_thickness ; /* === Remove column from degree list =========================== */ cur_score = Col [col].shared2.score ; prev_col = Col [col].shared3.prev ; next_col = Col [col].shared4.degree_next ; ASSERT (cur_score >= 0) ; ASSERT (cur_score <= n_col) ; ASSERT (cur_score >= EMPTY) ; if (prev_col == EMPTY) { head [cur_score] = next_col ; } else { Col [prev_col].shared4.degree_next = next_col ; } if (next_col != EMPTY) { Col [next_col].shared3.prev = prev_col ; } /* === Scan the column ========================================== */ cp = &A [Col [col].start] ; cp_end = cp + Col [col].length ; while (cp < cp_end) { /* get a row */ row = *cp++ ; row_mark = Row [row].shared2.mark ; /* skip if dead */ if (ROW_IS_MARKED_DEAD (row_mark)) { continue ; } ASSERT (row != pivot_row) ; set_difference = row_mark - tag_mark ; /* check if the row has been seen yet */ if (set_difference < 0) { ASSERT (Row [row].shared1.degree <= max_deg) ; set_difference = Row [row].shared1.degree ; } /* subtract column thickness from this row's set difference */ set_difference -= col_thickness ; ASSERT (set_difference >= 0) ; /* absorb this row if the set difference becomes zero */ if (set_difference == 0 && aggressive) { DEBUG3 (("aggressive absorption. Row: %d\n", row)) ; KILL_ROW (row) ; } else { /* save the new mark */ Row [row].shared2.mark = set_difference + tag_mark ; } } } #ifndef NDEBUG debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2-k-pivot_row_degree, max_deg) ; #endif /* NDEBUG */ /* === Add up set differences for each column ======================= */ DEBUG3 (("** Adding set differences phase. **\n")) ; /* for each column in pivot row */ rp = &A [pivot_row_start] ; rp_end = rp + pivot_row_length ; while (rp < rp_end) { /* get a column */ col = *rp++ ; ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; hash = 0 ; cur_score = 0 ; cp = &A [Col [col].start] ; /* compact the column */ new_cp = cp ; cp_end = cp + Col [col].length ; DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ; while (cp < cp_end) { /* get a row */ row = *cp++ ; ASSERT(row >= 0 && row < n_row) ; row_mark = Row [row].shared2.mark ; /* skip if dead */ if (ROW_IS_MARKED_DEAD (row_mark)) { DEBUG4 ((" Row %d, dead\n", row)) ; continue ; } DEBUG4 ((" Row %d, set diff %d\n", row, row_mark-tag_mark)); ASSERT (row_mark >= tag_mark) ; /* compact the column */ *new_cp++ = row ; /* compute hash function */ hash += row ; /* add set difference */ cur_score += row_mark - tag_mark ; /* integer overflow... */ cur_score = MIN (cur_score, n_col) ; } /* recompute the column's length */ Col [col].length = (Int) (new_cp - &A [Col [col].start]) ; /* === Further mass elimination ================================= */ if (Col [col].length == 0) { DEBUG4 (("further mass elimination. Col: %d\n", col)) ; /* nothing left but the pivot row in this column */ KILL_PRINCIPAL_COL (col) ; pivot_row_degree -= Col [col].shared1.thickness ; ASSERT (pivot_row_degree >= 0) ; /* order it */ Col [col].shared2.order = k ; /* increment order count by column thickness */ k += Col [col].shared1.thickness ; } else { /* === Prepare for supercolumn detection ==================== */ DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ; /* save score so far */ Col [col].shared2.score = cur_score ; /* add column to hash table, for supercolumn detection */ hash %= n_col + 1 ; DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ; ASSERT (((Int) hash) <= n_col) ; head_column = head [hash] ; if (head_column > EMPTY) { /* degree list "hash" is non-empty, use prev (shared3) of */ /* first column in degree list as head of hash bucket */ first_col = Col [head_column].shared3.headhash ; Col [head_column].shared3.headhash = col ; } else { /* degree list "hash" is empty, use head as hash bucket */ first_col = - (head_column + 2) ; head [hash] = - (col + 2) ; } Col [col].shared4.hash_next = first_col ; /* save hash function in Col [col].shared3.hash */ Col [col].shared3.hash = (Int) hash ; ASSERT (COL_IS_ALIVE (col)) ; } } /* The approximate external column degree is now computed. */ /* === Supercolumn detection ======================================== */ DEBUG3 (("** Supercolumn detection phase. **\n")) ; detect_super_cols ( #ifndef NDEBUG n_col, Row, #endif /* NDEBUG */ Col, A, head, pivot_row_start, pivot_row_length) ; /* === Kill the pivotal column ====================================== */ KILL_PRINCIPAL_COL (pivot_col) ; /* === Clear mark =================================================== */ tag_mark = clear_mark (tag_mark+max_deg+1, max_mark, n_row, Row) ; #ifndef NDEBUG DEBUG3 (("check3\n")) ; debug_mark (n_row, Row, tag_mark, max_mark) ; #endif /* NDEBUG */ /* === Finalize the new pivot row, and column scores ================ */ DEBUG3 (("** Finalize scores phase. **\n")) ; /* for each column in pivot row */ rp = &A [pivot_row_start] ; /* compact the pivot row */ new_rp = rp ; rp_end = rp + pivot_row_length ; while (rp < rp_end) { col = *rp++ ; /* skip dead columns */ if (COL_IS_DEAD (col)) { continue ; } *new_rp++ = col ; /* add new pivot row to column */ A [Col [col].start + (Col [col].length++)] = pivot_row ; /* retrieve score so far and add on pivot row's degree. */ /* (we wait until here for this in case the pivot */ /* row's degree was reduced due to mass elimination). */ cur_score = Col [col].shared2.score + pivot_row_degree ; /* calculate the max possible score as the number of */ /* external columns minus the 'k' value minus the */ /* columns thickness */ max_score = n_col - k - Col [col].shared1.thickness ; /* make the score the external degree of the union-of-rows */ cur_score -= Col [col].shared1.thickness ; /* make sure score is less or equal than the max score */ cur_score = MIN (cur_score, max_score) ; ASSERT (cur_score >= 0) ; /* store updated score */ Col [col].shared2.score = cur_score ; /* === Place column back in degree list ========================= */ ASSERT (min_score >= 0) ; ASSERT (min_score <= n_col) ; ASSERT (cur_score >= 0) ; ASSERT (cur_score <= n_col) ; ASSERT (head [cur_score] >= EMPTY) ; next_col = head [cur_score] ; Col [col].shared4.degree_next = next_col ; Col [col].shared3.prev = EMPTY ; if (next_col != EMPTY) { Col [next_col].shared3.prev = col ; } head [cur_score] = col ; /* see if this score is less than current min */ min_score = MIN (min_score, cur_score) ; } #ifndef NDEBUG debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2-k, max_deg) ; #endif /* NDEBUG */ /* === Resurrect the new pivot row ================================== */ if (pivot_row_degree > 0) { /* update pivot row length to reflect any cols that were killed */ /* during super-col detection and mass elimination */ Row [pivot_row].start = pivot_row_start ; Row [pivot_row].length = (Int) (new_rp - &A[pivot_row_start]) ; ASSERT (Row [pivot_row].length > 0) ; Row [pivot_row].shared1.degree = pivot_row_degree ; Row [pivot_row].shared2.mark = 0 ; /* pivot row is no longer dead */ DEBUG1 (("Resurrect Pivot_row %d deg: %d\n", pivot_row, pivot_row_degree)) ; } } /* === All principal columns have now been ordered ====================== */ return (ngarbage) ; } /* ========================================================================== */ /* === order_children ======================================================= */ /* ========================================================================== */ /* The find_ordering routine has ordered all of the principal columns (the representatives of the supercolumns). The non-principal columns have not yet been ordered. This routine orders those columns by walking up the parent tree (a column is a child of the column which absorbed it). The final permutation vector is then placed in p [0 ... n_col-1], with p [0] being the first column, and p [n_col-1] being the last. It doesn't look like it at first glance, but be assured that this routine takes time linear in the number of columns. Although not immediately obvious, the time taken by this routine is O (n_col), that is, linear in the number of columns. Not user-callable. */ PRIVATE void order_children ( /* === Parameters ======================================================= */ Int n_col, /* number of columns of A */ Colamd_Col Col [], /* of size n_col+1 */ Int p [] /* p [0 ... n_col-1] is the column permutation*/ ) { /* === Local variables ================================================== */ Int i ; /* loop counter for all columns */ Int c ; /* column index */ Int parent ; /* index of column's parent */ Int order ; /* column's order */ /* === Order each non-principal column ================================== */ for (i = 0 ; i < n_col ; i++) { /* find an un-ordered non-principal column */ ASSERT (COL_IS_DEAD (i)) ; if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == EMPTY) { parent = i ; /* once found, find its principal parent */ do { parent = Col [parent].shared1.parent ; } while (!COL_IS_DEAD_PRINCIPAL (parent)) ; /* now, order all un-ordered non-principal columns along path */ /* to this parent. collapse tree at the same time */ c = i ; /* get order of parent */ order = Col [parent].shared2.order ; do { ASSERT (Col [c].shared2.order == EMPTY) ; /* order this column */ Col [c].shared2.order = order++ ; /* collaps tree */ Col [c].shared1.parent = parent ; /* get immediate parent of this column */ c = Col [c].shared1.parent ; /* continue until we hit an ordered column. There are */ /* guarranteed not to be anymore unordered columns */ /* above an ordered column */ } while (Col [c].shared2.order == EMPTY) ; /* re-order the super_col parent to largest order for this group */ Col [parent].shared2.order = order ; } } /* === Generate the permutation ========================================= */ for (c = 0 ; c < n_col ; c++) { p [Col [c].shared2.order] = c ; } } /* ========================================================================== */ /* === detect_super_cols ==================================================== */ /* ========================================================================== */ /* Detects supercolumns by finding matches between columns in the hash buckets. Check amongst columns in the set A [row_start ... row_start + row_length-1]. The columns under consideration are currently *not* in the degree lists, and have already been placed in the hash buckets. The hash bucket for columns whose hash function is equal to h is stored as follows: if head [h] is >= 0, then head [h] contains a degree list, so: head [h] is the first column in degree bucket h. Col [head [h]].headhash gives the first column in hash bucket h. otherwise, the degree list is empty, and: -(head [h] + 2) is the first column in hash bucket h. For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous column" pointer. Col [c].shared3.hash is used instead as the hash number for that column. The value of Col [c].shared4.hash_next is the next column in the same hash bucket. Assuming no, or "few" hash collisions, the time taken by this routine is linear in the sum of the sizes (lengths) of each column whose score has just been computed in the approximate degree computation. Not user-callable. */ PRIVATE void detect_super_cols ( /* === Parameters ======================================================= */ #ifndef NDEBUG /* these two parameters are only needed when debugging is enabled: */ Int n_col, /* number of columns of A */ Colamd_Row Row [], /* of size n_row+1 */ #endif /* NDEBUG */ Colamd_Col Col [], /* of size n_col+1 */ Int A [], /* row indices of A */ Int head [], /* head of degree lists and hash buckets */ Int row_start, /* pointer to set of columns to check */ Int row_length /* number of columns to check */ ) { /* === Local variables ================================================== */ Int hash ; /* hash value for a column */ Int *rp ; /* pointer to a row */ Int c ; /* a column index */ Int super_c ; /* column index of the column to absorb into */ Int *cp1 ; /* column pointer for column super_c */ Int *cp2 ; /* column pointer for column c */ Int length ; /* length of column super_c */ Int prev_c ; /* column preceding c in hash bucket */ Int i ; /* loop counter */ Int *rp_end ; /* pointer to the end of the row */ Int col ; /* a column index in the row to check */ Int head_column ; /* first column in hash bucket or degree list */ Int first_col ; /* first column in hash bucket */ /* === Consider each column in the row ================================== */ rp = &A [row_start] ; rp_end = rp + row_length ; while (rp < rp_end) { col = *rp++ ; if (COL_IS_DEAD (col)) { continue ; } /* get hash number for this column */ hash = Col [col].shared3.hash ; ASSERT (hash <= n_col) ; /* === Get the first column in this hash bucket ===================== */ head_column = head [hash] ; if (head_column > EMPTY) { first_col = Col [head_column].shared3.headhash ; } else { first_col = - (head_column + 2) ; } /* === Consider each column in the hash bucket ====================== */ for (super_c = first_col ; super_c != EMPTY ; super_c = Col [super_c].shared4.hash_next) { ASSERT (COL_IS_ALIVE (super_c)) ; ASSERT (Col [super_c].shared3.hash == hash) ; length = Col [super_c].length ; /* prev_c is the column preceding column c in the hash bucket */ prev_c = super_c ; /* === Compare super_c with all columns after it ================ */ for (c = Col [super_c].shared4.hash_next ; c != EMPTY ; c = Col [c].shared4.hash_next) { ASSERT (c != super_c) ; ASSERT (COL_IS_ALIVE (c)) ; ASSERT (Col [c].shared3.hash == hash) ; /* not identical if lengths or scores are different */ if (Col [c].length != length || Col [c].shared2.score != Col [super_c].shared2.score) { prev_c = c ; continue ; } /* compare the two columns */ cp1 = &A [Col [super_c].start] ; cp2 = &A [Col [c].start] ; for (i = 0 ; i < length ; i++) { /* the columns are "clean" (no dead rows) */ ASSERT (ROW_IS_ALIVE (*cp1)) ; ASSERT (ROW_IS_ALIVE (*cp2)) ; /* row indices will same order for both supercols, */ /* no gather scatter nessasary */ if (*cp1++ != *cp2++) { break ; } } /* the two columns are different if the for-loop "broke" */ if (i != length) { prev_c = c ; continue ; } /* === Got it! two columns are identical =================== */ ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ; Col [super_c].shared1.thickness += Col [c].shared1.thickness ; Col [c].shared1.parent = super_c ; KILL_NON_PRINCIPAL_COL (c) ; /* order c later, in order_children() */ Col [c].shared2.order = EMPTY ; /* remove c from hash bucket */ Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; } } /* === Empty this hash bucket ======================================= */ if (head_column > EMPTY) { /* corresponding degree list "hash" is not empty */ Col [head_column].shared3.headhash = EMPTY ; } else { /* corresponding degree list "hash" is empty */ head [hash] = EMPTY ; } } } /* ========================================================================== */ /* === garbage_collection =================================================== */ /* ========================================================================== */ /* Defragments and compacts columns and rows in the workspace A. Used when all avaliable memory has been used while performing row merging. Returns the index of the first free position in A, after garbage collection. The time taken by this routine is linear is the size of the array A, which is itself linear in the number of nonzeros in the input matrix. Not user-callable. */ PRIVATE Int garbage_collection /* returns the new value of pfree */ ( /* === Parameters ======================================================= */ Int n_row, /* number of rows */ Int n_col, /* number of columns */ Colamd_Row Row [], /* row info */ Colamd_Col Col [], /* column info */ Int A [], /* A [0 ... Alen-1] holds the matrix */ Int *pfree /* &A [0] ... pfree is in use */ ) { /* === Local variables ================================================== */ Int *psrc ; /* source pointer */ Int *pdest ; /* destination pointer */ Int j ; /* counter */ Int r ; /* a row index */ Int c ; /* a column index */ Int length ; /* length of a row or column */ #ifndef NDEBUG Int debug_rows ; DEBUG2 (("Defrag..\n")) ; for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ; debug_rows = 0 ; #endif /* NDEBUG */ /* === Defragment the columns =========================================== */ pdest = &A[0] ; for (c = 0 ; c < n_col ; c++) { if (COL_IS_ALIVE (c)) { psrc = &A [Col [c].start] ; /* move and compact the column */ ASSERT (pdest <= psrc) ; Col [c].start = (Int) (pdest - &A [0]) ; length = Col [c].length ; for (j = 0 ; j < length ; j++) { r = *psrc++ ; if (ROW_IS_ALIVE (r)) { *pdest++ = r ; } } Col [c].length = (Int) (pdest - &A [Col [c].start]) ; } } /* === Prepare to defragment the rows =================================== */ for (r = 0 ; r < n_row ; r++) { if (ROW_IS_DEAD (r) || (Row [r].length == 0)) { /* This row is already dead, or is of zero length. Cannot compact * a row of zero length, so kill it. NOTE: in the current version, * there are no zero-length live rows. Kill the row (for the first * time, or again) just to be safe. */ KILL_ROW (r) ; } else { /* save first column index in Row [r].shared2.first_column */ psrc = &A [Row [r].start] ; Row [r].shared2.first_column = *psrc ; ASSERT (ROW_IS_ALIVE (r)) ; /* flag the start of the row with the one's complement of row */ *psrc = ONES_COMPLEMENT (r) ; #ifndef NDEBUG debug_rows++ ; #endif /* NDEBUG */ } } /* === Defragment the rows ============================================== */ psrc = pdest ; while (psrc < pfree) { /* find a negative number ... the start of a row */ if (*psrc++ < 0) { psrc-- ; /* get the row index */ r = ONES_COMPLEMENT (*psrc) ; ASSERT (r >= 0 && r < n_row) ; /* restore first column index */ *psrc = Row [r].shared2.first_column ; ASSERT (ROW_IS_ALIVE (r)) ; ASSERT (Row [r].length > 0) ; /* move and compact the row */ ASSERT (pdest <= psrc) ; Row [r].start = (Int) (pdest - &A [0]) ; length = Row [r].length ; for (j = 0 ; j < length ; j++) { c = *psrc++ ; if (COL_IS_ALIVE (c)) { *pdest++ = c ; } } Row [r].length = (Int) (pdest - &A [Row [r].start]) ; ASSERT (Row [r].length > 0) ; #ifndef NDEBUG debug_rows-- ; #endif /* NDEBUG */ } } /* ensure we found all the rows */ ASSERT (debug_rows == 0) ; /* === Return the new value of pfree ==================================== */ return ((Int) (pdest - &A [0])) ; } /* ========================================================================== */ /* === clear_mark =========================================================== */ /* ========================================================================== */ /* Clears the Row [].shared2.mark array, and returns the new tag_mark. Return value is the new tag_mark. Not user-callable. */ PRIVATE Int clear_mark /* return the new value for tag_mark */ ( /* === Parameters ======================================================= */ Int tag_mark, /* new value of tag_mark */ Int max_mark, /* max allowed value of tag_mark */ Int n_row, /* number of rows in A */ Colamd_Row Row [] /* Row [0 ... n_row-1].shared2.mark is set to zero */ ) { /* === Local variables ================================================== */ Int r ; if (tag_mark <= 0 || tag_mark >= max_mark) { for (r = 0 ; r < n_row ; r++) { if (ROW_IS_ALIVE (r)) { Row [r].shared2.mark = 0 ; } } tag_mark = 1 ; } return (tag_mark) ; } /* ========================================================================== */ /* === print_report ========================================================= */ /* ========================================================================== */ PRIVATE void print_report ( char *method, Int stats [COLAMD_STATS] ) { Int i1, i2, i3 ; PRINTF (("\n%s version %d.%d, %s: ", method, COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE)) ; if (!stats) { PRINTF (("No statistics available.\n")) ; return ; } i1 = stats [COLAMD_INFO1] ; i2 = stats [COLAMD_INFO2] ; i3 = stats [COLAMD_INFO3] ; if (stats [COLAMD_STATUS] >= 0) { PRINTF (("OK. ")) ; } else { PRINTF (("ERROR. ")) ; } switch (stats [COLAMD_STATUS]) { case COLAMD_OK_BUT_JUMBLED: PRINTF(("Matrix has unsorted or duplicate row indices.\n")) ; PRINTF(("%s: number of duplicate or out-of-order row indices: %d\n", method, i3)) ; PRINTF(("%s: last seen duplicate or out-of-order row index: %d\n", method, INDEX (i2))) ; PRINTF(("%s: last seen in column: %d", method, INDEX (i1))) ; /* no break - fall through to next case instead */ case COLAMD_OK: PRINTF(("\n")) ; PRINTF(("%s: number of dense or empty rows ignored: %d\n", method, stats [COLAMD_DENSE_ROW])) ; PRINTF(("%s: number of dense or empty columns ignored: %d\n", method, stats [COLAMD_DENSE_COL])) ; PRINTF(("%s: number of garbage collections performed: %d\n", method, stats [COLAMD_DEFRAG_COUNT])) ; break ; case COLAMD_ERROR_A_not_present: PRINTF(("Array A (row indices of matrix) not present.\n")) ; break ; case COLAMD_ERROR_p_not_present: PRINTF(("Array p (column pointers for matrix) not present.\n")) ; break ; case COLAMD_ERROR_nrow_negative: PRINTF(("Invalid number of rows (%d).\n", i1)) ; break ; case COLAMD_ERROR_ncol_negative: PRINTF(("Invalid number of columns (%d).\n", i1)) ; break ; case COLAMD_ERROR_nnz_negative: PRINTF(("Invalid number of nonzero entries (%d).\n", i1)) ; break ; case COLAMD_ERROR_p0_nonzero: PRINTF(("Invalid column pointer, p [0] = %d, must be zero.\n", i1)); break ; case COLAMD_ERROR_A_too_small: PRINTF(("Array A too small.\n")) ; PRINTF((" Need Alen >= %d, but given only Alen = %d.\n", i1, i2)) ; break ; case COLAMD_ERROR_col_length_negative: PRINTF (("Column %d has a negative number of nonzero entries (%d).\n", INDEX (i1), i2)) ; break ; case COLAMD_ERROR_row_index_out_of_bounds: PRINTF (("Row index (row %d) out of bounds (%d to %d) in column %d.\n", INDEX (i2), INDEX (0), INDEX (i3-1), INDEX (i1))) ; break ; case COLAMD_ERROR_out_of_memory: PRINTF(("Out of memory.\n")) ; break ; /* v2.4: internal-error case deleted */ } } /* ========================================================================== */ /* === colamd debugging routines ============================================ */ /* ========================================================================== */ /* When debugging is disabled, the remainder of this file is ignored. */ #ifndef NDEBUG /* ========================================================================== */ /* === debug_structures ===================================================== */ /* ========================================================================== */ /* At this point, all empty rows and columns are dead. All live columns are "clean" (containing no dead rows) and simplicial (no supercolumns yet). Rows may contain dead columns, but all live rows contain at least one live column. */ PRIVATE void debug_structures ( /* === Parameters ======================================================= */ Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int A [], Int n_col2 ) { /* === Local variables ================================================== */ Int i ; Int c ; Int *cp ; Int *cp_end ; Int len ; Int score ; Int r ; Int *rp ; Int *rp_end ; Int deg ; /* === Check A, Row, and Col ============================================ */ for (c = 0 ; c < n_col ; c++) { if (COL_IS_ALIVE (c)) { len = Col [c].length ; score = Col [c].shared2.score ; DEBUG4 (("initial live col %5d %5d %5d\n", c, len, score)) ; ASSERT (len > 0) ; ASSERT (score >= 0) ; ASSERT (Col [c].shared1.thickness == 1) ; cp = &A [Col [c].start] ; cp_end = cp + len ; while (cp < cp_end) { r = *cp++ ; ASSERT (ROW_IS_ALIVE (r)) ; } } else { i = Col [c].shared2.order ; ASSERT (i >= n_col2 && i < n_col) ; } } for (r = 0 ; r < n_row ; r++) { if (ROW_IS_ALIVE (r)) { i = 0 ; len = Row [r].length ; deg = Row [r].shared1.degree ; ASSERT (len > 0) ; ASSERT (deg > 0) ; rp = &A [Row [r].start] ; rp_end = rp + len ; while (rp < rp_end) { c = *rp++ ; if (COL_IS_ALIVE (c)) { i++ ; } } ASSERT (i > 0) ; } } } /* ========================================================================== */ /* === debug_deg_lists ====================================================== */ /* ========================================================================== */ /* Prints the contents of the degree lists. Counts the number of columns in the degree list and compares it to the total it should have. Also checks the row degrees. */ PRIVATE void debug_deg_lists ( /* === Parameters ======================================================= */ Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int head [], Int min_score, Int should, Int max_deg ) { /* === Local variables ================================================== */ Int deg ; Int col ; Int have ; Int row ; /* === Check the degree lists =========================================== */ if (n_col > 10000 && colamd_debug <= 0) { return ; } have = 0 ; DEBUG4 (("Degree lists: %d\n", min_score)) ; for (deg = 0 ; deg <= n_col ; deg++) { col = head [deg] ; if (col == EMPTY) { continue ; } DEBUG4 (("%d:", deg)) ; while (col != EMPTY) { DEBUG4 ((" %d", col)) ; have += Col [col].shared1.thickness ; ASSERT (COL_IS_ALIVE (col)) ; col = Col [col].shared4.degree_next ; } DEBUG4 (("\n")) ; } DEBUG4 (("should %d have %d\n", should, have)) ; ASSERT (should == have) ; /* === Check the row degrees ============================================ */ if (n_row > 10000 && colamd_debug <= 0) { return ; } for (row = 0 ; row < n_row ; row++) { if (ROW_IS_ALIVE (row)) { ASSERT (Row [row].shared1.degree <= max_deg) ; } } } /* ========================================================================== */ /* === debug_mark =========================================================== */ /* ========================================================================== */ /* Ensures that the tag_mark is less that the maximum and also ensures that each entry in the mark array is less than the tag mark. */ PRIVATE void debug_mark ( /* === Parameters ======================================================= */ Int n_row, Colamd_Row Row [], Int tag_mark, Int max_mark ) { /* === Local variables ================================================== */ Int r ; /* === Check the Row marks ============================================== */ ASSERT (tag_mark > 0 && tag_mark <= max_mark) ; if (n_row > 10000 && colamd_debug <= 0) { return ; } for (r = 0 ; r < n_row ; r++) { ASSERT (Row [r].shared2.mark < tag_mark) ; } } /* ========================================================================== */ /* === debug_matrix ========================================================= */ /* ========================================================================== */ /* Prints out the contents of the columns and the rows. */ PRIVATE void debug_matrix ( /* === Parameters ======================================================= */ Int n_row, Int n_col, Colamd_Row Row [], Colamd_Col Col [], Int A [] ) { /* === Local variables ================================================== */ Int r ; Int c ; Int *rp ; Int *rp_end ; Int *cp ; Int *cp_end ; /* === Dump the rows and columns of the matrix ========================== */ if (colamd_debug < 3) { return ; } DEBUG3 (("DUMP MATRIX:\n")) ; for (r = 0 ; r < n_row ; r++) { DEBUG3 (("Row %d alive? %d\n", r, ROW_IS_ALIVE (r))) ; if (ROW_IS_DEAD (r)) { continue ; } DEBUG3 (("start %d length %d degree %d\n", Row [r].start, Row [r].length, Row [r].shared1.degree)) ; rp = &A [Row [r].start] ; rp_end = rp + Row [r].length ; while (rp < rp_end) { c = *rp++ ; DEBUG4 ((" %d col %d\n", COL_IS_ALIVE (c), c)) ; } } for (c = 0 ; c < n_col ; c++) { DEBUG3 (("Col %d alive? %d\n", c, COL_IS_ALIVE (c))) ; if (COL_IS_DEAD (c)) { continue ; } DEBUG3 (("start %d length %d shared1 %d shared2 %d\n", Col [c].start, Col [c].length, Col [c].shared1.thickness, Col [c].shared2.score)) ; cp = &A [Col [c].start] ; cp_end = cp + Col [c].length ; while (cp < cp_end) { r = *cp++ ; DEBUG4 ((" %d row %d\n", ROW_IS_ALIVE (r), r)) ; } } } PRIVATE void colamd_get_debug ( char *method ) { FILE *f ; colamd_debug = 0 ; /* no debug printing */ f = fopen ("debug", "r") ; if (f == (FILE *) NULL) { colamd_debug = 0 ; } else { fscanf (f, "%d", &colamd_debug) ; fclose (f) ; } DEBUG0 (("%s: debug version, D = %d (THIS WILL BE SLOW!)\n", method, colamd_debug)) ; } #endif /* NDEBUG */ igraph/src/cs_ipvec.c0000644000176000001440000000231212325527073014306 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* x(p) = b, for dense vectors x and b; p=NULL denotes identity */ CS_INT cs_ipvec (const CS_INT *p, const CS_ENTRY *b, CS_ENTRY *x, CS_INT n) { CS_INT k ; if (!x || !b) return (0) ; /* check inputs */ for (k = 0 ; k < n ; k++) x [p ? p [k] : k] = b [k] ; return (1) ; } igraph/src/infomap_FlowGraph.cc0000644000176000001440000002615212325527073016270 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "infomap_FlowGraph.h" #define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 ) void FlowGraph::init(int n, const igraph_vector_t *v_weights) { alpha = 0.15; beta = 1.0 - alpha; Nnode = n; node = new Node*[Nnode]; if (v_weights) { for (int i=0;i 0.0) { if (from != to) { node[(int) from]->outLinks.push_back(make_pair((int)to, linkWeight)); node[(int) to]->inLinks.push_back(make_pair((int) from, linkWeight)); } } } } FlowGraph::FlowGraph(FlowGraph * fgraph) { int n = fgraph->Nnode; init(n, NULL); for (int i=0; inode[i]); } //XXX: quid de danglings et Ndanglings? alpha = fgraph->alpha ; beta = fgraph->beta ; exit = fgraph->exit; exitFlow = fgraph->exitFlow; exit_log_exit = fgraph->exit_log_exit; size_log_size = fgraph->size_log_size ; nodeSize_log_nodeSize = fgraph->nodeSize_log_nodeSize; codeLength = fgraph->codeLength; } /** construct a graph by extracting a subgraph from the given graph */ FlowGraph::FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members) { init(sub_Nnode, NULL); //XXX: use set of integer to ensure that elements are sorted set sub_mem; for (int j=0 ; j::iterator it_mem = sub_mem.begin(); vector sub_renumber = vector(fgraph->Nnode); // id --> sub_id for (int j=0; jNnode; j++) { sub_renumber[j] = -1; } for (int j=0; jteleportWeight = fgraph->node[orig_nr]->teleportWeight; node[j]->selfLink = fgraph->node[orig_nr]->selfLink; // Take care of self-link int orig_NoutLinks = fgraph->node[orig_nr]->outLinks.size(); int orig_NinLinks = fgraph->node[orig_nr]->inLinks.size(); sub_renumber[orig_nr] = j; for (int k=0; knode[orig_nr]->outLinks[k].first; int to_newnr = sub_renumber[to]; double link_weight = fgraph->node[orig_nr]->outLinks[k].second; if (to < orig_nr) { // we add links if the destination (to) has already be seen // (ie. smaller than current id) => orig if (sub_mem.find(to) != sub_mem.end()) { // printf("%2d | %4d to %4d\n", j, orig_nr, to); // printf("from %4d (%4d:%1.5f) to %4d (%4d)\n", j, orig_nr, // node[j]->selfLink, to_newnr, to); node[j]->outLinks.push_back(make_pair(to_newnr, link_weight)); node[to_newnr]->inLinks.push_back(make_pair(j, link_weight)); } } } for (int k=0; knode[orig_nr]->inLinks[k].first; int to_newnr = sub_renumber[to]; double link_weight = fgraph->node[orig_nr]->inLinks[k].second; if (to < orig_nr) { if (sub_mem.find(to) != sub_mem.end()) { node[j]->inLinks.push_back(make_pair(to_newnr,link_weight)); node[to_newnr]->outLinks.push_back(make_pair(j,link_weight)); } } } it_mem++; } } FlowGraph::~FlowGraph() { //printf("delete FlowGraph !\n"); for (int i=0;inode; int Nnode_tmp = fgraph->Nnode; fgraph->node = node; fgraph->Nnode = Nnode; node = node_tmp; Nnode = Nnode_tmp; calibrate(); } /** Initialisation of the graph, compute the flow inside the graph * - count danglings nodes * - normalized edge weights * - Call eigenvector() to compute steady state distribution * - call calibrate to compute codelenght */ void FlowGraph::initiate() { // Take care of dangling nodes, normalize outLinks, and calculate // total teleport weight Ndanglings = 0; double totTeleportWeight = 0.0; for (int i=0;iteleportWeight; for (int i=0;iteleportWeight /= totTeleportWeight; // normalize teleportation weight if (node[i]->outLinks.empty() && (node[i]->selfLink <= 0.0)) { danglings.push_back(i); Ndanglings++; } else { // Normalize the weights int NoutLinks = node[i]->outLinks.size(); double sum = node[i]->selfLink; // Take care of self-links for (int j=0;j < NoutLinks; j++) sum += node[i]->outLinks[j].second; node[i]->selfLink /= sum; for (int j=0;j < NoutLinks; j++) node[i]->outLinks[j].second /= sum; } } // Calculate steady state matrix eigenvector(); // Update links to represent flow for (int i=0; iselfLink = beta * node[i]->size * node[i]->selfLink; // (1 - \tau) * \pi_i * P_{ii} if (!node[i]->outLinks.empty()) { int NoutLinks = node[i]->outLinks.size(); for (int j=0;j < NoutLinks; j++) { node[i]->outLinks[j].second = beta * node[i]->size * node[i]->outLinks[j].second; // (1 - \tau) * \pi_i * P_{ij} } // Update values for corresponding inlink for (int j=0; j < NoutLinks; j++) { int NinLinks = node[node[i]->outLinks[j].first]->inLinks.size(); for (int k=0; k < NinLinks; k++) { if (node[node[i]->outLinks[j].first]->inLinks[k].first == i) { node[node[i]->outLinks[j].first]->inLinks[k].second = node[i]->outLinks[j].second; k = NinLinks; } } } } } // To be able to handle dangling nodes efficiently for (int i=0;ioutLinks.empty() && (node[i]->selfLink <= 0.0)) { node[i]->danglingSize = node[i]->size; } else { node[i]->danglingSize = 0.0; } nodeSize_log_nodeSize = 0.0 ; // The exit flow from each node at initiation for (int i=0;iexit = node[i]->size // Proba to be on i - (alpha * node[i]->size + beta * node[i]->danglingSize) * node[i]->teleportWeight // Proba teleport back to i - node[i]->selfLink; // Proba stay on i // node[i]->exit == q_{i\exit} nodeSize_log_nodeSize += plogp(node[i]->size); } calibrate(); } /* Compute steady state distribution (ie. PageRank) over the network * (for all i update node[i]->size) */ void FlowGraph::eigenvector() { vector size_tmp = vector(Nnode,1.0/Nnode); int Niterations = 0; double danglingSize; double sqdiff = 1.0; double sqdiff_old; double sum; do { // Calculate dangling size danglingSize = 0.0; for (int i=0;isize = (alpha + beta*danglingSize) * node[i]->teleportWeight; // Flow from network steps for (int i=0;isize += beta * node[i]->selfLink * size_tmp[i]; int Nlinks = node[i]->outLinks.size(); for (int j=0; j < Nlinks; j++) node[node[i]->outLinks[j].first]->size += beta * node[i]->outLinks[j].second * size_tmp[i]; } // Normalize sum = 0.0; for (int i=0;isize; } sqdiff_old = sqdiff; sqdiff = 0.0; for (int i=0;isize /= sum; sqdiff += fabs(node[i]->size - size_tmp[i]); size_tmp[i] = node[i]->size; } Niterations++; if (sqdiff == sqdiff_old) { alpha += 1.0e-10; beta = 1.0-alpha; } } while ((Niterations < 200) && (sqdiff > 1.0e-15 || Niterations < 50)); danglingSize = 0.0; for (int i=0;iexit + node[i]->size); // use of index codebook exitFlow += node[i]->exit; exit_log_exit += plogp(node[i]->exit); } exit = plogp(exitFlow); codeLength = exit - 2.0*exit_log_exit + size_log_size - nodeSize_log_nodeSize; } /* Restore the data from the given FlowGraph object */ void FlowGraph::back_to(FlowGraph * fgraph) { // delete current nodes for (int i=0 ; iNnode; // copy original ones node = new Node*[Nnode]; for (int i=0;inode[i]); } // restore atributs alpha = fgraph->alpha ; beta = fgraph->beta ; exit = fgraph->exit; exitFlow = fgraph->exitFlow; exit_log_exit = fgraph->exit_log_exit; size_log_size = fgraph->size_log_size ; nodeSize_log_nodeSize = fgraph->nodeSize_log_nodeSize; codeLength = fgraph->codeLength; } igraph/src/glpmpl03.c0000644000176000001440000064572212325527073014173 0ustar ripleyusers/* glpmpl03.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #define _GLPSTD_ERRNO #define _GLPSTD_STDIO #include "glpenv.h" #include "glpmpl.h" /**********************************************************************/ /* * * FLOATING-POINT NUMBERS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- fp_add - floating-point addition. -- -- This routine computes the sum x + y. */ double fp_add(MPL *mpl, double x, double y) { if (x > 0.0 && y > 0.0 && x > + 0.999 * DBL_MAX - y || x < 0.0 && y < 0.0 && x < - 0.999 * DBL_MAX - y) error(mpl, "%.*g + %.*g; floating-point overflow", DBL_DIG, x, DBL_DIG, y); return x + y; } /*---------------------------------------------------------------------- -- fp_sub - floating-point subtraction. -- -- This routine computes the difference x - y. */ double fp_sub(MPL *mpl, double x, double y) { if (x > 0.0 && y < 0.0 && x > + 0.999 * DBL_MAX + y || x < 0.0 && y > 0.0 && x < - 0.999 * DBL_MAX + y) error(mpl, "%.*g - %.*g; floating-point overflow", DBL_DIG, x, DBL_DIG, y); return x - y; } /*---------------------------------------------------------------------- -- fp_less - floating-point non-negative subtraction. -- -- This routine computes the non-negative difference max(0, x - y). */ double fp_less(MPL *mpl, double x, double y) { if (x < y) return 0.0; if (x > 0.0 && y < 0.0 && x > + 0.999 * DBL_MAX + y) error(mpl, "%.*g less %.*g; floating-point overflow", DBL_DIG, x, DBL_DIG, y); return x - y; } /*---------------------------------------------------------------------- -- fp_mul - floating-point multiplication. -- -- This routine computes the product x * y. */ double fp_mul(MPL *mpl, double x, double y) { if (fabs(y) > 1.0 && fabs(x) > (0.999 * DBL_MAX) / fabs(y)) error(mpl, "%.*g * %.*g; floating-point overflow", DBL_DIG, x, DBL_DIG, y); return x * y; } /*---------------------------------------------------------------------- -- fp_div - floating-point division. -- -- This routine computes the quotient x / y. */ double fp_div(MPL *mpl, double x, double y) { if (fabs(y) < DBL_MIN) error(mpl, "%.*g / %.*g; floating-point zero divide", DBL_DIG, x, DBL_DIG, y); if (fabs(y) < 1.0 && fabs(x) > (0.999 * DBL_MAX) * fabs(y)) error(mpl, "%.*g / %.*g; floating-point overflow", DBL_DIG, x, DBL_DIG, y); return x / y; } /*---------------------------------------------------------------------- -- fp_idiv - floating-point quotient of exact division. -- -- This routine computes the quotient of exact division x div y. */ double fp_idiv(MPL *mpl, double x, double y) { if (fabs(y) < DBL_MIN) error(mpl, "%.*g div %.*g; floating-point zero divide", DBL_DIG, x, DBL_DIG, y); if (fabs(y) < 1.0 && fabs(x) > (0.999 * DBL_MAX) * fabs(y)) error(mpl, "%.*g div %.*g; floating-point overflow", DBL_DIG, x, DBL_DIG, y); x /= y; return x > 0.0 ? floor(x) : x < 0.0 ? ceil(x) : 0.0; } /*---------------------------------------------------------------------- -- fp_mod - floating-point remainder of exact division. -- -- This routine computes the remainder of exact division x mod y. -- -- NOTE: By definition x mod y = x - y * floor(x / y). */ double fp_mod(MPL *mpl, double x, double y) { double r; xassert(mpl == mpl); if (x == 0.0) r = 0.0; else if (y == 0.0) r = x; else { r = fmod(fabs(x), fabs(y)); if (r != 0.0) { if (x < 0.0) r = - r; if (x > 0.0 && y < 0.0 || x < 0.0 && y > 0.0) r += y; } } return r; } /*---------------------------------------------------------------------- -- fp_power - floating-point exponentiation (raise to power). -- -- This routine computes the exponentiation x ** y. */ double fp_power(MPL *mpl, double x, double y) { double r; if (x == 0.0 && y <= 0.0 || x < 0.0 && y != floor(y)) error(mpl, "%.*g ** %.*g; result undefined", DBL_DIG, x, DBL_DIG, y); if (x == 0.0) goto eval; if (fabs(x) > 1.0 && y > +1.0 && +log(fabs(x)) > (0.999 * log(DBL_MAX)) / y || fabs(x) < 1.0 && y < -1.0 && +log(fabs(x)) < (0.999 * log(DBL_MAX)) / y) error(mpl, "%.*g ** %.*g; floating-point overflow", DBL_DIG, x, DBL_DIG, y); if (fabs(x) > 1.0 && y < -1.0 && -log(fabs(x)) < (0.999 * log(DBL_MAX)) / y || fabs(x) < 1.0 && y > +1.0 && -log(fabs(x)) > (0.999 * log(DBL_MAX)) / y) r = 0.0; else eval: r = pow(x, y); return r; } /*---------------------------------------------------------------------- -- fp_exp - floating-point base-e exponential. -- -- This routine computes the base-e exponential e ** x. */ double fp_exp(MPL *mpl, double x) { if (x > 0.999 * log(DBL_MAX)) error(mpl, "exp(%.*g); floating-point overflow", DBL_DIG, x); return exp(x); } /*---------------------------------------------------------------------- -- fp_log - floating-point natural logarithm. -- -- This routine computes the natural logarithm log x. */ double fp_log(MPL *mpl, double x) { if (x <= 0.0) error(mpl, "log(%.*g); non-positive argument", DBL_DIG, x); return log(x); } /*---------------------------------------------------------------------- -- fp_log10 - floating-point common (decimal) logarithm. -- -- This routine computes the common (decimal) logarithm lg x. */ double fp_log10(MPL *mpl, double x) { if (x <= 0.0) error(mpl, "log10(%.*g); non-positive argument", DBL_DIG, x); return log10(x); } /*---------------------------------------------------------------------- -- fp_sqrt - floating-point square root. -- -- This routine computes the square root x ** 0.5. */ double fp_sqrt(MPL *mpl, double x) { if (x < 0.0) error(mpl, "sqrt(%.*g); negative argument", DBL_DIG, x); return sqrt(x); } /*---------------------------------------------------------------------- -- fp_sin - floating-point trigonometric sine. -- -- This routine computes the trigonometric sine sin(x). */ double fp_sin(MPL *mpl, double x) { if (!(-1e6 <= x && x <= +1e6)) error(mpl, "sin(%.*g); argument too large", DBL_DIG, x); return sin(x); } /*---------------------------------------------------------------------- -- fp_cos - floating-point trigonometric cosine. -- -- This routine computes the trigonometric cosine cos(x). */ double fp_cos(MPL *mpl, double x) { if (!(-1e6 <= x && x <= +1e6)) error(mpl, "cos(%.*g); argument too large", DBL_DIG, x); return cos(x); } /*---------------------------------------------------------------------- -- fp_atan - floating-point trigonometric arctangent. -- -- This routine computes the trigonometric arctangent atan(x). */ double fp_atan(MPL *mpl, double x) { xassert(mpl == mpl); return atan(x); } /*---------------------------------------------------------------------- -- fp_atan2 - floating-point trigonometric arctangent. -- -- This routine computes the trigonometric arctangent atan(y / x). */ double fp_atan2(MPL *mpl, double y, double x) { xassert(mpl == mpl); return atan2(y, x); } /*---------------------------------------------------------------------- -- fp_round - round floating-point value to n fractional digits. -- -- This routine rounds given floating-point value x to n fractional -- digits with the formula: -- -- round(x, n) = floor(x * 10^n + 0.5) / 10^n. -- -- The parameter n is assumed to be integer. */ double fp_round(MPL *mpl, double x, double n) { double ten_to_n; if (n != floor(n)) error(mpl, "round(%.*g, %.*g); non-integer second argument", DBL_DIG, x, DBL_DIG, n); if (n <= DBL_DIG + 2) { ten_to_n = pow(10.0, n); if (fabs(x) < (0.999 * DBL_MAX) / ten_to_n) { x = floor(x * ten_to_n + 0.5); if (x != 0.0) x /= ten_to_n; } } return x; } /*---------------------------------------------------------------------- -- fp_trunc - truncate floating-point value to n fractional digits. -- -- This routine truncates given floating-point value x to n fractional -- digits with the formula: -- -- ( floor(x * 10^n) / 10^n, if x >= 0 -- trunc(x, n) = < -- ( ceil(x * 10^n) / 10^n, if x < 0 -- -- The parameter n is assumed to be integer. */ double fp_trunc(MPL *mpl, double x, double n) { double ten_to_n; if (n != floor(n)) error(mpl, "trunc(%.*g, %.*g); non-integer second argument", DBL_DIG, x, DBL_DIG, n); if (n <= DBL_DIG + 2) { ten_to_n = pow(10.0, n); if (fabs(x) < (0.999 * DBL_MAX) / ten_to_n) { x = (x >= 0.0 ? floor(x * ten_to_n) : ceil(x * ten_to_n)); if (x != 0.0) x /= ten_to_n; } } return x; } /**********************************************************************/ /* * * PSEUDO-RANDOM NUMBER GENERATORS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- fp_irand224 - pseudo-random integer in the range [0, 2^24). -- -- This routine returns a next pseudo-random integer (converted to -- floating-point) which is uniformly distributed between 0 and 2^24-1, -- inclusive. */ #define two_to_the_24 0x1000000 double fp_irand224(MPL *mpl) { return (double)rng_unif_rand(mpl->rand, two_to_the_24); } /*---------------------------------------------------------------------- -- fp_uniform01 - pseudo-random number in the range [0, 1). -- -- This routine returns a next pseudo-random number which is uniformly -- distributed in the range [0, 1). */ #define two_to_the_31 ((unsigned int)0x80000000) double fp_uniform01(MPL *mpl) { return (double)rng_next_rand(mpl->rand) / (double)two_to_the_31; } /*---------------------------------------------------------------------- -- fp_uniform - pseudo-random number in the range [a, b). -- -- This routine returns a next pseudo-random number which is uniformly -- distributed in the range [a, b). */ double fp_uniform(MPL *mpl, double a, double b) { double x; if (a >= b) error(mpl, "Uniform(%.*g, %.*g); invalid range", DBL_DIG, a, DBL_DIG, b); x = fp_uniform01(mpl); #if 0 x = a * (1.0 - x) + b * x; #else x = fp_add(mpl, a * (1.0 - x), b * x); #endif return x; } /*---------------------------------------------------------------------- -- fp_normal01 - Gaussian random variate with mu = 0 and sigma = 1. -- -- This routine returns a Gaussian random variate with zero mean and -- unit standard deviation. The polar (Box-Mueller) method is used. -- -- This code is a modified version of the routine gsl_ran_gaussian from -- the GNU Scientific Library Version 1.0. */ double fp_normal01(MPL *mpl) { double x, y, r2; do { /* choose x, y in uniform square (-1,-1) to (+1,+1) */ x = -1.0 + 2.0 * fp_uniform01(mpl); y = -1.0 + 2.0 * fp_uniform01(mpl); /* see if it is in the unit circle */ r2 = x * x + y * y; } while (r2 > 1.0 || r2 == 0.0); /* Box-Muller transform */ return y * sqrt(-2.0 * log (r2) / r2); } /*---------------------------------------------------------------------- -- fp_normal - Gaussian random variate with specified mu and sigma. -- -- This routine returns a Gaussian random variate with mean mu and -- standard deviation sigma. */ double fp_normal(MPL *mpl, double mu, double sigma) { double x; #if 0 x = mu + sigma * fp_normal01(mpl); #else x = fp_add(mpl, mu, fp_mul(mpl, sigma, fp_normal01(mpl))); #endif return x; } /**********************************************************************/ /* * * SEGMENTED CHARACTER STRINGS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- create_string - create character string. -- -- This routine creates a segmented character string, which is exactly -- equivalent to specified character string. */ STRING *create_string ( MPL *mpl, char buf[MAX_LENGTH+1] /* not changed */ ) #if 0 { STRING *head, *tail; int i, j; xassert(buf != NULL); xassert(strlen(buf) <= MAX_LENGTH); head = tail = dmp_get_atom(mpl->strings, sizeof(STRING)); for (i = j = 0; ; i++) { if ((tail->seg[j++] = buf[i]) == '\0') break; if (j == STRSEG_SIZE) tail = (tail->next = dmp_get_atom(mpl->strings, sizeof(STRING))), j = 0; } tail->next = NULL; return head; } #else { STRING *str; xassert(strlen(buf) <= MAX_LENGTH); str = dmp_get_atom(mpl->strings, strlen(buf)+1); strcpy(str, buf); return str; } #endif /*---------------------------------------------------------------------- -- copy_string - make copy of character string. -- -- This routine returns an exact copy of segmented character string. */ STRING *copy_string ( MPL *mpl, STRING *str /* not changed */ ) #if 0 { STRING *head, *tail; xassert(str != NULL); head = tail = dmp_get_atom(mpl->strings, sizeof(STRING)); for (; str != NULL; str = str->next) { memcpy(tail->seg, str->seg, STRSEG_SIZE); if (str->next != NULL) tail = (tail->next = dmp_get_atom(mpl->strings, sizeof(STRING))); } tail->next = NULL; return head; } #else { xassert(mpl == mpl); return create_string(mpl, str); } #endif /*---------------------------------------------------------------------- -- compare_strings - compare one character string with another. -- -- This routine compares one segmented character strings with another -- and returns the result of comparison as follows: -- -- = 0 - both strings are identical; -- < 0 - the first string precedes the second one; -- > 0 - the first string follows the second one. */ int compare_strings ( MPL *mpl, STRING *str1, /* not changed */ STRING *str2 /* not changed */ ) #if 0 { int j, c1, c2; xassert(mpl == mpl); for (;; str1 = str1->next, str2 = str2->next) { xassert(str1 != NULL); xassert(str2 != NULL); for (j = 0; j < STRSEG_SIZE; j++) { c1 = (unsigned char)str1->seg[j]; c2 = (unsigned char)str2->seg[j]; if (c1 < c2) return -1; if (c1 > c2) return +1; if (c1 == '\0') goto done; } } done: return 0; } #else { xassert(mpl == mpl); return strcmp(str1, str2); } #endif /*---------------------------------------------------------------------- -- fetch_string - extract content of character string. -- -- This routine returns a character string, which is exactly equivalent -- to specified segmented character string. */ char *fetch_string ( MPL *mpl, STRING *str, /* not changed */ char buf[MAX_LENGTH+1] /* modified */ ) #if 0 { int i, j; xassert(mpl == mpl); xassert(buf != NULL); for (i = 0; ; str = str->next) { xassert(str != NULL); for (j = 0; j < STRSEG_SIZE; j++) if ((buf[i++] = str->seg[j]) == '\0') goto done; } done: xassert(strlen(buf) <= MAX_LENGTH); return buf; } #else { xassert(mpl == mpl); return strcpy(buf, str); } #endif /*---------------------------------------------------------------------- -- delete_string - delete character string. -- -- This routine deletes specified segmented character string. */ void delete_string ( MPL *mpl, STRING *str /* destroyed */ ) #if 0 { STRING *temp; xassert(str != NULL); while (str != NULL) { temp = str; str = str->next; dmp_free_atom(mpl->strings, temp, sizeof(STRING)); } return; } #else { dmp_free_atom(mpl->strings, str, strlen(str)+1); return; } #endif /**********************************************************************/ /* * * SYMBOLS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- create_symbol_num - create symbol of numeric type. -- -- This routine creates a symbol, which has a numeric value specified -- as floating-point number. */ SYMBOL *create_symbol_num(MPL *mpl, double num) { SYMBOL *sym; sym = dmp_get_atom(mpl->symbols, sizeof(SYMBOL)); sym->num = num; sym->str = NULL; return sym; } /*---------------------------------------------------------------------- -- create_symbol_str - create symbol of abstract type. -- -- This routine creates a symbol, which has an abstract value specified -- as segmented character string. */ SYMBOL *create_symbol_str ( MPL *mpl, STRING *str /* destroyed */ ) { SYMBOL *sym; xassert(str != NULL); sym = dmp_get_atom(mpl->symbols, sizeof(SYMBOL)); sym->num = 0.0; sym->str = str; return sym; } /*---------------------------------------------------------------------- -- copy_symbol - make copy of symbol. -- -- This routine returns an exact copy of symbol. */ SYMBOL *copy_symbol ( MPL *mpl, SYMBOL *sym /* not changed */ ) { SYMBOL *copy; xassert(sym != NULL); copy = dmp_get_atom(mpl->symbols, sizeof(SYMBOL)); if (sym->str == NULL) { copy->num = sym->num; copy->str = NULL; } else { copy->num = 0.0; copy->str = copy_string(mpl, sym->str); } return copy; } /*---------------------------------------------------------------------- -- compare_symbols - compare one symbol with another. -- -- This routine compares one symbol with another and returns the result -- of comparison as follows: -- -- = 0 - both symbols are identical; -- < 0 - the first symbol precedes the second one; -- > 0 - the first symbol follows the second one. -- -- Note that the linear order, in which symbols follow each other, is -- implementation-dependent. It may be not an alphabetical order. */ int compare_symbols ( MPL *mpl, SYMBOL *sym1, /* not changed */ SYMBOL *sym2 /* not changed */ ) { xassert(sym1 != NULL); xassert(sym2 != NULL); /* let all numeric quantities precede all symbolic quantities */ if (sym1->str == NULL && sym2->str == NULL) { if (sym1->num < sym2->num) return -1; if (sym1->num > sym2->num) return +1; return 0; } if (sym1->str == NULL) return -1; if (sym2->str == NULL) return +1; return compare_strings(mpl, sym1->str, sym2->str); } /*---------------------------------------------------------------------- -- delete_symbol - delete symbol. -- -- This routine deletes specified symbol. */ void delete_symbol ( MPL *mpl, SYMBOL *sym /* destroyed */ ) { xassert(sym != NULL); if (sym->str != NULL) delete_string(mpl, sym->str); dmp_free_atom(mpl->symbols, sym, sizeof(SYMBOL)); return; } /*---------------------------------------------------------------------- -- format_symbol - format symbol for displaying or printing. -- -- This routine converts specified symbol to a charater string, which -- is suitable for displaying or printing. -- -- The resultant string is never longer than 255 characters. If it gets -- longer, it is truncated from the right and appended by dots. */ char *format_symbol ( MPL *mpl, SYMBOL *sym /* not changed */ ) { char *buf = mpl->sym_buf; xassert(sym != NULL); if (sym->str == NULL) sprintf(buf, "%.*g", DBL_DIG, sym->num); else { char str[MAX_LENGTH+1]; int quoted, j, len; fetch_string(mpl, sym->str, str); if (!(isalpha((unsigned char)str[0]) || str[0] == '_')) quoted = 1; else { quoted = 0; for (j = 1; str[j] != '\0'; j++) { if (!(isalnum((unsigned char)str[j]) || strchr("+-._", (unsigned char)str[j]) != NULL)) { quoted = 1; break; } } } # define safe_append(c) \ (void)(len < 255 ? (buf[len++] = (char)(c)) : 0) buf[0] = '\0', len = 0; if (quoted) safe_append('\''); for (j = 0; str[j] != '\0'; j++) { if (quoted && str[j] == '\'') safe_append('\''); safe_append(str[j]); } if (quoted) safe_append('\''); # undef safe_append buf[len] = '\0'; if (len == 255) strcpy(buf+252, "..."); } xassert(strlen(buf) <= 255); return buf; } /*---------------------------------------------------------------------- -- concat_symbols - concatenate one symbol with another. -- -- This routine concatenates values of two given symbols and assigns -- the resultant character string to a new symbol, which is returned on -- exit. Both original symbols are destroyed. */ SYMBOL *concat_symbols ( MPL *mpl, SYMBOL *sym1, /* destroyed */ SYMBOL *sym2 /* destroyed */ ) { char str1[MAX_LENGTH+1], str2[MAX_LENGTH+1]; xassert(MAX_LENGTH >= DBL_DIG + DBL_DIG); if (sym1->str == NULL) sprintf(str1, "%.*g", DBL_DIG, sym1->num); else fetch_string(mpl, sym1->str, str1); if (sym2->str == NULL) sprintf(str2, "%.*g", DBL_DIG, sym2->num); else fetch_string(mpl, sym2->str, str2); if (strlen(str1) + strlen(str2) > MAX_LENGTH) { char buf[255+1]; strcpy(buf, format_symbol(mpl, sym1)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s & %s; resultant symbol exceeds %d characters", buf, format_symbol(mpl, sym2), MAX_LENGTH); } delete_symbol(mpl, sym1); delete_symbol(mpl, sym2); return create_symbol_str(mpl, create_string(mpl, strcat(str1, str2))); } /**********************************************************************/ /* * * N-TUPLES * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- create_tuple - create n-tuple. -- -- This routine creates a n-tuple, which initially has no components, -- i.e. which is 0-tuple. */ TUPLE *create_tuple(MPL *mpl) { TUPLE *tuple; xassert(mpl == mpl); tuple = NULL; return tuple; } /*---------------------------------------------------------------------- -- expand_tuple - append symbol to n-tuple. -- -- This routine expands n-tuple appending to it a given symbol, which -- becomes its new last component. */ TUPLE *expand_tuple ( MPL *mpl, TUPLE *tuple, /* destroyed */ SYMBOL *sym /* destroyed */ ) { TUPLE *tail, *temp; xassert(sym != NULL); /* create a new component */ tail = dmp_get_atom(mpl->tuples, sizeof(TUPLE)); tail->sym = sym; tail->next = NULL; /* and append it to the component list */ if (tuple == NULL) tuple = tail; else { for (temp = tuple; temp->next != NULL; temp = temp->next); temp->next = tail; } return tuple; } /*---------------------------------------------------------------------- -- tuple_dimen - determine dimension of n-tuple. -- -- This routine returns dimension of n-tuple, i.e. number of components -- in the n-tuple. */ int tuple_dimen ( MPL *mpl, TUPLE *tuple /* not changed */ ) { TUPLE *temp; int dim = 0; xassert(mpl == mpl); for (temp = tuple; temp != NULL; temp = temp->next) dim++; return dim; } /*---------------------------------------------------------------------- -- copy_tuple - make copy of n-tuple. -- -- This routine returns an exact copy of n-tuple. */ TUPLE *copy_tuple ( MPL *mpl, TUPLE *tuple /* not changed */ ) { TUPLE *head, *tail; if (tuple == NULL) head = NULL; else { head = tail = dmp_get_atom(mpl->tuples, sizeof(TUPLE)); for (; tuple != NULL; tuple = tuple->next) { xassert(tuple->sym != NULL); tail->sym = copy_symbol(mpl, tuple->sym); if (tuple->next != NULL) tail = (tail->next = dmp_get_atom(mpl->tuples, sizeof(TUPLE))); } tail->next = NULL; } return head; } /*---------------------------------------------------------------------- -- compare_tuples - compare one n-tuple with another. -- -- This routine compares two given n-tuples, which must have the same -- dimension (not checked for the sake of efficiency), and returns one -- of the following codes: -- -- = 0 - both n-tuples are identical; -- < 0 - the first n-tuple precedes the second one; -- > 0 - the first n-tuple follows the second one. -- -- Note that the linear order, in which n-tuples follow each other, is -- implementation-dependent. It may be not an alphabetical order. */ int compare_tuples ( MPL *mpl, TUPLE *tuple1, /* not changed */ TUPLE *tuple2 /* not changed */ ) { TUPLE *item1, *item2; int ret; xassert(mpl == mpl); for (item1 = tuple1, item2 = tuple2; item1 != NULL; item1 = item1->next, item2 = item2->next) { xassert(item2 != NULL); xassert(item1->sym != NULL); xassert(item2->sym != NULL); ret = compare_symbols(mpl, item1->sym, item2->sym); if (ret != 0) return ret; } xassert(item2 == NULL); return 0; } /*---------------------------------------------------------------------- -- build_subtuple - build subtuple of given n-tuple. -- -- This routine builds subtuple, which consists of first dim components -- of given n-tuple. */ TUPLE *build_subtuple ( MPL *mpl, TUPLE *tuple, /* not changed */ int dim ) { TUPLE *head, *temp; int j; head = create_tuple(mpl); for (j = 1, temp = tuple; j <= dim; j++, temp = temp->next) { xassert(temp != NULL); head = expand_tuple(mpl, head, copy_symbol(mpl, temp->sym)); } return head; } /*---------------------------------------------------------------------- -- delete_tuple - delete n-tuple. -- -- This routine deletes specified n-tuple. */ void delete_tuple ( MPL *mpl, TUPLE *tuple /* destroyed */ ) { TUPLE *temp; while (tuple != NULL) { temp = tuple; tuple = temp->next; xassert(temp->sym != NULL); delete_symbol(mpl, temp->sym); dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE)); } return; } /*---------------------------------------------------------------------- -- format_tuple - format n-tuple for displaying or printing. -- -- This routine converts specified n-tuple to a character string, which -- is suitable for displaying or printing. -- -- The resultant string is never longer than 255 characters. If it gets -- longer, it is truncated from the right and appended by dots. */ char *format_tuple ( MPL *mpl, int c, TUPLE *tuple /* not changed */ ) { TUPLE *temp; int dim, j, len; char *buf = mpl->tup_buf, str[255+1], *save; # define safe_append(c) \ (void)(len < 255 ? (buf[len++] = (char)(c)) : 0) buf[0] = '\0', len = 0; dim = tuple_dimen(mpl, tuple); if (c == '[' && dim > 0) safe_append('['); if (c == '(' && dim > 1) safe_append('('); for (temp = tuple; temp != NULL; temp = temp->next) { if (temp != tuple) safe_append(','); xassert(temp->sym != NULL); save = mpl->sym_buf; mpl->sym_buf = str; format_symbol(mpl, temp->sym); mpl->sym_buf = save; xassert(strlen(str) < sizeof(str)); for (j = 0; str[j] != '\0'; j++) safe_append(str[j]); } if (c == '[' && dim > 0) safe_append(']'); if (c == '(' && dim > 1) safe_append(')'); # undef safe_append buf[len] = '\0'; if (len == 255) strcpy(buf+252, "..."); xassert(strlen(buf) <= 255); return buf; } /**********************************************************************/ /* * * ELEMENTAL SETS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- create_elemset - create elemental set. -- -- This routine creates an elemental set, whose members are n-tuples of -- specified dimension. Being created the set is initially empty. */ ELEMSET *create_elemset(MPL *mpl, int dim) { ELEMSET *set; xassert(dim > 0); set = create_array(mpl, A_NONE, dim); return set; } /*---------------------------------------------------------------------- -- find_tuple - check if elemental set contains given n-tuple. -- -- This routine finds given n-tuple in specified elemental set in order -- to check if the set contains that n-tuple. If the n-tuple is found, -- the routine returns pointer to corresponding array member. Otherwise -- null pointer is returned. */ MEMBER *find_tuple ( MPL *mpl, ELEMSET *set, /* not changed */ TUPLE *tuple /* not changed */ ) { xassert(set != NULL); xassert(set->type == A_NONE); xassert(set->dim == tuple_dimen(mpl, tuple)); return find_member(mpl, set, tuple); } /*---------------------------------------------------------------------- -- add_tuple - add new n-tuple to elemental set. -- -- This routine adds given n-tuple to specified elemental set. -- -- For the sake of efficiency this routine doesn't check whether the -- set already contains the same n-tuple or not. Therefore the calling -- program should use the routine find_tuple (if necessary) in order to -- make sure that the given n-tuple is not contained in the set, since -- duplicate n-tuples within the same set are not allowed. */ MEMBER *add_tuple ( MPL *mpl, ELEMSET *set, /* modified */ TUPLE *tuple /* destroyed */ ) { MEMBER *memb; xassert(set != NULL); xassert(set->type == A_NONE); xassert(set->dim == tuple_dimen(mpl, tuple)); memb = add_member(mpl, set, tuple); memb->value.none = NULL; return memb; } /*---------------------------------------------------------------------- -- check_then_add - check and add new n-tuple to elemental set. -- -- This routine is equivalent to the routine add_tuple except that it -- does check for duplicate n-tuples. */ MEMBER *check_then_add ( MPL *mpl, ELEMSET *set, /* modified */ TUPLE *tuple /* destroyed */ ) { if (find_tuple(mpl, set, tuple) != NULL) error(mpl, "duplicate tuple %s detected", format_tuple(mpl, '(', tuple)); return add_tuple(mpl, set, tuple); } /*---------------------------------------------------------------------- -- copy_elemset - make copy of elemental set. -- -- This routine makes an exact copy of elemental set. */ ELEMSET *copy_elemset ( MPL *mpl, ELEMSET *set /* not changed */ ) { ELEMSET *copy; MEMBER *memb; xassert(set != NULL); xassert(set->type == A_NONE); xassert(set->dim > 0); copy = create_elemset(mpl, set->dim); for (memb = set->head; memb != NULL; memb = memb->next) add_tuple(mpl, copy, copy_tuple(mpl, memb->tuple)); return copy; } /*---------------------------------------------------------------------- -- delete_elemset - delete elemental set. -- -- This routine deletes specified elemental set. */ void delete_elemset ( MPL *mpl, ELEMSET *set /* destroyed */ ) { xassert(set != NULL); xassert(set->type == A_NONE); delete_array(mpl, set); return; } /*---------------------------------------------------------------------- -- arelset_size - compute size of "arithmetic" elemental set. -- -- This routine computes the size of "arithmetic" elemental set, which -- is specified in the form of arithmetic progression: -- -- { t0 .. tf by dt }. -- -- The size is computed using the formula: -- -- n = max(0, floor((tf - t0) / dt) + 1). */ int arelset_size(MPL *mpl, double t0, double tf, double dt) { double temp; if (dt == 0.0) error(mpl, "%.*g .. %.*g by %.*g; zero stride not allowed", DBL_DIG, t0, DBL_DIG, tf, DBL_DIG, dt); if (tf > 0.0 && t0 < 0.0 && tf > + 0.999 * DBL_MAX + t0) temp = +DBL_MAX; else if (tf < 0.0 && t0 > 0.0 && tf < - 0.999 * DBL_MAX + t0) temp = -DBL_MAX; else temp = tf - t0; if (fabs(dt) < 1.0 && fabs(temp) > (0.999 * DBL_MAX) * fabs(dt)) { if (temp > 0.0 && dt > 0.0 || temp < 0.0 && dt < 0.0) temp = +DBL_MAX; else temp = 0.0; } else { temp = floor(temp / dt) + 1.0; if (temp < 0.0) temp = 0.0; } xassert(temp >= 0.0); if (temp > (double)(INT_MAX - 1)) error(mpl, "%.*g .. %.*g by %.*g; set too large", DBL_DIG, t0, DBL_DIG, tf, DBL_DIG, dt); return (int)(temp + 0.5); } /*---------------------------------------------------------------------- -- arelset_member - compute member of "arithmetic" elemental set. -- -- This routine returns a numeric value of symbol, which is equivalent -- to j-th member of given "arithmetic" elemental set specified in the -- form of arithmetic progression: -- -- { t0 .. tf by dt }. -- -- The symbol value is computed with the formula: -- -- j-th member = t0 + (j - 1) * dt, -- -- The number j must satisfy to the restriction 1 <= j <= n, where n is -- the set size computed by the routine arelset_size. */ double arelset_member(MPL *mpl, double t0, double tf, double dt, int j) { xassert(1 <= j && j <= arelset_size(mpl, t0, tf, dt)); return t0 + (double)(j - 1) * dt; } /*---------------------------------------------------------------------- -- create_arelset - create "arithmetic" elemental set. -- -- This routine creates "arithmetic" elemental set, which is specified -- in the form of arithmetic progression: -- -- { t0 .. tf by dt }. -- -- Components of this set are 1-tuples. */ ELEMSET *create_arelset(MPL *mpl, double t0, double tf, double dt) { ELEMSET *set; int j, n; set = create_elemset(mpl, 1); n = arelset_size(mpl, t0, tf, dt); for (j = 1; j <= n; j++) { add_tuple ( mpl, set, expand_tuple ( mpl, create_tuple(mpl), create_symbol_num ( mpl, arelset_member(mpl, t0, tf, dt, j) ) ) ); } return set; } /*---------------------------------------------------------------------- -- set_union - union of two elemental sets. -- -- This routine computes the union: -- -- X U Y = { j | (j in X) or (j in Y) }, -- -- where X and Y are given elemental sets (destroyed on exit). */ ELEMSET *set_union ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ) { MEMBER *memb; xassert(X != NULL); xassert(X->type == A_NONE); xassert(X->dim > 0); xassert(Y != NULL); xassert(Y->type == A_NONE); xassert(Y->dim > 0); xassert(X->dim == Y->dim); for (memb = Y->head; memb != NULL; memb = memb->next) { if (find_tuple(mpl, X, memb->tuple) == NULL) add_tuple(mpl, X, copy_tuple(mpl, memb->tuple)); } delete_elemset(mpl, Y); return X; } /*---------------------------------------------------------------------- -- set_diff - difference between two elemental sets. -- -- This routine computes the difference: -- -- X \ Y = { j | (j in X) and (j not in Y) }, -- -- where X and Y are given elemental sets (destroyed on exit). */ ELEMSET *set_diff ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ) { ELEMSET *Z; MEMBER *memb; xassert(X != NULL); xassert(X->type == A_NONE); xassert(X->dim > 0); xassert(Y != NULL); xassert(Y->type == A_NONE); xassert(Y->dim > 0); xassert(X->dim == Y->dim); Z = create_elemset(mpl, X->dim); for (memb = X->head; memb != NULL; memb = memb->next) { if (find_tuple(mpl, Y, memb->tuple) == NULL) add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); } delete_elemset(mpl, X); delete_elemset(mpl, Y); return Z; } /*---------------------------------------------------------------------- -- set_symdiff - symmetric difference between two elemental sets. -- -- This routine computes the symmetric difference: -- -- X (+) Y = (X \ Y) U (Y \ X), -- -- where X and Y are given elemental sets (destroyed on exit). */ ELEMSET *set_symdiff ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ) { ELEMSET *Z; MEMBER *memb; xassert(X != NULL); xassert(X->type == A_NONE); xassert(X->dim > 0); xassert(Y != NULL); xassert(Y->type == A_NONE); xassert(Y->dim > 0); xassert(X->dim == Y->dim); /* Z := X \ Y */ Z = create_elemset(mpl, X->dim); for (memb = X->head; memb != NULL; memb = memb->next) { if (find_tuple(mpl, Y, memb->tuple) == NULL) add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); } /* Z := Z U (Y \ X) */ for (memb = Y->head; memb != NULL; memb = memb->next) { if (find_tuple(mpl, X, memb->tuple) == NULL) add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); } delete_elemset(mpl, X); delete_elemset(mpl, Y); return Z; } /*---------------------------------------------------------------------- -- set_inter - intersection of two elemental sets. -- -- This routine computes the intersection: -- -- X ^ Y = { j | (j in X) and (j in Y) }, -- -- where X and Y are given elemental sets (destroyed on exit). */ ELEMSET *set_inter ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ) { ELEMSET *Z; MEMBER *memb; xassert(X != NULL); xassert(X->type == A_NONE); xassert(X->dim > 0); xassert(Y != NULL); xassert(Y->type == A_NONE); xassert(Y->dim > 0); xassert(X->dim == Y->dim); Z = create_elemset(mpl, X->dim); for (memb = X->head; memb != NULL; memb = memb->next) { if (find_tuple(mpl, Y, memb->tuple) != NULL) add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple)); } delete_elemset(mpl, X); delete_elemset(mpl, Y); return Z; } /*---------------------------------------------------------------------- -- set_cross - cross (Cartesian) product of two elemental sets. -- -- This routine computes the cross (Cartesian) product: -- -- X x Y = { (i,j) | (i in X) and (j in Y) }, -- -- where X and Y are given elemental sets (destroyed on exit). */ ELEMSET *set_cross ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ) { ELEMSET *Z; MEMBER *memx, *memy; TUPLE *tuple, *temp; xassert(X != NULL); xassert(X->type == A_NONE); xassert(X->dim > 0); xassert(Y != NULL); xassert(Y->type == A_NONE); xassert(Y->dim > 0); Z = create_elemset(mpl, X->dim + Y->dim); for (memx = X->head; memx != NULL; memx = memx->next) { for (memy = Y->head; memy != NULL; memy = memy->next) { tuple = copy_tuple(mpl, memx->tuple); for (temp = memy->tuple; temp != NULL; temp = temp->next) tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, temp->sym)); add_tuple(mpl, Z, tuple); } } delete_elemset(mpl, X); delete_elemset(mpl, Y); return Z; } /**********************************************************************/ /* * * ELEMENTAL VARIABLES * * */ /**********************************************************************/ /* (there are no specific routines for elemental variables) */ /**********************************************************************/ /* * * LINEAR FORMS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- constant_term - create constant term. -- -- This routine creates the linear form, which is a constant term. */ FORMULA *constant_term(MPL *mpl, double coef) { FORMULA *form; if (coef == 0.0) form = NULL; else { form = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); form->coef = coef; form->var = NULL; form->next = NULL; } return form; } /*---------------------------------------------------------------------- -- single_variable - create single variable. -- -- This routine creates the linear form, which is a single elemental -- variable. */ FORMULA *single_variable ( MPL *mpl, ELEMVAR *var /* referenced */ ) { FORMULA *form; xassert(var != NULL); form = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); form->coef = 1.0; form->var = var; form->next = NULL; return form; } /*---------------------------------------------------------------------- -- copy_formula - make copy of linear form. -- -- This routine returns an exact copy of linear form. */ FORMULA *copy_formula ( MPL *mpl, FORMULA *form /* not changed */ ) { FORMULA *head, *tail; if (form == NULL) head = NULL; else { head = tail = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); for (; form != NULL; form = form->next) { tail->coef = form->coef; tail->var = form->var; if (form->next != NULL) tail = (tail->next = dmp_get_atom(mpl->formulae, sizeof(FORMULA))); } tail->next = NULL; } return head; } /*---------------------------------------------------------------------- -- delete_formula - delete linear form. -- -- This routine deletes specified linear form. */ void delete_formula ( MPL *mpl, FORMULA *form /* destroyed */ ) { FORMULA *temp; while (form != NULL) { temp = form; form = form->next; dmp_free_atom(mpl->formulae, temp, sizeof(FORMULA)); } return; } /*---------------------------------------------------------------------- -- linear_comb - linear combination of two linear forms. -- -- This routine computes the linear combination: -- -- a * fx + b * fy, -- -- where a and b are numeric coefficients, fx and fy are linear forms -- (destroyed on exit). */ FORMULA *linear_comb ( MPL *mpl, double a, FORMULA *fx, /* destroyed */ double b, FORMULA *fy /* destroyed */ ) { FORMULA *form = NULL, *term, *temp; double c0 = 0.0; for (term = fx; term != NULL; term = term->next) { if (term->var == NULL) c0 = fp_add(mpl, c0, fp_mul(mpl, a, term->coef)); else term->var->temp = fp_add(mpl, term->var->temp, fp_mul(mpl, a, term->coef)); } for (term = fy; term != NULL; term = term->next) { if (term->var == NULL) c0 = fp_add(mpl, c0, fp_mul(mpl, b, term->coef)); else term->var->temp = fp_add(mpl, term->var->temp, fp_mul(mpl, b, term->coef)); } for (term = fx; term != NULL; term = term->next) { if (term->var != NULL && term->var->temp != 0.0) { temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); temp->coef = term->var->temp, temp->var = term->var; temp->next = form, form = temp; term->var->temp = 0.0; } } for (term = fy; term != NULL; term = term->next) { if (term->var != NULL && term->var->temp != 0.0) { temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); temp->coef = term->var->temp, temp->var = term->var; temp->next = form, form = temp; term->var->temp = 0.0; } } if (c0 != 0.0) { temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA)); temp->coef = c0, temp->var = NULL; temp->next = form, form = temp; } delete_formula(mpl, fx); delete_formula(mpl, fy); return form; } /*---------------------------------------------------------------------- -- remove_constant - remove constant term from linear form. -- -- This routine removes constant term from linear form and stores its -- value to given location. */ FORMULA *remove_constant ( MPL *mpl, FORMULA *form, /* destroyed */ double *coef /* modified */ ) { FORMULA *head = NULL, *temp; *coef = 0.0; while (form != NULL) { temp = form; form = form->next; if (temp->var == NULL) { /* constant term */ *coef = fp_add(mpl, *coef, temp->coef); dmp_free_atom(mpl->formulae, temp, sizeof(FORMULA)); } else { /* linear term */ temp->next = head; head = temp; } } return head; } /*---------------------------------------------------------------------- -- reduce_terms - reduce identical terms in linear form. -- -- This routine reduces identical terms in specified linear form. */ FORMULA *reduce_terms ( MPL *mpl, FORMULA *form /* destroyed */ ) { FORMULA *term, *next_term; double c0 = 0.0; for (term = form; term != NULL; term = term->next) { if (term->var == NULL) c0 = fp_add(mpl, c0, term->coef); else term->var->temp = fp_add(mpl, term->var->temp, term->coef); } next_term = form, form = NULL; for (term = next_term; term != NULL; term = next_term) { next_term = term->next; if (term->var == NULL && c0 != 0.0) { term->coef = c0, c0 = 0.0; term->next = form, form = term; } else if (term->var != NULL && term->var->temp != 0.0) { term->coef = term->var->temp, term->var->temp = 0.0; term->next = form, form = term; } else dmp_free_atom(mpl->formulae, term, sizeof(FORMULA)); } return form; } /**********************************************************************/ /* * * ELEMENTAL CONSTRAINTS * * */ /**********************************************************************/ /* (there are no specific routines for elemental constraints) */ /**********************************************************************/ /* * * GENERIC VALUES * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- delete_value - delete generic value. -- -- This routine deletes specified generic value. -- -- NOTE: The generic value to be deleted must be valid. */ void delete_value ( MPL *mpl, int type, VALUE *value /* content destroyed */ ) { xassert(value != NULL); switch (type) { case A_NONE: value->none = NULL; break; case A_NUMERIC: value->num = 0.0; break; case A_SYMBOLIC: delete_symbol(mpl, value->sym), value->sym = NULL; break; case A_LOGICAL: value->bit = 0; break; case A_TUPLE: delete_tuple(mpl, value->tuple), value->tuple = NULL; break; case A_ELEMSET: delete_elemset(mpl, value->set), value->set = NULL; break; case A_ELEMVAR: value->var = NULL; break; case A_FORMULA: delete_formula(mpl, value->form), value->form = NULL; break; case A_ELEMCON: value->con = NULL; break; default: xassert(type != type); } return; } /**********************************************************************/ /* * * SYMBOLICALLY INDEXED ARRAYS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- create_array - create array. -- -- This routine creates an array of specified type and dimension. Being -- created the array is initially empty. -- -- The type indicator determines generic values, which can be assigned -- to the array members: -- -- A_NONE - none (members have no assigned values) -- A_NUMERIC - floating-point numbers -- A_SYMBOLIC - symbols -- A_ELEMSET - elemental sets -- A_ELEMVAR - elemental variables -- A_ELEMCON - elemental constraints -- -- The dimension may be 0, in which case the array consists of the only -- member (such arrays represent 0-dimensional objects). */ ARRAY *create_array(MPL *mpl, int type, int dim) { ARRAY *array; xassert(type == A_NONE || type == A_NUMERIC || type == A_SYMBOLIC || type == A_ELEMSET || type == A_ELEMVAR || type == A_ELEMCON); xassert(dim >= 0); array = dmp_get_atom(mpl->arrays, sizeof(ARRAY)); array->type = type; array->dim = dim; array->size = 0; array->head = NULL; array->tail = NULL; array->tree = NULL; array->prev = NULL; array->next = mpl->a_list; /* include the array in the global array list */ if (array->next != NULL) array->next->prev = array; mpl->a_list = array; return array; } /*---------------------------------------------------------------------- -- find_member - find array member with given n-tuple. -- -- This routine finds an array member, which has given n-tuple. If the -- array is short, the linear search is used. Otherwise the routine -- autimatically creates the search tree (i.e. the array index) to find -- members for logarithmic time. */ static int compare_member_tuples(void *info, const void *key1, const void *key2) { /* this is an auxiliary routine used to compare keys, which are n-tuples assigned to array members */ return compare_tuples((MPL *)info, (TUPLE *)key1, (TUPLE *)key2); } MEMBER *find_member ( MPL *mpl, ARRAY *array, /* not changed */ TUPLE *tuple /* not changed */ ) { MEMBER *memb; xassert(array != NULL); /* the n-tuple must have the same dimension as the array */ xassert(tuple_dimen(mpl, tuple) == array->dim); /* if the array is large enough, create the search tree and index all existing members of the array */ if (array->size > 30 && array->tree == NULL) { array->tree = avl_create_tree(compare_member_tuples, mpl); for (memb = array->head; memb != NULL; memb = memb->next) avl_set_node_link(avl_insert_node(array->tree, memb->tuple), (void *)memb); } /* find a member, which has the given tuple */ if (array->tree == NULL) { /* the search tree doesn't exist; use the linear search */ for (memb = array->head; memb != NULL; memb = memb->next) if (compare_tuples(mpl, memb->tuple, tuple) == 0) break; } else { /* the search tree exists; use the binary search */ AVLNODE *node; node = avl_find_node(array->tree, tuple); memb = (MEMBER *)(node == NULL ? NULL : avl_get_node_link(node)); } return memb; } /*---------------------------------------------------------------------- -- add_member - add new member to array. -- -- This routine creates a new member with given n-tuple and adds it to -- specified array. -- -- For the sake of efficiency this routine doesn't check whether the -- array already contains a member with the given n-tuple or not. Thus, -- if necessary, the calling program should use the routine find_member -- in order to be sure that the array contains no member with the same -- n-tuple, because members with duplicate n-tuples are not allowed. -- -- This routine assigns no generic value to the new member, because the -- calling program must do that. */ MEMBER *add_member ( MPL *mpl, ARRAY *array, /* modified */ TUPLE *tuple /* destroyed */ ) { MEMBER *memb; xassert(array != NULL); /* the n-tuple must have the same dimension as the array */ xassert(tuple_dimen(mpl, tuple) == array->dim); /* create new member */ memb = dmp_get_atom(mpl->members, sizeof(MEMBER)); memb->tuple = tuple; memb->next = NULL; memset(&memb->value, '?', sizeof(VALUE)); /* and append it to the member list */ array->size++; if (array->head == NULL) array->head = memb; else array->tail->next = memb; array->tail = memb; /* if the search tree exists, index the new member */ if (array->tree != NULL) avl_set_node_link(avl_insert_node(array->tree, memb->tuple), (void *)memb); return memb; } /*---------------------------------------------------------------------- -- delete_array - delete array. -- -- This routine deletes specified array. -- -- Generic values assigned to the array members are not deleted by this -- routine. The calling program itself must delete all assigned generic -- values before deleting the array. */ void delete_array ( MPL *mpl, ARRAY *array /* destroyed */ ) { MEMBER *memb; xassert(array != NULL); /* delete all existing array members */ while (array->head != NULL) { memb = array->head; array->head = memb->next; delete_tuple(mpl, memb->tuple); dmp_free_atom(mpl->members, memb, sizeof(MEMBER)); } /* if the search tree exists, also delete it */ if (array->tree != NULL) avl_delete_tree(array->tree); /* remove the array from the global array list */ if (array->prev == NULL) mpl->a_list = array->next; else array->prev->next = array->next; if (array->next == NULL) ; else array->next->prev = array->prev; /* delete the array descriptor */ dmp_free_atom(mpl->arrays, array, sizeof(ARRAY)); return; } /**********************************************************************/ /* * * DOMAINS AND DUMMY INDICES * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- assign_dummy_index - assign new value to dummy index. -- -- This routine assigns new value to specified dummy index and, that is -- important, invalidates all temporary resultant values, which depends -- on that dummy index. */ void assign_dummy_index ( MPL *mpl, DOMAIN_SLOT *slot, /* modified */ SYMBOL *value /* not changed */ ) { CODE *leaf, *code; xassert(slot != NULL); xassert(value != NULL); /* delete the current value assigned to the dummy index */ if (slot->value != NULL) { /* if the current value and the new one are identical, actual assignment is not needed */ if (compare_symbols(mpl, slot->value, value) == 0) goto done; /* delete a symbol, which is the current value */ delete_symbol(mpl, slot->value), slot->value = NULL; } /* now walk through all the pseudo-codes with op = O_INDEX, which refer to the dummy index to be changed (these pseudo-codes are leaves in the forest of *all* expressions in the database) */ for (leaf = slot->list; leaf != NULL; leaf = leaf->arg.index. next) { xassert(leaf->op == O_INDEX); /* invalidate all resultant values, which depend on the dummy index, walking from the current leaf toward the root of the corresponding expression tree */ for (code = leaf; code != NULL; code = code->up) { if (code->valid) { /* invalidate and delete resultant value */ code->valid = 0; delete_value(mpl, code->type, &code->value); } } } /* assign new value to the dummy index */ slot->value = copy_symbol(mpl, value); done: return; } /*---------------------------------------------------------------------- -- update_dummy_indices - update current values of dummy indices. -- -- This routine assigns components of "backup" n-tuple to dummy indices -- of specified domain block. If no "backup" n-tuple is defined for the -- domain block, values of the dummy indices remain untouched. */ void update_dummy_indices ( MPL *mpl, DOMAIN_BLOCK *block /* not changed */ ) { DOMAIN_SLOT *slot; TUPLE *temp; if (block->backup != NULL) { for (slot = block->list, temp = block->backup; slot != NULL; slot = slot->next, temp = temp->next) { xassert(temp != NULL); xassert(temp->sym != NULL); assign_dummy_index(mpl, slot, temp->sym); } } return; } /*---------------------------------------------------------------------- -- enter_domain_block - enter domain block. -- -- Let specified domain block have the form: -- -- { ..., (j1, j2, ..., jn) in J, ... } -- -- where j1, j2, ..., jn are dummy indices, J is a basic set. -- -- This routine does the following: -- -- 1. Checks if the given n-tuple is a member of the basic set J. Note -- that J being *out of the scope* of the domain block cannot depend -- on the dummy indices in the same and inner domain blocks, so it -- can be computed before the dummy indices are assigned new values. -- If this check fails, the routine returns with non-zero code. -- -- 2. Saves current values of the dummy indices j1, j2, ..., jn. -- -- 3. Assigns new values, which are components of the given n-tuple, to -- the dummy indices j1, j2, ..., jn. If dimension of the n-tuple is -- larger than n, its extra components n+1, n+2, ... are not used. -- -- 4. Calls the formal routine func which either enters the next domain -- block or evaluates some code within the domain scope. -- -- 5. Restores former values of the dummy indices j1, j2, ..., jn. -- -- Since current values assigned to the dummy indices on entry to this -- routine are restored on exit, the formal routine func is allowed to -- call this routine recursively. */ int enter_domain_block ( MPL *mpl, DOMAIN_BLOCK *block, /* not changed */ TUPLE *tuple, /* not changed */ void *info, void (*func)(MPL *mpl, void *info) ) { TUPLE *backup; int ret = 0; /* check if the given n-tuple is a member of the basic set */ xassert(block->code != NULL); if (!is_member(mpl, block->code, tuple)) { ret = 1; goto done; } /* save reference to "backup" n-tuple, which was used to assign current values of the dummy indices (it is sufficient to save reference, not value, because that n-tuple is defined in some outer level of recursion and therefore cannot be changed on this and deeper recursive calls) */ backup = block->backup; /* set up new "backup" n-tuple, which defines new values of the dummy indices */ block->backup = tuple; /* assign new values to the dummy indices */ update_dummy_indices(mpl, block); /* call the formal routine that does the rest part of the job */ func(mpl, info); /* restore reference to the former "backup" n-tuple */ block->backup = backup; /* restore former values of the dummy indices; note that if the domain block just escaped has no other active instances which may exist due to recursion (it is indicated by a null pointer to the former n-tuple), former values of the dummy indices are undefined; therefore in this case the routine keeps currently assigned values of the dummy indices that involves keeping all dependent temporary results and thereby, if this domain block is not used recursively, allows improving efficiency */ update_dummy_indices(mpl, block); done: return ret; } /*---------------------------------------------------------------------- -- eval_within_domain - perform evaluation within domain scope. -- -- This routine assigns new values (symbols) to all dummy indices of -- specified domain and calls the formal routine func, which is used to -- evaluate some code in the domain scope. Each free dummy index in the -- domain is assigned a value specified in the corresponding component -- of given n-tuple. Non-free dummy indices are assigned values, which -- are computed by this routine. -- -- Number of components in the given n-tuple must be the same as number -- of free indices in the domain. -- -- If the given n-tuple is not a member of the domain set, the routine -- func is not called, and non-zero code is returned. -- -- For the sake of convenience it is allowed to specify domain as NULL -- (then n-tuple also must be 0-tuple, i.e. empty), in which case this -- routine just calls the routine func and returns zero. -- -- This routine allows recursive calls from the routine func providing -- correct values of dummy indices for each instance. -- -- NOTE: The n-tuple passed to this routine must not be changed by any -- other routines called from the formal routine func until this -- routine has returned. */ struct eval_domain_info { /* working info used by the routine eval_within_domain */ DOMAIN *domain; /* domain, which has to be entered */ DOMAIN_BLOCK *block; /* domain block, which is currently processed */ TUPLE *tuple; /* tail of original n-tuple, whose components have to be assigned to free dummy indices in the current domain block */ void *info; /* transit pointer passed to the formal routine func */ void (*func)(MPL *mpl, void *info); /* routine, which has to be executed in the domain scope */ int failure; /* this flag indicates that given n-tuple is not a member of the domain set */ }; static void eval_domain_func(MPL *mpl, void *_my_info) { /* this routine recursively enters into the domain scope and then calls the routine func */ struct eval_domain_info *my_info = _my_info; if (my_info->block != NULL) { /* the current domain block to be entered exists */ DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; TUPLE *tuple = NULL, *temp = NULL; /* save pointer to the current domain block */ block = my_info->block; /* and get ready to enter the next block (if it exists) */ my_info->block = block->next; /* construct temporary n-tuple, whose components correspond to dummy indices (slots) of the current domain; components of the temporary n-tuple that correspond to free dummy indices are assigned references (not values!) to symbols specified in the corresponding components of the given n-tuple, while other components that correspond to non-free dummy indices are assigned symbolic values computed here */ for (slot = block->list; slot != NULL; slot = slot->next) { /* create component that corresponds to the current slot */ if (tuple == NULL) tuple = temp = dmp_get_atom(mpl->tuples, sizeof(TUPLE)); else temp = (temp->next = dmp_get_atom(mpl->tuples, sizeof(TUPLE))); if (slot->code == NULL) { /* dummy index is free; take reference to symbol, which is specified in the corresponding component of given n-tuple */ xassert(my_info->tuple != NULL); temp->sym = my_info->tuple->sym; xassert(temp->sym != NULL); my_info->tuple = my_info->tuple->next; } else { /* dummy index is non-free; compute symbolic value to be temporarily assigned to the dummy index */ temp->sym = eval_symbolic(mpl, slot->code); } } temp->next = NULL; /* enter the current domain block */ if (enter_domain_block(mpl, block, tuple, my_info, eval_domain_func)) my_info->failure = 1; /* delete temporary n-tuple as well as symbols that correspond to non-free dummy indices (they were computed here) */ for (slot = block->list; slot != NULL; slot = slot->next) { xassert(tuple != NULL); temp = tuple; tuple = tuple->next; if (slot->code != NULL) { /* dummy index is non-free; delete symbolic value */ delete_symbol(mpl, temp->sym); } /* delete component that corresponds to the current slot */ dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE)); } } else { /* there are no more domain blocks, i.e. we have reached the domain scope */ xassert(my_info->tuple == NULL); /* check optional predicate specified for the domain */ if (my_info->domain->code != NULL && !eval_logical(mpl, my_info->domain->code)) { /* the predicate is false */ my_info->failure = 2; } else { /* the predicate is true; do the job */ my_info->func(mpl, my_info->info); } } return; } int eval_within_domain ( MPL *mpl, DOMAIN *domain, /* not changed */ TUPLE *tuple, /* not changed */ void *info, void (*func)(MPL *mpl, void *info) ) { /* this routine performs evaluation within domain scope */ struct eval_domain_info _my_info, *my_info = &_my_info; if (domain == NULL) { xassert(tuple == NULL); func(mpl, info); my_info->failure = 0; } else { xassert(tuple != NULL); my_info->domain = domain; my_info->block = domain->list; my_info->tuple = tuple; my_info->info = info; my_info->func = func; my_info->failure = 0; /* enter the very first domain block */ eval_domain_func(mpl, my_info); } return my_info->failure; } /*---------------------------------------------------------------------- -- loop_within_domain - perform iterations within domain scope. -- -- This routine iteratively assigns new values (symbols) to the dummy -- indices of specified domain by enumerating all n-tuples, which are -- members of the domain set, and for every n-tuple it calls the formal -- routine func to evaluate some code within the domain scope. -- -- If the routine func returns non-zero, enumeration within the domain -- is prematurely terminated. -- -- For the sake of convenience it is allowed to specify domain as NULL, -- in which case this routine just calls the routine func only once and -- returns zero. -- -- This routine allows recursive calls from the routine func providing -- correct values of dummy indices for each instance. */ struct loop_domain_info { /* working info used by the routine loop_within_domain */ DOMAIN *domain; /* domain, which has to be entered */ DOMAIN_BLOCK *block; /* domain block, which is currently processed */ int looping; /* clearing this flag leads to terminating enumeration */ void *info; /* transit pointer passed to the formal routine func */ int (*func)(MPL *mpl, void *info); /* routine, which needs to be executed in the domain scope */ }; static void loop_domain_func(MPL *mpl, void *_my_info) { /* this routine enumerates all n-tuples in the basic set of the current domain block, enters recursively into the domain scope for every n-tuple, and then calls the routine func */ struct loop_domain_info *my_info = _my_info; if (my_info->block != NULL) { /* the current domain block to be entered exists */ DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; TUPLE *bound; /* save pointer to the current domain block */ block = my_info->block; /* and get ready to enter the next block (if it exists) */ my_info->block = block->next; /* compute symbolic values, at which non-free dummy indices of the current domain block are bound; since that values don't depend on free dummy indices of the current block, they can be computed once out of the enumeration loop */ bound = create_tuple(mpl); for (slot = block->list; slot != NULL; slot = slot->next) { if (slot->code != NULL) bound = expand_tuple(mpl, bound, eval_symbolic(mpl, slot->code)); } /* start enumeration */ xassert(block->code != NULL); if (block->code->op == O_DOTS) { /* the basic set is "arithmetic", in which case it doesn't need to be computed explicitly */ TUPLE *tuple; int n, j; double t0, tf, dt; /* compute "parameters" of the basic set */ t0 = eval_numeric(mpl, block->code->arg.arg.x); tf = eval_numeric(mpl, block->code->arg.arg.y); if (block->code->arg.arg.z == NULL) dt = 1.0; else dt = eval_numeric(mpl, block->code->arg.arg.z); /* determine cardinality of the basic set */ n = arelset_size(mpl, t0, tf, dt); /* create dummy 1-tuple for members of the basic set */ tuple = expand_tuple(mpl, create_tuple(mpl), create_symbol_num(mpl, 0.0)); /* in case of "arithmetic" set there is exactly one dummy index, which cannot be non-free */ xassert(bound == NULL); /* walk through 1-tuples of the basic set */ for (j = 1; j <= n && my_info->looping; j++) { /* construct dummy 1-tuple for the current member */ tuple->sym->num = arelset_member(mpl, t0, tf, dt, j); /* enter the current domain block */ enter_domain_block(mpl, block, tuple, my_info, loop_domain_func); } /* delete dummy 1-tuple */ delete_tuple(mpl, tuple); } else { /* the basic set is of general kind, in which case it needs to be explicitly computed */ ELEMSET *set; MEMBER *memb; TUPLE *temp1, *temp2; /* compute the basic set */ set = eval_elemset(mpl, block->code); /* walk through all n-tuples of the basic set */ for (memb = set->head; memb != NULL && my_info->looping; memb = memb->next) { /* all components of the current n-tuple that correspond to non-free dummy indices must be feasible; otherwise the n-tuple is not in the basic set */ temp1 = memb->tuple; temp2 = bound; for (slot = block->list; slot != NULL; slot = slot->next) { xassert(temp1 != NULL); if (slot->code != NULL) { /* non-free dummy index */ xassert(temp2 != NULL); if (compare_symbols(mpl, temp1->sym, temp2->sym) != 0) { /* the n-tuple is not in the basic set */ goto skip; } temp2 = temp2->next; } temp1 = temp1->next; } xassert(temp1 == NULL); xassert(temp2 == NULL); /* enter the current domain block */ enter_domain_block(mpl, block, memb->tuple, my_info, loop_domain_func); skip: ; } /* delete the basic set */ delete_elemset(mpl, set); } /* delete symbolic values binding non-free dummy indices */ delete_tuple(mpl, bound); /* restore pointer to the current domain block */ my_info->block = block; } else { /* there are no more domain blocks, i.e. we have reached the domain scope */ /* check optional predicate specified for the domain */ if (my_info->domain->code != NULL && !eval_logical(mpl, my_info->domain->code)) { /* the predicate is false */ /* nop */; } else { /* the predicate is true; do the job */ my_info->looping = !my_info->func(mpl, my_info->info); } } return; } void loop_within_domain ( MPL *mpl, DOMAIN *domain, /* not changed */ void *info, int (*func)(MPL *mpl, void *info) ) { /* this routine performs iterations within domain scope */ struct loop_domain_info _my_info, *my_info = &_my_info; if (domain == NULL) func(mpl, info); else { my_info->domain = domain; my_info->block = domain->list; my_info->looping = 1; my_info->info = info; my_info->func = func; /* enter the very first domain block */ loop_domain_func(mpl, my_info); } return; } /*---------------------------------------------------------------------- -- out_of_domain - raise domain exception. -- -- This routine is called when a reference is made to a member of some -- model object, but its n-tuple is out of the object domain. */ void out_of_domain ( MPL *mpl, char *name, /* not changed */ TUPLE *tuple /* not changed */ ) { xassert(name != NULL); xassert(tuple != NULL); error(mpl, "%s%s out of domain", name, format_tuple(mpl, '[', tuple)); /* no return */ } /*---------------------------------------------------------------------- -- get_domain_tuple - obtain current n-tuple from domain. -- -- This routine constructs n-tuple, whose components are current values -- assigned to *free* dummy indices of specified domain. -- -- For the sake of convenience it is allowed to specify domain as NULL, -- in which case this routine returns 0-tuple. -- -- NOTE: This routine must not be called out of domain scope. */ TUPLE *get_domain_tuple ( MPL *mpl, DOMAIN *domain /* not changed */ ) { DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; TUPLE *tuple; tuple = create_tuple(mpl); if (domain != NULL) { for (block = domain->list; block != NULL; block = block->next) { for (slot = block->list; slot != NULL; slot = slot->next) { if (slot->code == NULL) { xassert(slot->value != NULL); tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, slot->value)); } } } } return tuple; } /*---------------------------------------------------------------------- -- clean_domain - clean domain. -- -- This routine cleans specified domain that assumes deleting all stuff -- dynamically allocated during the generation phase. */ void clean_domain(MPL *mpl, DOMAIN *domain) { DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; /* if no domain is specified, do nothing */ if (domain == NULL) goto done; /* clean all domain blocks */ for (block = domain->list; block != NULL; block = block->next) { /* clean all domain slots */ for (slot = block->list; slot != NULL; slot = slot->next) { /* clean pseudo-code for computing bound value */ clean_code(mpl, slot->code); /* delete symbolic value assigned to dummy index */ if (slot->value != NULL) delete_symbol(mpl, slot->value), slot->value = NULL; } /* clean pseudo-code for computing basic set */ clean_code(mpl, block->code); } /* clean pseudo-code for computing domain predicate */ clean_code(mpl, domain->code); done: return; } /**********************************************************************/ /* * * MODEL SETS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- check_elem_set - check elemental set assigned to set member. -- -- This routine checks if given elemental set being assigned to member -- of specified model set satisfies to all restrictions. -- -- NOTE: This routine must not be called out of domain scope. */ void check_elem_set ( MPL *mpl, SET *set, /* not changed */ TUPLE *tuple, /* not changed */ ELEMSET *refer /* not changed */ ) { WITHIN *within; MEMBER *memb; int eqno; /* elemental set must be within all specified supersets */ for (within = set->within, eqno = 1; within != NULL; within = within->next, eqno++) { xassert(within->code != NULL); for (memb = refer->head; memb != NULL; memb = memb->next) { if (!is_member(mpl, within->code, memb->tuple)) { char buf[255+1]; strcpy(buf, format_tuple(mpl, '(', memb->tuple)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s%s contains %s which not within specified " "set; see (%d)", set->name, format_tuple(mpl, '[', tuple), buf, eqno); } } } return; } /*---------------------------------------------------------------------- -- take_member_set - obtain elemental set assigned to set member. -- -- This routine obtains a reference to elemental set assigned to given -- member of specified model set and returns it on exit. -- -- NOTE: This routine must not be called out of domain scope. */ ELEMSET *take_member_set /* returns reference, not value */ ( MPL *mpl, SET *set, /* not changed */ TUPLE *tuple /* not changed */ ) { MEMBER *memb; ELEMSET *refer; /* find member in the set array */ memb = find_member(mpl, set->array, tuple); if (memb != NULL) { /* member exists, so just take the reference */ refer = memb->value.set; } else if (set->assign != NULL) { /* compute value using assignment expression */ refer = eval_elemset(mpl, set->assign); add: /* check that the elemental set satisfies to all restrictions, assign it to new member, and add the member to the array */ check_elem_set(mpl, set, tuple, refer); memb = add_member(mpl, set->array, copy_tuple(mpl, tuple)); memb->value.set = refer; } else if (set->option != NULL) { /* compute default elemental set */ refer = eval_elemset(mpl, set->option); goto add; } else { /* no value (elemental set) is provided */ error(mpl, "no value for %s%s", set->name, format_tuple(mpl, '[', tuple)); } return refer; } /*---------------------------------------------------------------------- -- eval_member_set - evaluate elemental set assigned to set member. -- -- This routine evaluates a reference to elemental set assigned to given -- member of specified model set and returns it on exit. */ struct eval_set_info { /* working info used by the routine eval_member_set */ SET *set; /* model set */ TUPLE *tuple; /* n-tuple, which defines set member */ MEMBER *memb; /* normally this pointer is NULL; the routine uses this pointer to check data provided in the data section, in which case it points to a member currently checked; this check is performed automatically only once when a reference to any member occurs for the first time */ ELEMSET *refer; /* evaluated reference to elemental set */ }; static void eval_set_func(MPL *mpl, void *_info) { /* this is auxiliary routine to work within domain scope */ struct eval_set_info *info = _info; if (info->memb != NULL) { /* checking call; check elemental set being assigned */ check_elem_set(mpl, info->set, info->memb->tuple, info->memb->value.set); } else { /* normal call; evaluate member, which has given n-tuple */ info->refer = take_member_set(mpl, info->set, info->tuple); } return; } #if 1 /* 12/XII-2008 */ static void saturate_set(MPL *mpl, SET *set) { GADGET *gadget = set->gadget; ELEMSET *data; MEMBER *elem, *memb; TUPLE *tuple, *work[20]; int i; xprintf("Generating %s...\n", set->name); eval_whole_set(mpl, gadget->set); /* gadget set must have exactly one member */ xassert(gadget->set->array != NULL); xassert(gadget->set->array->head != NULL); xassert(gadget->set->array->head == gadget->set->array->tail); data = gadget->set->array->head->value.set; xassert(data->type == A_NONE); xassert(data->dim == gadget->set->dimen); /* walk thru all elements of the plain set */ for (elem = data->head; elem != NULL; elem = elem->next) { /* create a copy of n-tuple */ tuple = copy_tuple(mpl, elem->tuple); /* rearrange component of the n-tuple */ for (i = 0; i < gadget->set->dimen; i++) work[i] = NULL; for (i = 0; tuple != NULL; tuple = tuple->next) work[gadget->ind[i++]-1] = tuple; xassert(i == gadget->set->dimen); for (i = 0; i < gadget->set->dimen; i++) { xassert(work[i] != NULL); work[i]->next = work[i+1]; } /* construct subscript list from first set->dim components */ if (set->dim == 0) tuple = NULL; else tuple = work[0], work[set->dim-1]->next = NULL; /* find corresponding member of the set to be initialized */ memb = find_member(mpl, set->array, tuple); if (memb == NULL) { /* not found; add new member to the set and assign it empty elemental set */ memb = add_member(mpl, set->array, tuple); memb->value.set = create_elemset(mpl, set->dimen); } else { /* found; free subscript list */ delete_tuple(mpl, tuple); } /* construct new n-tuple from rest set->dimen components */ tuple = work[set->dim]; xassert(set->dim + set->dimen == gadget->set->dimen); work[gadget->set->dimen-1]->next = NULL; /* and add it to the elemental set assigned to the member (no check for duplicates is needed) */ add_tuple(mpl, memb->value.set, tuple); } /* the set has been saturated with data */ set->data = 1; return; } #endif ELEMSET *eval_member_set /* returns reference, not value */ ( MPL *mpl, SET *set, /* not changed */ TUPLE *tuple /* not changed */ ) { /* this routine evaluates set member */ struct eval_set_info _info, *info = &_info; xassert(set->dim == tuple_dimen(mpl, tuple)); info->set = set; info->tuple = tuple; #if 1 /* 12/XII-2008 */ if (set->gadget != NULL && set->data == 0) { /* initialize the set with data from a plain set */ saturate_set(mpl, set); } #endif if (set->data == 1) { /* check data, which are provided in the data section, but not checked yet */ /* save pointer to the last array member; note that during the check new members may be added beyond the last member due to references to the same parameter from default expression as well as from expressions that define restricting supersets; however, values assigned to the new members will be checked by other routine, so we don't need to check them here */ MEMBER *tail = set->array->tail; /* change the data status to prevent infinite recursive loop due to references to the same set during the check */ set->data = 2; /* check elemental sets assigned to array members in the data section until the marked member has been reached */ for (info->memb = set->array->head; info->memb != NULL; info->memb = info->memb->next) { if (eval_within_domain(mpl, set->domain, info->memb->tuple, info, eval_set_func)) out_of_domain(mpl, set->name, info->memb->tuple); if (info->memb == tail) break; } /* the check has been finished */ } /* evaluate member, which has given n-tuple */ info->memb = NULL; if (eval_within_domain(mpl, info->set->domain, info->tuple, info, eval_set_func)) out_of_domain(mpl, set->name, info->tuple); /* bring evaluated reference to the calling program */ return info->refer; } /*---------------------------------------------------------------------- -- eval_whole_set - evaluate model set over entire domain. -- -- This routine evaluates all members of specified model set over entire -- domain. */ static int whole_set_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ SET *set = (SET *)info; TUPLE *tuple = get_domain_tuple(mpl, set->domain); eval_member_set(mpl, set, tuple); delete_tuple(mpl, tuple); return 0; } void eval_whole_set(MPL *mpl, SET *set) { loop_within_domain(mpl, set->domain, set, whole_set_func); return; } /*---------------------------------------------------------------------- -- clean set - clean model set. -- -- This routine cleans specified model set that assumes deleting all -- stuff dynamically allocated during the generation phase. */ void clean_set(MPL *mpl, SET *set) { WITHIN *within; MEMBER *memb; /* clean subscript domain */ clean_domain(mpl, set->domain); /* clean pseudo-code for computing supersets */ for (within = set->within; within != NULL; within = within->next) clean_code(mpl, within->code); /* clean pseudo-code for computing assigned value */ clean_code(mpl, set->assign); /* clean pseudo-code for computing default value */ clean_code(mpl, set->option); /* reset data status flag */ set->data = 0; /* delete content array */ for (memb = set->array->head; memb != NULL; memb = memb->next) delete_value(mpl, set->array->type, &memb->value); delete_array(mpl, set->array), set->array = NULL; return; } /**********************************************************************/ /* * * MODEL PARAMETERS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- check_value_num - check numeric value assigned to parameter member. -- -- This routine checks if numeric value being assigned to some member -- of specified numeric model parameter satisfies to all restrictions. -- -- NOTE: This routine must not be called out of domain scope. */ void check_value_num ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple, /* not changed */ double value ) { CONDITION *cond; WITHIN *in; int eqno; /* the value must satisfy to the parameter type */ switch (par->type) { case A_NUMERIC: break; case A_INTEGER: if (value != floor(value)) error(mpl, "%s%s = %.*g not integer", par->name, format_tuple(mpl, '[', tuple), DBL_DIG, value); break; case A_BINARY: if (!(value == 0.0 || value == 1.0)) error(mpl, "%s%s = %.*g not binary", par->name, format_tuple(mpl, '[', tuple), DBL_DIG, value); break; default: xassert(par != par); } /* the value must satisfy to all specified conditions */ for (cond = par->cond, eqno = 1; cond != NULL; cond = cond->next, eqno++) { double bound; char *rho; xassert(cond->code != NULL); bound = eval_numeric(mpl, cond->code); switch (cond->rho) { case O_LT: if (!(value < bound)) { rho = "<"; err: error(mpl, "%s%s = %.*g not %s %.*g; see (%d)", par->name, format_tuple(mpl, '[', tuple), DBL_DIG, value, rho, DBL_DIG, bound, eqno); } break; case O_LE: if (!(value <= bound)) { rho = "<="; goto err; } break; case O_EQ: if (!(value == bound)) { rho = "="; goto err; } break; case O_GE: if (!(value >= bound)) { rho = ">="; goto err; } break; case O_GT: if (!(value > bound)) { rho = ">"; goto err; } break; case O_NE: if (!(value != bound)) { rho = "<>"; goto err; } break; default: xassert(cond != cond); } } /* the value must be in all specified supersets */ for (in = par->in, eqno = 1; in != NULL; in = in->next, eqno++) { TUPLE *dummy; xassert(in->code != NULL); xassert(in->code->dim == 1); dummy = expand_tuple(mpl, create_tuple(mpl), create_symbol_num(mpl, value)); if (!is_member(mpl, in->code, dummy)) error(mpl, "%s%s = %.*g not in specified set; see (%d)", par->name, format_tuple(mpl, '[', tuple), DBL_DIG, value, eqno); delete_tuple(mpl, dummy); } return; } /*---------------------------------------------------------------------- -- take_member_num - obtain num. value assigned to parameter member. -- -- This routine obtains a numeric value assigned to member of specified -- numeric model parameter and returns it on exit. -- -- NOTE: This routine must not be called out of domain scope. */ double take_member_num ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ) { MEMBER *memb; double value; /* find member in the parameter array */ memb = find_member(mpl, par->array, tuple); if (memb != NULL) { /* member exists, so just take its value */ value = memb->value.num; } else if (par->assign != NULL) { /* compute value using assignment expression */ value = eval_numeric(mpl, par->assign); add: /* check that the value satisfies to all restrictions, assign it to new member, and add the member to the array */ check_value_num(mpl, par, tuple, value); memb = add_member(mpl, par->array, copy_tuple(mpl, tuple)); memb->value.num = value; } else if (par->option != NULL) { /* compute default value */ value = eval_numeric(mpl, par->option); goto add; } else if (par->defval != NULL) { /* take default value provided in the data section */ if (par->defval->str != NULL) error(mpl, "cannot convert %s to floating-point number", format_symbol(mpl, par->defval)); value = par->defval->num; goto add; } else { /* no value is provided */ error(mpl, "no value for %s%s", par->name, format_tuple(mpl, '[', tuple)); } return value; } /*---------------------------------------------------------------------- -- eval_member_num - evaluate num. value assigned to parameter member. -- -- This routine evaluates a numeric value assigned to given member of -- specified numeric model parameter and returns it on exit. */ struct eval_num_info { /* working info used by the routine eval_member_num */ PARAMETER *par; /* model parameter */ TUPLE *tuple; /* n-tuple, which defines parameter member */ MEMBER *memb; /* normally this pointer is NULL; the routine uses this pointer to check data provided in the data section, in which case it points to a member currently checked; this check is performed automatically only once when a reference to any member occurs for the first time */ double value; /* evaluated numeric value */ }; static void eval_num_func(MPL *mpl, void *_info) { /* this is auxiliary routine to work within domain scope */ struct eval_num_info *info = _info; if (info->memb != NULL) { /* checking call; check numeric value being assigned */ check_value_num(mpl, info->par, info->memb->tuple, info->memb->value.num); } else { /* normal call; evaluate member, which has given n-tuple */ info->value = take_member_num(mpl, info->par, info->tuple); } return; } double eval_member_num ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ) { /* this routine evaluates numeric parameter member */ struct eval_num_info _info, *info = &_info; xassert(par->type == A_NUMERIC || par->type == A_INTEGER || par->type == A_BINARY); xassert(par->dim == tuple_dimen(mpl, tuple)); info->par = par; info->tuple = tuple; if (par->data == 1) { /* check data, which are provided in the data section, but not checked yet */ /* save pointer to the last array member; note that during the check new members may be added beyond the last member due to references to the same parameter from default expression as well as from expressions that define restricting conditions; however, values assigned to the new members will be checked by other routine, so we don't need to check them here */ MEMBER *tail = par->array->tail; /* change the data status to prevent infinite recursive loop due to references to the same parameter during the check */ par->data = 2; /* check values assigned to array members in the data section until the marked member has been reached */ for (info->memb = par->array->head; info->memb != NULL; info->memb = info->memb->next) { if (eval_within_domain(mpl, par->domain, info->memb->tuple, info, eval_num_func)) out_of_domain(mpl, par->name, info->memb->tuple); if (info->memb == tail) break; } /* the check has been finished */ } /* evaluate member, which has given n-tuple */ info->memb = NULL; if (eval_within_domain(mpl, info->par->domain, info->tuple, info, eval_num_func)) out_of_domain(mpl, par->name, info->tuple); /* bring evaluated value to the calling program */ return info->value; } /*---------------------------------------------------------------------- -- check_value_sym - check symbolic value assigned to parameter member. -- -- This routine checks if symbolic value being assigned to some member -- of specified symbolic model parameter satisfies to all restrictions. -- -- NOTE: This routine must not be called out of domain scope. */ void check_value_sym ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple, /* not changed */ SYMBOL *value /* not changed */ ) { CONDITION *cond; WITHIN *in; int eqno; /* the value must satisfy to all specified conditions */ for (cond = par->cond, eqno = 1; cond != NULL; cond = cond->next, eqno++) { SYMBOL *bound; char buf[255+1]; xassert(cond->code != NULL); bound = eval_symbolic(mpl, cond->code); switch (cond->rho) { #if 1 /* 13/VIII-2008 */ case O_LT: if (!(compare_symbols(mpl, value, bound) < 0)) { strcpy(buf, format_symbol(mpl, bound)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s%s = %s not < %s", par->name, format_tuple(mpl, '[', tuple), format_symbol(mpl, value), buf, eqno); } break; case O_LE: if (!(compare_symbols(mpl, value, bound) <= 0)) { strcpy(buf, format_symbol(mpl, bound)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s%s = %s not <= %s", par->name, format_tuple(mpl, '[', tuple), format_symbol(mpl, value), buf, eqno); } break; #endif case O_EQ: if (!(compare_symbols(mpl, value, bound) == 0)) { strcpy(buf, format_symbol(mpl, bound)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s%s = %s not = %s", par->name, format_tuple(mpl, '[', tuple), format_symbol(mpl, value), buf, eqno); } break; #if 1 /* 13/VIII-2008 */ case O_GE: if (!(compare_symbols(mpl, value, bound) >= 0)) { strcpy(buf, format_symbol(mpl, bound)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s%s = %s not >= %s", par->name, format_tuple(mpl, '[', tuple), format_symbol(mpl, value), buf, eqno); } break; case O_GT: if (!(compare_symbols(mpl, value, bound) > 0)) { strcpy(buf, format_symbol(mpl, bound)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s%s = %s not > %s", par->name, format_tuple(mpl, '[', tuple), format_symbol(mpl, value), buf, eqno); } break; #endif case O_NE: if (!(compare_symbols(mpl, value, bound) != 0)) { strcpy(buf, format_symbol(mpl, bound)); xassert(strlen(buf) < sizeof(buf)); error(mpl, "%s%s = %s not <> %s", par->name, format_tuple(mpl, '[', tuple), format_symbol(mpl, value), buf, eqno); } break; default: xassert(cond != cond); } delete_symbol(mpl, bound); } /* the value must be in all specified supersets */ for (in = par->in, eqno = 1; in != NULL; in = in->next, eqno++) { TUPLE *dummy; xassert(in->code != NULL); xassert(in->code->dim == 1); dummy = expand_tuple(mpl, create_tuple(mpl), copy_symbol(mpl, value)); if (!is_member(mpl, in->code, dummy)) error(mpl, "%s%s = %s not in specified set; see (%d)", par->name, format_tuple(mpl, '[', tuple), format_symbol(mpl, value), eqno); delete_tuple(mpl, dummy); } return; } /*---------------------------------------------------------------------- -- take_member_sym - obtain symb. value assigned to parameter member. -- -- This routine obtains a symbolic value assigned to member of specified -- symbolic model parameter and returns it on exit. -- -- NOTE: This routine must not be called out of domain scope. */ SYMBOL *take_member_sym /* returns value, not reference */ ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ) { MEMBER *memb; SYMBOL *value; /* find member in the parameter array */ memb = find_member(mpl, par->array, tuple); if (memb != NULL) { /* member exists, so just take its value */ value = copy_symbol(mpl, memb->value.sym); } else if (par->assign != NULL) { /* compute value using assignment expression */ value = eval_symbolic(mpl, par->assign); add: /* check that the value satisfies to all restrictions, assign it to new member, and add the member to the array */ check_value_sym(mpl, par, tuple, value); memb = add_member(mpl, par->array, copy_tuple(mpl, tuple)); memb->value.sym = copy_symbol(mpl, value); } else if (par->option != NULL) { /* compute default value */ value = eval_symbolic(mpl, par->option); goto add; } else if (par->defval != NULL) { /* take default value provided in the data section */ value = copy_symbol(mpl, par->defval); goto add; } else { /* no value is provided */ error(mpl, "no value for %s%s", par->name, format_tuple(mpl, '[', tuple)); } return value; } /*---------------------------------------------------------------------- -- eval_member_sym - evaluate symb. value assigned to parameter member. -- -- This routine evaluates a symbolic value assigned to given member of -- specified symbolic model parameter and returns it on exit. */ struct eval_sym_info { /* working info used by the routine eval_member_sym */ PARAMETER *par; /* model parameter */ TUPLE *tuple; /* n-tuple, which defines parameter member */ MEMBER *memb; /* normally this pointer is NULL; the routine uses this pointer to check data provided in the data section, in which case it points to a member currently checked; this check is performed automatically only once when a reference to any member occurs for the first time */ SYMBOL *value; /* evaluated symbolic value */ }; static void eval_sym_func(MPL *mpl, void *_info) { /* this is auxiliary routine to work within domain scope */ struct eval_sym_info *info = _info; if (info->memb != NULL) { /* checking call; check symbolic value being assigned */ check_value_sym(mpl, info->par, info->memb->tuple, info->memb->value.sym); } else { /* normal call; evaluate member, which has given n-tuple */ info->value = take_member_sym(mpl, info->par, info->tuple); } return; } SYMBOL *eval_member_sym /* returns value, not reference */ ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ) { /* this routine evaluates symbolic parameter member */ struct eval_sym_info _info, *info = &_info; xassert(par->type == A_SYMBOLIC); xassert(par->dim == tuple_dimen(mpl, tuple)); info->par = par; info->tuple = tuple; if (par->data == 1) { /* check data, which are provided in the data section, but not checked yet */ /* save pointer to the last array member; note that during the check new members may be added beyond the last member due to references to the same parameter from default expression as well as from expressions that define restricting conditions; however, values assigned to the new members will be checked by other routine, so we don't need to check them here */ MEMBER *tail = par->array->tail; /* change the data status to prevent infinite recursive loop due to references to the same parameter during the check */ par->data = 2; /* check values assigned to array members in the data section until the marked member has been reached */ for (info->memb = par->array->head; info->memb != NULL; info->memb = info->memb->next) { if (eval_within_domain(mpl, par->domain, info->memb->tuple, info, eval_sym_func)) out_of_domain(mpl, par->name, info->memb->tuple); if (info->memb == tail) break; } /* the check has been finished */ } /* evaluate member, which has given n-tuple */ info->memb = NULL; if (eval_within_domain(mpl, info->par->domain, info->tuple, info, eval_sym_func)) out_of_domain(mpl, par->name, info->tuple); /* bring evaluated value to the calling program */ return info->value; } /*---------------------------------------------------------------------- -- eval_whole_par - evaluate model parameter over entire domain. -- -- This routine evaluates all members of specified model parameter over -- entire domain. */ static int whole_par_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ PARAMETER *par = (PARAMETER *)info; TUPLE *tuple = get_domain_tuple(mpl, par->domain); switch (par->type) { case A_NUMERIC: case A_INTEGER: case A_BINARY: eval_member_num(mpl, par, tuple); break; case A_SYMBOLIC: delete_symbol(mpl, eval_member_sym(mpl, par, tuple)); break; default: xassert(par != par); } delete_tuple(mpl, tuple); return 0; } void eval_whole_par(MPL *mpl, PARAMETER *par) { loop_within_domain(mpl, par->domain, par, whole_par_func); return; } /*---------------------------------------------------------------------- -- clean_parameter - clean model parameter. -- -- This routine cleans specified model parameter that assumes deleting -- all stuff dynamically allocated during the generation phase. */ void clean_parameter(MPL *mpl, PARAMETER *par) { CONDITION *cond; WITHIN *in; MEMBER *memb; /* clean subscript domain */ clean_domain(mpl, par->domain); /* clean pseudo-code for computing restricting conditions */ for (cond = par->cond; cond != NULL; cond = cond->next) clean_code(mpl, cond->code); /* clean pseudo-code for computing restricting supersets */ for (in = par->in; in != NULL; in = in->next) clean_code(mpl, in->code); /* clean pseudo-code for computing assigned value */ clean_code(mpl, par->assign); /* clean pseudo-code for computing default value */ clean_code(mpl, par->option); /* reset data status flag */ par->data = 0; /* delete default symbolic value */ if (par->defval != NULL) delete_symbol(mpl, par->defval), par->defval = NULL; /* delete content array */ for (memb = par->array->head; memb != NULL; memb = memb->next) delete_value(mpl, par->array->type, &memb->value); delete_array(mpl, par->array), par->array = NULL; return; } /**********************************************************************/ /* * * MODEL VARIABLES * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- take_member_var - obtain reference to elemental variable. -- -- This routine obtains a reference to elemental variable assigned to -- given member of specified model variable and returns it on exit. If -- necessary, new elemental variable is created. -- -- NOTE: This routine must not be called out of domain scope. */ ELEMVAR *take_member_var /* returns reference */ ( MPL *mpl, VARIABLE *var, /* not changed */ TUPLE *tuple /* not changed */ ) { MEMBER *memb; ELEMVAR *refer; /* find member in the variable array */ memb = find_member(mpl, var->array, tuple); if (memb != NULL) { /* member exists, so just take the reference */ refer = memb->value.var; } else { /* member is referenced for the first time and therefore does not exist; create new elemental variable, assign it to new member, and add the member to the variable array */ memb = add_member(mpl, var->array, copy_tuple(mpl, tuple)); refer = (memb->value.var = dmp_get_atom(mpl->elemvars, sizeof(ELEMVAR))); refer->j = 0; refer->var = var; refer->memb = memb; /* compute lower bound */ if (var->lbnd == NULL) refer->lbnd = 0.0; else refer->lbnd = eval_numeric(mpl, var->lbnd); /* compute upper bound */ if (var->ubnd == NULL) refer->ubnd = 0.0; else if (var->ubnd == var->lbnd) refer->ubnd = refer->lbnd; else refer->ubnd = eval_numeric(mpl, var->ubnd); /* nullify working quantity */ refer->temp = 0.0; #if 1 /* 15/V-2010 */ /* solution has not been obtained by the solver yet */ refer->stat = 0; refer->prim = refer->dual = 0.0; #endif } return refer; } /*---------------------------------------------------------------------- -- eval_member_var - evaluate reference to elemental variable. -- -- This routine evaluates a reference to elemental variable assigned to -- member of specified model variable and returns it on exit. */ struct eval_var_info { /* working info used by the routine eval_member_var */ VARIABLE *var; /* model variable */ TUPLE *tuple; /* n-tuple, which defines variable member */ ELEMVAR *refer; /* evaluated reference to elemental variable */ }; static void eval_var_func(MPL *mpl, void *_info) { /* this is auxiliary routine to work within domain scope */ struct eval_var_info *info = _info; info->refer = take_member_var(mpl, info->var, info->tuple); return; } ELEMVAR *eval_member_var /* returns reference */ ( MPL *mpl, VARIABLE *var, /* not changed */ TUPLE *tuple /* not changed */ ) { /* this routine evaluates variable member */ struct eval_var_info _info, *info = &_info; xassert(var->dim == tuple_dimen(mpl, tuple)); info->var = var; info->tuple = tuple; /* evaluate member, which has given n-tuple */ if (eval_within_domain(mpl, info->var->domain, info->tuple, info, eval_var_func)) out_of_domain(mpl, var->name, info->tuple); /* bring evaluated reference to the calling program */ return info->refer; } /*---------------------------------------------------------------------- -- eval_whole_var - evaluate model variable over entire domain. -- -- This routine evaluates all members of specified model variable over -- entire domain. */ static int whole_var_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ VARIABLE *var = (VARIABLE *)info; TUPLE *tuple = get_domain_tuple(mpl, var->domain); eval_member_var(mpl, var, tuple); delete_tuple(mpl, tuple); return 0; } void eval_whole_var(MPL *mpl, VARIABLE *var) { loop_within_domain(mpl, var->domain, var, whole_var_func); return; } /*---------------------------------------------------------------------- -- clean_variable - clean model variable. -- -- This routine cleans specified model variable that assumes deleting -- all stuff dynamically allocated during the generation phase. */ void clean_variable(MPL *mpl, VARIABLE *var) { MEMBER *memb; /* clean subscript domain */ clean_domain(mpl, var->domain); /* clean code for computing lower bound */ clean_code(mpl, var->lbnd); /* clean code for computing upper bound */ if (var->ubnd != var->lbnd) clean_code(mpl, var->ubnd); /* delete content array */ for (memb = var->array->head; memb != NULL; memb = memb->next) dmp_free_atom(mpl->elemvars, memb->value.var, sizeof(ELEMVAR)); delete_array(mpl, var->array), var->array = NULL; return; } /**********************************************************************/ /* * * MODEL CONSTRAINTS AND OBJECTIVES * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- take_member_con - obtain reference to elemental constraint. -- -- This routine obtains a reference to elemental constraint assigned -- to given member of specified model constraint and returns it on exit. -- If necessary, new elemental constraint is created. -- -- NOTE: This routine must not be called out of domain scope. */ ELEMCON *take_member_con /* returns reference */ ( MPL *mpl, CONSTRAINT *con, /* not changed */ TUPLE *tuple /* not changed */ ) { MEMBER *memb; ELEMCON *refer; /* find member in the constraint array */ memb = find_member(mpl, con->array, tuple); if (memb != NULL) { /* member exists, so just take the reference */ refer = memb->value.con; } else { /* member is referenced for the first time and therefore does not exist; create new elemental constraint, assign it to new member, and add the member to the constraint array */ memb = add_member(mpl, con->array, copy_tuple(mpl, tuple)); refer = (memb->value.con = dmp_get_atom(mpl->elemcons, sizeof(ELEMCON))); refer->i = 0; refer->con = con; refer->memb = memb; /* compute linear form */ xassert(con->code != NULL); refer->form = eval_formula(mpl, con->code); /* compute lower and upper bounds */ if (con->lbnd == NULL && con->ubnd == NULL) { /* objective has no bounds */ double temp; xassert(con->type == A_MINIMIZE || con->type == A_MAXIMIZE); /* carry the constant term to the right-hand side */ refer->form = remove_constant(mpl, refer->form, &temp); refer->lbnd = refer->ubnd = - temp; } else if (con->lbnd != NULL && con->ubnd == NULL) { /* constraint a * x + b >= c * y + d is transformed to the standard form a * x - c * y >= d - b */ double temp; xassert(con->type == A_CONSTRAINT); refer->form = linear_comb(mpl, +1.0, refer->form, -1.0, eval_formula(mpl, con->lbnd)); refer->form = remove_constant(mpl, refer->form, &temp); refer->lbnd = - temp; refer->ubnd = 0.0; } else if (con->lbnd == NULL && con->ubnd != NULL) { /* constraint a * x + b <= c * y + d is transformed to the standard form a * x - c * y <= d - b */ double temp; xassert(con->type == A_CONSTRAINT); refer->form = linear_comb(mpl, +1.0, refer->form, -1.0, eval_formula(mpl, con->ubnd)); refer->form = remove_constant(mpl, refer->form, &temp); refer->lbnd = 0.0; refer->ubnd = - temp; } else if (con->lbnd == con->ubnd) { /* constraint a * x + b = c * y + d is transformed to the standard form a * x - c * y = d - b */ double temp; xassert(con->type == A_CONSTRAINT); refer->form = linear_comb(mpl, +1.0, refer->form, -1.0, eval_formula(mpl, con->lbnd)); refer->form = remove_constant(mpl, refer->form, &temp); refer->lbnd = refer->ubnd = - temp; } else { /* ranged constraint c <= a * x + b <= d is transformed to the standard form c - b <= a * x <= d - b */ double temp, temp1, temp2; xassert(con->type == A_CONSTRAINT); refer->form = remove_constant(mpl, refer->form, &temp); xassert(remove_constant(mpl, eval_formula(mpl, con->lbnd), &temp1) == NULL); xassert(remove_constant(mpl, eval_formula(mpl, con->ubnd), &temp2) == NULL); refer->lbnd = fp_sub(mpl, temp1, temp); refer->ubnd = fp_sub(mpl, temp2, temp); } #if 1 /* 15/V-2010 */ /* solution has not been obtained by the solver yet */ refer->stat = 0; refer->prim = refer->dual = 0.0; #endif } return refer; } /*---------------------------------------------------------------------- -- eval_member_con - evaluate reference to elemental constraint. -- -- This routine evaluates a reference to elemental constraint assigned -- to member of specified model constraint and returns it on exit. */ struct eval_con_info { /* working info used by the routine eval_member_con */ CONSTRAINT *con; /* model constraint */ TUPLE *tuple; /* n-tuple, which defines constraint member */ ELEMCON *refer; /* evaluated reference to elemental constraint */ }; static void eval_con_func(MPL *mpl, void *_info) { /* this is auxiliary routine to work within domain scope */ struct eval_con_info *info = _info; info->refer = take_member_con(mpl, info->con, info->tuple); return; } ELEMCON *eval_member_con /* returns reference */ ( MPL *mpl, CONSTRAINT *con, /* not changed */ TUPLE *tuple /* not changed */ ) { /* this routine evaluates constraint member */ struct eval_con_info _info, *info = &_info; xassert(con->dim == tuple_dimen(mpl, tuple)); info->con = con; info->tuple = tuple; /* evaluate member, which has given n-tuple */ if (eval_within_domain(mpl, info->con->domain, info->tuple, info, eval_con_func)) out_of_domain(mpl, con->name, info->tuple); /* bring evaluated reference to the calling program */ return info->refer; } /*---------------------------------------------------------------------- -- eval_whole_con - evaluate model constraint over entire domain. -- -- This routine evaluates all members of specified model constraint over -- entire domain. */ static int whole_con_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ CONSTRAINT *con = (CONSTRAINT *)info; TUPLE *tuple = get_domain_tuple(mpl, con->domain); eval_member_con(mpl, con, tuple); delete_tuple(mpl, tuple); return 0; } void eval_whole_con(MPL *mpl, CONSTRAINT *con) { loop_within_domain(mpl, con->domain, con, whole_con_func); return; } /*---------------------------------------------------------------------- -- clean_constraint - clean model constraint. -- -- This routine cleans specified model constraint that assumes deleting -- all stuff dynamically allocated during the generation phase. */ void clean_constraint(MPL *mpl, CONSTRAINT *con) { MEMBER *memb; /* clean subscript domain */ clean_domain(mpl, con->domain); /* clean code for computing main linear form */ clean_code(mpl, con->code); /* clean code for computing lower bound */ clean_code(mpl, con->lbnd); /* clean code for computing upper bound */ if (con->ubnd != con->lbnd) clean_code(mpl, con->ubnd); /* delete content array */ for (memb = con->array->head; memb != NULL; memb = memb->next) { delete_formula(mpl, memb->value.con->form); dmp_free_atom(mpl->elemcons, memb->value.con, sizeof(ELEMCON)); } delete_array(mpl, con->array), con->array = NULL; return; } /**********************************************************************/ /* * * PSEUDO-CODE * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- eval_numeric - evaluate pseudo-code to determine numeric value. -- -- This routine evaluates specified pseudo-code to determine resultant -- numeric value, which is returned on exit. */ struct iter_num_info { /* working info used by the routine iter_num_func */ CODE *code; /* pseudo-code for iterated operation to be performed */ double value; /* resultant value */ }; static int iter_num_func(MPL *mpl, void *_info) { /* this is auxiliary routine used to perform iterated operation on numeric "integrand" within domain scope */ struct iter_num_info *info = _info; double temp; temp = eval_numeric(mpl, info->code->arg.loop.x); switch (info->code->op) { case O_SUM: /* summation over domain */ info->value = fp_add(mpl, info->value, temp); break; case O_PROD: /* multiplication over domain */ info->value = fp_mul(mpl, info->value, temp); break; case O_MINIMUM: /* minimum over domain */ if (info->value > temp) info->value = temp; break; case O_MAXIMUM: /* maximum over domain */ if (info->value < temp) info->value = temp; break; default: xassert(info != info); } return 0; } double eval_numeric(MPL *mpl, CODE *code) { double value; xassert(code != NULL); xassert(code->type == A_NUMERIC); xassert(code->dim == 0); /* if the operation has a side effect, invalidate and delete the resultant value */ if (code->vflag && code->valid) { code->valid = 0; delete_value(mpl, code->type, &code->value); } /* if resultant value is valid, no evaluation is needed */ if (code->valid) { value = code->value.num; goto done; } /* evaluate pseudo-code recursively */ switch (code->op) { case O_NUMBER: /* take floating-point number */ value = code->arg.num; break; case O_MEMNUM: /* take member of numeric parameter */ { TUPLE *tuple; ARG_LIST *e; tuple = create_tuple(mpl); for (e = code->arg.par.list; e != NULL; e = e->next) tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, e->x)); value = eval_member_num(mpl, code->arg.par.par, tuple); delete_tuple(mpl, tuple); } break; case O_MEMVAR: /* take computed value of elemental variable */ { TUPLE *tuple; ARG_LIST *e; #if 1 /* 15/V-2010 */ ELEMVAR *var; #endif tuple = create_tuple(mpl); for (e = code->arg.var.list; e != NULL; e = e->next) tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, e->x)); #if 0 /* 15/V-2010 */ value = eval_member_var(mpl, code->arg.var.var, tuple) ->value; #else var = eval_member_var(mpl, code->arg.var.var, tuple); switch (code->arg.var.suff) { case DOT_LB: if (var->var->lbnd == NULL) value = -DBL_MAX; else value = var->lbnd; break; case DOT_UB: if (var->var->ubnd == NULL) value = +DBL_MAX; else value = var->ubnd; break; case DOT_STATUS: value = var->stat; break; case DOT_VAL: value = var->prim; break; case DOT_DUAL: value = var->dual; break; default: xassert(code != code); } #endif delete_tuple(mpl, tuple); } break; #if 1 /* 15/V-2010 */ case O_MEMCON: /* take computed value of elemental constraint */ { TUPLE *tuple; ARG_LIST *e; ELEMCON *con; tuple = create_tuple(mpl); for (e = code->arg.con.list; e != NULL; e = e->next) tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, e->x)); con = eval_member_con(mpl, code->arg.con.con, tuple); switch (code->arg.con.suff) { case DOT_LB: if (con->con->lbnd == NULL) value = -DBL_MAX; else value = con->lbnd; break; case DOT_UB: if (con->con->ubnd == NULL) value = +DBL_MAX; else value = con->ubnd; break; case DOT_STATUS: value = con->stat; break; case DOT_VAL: value = con->prim; break; case DOT_DUAL: value = con->dual; break; default: xassert(code != code); } delete_tuple(mpl, tuple); } break; #endif case O_IRAND224: /* pseudo-random in [0, 2^24-1] */ value = fp_irand224(mpl); break; case O_UNIFORM01: /* pseudo-random in [0, 1) */ value = fp_uniform01(mpl); break; case O_NORMAL01: /* gaussian random, mu = 0, sigma = 1 */ value = fp_normal01(mpl); break; case O_GMTIME: /* current calendar time */ value = fn_gmtime(mpl); break; case O_CVTNUM: /* conversion to numeric */ { SYMBOL *sym; sym = eval_symbolic(mpl, code->arg.arg.x); #if 0 /* 23/XI-2008 */ if (sym->str != NULL) error(mpl, "cannot convert %s to floating-point numbe" "r", format_symbol(mpl, sym)); value = sym->num; #else if (sym->str == NULL) value = sym->num; else { if (str2num(sym->str, &value)) error(mpl, "cannot convert %s to floating-point nu" "mber", format_symbol(mpl, sym)); } #endif delete_symbol(mpl, sym); } break; case O_PLUS: /* unary plus */ value = + eval_numeric(mpl, code->arg.arg.x); break; case O_MINUS: /* unary minus */ value = - eval_numeric(mpl, code->arg.arg.x); break; case O_ABS: /* absolute value */ value = fabs(eval_numeric(mpl, code->arg.arg.x)); break; case O_CEIL: /* round upward ("ceiling of x") */ value = ceil(eval_numeric(mpl, code->arg.arg.x)); break; case O_FLOOR: /* round downward ("floor of x") */ value = floor(eval_numeric(mpl, code->arg.arg.x)); break; case O_EXP: /* base-e exponential */ value = fp_exp(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_LOG: /* natural logarithm */ value = fp_log(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_LOG10: /* common (decimal) logarithm */ value = fp_log10(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_SQRT: /* square root */ value = fp_sqrt(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_SIN: /* trigonometric sine */ value = fp_sin(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_COS: /* trigonometric cosine */ value = fp_cos(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_ATAN: /* trigonometric arctangent (one argument) */ value = fp_atan(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_ATAN2: /* trigonometric arctangent (two arguments) */ value = fp_atan2(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_ROUND: /* round to nearest integer */ value = fp_round(mpl, eval_numeric(mpl, code->arg.arg.x), 0.0); break; case O_ROUND2: /* round to n fractional digits */ value = fp_round(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_TRUNC: /* truncate to nearest integer */ value = fp_trunc(mpl, eval_numeric(mpl, code->arg.arg.x), 0.0); break; case O_TRUNC2: /* truncate to n fractional digits */ value = fp_trunc(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_ADD: /* addition */ value = fp_add(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_SUB: /* subtraction */ value = fp_sub(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_LESS: /* non-negative subtraction */ value = fp_less(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_MUL: /* multiplication */ value = fp_mul(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_DIV: /* division */ value = fp_div(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_IDIV: /* quotient of exact division */ value = fp_idiv(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_MOD: /* remainder of exact division */ value = fp_mod(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_POWER: /* exponentiation (raise to power) */ value = fp_power(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_UNIFORM: /* pseudo-random in [a, b) */ value = fp_uniform(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_NORMAL: /* gaussian random, given mu and sigma */ value = fp_normal(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y)); break; case O_CARD: { ELEMSET *set; set = eval_elemset(mpl, code->arg.arg.x); value = set->size; delete_array(mpl, set); } break; case O_LENGTH: { SYMBOL *sym; char str[MAX_LENGTH+1]; sym = eval_symbolic(mpl, code->arg.arg.x); if (sym->str == NULL) sprintf(str, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, str); delete_symbol(mpl, sym); value = strlen(str); } break; case O_STR2TIME: { SYMBOL *sym; char str[MAX_LENGTH+1], fmt[MAX_LENGTH+1]; sym = eval_symbolic(mpl, code->arg.arg.x); if (sym->str == NULL) sprintf(str, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, str); delete_symbol(mpl, sym); sym = eval_symbolic(mpl, code->arg.arg.y); if (sym->str == NULL) sprintf(fmt, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, fmt); delete_symbol(mpl, sym); value = fn_str2time(mpl, str, fmt); } break; case O_FORK: /* if-then-else */ if (eval_logical(mpl, code->arg.arg.x)) value = eval_numeric(mpl, code->arg.arg.y); else if (code->arg.arg.z == NULL) value = 0.0; else value = eval_numeric(mpl, code->arg.arg.z); break; case O_MIN: /* minimal value (n-ary) */ { ARG_LIST *e; double temp; value = +DBL_MAX; for (e = code->arg.list; e != NULL; e = e->next) { temp = eval_numeric(mpl, e->x); if (value > temp) value = temp; } } break; case O_MAX: /* maximal value (n-ary) */ { ARG_LIST *e; double temp; value = -DBL_MAX; for (e = code->arg.list; e != NULL; e = e->next) { temp = eval_numeric(mpl, e->x); if (value < temp) value = temp; } } break; case O_SUM: /* summation over domain */ { struct iter_num_info _info, *info = &_info; info->code = code; info->value = 0.0; loop_within_domain(mpl, code->arg.loop.domain, info, iter_num_func); value = info->value; } break; case O_PROD: /* multiplication over domain */ { struct iter_num_info _info, *info = &_info; info->code = code; info->value = 1.0; loop_within_domain(mpl, code->arg.loop.domain, info, iter_num_func); value = info->value; } break; case O_MINIMUM: /* minimum over domain */ { struct iter_num_info _info, *info = &_info; info->code = code; info->value = +DBL_MAX; loop_within_domain(mpl, code->arg.loop.domain, info, iter_num_func); if (info->value == +DBL_MAX) error(mpl, "min{} over empty set; result undefined"); value = info->value; } break; case O_MAXIMUM: /* maximum over domain */ { struct iter_num_info _info, *info = &_info; info->code = code; info->value = -DBL_MAX; loop_within_domain(mpl, code->arg.loop.domain, info, iter_num_func); if (info->value == -DBL_MAX) error(mpl, "max{} over empty set; result undefined"); value = info->value; } break; default: xassert(code != code); } /* save resultant value */ xassert(!code->valid); code->valid = 1; code->value.num = value; done: return value; } /*---------------------------------------------------------------------- -- eval_symbolic - evaluate pseudo-code to determine symbolic value. -- -- This routine evaluates specified pseudo-code to determine resultant -- symbolic value, which is returned on exit. */ SYMBOL *eval_symbolic(MPL *mpl, CODE *code) { SYMBOL *value; xassert(code != NULL); xassert(code->type == A_SYMBOLIC); xassert(code->dim == 0); /* if the operation has a side effect, invalidate and delete the resultant value */ if (code->vflag && code->valid) { code->valid = 0; delete_value(mpl, code->type, &code->value); } /* if resultant value is valid, no evaluation is needed */ if (code->valid) { value = copy_symbol(mpl, code->value.sym); goto done; } /* evaluate pseudo-code recursively */ switch (code->op) { case O_STRING: /* take character string */ value = create_symbol_str(mpl, create_string(mpl, code->arg.str)); break; case O_INDEX: /* take dummy index */ xassert(code->arg.index.slot->value != NULL); value = copy_symbol(mpl, code->arg.index.slot->value); break; case O_MEMSYM: /* take member of symbolic parameter */ { TUPLE *tuple; ARG_LIST *e; tuple = create_tuple(mpl); for (e = code->arg.par.list; e != NULL; e = e->next) tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, e->x)); value = eval_member_sym(mpl, code->arg.par.par, tuple); delete_tuple(mpl, tuple); } break; case O_CVTSYM: /* conversion to symbolic */ value = create_symbol_num(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_CONCAT: /* concatenation */ value = concat_symbols(mpl, eval_symbolic(mpl, code->arg.arg.x), eval_symbolic(mpl, code->arg.arg.y)); break; case O_FORK: /* if-then-else */ if (eval_logical(mpl, code->arg.arg.x)) value = eval_symbolic(mpl, code->arg.arg.y); else if (code->arg.arg.z == NULL) value = create_symbol_num(mpl, 0.0); else value = eval_symbolic(mpl, code->arg.arg.z); break; case O_SUBSTR: case O_SUBSTR3: { double pos, len; char str[MAX_LENGTH+1]; value = eval_symbolic(mpl, code->arg.arg.x); if (value->str == NULL) sprintf(str, "%.*g", DBL_DIG, value->num); else fetch_string(mpl, value->str, str); delete_symbol(mpl, value); if (code->op == O_SUBSTR) { pos = eval_numeric(mpl, code->arg.arg.y); if (pos != floor(pos)) error(mpl, "substr('...', %.*g); non-integer secon" "d argument", DBL_DIG, pos); if (pos < 1 || pos > strlen(str) + 1) error(mpl, "substr('...', %.*g); substring out of " "range", DBL_DIG, pos); } else { pos = eval_numeric(mpl, code->arg.arg.y); len = eval_numeric(mpl, code->arg.arg.z); if (pos != floor(pos) || len != floor(len)) error(mpl, "substr('...', %.*g, %.*g); non-integer" " second and/or third argument", DBL_DIG, pos, DBL_DIG, len); if (pos < 1 || len < 0 || pos + len > strlen(str) + 1) error(mpl, "substr('...', %.*g, %.*g); substring o" "ut of range", DBL_DIG, pos, DBL_DIG, len); str[(int)pos + (int)len - 1] = '\0'; } value = create_symbol_str(mpl, create_string(mpl, str + (int)pos - 1)); } break; case O_TIME2STR: { double num; SYMBOL *sym; char str[MAX_LENGTH+1], fmt[MAX_LENGTH+1]; num = eval_numeric(mpl, code->arg.arg.x); sym = eval_symbolic(mpl, code->arg.arg.y); if (sym->str == NULL) sprintf(fmt, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, fmt); delete_symbol(mpl, sym); fn_time2str(mpl, str, num, fmt); value = create_symbol_str(mpl, create_string(mpl, str)); } break; default: xassert(code != code); } /* save resultant value */ xassert(!code->valid); code->valid = 1; code->value.sym = copy_symbol(mpl, value); done: return value; } /*---------------------------------------------------------------------- -- eval_logical - evaluate pseudo-code to determine logical value. -- -- This routine evaluates specified pseudo-code to determine resultant -- logical value, which is returned on exit. */ struct iter_log_info { /* working info used by the routine iter_log_func */ CODE *code; /* pseudo-code for iterated operation to be performed */ int value; /* resultant value */ }; static int iter_log_func(MPL *mpl, void *_info) { /* this is auxiliary routine used to perform iterated operation on logical "integrand" within domain scope */ struct iter_log_info *info = _info; int ret = 0; switch (info->code->op) { case O_FORALL: /* conjunction over domain */ info->value &= eval_logical(mpl, info->code->arg.loop.x); if (!info->value) ret = 1; break; case O_EXISTS: /* disjunction over domain */ info->value |= eval_logical(mpl, info->code->arg.loop.x); if (info->value) ret = 1; break; default: xassert(info != info); } return ret; } int eval_logical(MPL *mpl, CODE *code) { int value; xassert(code->type == A_LOGICAL); xassert(code->dim == 0); /* if the operation has a side effect, invalidate and delete the resultant value */ if (code->vflag && code->valid) { code->valid = 0; delete_value(mpl, code->type, &code->value); } /* if resultant value is valid, no evaluation is needed */ if (code->valid) { value = code->value.bit; goto done; } /* evaluate pseudo-code recursively */ switch (code->op) { case O_CVTLOG: /* conversion to logical */ value = (eval_numeric(mpl, code->arg.arg.x) != 0.0); break; case O_NOT: /* negation (logical "not") */ value = !eval_logical(mpl, code->arg.arg.x); break; case O_LT: /* comparison on 'less than' */ #if 0 /* 02/VIII-2008 */ value = (eval_numeric(mpl, code->arg.arg.x) < eval_numeric(mpl, code->arg.arg.y)); #else xassert(code->arg.arg.x != NULL); if (code->arg.arg.x->type == A_NUMERIC) value = (eval_numeric(mpl, code->arg.arg.x) < eval_numeric(mpl, code->arg.arg.y)); else { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); value = (compare_symbols(mpl, sym1, sym2) < 0); delete_symbol(mpl, sym1); delete_symbol(mpl, sym2); } #endif break; case O_LE: /* comparison on 'not greater than' */ #if 0 /* 02/VIII-2008 */ value = (eval_numeric(mpl, code->arg.arg.x) <= eval_numeric(mpl, code->arg.arg.y)); #else xassert(code->arg.arg.x != NULL); if (code->arg.arg.x->type == A_NUMERIC) value = (eval_numeric(mpl, code->arg.arg.x) <= eval_numeric(mpl, code->arg.arg.y)); else { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); value = (compare_symbols(mpl, sym1, sym2) <= 0); delete_symbol(mpl, sym1); delete_symbol(mpl, sym2); } #endif break; case O_EQ: /* comparison on 'equal to' */ xassert(code->arg.arg.x != NULL); if (code->arg.arg.x->type == A_NUMERIC) value = (eval_numeric(mpl, code->arg.arg.x) == eval_numeric(mpl, code->arg.arg.y)); else { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); value = (compare_symbols(mpl, sym1, sym2) == 0); delete_symbol(mpl, sym1); delete_symbol(mpl, sym2); } break; case O_GE: /* comparison on 'not less than' */ #if 0 /* 02/VIII-2008 */ value = (eval_numeric(mpl, code->arg.arg.x) >= eval_numeric(mpl, code->arg.arg.y)); #else xassert(code->arg.arg.x != NULL); if (code->arg.arg.x->type == A_NUMERIC) value = (eval_numeric(mpl, code->arg.arg.x) >= eval_numeric(mpl, code->arg.arg.y)); else { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); value = (compare_symbols(mpl, sym1, sym2) >= 0); delete_symbol(mpl, sym1); delete_symbol(mpl, sym2); } #endif break; case O_GT: /* comparison on 'greater than' */ #if 0 /* 02/VIII-2008 */ value = (eval_numeric(mpl, code->arg.arg.x) > eval_numeric(mpl, code->arg.arg.y)); #else xassert(code->arg.arg.x != NULL); if (code->arg.arg.x->type == A_NUMERIC) value = (eval_numeric(mpl, code->arg.arg.x) > eval_numeric(mpl, code->arg.arg.y)); else { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); value = (compare_symbols(mpl, sym1, sym2) > 0); delete_symbol(mpl, sym1); delete_symbol(mpl, sym2); } #endif break; case O_NE: /* comparison on 'not equal to' */ xassert(code->arg.arg.x != NULL); if (code->arg.arg.x->type == A_NUMERIC) value = (eval_numeric(mpl, code->arg.arg.x) != eval_numeric(mpl, code->arg.arg.y)); else { SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x); SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y); value = (compare_symbols(mpl, sym1, sym2) != 0); delete_symbol(mpl, sym1); delete_symbol(mpl, sym2); } break; case O_AND: /* conjunction (logical "and") */ value = eval_logical(mpl, code->arg.arg.x) && eval_logical(mpl, code->arg.arg.y); break; case O_OR: /* disjunction (logical "or") */ value = eval_logical(mpl, code->arg.arg.x) || eval_logical(mpl, code->arg.arg.y); break; case O_IN: /* test on 'x in Y' */ { TUPLE *tuple; tuple = eval_tuple(mpl, code->arg.arg.x); value = is_member(mpl, code->arg.arg.y, tuple); delete_tuple(mpl, tuple); } break; case O_NOTIN: /* test on 'x not in Y' */ { TUPLE *tuple; tuple = eval_tuple(mpl, code->arg.arg.x); value = !is_member(mpl, code->arg.arg.y, tuple); delete_tuple(mpl, tuple); } break; case O_WITHIN: /* test on 'X within Y' */ { ELEMSET *set; MEMBER *memb; set = eval_elemset(mpl, code->arg.arg.x); value = 1; for (memb = set->head; memb != NULL; memb = memb->next) { if (!is_member(mpl, code->arg.arg.y, memb->tuple)) { value = 0; break; } } delete_elemset(mpl, set); } break; case O_NOTWITHIN: /* test on 'X not within Y' */ { ELEMSET *set; MEMBER *memb; set = eval_elemset(mpl, code->arg.arg.x); value = 1; for (memb = set->head; memb != NULL; memb = memb->next) { if (is_member(mpl, code->arg.arg.y, memb->tuple)) { value = 0; break; } } delete_elemset(mpl, set); } break; case O_FORALL: /* conjunction (A-quantification) */ { struct iter_log_info _info, *info = &_info; info->code = code; info->value = 1; loop_within_domain(mpl, code->arg.loop.domain, info, iter_log_func); value = info->value; } break; case O_EXISTS: /* disjunction (E-quantification) */ { struct iter_log_info _info, *info = &_info; info->code = code; info->value = 0; loop_within_domain(mpl, code->arg.loop.domain, info, iter_log_func); value = info->value; } break; default: xassert(code != code); } /* save resultant value */ xassert(!code->valid); code->valid = 1; code->value.bit = value; done: return value; } /*---------------------------------------------------------------------- -- eval_tuple - evaluate pseudo-code to construct n-tuple. -- -- This routine evaluates specified pseudo-code to construct resultant -- n-tuple, which is returned on exit. */ TUPLE *eval_tuple(MPL *mpl, CODE *code) { TUPLE *value; xassert(code != NULL); xassert(code->type == A_TUPLE); xassert(code->dim > 0); /* if the operation has a side effect, invalidate and delete the resultant value */ if (code->vflag && code->valid) { code->valid = 0; delete_value(mpl, code->type, &code->value); } /* if resultant value is valid, no evaluation is needed */ if (code->valid) { value = copy_tuple(mpl, code->value.tuple); goto done; } /* evaluate pseudo-code recursively */ switch (code->op) { case O_TUPLE: /* make n-tuple */ { ARG_LIST *e; value = create_tuple(mpl); for (e = code->arg.list; e != NULL; e = e->next) value = expand_tuple(mpl, value, eval_symbolic(mpl, e->x)); } break; case O_CVTTUP: /* convert to 1-tuple */ value = expand_tuple(mpl, create_tuple(mpl), eval_symbolic(mpl, code->arg.arg.x)); break; default: xassert(code != code); } /* save resultant value */ xassert(!code->valid); code->valid = 1; code->value.tuple = copy_tuple(mpl, value); done: return value; } /*---------------------------------------------------------------------- -- eval_elemset - evaluate pseudo-code to construct elemental set. -- -- This routine evaluates specified pseudo-code to construct resultant -- elemental set, which is returned on exit. */ struct iter_set_info { /* working info used by the routine iter_set_func */ CODE *code; /* pseudo-code for iterated operation to be performed */ ELEMSET *value; /* resultant value */ }; static int iter_set_func(MPL *mpl, void *_info) { /* this is auxiliary routine used to perform iterated operation on n-tuple "integrand" within domain scope */ struct iter_set_info *info = _info; TUPLE *tuple; switch (info->code->op) { case O_SETOF: /* compute next n-tuple and add it to the set; in this case duplicate n-tuples are silently ignored */ tuple = eval_tuple(mpl, info->code->arg.loop.x); if (find_tuple(mpl, info->value, tuple) == NULL) add_tuple(mpl, info->value, tuple); else delete_tuple(mpl, tuple); break; case O_BUILD: /* construct next n-tuple using current values assigned to *free* dummy indices as its components and add it to the set; in this case duplicate n-tuples cannot appear */ add_tuple(mpl, info->value, get_domain_tuple(mpl, info->code->arg.loop.domain)); break; default: xassert(info != info); } return 0; } ELEMSET *eval_elemset(MPL *mpl, CODE *code) { ELEMSET *value; xassert(code != NULL); xassert(code->type == A_ELEMSET); xassert(code->dim > 0); /* if the operation has a side effect, invalidate and delete the resultant value */ if (code->vflag && code->valid) { code->valid = 0; delete_value(mpl, code->type, &code->value); } /* if resultant value is valid, no evaluation is needed */ if (code->valid) { value = copy_elemset(mpl, code->value.set); goto done; } /* evaluate pseudo-code recursively */ switch (code->op) { case O_MEMSET: /* take member of set */ { TUPLE *tuple; ARG_LIST *e; tuple = create_tuple(mpl); for (e = code->arg.set.list; e != NULL; e = e->next) tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, e->x)); value = copy_elemset(mpl, eval_member_set(mpl, code->arg.set.set, tuple)); delete_tuple(mpl, tuple); } break; case O_MAKE: /* make elemental set of n-tuples */ { ARG_LIST *e; value = create_elemset(mpl, code->dim); for (e = code->arg.list; e != NULL; e = e->next) check_then_add(mpl, value, eval_tuple(mpl, e->x)); } break; case O_UNION: /* union of two elemental sets */ value = set_union(mpl, eval_elemset(mpl, code->arg.arg.x), eval_elemset(mpl, code->arg.arg.y)); break; case O_DIFF: /* difference between two elemental sets */ value = set_diff(mpl, eval_elemset(mpl, code->arg.arg.x), eval_elemset(mpl, code->arg.arg.y)); break; case O_SYMDIFF: /* symmetric difference between two elemental sets */ value = set_symdiff(mpl, eval_elemset(mpl, code->arg.arg.x), eval_elemset(mpl, code->arg.arg.y)); break; case O_INTER: /* intersection of two elemental sets */ value = set_inter(mpl, eval_elemset(mpl, code->arg.arg.x), eval_elemset(mpl, code->arg.arg.y)); break; case O_CROSS: /* cross (Cartesian) product of two elemental sets */ value = set_cross(mpl, eval_elemset(mpl, code->arg.arg.x), eval_elemset(mpl, code->arg.arg.y)); break; case O_DOTS: /* build "arithmetic" elemental set */ value = create_arelset(mpl, eval_numeric(mpl, code->arg.arg.x), eval_numeric(mpl, code->arg.arg.y), code->arg.arg.z == NULL ? 1.0 : eval_numeric(mpl, code->arg.arg.z)); break; case O_FORK: /* if-then-else */ if (eval_logical(mpl, code->arg.arg.x)) value = eval_elemset(mpl, code->arg.arg.y); else value = eval_elemset(mpl, code->arg.arg.z); break; case O_SETOF: /* compute elemental set */ { struct iter_set_info _info, *info = &_info; info->code = code; info->value = create_elemset(mpl, code->dim); loop_within_domain(mpl, code->arg.loop.domain, info, iter_set_func); value = info->value; } break; case O_BUILD: /* build elemental set identical to domain set */ { struct iter_set_info _info, *info = &_info; info->code = code; info->value = create_elemset(mpl, code->dim); loop_within_domain(mpl, code->arg.loop.domain, info, iter_set_func); value = info->value; } break; default: xassert(code != code); } /* save resultant value */ xassert(!code->valid); code->valid = 1; code->value.set = copy_elemset(mpl, value); done: return value; } /*---------------------------------------------------------------------- -- is_member - check if n-tuple is in set specified by pseudo-code. -- -- This routine checks if given n-tuple is a member of elemental set -- specified in the form of pseudo-code (i.e. by expression). -- -- The n-tuple may have more components that dimension of the elemental -- set, in which case the extra components are ignored. */ static void null_func(MPL *mpl, void *info) { /* this is dummy routine used to enter the domain scope */ xassert(mpl == mpl); xassert(info == NULL); return; } int is_member(MPL *mpl, CODE *code, TUPLE *tuple) { int value; xassert(code != NULL); xassert(code->type == A_ELEMSET); xassert(code->dim > 0); xassert(tuple != NULL); switch (code->op) { case O_MEMSET: /* check if given n-tuple is member of elemental set, which is assigned to member of model set */ { ARG_LIST *e; TUPLE *temp; ELEMSET *set; /* evaluate reference to elemental set */ temp = create_tuple(mpl); for (e = code->arg.set.list; e != NULL; e = e->next) temp = expand_tuple(mpl, temp, eval_symbolic(mpl, e->x)); set = eval_member_set(mpl, code->arg.set.set, temp); delete_tuple(mpl, temp); /* check if the n-tuple is contained in the set array */ temp = build_subtuple(mpl, tuple, set->dim); value = (find_tuple(mpl, set, temp) != NULL); delete_tuple(mpl, temp); } break; case O_MAKE: /* check if given n-tuple is member of literal set */ { ARG_LIST *e; TUPLE *temp, *that; value = 0; temp = build_subtuple(mpl, tuple, code->dim); for (e = code->arg.list; e != NULL; e = e->next) { that = eval_tuple(mpl, e->x); value = (compare_tuples(mpl, temp, that) == 0); delete_tuple(mpl, that); if (value) break; } delete_tuple(mpl, temp); } break; case O_UNION: value = is_member(mpl, code->arg.arg.x, tuple) || is_member(mpl, code->arg.arg.y, tuple); break; case O_DIFF: value = is_member(mpl, code->arg.arg.x, tuple) && !is_member(mpl, code->arg.arg.y, tuple); break; case O_SYMDIFF: { int in1 = is_member(mpl, code->arg.arg.x, tuple); int in2 = is_member(mpl, code->arg.arg.y, tuple); value = (in1 && !in2) || (!in1 && in2); } break; case O_INTER: value = is_member(mpl, code->arg.arg.x, tuple) && is_member(mpl, code->arg.arg.y, tuple); break; case O_CROSS: { int j; value = is_member(mpl, code->arg.arg.x, tuple); if (value) { for (j = 1; j <= code->arg.arg.x->dim; j++) { xassert(tuple != NULL); tuple = tuple->next; } value = is_member(mpl, code->arg.arg.y, tuple); } } break; case O_DOTS: /* check if given 1-tuple is member of "arithmetic" set */ { int j; double x, t0, tf, dt; xassert(code->dim == 1); /* compute "parameters" of the "arithmetic" set */ t0 = eval_numeric(mpl, code->arg.arg.x); tf = eval_numeric(mpl, code->arg.arg.y); if (code->arg.arg.z == NULL) dt = 1.0; else dt = eval_numeric(mpl, code->arg.arg.z); /* make sure the parameters are correct */ arelset_size(mpl, t0, tf, dt); /* if component of 1-tuple is symbolic, not numeric, the 1-tuple cannot be member of "arithmetic" set */ xassert(tuple->sym != NULL); if (tuple->sym->str != NULL) { value = 0; break; } /* determine numeric value of the component */ x = tuple->sym->num; /* if the component value is out of the set range, the 1-tuple is not in the set */ if (dt > 0.0 && !(t0 <= x && x <= tf) || dt < 0.0 && !(tf <= x && x <= t0)) { value = 0; break; } /* estimate ordinal number of the 1-tuple in the set */ j = (int)(((x - t0) / dt) + 0.5) + 1; /* perform the main check */ value = (arelset_member(mpl, t0, tf, dt, j) == x); } break; case O_FORK: /* check if given n-tuple is member of conditional set */ if (eval_logical(mpl, code->arg.arg.x)) value = is_member(mpl, code->arg.arg.y, tuple); else value = is_member(mpl, code->arg.arg.z, tuple); break; case O_SETOF: /* check if given n-tuple is member of computed set */ /* it is not clear how to efficiently perform the check not computing the entire elemental set :+( */ error(mpl, "implementation restriction; in/within setof{} n" "ot allowed"); break; case O_BUILD: /* check if given n-tuple is member of domain set */ { TUPLE *temp; temp = build_subtuple(mpl, tuple, code->dim); /* try to enter the domain scope; if it is successful, the n-tuple is in the domain set */ value = (eval_within_domain(mpl, code->arg.loop.domain, temp, NULL, null_func) == 0); delete_tuple(mpl, temp); } break; default: xassert(code != code); } return value; } /*---------------------------------------------------------------------- -- eval_formula - evaluate pseudo-code to construct linear form. -- -- This routine evaluates specified pseudo-code to construct resultant -- linear form, which is returned on exit. */ struct iter_form_info { /* working info used by the routine iter_form_func */ CODE *code; /* pseudo-code for iterated operation to be performed */ FORMULA *value; /* resultant value */ FORMULA *tail; /* pointer to the last term */ }; static int iter_form_func(MPL *mpl, void *_info) { /* this is auxiliary routine used to perform iterated operation on linear form "integrand" within domain scope */ struct iter_form_info *info = _info; switch (info->code->op) { case O_SUM: /* summation over domain */ #if 0 info->value = linear_comb(mpl, +1.0, info->value, +1.0, eval_formula(mpl, info->code->arg.loop.x)); #else /* the routine linear_comb needs to look through all terms of both linear forms to reduce identical terms, so using it here is not a good idea (for example, evaluation of sum{i in 1..n} x[i] required quadratic time); the better idea is to gather all terms of the integrand in one list and reduce identical terms only once after all terms of the resultant linear form have been evaluated */ { FORMULA *form, *term; form = eval_formula(mpl, info->code->arg.loop.x); if (info->value == NULL) { xassert(info->tail == NULL); info->value = form; } else { xassert(info->tail != NULL); info->tail->next = form; } for (term = form; term != NULL; term = term->next) info->tail = term; } #endif break; default: xassert(info != info); } return 0; } FORMULA *eval_formula(MPL *mpl, CODE *code) { FORMULA *value; xassert(code != NULL); xassert(code->type == A_FORMULA); xassert(code->dim == 0); /* if the operation has a side effect, invalidate and delete the resultant value */ if (code->vflag && code->valid) { code->valid = 0; delete_value(mpl, code->type, &code->value); } /* if resultant value is valid, no evaluation is needed */ if (code->valid) { value = copy_formula(mpl, code->value.form); goto done; } /* evaluate pseudo-code recursively */ switch (code->op) { case O_MEMVAR: /* take member of variable */ { TUPLE *tuple; ARG_LIST *e; tuple = create_tuple(mpl); for (e = code->arg.var.list; e != NULL; e = e->next) tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl, e->x)); #if 1 /* 15/V-2010 */ xassert(code->arg.var.suff == DOT_NONE); #endif value = single_variable(mpl, eval_member_var(mpl, code->arg.var.var, tuple)); delete_tuple(mpl, tuple); } break; case O_CVTLFM: /* convert to linear form */ value = constant_term(mpl, eval_numeric(mpl, code->arg.arg.x)); break; case O_PLUS: /* unary plus */ value = linear_comb(mpl, 0.0, constant_term(mpl, 0.0), +1.0, eval_formula(mpl, code->arg.arg.x)); break; case O_MINUS: /* unary minus */ value = linear_comb(mpl, 0.0, constant_term(mpl, 0.0), -1.0, eval_formula(mpl, code->arg.arg.x)); break; case O_ADD: /* addition */ value = linear_comb(mpl, +1.0, eval_formula(mpl, code->arg.arg.x), +1.0, eval_formula(mpl, code->arg.arg.y)); break; case O_SUB: /* subtraction */ value = linear_comb(mpl, +1.0, eval_formula(mpl, code->arg.arg.x), -1.0, eval_formula(mpl, code->arg.arg.y)); break; case O_MUL: /* multiplication */ xassert(code->arg.arg.x != NULL); xassert(code->arg.arg.y != NULL); if (code->arg.arg.x->type == A_NUMERIC) { xassert(code->arg.arg.y->type == A_FORMULA); value = linear_comb(mpl, eval_numeric(mpl, code->arg.arg.x), eval_formula(mpl, code->arg.arg.y), 0.0, constant_term(mpl, 0.0)); } else { xassert(code->arg.arg.x->type == A_FORMULA); xassert(code->arg.arg.y->type == A_NUMERIC); value = linear_comb(mpl, eval_numeric(mpl, code->arg.arg.y), eval_formula(mpl, code->arg.arg.x), 0.0, constant_term(mpl, 0.0)); } break; case O_DIV: /* division */ value = linear_comb(mpl, fp_div(mpl, 1.0, eval_numeric(mpl, code->arg.arg.y)), eval_formula(mpl, code->arg.arg.x), 0.0, constant_term(mpl, 0.0)); break; case O_FORK: /* if-then-else */ if (eval_logical(mpl, code->arg.arg.x)) value = eval_formula(mpl, code->arg.arg.y); else if (code->arg.arg.z == NULL) value = constant_term(mpl, 0.0); else value = eval_formula(mpl, code->arg.arg.z); break; case O_SUM: /* summation over domain */ { struct iter_form_info _info, *info = &_info; info->code = code; info->value = constant_term(mpl, 0.0); info->tail = NULL; loop_within_domain(mpl, code->arg.loop.domain, info, iter_form_func); value = reduce_terms(mpl, info->value); } break; default: xassert(code != code); } /* save resultant value */ xassert(!code->valid); code->valid = 1; code->value.form = copy_formula(mpl, value); done: return value; } /*---------------------------------------------------------------------- -- clean_code - clean pseudo-code. -- -- This routine recursively cleans specified pseudo-code that assumes -- deleting all temporary resultant values. */ void clean_code(MPL *mpl, CODE *code) { ARG_LIST *e; /* if no pseudo-code is specified, do nothing */ if (code == NULL) goto done; /* if resultant value is valid (exists), delete it */ if (code->valid) { code->valid = 0; delete_value(mpl, code->type, &code->value); } /* recursively clean pseudo-code for operands */ switch (code->op) { case O_NUMBER: case O_STRING: case O_INDEX: break; case O_MEMNUM: case O_MEMSYM: for (e = code->arg.par.list; e != NULL; e = e->next) clean_code(mpl, e->x); break; case O_MEMSET: for (e = code->arg.set.list; e != NULL; e = e->next) clean_code(mpl, e->x); break; case O_MEMVAR: for (e = code->arg.var.list; e != NULL; e = e->next) clean_code(mpl, e->x); break; #if 1 /* 15/V-2010 */ case O_MEMCON: for (e = code->arg.con.list; e != NULL; e = e->next) clean_code(mpl, e->x); break; #endif case O_TUPLE: case O_MAKE: for (e = code->arg.list; e != NULL; e = e->next) clean_code(mpl, e->x); break; case O_SLICE: xassert(code != code); case O_IRAND224: case O_UNIFORM01: case O_NORMAL01: case O_GMTIME: break; case O_CVTNUM: case O_CVTSYM: case O_CVTLOG: case O_CVTTUP: case O_CVTLFM: case O_PLUS: case O_MINUS: case O_NOT: case O_ABS: case O_CEIL: case O_FLOOR: case O_EXP: case O_LOG: case O_LOG10: case O_SQRT: case O_SIN: case O_COS: case O_ATAN: case O_ROUND: case O_TRUNC: case O_CARD: case O_LENGTH: /* unary operation */ clean_code(mpl, code->arg.arg.x); break; case O_ADD: case O_SUB: case O_LESS: case O_MUL: case O_DIV: case O_IDIV: case O_MOD: case O_POWER: case O_ATAN2: case O_ROUND2: case O_TRUNC2: case O_UNIFORM: case O_NORMAL: case O_CONCAT: case O_LT: case O_LE: case O_EQ: case O_GE: case O_GT: case O_NE: case O_AND: case O_OR: case O_UNION: case O_DIFF: case O_SYMDIFF: case O_INTER: case O_CROSS: case O_IN: case O_NOTIN: case O_WITHIN: case O_NOTWITHIN: case O_SUBSTR: case O_STR2TIME: case O_TIME2STR: /* binary operation */ clean_code(mpl, code->arg.arg.x); clean_code(mpl, code->arg.arg.y); break; case O_DOTS: case O_FORK: case O_SUBSTR3: /* ternary operation */ clean_code(mpl, code->arg.arg.x); clean_code(mpl, code->arg.arg.y); clean_code(mpl, code->arg.arg.z); break; case O_MIN: case O_MAX: /* n-ary operation */ for (e = code->arg.list; e != NULL; e = e->next) clean_code(mpl, e->x); break; case O_SUM: case O_PROD: case O_MINIMUM: case O_MAXIMUM: case O_FORALL: case O_EXISTS: case O_SETOF: case O_BUILD: /* iterated operation */ clean_domain(mpl, code->arg.loop.domain); clean_code(mpl, code->arg.loop.x); break; default: xassert(code->op != code->op); } done: return; } #if 1 /* 11/II-2008 */ /**********************************************************************/ /* * * DATA TABLES * * */ /**********************************************************************/ int mpl_tab_num_args(TABDCA *dca) { /* returns the number of arguments */ return dca->na; } const char *mpl_tab_get_arg(TABDCA *dca, int k) { /* returns pointer to k-th argument */ xassert(1 <= k && k <= dca->na); return dca->arg[k]; } int mpl_tab_num_flds(TABDCA *dca) { /* returns the number of fields */ return dca->nf; } const char *mpl_tab_get_name(TABDCA *dca, int k) { /* returns pointer to name of k-th field */ xassert(1 <= k && k <= dca->nf); return dca->name[k]; } int mpl_tab_get_type(TABDCA *dca, int k) { /* returns type of k-th field */ xassert(1 <= k && k <= dca->nf); return dca->type[k]; } double mpl_tab_get_num(TABDCA *dca, int k) { /* returns numeric value of k-th field */ xassert(1 <= k && k <= dca->nf); xassert(dca->type[k] == 'N'); return dca->num[k]; } const char *mpl_tab_get_str(TABDCA *dca, int k) { /* returns pointer to string value of k-th field */ xassert(1 <= k && k <= dca->nf); xassert(dca->type[k] == 'S'); xassert(dca->str[k] != NULL); return dca->str[k]; } void mpl_tab_set_num(TABDCA *dca, int k, double num) { /* assign numeric value to k-th field */ xassert(1 <= k && k <= dca->nf); xassert(dca->type[k] == '?'); dca->type[k] = 'N'; dca->num[k] = num; return; } void mpl_tab_set_str(TABDCA *dca, int k, const char *str) { /* assign string value to k-th field */ xassert(1 <= k && k <= dca->nf); xassert(dca->type[k] == '?'); xassert(strlen(str) <= MAX_LENGTH); xassert(dca->str[k] != NULL); dca->type[k] = 'S'; strcpy(dca->str[k], str); return; } static int write_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ TABLE *tab = info; TABDCA *dca = mpl->dca; TABOUT *out; SYMBOL *sym; int k; char buf[MAX_LENGTH+1]; /* evaluate field values */ k = 0; for (out = tab->u.out.list; out != NULL; out = out->next) { k++; switch (out->code->type) { case A_NUMERIC: dca->type[k] = 'N'; dca->num[k] = eval_numeric(mpl, out->code); dca->str[k][0] = '\0'; break; case A_SYMBOLIC: sym = eval_symbolic(mpl, out->code); if (sym->str == NULL) { dca->type[k] = 'N'; dca->num[k] = sym->num; dca->str[k][0] = '\0'; } else { dca->type[k] = 'S'; dca->num[k] = 0.0; fetch_string(mpl, sym->str, buf); strcpy(dca->str[k], buf); } delete_symbol(mpl, sym); break; default: xassert(out != out); } } /* write record to output table */ mpl_tab_drv_write(mpl); return 0; } void execute_table(MPL *mpl, TABLE *tab) { /* execute table statement */ TABARG *arg; TABFLD *fld; TABIN *in; TABOUT *out; TABDCA *dca; SET *set; int k; char buf[MAX_LENGTH+1]; /* allocate table driver communication area */ xassert(mpl->dca == NULL); mpl->dca = dca = xmalloc(sizeof(TABDCA)); dca->id = 0; dca->link = NULL; dca->na = 0; dca->arg = NULL; dca->nf = 0; dca->name = NULL; dca->type = NULL; dca->num = NULL; dca->str = NULL; /* allocate arguments */ xassert(dca->na == 0); for (arg = tab->arg; arg != NULL; arg = arg->next) dca->na++; dca->arg = xcalloc(1+dca->na, sizeof(char *)); #if 1 /* 28/IX-2008 */ for (k = 1; k <= dca->na; k++) dca->arg[k] = NULL; #endif /* evaluate argument values */ k = 0; for (arg = tab->arg; arg != NULL; arg = arg->next) { SYMBOL *sym; k++; xassert(arg->code->type == A_SYMBOLIC); sym = eval_symbolic(mpl, arg->code); if (sym->str == NULL) sprintf(buf, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, buf); delete_symbol(mpl, sym); dca->arg[k] = xmalloc(strlen(buf)+1); strcpy(dca->arg[k], buf); } /* perform table input/output */ switch (tab->type) { case A_INPUT: goto read_table; case A_OUTPUT: goto write_table; default: xassert(tab != tab); } read_table: /* read data from input table */ /* add the only member to the control set and assign it empty elemental set */ set = tab->u.in.set; if (set != NULL) { if (set->data) error(mpl, "%s already provided with data", set->name); xassert(set->array->head == NULL); add_member(mpl, set->array, NULL)->value.set = create_elemset(mpl, set->dimen); set->data = 1; } /* check parameters specified in the input list */ for (in = tab->u.in.list; in != NULL; in = in->next) { if (in->par->data) error(mpl, "%s already provided with data", in->par->name); in->par->data = 1; } /* allocate and initialize fields */ xassert(dca->nf == 0); for (fld = tab->u.in.fld; fld != NULL; fld = fld->next) dca->nf++; for (in = tab->u.in.list; in != NULL; in = in->next) dca->nf++; dca->name = xcalloc(1+dca->nf, sizeof(char *)); dca->type = xcalloc(1+dca->nf, sizeof(int)); dca->num = xcalloc(1+dca->nf, sizeof(double)); dca->str = xcalloc(1+dca->nf, sizeof(char *)); k = 0; for (fld = tab->u.in.fld; fld != NULL; fld = fld->next) { k++; dca->name[k] = fld->name; dca->type[k] = '?'; dca->num[k] = 0.0; dca->str[k] = xmalloc(MAX_LENGTH+1); dca->str[k][0] = '\0'; } for (in = tab->u.in.list; in != NULL; in = in->next) { k++; dca->name[k] = in->name; dca->type[k] = '?'; dca->num[k] = 0.0; dca->str[k] = xmalloc(MAX_LENGTH+1); dca->str[k][0] = '\0'; } /* open input table */ mpl_tab_drv_open(mpl, 'R'); /* read and process records */ for (;;) { TUPLE *tup; /* reset field types */ for (k = 1; k <= dca->nf; k++) dca->type[k] = '?'; /* read next record */ if (mpl_tab_drv_read(mpl)) break; /* all fields must be set by the driver */ for (k = 1; k <= dca->nf; k++) { if (dca->type[k] == '?') error(mpl, "field %s missing in input table", dca->name[k]); } /* construct n-tuple */ tup = create_tuple(mpl); k = 0; for (fld = tab->u.in.fld; fld != NULL; fld = fld->next) { k++; xassert(k <= dca->nf); switch (dca->type[k]) { case 'N': tup = expand_tuple(mpl, tup, create_symbol_num(mpl, dca->num[k])); break; case 'S': xassert(strlen(dca->str[k]) <= MAX_LENGTH); tup = expand_tuple(mpl, tup, create_symbol_str(mpl, create_string(mpl, dca->str[k]))); break; default: xassert(dca != dca); } } /* add n-tuple just read to the control set */ if (tab->u.in.set != NULL) check_then_add(mpl, tab->u.in.set->array->head->value.set, copy_tuple(mpl, tup)); /* assign values to the parameters in the input list */ for (in = tab->u.in.list; in != NULL; in = in->next) { MEMBER *memb; k++; xassert(k <= dca->nf); /* there must be no member with the same n-tuple */ if (find_member(mpl, in->par->array, tup) != NULL) error(mpl, "%s%s already defined", in->par->name, format_tuple(mpl, '[', tup)); /* create new parameter member with given n-tuple */ memb = add_member(mpl, in->par->array, copy_tuple(mpl, tup)) ; /* assign value to the parameter member */ switch (in->par->type) { case A_NUMERIC: case A_INTEGER: case A_BINARY: if (dca->type[k] != 'N') error(mpl, "%s requires numeric data", in->par->name); memb->value.num = dca->num[k]; break; case A_SYMBOLIC: switch (dca->type[k]) { case 'N': memb->value.sym = create_symbol_num(mpl, dca->num[k]); break; case 'S': xassert(strlen(dca->str[k]) <= MAX_LENGTH); memb->value.sym = create_symbol_str(mpl, create_string(mpl,dca->str[k])); break; default: xassert(dca != dca); } break; default: xassert(in != in); } } /* n-tuple is no more needed */ delete_tuple(mpl, tup); } /* close input table */ mpl_tab_drv_close(mpl); goto done; write_table: /* write data to output table */ /* allocate and initialize fields */ xassert(dca->nf == 0); for (out = tab->u.out.list; out != NULL; out = out->next) dca->nf++; dca->name = xcalloc(1+dca->nf, sizeof(char *)); dca->type = xcalloc(1+dca->nf, sizeof(int)); dca->num = xcalloc(1+dca->nf, sizeof(double)); dca->str = xcalloc(1+dca->nf, sizeof(char *)); k = 0; for (out = tab->u.out.list; out != NULL; out = out->next) { k++; dca->name[k] = out->name; dca->type[k] = '?'; dca->num[k] = 0.0; dca->str[k] = xmalloc(MAX_LENGTH+1); dca->str[k][0] = '\0'; } /* open output table */ mpl_tab_drv_open(mpl, 'W'); /* evaluate fields and write records */ loop_within_domain(mpl, tab->u.out.domain, tab, write_func); /* close output table */ mpl_tab_drv_close(mpl); done: /* free table driver communication area */ free_dca(mpl); return; } void free_dca(MPL *mpl) { /* free table driver communucation area */ TABDCA *dca = mpl->dca; int k; if (dca != NULL) { if (dca->link != NULL) mpl_tab_drv_close(mpl); if (dca->arg != NULL) { for (k = 1; k <= dca->na; k++) #if 1 /* 28/IX-2008 */ if (dca->arg[k] != NULL) #endif xfree(dca->arg[k]); xfree(dca->arg); } if (dca->name != NULL) xfree(dca->name); if (dca->type != NULL) xfree(dca->type); if (dca->num != NULL) xfree(dca->num); if (dca->str != NULL) { for (k = 1; k <= dca->nf; k++) xfree(dca->str[k]); xfree(dca->str); } xfree(dca), mpl->dca = NULL; } return; } void clean_table(MPL *mpl, TABLE *tab) { /* clean table statement */ TABARG *arg; TABOUT *out; /* clean string list */ for (arg = tab->arg; arg != NULL; arg = arg->next) clean_code(mpl, arg->code); switch (tab->type) { case A_INPUT: break; case A_OUTPUT: /* clean subscript domain */ clean_domain(mpl, tab->u.out.domain); /* clean output list */ for (out = tab->u.out.list; out != NULL; out = out->next) clean_code(mpl, out->code); break; default: xassert(tab != tab); } return; } #endif /**********************************************************************/ /* * * MODEL STATEMENTS * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- execute_check - execute check statement. -- -- This routine executes specified check statement. */ static int check_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ CHECK *chk = (CHECK *)info; if (!eval_logical(mpl, chk->code)) error(mpl, "check%s failed", format_tuple(mpl, '[', get_domain_tuple(mpl, chk->domain))); return 0; } void execute_check(MPL *mpl, CHECK *chk) { loop_within_domain(mpl, chk->domain, chk, check_func); return; } /*---------------------------------------------------------------------- -- clean_check - clean check statement. -- -- This routine cleans specified check statement that assumes deleting -- all stuff dynamically allocated on generating/postsolving phase. */ void clean_check(MPL *mpl, CHECK *chk) { /* clean subscript domain */ clean_domain(mpl, chk->domain); /* clean pseudo-code for computing predicate */ clean_code(mpl, chk->code); return; } /*---------------------------------------------------------------------- -- execute_display - execute display statement. -- -- This routine executes specified display statement. */ static void display_set(MPL *mpl, SET *set, MEMBER *memb) { /* display member of model set */ ELEMSET *s = memb->value.set; MEMBER *m; write_text(mpl, "%s%s%s\n", set->name, format_tuple(mpl, '[', memb->tuple), s->head == NULL ? " is empty" : ":"); for (m = s->head; m != NULL; m = m->next) write_text(mpl, " %s\n", format_tuple(mpl, '(', m->tuple)); return; } static void display_par(MPL *mpl, PARAMETER *par, MEMBER *memb) { /* display member of model parameter */ switch (par->type) { case A_NUMERIC: case A_INTEGER: case A_BINARY: write_text(mpl, "%s%s = %.*g\n", par->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.num); break; case A_SYMBOLIC: write_text(mpl, "%s%s = %s\n", par->name, format_tuple(mpl, '[', memb->tuple), format_symbol(mpl, memb->value.sym)); break; default: xassert(par != par); } return; } #if 1 /* 15/V-2010 */ static void display_var(MPL *mpl, VARIABLE *var, MEMBER *memb, int suff) { /* display member of model variable */ if (suff == DOT_NONE || suff == DOT_VAL) write_text(mpl, "%s%s.val = %.*g\n", var->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.var->prim); else if (suff == DOT_LB) write_text(mpl, "%s%s.lb = %.*g\n", var->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.var->var->lbnd == NULL ? -DBL_MAX : memb->value.var->lbnd); else if (suff == DOT_UB) write_text(mpl, "%s%s.ub = %.*g\n", var->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.var->var->ubnd == NULL ? +DBL_MAX : memb->value.var->ubnd); else if (suff == DOT_STATUS) write_text(mpl, "%s%s.status = %d\n", var->name, format_tuple (mpl, '[', memb->tuple), memb->value.var->stat); else if (suff == DOT_DUAL) write_text(mpl, "%s%s.dual = %.*g\n", var->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.var->dual); else xassert(suff != suff); return; } #endif #if 1 /* 15/V-2010 */ static void display_con(MPL *mpl, CONSTRAINT *con, MEMBER *memb, int suff) { /* display member of model constraint */ if (suff == DOT_NONE || suff == DOT_VAL) write_text(mpl, "%s%s.val = %.*g\n", con->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.con->prim); else if (suff == DOT_LB) write_text(mpl, "%s%s.lb = %.*g\n", con->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.con->con->lbnd == NULL ? -DBL_MAX : memb->value.con->lbnd); else if (suff == DOT_UB) write_text(mpl, "%s%s.ub = %.*g\n", con->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.con->con->ubnd == NULL ? +DBL_MAX : memb->value.con->ubnd); else if (suff == DOT_STATUS) write_text(mpl, "%s%s.status = %d\n", con->name, format_tuple (mpl, '[', memb->tuple), memb->value.con->stat); else if (suff == DOT_DUAL) write_text(mpl, "%s%s.dual = %.*g\n", con->name, format_tuple(mpl, '[', memb->tuple), DBL_DIG, memb->value.con->dual); else xassert(suff != suff); return; } #endif static void display_memb(MPL *mpl, CODE *code) { /* display member specified by pseudo-code */ MEMBER memb; ARG_LIST *e; xassert(code->op == O_MEMNUM || code->op == O_MEMSYM || code->op == O_MEMSET || code->op == O_MEMVAR || code->op == O_MEMCON); memb.tuple = create_tuple(mpl); for (e = code->arg.par.list; e != NULL; e = e->next) memb.tuple = expand_tuple(mpl, memb.tuple, eval_symbolic(mpl, e->x)); switch (code->op) { case O_MEMNUM: memb.value.num = eval_member_num(mpl, code->arg.par.par, memb.tuple); display_par(mpl, code->arg.par.par, &memb); break; case O_MEMSYM: memb.value.sym = eval_member_sym(mpl, code->arg.par.par, memb.tuple); display_par(mpl, code->arg.par.par, &memb); delete_symbol(mpl, memb.value.sym); break; case O_MEMSET: memb.value.set = eval_member_set(mpl, code->arg.set.set, memb.tuple); display_set(mpl, code->arg.set.set, &memb); break; case O_MEMVAR: memb.value.var = eval_member_var(mpl, code->arg.var.var, memb.tuple); display_var (mpl, code->arg.var.var, &memb, code->arg.var.suff); break; case O_MEMCON: memb.value.con = eval_member_con(mpl, code->arg.con.con, memb.tuple); display_con (mpl, code->arg.con.con, &memb, code->arg.con.suff); break; default: xassert(code != code); } delete_tuple(mpl, memb.tuple); return; } static void display_code(MPL *mpl, CODE *code) { /* display value of expression */ switch (code->type) { case A_NUMERIC: /* numeric value */ { double num; num = eval_numeric(mpl, code); write_text(mpl, "%.*g\n", DBL_DIG, num); } break; case A_SYMBOLIC: /* symbolic value */ { SYMBOL *sym; sym = eval_symbolic(mpl, code); write_text(mpl, "%s\n", format_symbol(mpl, sym)); delete_symbol(mpl, sym); } break; case A_LOGICAL: /* logical value */ { int bit; bit = eval_logical(mpl, code); write_text(mpl, "%s\n", bit ? "true" : "false"); } break; case A_TUPLE: /* n-tuple */ { TUPLE *tuple; tuple = eval_tuple(mpl, code); write_text(mpl, "%s\n", format_tuple(mpl, '(', tuple)); delete_tuple(mpl, tuple); } break; case A_ELEMSET: /* elemental set */ { ELEMSET *set; MEMBER *memb; set = eval_elemset(mpl, code); if (set->head == 0) write_text(mpl, "set is empty\n"); for (memb = set->head; memb != NULL; memb = memb->next) write_text(mpl, " %s\n", format_tuple(mpl, '(', memb->tuple)); delete_elemset(mpl, set); } break; case A_FORMULA: /* linear form */ { FORMULA *form, *term; form = eval_formula(mpl, code); if (form == NULL) write_text(mpl, "linear form is empty\n"); for (term = form; term != NULL; term = term->next) { if (term->var == NULL) write_text(mpl, " %.*g\n", term->coef); else write_text(mpl, " %.*g %s%s\n", DBL_DIG, term->coef, term->var->var->name, format_tuple(mpl, '[', term->var->memb->tuple)); } delete_formula(mpl, form); } break; default: xassert(code != code); } return; } static int display_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ DISPLAY *dpy = (DISPLAY *)info; DISPLAY1 *entry; for (entry = dpy->list; entry != NULL; entry = entry->next) { if (entry->type == A_INDEX) { /* dummy index */ DOMAIN_SLOT *slot = entry->u.slot; write_text(mpl, "%s = %s\n", slot->name, format_symbol(mpl, slot->value)); } else if (entry->type == A_SET) { /* model set */ SET *set = entry->u.set; MEMBER *memb; if (set->assign != NULL) { /* the set has assignment expression; evaluate all its members over entire domain */ eval_whole_set(mpl, set); } else { /* the set has no assignment expression; refer to its any existing member ignoring resultant value to check the data provided the data section */ #if 1 /* 12/XII-2008 */ if (set->gadget != NULL && set->data == 0) { /* initialize the set with data from a plain set */ saturate_set(mpl, set); } #endif if (set->array->head != NULL) eval_member_set(mpl, set, set->array->head->tuple); } /* display all members of the set array */ if (set->array->head == NULL) write_text(mpl, "%s has empty content\n", set->name); for (memb = set->array->head; memb != NULL; memb = memb->next) display_set(mpl, set, memb); } else if (entry->type == A_PARAMETER) { /* model parameter */ PARAMETER *par = entry->u.par; MEMBER *memb; if (par->assign != NULL) { /* the parameter has an assignment expression; evaluate all its member over entire domain */ eval_whole_par(mpl, par); } else { /* the parameter has no assignment expression; refer to its any existing member ignoring resultant value to check the data provided in the data section */ if (par->array->head != NULL) { if (par->type != A_SYMBOLIC) eval_member_num(mpl, par, par->array->head->tuple); else delete_symbol(mpl, eval_member_sym(mpl, par, par->array->head->tuple)); } } /* display all members of the parameter array */ if (par->array->head == NULL) write_text(mpl, "%s has empty content\n", par->name); for (memb = par->array->head; memb != NULL; memb = memb->next) display_par(mpl, par, memb); } else if (entry->type == A_VARIABLE) { /* model variable */ VARIABLE *var = entry->u.var; MEMBER *memb; xassert(mpl->flag_p); /* display all members of the variable array */ if (var->array->head == NULL) write_text(mpl, "%s has empty content\n", var->name); for (memb = var->array->head; memb != NULL; memb = memb->next) display_var(mpl, var, memb, DOT_NONE); } else if (entry->type == A_CONSTRAINT) { /* model constraint */ CONSTRAINT *con = entry->u.con; MEMBER *memb; xassert(mpl->flag_p); /* display all members of the constraint array */ if (con->array->head == NULL) write_text(mpl, "%s has empty content\n", con->name); for (memb = con->array->head; memb != NULL; memb = memb->next) display_con(mpl, con, memb, DOT_NONE); } else if (entry->type == A_EXPRESSION) { /* expression */ CODE *code = entry->u.code; if (code->op == O_MEMNUM || code->op == O_MEMSYM || code->op == O_MEMSET || code->op == O_MEMVAR || code->op == O_MEMCON) display_memb(mpl, code); else display_code(mpl, code); } else xassert(entry != entry); } return 0; } void execute_display(MPL *mpl, DISPLAY *dpy) { loop_within_domain(mpl, dpy->domain, dpy, display_func); return; } /*---------------------------------------------------------------------- -- clean_display - clean display statement. -- -- This routine cleans specified display statement that assumes deleting -- all stuff dynamically allocated on generating/postsolving phase. */ void clean_display(MPL *mpl, DISPLAY *dpy) { DISPLAY1 *d; #if 0 /* 15/V-2010 */ ARG_LIST *e; #endif /* clean subscript domain */ clean_domain(mpl, dpy->domain); /* clean display list */ for (d = dpy->list; d != NULL; d = d->next) { /* clean pseudo-code for computing expression */ if (d->type == A_EXPRESSION) clean_code(mpl, d->u.code); #if 0 /* 15/V-2010 */ /* clean pseudo-code for computing subscripts */ for (e = d->list; e != NULL; e = e->next) clean_code(mpl, e->x); #endif } return; } /*---------------------------------------------------------------------- -- execute_printf - execute printf statement. -- -- This routine executes specified printf statement. */ #if 1 /* 14/VII-2006 */ static void print_char(MPL *mpl, int c) { if (mpl->prt_fp == NULL) write_char(mpl, c); else xfputc(c, mpl->prt_fp); return; } static void print_text(MPL *mpl, char *fmt, ...) { va_list arg; char buf[OUTBUF_SIZE], *c; va_start(arg, fmt); vsprintf(buf, fmt, arg); xassert(strlen(buf) < sizeof(buf)); va_end(arg); for (c = buf; *c != '\0'; c++) print_char(mpl, *c); return; } #endif static int printf_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ PRINTF *prt = (PRINTF *)info; PRINTF1 *entry; SYMBOL *sym; char fmt[MAX_LENGTH+1], *c, *from, save; /* evaluate format control string */ sym = eval_symbolic(mpl, prt->fmt); if (sym->str == NULL) sprintf(fmt, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, fmt); delete_symbol(mpl, sym); /* scan format control string and perform formatting output */ entry = prt->list; for (c = fmt; *c != '\0'; c++) { if (*c == '%') { /* scan format specifier */ from = c++; if (*c == '%') { print_char(mpl, '%'); continue; } if (entry == NULL) break; /* scan optional flags */ while (*c == '-' || *c == '+' || *c == ' ' || *c == '#' || *c == '0') c++; /* scan optional minimum field width */ while (isdigit((unsigned char)*c)) c++; /* scan optional precision */ if (*c == '.') { c++; while (isdigit((unsigned char)*c)) c++; } /* scan conversion specifier and perform formatting */ save = *(c+1), *(c+1) = '\0'; if (*c == 'd' || *c == 'i' || *c == 'e' || *c == 'E' || *c == 'f' || *c == 'F' || *c == 'g' || *c == 'G') { /* the specifier requires numeric value */ double value; xassert(entry != NULL); switch (entry->code->type) { case A_NUMERIC: value = eval_numeric(mpl, entry->code); break; case A_SYMBOLIC: sym = eval_symbolic(mpl, entry->code); if (sym->str != NULL) error(mpl, "cannot convert %s to floating-point" " number", format_symbol(mpl, sym)); value = sym->num; delete_symbol(mpl, sym); break; case A_LOGICAL: if (eval_logical(mpl, entry->code)) value = 1.0; else value = 0.0; break; default: xassert(entry != entry); } if (*c == 'd' || *c == 'i') { double int_max = (double)INT_MAX; if (!(-int_max <= value && value <= +int_max)) error(mpl, "cannot convert %.*g to integer", DBL_DIG, value); print_text(mpl, from, (int)floor(value + 0.5)); } else print_text(mpl, from, value); } else if (*c == 's') { /* the specifier requires symbolic value */ char value[MAX_LENGTH+1]; switch (entry->code->type) { case A_NUMERIC: sprintf(value, "%.*g", DBL_DIG, eval_numeric(mpl, entry->code)); break; case A_LOGICAL: if (eval_logical(mpl, entry->code)) strcpy(value, "T"); else strcpy(value, "F"); break; case A_SYMBOLIC: sym = eval_symbolic(mpl, entry->code); if (sym->str == NULL) sprintf(value, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, value); delete_symbol(mpl, sym); break; default: xassert(entry != entry); } print_text(mpl, from, value); } else error(mpl, "format specifier missing or invalid"); *(c+1) = save; entry = entry->next; } else if (*c == '\\') { /* write some control character */ c++; if (*c == 't') print_char(mpl, '\t'); else if (*c == 'n') print_char(mpl, '\n'); #if 1 /* 28/X-2010 */ else if (*c == '\0') { /* format string ends with backslash */ error(mpl, "invalid use of escape character \\ in format" " control string"); } #endif else print_char(mpl, *c); } else { /* write character without formatting */ print_char(mpl, *c); } } return 0; } #if 0 /* 14/VII-2006 */ void execute_printf(MPL *mpl, PRINTF *prt) { loop_within_domain(mpl, prt->domain, prt, printf_func); return; } #else void execute_printf(MPL *mpl, PRINTF *prt) { if (prt->fname == NULL) { /* switch to the standard output */ if (mpl->prt_fp != NULL) { xfclose(mpl->prt_fp), mpl->prt_fp = NULL; xfree(mpl->prt_file), mpl->prt_file = NULL; } } else { /* evaluate file name string */ SYMBOL *sym; char fname[MAX_LENGTH+1]; sym = eval_symbolic(mpl, prt->fname); if (sym->str == NULL) sprintf(fname, "%.*g", DBL_DIG, sym->num); else fetch_string(mpl, sym->str, fname); delete_symbol(mpl, sym); /* close the current print file, if necessary */ if (mpl->prt_fp != NULL && (!prt->app || strcmp(mpl->prt_file, fname) != 0)) { xfclose(mpl->prt_fp), mpl->prt_fp = NULL; xfree(mpl->prt_file), mpl->prt_file = NULL; } /* open the specified print file, if necessary */ if (mpl->prt_fp == NULL) { mpl->prt_fp = xfopen(fname, prt->app ? "a" : "w"); if (mpl->prt_fp == NULL) error(mpl, "unable to open `%s' for writing - %s", fname, xerrmsg()); mpl->prt_file = xmalloc(strlen(fname)+1); strcpy(mpl->prt_file, fname); } } loop_within_domain(mpl, prt->domain, prt, printf_func); if (mpl->prt_fp != NULL) { xfflush(mpl->prt_fp); if (xferror(mpl->prt_fp)) error(mpl, "writing error to `%s' - %s", mpl->prt_file, xerrmsg()); } return; } #endif /*---------------------------------------------------------------------- -- clean_printf - clean printf statement. -- -- This routine cleans specified printf statement that assumes deleting -- all stuff dynamically allocated on generating/postsolving phase. */ void clean_printf(MPL *mpl, PRINTF *prt) { PRINTF1 *p; /* clean subscript domain */ clean_domain(mpl, prt->domain); /* clean pseudo-code for computing format string */ clean_code(mpl, prt->fmt); /* clean printf list */ for (p = prt->list; p != NULL; p = p->next) { /* clean pseudo-code for computing value to be printed */ clean_code(mpl, p->code); } #if 1 /* 14/VII-2006 */ /* clean pseudo-code for computing file name string */ clean_code(mpl, prt->fname); #endif return; } /*---------------------------------------------------------------------- -- execute_for - execute for statement. -- -- This routine executes specified for statement. */ static int for_func(MPL *mpl, void *info) { /* this is auxiliary routine to work within domain scope */ FOR *fur = (FOR *)info; STATEMENT *stmt, *save; save = mpl->stmt; for (stmt = fur->list; stmt != NULL; stmt = stmt->next) execute_statement(mpl, stmt); mpl->stmt = save; return 0; } void execute_for(MPL *mpl, FOR *fur) { loop_within_domain(mpl, fur->domain, fur, for_func); return; } /*---------------------------------------------------------------------- -- clean_for - clean for statement. -- -- This routine cleans specified for statement that assumes deleting all -- stuff dynamically allocated on generating/postsolving phase. */ void clean_for(MPL *mpl, FOR *fur) { STATEMENT *stmt; /* clean subscript domain */ clean_domain(mpl, fur->domain); /* clean all sub-statements */ for (stmt = fur->list; stmt != NULL; stmt = stmt->next) clean_statement(mpl, stmt); return; } /*---------------------------------------------------------------------- -- execute_statement - execute specified model statement. -- -- This routine executes specified model statement. */ void execute_statement(MPL *mpl, STATEMENT *stmt) { mpl->stmt = stmt; switch (stmt->type) { case A_SET: case A_PARAMETER: case A_VARIABLE: break; case A_CONSTRAINT: xprintf("Generating %s...\n", stmt->u.con->name); eval_whole_con(mpl, stmt->u.con); break; case A_TABLE: switch (stmt->u.tab->type) { case A_INPUT: xprintf("Reading %s...\n", stmt->u.tab->name); break; case A_OUTPUT: xprintf("Writing %s...\n", stmt->u.tab->name); break; default: xassert(stmt != stmt); } execute_table(mpl, stmt->u.tab); break; case A_SOLVE: break; case A_CHECK: xprintf("Checking (line %d)...\n", stmt->line); execute_check(mpl, stmt->u.chk); break; case A_DISPLAY: write_text(mpl, "Display statement at line %d\n", stmt->line); execute_display(mpl, stmt->u.dpy); break; case A_PRINTF: execute_printf(mpl, stmt->u.prt); break; case A_FOR: execute_for(mpl, stmt->u.fur); break; default: xassert(stmt != stmt); } return; } /*---------------------------------------------------------------------- -- clean_statement - clean specified model statement. -- -- This routine cleans specified model statement that assumes deleting -- all stuff dynamically allocated on generating/postsolving phase. */ void clean_statement(MPL *mpl, STATEMENT *stmt) { switch(stmt->type) { case A_SET: clean_set(mpl, stmt->u.set); break; case A_PARAMETER: clean_parameter(mpl, stmt->u.par); break; case A_VARIABLE: clean_variable(mpl, stmt->u.var); break; case A_CONSTRAINT: clean_constraint(mpl, stmt->u.con); break; #if 1 /* 11/II-2008 */ case A_TABLE: clean_table(mpl, stmt->u.tab); break; #endif case A_SOLVE: break; case A_CHECK: clean_check(mpl, stmt->u.chk); break; case A_DISPLAY: clean_display(mpl, stmt->u.dpy); break; case A_PRINTF: clean_printf(mpl, stmt->u.prt); break; case A_FOR: clean_for(mpl, stmt->u.fur); break; default: xassert(stmt != stmt); } return; } /* eof */ igraph/src/DensityGrid_3d.h0000644000176000001440000000541112325527072015335 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __DENSITY_GRID_H__ #define __DENSITY_GRID_H__ // Compile time adjustable parameters #include using namespace std; #include "drl_layout_3d.h" #include "drl_Node_3d.h" #ifdef MUSE_MPI #include #endif namespace drl3d { class DensityGrid { public: // Methods void Init(); void Subtract(Node &n, bool first_add, bool fine_first_add, bool fineDensity); void Add(Node &n, bool fineDensity ); float GetDensity(float Nx, float Ny, float Nz, bool fineDensity); // Contructor/Destructor DensityGrid() {}; ~DensityGrid(); private: // Private Members void Subtract( Node &N ); void Add( Node &N ); void fineSubtract( Node &N ); void fineAdd( Node &N ); // new dynamic variables -- SBM float (*fall_off)[RADIUS*2+1][RADIUS*2+1]; float (*Density)[GRID_SIZE][GRID_SIZE]; deque* Bins; // old static variables //float fall_off[RADIUS*2+1][RADIUS*2+1]; //float Density[GRID_SIZE][GRID_SIZE]; //deque Bins[GRID_SIZE][GRID_SIZE]; }; } // namespace drl3d #endif // __DENSITY_GRID_H__ igraph/src/cliques.c0000644000176000001440000012176012325527072014170 0ustar ripleyusers /* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_cliques.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_constants.h" #include "igraph_adjlist.h" #include "igraph_interrupt_internal.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_stack.h" #include "igraph_types_internal.h" #include "config.h" #include #include /* memset */ void igraph_i_cliques_free_res(igraph_vector_ptr_t *res) { long i, n; n = igraph_vector_ptr_size(res); for (i=0; i(*new_member_storage)[m-1]) { (*new_member_storage)[m++]=v2; n=m; } else { m=n; } } else { m=n; } } /* See if new_member_storage is full. If so, reallocate */ if (m == new_member_storage_size) { IGRAPH_FINALLY_CLEAN(1); *new_member_storage = igraph_Realloc(*new_member_storage, (size_t) new_member_storage_size*2, igraph_real_t); if (*new_member_storage == 0) IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); new_member_storage_size *= 2; IGRAPH_FINALLY(igraph_free, *new_member_storage); } } } } /* Calculate how many cliques have we found */ *clique_count = n/size; IGRAPH_FINALLY_CLEAN(1); return 0; } /* Internal function for calculating cliques or independent vertex sets. * They are practically the same except that the complementer of the graph * should be used in the latter case. */ int igraph_i_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size, igraph_bool_t independent_vertices) { igraph_integer_t no_of_nodes; igraph_vector_t neis; igraph_real_t *member_storage=0, *new_member_storage, *c1; long int i, j, k, clique_count, old_clique_count; if (igraph_is_directed(graph)) IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); no_of_nodes = igraph_vcount(graph); if (min_size < 0) { min_size = 0; } if (max_size > no_of_nodes || max_size <= 0) { max_size = no_of_nodes; } igraph_vector_ptr_clear(res); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_FINALLY(igraph_i_cliques_free_res, res); /* Will be resized later, if needed. */ member_storage=igraph_Calloc(1, igraph_real_t); if (member_storage==0) { IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, member_storage); /* Find all 1-cliques: every vertex will be a clique */ new_member_storage=igraph_Calloc(no_of_nodes, igraph_real_t); if (new_member_storage==0) { IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, new_member_storage); for (i=0; i 1; i++) { /* Here new_member_storage contains the cliques found in the previous iteration. Save this into member_storage, might be needed later */ c1=member_storage; member_storage=new_member_storage; new_member_storage=c1; old_clique_count=clique_count; IGRAPH_ALLOW_INTERRUPTION(); /* Calculate the cliques */ IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_i_find_k_cliques(graph, i, member_storage, &new_member_storage, old_clique_count, &clique_count, &neis, independent_vertices)); IGRAPH_FINALLY(igraph_free, member_storage); IGRAPH_FINALLY(igraph_free, new_member_storage); /* Add the cliques just found to the result if requested */ if (i>=min_size && i<=max_size) { for (j=0, k=0; j * Cliques are fully connected subgraphs of a graph. * * * If you are only interested in the size of the largest clique in the graph, * use \ref igraph_clique_number() instead. * * The current implementation of this function searches * for maximal independent vertex sets (see \ref * igraph_maximal_independent_vertex_sets()) in the complementer graph * using the algorithm published in: * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm * for generating all the maximal independent sets. SIAM J Computing, * 6:505--517, 1977. * * \param graph The input graph. * \param res Pointer to a pointer vector, the result will be stored * here, ie. \c res will contain pointers to \c igraph_vector_t * objects which contain the indices of vertices involved in a clique. * The pointer vector will be resized if needed but note that the * objects in the pointer vector will not be freed. * \param min_size Integer giving the minimum size of the cliques to be * returned. If negative or zero, no lower bound will be used. * \param max_size Integer giving the maximum size of the cliques to be * returned. If negative or zero, no upper bound will be used. * \return Error code. * * \sa \ref igraph_largest_cliques() and \ref igraph_clique_number(). * * Time complexity: TODO * * \example examples/simple/igraph_cliques.c */ int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size) { return igraph_i_cliques(graph, res, min_size, max_size, 0); } typedef int(*igraph_i_maximal_clique_func_t)(const igraph_vector_t*, void*, igraph_bool_t*); typedef struct { igraph_vector_ptr_t* result; igraph_integer_t min_size; igraph_integer_t max_size; } igraph_i_maximal_clique_data_t; int igraph_i_maximal_cliques(const igraph_t *graph, igraph_i_maximal_clique_func_t func, void* data); int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t *clique_number, igraph_bool_t keep_only_largest, igraph_bool_t complementer); /** * \function igraph_independent_vertex_sets * \brief Find all independent vertex sets in a graph * * * A vertex set is considered independent if there are no edges between * them. * * * If you are interested in the size of the largest independent vertex set, * use \ref igraph_independence_number() instead. * * * The current implementation was ported to igraph from the Very Nauty Graph * Library by Keith Briggs and uses the algorithm from the paper * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm * for generating all the maximal independent sets. SIAM J Computing, * 6:505--517, 1977. * * \param graph The input graph. * \param res Pointer to a pointer vector, the result will be stored * here, ie. \c res will contain pointers to \c igraph_vector_t * objects which contain the indices of vertices involved in an independent * vertex set. The pointer vector will be resized if needed but note that the * objects in the pointer vector will not be freed. * \param min_size Integer giving the minimum size of the sets to be * returned. If negative or zero, no lower bound will be used. * \param max_size Integer giving the maximum size of the sets to be * returned. If negative or zero, no upper bound will be used. * \return Error code. * * \sa \ref igraph_largest_independent_vertex_sets(), * \ref igraph_independence_number(). * * Time complexity: TODO * * \example examples/simple/igraph_independent_sets.c */ int igraph_independent_vertex_sets(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size) { return igraph_i_cliques(graph, res, min_size, max_size, 1); } /** * \function igraph_largest_independent_vertex_sets * \brief Finds the largest independent vertex set(s) in a graph. * * * An independent vertex set is largest if there is no other * independent vertex set with more vertices in the graph. * * * The current implementation was ported to igraph from the Very Nauty Graph * Library by Keith Briggs and uses the algorithm from the paper * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm * for generating all the maximal independent sets. SIAM J Computing, * 6:505--517, 1977. * * \param graph The input graph. * \param res Pointer to a pointer vector, the result will be stored * here. It will be resized as needed. * \return Error code. * * \sa \ref igraph_independent_vertex_sets(), \ref * igraph_maximal_independent_vertex_sets(). * * Time complexity: TODO */ int igraph_largest_independent_vertex_sets(const igraph_t *graph, igraph_vector_ptr_t *res) { return igraph_i_maximal_or_largest_cliques_or_indsets(graph, res, 0, 1, 0); } typedef struct igraph_i_max_ind_vsets_data_t { igraph_integer_t matrix_size; igraph_adjlist_t adj_list; /* Adjacency list of the graph */ igraph_vector_t deg; /* Degrees of individual nodes */ igraph_set_t* buckets; /* Bucket array */ /* The IS value for each node. Still to be explained :) */ igraph_integer_t* IS; igraph_integer_t largest_set_size; /* Size of the largest set encountered */ igraph_bool_t keep_only_largest; /* True if we keep only the largest sets */ } igraph_i_max_ind_vsets_data_t; int igraph_i_maximal_independent_vertex_sets_backtrack(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_i_max_ind_vsets_data_t *clqdata, igraph_integer_t level) { long int v1, v2, v3, c, j, k; igraph_vector_int_t *neis1, *neis2; igraph_bool_t f; igraph_integer_t j1; long int it_state; IGRAPH_ALLOW_INTERRUPTION(); if (level >= clqdata->matrix_size-1) { igraph_integer_t size=0; if (res) { igraph_vector_t *vec; vec = igraph_Calloc(1, igraph_vector_t); if (vec == 0) IGRAPH_ERROR("igraph_i_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); IGRAPH_VECTOR_INIT_FINALLY(vec, 0); for (v1=0; v1matrix_size; v1++) if (clqdata->IS[v1] == 0) { IGRAPH_CHECK(igraph_vector_push_back(vec, v1)); } size=(igraph_integer_t) igraph_vector_size(vec); if (!clqdata->keep_only_largest) IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); else { if (size > clqdata->largest_set_size) { /* We are keeping only the largest sets, and we've found one that's * larger than all previous sets, so we have to clear the list */ j=igraph_vector_ptr_size(res); for (v1=0; v1largest_set_size) { IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); } else { igraph_vector_destroy(vec); free(vec); } } IGRAPH_FINALLY_CLEAN(1); } else { for (v1=0, size=0; v1matrix_size; v1++) if (clqdata->IS[v1] == 0) size++; } if (size>clqdata->largest_set_size) clqdata->largest_set_size=size; } else { v1 = level+1; /* Count the number of vertices with an index less than v1 that have * an IS value of zero */ neis1 = igraph_adjlist_get(&clqdata->adj_list, v1); c = 0; j = 0; while (jdeg)[v1] && (v2=(long int) VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) c++; j++; } if (c == 0) { /* If there are no such nodes... */ j = 0; while (jdeg)[v1] && (v2=(long int) VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]++; j++; } IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph,res,clqdata, (igraph_integer_t) v1)); j = 0; while (jdeg)[v1] && (v2=(long int) VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]--; j++; } } else { /* If there are such nodes, store the count in the IS value of v1 */ clqdata->IS[v1] = (igraph_integer_t) c; IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph,res,clqdata, (igraph_integer_t) v1)); clqdata->IS[v1] = 0; f=1; j=0; while (jdeg)[v1] && (v2=(long int) VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) { IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1], (igraph_integer_t) j)); neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); k = 0; while (kdeg)[v2] && (v3=(long int) VECTOR(*neis2)[k])<=level) { clqdata->IS[v3]--; if (clqdata->IS[v3] == 0) f=0; k++; } } clqdata->IS[v2]++; j++; } if (f) IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph,res,clqdata, (igraph_integer_t) v1)); j=0; while (jdeg)[v1] && (v2=(long int) VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]--; j++; } it_state=0; while (igraph_set_iterate(&clqdata->buckets[v1], &it_state, &j1)) { j=(long)j1; v2=(long int) VECTOR(*neis1)[j]; neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); k = 0; while (kdeg)[v2] && (v3=(long int) VECTOR(*neis2)[k])<=level) { clqdata->IS[v3]++; k++; } } igraph_set_clear(&clqdata->buckets[v1]); } } return 0; } void igraph_i_free_set_array(igraph_set_t* array) { long int i = 0; while (igraph_set_inited(array+i)) { igraph_set_destroy(array+i); i++; } igraph_Free(array); } /** * \function igraph_maximal_independent_vertex_sets * \brief Find all maximal independent vertex sets of a graph * * * A maximal independent vertex set is an independent vertex set which * can't be extended any more by adding a new vertex to it. * * * The algorithm used here is based on the following paper: * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm for * generating all the maximal independent sets. SIAM J Computing, * 6:505--517, 1977. * * * The implementation was originally written by Kevin O'Neill and modified * by K M Briggs in the Very Nauty Graph Library. I simply re-wrote it to * use igraph's data structures. * * * If you are interested in the size of the largest independent vertex set, * use \ref igraph_independence_number() instead. * * \param graph The input graph. * \param res Pointer to a pointer vector, the result will be stored * here, ie. \c res will contain pointers to \c igraph_vector_t * objects which contain the indices of vertices involved in an independent * vertex set. The pointer vector will be resized if needed but note that the * objects in the pointer vector will not be freed. * \return Error code. * * \sa \ref igraph_maximal_cliques(), \ref * igraph_independence_number() * * Time complexity: TODO. */ int igraph_maximal_independent_vertex_sets(const igraph_t *graph, igraph_vector_ptr_t *res) { igraph_i_max_ind_vsets_data_t clqdata; igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i; if (igraph_is_directed(graph)) IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); clqdata.matrix_size=no_of_nodes; clqdata.keep_only_largest=0; IGRAPH_CHECK(igraph_adjlist_init(graph, &clqdata.adj_list, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list); clqdata.IS = igraph_Calloc(no_of_nodes, igraph_integer_t); if (clqdata.IS == 0) IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, clqdata.IS); IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); for (i=0; i * The independence number of a graph is the cardinality of the largest * independent vertex set. * * * The current implementation was ported to igraph from the Very Nauty Graph * Library by Keith Briggs and uses the algorithm from the paper * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm * for generating all the maximal independent sets. SIAM J Computing, * 6:505--517, 1977. * * \param graph The input graph. * \param no The independence number will be returned to the \c * igraph_integer_t pointed by this variable. * \return Error code. * * \sa \ref igraph_independent_vertex_sets(). * * Time complexity: TODO. */ int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no) { igraph_i_max_ind_vsets_data_t clqdata; igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i; if (igraph_is_directed(graph)) IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); clqdata.matrix_size=no_of_nodes; clqdata.keep_only_largest=0; IGRAPH_CHECK(igraph_adjlist_init(graph, &clqdata.adj_list, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list); clqdata.IS = igraph_Calloc(no_of_nodes, igraph_integer_t); if (clqdata.IS == 0) IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, clqdata.IS); IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); for (i=0; imin_size || size > data->max_size) return IGRAPH_SUCCESS; vec = igraph_Calloc(1, igraph_vector_t); if (vec == 0) IGRAPH_ERROR("cannot allocate memory for storing next clique", IGRAPH_ENOMEM); IGRAPH_CHECK(igraph_vector_copy(vec, clique)); IGRAPH_CHECK(igraph_vector_ptr_push_back(data->result, vec)); return IGRAPH_SUCCESS; } int igraph_i_largest_cliques_store(const igraph_vector_t* clique, void* data, igraph_bool_t* cont) { igraph_vector_ptr_t* result = (igraph_vector_ptr_t*)data; igraph_vector_t* vec; long int i, n; IGRAPH_UNUSED(cont); /* Is the current clique at least as large as the others that we have found? */ if (!igraph_vector_ptr_empty(result)) { n = igraph_vector_size(clique); if (n < igraph_vector_size(VECTOR(*result)[0])) return IGRAPH_SUCCESS; if (n > igraph_vector_size(VECTOR(*result)[0])) { for (i = 0; i < igraph_vector_ptr_size(result); i++) igraph_vector_destroy(VECTOR(*result)[i]); igraph_vector_ptr_free_all(result); igraph_vector_ptr_resize(result, 0); } } vec = igraph_Calloc(1, igraph_vector_t); if (vec == 0) IGRAPH_ERROR("cannot allocate memory for storing next clique", IGRAPH_ENOMEM); IGRAPH_CHECK(igraph_vector_copy(vec, clique)); IGRAPH_CHECK(igraph_vector_ptr_push_back(result, vec)); return IGRAPH_SUCCESS; } /** * \function igraph_largest_cliques * \brief Finds the largest clique(s) in a graph. * * * A clique is largest (quite intuitively) if there is no other clique * in the graph which contains more vertices. * * * Note that this is not necessarily the same as a maximal clique, * ie. the largest cliques are always maximal but a maximal clique is * not always largest. * * The current implementation of this function searches * for maximal cliques using \ref igraph_maximal_cliques() and drops * those that are not the largest. * * The implementation of this function changed between * igraph 0.5 and 0.6, so the order of the cliques and the order of * vertices within the cliques will almost surely be different between * these two versions. * * \param graph The input graph. * \param res Pointer to an initialized pointer vector, the result * will be stored here. It will be resized as needed. Note that * vertices of a clique may be returned in arbitrary order. * \return Error code. * * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques() * * Time complexity: O(3^(|V|/3)) worst case. */ int igraph_largest_cliques(const igraph_t *graph, igraph_vector_ptr_t *res) { igraph_vector_ptr_clear(res); IGRAPH_FINALLY(igraph_i_cliques_free_res, res); IGRAPH_CHECK(igraph_i_maximal_cliques(graph, &igraph_i_largest_cliques_store, (void*)res)); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * \function igraph_clique_number * \brief Find the clique number of the graph * * * The clique number of a graph is the size of the largest clique. * * \param graph The input graph. * \param no The clique number will be returned to the \c igraph_integer_t * pointed by this variable. * \return Error code. * * \sa \ref igraph_cliques(), \ref igraph_largest_cliques(). * * Time complexity: O(3^(|V|/3)) worst case. */ int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no) { *no = 0; return igraph_i_maximal_cliques(graph, &igraph_i_maximal_cliques_store_max_size, (void*)no); } typedef struct { igraph_vector_int_t cand; igraph_vector_int_t fini; igraph_vector_int_t cand_filtered; } igraph_i_maximal_cliques_stack_frame; void igraph_i_maximal_cliques_stack_frame_destroy(igraph_i_maximal_cliques_stack_frame *frame) { igraph_vector_int_destroy(&frame->cand); igraph_vector_int_destroy(&frame->fini); igraph_vector_int_destroy(&frame->cand_filtered); } void igraph_i_maximal_cliques_stack_destroy(igraph_stack_ptr_t *stack) { igraph_i_maximal_cliques_stack_frame *frame; while (!igraph_stack_ptr_empty(stack)) { frame = (igraph_i_maximal_cliques_stack_frame*)igraph_stack_ptr_pop(stack); igraph_i_maximal_cliques_stack_frame_destroy(frame); free(frame); } igraph_stack_ptr_destroy(stack); } int igraph_i_maximal_cliques(const igraph_t *graph, igraph_i_maximal_clique_func_t func, void* data) { int directed=igraph_is_directed(graph); long int i, j, k, l; igraph_integer_t no_of_nodes, nodes_to_check, nodes_done; igraph_integer_t best_cand = 0, best_cand_degree = 0, best_fini_cand_degree; igraph_adjlist_t adj_list; igraph_stack_ptr_t stack; igraph_i_maximal_cliques_stack_frame frame, *new_frame_ptr; igraph_vector_t clique; igraph_vector_int_t new_cand, new_fini, cn, best_cand_nbrs, best_fini_cand_nbrs; igraph_bool_t cont = 1; int assret; if (directed) IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); no_of_nodes = igraph_vcount(graph); if (no_of_nodes == 0) return IGRAPH_SUCCESS; /* Construct an adjacency list representation */ IGRAPH_CHECK(igraph_adjlist_init(graph, &adj_list, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adj_list); IGRAPH_CHECK(igraph_adjlist_simplify(&adj_list)); igraph_adjlist_sort(&adj_list); /* Initialize stack */ IGRAPH_CHECK(igraph_stack_ptr_init(&stack, 0)); IGRAPH_FINALLY(igraph_i_maximal_cliques_stack_destroy, &stack); /* Create the initial (empty) clique */ IGRAPH_VECTOR_INIT_FINALLY(&clique, 0); /* Initialize new_cand, new_fini, cn, best_cand_nbrs and best_fini_cand_nbrs (will be used later) */ igraph_vector_int_init(&new_cand, 0); IGRAPH_FINALLY(igraph_vector_int_destroy, &new_cand); igraph_vector_int_init(&new_fini, 0); IGRAPH_FINALLY(igraph_vector_int_destroy, &new_fini); igraph_vector_int_init(&cn, 0); IGRAPH_FINALLY(igraph_vector_int_destroy, &cn); igraph_vector_int_init(&best_cand_nbrs, 0); IGRAPH_FINALLY(igraph_vector_int_destroy, &best_cand_nbrs); igraph_vector_int_init(&best_fini_cand_nbrs, 0); IGRAPH_FINALLY(igraph_vector_int_destroy, &best_fini_cand_nbrs); /* Find the vertex with the highest degree */ best_cand = 0; best_cand_degree = (igraph_integer_t) igraph_vector_int_size(igraph_adjlist_get(&adj_list, 0)); for (i = 1; i < no_of_nodes; i++) { j = igraph_vector_int_size(igraph_adjlist_get(&adj_list, i)); if (j > best_cand_degree) { best_cand = (igraph_integer_t) i; best_cand_degree = (igraph_integer_t) j; } } /* Create the initial stack frame */ IGRAPH_CHECK(igraph_vector_int_init_seq(&frame.cand, 0, no_of_nodes-1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &frame.cand); IGRAPH_CHECK(igraph_vector_int_init(&frame.fini, 0)); IGRAPH_FINALLY(igraph_vector_int_destroy, &frame.fini); IGRAPH_CHECK(igraph_vector_int_init(&frame.cand_filtered, 0)); IGRAPH_FINALLY(igraph_vector_int_destroy, &frame.cand_filtered); IGRAPH_CHECK(igraph_vector_int_difference_sorted(&frame.cand, igraph_adjlist_get(&adj_list, best_cand), &frame.cand_filtered)); IGRAPH_FINALLY_CLEAN(3); IGRAPH_FINALLY(igraph_i_maximal_cliques_stack_frame_destroy, &frame); /* TODO: frame.cand and frame.fini should be a set instead of a vector */ /* Main loop starts here */ nodes_to_check = (igraph_integer_t) igraph_vector_int_size(&frame.cand_filtered); nodes_done = 0; while (!igraph_vector_int_empty(&frame.cand_filtered) || !igraph_stack_ptr_empty(&stack)) { if (igraph_vector_int_empty(&frame.cand_filtered)) { /* No candidates left to check in this stack frame, pop out the previous stack frame */ igraph_i_maximal_cliques_stack_frame *newframe = igraph_stack_ptr_pop(&stack); igraph_i_maximal_cliques_stack_frame_destroy(&frame); frame = *newframe; free(newframe); if (igraph_stack_ptr_size(&stack) == 1) { /* We will be using the next candidate node in the next iteration, so we can increase * nodes_done by 1 */ nodes_done++; } /* For efficiency reasons, we only check for interruption and show progress here */ IGRAPH_PROGRESS("Maximal cliques: ", 100.0 * nodes_done / nodes_to_check, NULL); IGRAPH_ALLOW_INTERRUPTION(); igraph_vector_pop_back(&clique); continue; } /* Try the next node in the clique */ i = (long int) igraph_vector_int_pop_back(&frame.cand_filtered); IGRAPH_CHECK(igraph_vector_push_back(&clique, i)); /* Remove the node from the candidate list */ assret=igraph_vector_int_binsearch(&frame.cand, i, &j); assert(assret); igraph_vector_int_remove(&frame.cand, j); /* Add the node to the finished list */ assret = !igraph_vector_int_binsearch(&frame.fini, i, &j); assert(assret); IGRAPH_CHECK(igraph_vector_int_insert(&frame.fini, j, i)); /* Create new_cand and new_fini */ IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&frame.cand, igraph_adjlist_get(&adj_list, i), &new_cand)); IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&frame.fini, igraph_adjlist_get(&adj_list, i), &new_fini)); /* Do we have anything more to search? */ if (igraph_vector_int_empty(&new_cand)) { if (igraph_vector_int_empty(&new_fini)) { /* We have a maximal clique here */ IGRAPH_CHECK(func(&clique, data, &cont)); if (!cont) { /* The callback function requested to stop the search */ break; } } igraph_vector_pop_back(&clique); continue; } if (igraph_vector_int_empty(&new_fini) && igraph_vector_int_size(&new_cand) == 1) { /* Shortcut: only one node left */ IGRAPH_CHECK(igraph_vector_push_back(&clique, VECTOR(new_cand)[0])); IGRAPH_CHECK(func(&clique, data, &cont)); if (!cont) { /* The callback function requested to stop the search */ break; } igraph_vector_pop_back(&clique); igraph_vector_pop_back(&clique); continue; } /* Find the next best candidate node in new_fini */ l = igraph_vector_int_size(&new_cand); best_cand_degree = -1; j = igraph_vector_int_size(&new_fini); for (i = 0; i < j; i++) { k = (long int)VECTOR(new_fini)[i]; IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&new_cand, igraph_adjlist_get(&adj_list, k), &cn)); if (igraph_vector_int_size(&cn) > best_cand_degree) { best_cand_degree = (igraph_integer_t) igraph_vector_int_size(&cn); IGRAPH_CHECK(igraph_vector_int_update(&best_fini_cand_nbrs, &cn)); if (best_cand_degree == l) { /* Cool, we surely have the best candidate node here as best_cand_degree can't get any better */ break; } } } /* Shortcut here: we don't have to examine new_cand */ if (best_cand_degree == l) { igraph_vector_pop_back(&clique); continue; } /* Still finding best candidate node */ best_fini_cand_degree = best_cand_degree; best_cand_degree = -1; j = igraph_vector_int_size(&new_cand); l = l - 1; for (i = 0; i < j; i++) { k = (long int)VECTOR(new_cand)[i]; IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&new_cand, igraph_adjlist_get(&adj_list, k), &cn)); if (igraph_vector_int_size(&cn) > best_cand_degree) { best_cand_degree = (igraph_integer_t) igraph_vector_int_size(&cn); IGRAPH_CHECK(igraph_vector_int_update(&best_cand_nbrs, &cn)); if (best_cand_degree == l) { /* Cool, we surely have the best candidate node here as best_cand_degree can't get any better */ break; } } } /* Create a new stack frame in case we back out later */ new_frame_ptr = igraph_Calloc(1, igraph_i_maximal_cliques_stack_frame); if (new_frame_ptr == 0) { IGRAPH_ERROR("cannot allocate new stack frame", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, new_frame_ptr); *new_frame_ptr = frame; memset(&frame, 0, sizeof(frame)); IGRAPH_CHECK(igraph_stack_ptr_push(&stack, new_frame_ptr)); IGRAPH_FINALLY_CLEAN(1); /* ownership of new_frame_ptr taken by the stack */ /* Ownership of the current frame and its vectors (frame.cand, frame.done, frame.cand_filtered) * is taken by the stack from now on. Vectors in frame must be re-initialized with new_cand, * new_fini and stuff. The old frame.cand and frame.fini won't be leaked because they are * managed by the stack now. */ frame.cand = new_cand; frame.fini = new_fini; IGRAPH_CHECK(igraph_vector_int_init(&new_cand, 0)); IGRAPH_CHECK(igraph_vector_int_init(&new_fini, 0)); IGRAPH_CHECK(igraph_vector_int_init(&frame.cand_filtered, 0)); /* Adjust frame.cand_filtered */ if (best_cand_degree < best_fini_cand_degree) { IGRAPH_CHECK(igraph_vector_int_difference_sorted(&frame.cand, &best_fini_cand_nbrs, &frame.cand_filtered)); } else { IGRAPH_CHECK(igraph_vector_int_difference_sorted(&frame.cand, &best_cand_nbrs, &frame.cand_filtered)); } } IGRAPH_PROGRESS("Maximal cliques: ", 100.0, NULL); igraph_adjlist_destroy(&adj_list); igraph_vector_destroy(&clique); igraph_vector_int_destroy(&new_cand); igraph_vector_int_destroy(&new_fini); igraph_vector_int_destroy(&cn); igraph_vector_int_destroy(&best_cand_nbrs); igraph_vector_int_destroy(&best_fini_cand_nbrs); igraph_i_maximal_cliques_stack_frame_destroy(&frame); igraph_i_maximal_cliques_stack_destroy(&stack); IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; } int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t *clique_number, igraph_bool_t keep_only_largest, igraph_bool_t complementer) { igraph_i_max_ind_vsets_data_t clqdata; igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i; if (igraph_is_directed(graph)) IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); clqdata.matrix_size=no_of_nodes; clqdata.keep_only_largest=keep_only_largest; if (complementer) IGRAPH_CHECK(igraph_adjlist_init_complementer(graph, &clqdata.adj_list, IGRAPH_ALL, 0)); else IGRAPH_CHECK(igraph_adjlist_init(graph, &clqdata.adj_list, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list); clqdata.IS = igraph_Calloc(no_of_nodes, igraph_integer_t); if (clqdata.IS == 0) IGRAPH_ERROR("igraph_i_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, clqdata.IS); IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_matrix.h" #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "matrix.pmt" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_INT #include "igraph_pmt.h" #include "matrix.pmt" #include "igraph_pmt_off.h" #undef BASE_INT #define BASE_LONG #include "igraph_pmt.h" #include "matrix.pmt" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "matrix.pmt" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "matrix.pmt" #include "igraph_pmt_off.h" #undef BASE_BOOL #define BASE_COMPLEX #include "igraph_pmt.h" #include "matrix.pmt" #include "igraph_pmt_off.h" #undef BASE_COMPLEX #ifndef USING_R int igraph_matrix_complex_print(const igraph_matrix_complex_t *m) { long int nr=igraph_matrix_complex_nrow(m); long int nc=igraph_matrix_complex_ncol(m); long int i, j; for (i=0; idata, &real->data)); return 0; } int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, igraph_matrix_t *imag) { long int nrow=igraph_matrix_complex_nrow(v); long int ncol=igraph_matrix_complex_ncol(v); IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol)); IGRAPH_CHECK(igraph_vector_complex_imag(&v->data, &imag->data)); return 0; } int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, igraph_matrix_t *real, igraph_matrix_t *imag) { long int nrow=igraph_matrix_complex_nrow(v); long int ncol=igraph_matrix_complex_ncol(v); IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol)); IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol)); IGRAPH_CHECK(igraph_vector_complex_realimag(&v->data, &real->data, &imag->data)); return 0; } int igraph_matrix_complex_create(igraph_matrix_complex_t *v, const igraph_matrix_t *real, const igraph_matrix_t *imag) { IGRAPH_CHECK(igraph_vector_complex_create(&v->data, &real->data, &imag->data)); return 0; } int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, const igraph_matrix_t *r, const igraph_matrix_t *theta) { IGRAPH_CHECK(igraph_vector_complex_create_polar(&v->data, &r->data, &theta->data)); return 0; } igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, const igraph_matrix_t *rhs, igraph_real_t tol) { return igraph_vector_e_tol(&lhs->data, &rhs->data, tol); } igraph/src/igraph_random.h0000644000176000001440000001024112325527073015332 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef REST_RANDOM_H #define REST_RANDOM_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS #include "igraph_types.h" #include #include /* The new RNG interface is (somewhat) modelled based on the GSL */ typedef struct igraph_rng_type_t { const char *name; unsigned long int min; unsigned long int max; int (*init)(void **state); void (*destroy)(void *state); int (*seed)(void *state, unsigned long int seed); unsigned long int (*get)(void *state); igraph_real_t (*get_real)(void *state); igraph_real_t (*get_norm)(void *state); igraph_real_t (*get_geom)(void *state, igraph_real_t p); igraph_real_t (*get_binom)(void *state, long int n, igraph_real_t p); igraph_real_t (*get_exp)(void *state, igraph_real_t rate); } igraph_rng_type_t; typedef struct igraph_rng_t { const igraph_rng_type_t *type; void *state; int def; } igraph_rng_t; /* --------------------------------- */ int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type); void igraph_rng_destroy(igraph_rng_t *rng); int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed); unsigned long int igraph_rng_max(igraph_rng_t *rng); unsigned long int igraph_rng_min(igraph_rng_t *rng); const char *igraph_rng_name(igraph_rng_t *rng); long int igraph_rng_get_integer(igraph_rng_t *rng, long int l, long int h); igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, igraph_real_t m, igraph_real_t s); igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, igraph_real_t l, igraph_real_t h); igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng); igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p); igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n, igraph_real_t p); igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); unsigned long int igraph_rng_get_int31(igraph_rng_t *rng); igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); /* --------------------------------- */ extern const igraph_rng_type_t igraph_rngtype_glibc2; extern const igraph_rng_type_t igraph_rngtype_rand; extern const igraph_rng_type_t igraph_rngtype_mt19937; igraph_rng_t *igraph_rng_default(void); void igraph_rng_set_default(igraph_rng_t *rng); /* --------------------------------- */ #ifdef USING_R void GetRNGstate(void); void PutRNGstate(void); #define RNG_BEGIN() GetRNGstate() #define RNG_END() PutRNGstate() #else #define RNG_BEGIN() if (igraph_rng_default()->def==1) { \ igraph_rng_seed(igraph_rng_default(), time(0)); \ igraph_rng_default()->def=2; \ } #define RNG_END() /* do nothing */ #endif #define RNG_INTEGER(l,h) (igraph_rng_get_integer(igraph_rng_default(),(l),(h))) #define RNG_NORMAL(m,s) (igraph_rng_get_normal(igraph_rng_default(),(m),(s))) #define RNG_UNIF(l,h) (igraph_rng_get_unif(igraph_rng_default(),(l),(h))) #define RNG_UNIF01() (igraph_rng_get_unif01(igraph_rng_default())) #define RNG_GEOM(p) (igraph_rng_get_geom(igraph_rng_default(),(p))) #define RNG_BINOM(n,p) (igraph_rng_get_binom(igraph_rng_default(),(n),(p))) #define RNG_INT31() (igraph_rng_get_int31(igraph_rng_default())) __END_DECLS #endif igraph/src/bigint.c0000644000176000001440000002146312325527072013776 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bigint.h" #include "igraph_error.h" #include "igraph_memory.h" #define BASE_LIMB #include "igraph_pmt.h" #include "vector.pmt" #include "igraph_pmt_off.h" #undef BASE_LIMB int igraph_biguint_init(igraph_biguint_t *b) { IGRAPH_CHECK(igraph_vector_limb_init(&b->v, IGRAPH_BIGUINT_DEFAULT_SIZE)); igraph_vector_limb_clear(&b->v); return 0; } void igraph_biguint_destroy(igraph_biguint_t *b) { igraph_vector_limb_destroy(&b->v); } int igraph_biguint_copy(igraph_biguint_t *to, igraph_biguint_t *from) { return igraph_vector_limb_copy(&to->v, &from->v); } int igraph_biguint_extend(igraph_biguint_t *b, limb_t l) { return igraph_vector_limb_push_back(&b->v, l); } int igraph_biguint_size(igraph_biguint_t *b) { return (int) igraph_vector_limb_size(&b->v); } int igraph_biguint_resize(igraph_biguint_t *b, int newlength) { int origlen=igraph_biguint_size(b); IGRAPH_CHECK(igraph_vector_limb_resize(&b->v, newlength)); if (newlength > origlen) { memset(VECTOR(b->v) + origlen, 0, (size_t) (newlength-origlen) * sizeof(limb_t)); } return 0; } int igraph_biguint_reserve(igraph_biguint_t *b, int length) { return igraph_vector_limb_reserve(&b->v, length); } int igraph_biguint_zero(igraph_biguint_t *b) { igraph_vector_limb_clear(&b->v); return 0; } int igraph_biguint_set_limb(igraph_biguint_t *b, int value) { IGRAPH_CHECK(igraph_vector_limb_resize(&b->v, 1)); VECTOR(b->v)[0]=(limb_t) value; return 0; } igraph_real_t igraph_biguint_get(igraph_biguint_t *b) { int size=igraph_biguint_size(b); int i; double val=VECTOR(b->v)[size-1]; if (size==0) { return 0.0; } for (i=size-2; i>=0; i--) { val = val * LIMBMASK + VECTOR(b->v)[i]; if (!IGRAPH_FINITE(val)) break; } return val; } int igraph_biguint_compare_limb(igraph_biguint_t *b, limb_t l) { int n=igraph_biguint_size(b); return bn_cmp_limb(VECTOR(b->v), l, (count_t) n); } int igraph_biguint_compare(igraph_biguint_t *left, igraph_biguint_t *right) { /* bn_cmp requires the two numbers to have the same number of limbs, so we do this partially by hand here */ int size_left=igraph_biguint_size(left); int size_right=igraph_biguint_size(right); while (size_left > size_right) { if (VECTOR(left->v)[--size_left] > 0) { return +1; } } while (size_right > size_left) { if (VECTOR(right->v)[--size_right] > 0) { return -1; } } return bn_cmp( VECTOR(left->v), VECTOR(right->v), (count_t) size_right ); } igraph_bool_t igraph_biguint_equal(igraph_biguint_t *left, igraph_biguint_t *right) { return 0 == igraph_biguint_compare(left, right); } igraph_bool_t igraph_biguint_bigger(igraph_biguint_t *left, igraph_biguint_t *right) { return 0 < igraph_biguint_compare(left, right); } igraph_bool_t igraph_biguint_biggerorequal(igraph_biguint_t *left, igraph_biguint_t *right) { return 0 <= igraph_biguint_compare(left, right); } int igraph_biguint_inc(igraph_biguint_t *res, igraph_biguint_t *b) { return igraph_biguint_add_limb(res, b, 1); } int igraph_biguint_dec(igraph_biguint_t *res, igraph_biguint_t *b) { return igraph_biguint_sub_limb(res, b, 1); } int igraph_biguint_add_limb(igraph_biguint_t *res, igraph_biguint_t *b, limb_t l) { int nlimb=igraph_biguint_size(b); limb_t carry; if (res != b) { IGRAPH_CHECK(igraph_biguint_resize(res, nlimb)); } carry=bn_add_limb( VECTOR(res->v), VECTOR(b->v), l, (count_t) nlimb); if (carry) { IGRAPH_CHECK(igraph_biguint_extend(res, carry)); } return 0; } int igraph_biguint_sub_limb(igraph_biguint_t *res, igraph_biguint_t *b, limb_t l) { int nlimb=igraph_biguint_size(b); if (res != b) { IGRAPH_CHECK(igraph_biguint_resize(res, nlimb)); } /* We don't check the return value here */ bn_sub_limb( VECTOR(res->v), VECTOR(b->v), l, (count_t) nlimb); return 0; } int igraph_biguint_mul_limb(igraph_biguint_t *res, igraph_biguint_t *b, limb_t l) { int nlimb=igraph_biguint_size(b); limb_t carry; if (res!= b) { IGRAPH_CHECK(igraph_biguint_resize(res, nlimb)); } carry=bn_mul_limb( VECTOR(res->v), VECTOR(b->v), l, (count_t) nlimb); if (carry) { IGRAPH_CHECK(igraph_biguint_extend(res, carry)); } return 0; } int igraph_biguint_add(igraph_biguint_t *res, igraph_biguint_t *left, igraph_biguint_t *right) { int size_left=igraph_biguint_size(left); int size_right=igraph_biguint_size(right); limb_t carry; if (size_left > size_right) { IGRAPH_CHECK(igraph_biguint_resize(right, size_left)); size_right=size_left; } else if (size_left < size_right) { IGRAPH_CHECK(igraph_biguint_resize(left, size_right)); size_left=size_right; } IGRAPH_CHECK(igraph_biguint_resize(res, size_left)); carry=bn_add( VECTOR(res->v), VECTOR(left->v), VECTOR(right->v), (count_t) size_left); if (carry) { IGRAPH_CHECK(igraph_biguint_extend(res, carry)); } return 0; } int igraph_biguint_sub(igraph_biguint_t *res, igraph_biguint_t *left, igraph_biguint_t *right) { int size_left=igraph_biguint_size(left); int size_right=igraph_biguint_size(right); if (size_left > size_right) { IGRAPH_CHECK(igraph_biguint_resize(right, size_left)); size_right=size_left; } else if (size_left < size_right) { IGRAPH_CHECK(igraph_biguint_resize(left, size_right)); size_left=size_right; } IGRAPH_CHECK(igraph_biguint_resize(res, size_left)); /* We don't check return value, left should not be smaller than right! */ bn_sub( VECTOR(res->v), VECTOR(left->v), VECTOR(right->v), (count_t) size_left); return 0; } int igraph_biguint_mul(igraph_biguint_t *res, igraph_biguint_t *left, igraph_biguint_t *right) { int size_left=igraph_biguint_size(left); int size_right=igraph_biguint_size(right); if (size_left > size_right) { IGRAPH_CHECK(igraph_biguint_resize(right, size_left)); size_right=size_left; } else if (size_left < size_right) { IGRAPH_CHECK(igraph_biguint_resize(left, size_right)); size_left=size_right; } IGRAPH_CHECK(igraph_biguint_resize(res, 2*size_left)); bn_mul( VECTOR(res->v), VECTOR(left->v), VECTOR(right->v), (count_t) size_left ); return 0; } int igraph_biguint_div(igraph_biguint_t *q, igraph_biguint_t *r, igraph_biguint_t *u, igraph_biguint_t *v) { int ret; int size_q=igraph_biguint_size(q); int size_r=igraph_biguint_size(r); int size_u=igraph_biguint_size(u); int size_v=igraph_biguint_size(v); int size_qru = size_q > size_r ? size_q : size_r; size_qru = size_u > size_qru ? size_u : size_qru; if (size_q < size_qru) { IGRAPH_CHECK(igraph_biguint_resize(q, size_qru)); } if (size_r < size_qru) { IGRAPH_CHECK(igraph_biguint_resize(r, size_qru)); } if (size_u < size_qru) { IGRAPH_CHECK(igraph_biguint_resize(u, size_qru)); } ret=bn_div( VECTOR(q->v), VECTOR(r->v), VECTOR(u->v), VECTOR(v->v), (count_t) size_qru, (count_t) size_v ); if (ret) { IGRAPH_ERROR("Bigint division by zero", IGRAPH_EDIVZERO); } return 0; } #ifndef USING_R int igraph_biguint_print(igraph_biguint_t *b) { return igraph_biguint_fprint(b, stdout); } #endif int igraph_biguint_fprint(igraph_biguint_t *b, FILE *file) { /* It is hard to control memory allocation for the bn2d function, so we do our own version */ int n=igraph_biguint_size(b); long int size=12*n+1; igraph_biguint_t tmp; char *dst; limb_t r; /* Zero? */ if (!bn_cmp_limb(VECTOR(b->v), 0, (count_t) n)) { fputs("0", file); return 0; } IGRAPH_CHECK(igraph_biguint_copy(&tmp, b)); IGRAPH_FINALLY(igraph_biguint_destroy, &tmp); dst=igraph_Calloc(size, char); if (!dst) { IGRAPH_ERROR("Cannot print big number", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, dst); size--; dst[size]='\0'; while (0 != bn_cmp_limb(VECTOR(tmp.v), 0, (count_t) n)) { r=bn_div_limb(VECTOR(tmp.v), VECTOR(tmp.v), 10, (count_t) n); dst[--size] = '0' + (char) r; } fputs(&dst[size], file); igraph_Free(dst); igraph_biguint_destroy(&tmp); IGRAPH_FINALLY_CLEAN(2); return 0; } igraph/src/igraph_heap.h0000644000176000001440000000436612325527073015002 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_HEAP_H #define IGRAPH_HEAP_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* -------------------------------------------------- */ /* Heap */ /* -------------------------------------------------- */ /** * Heap data type. * \ingroup internal */ #define BASE_IGRAPH_REAL #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MAX #define HEAP_TYPE_MIN #include "igraph_pmt.h" #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN #undef BASE_IGRAPH_REAL #define BASE_LONG #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MAX #define HEAP_TYPE_MIN #include "igraph_pmt.h" #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN #undef BASE_LONG #define BASE_CHAR #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MAX #define HEAP_TYPE_MIN #include "igraph_pmt.h" #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN #undef BASE_CHAR #define IGRAPH_HEAP_NULL { 0,0,0 } __END_DECLS #endif igraph/src/igraph_array.h0000644000176000001440000000344712325527073015202 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_ARRAY_H #define IGRAPH_ARRAY_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* -------------------------------------------------- */ /* 3D array */ /* -------------------------------------------------- */ #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "igraph_array_pmt.h" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "igraph_array_pmt.h" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_array_pmt.h" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "igraph_array_pmt.h" #include "igraph_pmt_off.h" #undef BASE_BOOL __END_DECLS #endif igraph/src/glpapi04.c0000644000176000001440000001102412325527073014133 0ustar ripleyusers/* glpapi04.c (problem scaling routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" /*********************************************************************** * NAME * * glp_set_rii - set (change) row scale factor * * SYNOPSIS * * void glp_set_rii(glp_prob *lp, int i, double rii); * * DESCRIPTION * * The routine glp_set_rii sets (changes) the scale factor r[i,i] for * i-th row of the specified problem object. */ void glp_set_rii(glp_prob *lp, int i, double rii) { if (!(1 <= i && i <= lp->m)) xerror("glp_set_rii: i = %d; row number out of range\n", i); if (rii <= 0.0) xerror("glp_set_rii: i = %d; rii = %g; invalid scale factor\n", i, rii); if (lp->valid && lp->row[i]->rii != rii) { GLPAIJ *aij; for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) { if (aij->col->stat == GLP_BS) { /* invalidate the basis factorization */ lp->valid = 0; break; } } } lp->row[i]->rii = rii; return; } /*********************************************************************** * NAME * * glp_set sjj - set (change) column scale factor * * SYNOPSIS * * void glp_set_sjj(glp_prob *lp, int j, double sjj); * * DESCRIPTION * * The routine glp_set_sjj sets (changes) the scale factor s[j,j] for * j-th column of the specified problem object. */ void glp_set_sjj(glp_prob *lp, int j, double sjj) { if (!(1 <= j && j <= lp->n)) xerror("glp_set_sjj: j = %d; column number out of range\n", j); if (sjj <= 0.0) xerror("glp_set_sjj: j = %d; sjj = %g; invalid scale factor\n", j, sjj); if (lp->valid && lp->col[j]->sjj != sjj && lp->col[j]->stat == GLP_BS) { /* invalidate the basis factorization */ lp->valid = 0; } lp->col[j]->sjj = sjj; return; } /*********************************************************************** * NAME * * glp_get_rii - retrieve row scale factor * * SYNOPSIS * * double glp_get_rii(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_rii returns current scale factor r[i,i] for i-th * row of the specified problem object. */ double glp_get_rii(glp_prob *lp, int i) { if (!(1 <= i && i <= lp->m)) xerror("glp_get_rii: i = %d; row number out of range\n", i); return lp->row[i]->rii; } /*********************************************************************** * NAME * * glp_get_sjj - retrieve column scale factor * * SYNOPSIS * * double glp_get_sjj(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_sjj returns current scale factor s[j,j] for j-th * column of the specified problem object. */ double glp_get_sjj(glp_prob *lp, int j) { if (!(1 <= j && j <= lp->n)) xerror("glp_get_sjj: j = %d; column number out of range\n", j); return lp->col[j]->sjj; } /*********************************************************************** * NAME * * glp_unscale_prob - unscale problem data * * SYNOPSIS * * void glp_unscale_prob(glp_prob *lp); * * DESCRIPTION * * The routine glp_unscale_prob performs unscaling of problem data for * the specified problem object. * * "Unscaling" means replacing the current scaling matrices R and S by * unity matrices that cancels the scaling effect. */ void glp_unscale_prob(glp_prob *lp) { int m = glp_get_num_rows(lp); int n = glp_get_num_cols(lp); int i, j; for (i = 1; i <= m; i++) glp_set_rii(lp, i, 1.0); for (j = 1; j <= n; j++) glp_set_sjj(lp, j, 1.0); return; } /* eof */ igraph/src/glpnpp05.c0000644000176000001440000006377512325527073014204 0ustar ripleyusers/* glpnpp05.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpnpp.h" /*********************************************************************** * NAME * * npp_clean_prob - perform initial LP/MIP processing * * SYNOPSIS * * #include "glpnpp.h" * void npp_clean_prob(NPP *npp); * * DESCRIPTION * * The routine npp_clean_prob performs initial LP/MIP processing that * currently includes: * * 1) removing free rows; * * 2) replacing double-sided constraint rows with almost identical * bounds, by equality constraint rows; * * 3) removing fixed columns; * * 4) replacing double-bounded columns with almost identical bounds by * fixed columns and removing those columns; * * 5) initial processing constraint coefficients (not implemented); * * 6) initial processing objective coefficients (not implemented). */ void npp_clean_prob(NPP *npp) { /* perform initial LP/MIP processing */ NPPROW *row, *next_row; NPPCOL *col, *next_col; int ret; xassert(npp == npp); /* process rows which originally are free */ for (row = npp->r_head; row != NULL; row = next_row) { next_row = row->next; if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) { /* process free row */ #ifdef GLP_DEBUG xprintf("1"); #endif npp_free_row(npp, row); /* row was deleted */ } } /* process rows which originally are double-sided inequalities */ for (row = npp->r_head; row != NULL; row = next_row) { next_row = row->next; if (row->lb != -DBL_MAX && row->ub != +DBL_MAX && row->lb < row->ub) { ret = npp_make_equality(npp, row); if (ret == 0) ; else if (ret == 1) { /* row was replaced by equality constraint */ #ifdef GLP_DEBUG xprintf("2"); #endif } else xassert(ret != ret); } } /* process columns which are originally fixed */ for (col = npp->c_head; col != NULL; col = next_col) { next_col = col->next; if (col->lb == col->ub) { /* process fixed column */ #ifdef GLP_DEBUG xprintf("3"); #endif npp_fixed_col(npp, col); /* column was deleted */ } } /* process columns which are originally double-bounded */ for (col = npp->c_head; col != NULL; col = next_col) { next_col = col->next; if (col->lb != -DBL_MAX && col->ub != +DBL_MAX && col->lb < col->ub) { ret = npp_make_fixed(npp, col); if (ret == 0) ; else if (ret == 1) { /* column was replaced by fixed column; process it */ #ifdef GLP_DEBUG xprintf("4"); #endif npp_fixed_col(npp, col); /* column was deleted */ } } } return; } /*********************************************************************** * NAME * * npp_process_row - perform basic row processing * * SYNOPSIS * * #include "glpnpp.h" * int npp_process_row(NPP *npp, NPPROW *row, int hard); * * DESCRIPTION * * The routine npp_process_row performs basic row processing that * currently includes: * * 1) removing empty row; * * 2) removing equality constraint row singleton and corresponding * column; * * 3) removing inequality constraint row singleton and corresponding * column if it was fixed; * * 4) performing general row analysis; * * 5) removing redundant row bounds; * * 6) removing forcing row and corresponding columns; * * 7) removing row which becomes free due to redundant bounds; * * 8) computing implied bounds for all columns in the row and using * them to strengthen current column bounds (MIP only, optional, * performed if the flag hard is on). * * Additionally the routine may activate affected rows and/or columns * for further processing. * * RETURNS * * 0 success; * * GLP_ENOPFS primal/integer infeasibility detected; * * GLP_ENODFS dual infeasibility detected. */ int npp_process_row(NPP *npp, NPPROW *row, int hard) { /* perform basic row processing */ NPPCOL *col; NPPAIJ *aij, *next_aij, *aaa; int ret; /* row must not be free */ xassert(!(row->lb == -DBL_MAX && row->ub == +DBL_MAX)); /* start processing row */ if (row->ptr == NULL) { /* empty row */ ret = npp_empty_row(npp, row); if (ret == 0) { /* row was deleted */ #ifdef GLP_DEBUG xprintf("A"); #endif return 0; } else if (ret == 1) { /* primal infeasibility */ return GLP_ENOPFS; } else xassert(ret != ret); } if (row->ptr->r_next == NULL) { /* row singleton */ col = row->ptr->col; if (row->lb == row->ub) { /* equality constraint */ ret = npp_eq_singlet(npp, row); if (ret == 0) { /* column was fixed, row was deleted */ #ifdef GLP_DEBUG xprintf("B"); #endif /* activate rows affected by column */ for (aij = col->ptr; aij != NULL; aij = aij->c_next) npp_activate_row(npp, aij->row); /* process fixed column */ npp_fixed_col(npp, col); /* column was deleted */ return 0; } else if (ret == 1 || ret == 2) { /* primal/integer infeasibility */ return GLP_ENOPFS; } else xassert(ret != ret); } else { /* inequality constraint */ ret = npp_ineq_singlet(npp, row); if (0 <= ret && ret <= 3) { /* row was deleted */ #ifdef GLP_DEBUG xprintf("C"); #endif /* activate column, since its length was changed due to row deletion */ npp_activate_col(npp, col); if (ret >= 2) { /* column bounds changed significantly or column was fixed */ /* activate rows affected by column */ for (aij = col->ptr; aij != NULL; aij = aij->c_next) npp_activate_row(npp, aij->row); } if (ret == 3) { /* column was fixed; process it */ #ifdef GLP_DEBUG xprintf("D"); #endif npp_fixed_col(npp, col); /* column was deleted */ } return 0; } else if (ret == 4) { /* primal infeasibility */ return GLP_ENOPFS; } else xassert(ret != ret); } } #if 0 /* sometimes this causes too large round-off errors; probably pivot coefficient should be chosen more carefully */ if (row->ptr->r_next->r_next == NULL) { /* row doubleton */ if (row->lb == row->ub) { /* equality constraint */ if (!(row->ptr->col->is_int || row->ptr->r_next->col->is_int)) { /* both columns are continuous */ NPPCOL *q; q = npp_eq_doublet(npp, row); if (q != NULL) { /* column q was eliminated */ #ifdef GLP_DEBUG xprintf("E"); #endif /* now column q is singleton of type "implied slack variable"; we process it here to make sure that on recovering basic solution the row is always active equality constraint (as required by the routine rcv_eq_doublet) */ xassert(npp_process_col(npp, q) == 0); /* column q was deleted; note that row p also may be deleted */ return 0; } } } } #endif /* general row analysis */ ret = npp_analyze_row(npp, row); xassert(0x00 <= ret && ret <= 0xFF); if (ret == 0x33) { /* row bounds are inconsistent with column bounds */ return GLP_ENOPFS; } if ((ret & 0x0F) == 0x00) { /* row lower bound does not exist or redundant */ if (row->lb != -DBL_MAX) { /* remove redundant row lower bound */ #ifdef GLP_DEBUG xprintf("F"); #endif npp_inactive_bound(npp, row, 0); } } else if ((ret & 0x0F) == 0x01) { /* row lower bound can be active */ /* see below */ } else if ((ret & 0x0F) == 0x02) { /* row lower bound is a forcing bound */ #ifdef GLP_DEBUG xprintf("G"); #endif /* process forcing row */ if (npp_forcing_row(npp, row, 0) == 0) fixup: { /* columns were fixed, row was made free */ for (aij = row->ptr; aij != NULL; aij = next_aij) { /* process column fixed by forcing row */ #ifdef GLP_DEBUG xprintf("H"); #endif col = aij->col; next_aij = aij->r_next; /* activate rows affected by column */ for (aaa = col->ptr; aaa != NULL; aaa = aaa->c_next) npp_activate_row(npp, aaa->row); /* process fixed column */ npp_fixed_col(npp, col); /* column was deleted */ } /* process free row (which now is empty due to deletion of all its columns) */ npp_free_row(npp, row); /* row was deleted */ return 0; } } else xassert(ret != ret); if ((ret & 0xF0) == 0x00) { /* row upper bound does not exist or redundant */ if (row->ub != +DBL_MAX) { /* remove redundant row upper bound */ #ifdef GLP_DEBUG xprintf("I"); #endif npp_inactive_bound(npp, row, 1); } } else if ((ret & 0xF0) == 0x10) { /* row upper bound can be active */ /* see below */ } else if ((ret & 0xF0) == 0x20) { /* row upper bound is a forcing bound */ #ifdef GLP_DEBUG xprintf("J"); #endif /* process forcing row */ if (npp_forcing_row(npp, row, 1) == 0) goto fixup; } else xassert(ret != ret); if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) { /* row became free due to redundant bounds removal */ #ifdef GLP_DEBUG xprintf("K"); #endif /* activate its columns, since their length will change due to row deletion */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) npp_activate_col(npp, aij->col); /* process free row */ npp_free_row(npp, row); /* row was deleted */ return 0; } #if 1 /* 23/XII-2009 */ /* row lower and/or upper bounds can be active */ if (npp->sol == GLP_MIP && hard) { /* improve current column bounds (optional) */ if (npp_improve_bounds(npp, row, 1) < 0) return GLP_ENOPFS; } #endif return 0; } /*********************************************************************** * NAME * * npp_improve_bounds - improve current column bounds * * SYNOPSIS * * #include "glpnpp.h" * int npp_improve_bounds(NPP *npp, NPPROW *row, int flag); * * DESCRIPTION * * The routine npp_improve_bounds analyzes specified row (inequality * or equality constraint) to determine implied column bounds and then * uses these bounds to improve (strengthen) current column bounds. * * If the flag is on and current column bounds changed significantly * or the column was fixed, the routine activate rows affected by the * column for further processing. (This feature is intended to be used * in the main loop of the routine npp_process_row.) * * NOTE: This operation can be used for MIP problem only. * * RETURNS * * The routine npp_improve_bounds returns the number of significantly * changed bounds plus the number of column having been fixed due to * bound improvements. However, if the routine detects primal/integer * infeasibility, it returns a negative value. */ int npp_improve_bounds(NPP *npp, NPPROW *row, int flag) { /* improve current column bounds */ NPPCOL *col; NPPAIJ *aij, *next_aij, *aaa; int kase, ret, count = 0; double lb, ub; xassert(npp->sol == GLP_MIP); /* row must not be free */ xassert(!(row->lb == -DBL_MAX && row->ub == +DBL_MAX)); /* determine implied column bounds */ npp_implied_bounds(npp, row); /* and use these bounds to strengthen current column bounds */ for (aij = row->ptr; aij != NULL; aij = next_aij) { col = aij->col; next_aij = aij->r_next; for (kase = 0; kase <= 1; kase++) { /* save current column bounds */ lb = col->lb, ub = col->ub; if (kase == 0) { /* process implied column lower bound */ if (col->ll.ll == -DBL_MAX) continue; ret = npp_implied_lower(npp, col, col->ll.ll); } else { /* process implied column upper bound */ if (col->uu.uu == +DBL_MAX) continue; ret = npp_implied_upper(npp, col, col->uu.uu); } if (ret == 0 || ret == 1) { /* current column bounds did not change or changed, but not significantly; restore current column bounds */ col->lb = lb, col->ub = ub; } else if (ret == 2 || ret == 3) { /* current column bounds changed significantly or column was fixed */ #ifdef GLP_DEBUG xprintf("L"); #endif count++; /* activate other rows affected by column, if required */ if (flag) { for (aaa = col->ptr; aaa != NULL; aaa = aaa->c_next) { if (aaa->row != row) npp_activate_row(npp, aaa->row); } } if (ret == 3) { /* process fixed column */ #ifdef GLP_DEBUG xprintf("M"); #endif npp_fixed_col(npp, col); /* column was deleted */ break; /* for kase */ } } else if (ret == 4) { /* primal/integer infeasibility */ return -1; } else xassert(ret != ret); } } return count; } /*********************************************************************** * NAME * * npp_process_col - perform basic column processing * * SYNOPSIS * * #include "glpnpp.h" * int npp_process_col(NPP *npp, NPPCOL *col); * * DESCRIPTION * * The routine npp_process_col performs basic column processing that * currently includes: * * 1) fixing and removing empty column; * * 2) removing column singleton, which is implied slack variable, and * corresponding row if it becomes free; * * 3) removing bounds of column, which is implied free variable, and * replacing corresponding row by equality constraint. * * Additionally the routine may activate affected rows and/or columns * for further processing. * * RETURNS * * 0 success; * * GLP_ENOPFS primal/integer infeasibility detected; * * GLP_ENODFS dual infeasibility detected. */ int npp_process_col(NPP *npp, NPPCOL *col) { /* perform basic column processing */ NPPROW *row; NPPAIJ *aij; int ret; /* column must not be fixed */ xassert(col->lb < col->ub); /* start processing column */ if (col->ptr == NULL) { /* empty column */ ret = npp_empty_col(npp, col); if (ret == 0) { /* column was fixed and deleted */ #ifdef GLP_DEBUG xprintf("N"); #endif return 0; } else if (ret == 1) { /* dual infeasibility */ return GLP_ENODFS; } else xassert(ret != ret); } if (col->ptr->c_next == NULL) { /* column singleton */ row = col->ptr->row; if (row->lb == row->ub) { /* equality constraint */ if (!col->is_int) slack: { /* implied slack variable */ #ifdef GLP_DEBUG xprintf("O"); #endif npp_implied_slack(npp, col); /* column was deleted */ if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) { /* row became free due to implied slack variable */ #ifdef GLP_DEBUG xprintf("P"); #endif /* activate columns affected by row */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) npp_activate_col(npp, aij->col); /* process free row */ npp_free_row(npp, row); /* row was deleted */ } else { /* row became inequality constraint; activate it since its length changed due to column deletion */ npp_activate_row(npp, row); } return 0; } } else { /* inequality constraint */ if (!col->is_int) { ret = npp_implied_free(npp, col); if (ret == 0) { /* implied free variable */ #ifdef GLP_DEBUG xprintf("Q"); #endif /* column bounds were removed, row was replaced by equality constraint */ goto slack; } else if (ret == 1) { /* column is not implied free variable, because its lower and/or upper bounds can be active */ } else if (ret == 2) { /* dual infeasibility */ return GLP_ENODFS; } } } } /* column still exists */ return 0; } /*********************************************************************** * NAME * * npp_process_prob - perform basic LP/MIP processing * * SYNOPSIS * * #include "glpnpp.h" * int npp_process_prob(NPP *npp, int hard); * * DESCRIPTION * * The routine npp_process_prob performs basic LP/MIP processing that * currently includes: * * 1) initial LP/MIP processing (see the routine npp_clean_prob), * * 2) basic row processing (see the routine npp_process_row), and * * 3) basic column processing (see the routine npp_process_col). * * If the flag hard is on, the routine attempts to improve current * column bounds multiple times within the main processing loop, in * which case this feature may take a time. Otherwise, if the flag hard * is off, improving column bounds is performed only once at the end of * the main loop. (Note that this feature is used for MIP only.) * * The routine uses two sets: the set of active rows and the set of * active columns. Rows/columns are marked by a flag (the field temp in * NPPROW/NPPCOL). If the flag is non-zero, the row/column is active, * in which case it is placed in the beginning of the row/column list; * otherwise, if the flag is zero, the row/column is inactive, in which * case it is placed in the end of the row/column list. If a row/column * being currently processed may affect other rows/columns, the latters * are activated for further processing. * * RETURNS * * 0 success; * * GLP_ENOPFS primal/integer infeasibility detected; * * GLP_ENODFS dual infeasibility detected. */ int npp_process_prob(NPP *npp, int hard) { /* perform basic LP/MIP processing */ NPPROW *row; NPPCOL *col; int processing, ret; /* perform initial LP/MIP processing */ npp_clean_prob(npp); /* activate all remaining rows and columns */ for (row = npp->r_head; row != NULL; row = row->next) row->temp = 1; for (col = npp->c_head; col != NULL; col = col->next) col->temp = 1; /* main processing loop */ processing = 1; while (processing) { processing = 0; /* process all active rows */ for (;;) { row = npp->r_head; if (row == NULL || !row->temp) break; npp_deactivate_row(npp, row); ret = npp_process_row(npp, row, hard); if (ret != 0) goto done; processing = 1; } /* process all active columns */ for (;;) { col = npp->c_head; if (col == NULL || !col->temp) break; npp_deactivate_col(npp, col); ret = npp_process_col(npp, col); if (ret != 0) goto done; processing = 1; } } #if 1 /* 23/XII-2009 */ if (npp->sol == GLP_MIP && !hard) { /* improve current column bounds (optional) */ for (row = npp->r_head; row != NULL; row = row->next) { if (npp_improve_bounds(npp, row, 0) < 0) { ret = GLP_ENOPFS; goto done; } } } #endif /* all seems ok */ ret = 0; done: xassert(ret == 0 || ret == GLP_ENOPFS || ret == GLP_ENODFS); #ifdef GLP_DEBUG xprintf("\n"); #endif return ret; } /**********************************************************************/ int npp_simplex(NPP *npp, const glp_smcp *parm) { /* process LP prior to applying primal/dual simplex method */ int ret; xassert(npp->sol == GLP_SOL); xassert(parm == parm); ret = npp_process_prob(npp, 0); return ret; } /**********************************************************************/ int npp_integer(NPP *npp, const glp_iocp *parm) { /* process MIP prior to applying branch-and-bound method */ NPPROW *row, *prev_row; NPPCOL *col; NPPAIJ *aij; int count, ret; xassert(npp->sol == GLP_MIP); xassert(parm == parm); /*==============================================================*/ /* perform basic MIP processing */ ret = npp_process_prob(npp, 1); if (ret != 0) goto done; /*==============================================================*/ /* binarize problem, if required */ if (parm->binarize) npp_binarize_prob(npp); /*==============================================================*/ /* identify hidden packing inequalities */ count = 0; /* new rows will be added to the end of the row list, so we go from the end to beginning of the row list */ for (row = npp->r_tail; row != NULL; row = prev_row) { prev_row = row->prev; /* skip free row */ if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) continue; /* skip equality constraint */ if (row->lb == row->ub) continue; /* skip row having less than two variables */ if (row->ptr == NULL || row->ptr->r_next == NULL) continue; /* skip row having non-binary variables */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) { col = aij->col; if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) break; } if (aij != NULL) continue; count += npp_hidden_packing(npp, row); } if (count > 0) xprintf("%d hidden packing inequaliti(es) were detected\n", count); /*==============================================================*/ /* identify hidden covering inequalities */ count = 0; /* new rows will be added to the end of the row list, so we go from the end to beginning of the row list */ for (row = npp->r_tail; row != NULL; row = prev_row) { prev_row = row->prev; /* skip free row */ if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) continue; /* skip equality constraint */ if (row->lb == row->ub) continue; /* skip row having less than three variables */ if (row->ptr == NULL || row->ptr->r_next == NULL || row->ptr->r_next->r_next == NULL) continue; /* skip row having non-binary variables */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) { col = aij->col; if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) break; } if (aij != NULL) continue; count += npp_hidden_covering(npp, row); } if (count > 0) xprintf("%d hidden covering inequaliti(es) were detected\n", count); /*==============================================================*/ /* reduce inequality constraint coefficients */ count = 0; /* new rows will be added to the end of the row list, so we go from the end to beginning of the row list */ for (row = npp->r_tail; row != NULL; row = prev_row) { prev_row = row->prev; /* skip equality constraint */ if (row->lb == row->ub) continue; count += npp_reduce_ineq_coef(npp, row); } if (count > 0) xprintf("%d constraint coefficient(s) were reduced\n", count); /*==============================================================*/ #ifdef GLP_DEBUG routine(npp); #endif /*==============================================================*/ /* all seems ok */ ret = 0; done: return ret; } /* eof */ igraph/src/glpenv03.c0000644000176000001440000001533312325527073014160 0ustar ripleyusers/* glpenv03.c (terminal output) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wunused-label" #pragma clang diagnostic ignored "-Wunused-variable" #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_printf - write formatted output to terminal * * SYNOPSIS * * void glp_printf(const char *fmt, ...); * * DESCRIPTION * * The routine glp_printf uses the format control string fmt to format * its parameters and writes the formatted output to the terminal. */ void glp_printf(const char *fmt, ...) { va_list arg; /* va_start(arg, fmt); */ /* xvprintf(fmt, arg); */ /* va_end(arg); */ return; } /*********************************************************************** * NAME * * glp_vprintf - write formatted output to terminal * * SYNOPSIS * * void glp_vprintf(const char *fmt, va_list arg); * * DESCRIPTION * * The routine glp_vprintf uses the format control string fmt to format * its parameters specified by the list arg and writes the formatted * output to the terminal. */ void glp_vprintf(const char *fmt, va_list arg) { ENV *env = get_env_ptr(); /* if terminal output is disabled, do nothing */ /* if (!env->term_out) goto skip; */ /* /\* format the output *\/ */ /* vsprintf(env->term_buf, fmt, arg); */ /* /\* pass the output to the user-defined routine *\/ */ /* if (env->term_hook != NULL) */ /* { if (env->term_hook(env->term_info, env->term_buf) != 0) */ /* goto skip; */ /* } */ /* /\* send the output to the terminal *\/ */ /* fputs(env->term_buf, stdout); */ /* fflush(stdout); */ /* /\* copy the output to the text file *\/ */ /* if (env->tee_file != NULL) */ /* { fputs(env->term_buf, env->tee_file); */ /* fflush(env->tee_file); */ /* } */ skip: return; } /*********************************************************************** * NAME * * glp_term_out - enable/disable terminal output * * SYNOPSIS * * int glp_term_out(int flag); * * DESCRIPTION * * Depending on the parameter flag the routine glp_term_out enables or * disables terminal output performed by glpk routines: * * GLP_ON - enable terminal output; * GLP_OFF - disable terminal output. * * RETURNS * * The routine glp_term_out returns the previous value of the terminal * output flag. */ int glp_term_out(int flag) { ENV *env = get_env_ptr(); int old = env->term_out; if (!(flag == GLP_ON || flag == GLP_OFF)) xerror("glp_term_out: flag = %d; invalid value\n", flag); env->term_out = flag; return old; } /*********************************************************************** * NAME * * glp_term_hook - install hook to intercept terminal output * * SYNOPSIS * * void glp_term_hook(int (*func)(void *info, const char *s), * void *info); * * DESCRIPTION * * The routine glp_term_hook installs a user-defined hook routine to * intercept all terminal output performed by glpk routines. * * This feature can be used to redirect the terminal output to other * destination, for example to a file or a text window. * * The parameter func specifies the user-defined hook routine. It is * called from an internal printing routine, which passes to it two * parameters: info and s. The parameter info is a transit pointer, * specified in the corresponding call to the routine glp_term_hook; * it may be used to pass some information to the hook routine. The * parameter s is a pointer to the null terminated character string, * which is intended to be written to the terminal. If the hook routine * returns zero, the printing routine writes the string s to the * terminal in a usual way; otherwise, if the hook routine returns * non-zero, no terminal output is performed. * * To uninstall the hook routine the parameters func and info should be * specified as NULL. */ void glp_term_hook(int (*func)(void *info, const char *s), void *info) { ENV *env = get_env_ptr(); if (func == NULL) { env->term_hook = NULL; env->term_info = NULL; } else { env->term_hook = func; env->term_info = info; } return; } /*********************************************************************** * NAME * * glp_open_tee - start copying terminal output to text file * * SYNOPSIS * * int glp_open_tee(const char *fname); * * DESCRIPTION * * The routine glp_open_tee starts copying all the terminal output to * an output text file, whose name is specified by the character string * fname. * * RETURNS * * 0 - operation successful * 1 - copying terminal output is already active * 2 - unable to create output file */ int glp_open_tee(const char *fname) { ENV *env = get_env_ptr(); if (env->tee_file != NULL) { /* copying terminal output is already active */ return 1; } env->tee_file = fopen(fname, "w"); if (env->tee_file == NULL) { /* unable to create output file */ return 2; } return 0; } /*********************************************************************** * NAME * * glp_close_tee - stop copying terminal output to text file * * SYNOPSIS * * int glp_close_tee(void); * * DESCRIPTION * * The routine glp_close_tee stops copying the terminal output to the * output text file previously open by the routine glp_open_tee closing * that file. * * RETURNS * * 0 - operation successful * 1 - copying terminal output was not started */ int glp_close_tee(void) { ENV *env = get_env_ptr(); if (env->tee_file == NULL) { /* copying terminal output was not started */ return 1; } fclose(env->tee_file); env->tee_file = NULL; return 0; } /* eof */ igraph/src/glplpf.c0000644000176000001440000007552712325527073014021 0ustar ripleyusers/* glplpf.c (LP basis factorization, Schur complement version) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wself-assign" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glplpf.h" #include "glpenv.h" #define xfault xerror #define _GLPLPF_DEBUG 0 /* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ #define M_MAX 100000000 /* = 100*10^6 */ /* maximal order of the basis matrix */ /*********************************************************************** * NAME * * lpf_create_it - create LP basis factorization * * SYNOPSIS * * #include "glplpf.h" * LPF *lpf_create_it(void); * * DESCRIPTION * * The routine lpf_create_it creates a program object, which represents * a factorization of LP basis. * * RETURNS * * The routine lpf_create_it returns a pointer to the object created. */ LPF *lpf_create_it(void) { LPF *lpf; #if _GLPLPF_DEBUG xprintf("lpf_create_it: warning: debug mode enabled\n"); #endif lpf = xmalloc(sizeof(LPF)); lpf->valid = 0; lpf->m0_max = lpf->m0 = 0; lpf->luf = luf_create_it(); lpf->m = 0; lpf->B = NULL; lpf->n_max = 50; lpf->n = 0; lpf->R_ptr = lpf->R_len = NULL; lpf->S_ptr = lpf->S_len = NULL; lpf->scf = NULL; lpf->P_row = lpf->P_col = NULL; lpf->Q_row = lpf->Q_col = NULL; lpf->v_size = 1000; lpf->v_ptr = 0; lpf->v_ind = NULL; lpf->v_val = NULL; lpf->work1 = lpf->work2 = NULL; return lpf; } /*********************************************************************** * NAME * * lpf_factorize - compute LP basis factorization * * SYNOPSIS * * #include "glplpf.h" * int lpf_factorize(LPF *lpf, int m, const int bh[], int (*col) * (void *info, int j, int ind[], double val[]), void *info); * * DESCRIPTION * * The routine lpf_factorize computes the factorization of the basis * matrix B specified by the routine col. * * The parameter lpf specified the basis factorization data structure * created with the routine lpf_create_it. * * The parameter m specifies the order of B, m > 0. * * The array bh specifies the basis header: bh[j], 1 <= j <= m, is the * number of j-th column of B in some original matrix. The array bh is * optional and can be specified as NULL. * * The formal routine col specifies the matrix B to be factorized. To * obtain j-th column of A the routine lpf_factorize calls the routine * col with the parameter j (1 <= j <= n). In response the routine col * should store row indices and numerical values of non-zero elements * of j-th column of B to locations ind[1,...,len] and val[1,...,len], * respectively, where len is the number of non-zeros in j-th column * returned on exit. Neither zero nor duplicate elements are allowed. * * The parameter info is a transit pointer passed to the routine col. * * RETURNS * * 0 The factorization has been successfully computed. * * LPF_ESING * The specified matrix is singular within the working precision. * * LPF_ECOND * The specified matrix is ill-conditioned. * * For more details see comments to the routine luf_factorize. */ int lpf_factorize(LPF *lpf, int m, const int bh[], int (*col) (void *info, int j, int ind[], double val[]), void *info) { int k, ret; #if _GLPLPF_DEBUG int i, j, len, *ind; double *B, *val; #endif xassert(bh == bh); if (m < 1) xfault("lpf_factorize: m = %d; invalid parameter\n", m); if (m > M_MAX) xfault("lpf_factorize: m = %d; matrix too big\n", m); lpf->m0 = lpf->m = m; /* invalidate the factorization */ lpf->valid = 0; /* allocate/reallocate arrays, if necessary */ if (lpf->R_ptr == NULL) lpf->R_ptr = xcalloc(1+lpf->n_max, sizeof(int)); if (lpf->R_len == NULL) lpf->R_len = xcalloc(1+lpf->n_max, sizeof(int)); if (lpf->S_ptr == NULL) lpf->S_ptr = xcalloc(1+lpf->n_max, sizeof(int)); if (lpf->S_len == NULL) lpf->S_len = xcalloc(1+lpf->n_max, sizeof(int)); if (lpf->scf == NULL) lpf->scf = scf_create_it(lpf->n_max); if (lpf->v_ind == NULL) lpf->v_ind = xcalloc(1+lpf->v_size, sizeof(int)); if (lpf->v_val == NULL) lpf->v_val = xcalloc(1+lpf->v_size, sizeof(double)); if (lpf->m0_max < m) { if (lpf->P_row != NULL) xfree(lpf->P_row); if (lpf->P_col != NULL) xfree(lpf->P_col); if (lpf->Q_row != NULL) xfree(lpf->Q_row); if (lpf->Q_col != NULL) xfree(lpf->Q_col); if (lpf->work1 != NULL) xfree(lpf->work1); if (lpf->work2 != NULL) xfree(lpf->work2); lpf->m0_max = m + 100; lpf->P_row = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); lpf->P_col = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); lpf->Q_row = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); lpf->Q_col = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(int)); lpf->work1 = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(double)); lpf->work2 = xcalloc(1+lpf->m0_max+lpf->n_max, sizeof(double)); } /* try to factorize the basis matrix */ switch (luf_factorize(lpf->luf, m, col, info)) { case 0: break; case LUF_ESING: ret = LPF_ESING; goto done; case LUF_ECOND: ret = LPF_ECOND; goto done; default: xassert(lpf != lpf); } /* the basis matrix has been successfully factorized */ lpf->valid = 1; #if _GLPLPF_DEBUG /* store the basis matrix for debugging */ if (lpf->B != NULL) xfree(lpf->B); xassert(m <= 32767); lpf->B = B = xcalloc(1+m*m, sizeof(double)); ind = xcalloc(1+m, sizeof(int)); val = xcalloc(1+m, sizeof(double)); for (k = 1; k <= m * m; k++) B[k] = 0.0; for (j = 1; j <= m; j++) { len = col(info, j, ind, val); xassert(0 <= len && len <= m); for (k = 1; k <= len; k++) { i = ind[k]; xassert(1 <= i && i <= m); xassert(B[(i - 1) * m + j] == 0.0); xassert(val[k] != 0.0); B[(i - 1) * m + j] = val[k]; } } xfree(ind); xfree(val); #endif /* B = B0, so there are no additional rows/columns */ lpf->n = 0; /* reset the Schur complement factorization */ scf_reset_it(lpf->scf); /* P := Q := I */ for (k = 1; k <= m; k++) { lpf->P_row[k] = lpf->P_col[k] = k; lpf->Q_row[k] = lpf->Q_col[k] = k; } /* make all SVA locations free */ lpf->v_ptr = 1; ret = 0; done: /* return to the calling program */ return ret; } /*********************************************************************** * The routine r_prod computes the product y := y + alpha * R * x, * where x is a n-vector, alpha is a scalar, y is a m0-vector. * * Since matrix R is available by columns, the product is computed as * a linear combination: * * y := y + alpha * (R[1] * x[1] + ... + R[n] * x[n]), * * where R[j] is j-th column of R. */ static void r_prod(LPF *lpf, double y[], double a, const double x[]) { int n = lpf->n; int *R_ptr = lpf->R_ptr; int *R_len = lpf->R_len; int *v_ind = lpf->v_ind; double *v_val = lpf->v_val; int j, beg, end, ptr; double t; for (j = 1; j <= n; j++) { if (x[j] == 0.0) continue; /* y := y + alpha * R[j] * x[j] */ t = a * x[j]; beg = R_ptr[j]; end = beg + R_len[j]; for (ptr = beg; ptr < end; ptr++) y[v_ind[ptr]] += t * v_val[ptr]; } return; } /*********************************************************************** * The routine rt_prod computes the product y := y + alpha * R' * x, * where R' is a matrix transposed to R, x is a m0-vector, alpha is a * scalar, y is a n-vector. * * Since matrix R is available by columns, the product components are * computed as inner products: * * y[j] := y[j] + alpha * (j-th column of R) * x * * for j = 1, 2, ..., n. */ static void rt_prod(LPF *lpf, double y[], double a, const double x[]) { int n = lpf->n; int *R_ptr = lpf->R_ptr; int *R_len = lpf->R_len; int *v_ind = lpf->v_ind; double *v_val = lpf->v_val; int j, beg, end, ptr; double t; for (j = 1; j <= n; j++) { /* t := (j-th column of R) * x */ t = 0.0; beg = R_ptr[j]; end = beg + R_len[j]; for (ptr = beg; ptr < end; ptr++) t += v_val[ptr] * x[v_ind[ptr]]; /* y[j] := y[j] + alpha * t */ y[j] += a * t; } return; } /*********************************************************************** * The routine s_prod computes the product y := y + alpha * S * x, * where x is a m0-vector, alpha is a scalar, y is a n-vector. * * Since matrix S is available by rows, the product components are * computed as inner products: * * y[i] = y[i] + alpha * (i-th row of S) * x * * for i = 1, 2, ..., n. */ static void s_prod(LPF *lpf, double y[], double a, const double x[]) { int n = lpf->n; int *S_ptr = lpf->S_ptr; int *S_len = lpf->S_len; int *v_ind = lpf->v_ind; double *v_val = lpf->v_val; int i, beg, end, ptr; double t; for (i = 1; i <= n; i++) { /* t := (i-th row of S) * x */ t = 0.0; beg = S_ptr[i]; end = beg + S_len[i]; for (ptr = beg; ptr < end; ptr++) t += v_val[ptr] * x[v_ind[ptr]]; /* y[i] := y[i] + alpha * t */ y[i] += a * t; } return; } /*********************************************************************** * The routine st_prod computes the product y := y + alpha * S' * x, * where S' is a matrix transposed to S, x is a n-vector, alpha is a * scalar, y is m0-vector. * * Since matrix R is available by rows, the product is computed as a * linear combination: * * y := y + alpha * (S'[1] * x[1] + ... + S'[n] * x[n]), * * where S'[i] is i-th row of S. */ static void st_prod(LPF *lpf, double y[], double a, const double x[]) { int n = lpf->n; int *S_ptr = lpf->S_ptr; int *S_len = lpf->S_len; int *v_ind = lpf->v_ind; double *v_val = lpf->v_val; int i, beg, end, ptr; double t; for (i = 1; i <= n; i++) { if (x[i] == 0.0) continue; /* y := y + alpha * S'[i] * x[i] */ t = a * x[i]; beg = S_ptr[i]; end = beg + S_len[i]; for (ptr = beg; ptr < end; ptr++) y[v_ind[ptr]] += t * v_val[ptr]; } return; } #if _GLPLPF_DEBUG /*********************************************************************** * The routine check_error computes the maximal relative error between * left- and right-hand sides for the system B * x = b (if tr is zero) * or B' * x = b (if tr is non-zero), where B' is a matrix transposed * to B. (This routine is intended for debugging only.) */ static void check_error(LPF *lpf, int tr, const double x[], const double b[]) { int m = lpf->m; double *B = lpf->B; int i, j; double d, dmax = 0.0, s, t, tmax; for (i = 1; i <= m; i++) { s = 0.0; tmax = 1.0; for (j = 1; j <= m; j++) { if (!tr) t = B[m * (i - 1) + j] * x[j]; else t = B[m * (j - 1) + i] * x[j]; if (tmax < fabs(t)) tmax = fabs(t); s += t; } d = fabs(s - b[i]) / tmax; if (dmax < d) dmax = d; } if (dmax > 1e-8) xprintf("%s: dmax = %g; relative error too large\n", !tr ? "lpf_ftran" : "lpf_btran", dmax); return; } #endif /*********************************************************************** * NAME * * lpf_ftran - perform forward transformation (solve system B*x = b) * * SYNOPSIS * * #include "glplpf.h" * void lpf_ftran(LPF *lpf, double x[]); * * DESCRIPTION * * The routine lpf_ftran performs forward transformation, i.e. solves * the system B*x = b, where B is the basis matrix, x is the vector of * unknowns to be computed, b is the vector of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. * * BACKGROUND * * Solution of the system B * x = b can be obtained by solving the * following augmented system: * * ( B F^) ( x ) ( b ) * ( ) ( ) = ( ) * ( G^ H^) ( y ) ( 0 ) * * which, using the main equality, can be written as follows: * * ( L0 0 ) ( U0 R ) ( x ) ( b ) * P ( ) ( ) Q ( ) = ( ) * ( S I ) ( 0 C ) ( y ) ( 0 ) * * therefore, * * ( x ) ( U0 R )-1 ( L0 0 )-1 ( b ) * ( ) = Q' ( ) ( ) P' ( ) * ( y ) ( 0 C ) ( S I ) ( 0 ) * * Thus, computing the solution includes the following steps: * * 1. Compute * * ( f ) ( b ) * ( ) = P' ( ) * ( g ) ( 0 ) * * 2. Solve the system * * ( f1 ) ( L0 0 )-1 ( f ) ( L0 0 ) ( f1 ) ( f ) * ( ) = ( ) ( ) => ( ) ( ) = ( ) * ( g1 ) ( S I ) ( g ) ( S I ) ( g1 ) ( g ) * * from which it follows that: * * { L0 * f1 = f f1 = inv(L0) * f * { => * { S * f1 + g1 = g g1 = g - S * f1 * * 3. Solve the system * * ( f2 ) ( U0 R )-1 ( f1 ) ( U0 R ) ( f2 ) ( f1 ) * ( ) = ( ) ( ) => ( ) ( ) = ( ) * ( g2 ) ( 0 C ) ( g1 ) ( 0 C ) ( g2 ) ( g1 ) * * from which it follows that: * * { U0 * f2 + R * g2 = f1 f2 = inv(U0) * (f1 - R * g2) * { => * { C * g2 = g1 g2 = inv(C) * g1 * * 4. Compute * * ( x ) ( f2 ) * ( ) = Q' ( ) * ( y ) ( g2 ) */ void lpf_ftran(LPF *lpf, double x[]) { int m0 = lpf->m0; int m = lpf->m; int n = lpf->n; int *P_col = lpf->P_col; int *Q_col = lpf->Q_col; double *fg = lpf->work1; double *f = fg; double *g = fg + m0; int i, ii; #if _GLPLPF_DEBUG double *b; #endif if (!lpf->valid) xfault("lpf_ftran: the factorization is not valid\n"); xassert(0 <= m && m <= m0 + n); #if _GLPLPF_DEBUG /* save the right-hand side vector */ b = xcalloc(1+m, sizeof(double)); for (i = 1; i <= m; i++) b[i] = x[i]; #endif /* (f g) := inv(P) * (b 0) */ for (i = 1; i <= m0 + n; i++) fg[i] = ((ii = P_col[i]) <= m ? x[ii] : 0.0); /* f1 := inv(L0) * f */ luf_f_solve(lpf->luf, 0, f); /* g1 := g - S * f1 */ s_prod(lpf, g, -1.0, f); /* g2 := inv(C) * g1 */ scf_solve_it(lpf->scf, 0, g); /* f2 := inv(U0) * (f1 - R * g2) */ r_prod(lpf, f, -1.0, g); luf_v_solve(lpf->luf, 0, f); /* (x y) := inv(Q) * (f2 g2) */ for (i = 1; i <= m; i++) x[i] = fg[Q_col[i]]; #if _GLPLPF_DEBUG /* check relative error in solution */ check_error(lpf, 0, x, b); xfree(b); #endif return; } /*********************************************************************** * NAME * * lpf_btran - perform backward transformation (solve system B'*x = b) * * SYNOPSIS * * #include "glplpf.h" * void lpf_btran(LPF *lpf, double x[]); * * DESCRIPTION * * The routine lpf_btran performs backward transformation, i.e. solves * the system B'*x = b, where B' is a matrix transposed to the basis * matrix B, x is the vector of unknowns to be computed, b is the vector * of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. * * BACKGROUND * * Solution of the system B' * x = b, where B' is a matrix transposed * to B, can be obtained by solving the following augmented system: * * ( B F^)T ( x ) ( b ) * ( ) ( ) = ( ) * ( G^ H^) ( y ) ( 0 ) * * which, using the main equality, can be written as follows: * * T ( U0 R )T ( L0 0 )T T ( x ) ( b ) * Q ( ) ( ) P ( ) = ( ) * ( 0 C ) ( S I ) ( y ) ( 0 ) * * or, equivalently, as follows: * * ( U'0 0 ) ( L'0 S') ( x ) ( b ) * Q' ( ) ( ) P' ( ) = ( ) * ( R' C') ( 0 I ) ( y ) ( 0 ) * * therefore, * * ( x ) ( L'0 S')-1 ( U'0 0 )-1 ( b ) * ( ) = P ( ) ( ) Q ( ) * ( y ) ( 0 I ) ( R' C') ( 0 ) * * Thus, computing the solution includes the following steps: * * 1. Compute * * ( f ) ( b ) * ( ) = Q ( ) * ( g ) ( 0 ) * * 2. Solve the system * * ( f1 ) ( U'0 0 )-1 ( f ) ( U'0 0 ) ( f1 ) ( f ) * ( ) = ( ) ( ) => ( ) ( ) = ( ) * ( g1 ) ( R' C') ( g ) ( R' C') ( g1 ) ( g ) * * from which it follows that: * * { U'0 * f1 = f f1 = inv(U'0) * f * { => * { R' * f1 + C' * g1 = g g1 = inv(C') * (g - R' * f1) * * 3. Solve the system * * ( f2 ) ( L'0 S')-1 ( f1 ) ( L'0 S') ( f2 ) ( f1 ) * ( ) = ( ) ( ) => ( ) ( ) = ( ) * ( g2 ) ( 0 I ) ( g1 ) ( 0 I ) ( g2 ) ( g1 ) * * from which it follows that: * * { L'0 * f2 + S' * g2 = f1 * { => f2 = inv(L'0) * ( f1 - S' * g2) * { g2 = g1 * * 4. Compute * * ( x ) ( f2 ) * ( ) = P ( ) * ( y ) ( g2 ) */ void lpf_btran(LPF *lpf, double x[]) { int m0 = lpf->m0; int m = lpf->m; int n = lpf->n; int *P_row = lpf->P_row; int *Q_row = lpf->Q_row; double *fg = lpf->work1; double *f = fg; double *g = fg + m0; int i, ii; #if _GLPLPF_DEBUG double *b; #endif if (!lpf->valid) xfault("lpf_btran: the factorization is not valid\n"); xassert(0 <= m && m <= m0 + n); #if _GLPLPF_DEBUG /* save the right-hand side vector */ b = xcalloc(1+m, sizeof(double)); for (i = 1; i <= m; i++) b[i] = x[i]; #endif /* (f g) := Q * (b 0) */ for (i = 1; i <= m0 + n; i++) fg[i] = ((ii = Q_row[i]) <= m ? x[ii] : 0.0); /* f1 := inv(U'0) * f */ luf_v_solve(lpf->luf, 1, f); /* g1 := inv(C') * (g - R' * f1) */ rt_prod(lpf, g, -1.0, f); scf_solve_it(lpf->scf, 1, g); /* g2 := g1 */ g = g; /* f2 := inv(L'0) * (f1 - S' * g2) */ st_prod(lpf, f, -1.0, g); luf_f_solve(lpf->luf, 1, f); /* (x y) := P * (f2 g2) */ for (i = 1; i <= m; i++) x[i] = fg[P_row[i]]; #if _GLPLPF_DEBUG /* check relative error in solution */ check_error(lpf, 1, x, b); xfree(b); #endif return; } /*********************************************************************** * The routine enlarge_sva enlarges the Sparse Vector Area to new_size * locations by reallocating the arrays v_ind and v_val. */ static void enlarge_sva(LPF *lpf, int new_size) { int v_size = lpf->v_size; int used = lpf->v_ptr - 1; int *v_ind = lpf->v_ind; double *v_val = lpf->v_val; xassert(v_size < new_size); while (v_size < new_size) v_size += v_size; lpf->v_size = v_size; lpf->v_ind = xcalloc(1+v_size, sizeof(int)); lpf->v_val = xcalloc(1+v_size, sizeof(double)); xassert(used >= 0); memcpy(&lpf->v_ind[1], &v_ind[1], used * sizeof(int)); memcpy(&lpf->v_val[1], &v_val[1], used * sizeof(double)); xfree(v_ind); xfree(v_val); return; } /*********************************************************************** * NAME * * lpf_update_it - update LP basis factorization * * SYNOPSIS * * #include "glplpf.h" * int lpf_update_it(LPF *lpf, int j, int bh, int len, const int ind[], * const double val[]); * * DESCRIPTION * * The routine lpf_update_it updates the factorization of the basis * matrix B after replacing its j-th column by a new vector. * * The parameter j specifies the number of column of B, which has been * replaced, 1 <= j <= m, where m is the order of B. * * The parameter bh specifies the basis header entry for the new column * of B, which is the number of the new column in some original matrix. * This parameter is optional and can be specified as 0. * * Row indices and numerical values of non-zero elements of the new * column of B should be placed in locations ind[1], ..., ind[len] and * val[1], ..., val[len], resp., where len is the number of non-zeros * in the column. Neither zero nor duplicate elements are allowed. * * RETURNS * * 0 The factorization has been successfully updated. * * LPF_ESING * New basis B is singular within the working precision. * * LPF_ELIMIT * Maximal number of additional rows and columns has been reached. * * BACKGROUND * * Let j-th column of the current basis matrix B have to be replaced by * a new column a. This replacement is equivalent to removing the old * j-th column by fixing it at zero and introducing the new column as * follows: * * ( B F^| a ) * ( B F^) ( | ) * ( ) ---> ( G^ H^| 0 ) * ( G^ H^) (-------+---) * ( e'j 0 | 0 ) * * where ej is a unit vector with 1 in j-th position which used to fix * the old j-th column of B (at zero). Then using the main equality we * have: * * ( B F^| a ) ( B0 F | f ) * ( | ) ( P 0 ) ( | ) ( Q 0 ) * ( G^ H^| 0 ) = ( ) ( G H | g ) ( ) = * (-------+---) ( 0 1 ) (-------+---) ( 0 1 ) * ( e'j 0 | 0 ) ( v' w'| 0 ) * * [ ( B0 F )| ( f ) ] [ ( B0 F ) | ( f ) ] * [ P ( )| P ( ) ] ( Q 0 ) [ P ( ) Q| P ( ) ] * = [ ( G H )| ( g ) ] ( ) = [ ( G H ) | ( g ) ] * [------------+-------- ] ( 0 1 ) [-------------+---------] * [ ( v' w')| 0 ] [ ( v' w') Q| 0 ] * * where: * * ( a ) ( f ) ( f ) ( a ) * ( ) = P ( ) => ( ) = P' * ( ) * ( 0 ) ( g ) ( g ) ( 0 ) * * ( ej ) ( v ) ( v ) ( ej ) * ( e'j 0 ) = ( v' w' ) Q => ( ) = Q' ( ) => ( ) = Q ( ) * ( 0 ) ( w ) ( w ) ( 0 ) * * On the other hand: * * ( B0| F f ) * ( P 0 ) (---+------) ( Q 0 ) ( B0 new F ) * ( ) ( G | H g ) ( ) = new P ( ) new Q * ( 0 1 ) ( | ) ( 0 1 ) ( new G new H ) * ( v'| w' 0 ) * * where: * ( G ) ( H g ) * new F = ( F f ), new G = ( ), new H = ( ), * ( v') ( w' 0 ) * * ( P 0 ) ( Q 0 ) * new P = ( ) , new Q = ( ) . * ( 0 1 ) ( 0 1 ) * * The factorization structure for the new augmented matrix remains the * same, therefore: * * ( B0 new F ) ( L0 0 ) ( U0 new R ) * new P ( ) new Q = ( ) ( ) * ( new G new H ) ( new S I ) ( 0 new C ) * * where: * * new F = L0 * new R => * * new R = inv(L0) * new F = inv(L0) * (F f) = ( R inv(L0)*f ) * * new G = new S * U0 => * * ( G ) ( S ) * new S = new G * inv(U0) = ( ) * inv(U0) = ( ) * ( v') ( v'*inv(U0) ) * * new H = new S * new R + new C => * * new C = new H - new S * new R = * * ( H g ) ( S ) * = ( ) - ( ) * ( R inv(L0)*f ) = * ( w' 0 ) ( v'*inv(U0) ) * * ( H - S*R g - S*inv(L0)*f ) ( C x ) * = ( ) = ( ) * ( w'- v'*inv(U0)*R -v'*inv(U0)*inv(L0)*f) ( y' z ) * * Note that new C is resulted by expanding old C with new column x, * row y', and diagonal element z, where: * * x = g - S * inv(L0) * f = g - S * (new column of R) * * y = w - R'* inv(U'0)* v = w - R'* (new row of S) * * z = - (new row of S) * (new column of R) * * Finally, to replace old B by new B we have to permute j-th and last * (just added) columns of the matrix * * ( B F^| a ) * ( | ) * ( G^ H^| 0 ) * (-------+---) * ( e'j 0 | 0 ) * * and to keep the main equality do the same for matrix Q. */ int lpf_update_it(LPF *lpf, int j, int bh, int len, const int ind[], const double val[]) { int m0 = lpf->m0; int m = lpf->m; #if _GLPLPF_DEBUG double *B = lpf->B; #endif int n = lpf->n; int *R_ptr = lpf->R_ptr; int *R_len = lpf->R_len; int *S_ptr = lpf->S_ptr; int *S_len = lpf->S_len; int *P_row = lpf->P_row; int *P_col = lpf->P_col; int *Q_row = lpf->Q_row; int *Q_col = lpf->Q_col; int v_ptr = lpf->v_ptr; int *v_ind = lpf->v_ind; double *v_val = lpf->v_val; double *a = lpf->work2; /* new column */ double *fg = lpf->work1, *f = fg, *g = fg + m0; double *vw = lpf->work2, *v = vw, *w = vw + m0; double *x = g, *y = w, z; int i, ii, k, ret; xassert(bh == bh); if (!lpf->valid) xfault("lpf_update_it: the factorization is not valid\n"); if (!(1 <= j && j <= m)) xfault("lpf_update_it: j = %d; column number out of range\n", j); xassert(0 <= m && m <= m0 + n); /* check if the basis factorization can be expanded */ if (n == lpf->n_max) { lpf->valid = 0; ret = LPF_ELIMIT; goto done; } /* convert new j-th column of B to dense format */ for (i = 1; i <= m; i++) a[i] = 0.0; for (k = 1; k <= len; k++) { i = ind[k]; if (!(1 <= i && i <= m)) xfault("lpf_update_it: ind[%d] = %d; row number out of rang" "e\n", k, i); if (a[i] != 0.0) xfault("lpf_update_it: ind[%d] = %d; duplicate row index no" "t allowed\n", k, i); if (val[k] == 0.0) xfault("lpf_update_it: val[%d] = %g; zero element not allow" "ed\n", k, val[k]); a[i] = val[k]; } #if _GLPLPF_DEBUG /* change column in the basis matrix for debugging */ for (i = 1; i <= m; i++) B[(i - 1) * m + j] = a[i]; #endif /* (f g) := inv(P) * (a 0) */ for (i = 1; i <= m0+n; i++) fg[i] = ((ii = P_col[i]) <= m ? a[ii] : 0.0); /* (v w) := Q * (ej 0) */ for (i = 1; i <= m0+n; i++) vw[i] = 0.0; vw[Q_col[j]] = 1.0; /* f1 := inv(L0) * f (new column of R) */ luf_f_solve(lpf->luf, 0, f); /* v1 := inv(U'0) * v (new row of S) */ luf_v_solve(lpf->luf, 1, v); /* we need at most 2 * m0 available locations in the SVA to store new column of matrix R and new row of matrix S */ if (lpf->v_size < v_ptr + m0 + m0) { enlarge_sva(lpf, v_ptr + m0 + m0); v_ind = lpf->v_ind; v_val = lpf->v_val; } /* store new column of R */ R_ptr[n+1] = v_ptr; for (i = 1; i <= m0; i++) { if (f[i] != 0.0) v_ind[v_ptr] = i, v_val[v_ptr] = f[i], v_ptr++; } R_len[n+1] = v_ptr - lpf->v_ptr; lpf->v_ptr = v_ptr; /* store new row of S */ S_ptr[n+1] = v_ptr; for (i = 1; i <= m0; i++) { if (v[i] != 0.0) v_ind[v_ptr] = i, v_val[v_ptr] = v[i], v_ptr++; } S_len[n+1] = v_ptr - lpf->v_ptr; lpf->v_ptr = v_ptr; /* x := g - S * f1 (new column of C) */ s_prod(lpf, x, -1.0, f); /* y := w - R' * v1 (new row of C) */ rt_prod(lpf, y, -1.0, v); /* z := - v1 * f1 (new diagonal element of C) */ z = 0.0; for (i = 1; i <= m0; i++) z -= v[i] * f[i]; /* update factorization of new matrix C */ switch (scf_update_exp(lpf->scf, x, y, z)) { case 0: break; case SCF_ESING: lpf->valid = 0; ret = LPF_ESING; goto done; case SCF_ELIMIT: xassert(lpf != lpf); default: xassert(lpf != lpf); } /* expand matrix P */ P_row[m0+n+1] = P_col[m0+n+1] = m0+n+1; /* expand matrix Q */ Q_row[m0+n+1] = Q_col[m0+n+1] = m0+n+1; /* permute j-th and last (just added) column of matrix Q */ i = Q_col[j], ii = Q_col[m0+n+1]; Q_row[i] = m0+n+1, Q_col[m0+n+1] = i; Q_row[ii] = j, Q_col[j] = ii; /* increase the number of additional rows and columns */ lpf->n++; xassert(lpf->n <= lpf->n_max); /* the factorization has been successfully updated */ ret = 0; done: /* return to the calling program */ return ret; } /*********************************************************************** * NAME * * lpf_delete_it - delete LP basis factorization * * SYNOPSIS * * #include "glplpf.h" * void lpf_delete_it(LPF *lpf) * * DESCRIPTION * * The routine lpf_delete_it deletes LP basis factorization specified * by the parameter lpf and frees all memory allocated to this program * object. */ void lpf_delete_it(LPF *lpf) { luf_delete_it(lpf->luf); #if _GLPLPF_DEBUG if (lpf->B != NULL) xfree(lpf->B); #else xassert(lpf->B == NULL); #endif if (lpf->R_ptr != NULL) xfree(lpf->R_ptr); if (lpf->R_len != NULL) xfree(lpf->R_len); if (lpf->S_ptr != NULL) xfree(lpf->S_ptr); if (lpf->S_len != NULL) xfree(lpf->S_len); if (lpf->scf != NULL) scf_delete_it(lpf->scf); if (lpf->P_row != NULL) xfree(lpf->P_row); if (lpf->P_col != NULL) xfree(lpf->P_col); if (lpf->Q_row != NULL) xfree(lpf->Q_row); if (lpf->Q_col != NULL) xfree(lpf->Q_col); if (lpf->v_ind != NULL) xfree(lpf->v_ind); if (lpf->v_val != NULL) xfree(lpf->v_val); if (lpf->work1 != NULL) xfree(lpf->work1); if (lpf->work2 != NULL) xfree(lpf->work2); xfree(lpf); return; } /* eof */ igraph/src/glpenv05.c0000644000176000001440000001624612325527073014166 0ustar ripleyusers/* glpenv05.c (memory allocation) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpapi.h" /* some processors need data to be properly aligned; the macro align_datasize enlarges the specified size of a data item to provide a proper alignment of immediately following data */ #define align_datasize(size) ((((size) + 15) / 16) * 16) /* 16 bytes is sufficient in both 32- and 64-bit environments (8 bytes is not sufficient in 64-bit environment due to jmp_buf) */ /*********************************************************************** * NAME * * glp_malloc - allocate memory block * * SYNOPSIS * * void *glp_malloc(int size); * * DESCRIPTION * * The routine glp_malloc allocates a memory block of size bytes long. * * Note that being allocated the memory block contains arbitrary data * (not binary zeros). * * RETURNS * * The routine glp_malloc returns a pointer to the allocated block. * To free this block the routine glp_free (not free!) must be used. */ void *glp_malloc(int size) { ENV *env = get_env_ptr(); MEM *desc; int size_of_desc = align_datasize(sizeof(MEM)); if (size < 1 || size > INT_MAX - size_of_desc) xerror("glp_malloc: size = %d; invalid parameter\n", size); size += size_of_desc; if (xlcmp(xlset(size), xlsub(env->mem_limit, env->mem_total)) > 0) xerror("glp_malloc: memory limit exceeded\n"); if (env->mem_count == INT_MAX) xerror("glp_malloc: too many memory blocks allocated\n"); desc = malloc(size); if (desc == NULL) xerror("glp_malloc: no memory available\n"); memset(desc, '?', size); desc->flag = MEM_MAGIC; desc->size = size; desc->prev = NULL; desc->next = env->mem_ptr; if (desc->next != NULL) desc->next->prev = desc; env->mem_ptr = desc; env->mem_count++; if (env->mem_cpeak < env->mem_count) env->mem_cpeak = env->mem_count; env->mem_total = xladd(env->mem_total, xlset(size)); if (xlcmp(env->mem_tpeak, env->mem_total) < 0) env->mem_tpeak = env->mem_total; return (void *)((char *)desc + size_of_desc); } /*********************************************************************** * NAME * * glp_calloc - allocate memory block * * SYNOPSIS * * void *glp_calloc(int n, int size); * * DESCRIPTION * * The routine glp_calloc allocates a memory block of (n*size) bytes * long. * * Note that being allocated the memory block contains arbitrary data * (not binary zeros). * * RETURNS * * The routine glp_calloc returns a pointer to the allocated block. * To free this block the routine glp_free (not free!) must be used. */ void *glp_calloc(int n, int size) { if (n < 1) xerror("glp_calloc: n = %d; invalid parameter\n", n); if (size < 1) xerror("glp_calloc: size = %d; invalid parameter\n", size); if (n > INT_MAX / size) xerror("glp_calloc: n = %d; size = %d; array too big\n", n, size); return xmalloc(n * size); } /*********************************************************************** * NAME * * glp_free - free memory block * * SYNOPSIS * * void glp_free(void *ptr); * * DESCRIPTION * * The routine glp_free frees a memory block pointed to by ptr, which * was previuosly allocated by the routine glp_malloc or glp_calloc. */ void glp_free(void *ptr) { ENV *env = get_env_ptr(); MEM *desc; int size_of_desc = align_datasize(sizeof(MEM)); if (ptr == NULL) xerror("glp_free: ptr = %p; null pointer\n", ptr); desc = (void *)((char *)ptr - size_of_desc); if (desc->flag != MEM_MAGIC) xerror("glp_free: ptr = %p; invalid pointer\n", ptr); if (env->mem_count == 0 || xlcmp(env->mem_total, xlset(desc->size)) < 0) xerror("glp_free: memory allocation error\n"); if (desc->prev == NULL) env->mem_ptr = desc->next; else desc->prev->next = desc->next; if (desc->next == NULL) ; else desc->next->prev = desc->prev; env->mem_count--; env->mem_total = xlsub(env->mem_total, xlset(desc->size)); memset(desc, '?', size_of_desc); free(desc); return; } /*********************************************************************** * NAME * * glp_mem_limit - set memory usage limit * * SYNOPSIS * * void glp_mem_limit(int limit); * * DESCRIPTION * * The routine glp_mem_limit limits the amount of memory available for * dynamic allocation (in GLPK routines) to limit megabytes. */ void glp_mem_limit(int limit) { ENV *env = get_env_ptr(); if (limit < 0) xerror("glp_mem_limit: limit = %d; invalid parameter\n", limit); env->mem_limit = xlmul(xlset(limit), xlset(1 << 20)); return; } /*********************************************************************** * NAME * * glp_mem_usage - get memory usage information * * SYNOPSIS * * void glp_mem_usage(int *count, int *cpeak, glp_long *total, * glp_long *tpeak); * * DESCRIPTION * * The routine glp_mem_usage reports some information about utilization * of the memory by GLPK routines. Information is stored to locations * specified by corresponding parameters (see below). Any parameter can * be specified as NULL, in which case corresponding information is not * stored. * * *count is the number of the memory blocks currently allocated by the * routines xmalloc and xcalloc (one call to xmalloc or xcalloc results * in allocating one memory block). * * *cpeak is the peak value of *count reached since the initialization * of the GLPK library environment. * * *total is the total amount, in bytes, of the memory blocks currently * allocated by the routines xmalloc and xcalloc. * * *tpeak is the peak value of *total reached since the initialization * of the GLPK library envirionment. */ void glp_mem_usage(int *count, int *cpeak, glp_long *total, glp_long *tpeak) { ENV *env = get_env_ptr(); if (count != NULL) *count = env->mem_count; if (cpeak != NULL) *cpeak = env->mem_cpeak; if (total != NULL) *total = env->mem_total; if (tpeak != NULL) *tpeak = env->mem_tpeak; return; } /* eof */ igraph/src/gengraph_vertex_cover.h0000644000176000001440000000410412325527073017107 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _VERTEX_COVER_H #define _VERTEX_COVER_H // vertex_cover() builds a list of vertices which covers every edge of the graph // Input is a classical adjacency-list graph // As an output, vertex_cover() modify the degrees in degs[], so that // any vertex with a degree > 0 belongs to the vertex coverage. // Moreover, vertex_cover() keeps links[] intact, permuting only the adjacency lists #include "gengraph_box_list.h" namespace gengraph { void vertex_cover(int n, int *links, int *deg, int **neigh = NULL) { int i; // create and initialize neigh[] if (neigh==NULL) { neigh = new int*[n]; neigh[0] = links; for(i=1; i=0) bl.pop_vertex(v, neigh); // remove vertex of max degree and its highest-degree neighbour if(!bl.is_empty()) { v=bl.get_max(); int *w = neigh[v]; register int v2 = *(w++); register int dm = deg[v2]; register int k = deg[v]-1; while(k--) if(deg[*(w++)]>dm) { v2 = *(w-1); dm=deg[v2]; }; bl.pop_vertex(v, neigh); bl.pop_vertex(v2,neigh); } } while(!bl.is_empty()); } } // namespace gengraph #endif //_VERTEX_COVER_H igraph/src/gengraph_header.h0000644000176000001440000000576012325527073015635 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gengraph_definitions.h" #include #include #include "gengraph_random.h" namespace gengraph { static KW_RNG::RNG _my_random; int my_random() { return _my_random.rand_int31(); } void my_srandom(int x) { _my_random.init(x,!x*13,x*x+1,(x>>16)+(x<<16)); } int my_binomial(double pp, int n) { return _my_random.binomial(pp,n); } double my_random01() { return _my_random.rand_halfopen01(); } } #ifdef _WIN32 #include #include void set_priority_low() { HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,TRUE,_getpid()); SetPriorityClass(hProcess,IDLE_PRIORITY_CLASS); } #else #include #endif namespace gengraph { static int VERB; int VERBOSE() { return VERB; } void SET_VERBOSE(int v) { VERB = v; } //Hash profiling static unsigned long _hash_rm_i = 0; static unsigned long _hash_rm_c = 0; static unsigned long _hash_add_i = 0; static unsigned long _hash_add_c = 0; static unsigned long _hash_put_i = 0; static unsigned long _hash_put_c = 0; static unsigned long _hash_find_i = 0; static unsigned long _hash_find_c = 0; static unsigned long _hash_rand_i = 0; static unsigned long _hash_rand_c = 0; static unsigned long _hash_expand = 0; inline void _hash_add_iter() { _hash_add_i++; } inline void _hash_add_call() { _hash_add_c++; } inline void _hash_put_iter() { _hash_put_i++; } inline void _hash_put_call() { _hash_put_c++; } inline void _hash_rm_iter() { _hash_rm_i++; } inline void _hash_rm_call() { _hash_rm_c++; } inline void _hash_find_iter() { _hash_find_i++; } inline void _hash_find_call() { _hash_find_c++; } inline void _hash_rand_iter() { _hash_rand_i++; } inline void _hash_rand_call() { _hash_rand_c++; } inline void _hash_expand_call() { _hash_expand++; } // void _hash_prof() { // fprintf(stderr,"HASH_ADD : %lu / %lu\n", _hash_add_c , _hash_add_i); // fprintf(stderr,"HASH_PUT : %lu / %lu\n", _hash_put_c , _hash_put_i); // fprintf(stderr,"HASH_FIND: %lu / %lu\n", _hash_find_c, _hash_find_i); // fprintf(stderr,"HASH_RM : %lu / %lu\n", _hash_rm_c , _hash_rm_i); // fprintf(stderr,"HASH_RAND: %lu / %lu\n", _hash_rand_c, _hash_rand_i); // fprintf(stderr,"HASH_EXPAND : %lu calls\n", _hash_expand); // } } // namespace gengraph igraph/src/foreign-pajek-lexer.c0000644000176000001440000022556212325527073016367 0ustar ripleyusers#line 2 "lex.yy.c" #line 4 "lex.yy.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE igraph_pajek_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via igraph_pajek_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void igraph_pajek_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void igraph_pajek_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_pajek_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void igraph_pajek_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_pajek_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_pajek_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void igraph_pajek_yypop_buffer_state (yyscan_t yyscanner ); static void igraph_pajek_yyensure_buffer_stack (yyscan_t yyscanner ); static void igraph_pajek_yy_load_buffer_state (yyscan_t yyscanner ); static void igraph_pajek_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER igraph_pajek_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE igraph_pajek_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_pajek_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_pajek_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *igraph_pajek_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *igraph_pajek_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void igraph_pajek_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer igraph_pajek_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ igraph_pajek_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ igraph_pajek_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define igraph_pajek_yywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (yy_size_t) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 47 #define YY_END_OF_BUFFER 48 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[160] = { 0, 1, 1, 48, 46, 1, 12, 12, 46, 46, 46, 46, 46, 15, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 1, 12, 46, 0, 13, 46, 0, 2, 3, 46, 0, 14, 46, 46, 46, 46, 46, 15, 46, 46, 29, 46, 46, 46, 46, 46, 26, 46, 46, 46, 46, 46, 46, 38, 46, 46, 46, 46, 27, 46, 23, 22, 28, 46, 46, 30, 46, 46, 13, 2, 2, 14, 46, 46, 46, 46, 46, 15, 46, 15, 33, 34, 37, 19, 20, 46, 46, 31, 32, 18, 35, 36, 43, 41, 39, 46, 42, 46, 46, 46, 46, 46, 3, 46, 46, 46, 4, 46, 46, 45, 46, 21, 46, 25, 46, 46, 7, 46, 46, 46, 46, 24, 40, 44, 46, 46, 46, 8, 46, 46, 46, 46, 46, 46, 46, 11, 46, 46, 16, 17, 46, 46, 5, 46, 9, 46, 6, 10, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 5, 1, 1, 6, 1, 1, 7, 8, 9, 10, 1, 11, 12, 1, 13, 14, 15, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 1, 1, 1, 1, 41, 1, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[42] = { 0, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int16_t yy_base[167] = { 0, 0, 0, 288, 0, 285, 282, 282, 40, 44, 47, 36, 44, 53, 67, 42, 72, 255, 39, 265, 47, 96, 81, 84, 87, 91, 250, 99, 240, 239, 0, 277, 289, 103, 273, 0, 107, 74, 273, 113, 116, 268, 0, 243, 255, 257, 252, 251, 117, 108, 125, 289, 139, 142, 145, 148, 151, 289, 128, 155, 160, 163, 166, 169, 289, 172, 175, 178, 181, 289, 246, 289, 289, 289, 229, 242, 289, 246, 245, 289, 261, 130, 289, 246, 241, 228, 227, 228, 173, 176, 181, 289, 289, 289, 289, 289, 225, 195, 289, 289, 289, 289, 289, 289, 289, 289, 234, 289, 200, 237, 203, 240, 239, 251, 220, 232, 219, 213, 215, 206, 289, 209, 289, 212, 289, 230, 229, 220, 212, 220, 214, 218, 289, 289, 289, 207, 206, 215, 212, 199, 204, 217, 215, 218, 167, 168, 0, 135, 107, 289, 289, 91, 80, 0, 63, 0, 58, 0, 0, 289, 79, 222, 224, 226, 228, 230, 232 } ; static yyconst flex_int16_t yy_def[167] = { 0, 159, 1, 159, 160, 159, 159, 159, 161, 162, 163, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 159, 159, 161, 164, 160, 162, 165, 159, 165, 163, 166, 160, 160, 160, 160, 160, 160, 160, 160, 160, 159, 160, 160, 160, 160, 160, 159, 160, 160, 160, 160, 160, 160, 159, 160, 160, 160, 160, 159, 160, 159, 159, 159, 160, 160, 159, 160, 160, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 160, 160, 159, 159, 159, 159, 159, 159, 159, 159, 160, 159, 160, 160, 160, 160, 160, 159, 160, 160, 160, 160, 160, 160, 159, 160, 159, 160, 159, 160, 160, 160, 160, 160, 160, 160, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 159, 159, 160, 160, 160, 160, 160, 160, 160, 160, 0, 159, 159, 159, 159, 159, 159, 159 } ; static yyconst flex_int16_t yy_nxt[331] = { 0, 4, 5, 6, 7, 8, 9, 10, 4, 11, 4, 12, 4, 13, 13, 13, 14, 15, 16, 4, 4, 17, 4, 18, 19, 20, 21, 4, 4, 4, 22, 23, 24, 25, 4, 26, 4, 27, 28, 29, 4, 4, 34, 34, 34, 35, 37, 38, 39, 41, 41, 41, 43, 59, 60, 42, 44, 48, 48, 48, 55, 62, 63, 45, 46, 49, 48, 48, 48, 51, 51, 51, 47, 50, 57, 57, 57, 38, 39, 56, 30, 52, 53, 69, 69, 69, 71, 71, 71, 72, 72, 72, 158, 73, 73, 73, 157, 54, 64, 64, 64, 76, 76, 76, 70, 34, 34, 34, 35, 37, 38, 39, 65, 156, 66, 74, 81, 39, 41, 41, 41, 88, 88, 88, 42, 155, 67, 154, 68, 49, 48, 48, 48, 113, 80, 89, 89, 50, 90, 90, 90, 91, 91, 91, 92, 92, 92, 93, 93, 93, 94, 94, 94, 95, 95, 95, 96, 98, 98, 98, 153, 97, 99, 99, 99, 100, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, 104, 104, 104, 105, 105, 105, 107, 107, 107, 88, 88, 88, 90, 90, 90, 152, 50, 90, 90, 90, 120, 120, 120, 151, 106, 122, 122, 122, 124, 124, 124, 132, 132, 132, 133, 133, 133, 134, 134, 134, 149, 149, 149, 150, 150, 150, 33, 33, 36, 36, 40, 40, 34, 34, 37, 37, 41, 41, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 131, 130, 129, 128, 127, 113, 126, 125, 123, 121, 119, 118, 117, 116, 115, 114, 80, 112, 111, 110, 109, 108, 87, 86, 85, 84, 83, 82, 80, 79, 31, 78, 77, 75, 61, 58, 32, 32, 31, 159, 3, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159 } ; static yyconst flex_int16_t yy_chk[331] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 18, 18, 10, 11, 12, 12, 12, 15, 20, 20, 11, 11, 13, 13, 13, 13, 14, 14, 14, 11, 13, 16, 16, 16, 37, 37, 15, 160, 14, 14, 22, 22, 22, 23, 23, 23, 24, 24, 24, 156, 25, 25, 25, 154, 14, 21, 21, 21, 27, 27, 27, 22, 33, 33, 33, 33, 36, 36, 36, 21, 152, 21, 25, 39, 39, 40, 40, 40, 49, 49, 49, 40, 151, 21, 148, 21, 48, 48, 48, 48, 81, 81, 50, 50, 48, 50, 50, 50, 52, 52, 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 58, 59, 59, 59, 147, 58, 60, 60, 60, 61, 61, 61, 62, 62, 62, 63, 63, 63, 65, 65, 65, 66, 66, 66, 67, 67, 67, 68, 68, 68, 88, 88, 88, 89, 89, 89, 145, 88, 90, 90, 90, 97, 97, 97, 144, 67, 108, 108, 108, 110, 110, 110, 119, 119, 119, 121, 121, 121, 123, 123, 123, 142, 142, 142, 143, 143, 143, 161, 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, 166, 141, 140, 139, 138, 137, 136, 135, 131, 130, 129, 128, 127, 126, 125, 118, 117, 116, 115, 114, 113, 112, 111, 109, 106, 96, 87, 86, 85, 84, 83, 80, 78, 77, 75, 74, 70, 47, 46, 45, 44, 43, 41, 38, 34, 31, 29, 28, 26, 19, 17, 7, 6, 5, 3, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "igraph/src/foreign-pajek-lexer.l" /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #line 24 "igraph/src/foreign-pajek-lexer.l" /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-pajek-header.h" #include "foreign-pajek-parser.h" #define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); #define YY_NO_INPUT 1 #line 623 "lex.yy.c" #define INITIAL 0 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; yy_size_t yy_n_chars; yy_size_t yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r int igraph_pajek_yylex_init (yyscan_t* scanner); int igraph_pajek_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int igraph_pajek_yylex_destroy (yyscan_t yyscanner ); int igraph_pajek_yyget_debug (yyscan_t yyscanner ); void igraph_pajek_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE igraph_pajek_yyget_extra (yyscan_t yyscanner ); void igraph_pajek_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *igraph_pajek_yyget_in (yyscan_t yyscanner ); void igraph_pajek_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *igraph_pajek_yyget_out (yyscan_t yyscanner ); void igraph_pajek_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t igraph_pajek_yyget_leng (yyscan_t yyscanner ); char *igraph_pajek_yyget_text (yyscan_t yyscanner ); int igraph_pajek_yyget_lineno (yyscan_t yyscanner ); void igraph_pajek_yyset_lineno (int line_number ,yyscan_t yyscanner ); YYSTYPE * igraph_pajek_yyget_lval (yyscan_t yyscanner ); void igraph_pajek_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *igraph_pajek_yyget_lloc (yyscan_t yyscanner ); void igraph_pajek_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int igraph_pajek_yywrap (yyscan_t yyscanner ); #else extern int igraph_pajek_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ yy_size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int igraph_pajek_yylex \ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int igraph_pajek_yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; #line 81 "igraph/src/foreign-pajek-lexer.l" #line 865 "lex.yy.c" yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { igraph_pajek_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_pajek_yy_load_buffer_state(yyscanner ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 160 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 289 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 83 "igraph/src/foreign-pajek-lexer.l" { } YY_BREAK case 2: /* rule 2 can match eol */ YY_RULE_SETUP #line 84 "igraph/src/foreign-pajek-lexer.l" { } YY_BREAK case 3: /* rule 3 can match eol */ YY_RULE_SETUP #line 85 "igraph/src/foreign-pajek-lexer.l" { } YY_BREAK case 4: YY_RULE_SETUP #line 86 "igraph/src/foreign-pajek-lexer.l" { return NETWORKLINE; } YY_BREAK case 5: YY_RULE_SETUP #line 87 "igraph/src/foreign-pajek-lexer.l" { return NETWORKLINE; } YY_BREAK case 6: YY_RULE_SETUP #line 88 "igraph/src/foreign-pajek-lexer.l" { return VERTICESLINE; } YY_BREAK case 7: YY_RULE_SETUP #line 89 "igraph/src/foreign-pajek-lexer.l" { return ARCSLINE; } YY_BREAK case 8: YY_RULE_SETUP #line 90 "igraph/src/foreign-pajek-lexer.l" { return EDGESLINE; } YY_BREAK case 9: YY_RULE_SETUP #line 91 "igraph/src/foreign-pajek-lexer.l" { return ARCSLISTLINE; } YY_BREAK case 10: YY_RULE_SETUP #line 92 "igraph/src/foreign-pajek-lexer.l" { return EDGESLISTLINE; } YY_BREAK case 11: YY_RULE_SETUP #line 93 "igraph/src/foreign-pajek-lexer.l" { return MATRIXLINE; } YY_BREAK case 12: /* rule 12 can match eol */ YY_RULE_SETUP #line 94 "igraph/src/foreign-pajek-lexer.l" { yyextra->mode=0; return NEWLINE; } YY_BREAK case 13: /* rule 13 can match eol */ YY_RULE_SETUP #line 95 "igraph/src/foreign-pajek-lexer.l" { return QSTR; } YY_BREAK case 14: /* rule 14 can match eol */ YY_RULE_SETUP #line 96 "igraph/src/foreign-pajek-lexer.l" { return PSTR; } YY_BREAK case 15: YY_RULE_SETUP #line 97 "igraph/src/foreign-pajek-lexer.l" { return NUM; } YY_BREAK case 16: /* rule 16 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 6; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 100 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } } YY_BREAK case 17: /* rule 17 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 6; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 101 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } } YY_BREAK case 18: /* rule 18 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 102 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } } YY_BREAK case 19: /* rule 19 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 103 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } } YY_BREAK case 20: /* rule 20 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 104 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } } YY_BREAK case 21: /* rule 21 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 3; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 105 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } } YY_BREAK case 22: /* rule 22 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 106 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } } YY_BREAK case 23: /* rule 23 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 107 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } } YY_BREAK case 24: /* rule 24 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 4; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 108 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } } YY_BREAK case 25: /* rule 25 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 3; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 109 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } } YY_BREAK case 26: /* rule 26 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 111 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } } YY_BREAK case 27: /* rule 27 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 112 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } } YY_BREAK case 28: /* rule 28 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 113 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } } YY_BREAK case 29: /* rule 29 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 114 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } } YY_BREAK case 30: /* rule 30 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 115 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } } YY_BREAK case 31: /* rule 31 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 116 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } } YY_BREAK case 32: /* rule 32 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 117 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } } YY_BREAK case 33: /* rule 33 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 118 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } } YY_BREAK case 34: /* rule 34 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 119 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } } YY_BREAK case 35: /* rule 35 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 120 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } } YY_BREAK case 36: /* rule 36 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 121 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } } YY_BREAK case 37: /* rule 37 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 122 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } } YY_BREAK case 38: /* rule 38 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 123 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } } YY_BREAK case 39: /* rule 39 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 124 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } } YY_BREAK case 40: /* rule 40 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 4; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 126 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_LPHI; } else if (yyextra->mode==2) { return EP_LPHI; } else { return ALNUM; } } YY_BREAK case 41: /* rule 41 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 128 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_LC; } else if (yyextra->mode==2) { return EP_LC; } else { return ALNUM; } } YY_BREAK case 42: /* rule 42 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 130 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_LR; } else if (yyextra->mode==2) { return EP_LR; } else { return ALNUM; } } YY_BREAK case 43: /* rule 43 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 2; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 132 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_LA; } else if (yyextra->mode==2) { return EP_LA; } else { return ALNUM; } } YY_BREAK case 44: /* rule 44 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 4; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 134 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_SIZE; } else if (yyextra->mode==2) { return EP_SIZE; } else { return ALNUM; } } YY_BREAK case 45: /* rule 45 can match eol */ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 3; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 136 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->mode==1) { return VP_FOS; } else if (yyextra->mode==2) { return EP_FOS; } else { return ALNUM; } } YY_BREAK case 46: YY_RULE_SETUP #line 139 "igraph/src/foreign-pajek-lexer.l" { return ALNUM; } YY_BREAK case YY_STATE_EOF(INITIAL): #line 141 "igraph/src/foreign-pajek-lexer.l" { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return NEWLINE; } } YY_BREAK case 47: YY_RULE_SETUP #line 148 "igraph/src/foreign-pajek-lexer.l" ECHO; YY_BREAK #line 1329 "lex.yy.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * igraph_pajek_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( igraph_pajek_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of igraph_pajek_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { yy_size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { yy_size_t new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ igraph_pajek_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; igraph_pajek_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_pajek_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 160 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register char *yy_cp = yyg->yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 160 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 159); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ igraph_pajek_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( igraph_pajek_yywrap(yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void igraph_pajek_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ igraph_pajek_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_pajek_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); igraph_pajek_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void igraph_pajek_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * igraph_pajek_yypop_buffer_state(); * igraph_pajek_yypush_buffer_state(new_buffer); */ igraph_pajek_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; igraph_pajek_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (igraph_pajek_yywrap()) processing, but the only time this flag * is looked at is after igraph_pajek_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void igraph_pajek_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE igraph_pajek_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) igraph_pajek_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) igraph_pajek_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_create_buffer()" ); b->yy_is_our_buffer = 1; igraph_pajek_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with igraph_pajek_yy_create_buffer() * @param yyscanner The scanner object. */ void igraph_pajek_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) igraph_pajek_yyfree((void *) b->yy_ch_buf ,yyscanner ); igraph_pajek_yyfree((void *) b ,yyscanner ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a igraph_pajek_yyrestart() or at EOF. */ static void igraph_pajek_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; igraph_pajek_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then igraph_pajek_yy_init_buffer was _probably_ * called from igraph_pajek_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void igraph_pajek_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) igraph_pajek_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void igraph_pajek_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; igraph_pajek_yyensure_buffer_stack(yyscanner); /* This block is copied from igraph_pajek_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from igraph_pajek_yy_switch_to_buffer. */ igraph_pajek_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void igraph_pajek_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; igraph_pajek_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { igraph_pajek_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void igraph_pajek_yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_pajek_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_pajek_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_pajek_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) igraph_pajek_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; igraph_pajek_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to igraph_pajek_yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * igraph_pajek_yy_scan_bytes() instead. */ YY_BUFFER_STATE igraph_pajek_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return igraph_pajek_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to igraph_pajek_yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_pajek_yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n, i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) igraph_pajek_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = igraph_pajek_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in igraph_pajek_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE igraph_pajek_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int igraph_pajek_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int igraph_pajek_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *igraph_pajek_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *igraph_pajek_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ yy_size_t igraph_pajek_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *igraph_pajek_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void igraph_pajek_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void igraph_pajek_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_pajek_yyset_lineno called with no buffer" , yyscanner); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void igraph_pajek_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_pajek_yyset_column called with no buffer" , yyscanner); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see igraph_pajek_yy_switch_to_buffer */ void igraph_pajek_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void igraph_pajek_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int igraph_pajek_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void igraph_pajek_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * igraph_pajek_yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void igraph_pajek_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *igraph_pajek_yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void igraph_pajek_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* igraph_pajek_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int igraph_pajek_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_pajek_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* igraph_pajek_yylex_init_extra has the same functionality as igraph_pajek_yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to igraph_pajek_yyalloc in * the yyextra field. */ int igraph_pajek_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; igraph_pajek_yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_pajek_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); igraph_pajek_yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from igraph_pajek_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * igraph_pajek_yylex_init() */ return 0; } /* igraph_pajek_yylex_destroy is for both reentrant and non-reentrant scanners. */ int igraph_pajek_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ igraph_pajek_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; igraph_pajek_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ igraph_pajek_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ igraph_pajek_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * igraph_pajek_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ igraph_pajek_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *igraph_pajek_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *igraph_pajek_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void igraph_pajek_yyfree (void * ptr , yyscan_t yyscanner) { free( (char *) ptr ); /* see igraph_pajek_yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 148 "igraph/src/foreign-pajek-lexer.l" igraph/src/glpios02.c0000644000176000001440000006463512325527073014172 0ustar ripleyusers/* glpios02.c (preprocess current subproblem) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpios.h" /*********************************************************************** * prepare_row_info - prepare row info to determine implied bounds * * Given a row (linear form) * * n * sum a[j] * x[j] (1) * j=1 * * and bounds of columns (variables) * * l[j] <= x[j] <= u[j] (2) * * this routine computes f_min, j_min, f_max, j_max needed to determine * implied bounds. * * ALGORITHM * * Let J+ = {j : a[j] > 0} and J- = {j : a[j] < 0}. * * Parameters f_min and j_min are computed as follows: * * 1) if there is no x[k] such that k in J+ and l[k] = -inf or k in J- * and u[k] = +inf, then * * f_min := sum a[j] * l[j] + sum a[j] * u[j] * j in J+ j in J- * (3) * j_min := 0 * * 2) if there is exactly one x[k] such that k in J+ and l[k] = -inf * or k in J- and u[k] = +inf, then * * f_min := sum a[j] * l[j] + sum a[j] * u[j] * j in J+\{k} j in J-\{k} * (4) * j_min := k * * 3) if there are two or more x[k] such that k in J+ and l[k] = -inf * or k in J- and u[k] = +inf, then * * f_min := -inf * (5) * j_min := 0 * * Parameters f_max and j_max are computed in a similar way as follows: * * 1) if there is no x[k] such that k in J+ and u[k] = +inf or k in J- * and l[k] = -inf, then * * f_max := sum a[j] * u[j] + sum a[j] * l[j] * j in J+ j in J- * (6) * j_max := 0 * * 2) if there is exactly one x[k] such that k in J+ and u[k] = +inf * or k in J- and l[k] = -inf, then * * f_max := sum a[j] * u[j] + sum a[j] * l[j] * j in J+\{k} j in J-\{k} * (7) * j_max := k * * 3) if there are two or more x[k] such that k in J+ and u[k] = +inf * or k in J- and l[k] = -inf, then * * f_max := +inf * (8) * j_max := 0 */ struct f_info { int j_min, j_max; double f_min, f_max; }; static void prepare_row_info(int n, const double a[], const double l[], const double u[], struct f_info *f) { int j, j_min, j_max; double f_min, f_max; xassert(n >= 0); /* determine f_min and j_min */ f_min = 0.0, j_min = 0; for (j = 1; j <= n; j++) { if (a[j] > 0.0) { if (l[j] == -DBL_MAX) { if (j_min == 0) j_min = j; else { f_min = -DBL_MAX, j_min = 0; break; } } else f_min += a[j] * l[j]; } else if (a[j] < 0.0) { if (u[j] == +DBL_MAX) { if (j_min == 0) j_min = j; else { f_min = -DBL_MAX, j_min = 0; break; } } else f_min += a[j] * u[j]; } else xassert(a != a); } f->f_min = f_min, f->j_min = j_min; /* determine f_max and j_max */ f_max = 0.0, j_max = 0; for (j = 1; j <= n; j++) { if (a[j] > 0.0) { if (u[j] == +DBL_MAX) { if (j_max == 0) j_max = j; else { f_max = +DBL_MAX, j_max = 0; break; } } else f_max += a[j] * u[j]; } else if (a[j] < 0.0) { if (l[j] == -DBL_MAX) { if (j_max == 0) j_max = j; else { f_max = +DBL_MAX, j_max = 0; break; } } else f_max += a[j] * l[j]; } else xassert(a != a); } f->f_max = f_max, f->j_max = j_max; return; } /*********************************************************************** * row_implied_bounds - determine row implied bounds * * Given a row (linear form) * * n * sum a[j] * x[j] * j=1 * * and bounds of columns (variables) * * l[j] <= x[j] <= u[j] * * this routine determines implied bounds of the row. * * ALGORITHM * * Let J+ = {j : a[j] > 0} and J- = {j : a[j] < 0}. * * The implied lower bound of the row is computed as follows: * * L' := sum a[j] * l[j] + sum a[j] * u[j] (9) * j in J+ j in J- * * and as it follows from (3), (4), and (5): * * L' := if j_min = 0 then f_min else -inf (10) * * The implied upper bound of the row is computed as follows: * * U' := sum a[j] * u[j] + sum a[j] * l[j] (11) * j in J+ j in J- * * and as it follows from (6), (7), and (8): * * U' := if j_max = 0 then f_max else +inf (12) * * The implied bounds are stored in locations LL and UU. */ static void row_implied_bounds(const struct f_info *f, double *LL, double *UU) { *LL = (f->j_min == 0 ? f->f_min : -DBL_MAX); *UU = (f->j_max == 0 ? f->f_max : +DBL_MAX); return; } /*********************************************************************** * col_implied_bounds - determine column implied bounds * * Given a row (constraint) * * n * L <= sum a[j] * x[j] <= U (13) * j=1 * * and bounds of columns (variables) * * l[j] <= x[j] <= u[j] * * this routine determines implied bounds of variable x[k]. * * It is assumed that if L != -inf, the lower bound of the row can be * active, and if U != +inf, the upper bound of the row can be active. * * ALGORITHM * * From (13) it follows that * * L <= sum a[j] * x[j] + a[k] * x[k] <= U * j!=k * or * * L - sum a[j] * x[j] <= a[k] * x[k] <= U - sum a[j] * x[j] * j!=k j!=k * * Thus, if the row lower bound L can be active, implied lower bound of * term a[k] * x[k] can be determined as follows: * * ilb(a[k] * x[k]) = min(L - sum a[j] * x[j]) = * j!=k * (14) * = L - max sum a[j] * x[j] * j!=k * * where, as it follows from (6), (7), and (8) * * / f_max - a[k] * u[k], j_max = 0, a[k] > 0 * | * | f_max - a[k] * l[k], j_max = 0, a[k] < 0 * max sum a[j] * x[j] = { * j!=k | f_max, j_max = k * | * \ +inf, j_max != 0 * * and if the upper bound U can be active, implied upper bound of term * a[k] * x[k] can be determined as follows: * * iub(a[k] * x[k]) = max(U - sum a[j] * x[j]) = * j!=k * (15) * = U - min sum a[j] * x[j] * j!=k * * where, as it follows from (3), (4), and (5) * * / f_min - a[k] * l[k], j_min = 0, a[k] > 0 * | * | f_min - a[k] * u[k], j_min = 0, a[k] < 0 * min sum a[j] * x[j] = { * j!=k | f_min, j_min = k * | * \ -inf, j_min != 0 * * Since * * ilb(a[k] * x[k]) <= a[k] * x[k] <= iub(a[k] * x[k]) * * implied lower and upper bounds of x[k] are determined as follows: * * l'[k] := if a[k] > 0 then ilb / a[k] else ulb / a[k] (16) * * u'[k] := if a[k] > 0 then ulb / a[k] else ilb / a[k] (17) * * The implied bounds are stored in locations ll and uu. */ static void col_implied_bounds(const struct f_info *f, int n, const double a[], double L, double U, const double l[], const double u[], int k, double *ll, double *uu) { double ilb, iub; xassert(n >= 0); xassert(1 <= k && k <= n); /* determine implied lower bound of term a[k] * x[k] (14) */ if (L == -DBL_MAX || f->f_max == +DBL_MAX) ilb = -DBL_MAX; else if (f->j_max == 0) { if (a[k] > 0.0) { xassert(u[k] != +DBL_MAX); ilb = L - (f->f_max - a[k] * u[k]); } else if (a[k] < 0.0) { xassert(l[k] != -DBL_MAX); ilb = L - (f->f_max - a[k] * l[k]); } else xassert(a != a); } else if (f->j_max == k) ilb = L - f->f_max; else ilb = -DBL_MAX; /* determine implied upper bound of term a[k] * x[k] (15) */ if (U == +DBL_MAX || f->f_min == -DBL_MAX) iub = +DBL_MAX; else if (f->j_min == 0) { if (a[k] > 0.0) { xassert(l[k] != -DBL_MAX); iub = U - (f->f_min - a[k] * l[k]); } else if (a[k] < 0.0) { xassert(u[k] != +DBL_MAX); iub = U - (f->f_min - a[k] * u[k]); } else xassert(a != a); } else if (f->j_min == k) iub = U - f->f_min; else iub = +DBL_MAX; /* determine implied bounds of x[k] (16) and (17) */ #if 1 /* do not use a[k] if it has small magnitude to prevent wrong implied bounds; for example, 1e-15 * x1 >= x2 + x3, where x1 >= -10, x2, x3 >= 0, would lead to wrong conclusion that x1 >= 0 */ if (fabs(a[k]) < 1e-6) *ll = -DBL_MAX, *uu = +DBL_MAX; else #endif if (a[k] > 0.0) { *ll = (ilb == -DBL_MAX ? -DBL_MAX : ilb / a[k]); *uu = (iub == +DBL_MAX ? +DBL_MAX : iub / a[k]); } else if (a[k] < 0.0) { *ll = (iub == +DBL_MAX ? -DBL_MAX : iub / a[k]); *uu = (ilb == -DBL_MAX ? +DBL_MAX : ilb / a[k]); } else xassert(a != a); return; } /*********************************************************************** * check_row_bounds - check and relax original row bounds * * Given a row (constraint) * * n * L <= sum a[j] * x[j] <= U * j=1 * * and bounds of columns (variables) * * l[j] <= x[j] <= u[j] * * this routine checks the original row bounds L and U for feasibility * and redundancy. If the original lower bound L or/and upper bound U * cannot be active due to bounds of variables, the routine remove them * replacing by -inf or/and +inf, respectively. * * If no primal infeasibility is detected, the routine returns zero, * otherwise non-zero. */ static int check_row_bounds(const struct f_info *f, double *L_, double *U_) { int ret = 0; double L = *L_, U = *U_, LL, UU; /* determine implied bounds of the row */ row_implied_bounds(f, &LL, &UU); /* check if the original lower bound is infeasible */ if (L != -DBL_MAX) { double eps = 1e-3 * (1.0 + fabs(L)); if (UU < L - eps) { ret = 1; goto done; } } /* check if the original upper bound is infeasible */ if (U != +DBL_MAX) { double eps = 1e-3 * (1.0 + fabs(U)); if (LL > U + eps) { ret = 1; goto done; } } /* check if the original lower bound is redundant */ if (L != -DBL_MAX) { double eps = 1e-12 * (1.0 + fabs(L)); if (LL > L - eps) { /* it cannot be active, so remove it */ *L_ = -DBL_MAX; } } /* check if the original upper bound is redundant */ if (U != +DBL_MAX) { double eps = 1e-12 * (1.0 + fabs(U)); if (UU < U + eps) { /* it cannot be active, so remove it */ *U_ = +DBL_MAX; } } done: return ret; } /*********************************************************************** * check_col_bounds - check and tighten original column bounds * * Given a row (constraint) * * n * L <= sum a[j] * x[j] <= U * j=1 * * and bounds of columns (variables) * * l[j] <= x[j] <= u[j] * * for column (variable) x[j] this routine checks the original column * bounds l[j] and u[j] for feasibility and redundancy. If the original * lower bound l[j] or/and upper bound u[j] cannot be active due to * bounds of the constraint and other variables, the routine tighten * them replacing by corresponding implied bounds, if possible. * * NOTE: It is assumed that if L != -inf, the row lower bound can be * active, and if U != +inf, the row upper bound can be active. * * The flag means that variable x[j] is required to be integer. * * New actual bounds for x[j] are stored in locations lj and uj. * * If no primal infeasibility is detected, the routine returns zero, * otherwise non-zero. */ static int check_col_bounds(const struct f_info *f, int n, const double a[], double L, double U, const double l[], const double u[], int flag, int j, double *_lj, double *_uj) { int ret = 0; double lj, uj, ll, uu; xassert(n >= 0); xassert(1 <= j && j <= n); lj = l[j], uj = u[j]; /* determine implied bounds of the column */ col_implied_bounds(f, n, a, L, U, l, u, j, &ll, &uu); /* if x[j] is integral, round its implied bounds */ if (flag) { if (ll != -DBL_MAX) ll = (ll - floor(ll) < 1e-3 ? floor(ll) : ceil(ll)); if (uu != +DBL_MAX) uu = (ceil(uu) - uu < 1e-3 ? ceil(uu) : floor(uu)); } /* check if the original lower bound is infeasible */ if (lj != -DBL_MAX) { double eps = 1e-3 * (1.0 + fabs(lj)); if (uu < lj - eps) { ret = 1; goto done; } } /* check if the original upper bound is infeasible */ if (uj != +DBL_MAX) { double eps = 1e-3 * (1.0 + fabs(uj)); if (ll > uj + eps) { ret = 1; goto done; } } /* check if the original lower bound is redundant */ if (ll != -DBL_MAX) { double eps = 1e-3 * (1.0 + fabs(ll)); if (lj < ll - eps) { /* it cannot be active, so tighten it */ lj = ll; } } /* check if the original upper bound is redundant */ if (uu != +DBL_MAX) { double eps = 1e-3 * (1.0 + fabs(uu)); if (uj > uu + eps) { /* it cannot be active, so tighten it */ uj = uu; } } /* due to round-off errors it may happen that lj > uj (although lj < uj + eps, since no primal infeasibility is detected), so adjuct the new actual bounds to provide lj <= uj */ if (!(lj == -DBL_MAX || uj == +DBL_MAX)) { double t1 = fabs(lj), t2 = fabs(uj); double eps = 1e-10 * (1.0 + (t1 <= t2 ? t1 : t2)); if (lj > uj - eps) { if (lj == l[j]) uj = lj; else if (uj == u[j]) lj = uj; else if (t1 <= t2) uj = lj; else lj = uj; } } *_lj = lj, *_uj = uj; done: return ret; } /*********************************************************************** * check_efficiency - check if change in column bounds is efficient * * Given the original bounds of a column l and u and its new actual * bounds l' and u' (possibly tighten by the routine check_col_bounds) * this routine checks if the change in the column bounds is efficient * enough. If so, the routine returns non-zero, otherwise zero. * * The flag means that the variable is required to be integer. */ static int check_efficiency(int flag, double l, double u, double ll, double uu) { int eff = 0; /* check efficiency for lower bound */ if (l < ll) { if (flag || l == -DBL_MAX) eff++; else { double r; if (u == +DBL_MAX) r = 1.0 + fabs(l); else r = 1.0 + (u - l); if (ll - l >= 0.25 * r) eff++; } } /* check efficiency for upper bound */ if (u > uu) { if (flag || u == +DBL_MAX) eff++; else { double r; if (l == -DBL_MAX) r = 1.0 + fabs(u); else r = 1.0 + (u - l); if (u - uu >= 0.25 * r) eff++; } } return eff; } /*********************************************************************** * basic_preprocessing - perform basic preprocessing * * This routine performs basic preprocessing of the specified MIP that * includes relaxing some row bounds and tightening some column bounds. * * On entry the arrays L and U contains original row bounds, and the * arrays l and u contains original column bounds: * * L[0] is the lower bound of the objective row; * L[i], i = 1,...,m, is the lower bound of i-th row; * U[0] is the upper bound of the objective row; * U[i], i = 1,...,m, is the upper bound of i-th row; * l[0] is not used; * l[j], j = 1,...,n, is the lower bound of j-th column; * u[0] is not used; * u[j], j = 1,...,n, is the upper bound of j-th column. * * On exit the arrays L, U, l, and u contain new actual bounds of rows * and column in the same locations. * * The parameters nrs and num specify an initial list of rows to be * processed: * * nrs is the number of rows in the initial list, 0 <= nrs <= m+1; * num[0] is not used; * num[1,...,nrs] are row numbers (0 means the objective row). * * The parameter max_pass specifies the maximal number of times that * each row can be processed, max_pass > 0. * * If no primal infeasibility is detected, the routine returns zero, * otherwise non-zero. */ static int basic_preprocessing(glp_prob *mip, double L[], double U[], double l[], double u[], int nrs, const int num[], int max_pass) { int m = mip->m; int n = mip->n; struct f_info f; int i, j, k, len, size, ret = 0; int *ind, *list, *mark, *pass; double *val, *lb, *ub; xassert(0 <= nrs && nrs <= m+1); xassert(max_pass > 0); /* allocate working arrays */ ind = xcalloc(1+n, sizeof(int)); list = xcalloc(1+m+1, sizeof(int)); mark = xcalloc(1+m+1, sizeof(int)); memset(&mark[0], 0, (m+1) * sizeof(int)); pass = xcalloc(1+m+1, sizeof(int)); memset(&pass[0], 0, (m+1) * sizeof(int)); val = xcalloc(1+n, sizeof(double)); lb = xcalloc(1+n, sizeof(double)); ub = xcalloc(1+n, sizeof(double)); /* initialize the list of rows to be processed */ size = 0; for (k = 1; k <= nrs; k++) { i = num[k]; xassert(0 <= i && i <= m); /* duplicate row numbers are not allowed */ xassert(!mark[i]); list[++size] = i, mark[i] = 1; } xassert(size == nrs); /* process rows in the list until it becomes empty */ while (size > 0) { /* get a next row from the list */ i = list[size--], mark[i] = 0; /* increase the row processing count */ pass[i]++; /* if the row is free, skip it */ if (L[i] == -DBL_MAX && U[i] == +DBL_MAX) continue; /* obtain coefficients of the row */ len = 0; if (i == 0) { for (j = 1; j <= n; j++) { GLPCOL *col = mip->col[j]; if (col->coef != 0.0) len++, ind[len] = j, val[len] = col->coef; } } else { GLPROW *row = mip->row[i]; GLPAIJ *aij; for (aij = row->ptr; aij != NULL; aij = aij->r_next) len++, ind[len] = aij->col->j, val[len] = aij->val; } /* determine lower and upper bounds of columns corresponding to non-zero row coefficients */ for (k = 1; k <= len; k++) j = ind[k], lb[k] = l[j], ub[k] = u[j]; /* prepare the row info to determine implied bounds */ prepare_row_info(len, val, lb, ub, &f); /* check and relax bounds of the row */ if (check_row_bounds(&f, &L[i], &U[i])) { /* the feasible region is empty */ ret = 1; goto done; } /* if the row became free, drop it */ if (L[i] == -DBL_MAX && U[i] == +DBL_MAX) continue; /* process columns having non-zero coefficients in the row */ for (k = 1; k <= len; k++) { GLPCOL *col; int flag, eff; double ll, uu; /* take a next column in the row */ j = ind[k], col = mip->col[j]; flag = col->kind != GLP_CV; /* check and tighten bounds of the column */ if (check_col_bounds(&f, len, val, L[i], U[i], lb, ub, flag, k, &ll, &uu)) { /* the feasible region is empty */ ret = 1; goto done; } /* check if change in the column bounds is efficient */ eff = check_efficiency(flag, l[j], u[j], ll, uu); /* set new actual bounds of the column */ l[j] = ll, u[j] = uu; /* if the change is efficient, add all rows affected by the corresponding column, to the list */ if (eff > 0) { GLPAIJ *aij; for (aij = col->ptr; aij != NULL; aij = aij->c_next) { int ii = aij->row->i; /* if the row was processed maximal number of times, skip it */ if (pass[ii] >= max_pass) continue; /* if the row is free, skip it */ if (L[ii] == -DBL_MAX && U[ii] == +DBL_MAX) continue; /* put the row into the list */ if (mark[ii] == 0) { xassert(size <= m); list[++size] = ii, mark[ii] = 1; } } } } } done: /* free working arrays */ xfree(ind); xfree(list); xfree(mark); xfree(pass); xfree(val); xfree(lb); xfree(ub); return ret; } /*********************************************************************** * NAME * * ios_preprocess_node - preprocess current subproblem * * SYNOPSIS * * #include "glpios.h" * int ios_preprocess_node(glp_tree *tree, int max_pass); * * DESCRIPTION * * The routine ios_preprocess_node performs basic preprocessing of the * current subproblem. * * RETURNS * * If no primal infeasibility is detected, the routine returns zero, * otherwise non-zero. */ int ios_preprocess_node(glp_tree *tree, int max_pass) { glp_prob *mip = tree->mip; int m = mip->m; int n = mip->n; int i, j, nrs, *num, ret = 0; double *L, *U, *l, *u; /* the current subproblem must exist */ xassert(tree->curr != NULL); /* determine original row bounds */ L = xcalloc(1+m, sizeof(double)); U = xcalloc(1+m, sizeof(double)); switch (mip->mip_stat) { case GLP_UNDEF: L[0] = -DBL_MAX, U[0] = +DBL_MAX; break; case GLP_FEAS: switch (mip->dir) { case GLP_MIN: L[0] = -DBL_MAX, U[0] = mip->mip_obj - mip->c0; break; case GLP_MAX: L[0] = mip->mip_obj - mip->c0, U[0] = +DBL_MAX; break; default: xassert(mip != mip); } break; default: xassert(mip != mip); } for (i = 1; i <= m; i++) { L[i] = glp_get_row_lb(mip, i); U[i] = glp_get_row_ub(mip, i); } /* determine original column bounds */ l = xcalloc(1+n, sizeof(double)); u = xcalloc(1+n, sizeof(double)); for (j = 1; j <= n; j++) { l[j] = glp_get_col_lb(mip, j); u[j] = glp_get_col_ub(mip, j); } /* build the initial list of rows to be analyzed */ nrs = m + 1; num = xcalloc(1+nrs, sizeof(int)); for (i = 1; i <= nrs; i++) num[i] = i - 1; /* perform basic preprocessing */ if (basic_preprocessing(mip , L, U, l, u, nrs, num, max_pass)) { ret = 1; goto done; } /* set new actual (relaxed) row bounds */ for (i = 1; i <= m; i++) { /* consider only non-active rows to keep dual feasibility */ if (glp_get_row_stat(mip, i) == GLP_BS) { if (L[i] == -DBL_MAX && U[i] == +DBL_MAX) glp_set_row_bnds(mip, i, GLP_FR, 0.0, 0.0); else if (U[i] == +DBL_MAX) glp_set_row_bnds(mip, i, GLP_LO, L[i], 0.0); else if (L[i] == -DBL_MAX) glp_set_row_bnds(mip, i, GLP_UP, 0.0, U[i]); } } /* set new actual (tightened) column bounds */ for (j = 1; j <= n; j++) { int type; if (l[j] == -DBL_MAX && u[j] == +DBL_MAX) type = GLP_FR; else if (u[j] == +DBL_MAX) type = GLP_LO; else if (l[j] == -DBL_MAX) type = GLP_UP; else if (l[j] != u[j]) type = GLP_DB; else type = GLP_FX; glp_set_col_bnds(mip, j, type, l[j], u[j]); } done: /* free working arrays and return */ xfree(L); xfree(U); xfree(l); xfree(u); xfree(num); return ret; } /* eof */ igraph/src/cs_qr.c0000644000176000001440000001060412325527073013625 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma clang diagnostic ignored "-Wconversion" #include "cs.h" /* sparse QR factorization [V,beta,pinv,R] = qr (A) */ csn *cs_qr (const cs *A, const css *S) { CS_ENTRY *Rx, *Vx, *Ax, *x ; double *Beta ; CS_INT i, k, p, m, n, vnz, p1, top, m2, len, col, rnz, *s, *leftmost, *Ap, *Ai, *parent, *Rp, *Ri, *Vp, *Vi, *w, *pinv, *q ; cs *R, *V ; csn *N ; if (!CS_CSC (A) || !S) return (NULL) ; m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; q = S->q ; parent = S->parent ; pinv = S->pinv ; m2 = S->m2 ; vnz = S->lnz ; rnz = S->unz ; leftmost = S->leftmost ; w = cs_malloc (m2+n, sizeof (CS_INT)) ; /* get CS_INT workspace */ x = cs_malloc (m2, sizeof (CS_ENTRY)) ; /* get CS_ENTRY workspace */ N = cs_calloc (1, sizeof (csn)) ; /* allocate result */ if (!w || !x || !N) return (cs_ndone (N, NULL, w, x, 0)) ; s = w + m2 ; /* s is size n */ for (k = 0 ; k < m2 ; k++) x [k] = 0 ; /* clear workspace x */ N->L = V = cs_spalloc (m2, n, vnz, 1, 0) ; /* allocate result V */ N->U = R = cs_spalloc (m2, n, rnz, 1, 0) ; /* allocate result R */ N->B = Beta = cs_malloc (n, sizeof (double)) ; /* allocate result Beta */ if (!R || !V || !Beta) return (cs_ndone (N, NULL, w, x, 0)) ; Rp = R->p ; Ri = R->i ; Rx = R->x ; Vp = V->p ; Vi = V->i ; Vx = V->x ; for (i = 0 ; i < m2 ; i++) w [i] = -1 ; /* clear w, to mark nodes */ rnz = 0 ; vnz = 0 ; for (k = 0 ; k < n ; k++) /* compute V and R */ { Rp [k] = rnz ; /* R(:,k) starts here */ Vp [k] = p1 = vnz ; /* V(:,k) starts here */ w [k] = k ; /* add V(k,k) to pattern of V */ Vi [vnz++] = k ; top = n ; col = q ? q [k] : k ; for (p = Ap [col] ; p < Ap [col+1] ; p++) /* find R(:,k) pattern */ { i = leftmost [Ai [p]] ; /* i = min(find(A(i,q))) */ for (len = 0 ; w [i] != k ; i = parent [i]) /* traverse up to k */ { s [len++] = i ; w [i] = k ; } while (len > 0) s [--top] = s [--len] ; /* push path on stack */ i = pinv [Ai [p]] ; /* i = permuted row of A(:,col) */ x [i] = Ax [p] ; /* x (i) = A(:,col) */ if (i > k && w [i] < k) /* pattern of V(:,k) = x (k+1:m) */ { Vi [vnz++] = i ; /* add i to pattern of V(:,k) */ w [i] = k ; } } for (p = top ; p < n ; p++) /* for each i in pattern of R(:,k) */ { i = s [p] ; /* R(i,k) is nonzero */ cs_happly (V, i, Beta [i], x) ; /* apply (V(i),Beta(i)) to x */ Ri [rnz] = i ; /* R(i,k) = x(i) */ Rx [rnz++] = x [i] ; x [i] = 0 ; if (parent [i] == k) vnz = cs_scatter (V, i, 0, w, NULL, k, V, vnz); } for (p = p1 ; p < vnz ; p++) /* gather V(:,k) = x */ { Vx [p] = x [Vi [p]] ; x [Vi [p]] = 0 ; } Ri [rnz] = k ; /* R(k,k) = norm (x) */ Rx [rnz++] = cs_house (Vx+p1, Beta+k, vnz-p1) ; /* [v,beta]=house(x) */ } Rp [n] = rnz ; /* finalize R */ Vp [n] = vnz ; /* finalize V */ return (cs_ndone (N, NULL, w, x, 1)) ; /* success */ } igraph/src/igraph_vector_type.h0000644000176000001440000000205612325527073016422 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2013 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * Vector, dealing with arrays efficiently. * \ingroup types */ typedef struct TYPE(igraph_vector) { BASE* stor_begin; BASE* stor_end; BASE* end; } TYPE(igraph_vector); igraph/src/foreign-ncol-lexer.c0000644000176000001440000016071312325527073016224 0ustar ripleyusers#line 2 "lex.yy.c" #line 4 "lex.yy.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE igraph_ncol_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via igraph_ncol_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void igraph_ncol_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void igraph_ncol_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_ncol_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void igraph_ncol_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_ncol_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_ncol_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void igraph_ncol_yypop_buffer_state (yyscan_t yyscanner ); static void igraph_ncol_yyensure_buffer_stack (yyscan_t yyscanner ); static void igraph_ncol_yy_load_buffer_state (yyscan_t yyscanner ); static void igraph_ncol_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER igraph_ncol_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE igraph_ncol_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_ncol_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_ncol_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *igraph_ncol_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *igraph_ncol_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void igraph_ncol_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer igraph_ncol_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ igraph_ncol_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ igraph_ncol_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define igraph_ncol_yywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (yy_size_t) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 4 #define YY_END_OF_BUFFER 5 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[12] = { 0, 1, 1, 5, 3, 1, 2, 2, 3, 1, 2, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[5] = { 0, 1, 2, 3, 4 } ; static yyconst flex_int16_t yy_base[16] = { 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 10, 10, 7, 5, 2, 2 } ; static yyconst flex_int16_t yy_def[16] = { 0, 11, 1, 11, 12, 13, 14, 15, 12, 13, 11, 0, 11, 11, 11, 11 } ; static yyconst flex_int16_t yy_nxt[15] = { 0, 4, 5, 6, 7, 10, 10, 9, 8, 11, 3, 11, 11, 11, 11 } ; static yyconst flex_int16_t yy_chk[15] = { 0, 1, 1, 1, 1, 15, 14, 13, 12, 3, 11, 11, 11, 11, 11 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "igraph/src/foreign-ncol-lexer.l" /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #line 24 "igraph/src/foreign-ncol-lexer.l" /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-ncol-header.h" #include "foreign-ncol-parser.h" #define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); #define YY_NO_INPUT 1 #line 504 "lex.yy.c" #define INITIAL 0 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; yy_size_t yy_n_chars; yy_size_t yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r int igraph_ncol_yylex_init (yyscan_t* scanner); int igraph_ncol_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int igraph_ncol_yylex_destroy (yyscan_t yyscanner ); int igraph_ncol_yyget_debug (yyscan_t yyscanner ); void igraph_ncol_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE igraph_ncol_yyget_extra (yyscan_t yyscanner ); void igraph_ncol_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *igraph_ncol_yyget_in (yyscan_t yyscanner ); void igraph_ncol_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *igraph_ncol_yyget_out (yyscan_t yyscanner ); void igraph_ncol_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t igraph_ncol_yyget_leng (yyscan_t yyscanner ); char *igraph_ncol_yyget_text (yyscan_t yyscanner ); int igraph_ncol_yyget_lineno (yyscan_t yyscanner ); void igraph_ncol_yyset_lineno (int line_number ,yyscan_t yyscanner ); YYSTYPE * igraph_ncol_yyget_lval (yyscan_t yyscanner ); void igraph_ncol_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *igraph_ncol_yyget_lloc (yyscan_t yyscanner ); void igraph_ncol_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int igraph_ncol_yywrap (yyscan_t yyscanner ); #else extern int igraph_ncol_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ yy_size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int igraph_ncol_yylex \ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int igraph_ncol_yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; #line 80 "igraph/src/foreign-ncol-lexer.l" /* ------------------------------------------------whitespace------*/ #line 747 "lex.yy.c" yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { igraph_ncol_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_ncol_yy_load_buffer_state(yyscanner ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 12 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 10 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 83 "igraph/src/foreign-ncol-lexer.l" { } YY_BREAK /* ---------------------------------------------------newline------*/ case 2: /* rule 2 can match eol */ YY_RULE_SETUP #line 86 "igraph/src/foreign-ncol-lexer.l" { return NEWLINE; } YY_BREAK /* ----------------------------------------------alphanumeric------*/ case 3: YY_RULE_SETUP #line 89 "igraph/src/foreign-ncol-lexer.l" { return ALNUM; } YY_BREAK case YY_STATE_EOF(INITIAL): #line 91 "igraph/src/foreign-ncol-lexer.l" { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return NEWLINE; } } YY_BREAK case 4: YY_RULE_SETUP #line 99 "igraph/src/foreign-ncol-lexer.l" ECHO; YY_BREAK #line 867 "lex.yy.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * igraph_ncol_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( igraph_ncol_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of igraph_ncol_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { yy_size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { yy_size_t new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ igraph_ncol_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; igraph_ncol_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_ncol_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 12 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register char *yy_cp = yyg->yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 12 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 11); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ igraph_ncol_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( igraph_ncol_yywrap(yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void igraph_ncol_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ igraph_ncol_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_ncol_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); igraph_ncol_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void igraph_ncol_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * igraph_ncol_yypop_buffer_state(); * igraph_ncol_yypush_buffer_state(new_buffer); */ igraph_ncol_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; igraph_ncol_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (igraph_ncol_yywrap()) processing, but the only time this flag * is looked at is after igraph_ncol_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void igraph_ncol_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE igraph_ncol_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) igraph_ncol_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) igraph_ncol_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_create_buffer()" ); b->yy_is_our_buffer = 1; igraph_ncol_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with igraph_ncol_yy_create_buffer() * @param yyscanner The scanner object. */ void igraph_ncol_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) igraph_ncol_yyfree((void *) b->yy_ch_buf ,yyscanner ); igraph_ncol_yyfree((void *) b ,yyscanner ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a igraph_ncol_yyrestart() or at EOF. */ static void igraph_ncol_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; igraph_ncol_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then igraph_ncol_yy_init_buffer was _probably_ * called from igraph_ncol_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void igraph_ncol_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) igraph_ncol_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void igraph_ncol_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; igraph_ncol_yyensure_buffer_stack(yyscanner); /* This block is copied from igraph_ncol_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from igraph_ncol_yy_switch_to_buffer. */ igraph_ncol_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void igraph_ncol_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; igraph_ncol_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { igraph_ncol_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void igraph_ncol_yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_ncol_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_ncol_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_ncol_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) igraph_ncol_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; igraph_ncol_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to igraph_ncol_yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * igraph_ncol_yy_scan_bytes() instead. */ YY_BUFFER_STATE igraph_ncol_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return igraph_ncol_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to igraph_ncol_yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_ncol_yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n, i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) igraph_ncol_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = igraph_ncol_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in igraph_ncol_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE igraph_ncol_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int igraph_ncol_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int igraph_ncol_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *igraph_ncol_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *igraph_ncol_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ yy_size_t igraph_ncol_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *igraph_ncol_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void igraph_ncol_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void igraph_ncol_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_ncol_yyset_lineno called with no buffer" , yyscanner); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void igraph_ncol_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_ncol_yyset_column called with no buffer" , yyscanner); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see igraph_ncol_yy_switch_to_buffer */ void igraph_ncol_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void igraph_ncol_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int igraph_ncol_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void igraph_ncol_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * igraph_ncol_yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void igraph_ncol_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *igraph_ncol_yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void igraph_ncol_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* igraph_ncol_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int igraph_ncol_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_ncol_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* igraph_ncol_yylex_init_extra has the same functionality as igraph_ncol_yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to igraph_ncol_yyalloc in * the yyextra field. */ int igraph_ncol_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; igraph_ncol_yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_ncol_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); igraph_ncol_yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from igraph_ncol_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * igraph_ncol_yylex_init() */ return 0; } /* igraph_ncol_yylex_destroy is for both reentrant and non-reentrant scanners. */ int igraph_ncol_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ igraph_ncol_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; igraph_ncol_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ igraph_ncol_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ igraph_ncol_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * igraph_ncol_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ igraph_ncol_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *igraph_ncol_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *igraph_ncol_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void igraph_ncol_yyfree (void * ptr , yyscan_t yyscanner) { free( (char *) ptr ); /* see igraph_ncol_yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 99 "igraph/src/foreign-ncol-lexer.l" igraph/src/cs_post.c0000644000176000001440000000370112325527073014170 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* post order a forest */ CS_INT *cs_post (const CS_INT *parent, CS_INT n) { CS_INT j, k = 0, *post, *w, *head, *next, *stack ; if (!parent) return (NULL) ; /* check inputs */ post = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ w = cs_malloc (3*n, sizeof (CS_INT)) ; /* get workspace */ if (!w || !post) return (cs_idone (post, NULL, w, 0)) ; head = w ; next = w + n ; stack = w + 2*n ; for (j = 0 ; j < n ; j++) head [j] = -1 ; /* empty linked lists */ for (j = n-1 ; j >= 0 ; j--) /* traverse nodes in reverse order*/ { if (parent [j] == -1) continue ; /* j is a root */ next [j] = head [parent [j]] ; /* add j to list of its parent */ head [parent [j]] = j ; } for (j = 0 ; j < n ; j++) { if (parent [j] != -1) continue ; /* skip j if it is not a root */ k = cs_tdfs (j, k, head, next, post, stack) ; } return (cs_idone (post, NULL, w, 1)) ; /* success; free w, return post */ } igraph/src/foreign-dl-parser.c0000644000176000001440000017151312325527073016045 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Using locations. */ #define YYLSP_NEEDED 1 /* Substitute the variable and function names. */ #define yyparse igraph_dl_yyparse #define yylex igraph_dl_yylex #define yyerror igraph_dl_yyerror #define yylval igraph_dl_yylval #define yychar igraph_dl_yychar #define yydebug igraph_dl_yydebug #define yynerrs igraph_dl_yynerrs #define yylloc igraph_dl_yylloc /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { NUM = 258, NEWLINE = 259, DL = 260, NEQ = 261, DATA = 262, LABELS = 263, LABELSEMBEDDED = 264, FORMATFULLMATRIX = 265, FORMATEDGELIST1 = 266, FORMATNODELIST1 = 267, DIGIT = 268, LABEL = 269, EOFF = 270 }; #endif /* Tokens. */ #define NUM 258 #define NEWLINE 259 #define DL 260 #define NEQ 261 #define DATA 262 #define LABELS 263 #define LABELSEMBEDDED 264 #define FORMATFULLMATRIX 265 #define FORMATEDGELIST1 266 #define FORMATNODELIST1 267 #define DIGIT 268 #define LABEL 269 #define EOFF 270 /* Copy the first part of user declarations. */ #line 23 "igraph/src/foreign-dl-parser.y" /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include "igraph_hacks_internal.h" #include "igraph_math.h" #include "igraph_types_internal.h" #include "foreign-dl-header.h" #include "foreign-dl-parser.h" #include #define yyscan_t void* int igraph_dl_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, const char *s); char *igraph_dl_yyget_text (yyscan_t yyscanner ); int igraph_dl_yyget_leng (yyscan_t yyscanner ); int igraph_i_dl_add_str(char *newstr, int length, igraph_i_dl_parsedata_t *context); int igraph_i_dl_add_edge(long int from, long int to, igraph_i_dl_parsedata_t *context); int igraph_i_dl_add_edge_w(long int from, long int to, igraph_real_t weight, igraph_i_dl_parsedata_t *context); extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); #define scanner context->scanner /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 91 "igraph/src/foreign-dl-parser.y" { long int integer; igraph_real_t real; } /* Line 193 of yacc.c. */ #line 198 "y.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ /* Line 216 of yacc.c. */ #line 223 "y.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int i) #else static int YYID (i) int i; #endif { return i; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss; YYSTYPE yyvs; YYLTYPE yyls; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 4 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 118 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 16 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 37 /* YYNRULES -- Number of rules. */ #define YYNRULES 66 /* YYNRULES -- Number of states. */ #define YYNSTATES 137 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 270 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint8 yyprhs[] = { 0, 0, 3, 11, 12, 15, 16, 18, 20, 22, 24, 28, 30, 31, 33, 37, 45, 51, 52, 56, 57, 61, 62, 65, 67, 69, 73, 74, 78, 80, 82, 85, 89, 93, 96, 104, 110, 120, 130, 131, 134, 139, 143, 145, 146, 149, 154, 158, 160, 162, 166, 169, 177, 183, 193, 203, 204, 207, 211, 213, 214, 217, 218, 221, 225, 227, 228 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 17, 0, -1, 5, 6, 38, 4, 20, 18, 19, -1, -1, 18, 22, -1, -1, 15, -1, 21, -1, 34, -1, 43, -1, 10, 22, 23, -1, 23, -1, -1, 4, -1, 7, 22, 25, -1, 8, 22, 24, 22, 7, 22, 25, -1, 9, 22, 7, 22, 28, -1, -1, 24, 22, 14, -1, -1, 25, 26, 4, -1, -1, 26, 27, -1, 13, -1, 29, -1, 30, 4, 32, -1, -1, 30, 22, 31, -1, 14, -1, 33, -1, 32, 33, -1, 14, 26, 4, -1, 11, 22, 35, -1, 7, 36, -1, 8, 22, 24, 22, 7, 22, 36, -1, 9, 22, 7, 22, 39, -1, 8, 22, 24, 22, 9, 22, 7, 22, 39, -1, 9, 22, 8, 22, 24, 22, 7, 22, 39, -1, -1, 36, 37, -1, 38, 38, 41, 4, -1, 38, 38, 4, -1, 3, -1, -1, 39, 40, -1, 42, 42, 41, 4, -1, 42, 42, 4, -1, 3, -1, 14, -1, 12, 22, 44, -1, 7, 45, -1, 8, 22, 24, 22, 7, 22, 45, -1, 9, 22, 7, 22, 49, -1, 8, 22, 24, 22, 9, 22, 7, 22, 49, -1, 9, 22, 8, 22, 24, 22, 7, 22, 49, -1, -1, 45, 46, -1, 47, 48, 4, -1, 3, -1, -1, 48, 38, -1, -1, 49, 50, -1, 51, 52, 4, -1, 42, -1, -1, 52, 42, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 115, 115, 117, 117, 119, 119, 121, 122, 123, 126, 126, 128, 128, 130, 131, 132, 135, 136, 142, 142, 147, 147, 149, 159, 161, 163, 163, 165, 169, 173, 178, 182, 184, 185, 186, 187, 188, 191, 192, 195, 197, 201, 204, 205, 208, 210, 214, 217, 233, 235, 236, 237, 238, 239, 242, 243, 246, 248, 251, 251, 257, 258, 261, 263, 267, 267 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "NUM", "NEWLINE", "DL", "NEQ", "DATA", "LABELS", "LABELSEMBEDDED", "FORMATFULLMATRIX", "FORMATEDGELIST1", "FORMATNODELIST1", "DIGIT", "LABEL", "EOFF", "$accept", "input", "trail", "eof", "rest", "formfullmatrix", "newline", "fullmatrix", "labels", "fullmatrixdata", "zerooneseq", "zeroone", "labeledfullmatrixdata", "reallabeledfullmatrixdata", "labelseq", "label", "labeledmatrixlines", "labeledmatrixline", "edgelist1", "edgelist1rest", "edgelist1data", "edgelist1dataline", "integer", "labelededgelist1data", "labelededgelist1dataline", "weight", "elabel", "nodelist1", "nodelist1rest", "nodelist1data", "nodelist1dataline", "from", "tolist", "labelednodelist1data", "labelednodelist1dataline", "fromelabel", "labeltolist", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 21, 21, 22, 22, 23, 23, 23, 24, 24, 25, 25, 26, 26, 27, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 35, 35, 35, 36, 36, 37, 37, 38, 39, 39, 40, 40, 41, 42, 43, 44, 44, 44, 44, 44, 45, 45, 46, 47, 48, 48, 49, 49, 50, 51, 52, 52 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 7, 0, 2, 0, 1, 1, 1, 1, 3, 1, 0, 1, 3, 7, 5, 0, 3, 0, 3, 0, 2, 1, 1, 3, 0, 3, 1, 1, 2, 3, 3, 2, 7, 5, 9, 9, 0, 2, 4, 3, 1, 0, 2, 4, 3, 1, 1, 3, 2, 7, 5, 9, 9, 0, 2, 3, 1, 0, 2, 0, 2, 3, 1, 0, 2 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 0, 0, 0, 1, 42, 0, 0, 12, 12, 12, 12, 12, 12, 3, 7, 11, 8, 9, 13, 19, 17, 0, 0, 0, 0, 5, 14, 12, 12, 10, 38, 12, 12, 32, 55, 12, 12, 49, 6, 2, 4, 0, 0, 26, 33, 17, 0, 50, 17, 0, 20, 23, 22, 12, 18, 16, 24, 12, 39, 0, 12, 12, 12, 58, 56, 59, 12, 12, 12, 19, 0, 0, 0, 0, 43, 17, 0, 0, 61, 17, 15, 21, 25, 29, 28, 27, 47, 41, 0, 12, 12, 35, 12, 57, 60, 12, 12, 52, 12, 0, 30, 40, 38, 0, 48, 44, 0, 0, 55, 0, 64, 62, 65, 0, 31, 34, 12, 0, 12, 51, 12, 0, 12, 43, 46, 0, 43, 61, 63, 66, 61, 36, 45, 37, 53, 54 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 2, 26, 40, 14, 15, 20, 16, 28, 27, 42, 53, 56, 57, 58, 86, 83, 84, 17, 34, 45, 59, 60, 92, 106, 89, 107, 18, 38, 48, 65, 66, 77, 98, 112, 113, 122 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -93 static const yytype_int8 yypact[] = { 41, 3, 38, 47, -93, -93, 57, 56, 76, 76, 76, 76, 76, 76, -93, -93, -93, -93, -93, -93, -93, -93, 89, 48, 66, 69, 7, 70, 76, 76, -93, -93, 76, 76, -93, -93, 76, 76, -93, -93, -93, -93, 8, 19, -93, 47, -93, 6, 86, -93, 63, -93, -93, -93, 76, -93, -93, -93, 91, -93, 47, 76, 76, 76, -93, -93, -93, 76, 76, 76, -93, 83, 84, 82, 22, -93, -93, 88, 25, -93, -93, 70, -93, 83, -93, -93, -93, -93, -93, 95, 76, 76, 87, 76, -93, -93, 76, 76, 87, 76, 12, -93, -93, -93, 93, -93, -93, 87, 28, -93, 96, -93, -93, -93, 33, -93, 47, 76, 90, 76, 86, 76, 4, 76, -93, -93, 98, -93, -93, -93, -93, -93, 87, -93, 87, 87, 87 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -93, -93, -93, -93, -93, -93, -9, 81, -39, 35, 24, -93, -93, -93, -93, -93, -93, 26, -93, -93, 10, -93, 2, -76, -93, -11, -92, -93, -93, 9, -93, -93, -93, -59, -93, -93, -93 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -22 static const yytype_int16 yytable[] = { 21, 22, 23, 24, 25, 6, 111, 61, 129, 3, 67, 19, 51, 62, 63, 118, 115, 41, 105, 43, 44, 52, 39, 46, 47, 52, 54, 49, 50, 90, 130, 91, 96, 55, 97, 119, 55, 93, 4, 55, 123, 99, 55, 111, 111, 70, 1, 55, 132, 72, 5, 134, 74, 75, 76, 8, 9, 10, 78, 79, 80, 7, 73, 8, 9, 10, 11, 12, 13, 135, 68, 69, 136, 31, 32, 33, 35, 36, 37, 95, 19, 103, 104, -21, 108, 87, 88, 109, 110, 64, 114, 5, 94, 87, 125, 71, 29, 82, 85, 102, 117, 105, 133, 121, 30, 81, 100, 126, 124, 101, 127, 0, 128, 116, 131, 0, 0, 0, 120 }; static const yytype_int16 yycheck[] = { 9, 10, 11, 12, 13, 3, 98, 46, 4, 6, 49, 4, 4, 7, 8, 107, 4, 26, 14, 28, 29, 13, 15, 32, 33, 13, 7, 36, 37, 7, 122, 9, 7, 14, 9, 7, 14, 76, 0, 14, 7, 80, 14, 135, 136, 54, 5, 14, 124, 58, 3, 127, 61, 62, 63, 7, 8, 9, 67, 68, 69, 4, 60, 7, 8, 9, 10, 11, 12, 128, 7, 8, 131, 7, 8, 9, 7, 8, 9, 77, 4, 90, 91, 13, 93, 3, 4, 96, 97, 3, 99, 3, 4, 3, 4, 4, 7, 14, 14, 4, 7, 14, 4, 7, 23, 70, 82, 118, 117, 83, 119, -1, 121, 103, 123, -1, -1, -1, 109 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 5, 17, 6, 0, 3, 38, 4, 7, 8, 9, 10, 11, 12, 20, 21, 23, 34, 43, 4, 22, 22, 22, 22, 22, 22, 18, 25, 24, 7, 23, 7, 8, 9, 35, 7, 8, 9, 44, 15, 19, 22, 26, 22, 22, 36, 22, 22, 45, 22, 22, 4, 13, 27, 7, 14, 28, 29, 30, 37, 38, 24, 7, 8, 3, 46, 47, 24, 7, 8, 22, 4, 22, 38, 22, 22, 22, 48, 22, 22, 22, 25, 14, 32, 33, 14, 31, 3, 4, 41, 7, 9, 39, 24, 4, 38, 7, 9, 49, 24, 26, 33, 4, 22, 22, 14, 40, 42, 22, 22, 22, 42, 50, 51, 22, 4, 36, 7, 42, 7, 45, 7, 52, 7, 22, 4, 41, 22, 22, 4, 42, 22, 39, 4, 39, 49, 49 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) #else # define YYLEX yylex (&yylval, &yylloc, scanner) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location, context); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_dl_parsedata_t* context; #endif { if (!yyvaluep) return; YYUSE (yylocationp); YYUSE (context); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_dl_parsedata_t* context; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); YY_LOCATION_PRINT (yyoutput, *yylocationp); YYFPRINTF (yyoutput, ": "); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void yy_stack_print (bottom, top) yytype_int16 *bottom; yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); for (; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_dl_parsedata_t* context) #else static void yy_reduce_print (yyvsp, yylsp, yyrule, context) YYSTYPE *yyvsp; YYLTYPE *yylsp; int yyrule; igraph_i_dl_parsedata_t* context; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , context); fprintf (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, yylsp, Rule, context); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_dl_parsedata_t* context) #else static void yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) const char *yymsg; int yytype; YYSTYPE *yyvaluep; YYLTYPE *yylocationp; igraph_i_dl_parsedata_t* context; #endif { YYUSE (yyvaluep); YYUSE (yylocationp); YYUSE (context); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (igraph_i_dl_parsedata_t* context); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (igraph_i_dl_parsedata_t* context) #else int yyparse (context) igraph_i_dl_parsedata_t* context; #endif #endif { /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /* Location data for the look-ahead symbol. */ YYLTYPE yylloc; int yystate; int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss = yyssa; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[2]; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; yylsp = yyls; #if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Initialize the default location before parsing starts. */ yylloc.first_line = yylloc.last_line = 1; yylloc.first_column = yylloc.last_column = 0; #endif goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyls = yyls1; yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); YYSTACK_RELOCATE (yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a look-ahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 115 "igraph/src/foreign-dl-parser.y" { context->n=(yyvsp[(3) - (7)].integer); ;} break; case 7: #line 121 "igraph/src/foreign-dl-parser.y" { context->type=IGRAPH_DL_MATRIX; ;} break; case 8: #line 122 "igraph/src/foreign-dl-parser.y" { context->type=IGRAPH_DL_EDGELIST1; ;} break; case 9: #line 123 "igraph/src/foreign-dl-parser.y" { context->type=IGRAPH_DL_NODELIST1; ;} break; case 10: #line 126 "igraph/src/foreign-dl-parser.y" {;} break; case 11: #line 126 "igraph/src/foreign-dl-parser.y" {;} break; case 14: #line 130 "igraph/src/foreign-dl-parser.y" { ;} break; case 15: #line 131 "igraph/src/foreign-dl-parser.y" { ;} break; case 16: #line 132 "igraph/src/foreign-dl-parser.y" { ;} break; case 17: #line 135 "igraph/src/foreign-dl-parser.y" {;} break; case 18: #line 136 "igraph/src/foreign-dl-parser.y" { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner), context); ;} break; case 19: #line 142 "igraph/src/foreign-dl-parser.y" {;} break; case 20: #line 142 "igraph/src/foreign-dl-parser.y" { context->from += 1; context->to = 0; ;} break; case 22: #line 147 "igraph/src/foreign-dl-parser.y" { ;} break; case 23: #line 149 "igraph/src/foreign-dl-parser.y" { if (igraph_dl_yyget_text(scanner)[0]=='1') { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->from)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->to)); } context->to += 1; ;} break; case 24: #line 159 "igraph/src/foreign-dl-parser.y" {;} break; case 25: #line 161 "igraph/src/foreign-dl-parser.y" {;} break; case 28: #line 165 "igraph/src/foreign-dl-parser.y" { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner), context); ;} break; case 29: #line 169 "igraph/src/foreign-dl-parser.y" { context->from += 1; context->to = 0; ;} break; case 30: #line 173 "igraph/src/foreign-dl-parser.y" { context->from += 1; context->to = 0; ;} break; case 31: #line 178 "igraph/src/foreign-dl-parser.y" { ;} break; case 32: #line 182 "igraph/src/foreign-dl-parser.y" {;} break; case 33: #line 184 "igraph/src/foreign-dl-parser.y" {;} break; case 34: #line 185 "igraph/src/foreign-dl-parser.y" {;} break; case 35: #line 186 "igraph/src/foreign-dl-parser.y" {;} break; case 36: #line 187 "igraph/src/foreign-dl-parser.y" {;} break; case 37: #line 188 "igraph/src/foreign-dl-parser.y" {;} break; case 38: #line 191 "igraph/src/foreign-dl-parser.y" {;} break; case 39: #line 192 "igraph/src/foreign-dl-parser.y" {;} break; case 40: #line 195 "igraph/src/foreign-dl-parser.y" { igraph_i_dl_add_edge_w((yyvsp[(1) - (4)].integer)-1, (yyvsp[(2) - (4)].integer)-1, (yyvsp[(3) - (4)].real), context); ;} break; case 41: #line 197 "igraph/src/foreign-dl-parser.y" { igraph_i_dl_add_edge((yyvsp[(1) - (3)].integer)-1, (yyvsp[(2) - (3)].integer)-1, context); ;} break; case 42: #line 201 "igraph/src/foreign-dl-parser.y" { (yyval.integer)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner)); ;} break; case 43: #line 204 "igraph/src/foreign-dl-parser.y" {;} break; case 44: #line 205 "igraph/src/foreign-dl-parser.y" {;} break; case 45: #line 208 "igraph/src/foreign-dl-parser.y" { igraph_i_dl_add_edge_w((yyvsp[(1) - (4)].integer), (yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].real), context); ;} break; case 46: #line 210 "igraph/src/foreign-dl-parser.y" { igraph_i_dl_add_edge((yyvsp[(1) - (3)].integer), (yyvsp[(2) - (3)].integer), context); ;} break; case 47: #line 214 "igraph/src/foreign-dl-parser.y" { (yyval.real)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner)); ;} break; case 48: #line 217 "igraph/src/foreign-dl-parser.y" { /* Copy label list to trie, if needed */ if (igraph_strvector_size(&context->labels) != 0) { long int i, id, n=igraph_strvector_size(&context->labels); for (i=0; itrie, STR(context->labels, i), &id); } igraph_strvector_clear(&context->labels); } igraph_trie_get2(&context->trie, igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner), &(yyval.integer)); ;} break; case 49: #line 233 "igraph/src/foreign-dl-parser.y" {;} break; case 50: #line 235 "igraph/src/foreign-dl-parser.y" {;} break; case 51: #line 236 "igraph/src/foreign-dl-parser.y" {;} break; case 52: #line 237 "igraph/src/foreign-dl-parser.y" {;} break; case 53: #line 238 "igraph/src/foreign-dl-parser.y" {;} break; case 54: #line 239 "igraph/src/foreign-dl-parser.y" {;} break; case 55: #line 242 "igraph/src/foreign-dl-parser.y" {;} break; case 56: #line 243 "igraph/src/foreign-dl-parser.y" {;} break; case 57: #line 246 "igraph/src/foreign-dl-parser.y" {;} break; case 58: #line 248 "igraph/src/foreign-dl-parser.y" { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner)); ;} break; case 59: #line 251 "igraph/src/foreign-dl-parser.y" {;} break; case 60: #line 251 "igraph/src/foreign-dl-parser.y" { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->from-1)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[(2) - (2)].integer)-1)); ;} break; case 61: #line 257 "igraph/src/foreign-dl-parser.y" {;} break; case 62: #line 258 "igraph/src/foreign-dl-parser.y" {;} break; case 63: #line 261 "igraph/src/foreign-dl-parser.y" { ;} break; case 64: #line 263 "igraph/src/foreign-dl-parser.y" { context->from=(yyvsp[(1) - (1)].integer); ;} break; case 66: #line 267 "igraph/src/foreign-dl-parser.y" { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->from)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[(2) - (2)].integer))); ;} break; /* Line 1267 of yacc.c. */ #line 1887 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (&yylloc, context, YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (&yylloc, context, yymsg); } else { yyerror (&yylloc, context, YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } yyerror_range[0] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, context); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; yyerror_range[0] = yylsp[1-yylen]; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[0] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp, context); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; yyerror_range[1] = yylloc; /* Using YYLLOC is tempting, but would change the location of the look-ahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, context, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, context); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp, context); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } #line 273 "igraph/src/foreign-dl-parser.y" int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, const char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "%s in line %i", s, locp->first_line); return 0; } int igraph_i_dl_add_str(char *newstr, int length, igraph_i_dl_parsedata_t *context) { int tmp=newstr[length]; newstr[length]='\0'; IGRAPH_CHECK(igraph_strvector_add(&context->labels, newstr)); newstr[length]=tmp; return 0; } int igraph_i_dl_add_edge(long int from, long int to, igraph_i_dl_parsedata_t *context) { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, from)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, to)); return 0; } int igraph_i_dl_add_edge_w(long int from, long int to, igraph_real_t weight, igraph_i_dl_parsedata_t *context) { long int n=igraph_vector_size(&context->weights); long int n2=igraph_vector_size(&context->edges)/2; if (n != n2) { igraph_vector_resize(&context->weights, n2); for (; nweights)[n]=IGRAPH_NAN; } } IGRAPH_CHECK(igraph_i_dl_add_edge(from, to, context)); IGRAPH_CHECK(igraph_vector_push_back(&context->weights, weight)); return 0; } igraph/src/glpios11.c0000644000176000001440000002503412325527073014160 0ustar ripleyusers/* glpios11.c (process cuts stored in the local cut pool) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpios.h" /*********************************************************************** * NAME * * ios_process_cuts - process cuts stored in the local cut pool * * SYNOPSIS * * #include "glpios.h" * void ios_process_cuts(glp_tree *T); * * DESCRIPTION * * The routine ios_process_cuts analyzes each cut currently stored in * the local cut pool, which must be non-empty, and either adds the cut * to the current subproblem or just discards it. All cuts are assumed * to be locally valid. On exit the local cut pool remains unchanged. * * REFERENCES * * 1. E.Balas, S.Ceria, G.Cornuejols, "Mixed 0-1 Programming by * Lift-and-Project in a Branch-and-Cut Framework", Management Sc., * 42 (1996) 1229-1246. * * 2. G.Andreello, A.Caprara, and M.Fischetti, "Embedding Cuts in * a Branch&Cut Framework: a Computational Study with {0,1/2}-Cuts", * Preliminary Draft, October 28, 2003, pp.6-8. */ struct info { /* estimated cut efficiency */ IOSCUT *cut; /* pointer to cut in the cut pool */ char flag; /* if this flag is set, the cut is included into the current subproblem */ double eff; /* cut efficacy (normalized residual) */ double deg; /* lower bound to objective degradation */ }; static int fcmp(const void *arg1, const void *arg2) { const struct info *info1 = arg1, *info2 = arg2; if (info1->deg == 0.0 && info2->deg == 0.0) { if (info1->eff > info2->eff) return -1; if (info1->eff < info2->eff) return +1; } else { if (info1->deg > info2->deg) return -1; if (info1->deg < info2->deg) return +1; } return 0; } static double parallel(IOSCUT *a, IOSCUT *b, double work[]); void ios_process_cuts(glp_tree *T) { IOSPOOL *pool; IOSCUT *cut; IOSAIJ *aij; struct info *info; int k, kk, max_cuts, len, ret, *ind; double *val, *work; /* the current subproblem must exist */ xassert(T->curr != NULL); /* the pool must exist and be non-empty */ pool = T->local; xassert(pool != NULL); xassert(pool->size > 0); /* allocate working arrays */ info = xcalloc(1+pool->size, sizeof(struct info)); ind = xcalloc(1+T->n, sizeof(int)); val = xcalloc(1+T->n, sizeof(double)); work = xcalloc(1+T->n, sizeof(double)); for (k = 1; k <= T->n; k++) work[k] = 0.0; /* build the list of cuts stored in the cut pool */ for (k = 0, cut = pool->head; cut != NULL; cut = cut->next) k++, info[k].cut = cut, info[k].flag = 0; xassert(k == pool->size); /* estimate efficiency of all cuts in the cut pool */ for (k = 1; k <= pool->size; k++) { double temp, dy, dz; cut = info[k].cut; /* build the vector of cut coefficients and compute its Euclidean norm */ len = 0; temp = 0.0; for (aij = cut->ptr; aij != NULL; aij = aij->next) { xassert(1 <= aij->j && aij->j <= T->n); len++, ind[len] = aij->j, val[len] = aij->val; temp += aij->val * aij->val; } if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON; /* transform the cut to express it only through non-basic (auxiliary and structural) variables */ len = glp_transform_row(T->mip, len, ind, val); /* determine change in the cut value and in the objective value for the adjacent basis by simulating one step of the dual simplex */ ret = _glp_analyze_row(T->mip, len, ind, val, cut->type, cut->rhs, 1e-9, NULL, NULL, NULL, NULL, &dy, &dz); /* determine normalized residual and lower bound to objective degradation */ if (ret == 0) { info[k].eff = fabs(dy) / sqrt(temp); /* if some reduced costs violates (slightly) their zero bounds (i.e. have wrong signs) due to round-off errors, dz also may have wrong sign being close to zero */ if (T->mip->dir == GLP_MIN) { if (dz < 0.0) dz = 0.0; info[k].deg = + dz; } else /* GLP_MAX */ { if (dz > 0.0) dz = 0.0; info[k].deg = - dz; } } else if (ret == 1) { /* the constraint is not violated at the current point */ info[k].eff = info[k].deg = 0.0; } else if (ret == 2) { /* no dual feasible adjacent basis exists */ info[k].eff = 1.0; info[k].deg = DBL_MAX; } else xassert(ret != ret); /* if the degradation is too small, just ignore it */ if (info[k].deg < 0.01) info[k].deg = 0.0; } /* sort the list of cuts by decreasing objective degradation and then by decreasing efficacy */ qsort(&info[1], pool->size, sizeof(struct info), fcmp); /* only first (most efficient) max_cuts in the list are qualified as candidates to be added to the current subproblem */ max_cuts = (T->curr->level == 0 ? 90 : 10); if (max_cuts > pool->size) max_cuts = pool->size; /* add cuts to the current subproblem */ #if 0 xprintf("*** adding cuts ***\n"); #endif for (k = 1; k <= max_cuts; k++) { int i, len; /* if this cut seems to be inefficient, skip it */ if (info[k].deg < 0.01 && info[k].eff < 0.01) continue; /* if the angle between this cut and every other cut included in the current subproblem is small, skip this cut */ for (kk = 1; kk < k; kk++) { if (info[kk].flag) { if (parallel(info[k].cut, info[kk].cut, work) > 0.90) break; } } if (kk < k) continue; /* add this cut to the current subproblem */ #if 0 xprintf("eff = %g; deg = %g\n", info[k].eff, info[k].deg); #endif cut = info[k].cut, info[k].flag = 1; i = glp_add_rows(T->mip, 1); if (cut->name != NULL) glp_set_row_name(T->mip, i, cut->name); xassert(T->mip->row[i]->origin == GLP_RF_CUT); T->mip->row[i]->klass = cut->klass; len = 0; for (aij = cut->ptr; aij != NULL; aij = aij->next) len++, ind[len] = aij->j, val[len] = aij->val; glp_set_mat_row(T->mip, i, len, ind, val); xassert(cut->type == GLP_LO || cut->type == GLP_UP); glp_set_row_bnds(T->mip, i, cut->type, cut->rhs, cut->rhs); } /* free working arrays */ xfree(info); xfree(ind); xfree(val); xfree(work); return; } #if 0 /*********************************************************************** * Given a cut a * x >= b (<= b) the routine efficacy computes the cut * efficacy as follows: * * eff = d * (a * x~ - b) / ||a||, * * where d is -1 (in case of '>= b') or +1 (in case of '<= b'), x~ is * the vector of values of structural variables in optimal solution to * LP relaxation of the current subproblem, ||a|| is the Euclidean norm * of the vector of cut coefficients. * * If the cut is violated at point x~, the efficacy eff is positive, * and its value is the Euclidean distance between x~ and the cut plane * a * x = b in the space of structural variables. * * Following geometrical intuition, it is quite natural to consider * this distance as a first-order measure of the expected efficacy of * the cut: the larger the distance the better the cut [1]. */ static double efficacy(glp_tree *T, IOSCUT *cut) { glp_prob *mip = T->mip; IOSAIJ *aij; double s = 0.0, t = 0.0, temp; for (aij = cut->ptr; aij != NULL; aij = aij->next) { xassert(1 <= aij->j && aij->j <= mip->n); s += aij->val * mip->col[aij->j]->prim; t += aij->val * aij->val; } temp = sqrt(t); if (temp < DBL_EPSILON) temp = DBL_EPSILON; if (cut->type == GLP_LO) temp = (s >= cut->rhs ? 0.0 : (cut->rhs - s) / temp); else if (cut->type == GLP_UP) temp = (s <= cut->rhs ? 0.0 : (s - cut->rhs) / temp); else xassert(cut != cut); return temp; } #endif /*********************************************************************** * Given two cuts a1 * x >= b1 (<= b1) and a2 * x >= b2 (<= b2) the * routine parallel computes the cosine of angle between the cut planes * a1 * x = b1 and a2 * x = b2 (which is the acute angle between two * normals to these planes) in the space of structural variables as * follows: * * cos phi = (a1' * a2) / (||a1|| * ||a2||), * * where (a1' * a2) is a dot product of vectors of cut coefficients, * ||a1|| and ||a2|| are Euclidean norms of vectors a1 and a2. * * Note that requirement cos phi = 0 forces the cuts to be orthogonal, * i.e. with disjoint support, while requirement cos phi <= 0.999 means * only avoiding duplicate (parallel) cuts [1]. */ static double parallel(IOSCUT *a, IOSCUT *b, double work[]) { IOSAIJ *aij; double s = 0.0, sa = 0.0, sb = 0.0, temp; for (aij = a->ptr; aij != NULL; aij = aij->next) { work[aij->j] = aij->val; sa += aij->val * aij->val; } for (aij = b->ptr; aij != NULL; aij = aij->next) { s += work[aij->j] * aij->val; sb += aij->val * aij->val; } for (aij = a->ptr; aij != NULL; aij = aij->next) work[aij->j] = 0.0; temp = sqrt(sa) * sqrt(sb); if (temp < DBL_EPSILON * DBL_EPSILON) temp = DBL_EPSILON; return s / temp; } /* eof */ igraph/src/igraph_matrix.h0000644000176000001440000000553012325527073015363 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_MATRIX_H #define IGRAPH_MATRIX_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_vector.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Matrix, very similar to vector */ /* -------------------------------------------------- */ #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_INT #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" #include "igraph_pmt_off.h" #undef BASE_INT #define BASE_LONG #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" #include "igraph_pmt_off.h" #undef BASE_BOOL #define BASE_COMPLEX #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" #include "igraph_pmt_off.h" #undef BASE_COMPLEX #define IGRAPH_MATRIX_NULL { IGRAPH_VECTOR_NULL, 0, 0 } #define IGRAPH_MATRIX_INIT_FINALLY(m, nr, nc) \ do { IGRAPH_CHECK(igraph_matrix_init(m, nr, nc)); \ IGRAPH_FINALLY(igraph_matrix_destroy, m); } while (0) /** * \ingroup matrix * \define MATRIX * \brief Accessing an element of a matrix. * * Note that there are no range checks right now. * This functionality might be redefined as a proper function later. * \param m The matrix object. * \param i The index of the row, starting with zero. * \param j The index of the column, starting with zero. * * Time complexity: O(1). */ #define MATRIX(m,i,j) ((m).data.stor_begin[(m).nrow*(j)+(i)]) igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, const igraph_matrix_t *rhs, igraph_real_t tol); __END_DECLS #endif igraph/src/igraph_math.h0000644000176000001440000000377412325527073015020 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2008-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_MATH_H #define IGRAPH_MATH_H #include "config.h" #include #include #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* * Compiler-related hacks, mostly because of Microsoft Visual C++ */ double igraph_i_round(double X); int igraph_i_snprintf(char *buffer, size_t count, const char *format, ...); double igraph_log2(const double a); double igraph_log1p(double a); long double igraph_fabsl(long double a); double igraph_fmin(double a, double b); #ifndef HAVE_LOG2 #define log2(a) igraph_log2(a) #endif #ifndef HAVE_LOG1P #define log1p(a) igraph_log1p(a) #endif #ifndef HAVE_FABSL #define fabsl(a) igraph_fabsl(a) #endif #ifndef HAVE_FMIN #define fmin(a,b) igraph_fmin((a),(b)) #endif #ifndef HAVE_ROUND #define round igraph_i_round #endif #ifndef M_PI # define M_PI 3.14159265358979323846 #endif #ifndef M_LN2 # define M_LN2 0.69314718055994530942 #endif #ifndef M_SQRT2 # define M_SQRT2 1.4142135623730950488016887 #endif __END_DECLS #endif igraph/src/distances.c0000644000176000001440000001464012325527073014477 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_datatype.h" #include "igraph_dqueue.h" #include "igraph_iterators.h" #include "igraph_interrupt_internal.h" #include "igraph_vector.h" #include "igraph_interface.h" #include "igraph_adjlist.h" int igraph_i_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_neimode_t mode, const igraph_adjlist_t *adjlist) { int no_of_nodes=igraph_vcount(graph); igraph_dqueue_long_t q; igraph_vit_t vit; igraph_vector_int_t counted; int i, mark=1; igraph_vector_t vneis; igraph_vector_int_t *neis; IGRAPH_CHECK(igraph_dqueue_long_init(&q, 100)); IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); IGRAPH_CHECK(igraph_vector_int_init(&counted, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_int_destroy, &counted); if (!adjlist) { IGRAPH_VECTOR_INIT_FINALLY(&vneis, 0); } IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); igraph_vector_fill(res, -1); for (i=0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), mark++, i++) { long int source; source=IGRAPH_VIT_GET(vit); IGRAPH_CHECK(igraph_dqueue_long_push(&q, source)); IGRAPH_CHECK(igraph_dqueue_long_push(&q, 0)); VECTOR(counted)[source]=mark; IGRAPH_ALLOW_INTERRUPTION(); while (!igraph_dqueue_long_empty(&q)) { long int act=igraph_dqueue_long_pop(&q); long int dist=igraph_dqueue_long_pop(&q); int j, n; if (dist > VECTOR(*res)[i]) { VECTOR(*res)[i]=dist; } if (adjlist) { neis=igraph_adjlist_get(adjlist, act); n=(int) igraph_vector_int_size(neis); for (j=0; j * This implementation ignores vertex pairs that are in different * components. Isolated vertices have eccentricity zero. * * \param graph The input graph, it can be directed or undirected. * \param res Pointer to an initialized vector, the result is stored * here. * \param vids The vertices for which the eccentricity is calculated. * \param mode What kind of paths to consider for the calculation: * \c IGRAPH_OUT, paths that follow edge directions; * \c IGRAPH_IN, paths that follow the opposite directions; and * \c IGRAPH_ALL, paths that ignore edge directions. This argument * is ignored for undirected graphs. * \return Error code. * * Time complexity: O(v*(|V|+|E|)), where |V| is the number of * vertices, |E| is the number of edges and v is the number of * vertices for which eccentricity is calculated. * * \sa \ref igraph_radius(). * * \example examples/simple/igraph_eccentricity.c */ int igraph_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_neimode_t mode) { return igraph_i_eccentricity(graph, res, vids, mode, /*adjlist=*/ 0); } /** * \function igraph_radius * Radius of a graph * * The radius of a graph is the defined as the minimum eccentricity of * its vertices, see \ref igraph_eccentricity(). * * \param graph The input graph, it can be directed or undirected. * \param radius Pointer to a real variable, the result is stored * here. * \param mode What kind of paths to consider for the calculation: * \c IGRAPH_OUT, paths that follow edge directions; * \c IGRAPH_IN, paths that follow the opposite directions; and * \c IGRAPH_ALL, paths that ignore edge directions. This argument * is ignored for undirected graphs. * \return Error code. * * Time complexity: O(|V|(|V|+|E|)), where |V| is the number of * vertices and |E| is the number of edges. * * \sa \ref igraph_eccentricity(). * * \example examples/simple/igraph_radius.c */ int igraph_radius(const igraph_t *graph, igraph_real_t *radius, igraph_neimode_t mode) { int no_of_nodes=igraph_vcount(graph); if (no_of_nodes==0) { *radius = IGRAPH_NAN; } else { igraph_adjlist_t adjlist; igraph_vector_t ecc; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_VECTOR_INIT_FINALLY(&ecc, igraph_vcount(graph)); IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc, igraph_vss_all(), mode, &adjlist)); *radius = igraph_vector_min(&ecc); igraph_vector_destroy(&ecc); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(2); } return 0; } igraph/src/glpmps.c0000644000176000001440000013355212325527073014030 0ustar ripleyusers/* glpmps.c (MPS format routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wself-assign" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_init_mpscp - initialize MPS format control parameters * * SYNOPSIS * * void glp_init_mpscp(glp_mpscp *parm); * * DESCRIPTION * * The routine glp_init_mpscp initializes control parameters, which are * used by the MPS input/output routines glp_read_mps and glp_write_mps, * with default values. * * Default values of the control parameters are stored in the glp_mpscp * structure, which the parameter parm points to. */ void glp_init_mpscp(glp_mpscp *parm) { parm->blank = '\0'; parm->obj_name = NULL; parm->tol_mps = 1e-12; return; } static void check_parm(const char *func, const glp_mpscp *parm) { /* check control parameters */ if (!(0x00 <= parm->blank && parm->blank <= 0xFF) || !(parm->blank == '\0' || isprint(parm->blank))) xerror("%s: blank = 0x%02X; invalid parameter\n", func, parm->blank); if (!(parm->obj_name == NULL || strlen(parm->obj_name) <= 255)) xerror("%s: obj_name = \"%.12s...\"; parameter too long\n", func, parm->obj_name); if (!(0.0 <= parm->tol_mps && parm->tol_mps < 1.0)) xerror("%s: tol_mps = %g; invalid parameter\n", func, parm->tol_mps); return; } /*********************************************************************** * NAME * * glp_read_mps - read problem data in MPS format * * SYNOPSIS * * int glp_read_mps(glp_prob *P, int fmt, const glp_mpscp *parm, * const char *fname); * * DESCRIPTION * * The routine glp_read_mps reads problem data in MPS format from a * text file. * * The parameter fmt specifies the version of MPS format: * * GLP_MPS_DECK - fixed (ancient) MPS format; * GLP_MPS_FILE - free (modern) MPS format. * * The parameter parm is a pointer to the structure glp_mpscp, which * specifies control parameters used by the routine. If parm is NULL, * the routine uses default settings. * * The character string fname specifies a name of the text file to be * read. * * Note that before reading data the current content of the problem * object is completely erased with the routine glp_erase_prob. * * RETURNS * * If the operation was successful, the routine glp_read_mps returns * zero. Otherwise, it prints an error message and returns non-zero. */ struct csa { /* common storage area */ glp_prob *P; /* pointer to problem object */ int deck; /* MPS format (0 - free, 1 - fixed) */ const glp_mpscp *parm; /* pointer to control parameters */ const char *fname; /* name of input MPS file */ XFILE *fp; /* stream assigned to input MPS file */ jmp_buf jump; /* label for go to in case of error */ int recno; /* current record (card) number */ int recpos; /* current record (card) position */ int c; /* current character */ int fldno; /* current field number */ char field[255+1]; /* current field content */ int w80; /* warning 'record must not be longer than 80 chars' issued */ int wef; /* warning 'extra fields detected beyond field 6' issued */ int obj_row; /* objective row number */ void *work1, *work2, *work3; /* working arrays */ }; static void error(struct csa *csa, const char *fmt, ...) { /* print error message and terminate processing */ va_list arg; xprintf("%s:%d: ", csa->fname, csa->recno); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); longjmp(csa->jump, 1); /* no return */ } static void warning(struct csa *csa, const char *fmt, ...) { /* print warning message and continue processing */ va_list arg; xprintf("%s:%d: warning: ", csa->fname, csa->recno); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); return; } static void read_char(struct csa *csa) { /* read next character */ int c; if (csa->c == '\n') csa->recno++, csa->recpos = 0; csa->recpos++; read: c = xfgetc(csa->fp); if (c < 0) { if (xferror(csa->fp)) error(csa, "read error - %s\n", xerrmsg()); else if (csa->c == '\n') error(csa, "unexpected end of file\n"); else { warning(csa, "missing final end of line\n"); c = '\n'; } } else if (c == '\n') ; else if (csa->c == '\r') { c = '\r'; goto badc; } else if (csa->deck && c == '\r') { csa->c = '\r'; goto read; } else if (c == ' ') ; else if (isspace(c)) { if (csa->deck) badc: error(csa, "in fixed MPS format white-space character 0x%02" "X is not allowed\n", c); c = ' '; } else if (iscntrl(c)) error(csa, "invalid control character 0x%02X\n", c); if (csa->deck && csa->recpos == 81 && c != '\n' && csa->w80 < 1) { warning(csa, "in fixed MPS format record must not be longer th" "an 80 characters\n"); csa->w80++; } csa->c = c; return; } static int indicator(struct csa *csa, int name) { /* skip comment records and read possible indicator record */ int ret; /* reset current field number */ csa->fldno = 0; loop: /* read the very first character of the next record */ xassert(csa->c == '\n'); read_char(csa); if (csa->c == ' ' || csa->c == '\n') { /* data record */ ret = 0; } else if (csa->c == '*') { /* comment record */ while (csa->c != '\n') read_char(csa); goto loop; } else { /* indicator record */ int len = 0; while (csa->c != ' ' && csa->c != '\n' && len < 12) { csa->field[len++] = (char)csa->c; read_char(csa); } csa->field[len] = '\0'; if (!(strcmp(csa->field, "NAME") == 0 || strcmp(csa->field, "ROWS") == 0 || strcmp(csa->field, "COLUMNS") == 0 || strcmp(csa->field, "RHS") == 0 || strcmp(csa->field, "RANGES") == 0 || strcmp(csa->field, "BOUNDS") == 0 || strcmp(csa->field, "ENDATA") == 0)) error(csa, "invalid indicator record\n"); if (!name) { while (csa->c != '\n') read_char(csa); } ret = 1; } return ret; } static void read_field(struct csa *csa) { /* read next field of the current data record */ csa->fldno++; if (csa->deck) { /* fixed MPS format */ int beg, end, pos; /* determine predefined field positions */ if (csa->fldno == 1) beg = 2, end = 3; else if (csa->fldno == 2) beg = 5, end = 12; else if (csa->fldno == 3) beg = 15, end = 22; else if (csa->fldno == 4) beg = 25, end = 36; else if (csa->fldno == 5) beg = 40, end = 47; else if (csa->fldno == 6) beg = 50, end = 61; else xassert(csa != csa); /* skip blanks preceding the current field */ if (csa->c != '\n') { pos = csa->recpos; while (csa->recpos < beg) { if (csa->c == ' ') ; else if (csa->c == '\n') break; else error(csa, "in fixed MPS format positions %d-%d must " "be blank\n", pos, beg-1); read_char(csa); } } /* skip possible comment beginning in the field 3 or 5 */ if ((csa->fldno == 3 || csa->fldno == 5) && csa->c == '$') { while (csa->c != '\n') read_char(csa); } /* read the current field */ for (pos = beg; pos <= end; pos++) { if (csa->c == '\n') break; csa->field[pos-beg] = (char)csa->c; read_char(csa); } csa->field[pos-beg] = '\0'; strtrim(csa->field); /* skip blanks following the last field */ if (csa->fldno == 6 && csa->c != '\n') { while (csa->recpos <= 72) { if (csa->c == ' ') ; else if (csa->c == '\n') break; else error(csa, "in fixed MPS format positions 62-72 must " "be blank\n"); read_char(csa); } while (csa->c != '\n') read_char(csa); } } else { /* free MPS format */ int len; /* skip blanks preceding the current field */ while (csa->c == ' ') read_char(csa); /* skip possible comment */ if (csa->c == '$') { while (csa->c != '\n') read_char(csa); } /* read the current field */ len = 0; while (!(csa->c == ' ' || csa->c == '\n')) { if (len == 255) error(csa, "length of field %d exceeds 255 characters\n", csa->fldno++); csa->field[len++] = (char)csa->c; read_char(csa); } csa->field[len] = '\0'; /* skip anything following the last field (any extra fields are considered to be comments) */ if (csa->fldno == 6) { while (csa->c == ' ') read_char(csa); if (csa->c != '$' && csa->c != '\n' && csa->wef < 1) { warning(csa, "some extra field(s) detected beyond field " "6; field(s) ignored\n"); csa->wef++; } while (csa->c != '\n') read_char(csa); } } return; } static void patch_name(struct csa *csa, char *name) { /* process embedded blanks in symbolic name */ int blank = csa->parm->blank; if (blank == '\0') { /* remove emedded blanks */ strspx(name); } else { /* replace embedded blanks by specified character */ for (; *name != '\0'; name++) if (*name == ' ') *name = (char)blank; } return; } static double read_number(struct csa *csa) { /* read next field and convert it to floating-point number */ double x; char *s; /* read next field */ read_field(csa); xassert(csa->fldno == 4 || csa->fldno == 6); if (csa->field[0] == '\0') error(csa, "missing numeric value in field %d\n", csa->fldno); /* skip initial spaces of the field */ for (s = csa->field; *s == ' '; s++); /* perform conversion */ if (str2num(s, &x) != 0) error(csa, "cannot convert `%s' to floating-point number\n", s); return x; } static void skip_field(struct csa *csa) { /* read and skip next field (assumed to be blank) */ read_field(csa); if (csa->field[0] != '\0') error(csa, "field %d must be blank\n", csa->fldno); return; } static void read_name(struct csa *csa) { /* read NAME indicator record */ if (!(indicator(csa, 1) && strcmp(csa->field, "NAME") == 0)) error(csa, "missing NAME indicator record\n"); /* this indicator record looks like a data record; simulate that fields 1 and 2 were read */ csa->fldno = 2; /* field 3: model name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') warning(csa, "missing model name in field 3\n"); else glp_set_prob_name(csa->P, csa->field); /* skip anything following field 3 */ while (csa->c != '\n') read_char(csa); return; } static void read_rows(struct csa *csa) { /* read ROWS section */ int i, type; loop: if (indicator(csa, 0)) goto done; /* field 1: row type */ read_field(csa), strspx(csa->field); if (strcmp(csa->field, "N") == 0) type = GLP_FR; else if (strcmp(csa->field, "G") == 0) type = GLP_LO; else if (strcmp(csa->field, "L") == 0) type = GLP_UP; else if (strcmp(csa->field, "E") == 0) type = GLP_FX; else if (csa->field[0] == '\0') error(csa, "missing row type in field 1\n"); else error(csa, "invalid row type in field 1\n"); /* field 2: row name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') error(csa, "missing row name in field 2\n"); if (glp_find_row(csa->P, csa->field) != 0) error(csa, "row `%s' multiply specified\n", csa->field); i = glp_add_rows(csa->P, 1); glp_set_row_name(csa->P, i, csa->field); glp_set_row_bnds(csa->P, i, type, 0.0, 0.0); /* fields 3, 4, 5, and 6 must be blank */ skip_field(csa); skip_field(csa); skip_field(csa); skip_field(csa); goto loop; done: return; } static void read_columns(struct csa *csa) { /* read COLUMNS section */ int i, j, f, len, kind = GLP_CV, *ind; double aij, *val; char name[255+1], *flag; /* allocate working arrays */ csa->work1 = ind = xcalloc(1+csa->P->m, sizeof(int)); csa->work2 = val = xcalloc(1+csa->P->m, sizeof(double)); csa->work3 = flag = xcalloc(1+csa->P->m, sizeof(char)); memset(&flag[1], 0, csa->P->m); /* no current column exists */ j = 0, len = 0; loop: if (indicator(csa, 0)) goto done; /* field 1 must be blank */ if (csa->deck) { read_field(csa); if (csa->field[0] != '\0') error(csa, "field 1 must be blank\n"); } else csa->fldno++; /* field 2: column or kind name */ read_field(csa), patch_name(csa, csa->field); strcpy(name, csa->field); /* field 3: row name or keyword 'MARKER' */ read_field(csa), patch_name(csa, csa->field); if (strcmp(csa->field, "'MARKER'") == 0) { /* process kind data record */ /* field 4 must be blank */ if (csa->deck) { read_field(csa); if (csa->field[0] != '\0') error(csa, "field 4 must be blank\n"); } else csa->fldno++; /* field 5: keyword 'INTORG' or 'INTEND' */ read_field(csa), patch_name(csa, csa->field); if (strcmp(csa->field, "'INTORG'") == 0) kind = GLP_IV; else if (strcmp(csa->field, "'INTEND'") == 0) kind = GLP_CV; else if (csa->field[0] == '\0') error(csa, "missing keyword in field 5\n"); else error(csa, "invalid keyword in field 5\n"); /* field 6 must be blank */ skip_field(csa); goto loop; } /* process column name specified in field 2 */ if (name[0] == '\0') { /* the same column as in previous data record */ if (j == 0) error(csa, "missing column name in field 2\n"); } else if (j != 0 && strcmp(name, csa->P->col[j]->name) == 0) { /* the same column as in previous data record */ xassert(j != 0); } else { /* store the current column */ if (j != 0) { glp_set_mat_col(csa->P, j, len, ind, val); while (len > 0) flag[ind[len--]] = 0; } /* create new column */ if (glp_find_col(csa->P, name) != 0) error(csa, "column `%s' multiply specified\n", name); j = glp_add_cols(csa->P, 1); glp_set_col_name(csa->P, j, name); glp_set_col_kind(csa->P, j, kind); if (kind == GLP_CV) glp_set_col_bnds(csa->P, j, GLP_LO, 0.0, 0.0); else if (kind == GLP_IV) glp_set_col_bnds(csa->P, j, GLP_DB, 0.0, 1.0); else xassert(kind != kind); } /* process fields 3-4 and 5-6 */ for (f = 3; f <= 5; f += 2) { /* field 3 or 5: row name */ if (f == 3) { if (csa->field[0] == '\0') error(csa, "missing row name in field 3\n"); } else { read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') { /* if field 5 is blank, field 6 also must be blank */ skip_field(csa); continue; } } i = glp_find_row(csa->P, csa->field); if (i == 0) error(csa, "row `%s' not found\n", csa->field); if (flag[i]) error(csa, "duplicate coefficient in row `%s'\n", csa->field); /* field 4 or 6: coefficient value */ aij = read_number(csa); if (fabs(aij) < csa->parm->tol_mps) aij = 0.0; len++, ind[len] = i, val[len] = aij, flag[i] = 1; } goto loop; done: /* store the last column */ if (j != 0) glp_set_mat_col(csa->P, j, len, ind, val); /* free working arrays */ xfree(ind); xfree(val); xfree(flag); csa->work1 = csa->work2 = csa->work3 = NULL; return; } static void read_rhs(struct csa *csa) { /* read RHS section */ int i, f, v, type; double rhs; char name[255+1], *flag; /* allocate working array */ csa->work3 = flag = xcalloc(1+csa->P->m, sizeof(char)); memset(&flag[1], 0, csa->P->m); /* no current RHS vector exists */ v = 0; loop: if (indicator(csa, 0)) goto done; /* field 1 must be blank */ if (csa->deck) { read_field(csa); if (csa->field[0] != '\0') error(csa, "field 1 must be blank\n"); } else csa->fldno++; /* field 2: RHS vector name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') { /* the same RHS vector as in previous data record */ if (v == 0) { warning(csa, "missing RHS vector name in field 2\n"); goto blnk; } } else if (v != 0 && strcmp(csa->field, name) == 0) { /* the same RHS vector as in previous data record */ xassert(v != 0); } else blnk: { /* new RHS vector */ if (v != 0) error(csa, "multiple RHS vectors not supported\n"); v++; strcpy(name, csa->field); } /* process fields 3-4 and 5-6 */ for (f = 3; f <= 5; f += 2) { /* field 3 or 5: row name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') { if (f == 3) error(csa, "missing row name in field 3\n"); else { /* if field 5 is blank, field 6 also must be blank */ skip_field(csa); continue; } } i = glp_find_row(csa->P, csa->field); if (i == 0) error(csa, "row `%s' not found\n", csa->field); if (flag[i]) error(csa, "duplicate right-hand side for row `%s'\n", csa->field); /* field 4 or 6: right-hand side value */ rhs = read_number(csa); if (fabs(rhs) < csa->parm->tol_mps) rhs = 0.0; type = csa->P->row[i]->type; if (type == GLP_FR) { if (i == csa->obj_row) glp_set_obj_coef(csa->P, 0, rhs); else if (rhs != 0.0) warning(csa, "non-zero right-hand side for free row `%s'" " ignored\n", csa->P->row[i]->name); } else glp_set_row_bnds(csa->P, i, type, rhs, rhs); flag[i] = 1; } goto loop; done: /* free working array */ xfree(flag); csa->work3 = NULL; return; } static void read_ranges(struct csa *csa) { /* read RANGES section */ int i, f, v, type; double rhs, rng; char name[255+1], *flag; /* allocate working array */ csa->work3 = flag = xcalloc(1+csa->P->m, sizeof(char)); memset(&flag[1], 0, csa->P->m); /* no current RANGES vector exists */ v = 0; loop: if (indicator(csa, 0)) goto done; /* field 1 must be blank */ if (csa->deck) { read_field(csa); if (csa->field[0] != '\0') error(csa, "field 1 must be blank\n"); } else csa->fldno++; /* field 2: RANGES vector name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') { /* the same RANGES vector as in previous data record */ if (v == 0) { warning(csa, "missing RANGES vector name in field 2\n"); goto blnk; } } else if (v != 0 && strcmp(csa->field, name) == 0) { /* the same RANGES vector as in previous data record */ xassert(v != 0); } else blnk: { /* new RANGES vector */ if (v != 0) error(csa, "multiple RANGES vectors not supported\n"); v++; strcpy(name, csa->field); } /* process fields 3-4 and 5-6 */ for (f = 3; f <= 5; f += 2) { /* field 3 or 5: row name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') { if (f == 3) error(csa, "missing row name in field 3\n"); else { /* if field 5 is blank, field 6 also must be blank */ skip_field(csa); continue; } } i = glp_find_row(csa->P, csa->field); if (i == 0) error(csa, "row `%s' not found\n", csa->field); if (flag[i]) error(csa, "duplicate range for row `%s'\n", csa->field); /* field 4 or 6: range value */ rng = read_number(csa); if (fabs(rng) < csa->parm->tol_mps) rng = 0.0; type = csa->P->row[i]->type; if (type == GLP_FR) warning(csa, "range for free row `%s' ignored\n", csa->P->row[i]->name); else if (type == GLP_LO) { rhs = csa->P->row[i]->lb; glp_set_row_bnds(csa->P, i, rhs == 0.0 ? GLP_FX : GLP_DB, rhs, rhs + fabs(rng)); } else if (type == GLP_UP) { rhs = csa->P->row[i]->ub; glp_set_row_bnds(csa->P, i, rhs == 0.0 ? GLP_FX : GLP_DB, rhs - fabs(rng), rhs); } else if (type == GLP_FX) { rhs = csa->P->row[i]->lb; if (rng > 0.0) glp_set_row_bnds(csa->P, i, GLP_DB, rhs, rhs + rng); else if (rng < 0.0) glp_set_row_bnds(csa->P, i, GLP_DB, rhs + rng, rhs); } else xassert(type != type); flag[i] = 1; } goto loop; done: /* free working array */ xfree(flag); csa->work3 = NULL; return; } static void read_bounds(struct csa *csa) { /* read BOUNDS section */ GLPCOL *col; int j, v, mask, data; double bnd, lb, ub; char type[2+1], name[255+1], *flag; /* allocate working array */ csa->work3 = flag = xcalloc(1+csa->P->n, sizeof(char)); memset(&flag[1], 0, csa->P->n); /* no current BOUNDS vector exists */ v = 0; loop: if (indicator(csa, 0)) goto done; /* field 1: bound type */ read_field(csa); if (strcmp(csa->field, "LO") == 0) mask = 0x01, data = 1; else if (strcmp(csa->field, "UP") == 0) mask = 0x10, data = 1; else if (strcmp(csa->field, "FX") == 0) mask = 0x11, data = 1; else if (strcmp(csa->field, "FR") == 0) mask = 0x11, data = 0; else if (strcmp(csa->field, "MI") == 0) mask = 0x01, data = 0; else if (strcmp(csa->field, "PL") == 0) mask = 0x10, data = 0; else if (strcmp(csa->field, "LI") == 0) mask = 0x01, data = 1; else if (strcmp(csa->field, "UI") == 0) mask = 0x10, data = 1; else if (strcmp(csa->field, "BV") == 0) mask = 0x11, data = 0; else if (csa->field[0] == '\0') error(csa, "missing bound type in field 1\n"); else error(csa, "invalid bound type in field 1\n"); strcpy(type, csa->field); /* field 2: BOUNDS vector name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') { /* the same BOUNDS vector as in previous data record */ if (v == 0) { warning(csa, "missing BOUNDS vector name in field 2\n"); goto blnk; } } else if (v != 0 && strcmp(csa->field, name) == 0) { /* the same BOUNDS vector as in previous data record */ xassert(v != 0); } else blnk: { /* new BOUNDS vector */ if (v != 0) error(csa, "multiple BOUNDS vectors not supported\n"); v++; strcpy(name, csa->field); } /* field 3: column name */ read_field(csa), patch_name(csa, csa->field); if (csa->field[0] == '\0') error(csa, "missing column name in field 3\n"); j = glp_find_col(csa->P, csa->field); if (j == 0) error(csa, "column `%s' not found\n", csa->field); if ((flag[j] & mask) == 0x01) error(csa, "duplicate lower bound for column `%s'\n", csa->field); if ((flag[j] & mask) == 0x10) error(csa, "duplicate upper bound for column `%s'\n", csa->field); xassert((flag[j] & mask) == 0x00); /* field 4: bound value */ if (data) { bnd = read_number(csa); if (fabs(bnd) < csa->parm->tol_mps) bnd = 0.0; } else read_field(csa), bnd = 0.0; /* get current column bounds */ col = csa->P->col[j]; if (col->type == GLP_FR) lb = -DBL_MAX, ub = +DBL_MAX; else if (col->type == GLP_LO) lb = col->lb, ub = +DBL_MAX; else if (col->type == GLP_UP) lb = -DBL_MAX, ub = col->ub; else if (col->type == GLP_DB) lb = col->lb, ub = col->ub; else if (col->type == GLP_FX) lb = ub = col->lb; else xassert(col != col); /* change column bounds */ if (strcmp(type, "LO") == 0) lb = bnd; else if (strcmp(type, "UP") == 0) ub = bnd; else if (strcmp(type, "FX") == 0) lb = ub = bnd; else if (strcmp(type, "FR") == 0) lb = -DBL_MAX, ub = +DBL_MAX; else if (strcmp(type, "MI") == 0) lb = -DBL_MAX; else if (strcmp(type, "PL") == 0) ub = +DBL_MAX; else if (strcmp(type, "LI") == 0) { glp_set_col_kind(csa->P, j, GLP_IV); lb = ceil(bnd); } else if (strcmp(type, "UI") == 0) { glp_set_col_kind(csa->P, j, GLP_IV); ub = floor(bnd); } else if (strcmp(type, "BV") == 0) { glp_set_col_kind(csa->P, j, GLP_IV); lb = 0.0, ub = 1.0; } else xassert(type != type); /* set new column bounds */ if (lb == -DBL_MAX && ub == +DBL_MAX) glp_set_col_bnds(csa->P, j, GLP_FR, lb, ub); else if (ub == +DBL_MAX) glp_set_col_bnds(csa->P, j, GLP_LO, lb, ub); else if (lb == -DBL_MAX) glp_set_col_bnds(csa->P, j, GLP_UP, lb, ub); else if (lb != ub) glp_set_col_bnds(csa->P, j, GLP_DB, lb, ub); else glp_set_col_bnds(csa->P, j, GLP_FX, lb, ub); flag[j] |= (char)mask; /* fields 5 and 6 must be blank */ skip_field(csa); skip_field(csa); goto loop; done: /* free working array */ xfree(flag); csa->work3 = NULL; return; } int glp_read_mps(glp_prob *P, int fmt, const glp_mpscp *parm, const char *fname) { /* read problem data in MPS format */ glp_mpscp _parm; struct csa _csa, *csa = &_csa; int ret; xprintf("Reading problem data from `%s'...\n", fname); if (!(fmt == GLP_MPS_DECK || fmt == GLP_MPS_FILE)) xerror("glp_read_mps: fmt = %d; invalid parameter\n", fmt); if (parm == NULL) glp_init_mpscp(&_parm), parm = &_parm; /* check control parameters */ check_parm("glp_read_mps", parm); /* initialize common storage area */ csa->P = P; csa->deck = (fmt == GLP_MPS_DECK); csa->parm = parm; csa->fname = fname; csa->fp = NULL; if (setjmp(csa->jump)) { ret = 1; goto done; } csa->recno = csa->recpos = 0; csa->c = '\n'; csa->fldno = 0; csa->field[0] = '\0'; csa->w80 = csa->wef = 0; csa->obj_row = 0; csa->work1 = csa->work2 = csa->work3 = NULL; /* erase problem object */ glp_erase_prob(P); glp_create_index(P); /* open input MPS file */ csa->fp = xfopen(fname, "r"); if (csa->fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* read NAME indicator record */ read_name(csa); if (P->name != NULL) xprintf("Problem: %s\n", P->name); /* read ROWS section */ if (!(indicator(csa, 0) && strcmp(csa->field, "ROWS") == 0)) error(csa, "missing ROWS indicator record\n"); read_rows(csa); /* determine objective row */ if (parm->obj_name == NULL || parm->obj_name[0] == '\0') { /* use the first row of N type */ int i; for (i = 1; i <= P->m; i++) { if (P->row[i]->type == GLP_FR) { csa->obj_row = i; break; } } if (csa->obj_row == 0) warning(csa, "unable to determine objective row\n"); } else { /* use a row with specified name */ int i; for (i = 1; i <= P->m; i++) { xassert(P->row[i]->name != NULL); if (strcmp(parm->obj_name, P->row[i]->name) == 0) { csa->obj_row = i; break; } } if (csa->obj_row == 0) error(csa, "objective row `%s' not found\n", parm->obj_name); } if (csa->obj_row != 0) { glp_set_obj_name(P, P->row[csa->obj_row]->name); xprintf("Objective: %s\n", P->obj); } /* read COLUMNS section */ if (strcmp(csa->field, "COLUMNS") != 0) error(csa, "missing COLUMNS indicator record\n"); read_columns(csa); /* set objective coefficients */ if (csa->obj_row != 0) { GLPAIJ *aij; for (aij = P->row[csa->obj_row]->ptr; aij != NULL; aij = aij->r_next) glp_set_obj_coef(P, aij->col->j, aij->val); } /* read optional RHS section */ if (strcmp(csa->field, "RHS") == 0) read_rhs(csa); /* read optional RANGES section */ if (strcmp(csa->field, "RANGES") == 0) read_ranges(csa); /* read optional BOUNDS section */ if (strcmp(csa->field, "BOUNDS") == 0) read_bounds(csa); /* read ENDATA indicator record */ if (strcmp(csa->field, "ENDATA") != 0) error(csa, "invalid use of %s indicator record\n", csa->field); /* print some statistics */ xprintf("%d row%s, %d column%s, %d non-zero%s\n", P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", P->nnz, P->nnz == 1 ? "" : "s"); if (glp_get_num_int(P) > 0) { int ni = glp_get_num_int(P); int nb = glp_get_num_bin(P); if (ni == 1) { if (nb == 0) xprintf("One variable is integer\n"); else xprintf("One variable is binary\n"); } else { xprintf("%d integer variables, ", ni); if (nb == 0) xprintf("none"); else if (nb == 1) xprintf("one"); else if (nb == ni) xprintf("all"); else xprintf("%d", nb); xprintf(" of which %s binary\n", nb == 1 ? "is" : "are"); } } xprintf("%d records were read\n", csa->recno); /* problem data has been successfully read */ glp_delete_index(P); glp_sort_matrix(P); ret = 0; done: if (csa->fp != NULL) xfclose(csa->fp); if (csa->work1 != NULL) xfree(csa->work1); if (csa->work2 != NULL) xfree(csa->work2); if (csa->work3 != NULL) xfree(csa->work3); if (ret != 0) glp_erase_prob(P); return ret; } /*********************************************************************** * NAME * * glp_write_mps - write problem data in MPS format * * SYNOPSIS * * int glp_write_mps(glp_prob *P, int fmt, const glp_mpscp *parm, * const char *fname); * * DESCRIPTION * * The routine glp_write_mps writes problem data in MPS format to a * text file. * * The parameter fmt specifies the version of MPS format: * * GLP_MPS_DECK - fixed (ancient) MPS format; * GLP_MPS_FILE - free (modern) MPS format. * * The parameter parm is a pointer to the structure glp_mpscp, which * specifies control parameters used by the routine. If parm is NULL, * the routine uses default settings. * * The character string fname specifies a name of the text file to be * written. * * RETURNS * * If the operation was successful, the routine glp_read_mps returns * zero. Otherwise, it prints an error message and returns non-zero. */ #define csa csa1 struct csa { /* common storage area */ glp_prob *P; /* pointer to problem object */ int deck; /* MPS format (0 - free, 1 - fixed) */ const glp_mpscp *parm; /* pointer to control parameters */ char field[255+1]; /* field buffer */ }; static char *mps_name(struct csa *csa) { /* make problem name */ char *f; if (csa->P->name == NULL) csa->field[0] = '\0'; else if (csa->deck) { strncpy(csa->field, csa->P->name, 8); csa->field[8] = '\0'; } else strcpy(csa->field, csa->P->name); for (f = csa->field; *f != '\0'; f++) if (*f == ' ') *f = '_'; return csa->field; } static char *row_name(struct csa *csa, int i) { /* make i-th row name */ char *f; xassert(0 <= i && i <= csa->P->m); if (i == 0 || csa->P->row[i]->name == NULL || csa->deck && strlen(csa->P->row[i]->name) > 8) sprintf(csa->field, "R%07d", i); else { strcpy(csa->field, csa->P->row[i]->name); for (f = csa->field; *f != '\0'; f++) if (*f == ' ') *f = '_'; } return csa->field; } static char *col_name(struct csa *csa, int j) { /* make j-th column name */ char *f; xassert(1 <= j && j <= csa->P->n); if (csa->P->col[j]->name == NULL || csa->deck && strlen(csa->P->col[j]->name) > 8) sprintf(csa->field, "C%07d", j); else { strcpy(csa->field, csa->P->col[j]->name); for (f = csa->field; *f != '\0'; f++) if (*f == ' ') *f = '_'; } return csa->field; } static char *mps_numb(struct csa *csa, double val) { /* format floating-point number */ int dig; char *exp; for (dig = 12; dig >= 6; dig--) { if (val != 0.0 && fabs(val) < 0.002) sprintf(csa->field, "%.*E", dig-1, val); else sprintf(csa->field, "%.*G", dig, val); exp = strchr(csa->field, 'E'); if (exp != NULL) sprintf(exp+1, "%d", atoi(exp+1)); if (strlen(csa->field) <= 12) break; } xassert(strlen(csa->field) <= 12); return csa->field; } int glp_write_mps(glp_prob *P, int fmt, const glp_mpscp *parm, const char *fname) { /* write problem data in MPS format */ glp_mpscp _parm; struct csa _csa, *csa = &_csa; XFILE *fp; int out_obj, one_col = 0, empty = 0; int i, j, recno, marker, count, gap, ret; xprintf("Writing problem data to `%s'...\n", fname); if (!(fmt == GLP_MPS_DECK || fmt == GLP_MPS_FILE)) xerror("glp_write_mps: fmt = %d; invalid parameter\n", fmt); if (parm == NULL) glp_init_mpscp(&_parm), parm = &_parm; /* check control parameters */ check_parm("glp_write_mps", parm); /* initialize common storage area */ csa->P = P; csa->deck = (fmt == GLP_MPS_DECK); csa->parm = parm; /* create output MPS file */ fp = xfopen(fname, "w"), recno = 0; if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* write comment records */ xfprintf(fp, "* %-*s%s\n", P->name == NULL ? 1 : 12, "Problem:", P->name == NULL ? "" : P->name), recno++; xfprintf(fp, "* %-12s%s\n", "Class:", glp_get_num_int(P) == 0 ? "LP" : "MIP"), recno++; xfprintf(fp, "* %-12s%d\n", "Rows:", P->m), recno++; if (glp_get_num_int(P) == 0) xfprintf(fp, "* %-12s%d\n", "Columns:", P->n), recno++; else xfprintf(fp, "* %-12s%d (%d integer, %d binary)\n", "Columns:", P->n, glp_get_num_int(P), glp_get_num_bin(P)), recno++; xfprintf(fp, "* %-12s%d\n", "Non-zeros:", P->nnz), recno++; xfprintf(fp, "* %-12s%s\n", "Format:", csa->deck ? "Fixed MPS" : "Free MPS"), recno++; xfprintf(fp, "*\n", recno++); /* write NAME indicator record */ xfprintf(fp, "NAME%*s%s\n", P->name == NULL ? 0 : csa->deck ? 10 : 1, "", mps_name(csa)), recno++; #if 1 /* determine whether to write the objective row */ out_obj = 1; for (i = 1; i <= P->m; i++) { if (P->row[i]->type == GLP_FR) { out_obj = 0; break; } } #endif /* write ROWS section */ xfprintf(fp, "ROWS\n"), recno++; for (i = (out_obj ? 0 : 1); i <= P->m; i++) { int type; type = (i == 0 ? GLP_FR : P->row[i]->type); if (type == GLP_FR) type = 'N'; else if (type == GLP_LO) type = 'G'; else if (type == GLP_UP) type = 'L'; else if (type == GLP_DB || type == GLP_FX) type = 'E'; else xassert(type != type); xfprintf(fp, " %c%*s%s\n", type, csa->deck ? 2 : 1, "", row_name(csa, i)), recno++; } /* write COLUMNS section */ xfprintf(fp, "COLUMNS\n"), recno++; marker = 0; for (j = 1; j <= P->n; j++) { GLPAIJ cj, *aij; int kind; kind = P->col[j]->kind; if (kind == GLP_CV) { if (marker % 2 == 1) { /* close current integer block */ marker++; xfprintf(fp, "%*sM%07d%*s'MARKER'%*s'INTEND'\n", csa->deck ? 4 : 1, "", marker, csa->deck ? 2 : 1, "", csa->deck ? 17 : 1, ""), recno++; } } else if (kind == GLP_IV) { if (marker % 2 == 0) { /* open new integer block */ marker++; xfprintf(fp, "%*sM%07d%*s'MARKER'%*s'INTORG'\n", csa->deck ? 4 : 1, "", marker, csa->deck ? 2 : 1, "", csa->deck ? 17 : 1, ""), recno++; } } else xassert(kind != kind); if (out_obj && P->col[j]->coef != 0.0) { /* make fake objective coefficient */ aij = &cj; aij->row = NULL; aij->val = P->col[j]->coef; aij->c_next = P->col[j]->ptr; } else aij = P->col[j]->ptr; #if 1 /* FIXME */ if (aij == NULL) { /* empty column */ empty++; xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", csa->deck ? 8 : 1, col_name(csa, j)); /* we need a row */ xassert(P->m > 0); xfprintf(fp, "%*s%-*s", csa->deck ? 2 : 1, "", csa->deck ? 8 : 1, row_name(csa, 1)); xfprintf(fp, "%*s0%*s$ empty column\n", csa->deck ? 13 : 1, "", csa->deck ? 3 : 1, ""), recno++; } #endif count = 0; for (aij = aij; aij != NULL; aij = aij->c_next) { if (one_col || count % 2 == 0) xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", csa->deck ? 8 : 1, col_name(csa, j)); gap = (one_col || count % 2 == 0 ? 2 : 3); xfprintf(fp, "%*s%-*s", csa->deck ? gap : 1, "", csa->deck ? 8 : 1, row_name(csa, aij->row == NULL ? 0 : aij->row->i)); xfprintf(fp, "%*s%*s", csa->deck ? 2 : 1, "", csa->deck ? 12 : 1, mps_numb(csa, aij->val)), count++; if (one_col || count % 2 == 0) xfprintf(fp, "\n"), recno++; } if (!(one_col || count % 2 == 0)) xfprintf(fp, "\n"), recno++; } if (marker % 2 == 1) { /* close last integer block */ marker++; xfprintf(fp, "%*sM%07d%*s'MARKER'%*s'INTEND'\n", csa->deck ? 4 : 1, "", marker, csa->deck ? 2 : 1, "", csa->deck ? 17 : 1, ""), recno++; } #if 1 if (empty > 0) xprintf("Warning: problem has %d empty column(s)\n", empty); #endif /* write RHS section */ xfprintf(fp, "RHS\n"), recno++; count = 0; for (i = (out_obj ? 0 : 1); i <= P->m; i++) { int type; double rhs; if (i == 0) rhs = P->c0; else { type = P->row[i]->type; if (type == GLP_FR) rhs = 0.0; else if (type == GLP_LO) rhs = P->row[i]->lb; else if (type == GLP_UP) rhs = P->row[i]->ub; else if (type == GLP_DB || type == GLP_FX) rhs = P->row[i]->lb; else xassert(type != type); } if (rhs != 0.0) { if (one_col || count % 2 == 0) xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", csa->deck ? 8 : 1, "RHS1"); gap = (one_col || count % 2 == 0 ? 2 : 3); xfprintf(fp, "%*s%-*s", csa->deck ? gap : 1, "", csa->deck ? 8 : 1, row_name(csa, i)); xfprintf(fp, "%*s%*s", csa->deck ? 2 : 1, "", csa->deck ? 12 : 1, mps_numb(csa, rhs)), count++; if (one_col || count % 2 == 0) xfprintf(fp, "\n"), recno++; } } if (!(one_col || count % 2 == 0)) xfprintf(fp, "\n"), recno++; /* write RANGES section */ for (i = P->m; i >= 1; i--) if (P->row[i]->type == GLP_DB) break; if (i == 0) goto bnds; xfprintf(fp, "RANGES\n"), recno++; count = 0; for (i = 1; i <= P->m; i++) { if (P->row[i]->type == GLP_DB) { if (one_col || count % 2 == 0) xfprintf(fp, "%*s%-*s", csa->deck ? 4 : 1, "", csa->deck ? 8 : 1, "RNG1"); gap = (one_col || count % 2 == 0 ? 2 : 3); xfprintf(fp, "%*s%-*s", csa->deck ? gap : 1, "", csa->deck ? 8 : 1, row_name(csa, i)); xfprintf(fp, "%*s%*s", csa->deck ? 2 : 1, "", csa->deck ? 12 : 1, mps_numb(csa, P->row[i]->ub - P->row[i]->lb)), count++; if (one_col || count % 2 == 0) xfprintf(fp, "\n"), recno++; } } if (!(one_col || count % 2 == 0)) xfprintf(fp, "\n"), recno++; bnds: /* write BOUNDS section */ for (j = P->n; j >= 1; j--) if (!(P->col[j]->type == GLP_LO && P->col[j]->lb == 0.0)) break; if (j == 0) goto endt; xfprintf(fp, "BOUNDS\n"), recno++; for (j = 1; j <= P->n; j++) { int type, data[2]; double bnd[2]; char *spec[2]; spec[0] = spec[1] = NULL; type = P->col[j]->type; if (type == GLP_FR) spec[0] = "FR", data[0] = 0; else if (type == GLP_LO) { if (P->col[j]->lb != 0.0) spec[0] = "LO", data[0] = 1, bnd[0] = P->col[j]->lb; if (P->col[j]->kind == GLP_IV) spec[1] = "PL", data[1] = 0; } else if (type == GLP_UP) { spec[0] = "MI", data[0] = 0; spec[1] = "UP", data[1] = 1, bnd[1] = P->col[j]->ub; } else if (type == GLP_DB) { if (P->col[j]->lb != 0.0) spec[0] = "LO", data[0] = 1, bnd[0] = P->col[j]->lb; spec[1] = "UP", data[1] = 1, bnd[1] = P->col[j]->ub; } else if (type == GLP_FX) spec[0] = "FX", data[0] = 1, bnd[0] = P->col[j]->lb; else xassert(type != type); for (i = 0; i <= 1; i++) { if (spec[i] != NULL) { xfprintf(fp, " %s %-*s%*s%-*s", spec[i], csa->deck ? 8 : 1, "BND1", csa->deck ? 2 : 1, "", csa->deck ? 8 : 1, col_name(csa, j)); if (data[i]) xfprintf(fp, "%*s%*s", csa->deck ? 2 : 1, "", csa->deck ? 12 : 1, mps_numb(csa, bnd[i])); xfprintf(fp, "\n"), recno++; } } } endt: /* write ENDATA indicator record */ xfprintf(fp, "ENDATA\n"), recno++; xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* problem data has been successfully written */ xprintf("%d records were written\n", recno); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /* eof */ igraph/src/amd_aat.c0000644000176000001440000001346712325527072014115 0ustar ripleyusers/* ========================================================================= */ /* === AMD_aat ============================================================= */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* AMD_aat: compute the symmetry of the pattern of A, and count the number of * nonzeros each column of A+A' (excluding the diagonal). Assumes the input * matrix has no errors, with sorted columns and no duplicates * (AMD_valid (n, n, Ap, Ai) must be AMD_OK, but this condition is not * checked). */ #pragma clang diagnostic ignored "-Wsign-conversion" #include "amd_internal.h" GLOBAL size_t AMD_aat /* returns nz in A+A' */ ( Int n, const Int Ap [ ], const Int Ai [ ], Int Len [ ], /* Len [j]: length of column j of A+A', excl diagonal*/ Int Tp [ ], /* workspace of size n */ double Info [ ] ) { Int p1, p2, p, i, j, pj, pj2, k, nzdiag, nzboth, nz ; double sym ; size_t nzaat ; #ifndef NDEBUG AMD_debug_init ("AMD AAT") ; for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ; ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; #endif if (Info != (double *) NULL) { /* clear the Info array, if it exists */ for (i = 0 ; i < AMD_INFO ; i++) { Info [i] = EMPTY ; } Info [AMD_STATUS] = AMD_OK ; } for (k = 0 ; k < n ; k++) { Len [k] = 0 ; } nzdiag = 0 ; nzboth = 0 ; nz = Ap [n] ; for (k = 0 ; k < n ; k++) { p1 = Ap [k] ; p2 = Ap [k+1] ; AMD_DEBUG2 (("\nAAT Column: "ID" p1: "ID" p2: "ID"\n", k, p1, p2)) ; /* construct A+A' */ for (p = p1 ; p < p2 ; ) { /* scan the upper triangular part of A */ j = Ai [p] ; if (j < k) { /* entry A (j,k) is in the strictly upper triangular part, * add both A (j,k) and A (k,j) to the matrix A+A' */ Len [j]++ ; Len [k]++ ; AMD_DEBUG3 ((" upper ("ID","ID") ("ID","ID")\n", j,k, k,j)); p++ ; } else if (j == k) { /* skip the diagonal */ p++ ; nzdiag++ ; break ; } else /* j > k */ { /* first entry below the diagonal */ break ; } /* scan lower triangular part of A, in column j until reaching * row k. Start where last scan left off. */ ASSERT (Tp [j] != EMPTY) ; ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; pj2 = Ap [j+1] ; for (pj = Tp [j] ; pj < pj2 ; ) { i = Ai [pj] ; if (i < k) { /* A (i,j) is only in the lower part, not in upper. * add both A (i,j) and A (j,i) to the matrix A+A' */ Len [i]++ ; Len [j]++ ; AMD_DEBUG3 ((" lower ("ID","ID") ("ID","ID")\n", i,j, j,i)) ; pj++ ; } else if (i == k) { /* entry A (k,j) in lower part and A (j,k) in upper */ pj++ ; nzboth++ ; break ; } else /* i > k */ { /* consider this entry later, when k advances to i */ break ; } } Tp [j] = pj ; } /* Tp [k] points to the entry just below the diagonal in column k */ Tp [k] = p ; } /* clean up, for remaining mismatched entries */ for (j = 0 ; j < n ; j++) { for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) { i = Ai [pj] ; /* A (i,j) is only in the lower part, not in upper. * add both A (i,j) and A (j,i) to the matrix A+A' */ Len [i]++ ; Len [j]++ ; AMD_DEBUG3 ((" lower cleanup ("ID","ID") ("ID","ID")\n", i,j, j,i)) ; } } /* --------------------------------------------------------------------- */ /* compute the symmetry of the nonzero pattern of A */ /* --------------------------------------------------------------------- */ /* Given a matrix A, the symmetry of A is: * B = tril (spones (A), -1) + triu (spones (A), 1) ; * sym = nnz (B & B') / nnz (B) ; * or 1 if nnz (B) is zero. */ if (nz == nzdiag) { sym = 1 ; } else { sym = (2 * (double) nzboth) / ((double) (nz - nzdiag)) ; } nzaat = 0 ; for (k = 0 ; k < n ; k++) { nzaat += Len [k] ; } AMD_DEBUG1 (("AMD nz in A+A', excluding diagonal (nzaat) = %g\n", (double) nzaat)) ; AMD_DEBUG1 ((" nzboth: "ID" nz: "ID" nzdiag: "ID" symmetry: %g\n", nzboth, nz, nzdiag, sym)) ; if (Info != (double *) NULL) { Info [AMD_STATUS] = AMD_OK ; Info [AMD_N] = n ; Info [AMD_NZ] = nz ; Info [AMD_SYMMETRY] = sym ; /* symmetry of pattern of A */ Info [AMD_NZDIAG] = nzdiag ; /* nonzeros on diagonal of A */ Info [AMD_NZ_A_PLUS_AT] = nzaat ; /* nonzeros in A+A' */ } return (nzaat) ; } igraph/src/igraph_revolver.h0000644000176000001440000011766012325527073015733 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_REVOLVER_H #define IGRAPH_REVOLVER_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_adjlist.h" #include "igraph_matrix.h" #include "igraph_array.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Network evolution measurement, new implementation */ /* -------------------------------------------------- */ int igraph_evolver_d(igraph_t *graph, igraph_integer_t nodes, igraph_vector_t *kernel, const igraph_vector_t *outseq, const igraph_vector_t *outdist, igraph_integer_t m, igraph_bool_t directed); int igraph_revolver_d(const igraph_t *graph, igraph_integer_t niter, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, igraph_vector_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_d(const igraph_t *graph, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t pmaxind); int igraph_revolver_st_d(const igraph_t *graph, igraph_vector_t *st, const igraph_vector_t *kernel); int igraph_revolver_exp_d(const igraph_t *graphm, igraph_vector_t *expected, const igraph_vector_t *kernel, const igraph_vector_t *st, igraph_integer_t pmaxind); int igraph_revolver_error_d(const igraph_t *graph, const igraph_vector_t *kernel, const igraph_vector_t *st, igraph_integer_t maxind, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_d(const igraph_t *graph, const igraph_vector_t *kernel, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_ad(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t agebins, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_ad(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t pmaxind, igraph_integer_t agebins); int igraph_revolver_st_ad(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel); int igraph_revolver_exp_ad(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pmaxind, igraph_integer_t agebins); int igraph_revolver_error_ad(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pmaxind, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_ad(const igraph_t *graph, const igraph_matrix_t *kernel, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_ade(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t agebins, const igraph_vector_t *cats, igraph_array3_t *kernel, igraph_array3_t *sd, igraph_array3_t *norm, igraph_array3_t *cites, igraph_array3_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_ade(const igraph_t *graph, igraph_array3_t *kernel, igraph_array3_t *sd, igraph_array3_t *norm, igraph_array3_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebind); int igraph_revolver_st_ade(const igraph_t *graph, igraph_vector_t *st, const igraph_array3_t *kernel, const igraph_vector_t *cats); int igraph_revolver_exp_ade(const igraph_t *graph, igraph_array3_t *expected, const igraph_array3_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t nocats, igraph_integer_t maxdegree, igraph_integer_t agebins); int igraph_revolver_error_ade(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxdegree, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_ade(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *cats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_e(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *cats, igraph_vector_t *kernel, igraph_vector_t *st, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, igraph_vector_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_e(const igraph_t *graph, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats); int igraph_revolver_st_e(const igraph_t *graph, igraph_vector_t *st, const igraph_vector_t *kernel, const igraph_vector_t *cats); int igraph_revolver_exp_e(const igraph_t *graph, igraph_vector_t *expected, const igraph_vector_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats); int igraph_revolver_error_e(const igraph_t *graph, const igraph_vector_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_e(const igraph_t *graph, const igraph_vector_t *kernel, const igraph_vector_t *cats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_de(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *cats, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_de(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind); int igraph_revolver_st_de(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel, const igraph_vector_t *cats); int igraph_revolver_exp_de(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind); int igraph_revolver_error_de(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_de(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_l(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t agebins, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, igraph_vector_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_l(const igraph_t *graph, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t pagebins); int igraph_revolver_st_l(const igraph_t *graph, igraph_vector_t *st, const igraph_vector_t *kernel); int igraph_revolver_exp_l(const igraph_t *graph, igraph_vector_t *expected, const igraph_vector_t *kernel, const igraph_vector_t *st, igraph_integer_t pagebins); int igraph_revolver_error_l(const igraph_t *graph, const igraph_vector_t *kernel, const igraph_vector_t *st, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_l(const igraph_t *graph, const igraph_vector_t *kernel, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_dl(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t agebins, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_dl(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t pmaxind, igraph_integer_t pagebins); int igraph_revolver_st_dl(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel); int igraph_revolver_exp_dl(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pmaxind, igraph_integer_t pagebins); int igraph_revolver_error_dl(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pagebins, igraph_integer_t pmaxind, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_dl(const igraph_t *graph, const igraph_matrix_t *kernel, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_el(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *cats, igraph_integer_t agebins, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_el(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pagebins); int igraph_revolver_st_el(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel, const igraph_vector_t *cats); int igraph_revolver_exp_el(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pagebins); int igraph_revolver_error_el(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_el(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_r(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t window, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, igraph_vector_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_r(const igraph_t *graph, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t window, igraph_integer_t maxind); int igraph_revolver_st_r(const igraph_t *graph, igraph_vector_t *st, const igraph_vector_t *kernel, igraph_integer_t window); int igraph_revolver_exp_r(const igraph_t *graph, igraph_vector_t *expected, const igraph_vector_t *kernel, const igraph_vector_t *st, igraph_integer_t window, igraph_integer_t pmaxind); int igraph_revolver_error_r(const igraph_t *graph, const igraph_vector_t *kernel, const igraph_vector_t *st, igraph_integer_t window, igraph_integer_t maxind, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_r(const igraph_t *graph, const igraph_vector_t *kernel, igraph_integer_t window, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_ar(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t agebins, igraph_integer_t window, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_ar(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t pagebins, igraph_integer_t pwindow, igraph_integer_t maxind); int igraph_revolver_st_ar(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel, igraph_integer_t pwindow); int igraph_revolver_exp_ar(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t agebins, igraph_integer_t window, igraph_integer_t pmaxind); int igraph_revolver_error_ar(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pagebins, igraph_integer_t pwindow, igraph_integer_t maxind, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_ar(const igraph_t *graph, const igraph_matrix_t *kernel, igraph_integer_t window, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_di(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *cats, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_di(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind); int igraph_revolver_st_di(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel, const igraph_vector_t *cats); int igraph_revolver_exp_di(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind); int igraph_revolver_error_di(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_di(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_adi(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t agebins, const igraph_vector_t *cats, igraph_array3_t *kernel, igraph_array3_t *sd, igraph_array3_t *norm, igraph_array3_t *cites, igraph_array3_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_adi(const igraph_t *graph, igraph_array3_t *kernel, igraph_array3_t *sd, igraph_array3_t *norm, igraph_array3_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebins); int igraph_revolver_st_adi(const igraph_t *graph, igraph_vector_t *st, const igraph_array3_t *kernel, const igraph_vector_t *cats); int igraph_revolver_exp_adi(const igraph_t *graph, igraph_array3_t *expected, const igraph_array3_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebins); int igraph_revolver_error_adi(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_adi(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *cats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_il(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t agebins, const igraph_vector_t *cats, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_il(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pagebins); int igraph_revolver_st_il(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel, const igraph_vector_t *cats); int igraph_revolver_exp_il(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t nocats, igraph_integer_t pagebins); int igraph_revolver_error_il(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t nocats, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_il(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_ir(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t window, const igraph_vector_t *cats, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_ir(const igraph_t *graph, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t pwindow, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind); int igraph_revolver_st_ir(const igraph_t *graph, igraph_vector_t *st, const igraph_matrix_t *kernel, igraph_integer_t pwindow, const igraph_vector_t *cats); int igraph_revolver_exp_ir(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pwindow, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind); int igraph_revolver_error_ir(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pwindow, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_ir(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_integer_t window, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_air(const igraph_t *graph, igraph_integer_t niter, igraph_integer_t window, igraph_integer_t agebins, const igraph_vector_t *cats, igraph_array3_t *kernel, igraph_array3_t *sd, igraph_array3_t *norm, igraph_array3_t *cites, igraph_array3_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_air(const igraph_t *graph, igraph_array3_t *kernel, igraph_array3_t *sd, igraph_array3_t *norm, igraph_array3_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, igraph_real_t *logmax, const igraph_vector_t *st, igraph_integer_t pwindow, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebins); int igraph_revolver_st_air(const igraph_t *graph, igraph_vector_t *st, const igraph_array3_t *kernel, igraph_integer_t pwindow, const igraph_vector_t *cats); int igraph_revolver_exp_air(const igraph_t *graph, igraph_array3_t *expected, const igraph_array3_t *kernel, const igraph_vector_t *st, igraph_integer_t pwindow, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebins); int igraph_revolver_error_air(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *st, igraph_integer_t pwindow, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_error2_air(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *cats, igraph_integer_t window, igraph_real_t *logprob, igraph_real_t *lognull); /* Non-citation networks */ int igraph_revolver_d_d(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *vtime, const igraph_vector_t *etime, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_d_d(const igraph_t *graph, igraph_lazy_inclist_t *inclist, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, const igraph_vector_t *st, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events, igraph_integer_t pmaxdegree); int igraph_revolver_st_d_d(const igraph_t *graph, igraph_lazy_inclist_t *inclist, igraph_vector_t *st, const igraph_matrix_t *kernel, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events); int igraph_revolver_exp_d_d(const igraph_t *graph, igraph_lazy_inclist_t *inclist, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events, igraph_integer_t pmaxdegree); int igraph_revolver_error_d_d(const igraph_t *graph, igraph_lazy_inclist_t *inclist, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events, igraph_integer_t pmaxdegree, igraph_real_t *logprob, igraph_real_t *lognull); int igraph_revolver_p_p(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *vtime, const igraph_vector_t *etime, const igraph_vector_t *authors, const igraph_vector_t *eventsizes, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres); int igraph_revolver_mes_p_p(const igraph_t *graph, igraph_lazy_inclist_t *inclist, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres, const igraph_vector_t *st, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events, const igraph_vector_t *authors, const igraph_vector_t *eventsizes, igraph_integer_t pmaxpapers); int igraph_revolver_st_p_p(const igraph_t *graph, igraph_lazy_inclist_t *inclist, igraph_vector_t *st, const igraph_matrix_t *kernel, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events, const igraph_vector_t *authors, const igraph_vector_t *eventsizes, igraph_integer_t pmaxpapers); int igraph_revolver_exp_p_p(const igraph_t *graph, igraph_lazy_inclist_t *inclist, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events, const igraph_vector_t *authors, const igraph_vector_t *eventsizes, igraph_integer_t pmaxpapers); int igraph_revolver_error_p_p(const igraph_t *graph, igraph_lazy_inclist_t *inclist, const igraph_matrix_t *kernel, const igraph_vector_t *st, const igraph_vector_t *vtime, const igraph_vector_t *vtimeidx, const igraph_vector_t *etime, const igraph_vector_t *etimeidx, igraph_integer_t pno_of_events, const igraph_vector_t *authors, const igraph_vector_t *eventsizes, igraph_integer_t pmaxpapers, igraph_real_t *logprob, igraph_real_t *lognull); /* -------------------------------------------------- */ /* Maximum likelihood revolver */ /* -------------------------------------------------- */ int igraph_revolver_ml_d(const igraph_t *graph, igraph_integer_t niter, igraph_vector_t *kernel, igraph_vector_t *cites, igraph_real_t delta, const igraph_vector_t *filter, igraph_real_t *logprob, igraph_real_t *logmax); int igraph_revolver_probs_d(const igraph_t *graph, const igraph_vector_t *kernel, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting, igraph_bool_t ntk); int igraph_revolver_ml_de(const igraph_t *graph, igraph_integer_t niter, igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_matrix_t *cites, igraph_real_t delta, const igraph_vector_t *filter, igraph_real_t *logprob, igraph_real_t *logmax); int igraph_revolver_probs_de(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting); int igraph_revolver_ml_ade(const igraph_t *graph, igraph_integer_t niter, igraph_array3_t *kernel, const igraph_vector_t *cats, igraph_array3_t *cites, igraph_integer_t pagebins, igraph_real_t delta, const igraph_vector_t *filter, igraph_real_t *logprob, igraph_real_t *logmax); int igraph_revolver_probs_ade(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *cats, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting); int igraph_revolver_ml_f(const igraph_t *graph, igraph_integer_t niter, igraph_vector_t *kernel, igraph_vector_t *cites, igraph_real_t delta, igraph_real_t *logprob, igraph_real_t *logmax); int igraph_revolver_ml_df(const igraph_t *graph, igraph_integer_t niter, igraph_matrix_t *kernel, igraph_matrix_t *cites, igraph_real_t delta, igraph_real_t *logprob, igraph_real_t *logmax); int igraph_revolver_ml_l(const igraph_t *graph, igraph_integer_t niter, igraph_vector_t *kernel, igraph_vector_t *cites, igraph_integer_t pagebins, igraph_real_t delta, igraph_real_t *logprob, igraph_real_t *logmax); int igraph_revolver_ml_ad(const igraph_t *graph, igraph_integer_t niter, igraph_matrix_t *kernel, igraph_matrix_t *cites, igraph_integer_t pagebins, igraph_real_t delta, const igraph_vector_t *filter, igraph_real_t *logprob, igraph_real_t *logmax); int igraph_revolver_probs_ad(const igraph_t *graph, const igraph_matrix_t *kernel, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting, igraph_bool_t ntk); int igraph_revolver_ml_D(const igraph_t *graph, igraph_vector_t *res, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, igraph_scalar_function_t *A_fun, igraph_vector_function_t *dA_fun, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_D_alpha(const igraph_t *graph, igraph_real_t *alpha, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_D_alpha_a(const igraph_t *graph, igraph_real_t *alpha, igraph_real_t *a, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_DE(const igraph_t *graph, const igraph_vector_t *cats, igraph_vector_t *res, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, igraph_scalar_function_t *A_fun, igraph_vector_function_t *dA_fun, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount, igraph_vector_t *lastderiv); int igraph_revolver_ml_DE_alpha_a(const igraph_t *graph, const igraph_vector_t *cats, igraph_real_t *alpha, igraph_real_t *a, igraph_vector_t *coeffs, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_AD(const igraph_t *graph, igraph_vector_t *res, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, igraph_scalar_function_t *A_fun, igraph_vector_function_t *dA_fun, int agebins, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount, igraph_vector_t *lastderiv); int igraph_revolver_ml_AD_alpha_a_beta(const igraph_t *graph, igraph_real_t *alpha, igraph_real_t *a, igraph_real_t *beta, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, int agebins, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_AD_dpareto(const igraph_t *graph, igraph_real_t *alpha, igraph_real_t *a, igraph_real_t *paralpha, igraph_real_t *parbeta, igraph_real_t *parscale, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, int agebins, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_AD_dpareto_eval(const igraph_t *graph, igraph_real_t alpha, igraph_real_t a, igraph_real_t paralpha, igraph_real_t parbeta, igraph_real_t parscale, igraph_real_t *value, igraph_vector_t *deriv, int agebins, const igraph_vector_t *filter); int igraph_revolver_ml_ADE(const igraph_t *graph, const igraph_vector_t *cats, igraph_vector_t *res, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, igraph_scalar_function_t *A_fun, igraph_vector_function_t *dA_fun, int agebins, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount, igraph_vector_t *lastderiv); int igraph_revolver_probs_ADE(const igraph_t *graph, igraph_scalar_function_t *A_fun, const igraph_matrix_t *par, const igraph_vector_t *cats, const igraph_vector_t *gcats, int agebins, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting); int igraph_revolver_ml_ADE_alpha_a_beta(const igraph_t *graph, const igraph_vector_t *cats, igraph_real_t *alpha, igraph_real_t *a, igraph_real_t *beta, igraph_vector_t *coeffs, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, int agebins, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_ADE_dpareto(const igraph_t *graph, const igraph_vector_t *cats, igraph_real_t *alpha, igraph_real_t *a, igraph_real_t *paralpha, igraph_real_t *parbeta, igraph_real_t *parscale, igraph_vector_t *coeffs, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, int agebins, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_revolver_ml_ADE_dpareto_eval(const igraph_t *graph, const igraph_vector_t *cats, igraph_real_t alpha, igraph_real_t a, igraph_real_t paralpha, igraph_real_t parbeta, igraph_real_t parscale, const igraph_vector_t *coeffs, igraph_real_t *value, igraph_vector_t *deriv, int agebins, const igraph_vector_t *filter); int igraph_revolver_ml_ADE_dpareto_evalf(const igraph_t *graph, const igraph_vector_t *cats, const igraph_matrix_t *par, igraph_vector_t *value, int agebins, const igraph_vector_t *filter); int igraph_revolver_probs_ADE_dpareto(const igraph_t *graph, const igraph_matrix_t *par, const igraph_vector_t *cats, const igraph_vector_t *gcats, int agebins, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting); __END_DECLS #endif igraph/src/glpspm.c0000644000176000001440000006047312325527073014031 0ustar ripleyusers/* glpspm.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glphbm.h" #include "glprgr.h" #include "glpspm.h" /*********************************************************************** * NAME * * spm_create_mat - create general sparse matrix * * SYNOPSIS * * #include "glpspm.h" * SPM *spm_create_mat(int m, int n); * * DESCRIPTION * * The routine spm_create_mat creates a general sparse matrix having * m rows and n columns. Being created the matrix is zero (empty), i.e. * has no elements. * * RETURNS * * The routine returns a pointer to the matrix created. */ SPM *spm_create_mat(int m, int n) { SPM *A; xassert(0 <= m && m < INT_MAX); xassert(0 <= n && n < INT_MAX); A = xmalloc(sizeof(SPM)); A->m = m; A->n = n; if (m == 0 || n == 0) { A->pool = NULL; A->row = NULL; A->col = NULL; } else { int i, j; A->pool = dmp_create_pool(); A->row = xcalloc(1+m, sizeof(SPME *)); for (i = 1; i <= m; i++) A->row[i] = NULL; A->col = xcalloc(1+n, sizeof(SPME *)); for (j = 1; j <= n; j++) A->col[j] = NULL; } return A; } /*********************************************************************** * NAME * * spm_new_elem - add new element to sparse matrix * * SYNOPSIS * * #include "glpspm.h" * SPME *spm_new_elem(SPM *A, int i, int j, double val); * * DESCRIPTION * * The routine spm_new_elem adds a new element to the specified sparse * matrix. Parameters i, j, and val specify the row number, the column * number, and a numerical value of the element, respectively. * * RETURNS * * The routine returns a pointer to the new element added. */ SPME *spm_new_elem(SPM *A, int i, int j, double val) { SPME *e; xassert(1 <= i && i <= A->m); xassert(1 <= j && j <= A->n); e = dmp_get_atom(A->pool, sizeof(SPME)); e->i = i; e->j = j; e->val = val; e->r_prev = NULL; e->r_next = A->row[i]; if (e->r_next != NULL) e->r_next->r_prev = e; e->c_prev = NULL; e->c_next = A->col[j]; if (e->c_next != NULL) e->c_next->c_prev = e; A->row[i] = A->col[j] = e; return e; } /*********************************************************************** * NAME * * spm_delete_mat - delete general sparse matrix * * SYNOPSIS * * #include "glpspm.h" * void spm_delete_mat(SPM *A); * * DESCRIPTION * * The routine deletes the specified general sparse matrix freeing all * the memory allocated to this object. */ void spm_delete_mat(SPM *A) { /* delete sparse matrix */ if (A->pool != NULL) dmp_delete_pool(A->pool); if (A->row != NULL) xfree(A->row); if (A->col != NULL) xfree(A->col); xfree(A); return; } /*********************************************************************** * NAME * * spm_test_mat_e - create test sparse matrix of E(n,c) class * * SYNOPSIS * * #include "glpspm.h" * SPM *spm_test_mat_e(int n, int c); * * DESCRIPTION * * The routine spm_test_mat_e creates a test sparse matrix of E(n,c) * class as described in the book: Ole 0sterby, Zahari Zlatev. Direct * Methods for Sparse Matrices. Springer-Verlag, 1983. * * Matrix of E(n,c) class is a symmetric positive definite matrix of * the order n. It has the number 4 on its main diagonal and the number * -1 on its four co-diagonals, two of which are neighbour to the main * diagonal and two others are shifted from the main diagonal on the * distance c. * * It is necessary that n >= 3 and 2 <= c <= n-1. * * RETURNS * * The routine returns a pointer to the matrix created. */ SPM *spm_test_mat_e(int n, int c) { SPM *A; int i; xassert(n >= 3 && 2 <= c && c <= n-1); A = spm_create_mat(n, n); for (i = 1; i <= n; i++) spm_new_elem(A, i, i, 4.0); for (i = 1; i <= n-1; i++) { spm_new_elem(A, i, i+1, -1.0); spm_new_elem(A, i+1, i, -1.0); } for (i = 1; i <= n-c; i++) { spm_new_elem(A, i, i+c, -1.0); spm_new_elem(A, i+c, i, -1.0); } return A; } /*********************************************************************** * NAME * * spm_test_mat_d - create test sparse matrix of D(n,c) class * * SYNOPSIS * * #include "glpspm.h" * SPM *spm_test_mat_d(int n, int c); * * DESCRIPTION * * The routine spm_test_mat_d creates a test sparse matrix of D(n,c) * class as described in the book: Ole 0sterby, Zahari Zlatev. Direct * Methods for Sparse Matrices. Springer-Verlag, 1983. * * Matrix of D(n,c) class is a non-singular matrix of the order n. It * has unity main diagonal, three co-diagonals above the main diagonal * on the distance c, which are cyclically continued below the main * diagonal, and a triangle block of the size 10x10 in the upper right * corner. * * It is necessary that n >= 14 and 1 <= c <= n-13. * * RETURNS * * The routine returns a pointer to the matrix created. */ SPM *spm_test_mat_d(int n, int c) { SPM *A; int i, j; xassert(n >= 14 && 1 <= c && c <= n-13); A = spm_create_mat(n, n); for (i = 1; i <= n; i++) spm_new_elem(A, i, i, 1.0); for (i = 1; i <= n-c; i++) spm_new_elem(A, i, i+c, (double)(i+1)); for (i = n-c+1; i <= n; i++) spm_new_elem(A, i, i-n+c, (double)(i+1)); for (i = 1; i <= n-c-1; i++) spm_new_elem(A, i, i+c+1, (double)(-i)); for (i = n-c; i <= n; i++) spm_new_elem(A, i, i-n+c+1, (double)(-i)); for (i = 1; i <= n-c-2; i++) spm_new_elem(A, i, i+c+2, 16.0); for (i = n-c-1; i <= n; i++) spm_new_elem(A, i, i-n+c+2, 16.0); for (j = 1; j <= 10; j++) for (i = 1; i <= 11-j; i++) spm_new_elem(A, i, n-11+i+j, 100.0 * (double)j); return A; } /*********************************************************************** * NAME * * spm_show_mat - write sparse matrix pattern in BMP file format * * SYNOPSIS * * #include "glpspm.h" * int spm_show_mat(const SPM *A, const char *fname); * * DESCRIPTION * * The routine spm_show_mat writes pattern of the specified sparse * matrix in uncompressed BMP file format (Windows bitmap) to a binary * file whose name is specified by the character string fname. * * Each pixel corresponds to one matrix element. The pixel colors have * the following meaning: * * Black structurally zero element * White positive element * Cyan negative element * Green zero element * Red duplicate element * * RETURNS * * If no error occured, the routine returns zero. Otherwise, it prints * an appropriate error message and returns non-zero. */ int spm_show_mat(const SPM *A, const char *fname) { int m = A->m; int n = A->n; int i, j, k, ret; char *map; xprintf("spm_show_mat: writing matrix pattern to `%s'...\n", fname); xassert(1 <= m && m <= 32767); xassert(1 <= n && n <= 32767); map = xmalloc(m * n); memset(map, 0x08, m * n); for (i = 1; i <= m; i++) { SPME *e; for (e = A->row[i]; e != NULL; e = e->r_next) { j = e->j; xassert(1 <= j && j <= n); k = n * (i - 1) + (j - 1); if (map[k] != 0x08) map[k] = 0x0C; else if (e->val > 0.0) map[k] = 0x0F; else if (e->val < 0.0) map[k] = 0x0B; else map[k] = 0x0A; } } ret = rgr_write_bmp16(fname, m, n, map); xfree(map); return ret; } /*********************************************************************** * NAME * * spm_read_hbm - read sparse matrix in Harwell-Boeing format * * SYNOPSIS * * #include "glpspm.h" * SPM *spm_read_hbm(const char *fname); * * DESCRIPTION * * The routine spm_read_hbm reads a sparse matrix in the Harwell-Boeing * format from a text file whose name is the character string fname. * * Detailed description of the Harwell-Boeing format recognised by this * routine can be found in the following report: * * I.S.Duff, R.G.Grimes, J.G.Lewis. User's Guide for the Harwell-Boeing * Sparse Matrix Collection (Release I), TR/PA/92/86, October 1992. * * NOTE * * The routine spm_read_hbm reads the matrix "as is", due to which zero * and/or duplicate elements can appear in the matrix. * * RETURNS * * If no error occured, the routine returns a pointer to the matrix * created. Otherwise, the routine prints an appropriate error message * and returns NULL. */ SPM *spm_read_hbm(const char *fname) { SPM *A = NULL; HBM *hbm; int nrow, ncol, nnzero, i, j, beg, end, ptr, *colptr, *rowind; double val, *values; char *mxtype; hbm = hbm_read_mat(fname); if (hbm == NULL) { xprintf("spm_read_hbm: unable to read matrix\n"); goto fini; } mxtype = hbm->mxtype; nrow = hbm->nrow; ncol = hbm->ncol; nnzero = hbm->nnzero; colptr = hbm->colptr; rowind = hbm->rowind; values = hbm->values; if (!(strcmp(mxtype, "RSA") == 0 || strcmp(mxtype, "PSA") == 0 || strcmp(mxtype, "RUA") == 0 || strcmp(mxtype, "PUA") == 0 || strcmp(mxtype, "RRA") == 0 || strcmp(mxtype, "PRA") == 0)) { xprintf("spm_read_hbm: matrix type `%s' not supported\n", mxtype); goto fini; } A = spm_create_mat(nrow, ncol); if (mxtype[1] == 'S' || mxtype[1] == 'U') xassert(nrow == ncol); for (j = 1; j <= ncol; j++) { beg = colptr[j]; end = colptr[j+1]; xassert(1 <= beg && beg <= end && end <= nnzero + 1); for (ptr = beg; ptr < end; ptr++) { i = rowind[ptr]; xassert(1 <= i && i <= nrow); if (mxtype[0] == 'R') val = values[ptr]; else val = 1.0; spm_new_elem(A, i, j, val); if (mxtype[1] == 'S' && i != j) spm_new_elem(A, j, i, val); } } fini: if (hbm != NULL) hbm_free_mat(hbm); return A; } /*********************************************************************** * NAME * * spm_count_nnz - determine number of non-zeros in sparse matrix * * SYNOPSIS * * #include "glpspm.h" * int spm_count_nnz(const SPM *A); * * RETURNS * * The routine spm_count_nnz returns the number of structural non-zero * elements in the specified sparse matrix. */ int spm_count_nnz(const SPM *A) { SPME *e; int i, nnz = 0; for (i = 1; i <= A->m; i++) for (e = A->row[i]; e != NULL; e = e->r_next) nnz++; return nnz; } /*********************************************************************** * NAME * * spm_drop_zeros - remove zero elements from sparse matrix * * SYNOPSIS * * #include "glpspm.h" * int spm_drop_zeros(SPM *A, double eps); * * DESCRIPTION * * The routine spm_drop_zeros removes all elements from the specified * sparse matrix, whose absolute value is less than eps. * * If the parameter eps is 0, only zero elements are removed from the * matrix. * * RETURNS * * The routine returns the number of elements removed. */ int spm_drop_zeros(SPM *A, double eps) { SPME *e, *next; int i, count = 0; for (i = 1; i <= A->m; i++) { for (e = A->row[i]; e != NULL; e = next) { next = e->r_next; if (e->val == 0.0 || fabs(e->val) < eps) { /* remove element from the row list */ if (e->r_prev == NULL) A->row[e->i] = e->r_next; else e->r_prev->r_next = e->r_next; if (e->r_next == NULL) ; else e->r_next->r_prev = e->r_prev; /* remove element from the column list */ if (e->c_prev == NULL) A->col[e->j] = e->c_next; else e->c_prev->c_next = e->c_next; if (e->c_next == NULL) ; else e->c_next->c_prev = e->c_prev; /* return element to the memory pool */ dmp_free_atom(A->pool, e, sizeof(SPME)); count++; } } } return count; } /*********************************************************************** * NAME * * spm_read_mat - read sparse matrix from text file * * SYNOPSIS * * #include "glpspm.h" * SPM *spm_read_mat(const char *fname); * * DESCRIPTION * * The routine reads a sparse matrix from a text file whose name is * specified by the parameter fname. * * For the file format see description of the routine spm_write_mat. * * RETURNS * * On success the routine returns a pointer to the matrix created, * otherwise NULL. */ #if 1 SPM *spm_read_mat(const char *fname) { xassert(fname != fname); return NULL; } #else SPM *spm_read_mat(const char *fname) { SPM *A = NULL; PDS *pds; jmp_buf jump; int i, j, k, m, n, nnz, fail = 0; double val; xprintf("spm_read_mat: reading matrix from `%s'...\n", fname); pds = pds_open_file(fname); if (pds == NULL) { xprintf("spm_read_mat: unable to open `%s' - %s\n", fname, strerror(errno)); fail = 1; goto done; } if (setjmp(jump)) { fail = 1; goto done; } pds_set_jump(pds, jump); /* number of rows, number of columns, number of non-zeros */ m = pds_scan_int(pds); if (m < 0) pds_error(pds, "invalid number of rows\n"); n = pds_scan_int(pds); if (n < 0) pds_error(pds, "invalid number of columns\n"); nnz = pds_scan_int(pds); if (nnz < 0) pds_error(pds, "invalid number of non-zeros\n"); /* create matrix */ xprintf("spm_read_mat: %d rows, %d columns, %d non-zeros\n", m, n, nnz); A = spm_create_mat(m, n); /* read matrix elements */ for (k = 1; k <= nnz; k++) { /* row index, column index, element value */ i = pds_scan_int(pds); if (!(1 <= i && i <= m)) pds_error(pds, "row index out of range\n"); j = pds_scan_int(pds); if (!(1 <= j && j <= n)) pds_error(pds, "column index out of range\n"); val = pds_scan_num(pds); /* add new element to the matrix */ spm_new_elem(A, i, j, val); } xprintf("spm_read_mat: %d lines were read\n", pds->count); done: if (pds != NULL) pds_close_file(pds); if (fail && A != NULL) spm_delete_mat(A), A = NULL; return A; } #endif /*********************************************************************** * NAME * * spm_write_mat - write sparse matrix to text file * * SYNOPSIS * * #include "glpspm.h" * int spm_write_mat(const SPM *A, const char *fname); * * DESCRIPTION * * The routine spm_write_mat writes the specified sparse matrix to a * text file whose name is specified by the parameter fname. This file * can be read back with the routine spm_read_mat. * * RETURNS * * On success the routine returns zero, otherwise non-zero. * * FILE FORMAT * * The file created by the routine spm_write_mat is a plain text file, * which contains the following information: * * m n nnz * row[1] col[1] val[1] * row[2] col[2] val[2] * . . . * row[nnz] col[nnz] val[nnz] * * where: * m is the number of rows; * n is the number of columns; * nnz is the number of non-zeros; * row[k], k = 1,...,nnz, are row indices; * col[k], k = 1,...,nnz, are column indices; * val[k], k = 1,...,nnz, are element values. */ #if 1 int spm_write_mat(const SPM *A, const char *fname) { xassert(A != A); xassert(fname != fname); return 0; } #else int spm_write_mat(const SPM *A, const char *fname) { FILE *fp; int i, nnz, ret = 0; xprintf("spm_write_mat: writing matrix to `%s'...\n", fname); fp = fopen(fname, "w"); if (fp == NULL) { xprintf("spm_write_mat: unable to create `%s' - %s\n", fname, strerror(errno)); ret = 1; goto done; } /* number of rows, number of columns, number of non-zeros */ nnz = spm_count_nnz(A); fprintf(fp, "%d %d %d\n", A->m, A->n, nnz); /* walk through rows of the matrix */ for (i = 1; i <= A->m; i++) { SPME *e; /* walk through elements of i-th row */ for (e = A->row[i]; e != NULL; e = e->r_next) { /* row index, column index, element value */ fprintf(fp, "%d %d %.*g\n", e->i, e->j, DBL_DIG, e->val); } } fflush(fp); if (ferror(fp)) { xprintf("spm_write_mat: writing error on `%s' - %s\n", fname, strerror(errno)); ret = 1; goto done; } xprintf("spm_write_mat: %d lines were written\n", 1 + nnz); done: if (fp != NULL) fclose(fp); return ret; } #endif /*********************************************************************** * NAME * * spm_transpose - transpose sparse matrix * * SYNOPSIS * * #include "glpspm.h" * SPM *spm_transpose(const SPM *A); * * RETURNS * * The routine computes and returns sparse matrix B, which is a matrix * transposed to sparse matrix A. */ SPM *spm_transpose(const SPM *A) { SPM *B; int i; B = spm_create_mat(A->n, A->m); for (i = 1; i <= A->m; i++) { SPME *e; for (e = A->row[i]; e != NULL; e = e->r_next) spm_new_elem(B, e->j, i, e->val); } return B; } SPM *spm_add_sym(const SPM *A, const SPM *B) { /* add two sparse matrices (symbolic phase) */ SPM *C; int i, j, *flag; xassert(A->m == B->m); xassert(A->n == B->n); /* create resultant matrix */ C = spm_create_mat(A->m, A->n); /* allocate and clear the flag array */ flag = xcalloc(1+C->n, sizeof(int)); for (j = 1; j <= C->n; j++) flag[j] = 0; /* compute pattern of C = A + B */ for (i = 1; i <= C->m; i++) { SPME *e; /* at the beginning i-th row of C is empty */ /* (i-th row of C) := (i-th row of C) union (i-th row of A) */ for (e = A->row[i]; e != NULL; e = e->r_next) { /* (note that i-th row of A may have duplicate elements) */ j = e->j; if (!flag[j]) { spm_new_elem(C, i, j, 0.0); flag[j] = 1; } } /* (i-th row of C) := (i-th row of C) union (i-th row of B) */ for (e = B->row[i]; e != NULL; e = e->r_next) { /* (note that i-th row of B may have duplicate elements) */ j = e->j; if (!flag[j]) { spm_new_elem(C, i, j, 0.0); flag[j] = 1; } } /* reset the flag array */ for (e = C->row[i]; e != NULL; e = e->r_next) flag[e->j] = 0; } /* check and deallocate the flag array */ for (j = 1; j <= C->n; j++) xassert(!flag[j]); xfree(flag); return C; } void spm_add_num(SPM *C, double alfa, const SPM *A, double beta, const SPM *B) { /* add two sparse matrices (numeric phase) */ int i, j; double *work; /* allocate and clear the working array */ work = xcalloc(1+C->n, sizeof(double)); for (j = 1; j <= C->n; j++) work[j] = 0.0; /* compute matrix C = alfa * A + beta * B */ for (i = 1; i <= C->n; i++) { SPME *e; /* work := alfa * (i-th row of A) + beta * (i-th row of B) */ /* (note that A and/or B may have duplicate elements) */ for (e = A->row[i]; e != NULL; e = e->r_next) work[e->j] += alfa * e->val; for (e = B->row[i]; e != NULL; e = e->r_next) work[e->j] += beta * e->val; /* (i-th row of C) := work, work := 0 */ for (e = C->row[i]; e != NULL; e = e->r_next) { j = e->j; e->val = work[j]; work[j] = 0.0; } } /* check and deallocate the working array */ for (j = 1; j <= C->n; j++) xassert(work[j] == 0.0); xfree(work); return; } SPM *spm_add_mat(double alfa, const SPM *A, double beta, const SPM *B) { /* add two sparse matrices (driver routine) */ SPM *C; C = spm_add_sym(A, B); spm_add_num(C, alfa, A, beta, B); return C; } SPM *spm_mul_sym(const SPM *A, const SPM *B) { /* multiply two sparse matrices (symbolic phase) */ int i, j, k, *flag; SPM *C; xassert(A->n == B->m); /* create resultant matrix */ C = spm_create_mat(A->m, B->n); /* allocate and clear the flag array */ flag = xcalloc(1+C->n, sizeof(int)); for (j = 1; j <= C->n; j++) flag[j] = 0; /* compute pattern of C = A * B */ for (i = 1; i <= C->m; i++) { SPME *e, *ee; /* compute pattern of i-th row of C */ for (e = A->row[i]; e != NULL; e = e->r_next) { k = e->j; for (ee = B->row[k]; ee != NULL; ee = ee->r_next) { j = ee->j; /* if a[i,k] != 0 and b[k,j] != 0 then c[i,j] != 0 */ if (!flag[j]) { /* c[i,j] does not exist, so create it */ spm_new_elem(C, i, j, 0.0); flag[j] = 1; } } } /* reset the flag array */ for (e = C->row[i]; e != NULL; e = e->r_next) flag[e->j] = 0; } /* check and deallocate the flag array */ for (j = 1; j <= C->n; j++) xassert(!flag[j]); xfree(flag); return C; } void spm_mul_num(SPM *C, const SPM *A, const SPM *B) { /* multiply two sparse matrices (numeric phase) */ int i, j; double *work; /* allocate and clear the working array */ work = xcalloc(1+A->n, sizeof(double)); for (j = 1; j <= A->n; j++) work[j] = 0.0; /* compute matrix C = A * B */ for (i = 1; i <= C->m; i++) { SPME *e, *ee; double temp; /* work := (i-th row of A) */ /* (note that A may have duplicate elements) */ for (e = A->row[i]; e != NULL; e = e->r_next) work[e->j] += e->val; /* compute i-th row of C */ for (e = C->row[i]; e != NULL; e = e->r_next) { j = e->j; /* c[i,j] := work * (j-th column of B) */ temp = 0.0; for (ee = B->col[j]; ee != NULL; ee = ee->c_next) temp += work[ee->i] * ee->val; e->val = temp; } /* reset the working array */ for (e = A->row[i]; e != NULL; e = e->r_next) work[e->j] = 0.0; } /* check and deallocate the working array */ for (j = 1; j <= A->n; j++) xassert(work[j] == 0.0); xfree(work); return; } SPM *spm_mul_mat(const SPM *A, const SPM *B) { /* multiply two sparse matrices (driver routine) */ SPM *C; C = spm_mul_sym(A, B); spm_mul_num(C, A, B); return C; } PER *spm_create_per(int n) { /* create permutation matrix */ PER *P; int k; xassert(n >= 0); P = xmalloc(sizeof(PER)); P->n = n; P->row = xcalloc(1+n, sizeof(int)); P->col = xcalloc(1+n, sizeof(int)); /* initially it is identity matrix */ for (k = 1; k <= n; k++) P->row[k] = P->col[k] = k; return P; } void spm_check_per(PER *P) { /* check permutation matrix for correctness */ int i, j; xassert(P->n >= 0); for (i = 1; i <= P->n; i++) { j = P->row[i]; xassert(1 <= j && j <= P->n); xassert(P->col[j] == i); } return; } void spm_delete_per(PER *P) { /* delete permutation matrix */ xfree(P->row); xfree(P->col); xfree(P); return; } /* eof */ igraph/src/glpk_support.c0000644000176000001440000000430012325527073015243 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #ifdef HAVE_GLPK #include "igraph_types.h" #include "igraph_error.h" #include "igraph_interrupt_internal.h" #include #include #include void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info) { IGRAPH_UNUSED(tree); IGRAPH_UNUSED(info); IGRAPH_ALLOW_INTERRUPTION_NORETURN(); } int igraph_i_glpk_check(int retval, const char* message) { char* code = "none"; char message_and_code[4096]; if (retval == IGRAPH_SUCCESS) return IGRAPH_SUCCESS; /* handle errors */ #define HANDLE_CODE(c) case c: code = #c; retval = IGRAPH_##c; break; #define HANDLE_CODE2(c) case c: code = #c; retval = IGRAPH_FAILURE; break; switch (retval) { HANDLE_CODE(GLP_EBOUND); HANDLE_CODE(GLP_EROOT); HANDLE_CODE(GLP_ENOPFS); HANDLE_CODE(GLP_ENODFS); HANDLE_CODE(GLP_EFAIL); HANDLE_CODE(GLP_EMIPGAP); HANDLE_CODE(GLP_ETMLIM); HANDLE_CODE(GLP_ESTOP); HANDLE_CODE2(GLP_EBADB); HANDLE_CODE2(GLP_ESING); HANDLE_CODE2(GLP_ECOND); HANDLE_CODE2(GLP_EOBJLL); HANDLE_CODE2(GLP_EOBJUL); HANDLE_CODE2(GLP_EITLIM); default: IGRAPH_ERROR("unknown GLPK error", IGRAPH_FAILURE); } #undef HANDLE_CODE sprintf(message_and_code, "%s (%s)", message, code); IGRAPH_ERROR(message_and_code, retval); } #endif igraph/src/igraph_blas_internal.h0000644000176000001440000000325112325527073016672 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef BLAS_INTERNAL_H #define BLAS_INTERNAL_H /* Note: only files calling the BLAS routines directly need to include this header. */ #include "igraph_types.h" #include "config.h" #ifndef INTERNAL_BLAS #define igraphdaxpy_ daxpy_ #define igraphdger_ dger_ #define igraphdcopy_ dcopy_ #define igraphdscal_ dscal_ #define igraphdswap_ dswap_ #define igraphdgemv_ dgemv_ #define igraphddot_ ddot_ #define igraphdnrm2_ dnrm2_ #define igraphlsame_ lsame_ #define igraphdrot_ drot_ #define igraphidamax_ idamax_ #define igraphdtrmm_ dtrmm_ #define igraphdasum_ dasum_ #endif int igraphdgemv_(char *trans, int *m, int *n, igraph_real_t *alpha, igraph_real_t *a, int *lda, igraph_real_t *x, int *incx, igraph_real_t *beta, igraph_real_t *y, int *incy); #endif igraph/src/igraph_psumtree.h0000644000176000001440000000342212325527073015721 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_PSUMTREE_H #define IGRAPH_PSUMTREE_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_vector.h" __BEGIN_DECLS typedef struct { igraph_vector_t v; long int size; long int offset; } igraph_psumtree_t; int igraph_psumtree_init(igraph_psumtree_t *t, long int size); void igraph_psumtree_destroy(igraph_psumtree_t *t); igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx); long int igraph_psumtree_size(const igraph_psumtree_t *t); int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, igraph_real_t elem); int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, igraph_real_t new_value); igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t); __END_DECLS #endif igraph/src/hrg_splittree_eq.h0000644000176000001440000001506512325527073016071 0ustar ripleyusers/* -*- mode: C++ -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // **************************************************************************************************** // *** COPYRIGHT NOTICE ******************************************************************************* // splittree_eq.h - a binary search tree data structure for storing dendrogram split frequencies // Copyright (C) 2006-2008 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // **************************************************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark E.J. Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute // Created : 19 April 2006 // Modified : 19 May 2007 // : 20 May 2008 (cleaned up for public consumption) // // *********************************************************************** // // Data structure for storing the split frequences in the sampled // dendrograms. Data is stored efficiently as a red-black binary // search tree (this is a modified version of the rbtree.h file). // // *********************************************************************** #ifndef IGRAPH_HRG_SPLITTREE #define IGRAPH_HRG_SPLITTREE #include using namespace std; namespace fitHRG { // ******** Basic Structures ********************************************* #ifndef IGRAPH_HRG_SLIST #define IGRAPH_HRG_SLIST class slist { public: string x; // stored elementd in linked-list slist* next; // pointer to next elementd slist(): x(""), next(0) { } ~slist() { } }; #endif class keyValuePairSplit { public: string x; // elementsp split (string) double y; // stored weight (double) int c; // stored count (int) keyValuePairSplit* next; // linked-list pointer keyValuePairSplit(): x(""), y(0.0), c(0), next(0) { } ~keyValuePairSplit() { } }; // ******** Tree elementsp Class ***************************************** class elementsp { public: string split; // split represented as a string double weight; // total weight of this split int count; // number of observations of this split bool color; // F: BLACK, T: RED short int mark; // marker elementsp *parent; // pointer to parent node elementsp *left; // pointer for left subtree elementsp *right; // pointer for right subtree elementsp(): split(""), weight(0.0), count(0), color(false), mark(0), parent(0), left(0), right(0) { } ~elementsp() { } }; // ******** Red-Black Tree Class ***************************************** // This vector implementation is a red-black balanced binary tree data // structure. It provides find a stored elementsp in time O(log n), // find the maximum elementsp in time O(1), delete an elementsp in // time O(log n), and insert an elementsp in time O(log n). // // Note that the split="" is assumed to be a special value, and thus // you cannot insert such an item. Beware of this limitation. // class splittree { private: elementsp* root; // binary tree root elementsp* leaf; // all leaf nodes int support; // number of nodes in the tree double total_weight; // total weight stored int total_count; // total number of observations stored // left-rotation operator void rotateLeft(elementsp*); // right-rotation operator void rotateRight(elementsp*); // house-keeping after insertion void insertCleanup(elementsp*); // house-keeping after deletion void deleteCleanup(elementsp*); keyValuePairSplit* returnSubtreeAsList(elementsp*, keyValuePairSplit*); // delete subtree rooted at z void deleteSubTree(elementsp*); // returns minimum of subtree rooted at z elementsp* returnMinKey(elementsp*); // returns successor of z's key elementsp* returnSuccessor(elementsp*); public: // default constructor/destructor splittree(); ~splittree(); // returns value associated with searchKey double returnValue(const string); // returns T if searchKey found, and points foundNode at the // corresponding node elementsp* findItem(const string); // update total_count and total_weight void finishedThisRound(); // insert a new key with stored value bool insertItem(string, double); void clearTree(); // delete a node with given key void deleteItem(string); // delete the entire tree void deleteTree(); // return array of keys in tree string* returnArrayOfKeys(); // return list of keys in tree slist* returnListOfKeys(); // return the tree as a list of keyValuePairSplits keyValuePairSplit* returnTreeAsList(); // returns the maximum key in the tree keyValuePairSplit returnMaxKey(); // returns the minimum key in the tree keyValuePairSplit returnMinKey(); // returns number of items in tree int returnNodecount(); // returns list of splits with given number of Ms keyValuePairSplit* returnTheseSplits(const int); // returns sum of stored values double returnTotal(); }; } // namespace fitHRG #endif igraph/src/dsgets.f0000644000176000001440000001664112325527073014021 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsgets c c\Description: c Given the eigenvalues of the symmetric tridiagonal matrix H, c computes the NP shifts AMU that are zeros of the polynomial of c degree NP which filters out components of the unwanted eigenvectors c corresponding to the AMU's based on some given criteria. c c NOTE: This is called even in the case of user specified shifts in c order to sort the eigenvalues, and error bounds of H for later use. c c\Usage: c call igraphdsgets c ( ISHIFT, WHICH, KEV, NP, RITZ, BOUNDS, SHIFTS ) c c\Arguments c ISHIFT Integer. (INPUT) c Method for selecting the implicit shifts at each iteration. c ISHIFT = 0: user specified shifts c ISHIFT = 1: exact shift with respect to the matrix H. c c WHICH Character*2. (INPUT) c Shift selection criteria. c 'LM' -> KEV eigenvalues of largest magnitude are retained. c 'SM' -> KEV eigenvalues of smallest magnitude are retained. c 'LA' -> KEV eigenvalues of largest value are retained. c 'SA' -> KEV eigenvalues of smallest value are retained. c 'BE' -> KEV eigenvalues, half from each end of the spectrum. c If KEV is odd, compute one more from the high end. c c KEV Integer. (INPUT) c KEV+NP is the size of the matrix H. c c NP Integer. (INPUT) c Number of implicit shifts to be computed. c c RITZ Double precision array of length KEV+NP. (INPUT/OUTPUT) c On INPUT, RITZ contains the eigenvalues of H. c On OUTPUT, RITZ are sorted so that the unwanted eigenvalues c are in the first NP locations and the wanted part is in c the last KEV locations. When exact shifts are selected, the c unwanted part corresponds to the shifts to be applied. c c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) c Error bounds corresponding to the ordering in RITZ. c c SHIFTS Double precision array of length NP. (INPUT/OUTPUT) c On INPUT: contains the user specified shifts if ISHIFT = 0. c On OUTPUT: contains the shifts sorted into decreasing order c of magnitude with respect to the Ritz estimates contained in c BOUNDS. If ISHIFT = 0, SHIFTS is not modified on exit. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c igraphdsortr ARPACK utility sorting routine. c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdvout ARPACK utility routine that prints vectors. c dcopy Level 1 BLAS that copies one vector to another. c dswap Level 1 BLAS that swaps the contents of two vectors. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/93: Version ' 2.1' c c\SCCS Information: @(#) c FILE: sgets.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 c c\Remarks c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsgets ( ishift, which, kev, np, ritz, bounds, & shifts ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character*2 which integer ishift, kev, np c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & bounds(kev+np), ritz(kev+np), shifts(np) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c integer kevd2, msglvl c c %----------------------% c | External Subroutines | c %----------------------% c external dswap, dcopy, igraphdsortr, igraphsecond c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic max, min c c %-----------------------% c | Executable Statements | c %-----------------------% c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = msgets c if (which .eq. 'BE') then c c %-----------------------------------------------------% c | Both ends of the spectrum are requested. | c | Sort the eigenvalues into algebraically increasing | c | order first then swap high end of the spectrum next | c | to low end in appropriate locations. | c | NOTE: when np < floor(kev/2) be careful not to swap | c | overlapping locations. | c %-----------------------------------------------------% c call igraphdsortr ('LA', .true., kev+np, ritz, bounds) kevd2 = kev / 2 if ( kev .gt. 1 ) then call dswap ( min(kevd2,np), ritz, 1, & ritz( max(kevd2,np)+1 ), 1) call dswap ( min(kevd2,np), bounds, 1, & bounds( max(kevd2,np)+1 ), 1) end if c else c c %----------------------------------------------------% c | LM, SM, LA, SA case. | c | Sort the eigenvalues of H into the desired order | c | and apply the resulting order to BOUNDS. | c | The eigenvalues are sorted so that the wanted part | c | are always in the last KEV locations. | c %----------------------------------------------------% c call igraphdsortr (which, .true., kev+np, ritz, bounds) end if c if (ishift .eq. 1 .and. np .gt. 0) then c c %-------------------------------------------------------% c | Sort the unwanted Ritz values used as shifts so that | c | the ones with largest Ritz estimates are first. | c | This will tend to minimize the effects of the | c | forward instability of the iteration when the shifts | c | are applied in subroutine igraphdsapps. | c %-------------------------------------------------------% c call igraphdsortr ('SM', .true., np, bounds, ritz) call dcopy (np, ritz, 1, shifts, 1) end if c call igraphsecond (t1) tsgets = tsgets + (t1 - t0) c if (msglvl .gt. 0) then call igraphivout (logfil, 1, kev, ndigit, '_sgets: KEV is') call igraphivout (logfil, 1, np, ndigit, '_sgets: NP is') call igraphdvout (logfil, kev+np, ritz, ndigit, & '_sgets: Eigenvalues of current H matrix') call igraphdvout (logfil, kev+np, bounds, ndigit, & '_sgets: Associated Ritz estimates') end if c return c c %---------------% c | End of igraphdsgets | c %---------------% c end igraph/src/drl_graph_3d.h0000644000176000001440000001024412325527073015053 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // The graph class contains the methods necessary to draw the // graph. It calls on the density server class to obtain // position and density information #include "DensityGrid_3d.h" #include "igraph_layout.h" namespace drl3d { // layout schedule information struct layout_schedule { int iterations; float temperature; float attraction; float damping_mult; time_t time_elapsed; }; class graph { public: // Methods void init_parms ( int rand_seed, float edge_cut, float real_parm ); void init_parms ( const igraph_layout_drl_options_t *options ); int read_real ( const igraph_matrix_t *real_mat, const igraph_vector_bool_t *fixed); int draw_graph (igraph_matrix_t *res); float get_tot_energy ( ); // Con/Decon graph( const igraph_t *igraph, const igraph_layout_drl_options_t *options, const igraph_vector_t *weights); ~graph( ) { } private: // Methods int ReCompute ( ); void update_nodes ( ); float Compute_Node_Energy ( int node_ind ); void Solve_Analytic ( int node_ind, float &pos_x, float &pos_y, float &pos_z ); void get_positions ( vector &node_indices, float return_positions[3*MAX_PROCS] ); void update_density ( vector &node_indices, float old_positions[3*MAX_PROCS], float new_positions[3*MAX_PROCS] ); void update_node_pos ( int node_ind, float old_positions[3*MAX_PROCS], float new_positions[3*MAX_PROCS] ); // MPI information int myid, num_procs; // graph decomposition information int num_nodes; // number of nodes in graph float highest_sim; // highest sim for normalization map id_catalog; // id_catalog[file id] = internal id map > neighbors; // neighbors of nodes on this proc. // graph layout information vector positions; DensityGrid density_server; // original VxOrd information int STAGE, iterations; float temperature, attraction, damping_mult; float min_edges, CUT_END, cut_length_end, cut_off_length, cut_rate; bool first_add, fine_first_add, fineDensity; // scheduling variables layout_schedule liquid; layout_schedule expansion; layout_schedule cooldown; layout_schedule crunch; layout_schedule simmer; // timing statistics time_t start_time, stop_time; // online clustering information int real_iterations; // number of iterations to hold .real input fixed int tot_iterations; int tot_expected_iterations; // for progress bar bool real_fixed; }; } // namespace drl3d igraph/src/glplux.c0000644000176000001440000011400012325527073014024 0ustar ripleyusers/* glplux.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glplux.h" #define xfault xerror #define dmp_create_poolx(size) dmp_create_pool() /*---------------------------------------------------------------------- // lux_create - create LU-factorization. // // SYNOPSIS // // #include "glplux.h" // LUX *lux_create(int n); // // DESCRIPTION // // The routine lux_create creates LU-factorization data structure for // a matrix of the order n. Initially the factorization corresponds to // the unity matrix (F = V = P = Q = I, so A = I). // // RETURNS // // The routine returns a pointer to the created LU-factorization data // structure, which represents the unity matrix of the order n. */ LUX *lux_create(int n) { LUX *lux; int k; if (n < 1) xfault("lux_create: n = %d; invalid parameter\n", n); lux = xmalloc(sizeof(LUX)); lux->n = n; lux->pool = dmp_create_poolx(sizeof(LUXELM)); lux->F_row = xcalloc(1+n, sizeof(LUXELM *)); lux->F_col = xcalloc(1+n, sizeof(LUXELM *)); lux->V_piv = xcalloc(1+n, sizeof(mpq_t)); lux->V_row = xcalloc(1+n, sizeof(LUXELM *)); lux->V_col = xcalloc(1+n, sizeof(LUXELM *)); lux->P_row = xcalloc(1+n, sizeof(int)); lux->P_col = xcalloc(1+n, sizeof(int)); lux->Q_row = xcalloc(1+n, sizeof(int)); lux->Q_col = xcalloc(1+n, sizeof(int)); for (k = 1; k <= n; k++) { lux->F_row[k] = lux->F_col[k] = NULL; mpq_init(lux->V_piv[k]); mpq_set_si(lux->V_piv[k], 1, 1); lux->V_row[k] = lux->V_col[k] = NULL; lux->P_row[k] = lux->P_col[k] = k; lux->Q_row[k] = lux->Q_col[k] = k; } lux->rank = n; return lux; } /*---------------------------------------------------------------------- // initialize - initialize LU-factorization data structures. // // This routine initializes data structures for subsequent computing // the LU-factorization of a given matrix A, which is specified by the // formal routine col. On exit V = A and F = P = Q = I, where I is the // unity matrix. */ static void initialize(LUX *lux, int (*col)(void *info, int j, int ind[], mpq_t val[]), void *info, LUXWKA *wka) { int n = lux->n; DMP *pool = lux->pool; LUXELM **F_row = lux->F_row; LUXELM **F_col = lux->F_col; mpq_t *V_piv = lux->V_piv; LUXELM **V_row = lux->V_row; LUXELM **V_col = lux->V_col; int *P_row = lux->P_row; int *P_col = lux->P_col; int *Q_row = lux->Q_row; int *Q_col = lux->Q_col; int *R_len = wka->R_len; int *R_head = wka->R_head; int *R_prev = wka->R_prev; int *R_next = wka->R_next; int *C_len = wka->C_len; int *C_head = wka->C_head; int *C_prev = wka->C_prev; int *C_next = wka->C_next; LUXELM *fij, *vij; int i, j, k, len, *ind; mpq_t *val; /* F := I */ for (i = 1; i <= n; i++) { while (F_row[i] != NULL) { fij = F_row[i], F_row[i] = fij->r_next; mpq_clear(fij->val); dmp_free_atom(pool, fij, sizeof(LUXELM)); } } for (j = 1; j <= n; j++) F_col[j] = NULL; /* V := 0 */ for (k = 1; k <= n; k++) mpq_set_si(V_piv[k], 0, 1); for (i = 1; i <= n; i++) { while (V_row[i] != NULL) { vij = V_row[i], V_row[i] = vij->r_next; mpq_clear(vij->val); dmp_free_atom(pool, vij, sizeof(LUXELM)); } } for (j = 1; j <= n; j++) V_col[j] = NULL; /* V := A */ ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(mpq_t)); for (k = 1; k <= n; k++) mpq_init(val[k]); for (j = 1; j <= n; j++) { /* obtain j-th column of matrix A */ len = col(info, j, ind, val); if (!(0 <= len && len <= n)) xfault("lux_decomp: j = %d: len = %d; invalid column length" "\n", j, len); /* copy elements of j-th column to matrix V */ for (k = 1; k <= len; k++) { /* get row index of a[i,j] */ i = ind[k]; if (!(1 <= i && i <= n)) xfault("lux_decomp: j = %d: i = %d; row index out of ran" "ge\n", j, i); /* check for duplicate indices */ if (V_row[i] != NULL && V_row[i]->j == j) xfault("lux_decomp: j = %d: i = %d; duplicate row indice" "s not allowed\n", j, i); /* check for zero value */ if (mpq_sgn(val[k]) == 0) xfault("lux_decomp: j = %d: i = %d; zero elements not al" "lowed\n", j, i); /* add new element v[i,j] = a[i,j] to V */ vij = dmp_get_atom(pool, sizeof(LUXELM)); vij->i = i, vij->j = j; mpq_init(vij->val); mpq_set(vij->val, val[k]); vij->r_prev = NULL; vij->r_next = V_row[i]; vij->c_prev = NULL; vij->c_next = V_col[j]; if (vij->r_next != NULL) vij->r_next->r_prev = vij; if (vij->c_next != NULL) vij->c_next->c_prev = vij; V_row[i] = V_col[j] = vij; } } xfree(ind); for (k = 1; k <= n; k++) mpq_clear(val[k]); xfree(val); /* P := Q := I */ for (k = 1; k <= n; k++) P_row[k] = P_col[k] = Q_row[k] = Q_col[k] = k; /* the rank of A and V is not determined yet */ lux->rank = -1; /* initially the entire matrix V is active */ /* determine its row lengths */ for (i = 1; i <= n; i++) { len = 0; for (vij = V_row[i]; vij != NULL; vij = vij->r_next) len++; R_len[i] = len; } /* build linked lists of active rows */ for (len = 0; len <= n; len++) R_head[len] = 0; for (i = 1; i <= n; i++) { len = R_len[i]; R_prev[i] = 0; R_next[i] = R_head[len]; if (R_next[i] != 0) R_prev[R_next[i]] = i; R_head[len] = i; } /* determine its column lengths */ for (j = 1; j <= n; j++) { len = 0; for (vij = V_col[j]; vij != NULL; vij = vij->c_next) len++; C_len[j] = len; } /* build linked lists of active columns */ for (len = 0; len <= n; len++) C_head[len] = 0; for (j = 1; j <= n; j++) { len = C_len[j]; C_prev[j] = 0; C_next[j] = C_head[len]; if (C_next[j] != 0) C_prev[C_next[j]] = j; C_head[len] = j; } return; } /*---------------------------------------------------------------------- // find_pivot - choose a pivot element. // // This routine chooses a pivot element v[p,q] in the active submatrix // of matrix U = P*V*Q. // // It is assumed that on entry the matrix U has the following partially // triangularized form: // // 1 k n // 1 x x x x x x x x x x // . x x x x x x x x x // . . x x x x x x x x // . . . x x x x x x x // k . . . . * * * * * * // . . . . * * * * * * // . . . . * * * * * * // . . . . * * * * * * // . . . . * * * * * * // n . . . . * * * * * * // // where rows and columns k, k+1, ..., n belong to the active submatrix // (elements of the active submatrix are marked by '*'). // // Since the matrix U = P*V*Q is not stored, the routine works with the // matrix V. It is assumed that the row-wise representation corresponds // to the matrix V, but the column-wise representation corresponds to // the active submatrix of the matrix V, i.e. elements of the matrix V, // which does not belong to the active submatrix, are missing from the // column linked lists. It is also assumed that each active row of the // matrix V is in the set R[len], where len is number of non-zeros in // the row, and each active column of the matrix V is in the set C[len], // where len is number of non-zeros in the column (in the latter case // only elements of the active submatrix are counted; such elements are // marked by '*' on the figure above). // // Due to exact arithmetic any non-zero element of the active submatrix // can be chosen as a pivot. However, to keep sparsity of the matrix V // the routine uses Markowitz strategy, trying to choose such element // v[p,q], which has smallest Markowitz cost (nr[p]-1) * (nc[q]-1), // where nr[p] and nc[q] are the number of non-zero elements, resp., in // p-th row and in q-th column of the active submatrix. // // In order to reduce the search, i.e. not to walk through all elements // of the active submatrix, the routine exploits a technique proposed by // I.Duff. This technique is based on using the sets R[len] and C[len] // of active rows and columns. // // On exit the routine returns a pointer to a pivot v[p,q] chosen, or // NULL, if the active submatrix is empty. */ static LUXELM *find_pivot(LUX *lux, LUXWKA *wka) { int n = lux->n; LUXELM **V_row = lux->V_row; LUXELM **V_col = lux->V_col; int *R_len = wka->R_len; int *R_head = wka->R_head; int *R_next = wka->R_next; int *C_len = wka->C_len; int *C_head = wka->C_head; int *C_next = wka->C_next; LUXELM *piv, *some, *vij; int i, j, len, min_len, ncand, piv_lim = 5; double best, cost; /* nothing is chosen so far */ piv = NULL, best = DBL_MAX, ncand = 0; /* if in the active submatrix there is a column that has the only non-zero (column singleton), choose it as a pivot */ j = C_head[1]; if (j != 0) { xassert(C_len[j] == 1); piv = V_col[j]; xassert(piv != NULL && piv->c_next == NULL); goto done; } /* if in the active submatrix there is a row that has the only non-zero (row singleton), choose it as a pivot */ i = R_head[1]; if (i != 0) { xassert(R_len[i] == 1); piv = V_row[i]; xassert(piv != NULL && piv->r_next == NULL); goto done; } /* there are no singletons in the active submatrix; walk through other non-empty rows and columns */ for (len = 2; len <= n; len++) { /* consider active columns having len non-zeros */ for (j = C_head[len]; j != 0; j = C_next[j]) { /* j-th column has len non-zeros */ /* find an element in the row of minimal length */ some = NULL, min_len = INT_MAX; for (vij = V_col[j]; vij != NULL; vij = vij->c_next) { if (min_len > R_len[vij->i]) some = vij, min_len = R_len[vij->i]; /* if Markowitz cost of this element is not greater than (len-1)**2, it can be chosen right now; this heuristic reduces the search and works well in many cases */ if (min_len <= len) { piv = some; goto done; } } /* j-th column has been scanned */ /* the minimal element found is a next pivot candidate */ xassert(some != NULL); ncand++; /* compute its Markowitz cost */ cost = (double)(min_len - 1) * (double)(len - 1); /* choose between the current candidate and this element */ if (cost < best) piv = some, best = cost; /* if piv_lim candidates have been considered, there is a doubt that a much better candidate exists; therefore it is the time to terminate the search */ if (ncand == piv_lim) goto done; } /* now consider active rows having len non-zeros */ for (i = R_head[len]; i != 0; i = R_next[i]) { /* i-th row has len non-zeros */ /* find an element in the column of minimal length */ some = NULL, min_len = INT_MAX; for (vij = V_row[i]; vij != NULL; vij = vij->r_next) { if (min_len > C_len[vij->j]) some = vij, min_len = C_len[vij->j]; /* if Markowitz cost of this element is not greater than (len-1)**2, it can be chosen right now; this heuristic reduces the search and works well in many cases */ if (min_len <= len) { piv = some; goto done; } } /* i-th row has been scanned */ /* the minimal element found is a next pivot candidate */ xassert(some != NULL); ncand++; /* compute its Markowitz cost */ cost = (double)(len - 1) * (double)(min_len - 1); /* choose between the current candidate and this element */ if (cost < best) piv = some, best = cost; /* if piv_lim candidates have been considered, there is a doubt that a much better candidate exists; therefore it is the time to terminate the search */ if (ncand == piv_lim) goto done; } } done: /* bring the pivot v[p,q] to the factorizing routine */ return piv; } /*---------------------------------------------------------------------- // eliminate - perform gaussian elimination. // // This routine performs elementary gaussian transformations in order // to eliminate subdiagonal elements in the k-th column of the matrix // U = P*V*Q using the pivot element u[k,k], where k is the number of // the current elimination step. // // The parameter piv specifies the pivot element v[p,q] = u[k,k]. // // Each time when the routine applies the elementary transformation to // a non-pivot row of the matrix V, it stores the corresponding element // to the matrix F in order to keep the main equality A = F*V. // // The routine assumes that on entry the matrices L = P*F*inv(P) and // U = P*V*Q are the following: // // 1 k 1 k n // 1 1 . . . . . . . . . 1 x x x x x x x x x x // x 1 . . . . . . . . . x x x x x x x x x // x x 1 . . . . . . . . . x x x x x x x x // x x x 1 . . . . . . . . . x x x x x x x // k x x x x 1 . . . . . k . . . . * * * * * * // x x x x _ 1 . . . . . . . . # * * * * * // x x x x _ . 1 . . . . . . . # * * * * * // x x x x _ . . 1 . . . . . . # * * * * * // x x x x _ . . . 1 . . . . . # * * * * * // n x x x x _ . . . . 1 n . . . . # * * * * * // // matrix L matrix U // // where rows and columns of the matrix U with numbers k, k+1, ..., n // form the active submatrix (eliminated elements are marked by '#' and // other elements of the active submatrix are marked by '*'). Note that // each eliminated non-zero element u[i,k] of the matrix U gives the // corresponding element l[i,k] of the matrix L (marked by '_'). // // Actually all operations are performed on the matrix V. Should note // that the row-wise representation corresponds to the matrix V, but the // column-wise representation corresponds to the active submatrix of the // matrix V, i.e. elements of the matrix V, which doesn't belong to the // active submatrix, are missing from the column linked lists. // // Let u[k,k] = v[p,q] be the pivot. In order to eliminate subdiagonal // elements u[i',k] = v[i,q], i' = k+1, k+2, ..., n, the routine applies // the following elementary gaussian transformations: // // (i-th row of V) := (i-th row of V) - f[i,p] * (p-th row of V), // // where f[i,p] = v[i,q] / v[p,q] is a gaussian multiplier. // // Additionally, in order to keep the main equality A = F*V, each time // when the routine applies the transformation to i-th row of the matrix // V, it also adds f[i,p] as a new element to the matrix F. // // IMPORTANT: On entry the working arrays flag and work should contain // zeros. This status is provided by the routine on exit. */ static void eliminate(LUX *lux, LUXWKA *wka, LUXELM *piv, int flag[], mpq_t work[]) { DMP *pool = lux->pool; LUXELM **F_row = lux->F_row; LUXELM **F_col = lux->F_col; mpq_t *V_piv = lux->V_piv; LUXELM **V_row = lux->V_row; LUXELM **V_col = lux->V_col; int *R_len = wka->R_len; int *R_head = wka->R_head; int *R_prev = wka->R_prev; int *R_next = wka->R_next; int *C_len = wka->C_len; int *C_head = wka->C_head; int *C_prev = wka->C_prev; int *C_next = wka->C_next; LUXELM *fip, *vij, *vpj, *viq, *next; mpq_t temp; int i, j, p, q; mpq_init(temp); /* determine row and column indices of the pivot v[p,q] */ xassert(piv != NULL); p = piv->i, q = piv->j; /* remove p-th (pivot) row from the active set; it will never return there */ if (R_prev[p] == 0) R_head[R_len[p]] = R_next[p]; else R_next[R_prev[p]] = R_next[p]; if (R_next[p] == 0) ; else R_prev[R_next[p]] = R_prev[p]; /* remove q-th (pivot) column from the active set; it will never return there */ if (C_prev[q] == 0) C_head[C_len[q]] = C_next[q]; else C_next[C_prev[q]] = C_next[q]; if (C_next[q] == 0) ; else C_prev[C_next[q]] = C_prev[q]; /* store the pivot value in a separate array */ mpq_set(V_piv[p], piv->val); /* remove the pivot from p-th row */ if (piv->r_prev == NULL) V_row[p] = piv->r_next; else piv->r_prev->r_next = piv->r_next; if (piv->r_next == NULL) ; else piv->r_next->r_prev = piv->r_prev; R_len[p]--; /* remove the pivot from q-th column */ if (piv->c_prev == NULL) V_col[q] = piv->c_next; else piv->c_prev->c_next = piv->c_next; if (piv->c_next == NULL) ; else piv->c_next->c_prev = piv->c_prev; C_len[q]--; /* free the space occupied by the pivot */ mpq_clear(piv->val); dmp_free_atom(pool, piv, sizeof(LUXELM)); /* walk through p-th (pivot) row, which already does not contain the pivot v[p,q], and do the following... */ for (vpj = V_row[p]; vpj != NULL; vpj = vpj->r_next) { /* get column index of v[p,j] */ j = vpj->j; /* store v[p,j] in the working array */ flag[j] = 1; mpq_set(work[j], vpj->val); /* remove j-th column from the active set; it will return there later with a new length */ if (C_prev[j] == 0) C_head[C_len[j]] = C_next[j]; else C_next[C_prev[j]] = C_next[j]; if (C_next[j] == 0) ; else C_prev[C_next[j]] = C_prev[j]; /* v[p,j] leaves the active submatrix, so remove it from j-th column; however, v[p,j] is kept in p-th row */ if (vpj->c_prev == NULL) V_col[j] = vpj->c_next; else vpj->c_prev->c_next = vpj->c_next; if (vpj->c_next == NULL) ; else vpj->c_next->c_prev = vpj->c_prev; C_len[j]--; } /* now walk through q-th (pivot) column, which already does not contain the pivot v[p,q], and perform gaussian elimination */ while (V_col[q] != NULL) { /* element v[i,q] has to be eliminated */ viq = V_col[q]; /* get row index of v[i,q] */ i = viq->i; /* remove i-th row from the active set; later it will return there with a new length */ if (R_prev[i] == 0) R_head[R_len[i]] = R_next[i]; else R_next[R_prev[i]] = R_next[i]; if (R_next[i] == 0) ; else R_prev[R_next[i]] = R_prev[i]; /* compute gaussian multiplier f[i,p] = v[i,q] / v[p,q] and store it in the matrix F */ fip = dmp_get_atom(pool, sizeof(LUXELM)); fip->i = i, fip->j = p; mpq_init(fip->val); mpq_div(fip->val, viq->val, V_piv[p]); fip->r_prev = NULL; fip->r_next = F_row[i]; fip->c_prev = NULL; fip->c_next = F_col[p]; if (fip->r_next != NULL) fip->r_next->r_prev = fip; if (fip->c_next != NULL) fip->c_next->c_prev = fip; F_row[i] = F_col[p] = fip; /* v[i,q] has to be eliminated, so remove it from i-th row */ if (viq->r_prev == NULL) V_row[i] = viq->r_next; else viq->r_prev->r_next = viq->r_next; if (viq->r_next == NULL) ; else viq->r_next->r_prev = viq->r_prev; R_len[i]--; /* and also from q-th column */ V_col[q] = viq->c_next; C_len[q]--; /* free the space occupied by v[i,q] */ mpq_clear(viq->val); dmp_free_atom(pool, viq, sizeof(LUXELM)); /* perform gaussian transformation: (i-th row) := (i-th row) - f[i,p] * (p-th row) note that now p-th row, which is in the working array, does not contain the pivot v[p,q], and i-th row does not contain the element v[i,q] to be eliminated */ /* walk through i-th row and transform existing non-zero elements */ for (vij = V_row[i]; vij != NULL; vij = next) { next = vij->r_next; /* get column index of v[i,j] */ j = vij->j; /* v[i,j] := v[i,j] - f[i,p] * v[p,j] */ if (flag[j]) { /* v[p,j] != 0 */ flag[j] = 0; mpq_mul(temp, fip->val, work[j]); mpq_sub(vij->val, vij->val, temp); if (mpq_sgn(vij->val) == 0) { /* new v[i,j] is zero, so remove it from the active submatrix */ /* remove v[i,j] from i-th row */ if (vij->r_prev == NULL) V_row[i] = vij->r_next; else vij->r_prev->r_next = vij->r_next; if (vij->r_next == NULL) ; else vij->r_next->r_prev = vij->r_prev; R_len[i]--; /* remove v[i,j] from j-th column */ if (vij->c_prev == NULL) V_col[j] = vij->c_next; else vij->c_prev->c_next = vij->c_next; if (vij->c_next == NULL) ; else vij->c_next->c_prev = vij->c_prev; C_len[j]--; /* free the space occupied by v[i,j] */ mpq_clear(vij->val); dmp_free_atom(pool, vij, sizeof(LUXELM)); } } } /* now flag is the pattern of the set v[p,*] \ v[i,*] */ /* walk through p-th (pivot) row and create new elements in i-th row, which appear due to fill-in */ for (vpj = V_row[p]; vpj != NULL; vpj = vpj->r_next) { j = vpj->j; if (flag[j]) { /* create new non-zero v[i,j] = 0 - f[i,p] * v[p,j] and add it to i-th row and j-th column */ vij = dmp_get_atom(pool, sizeof(LUXELM)); vij->i = i, vij->j = j; mpq_init(vij->val); mpq_mul(vij->val, fip->val, work[j]); mpq_neg(vij->val, vij->val); vij->r_prev = NULL; vij->r_next = V_row[i]; vij->c_prev = NULL; vij->c_next = V_col[j]; if (vij->r_next != NULL) vij->r_next->r_prev = vij; if (vij->c_next != NULL) vij->c_next->c_prev = vij; V_row[i] = V_col[j] = vij; R_len[i]++, C_len[j]++; } else { /* there is no fill-in, because v[i,j] already exists in i-th row; restore the flag, which was reset before */ flag[j] = 1; } } /* now i-th row has been completely transformed and can return to the active set with a new length */ R_prev[i] = 0; R_next[i] = R_head[R_len[i]]; if (R_next[i] != 0) R_prev[R_next[i]] = i; R_head[R_len[i]] = i; } /* at this point q-th (pivot) column must be empty */ xassert(C_len[q] == 0); /* walk through p-th (pivot) row again and do the following... */ for (vpj = V_row[p]; vpj != NULL; vpj = vpj->r_next) { /* get column index of v[p,j] */ j = vpj->j; /* erase v[p,j] from the working array */ flag[j] = 0; mpq_set_si(work[j], 0, 1); /* now j-th column has been completely transformed, so it can return to the active list with a new length */ C_prev[j] = 0; C_next[j] = C_head[C_len[j]]; if (C_next[j] != 0) C_prev[C_next[j]] = j; C_head[C_len[j]] = j; } mpq_clear(temp); /* return to the factorizing routine */ return; } /*---------------------------------------------------------------------- // lux_decomp - compute LU-factorization. // // SYNOPSIS // // #include "glplux.h" // int lux_decomp(LUX *lux, int (*col)(void *info, int j, int ind[], // mpq_t val[]), void *info); // // DESCRIPTION // // The routine lux_decomp computes LU-factorization of a given square // matrix A. // // The parameter lux specifies LU-factorization data structure built by // means of the routine lux_create. // // The formal routine col specifies the original matrix A. In order to // obtain j-th column of the matrix A the routine lux_decomp calls the // routine col with the parameter j (1 <= j <= n, where n is the order // of A). In response the routine col should store row indices and // numerical values of non-zero elements of j-th column of A to the // locations ind[1], ..., ind[len] and val[1], ..., val[len], resp., // where len is the number of non-zeros in j-th column, which should be // returned on exit. Neiter zero nor duplicate elements are allowed. // // The parameter info is a transit pointer passed to the formal routine // col; it can be used for various purposes. // // RETURNS // // The routine lux_decomp returns the singularity flag. Zero flag means // that the original matrix A is non-singular while non-zero flag means // that A is (exactly!) singular. // // Note that LU-factorization is valid in both cases, however, in case // of singularity some rows of the matrix V (including pivot elements) // will be empty. // // REPAIRING SINGULAR MATRIX // // If the routine lux_decomp returns non-zero flag, it provides all // necessary information that can be used for "repairing" the matrix A, // where "repairing" means replacing linearly dependent columns of the // matrix A by appropriate columns of the unity matrix. This feature is // needed when the routine lux_decomp is used for reinverting the basis // matrix within the simplex method procedure. // // On exit linearly dependent columns of the matrix U have the numbers // rank+1, rank+2, ..., n, where rank is the exact rank of the matrix A // stored by the routine to the member lux->rank. The correspondence // between columns of A and U is the same as between columns of V and U. // Thus, linearly dependent columns of the matrix A have the numbers // Q_col[rank+1], Q_col[rank+2], ..., Q_col[n], where Q_col is an array // representing the permutation matrix Q in column-like format. It is // understood that each j-th linearly dependent column of the matrix U // should be replaced by the unity vector, where all elements are zero // except the unity diagonal element u[j,j]. On the other hand j-th row // of the matrix U corresponds to the row of the matrix V (and therefore // of the matrix A) with the number P_row[j], where P_row is an array // representing the permutation matrix P in row-like format. Thus, each // j-th linearly dependent column of the matrix U should be replaced by // a column of the unity matrix with the number P_row[j]. // // The code that repairs the matrix A may look like follows: // // for (j = rank+1; j <= n; j++) // { replace column Q_col[j] of the matrix A by column P_row[j] of // the unity matrix; // } // // where rank, P_row, and Q_col are members of the structure LUX. */ int lux_decomp(LUX *lux, int (*col)(void *info, int j, int ind[], mpq_t val[]), void *info) { int n = lux->n; LUXELM **V_row = lux->V_row; LUXELM **V_col = lux->V_col; int *P_row = lux->P_row; int *P_col = lux->P_col; int *Q_row = lux->Q_row; int *Q_col = lux->Q_col; LUXELM *piv, *vij; LUXWKA *wka; int i, j, k, p, q, t, *flag; mpq_t *work; /* allocate working area */ wka = xmalloc(sizeof(LUXWKA)); wka->R_len = xcalloc(1+n, sizeof(int)); wka->R_head = xcalloc(1+n, sizeof(int)); wka->R_prev = xcalloc(1+n, sizeof(int)); wka->R_next = xcalloc(1+n, sizeof(int)); wka->C_len = xcalloc(1+n, sizeof(int)); wka->C_head = xcalloc(1+n, sizeof(int)); wka->C_prev = xcalloc(1+n, sizeof(int)); wka->C_next = xcalloc(1+n, sizeof(int)); /* initialize LU-factorization data structures */ initialize(lux, col, info, wka); /* allocate working arrays */ flag = xcalloc(1+n, sizeof(int)); work = xcalloc(1+n, sizeof(mpq_t)); for (k = 1; k <= n; k++) { flag[k] = 0; mpq_init(work[k]); } /* main elimination loop */ for (k = 1; k <= n; k++) { /* choose a pivot element v[p,q] */ piv = find_pivot(lux, wka); if (piv == NULL) { /* no pivot can be chosen, because the active submatrix is empty */ break; } /* determine row and column indices of the pivot element */ p = piv->i, q = piv->j; /* let v[p,q] correspond to u[i',j']; permute k-th and i'-th rows and k-th and j'-th columns of the matrix U = P*V*Q to move the element u[i',j'] to the position u[k,k] */ i = P_col[p], j = Q_row[q]; xassert(k <= i && i <= n && k <= j && j <= n); /* permute k-th and i-th rows of the matrix U */ t = P_row[k]; P_row[i] = t, P_col[t] = i; P_row[k] = p, P_col[p] = k; /* permute k-th and j-th columns of the matrix U */ t = Q_col[k]; Q_col[j] = t, Q_row[t] = j; Q_col[k] = q, Q_row[q] = k; /* eliminate subdiagonal elements of k-th column of the matrix U = P*V*Q using the pivot element u[k,k] = v[p,q] */ eliminate(lux, wka, piv, flag, work); } /* determine the rank of A (and V) */ lux->rank = k - 1; /* free working arrays */ xfree(flag); for (k = 1; k <= n; k++) mpq_clear(work[k]); xfree(work); /* build column lists of the matrix V using its row lists */ for (j = 1; j <= n; j++) xassert(V_col[j] == NULL); for (i = 1; i <= n; i++) { for (vij = V_row[i]; vij != NULL; vij = vij->r_next) { j = vij->j; vij->c_prev = NULL; vij->c_next = V_col[j]; if (vij->c_next != NULL) vij->c_next->c_prev = vij; V_col[j] = vij; } } /* free working area */ xfree(wka->R_len); xfree(wka->R_head); xfree(wka->R_prev); xfree(wka->R_next); xfree(wka->C_len); xfree(wka->C_head); xfree(wka->C_prev); xfree(wka->C_next); xfree(wka); /* return to the calling program */ return (lux->rank < n); } /*---------------------------------------------------------------------- // lux_f_solve - solve system F*x = b or F'*x = b. // // SYNOPSIS // // #include "glplux.h" // void lux_f_solve(LUX *lux, int tr, mpq_t x[]); // // DESCRIPTION // // The routine lux_f_solve solves either the system F*x = b (if the // flag tr is zero) or the system F'*x = b (if the flag tr is non-zero), // where the matrix F is a component of LU-factorization specified by // the parameter lux, F' is a matrix transposed to F. // // On entry the array x should contain elements of the right-hand side // vector b in locations x[1], ..., x[n], where n is the order of the // matrix F. On exit this array will contain elements of the solution // vector x in the same locations. */ void lux_f_solve(LUX *lux, int tr, mpq_t x[]) { int n = lux->n; LUXELM **F_row = lux->F_row; LUXELM **F_col = lux->F_col; int *P_row = lux->P_row; LUXELM *fik, *fkj; int i, j, k; mpq_t temp; mpq_init(temp); if (!tr) { /* solve the system F*x = b */ for (j = 1; j <= n; j++) { k = P_row[j]; if (mpq_sgn(x[k]) != 0) { for (fik = F_col[k]; fik != NULL; fik = fik->c_next) { mpq_mul(temp, fik->val, x[k]); mpq_sub(x[fik->i], x[fik->i], temp); } } } } else { /* solve the system F'*x = b */ for (i = n; i >= 1; i--) { k = P_row[i]; if (mpq_sgn(x[k]) != 0) { for (fkj = F_row[k]; fkj != NULL; fkj = fkj->r_next) { mpq_mul(temp, fkj->val, x[k]); mpq_sub(x[fkj->j], x[fkj->j], temp); } } } } mpq_clear(temp); return; } /*---------------------------------------------------------------------- // lux_v_solve - solve system V*x = b or V'*x = b. // // SYNOPSIS // // #include "glplux.h" // void lux_v_solve(LUX *lux, int tr, double x[]); // // DESCRIPTION // // The routine lux_v_solve solves either the system V*x = b (if the // flag tr is zero) or the system V'*x = b (if the flag tr is non-zero), // where the matrix V is a component of LU-factorization specified by // the parameter lux, V' is a matrix transposed to V. // // On entry the array x should contain elements of the right-hand side // vector b in locations x[1], ..., x[n], where n is the order of the // matrix V. On exit this array will contain elements of the solution // vector x in the same locations. */ void lux_v_solve(LUX *lux, int tr, mpq_t x[]) { int n = lux->n; mpq_t *V_piv = lux->V_piv; LUXELM **V_row = lux->V_row; LUXELM **V_col = lux->V_col; int *P_row = lux->P_row; int *Q_col = lux->Q_col; LUXELM *vij; int i, j, k; mpq_t *b, temp; b = xcalloc(1+n, sizeof(mpq_t)); for (k = 1; k <= n; k++) mpq_init(b[k]), mpq_set(b[k], x[k]), mpq_set_si(x[k], 0, 1); mpq_init(temp); if (!tr) { /* solve the system V*x = b */ for (k = n; k >= 1; k--) { i = P_row[k], j = Q_col[k]; if (mpq_sgn(b[i]) != 0) { mpq_set(x[j], b[i]); mpq_div(x[j], x[j], V_piv[i]); for (vij = V_col[j]; vij != NULL; vij = vij->c_next) { mpq_mul(temp, vij->val, x[j]); mpq_sub(b[vij->i], b[vij->i], temp); } } } } else { /* solve the system V'*x = b */ for (k = 1; k <= n; k++) { i = P_row[k], j = Q_col[k]; if (mpq_sgn(b[j]) != 0) { mpq_set(x[i], b[j]); mpq_div(x[i], x[i], V_piv[i]); for (vij = V_row[i]; vij != NULL; vij = vij->r_next) { mpq_mul(temp, vij->val, x[i]); mpq_sub(b[vij->j], b[vij->j], temp); } } } } for (k = 1; k <= n; k++) mpq_clear(b[k]); mpq_clear(temp); xfree(b); return; } /*---------------------------------------------------------------------- // lux_solve - solve system A*x = b or A'*x = b. // // SYNOPSIS // // #include "glplux.h" // void lux_solve(LUX *lux, int tr, mpq_t x[]); // // DESCRIPTION // // The routine lux_solve solves either the system A*x = b (if the flag // tr is zero) or the system A'*x = b (if the flag tr is non-zero), // where the parameter lux specifies LU-factorization of the matrix A, // A' is a matrix transposed to A. // // On entry the array x should contain elements of the right-hand side // vector b in locations x[1], ..., x[n], where n is the order of the // matrix A. On exit this array will contain elements of the solution // vector x in the same locations. */ void lux_solve(LUX *lux, int tr, mpq_t x[]) { if (lux->rank < lux->n) xfault("lux_solve: LU-factorization has incomplete rank\n"); if (!tr) { /* A = F*V, therefore inv(A) = inv(V)*inv(F) */ lux_f_solve(lux, 0, x); lux_v_solve(lux, 0, x); } else { /* A' = V'*F', therefore inv(A') = inv(F')*inv(V') */ lux_v_solve(lux, 1, x); lux_f_solve(lux, 1, x); } return; } /*---------------------------------------------------------------------- // lux_delete - delete LU-factorization. // // SYNOPSIS // // #include "glplux.h" // void lux_delete(LUX *lux); // // DESCRIPTION // // The routine lux_delete deletes LU-factorization data structure, // which the parameter lux points to, freeing all the memory allocated // to this object. */ void lux_delete(LUX *lux) { int n = lux->n; LUXELM *fij, *vij; int i; for (i = 1; i <= n; i++) { for (fij = lux->F_row[i]; fij != NULL; fij = fij->r_next) mpq_clear(fij->val); mpq_clear(lux->V_piv[i]); for (vij = lux->V_row[i]; vij != NULL; vij = vij->r_next) mpq_clear(vij->val); } dmp_delete_pool(lux->pool); xfree(lux->F_row); xfree(lux->F_col); xfree(lux->V_piv); xfree(lux->V_row); xfree(lux->V_col); xfree(lux->P_row); xfree(lux->P_col); xfree(lux->Q_row); xfree(lux->Q_col); xfree(lux); return; } /* eof */ igraph/src/glpapi02.c0000644000176000001440000003241712325527073014142 0ustar ripleyusers/* glpapi02.c (problem retrieving routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_get_prob_name - retrieve problem name * * SYNOPSIS * * const char *glp_get_prob_name(glp_prob *lp); * * RETURNS * * The routine glp_get_prob_name returns a pointer to an internal * buffer, which contains symbolic name of the problem. However, if the * problem has no assigned name, the routine returns NULL. */ const char *glp_get_prob_name(glp_prob *lp) { char *name; name = lp->name; return name; } /*********************************************************************** * NAME * * glp_get_obj_name - retrieve objective function name * * SYNOPSIS * * const char *glp_get_obj_name(glp_prob *lp); * * RETURNS * * The routine glp_get_obj_name returns a pointer to an internal * buffer, which contains a symbolic name of the objective function. * However, if the objective function has no assigned name, the routine * returns NULL. */ const char *glp_get_obj_name(glp_prob *lp) { char *name; name = lp->obj; return name; } /*********************************************************************** * NAME * * glp_get_obj_dir - retrieve optimization direction flag * * SYNOPSIS * * int glp_get_obj_dir(glp_prob *lp); * * RETURNS * * The routine glp_get_obj_dir returns the optimization direction flag * (i.e. "sense" of the objective function): * * GLP_MIN - minimization; * GLP_MAX - maximization. */ int glp_get_obj_dir(glp_prob *lp) { int dir = lp->dir; return dir; } /*********************************************************************** * NAME * * glp_get_num_rows - retrieve number of rows * * SYNOPSIS * * int glp_get_num_rows(glp_prob *lp); * * RETURNS * * The routine glp_get_num_rows returns the current number of rows in * the specified problem object. */ int glp_get_num_rows(glp_prob *lp) { int m = lp->m; return m; } /*********************************************************************** * NAME * * glp_get_num_cols - retrieve number of columns * * SYNOPSIS * * int glp_get_num_cols(glp_prob *lp); * * RETURNS * * The routine glp_get_num_cols returns the current number of columns * in the specified problem object. */ int glp_get_num_cols(glp_prob *lp) { int n = lp->n; return n; } /*********************************************************************** * NAME * * glp_get_row_name - retrieve row name * * SYNOPSIS * * const char *glp_get_row_name(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_name returns a pointer to an internal * buffer, which contains symbolic name of i-th row. However, if i-th * row has no assigned name, the routine returns NULL. */ const char *glp_get_row_name(glp_prob *lp, int i) { char *name; if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_name: i = %d; row number out of range\n", i); name = lp->row[i]->name; return name; } /*********************************************************************** * NAME * * glp_get_col_name - retrieve column name * * SYNOPSIS * * const char *glp_get_col_name(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_name returns a pointer to an internal * buffer, which contains symbolic name of j-th column. However, if j-th * column has no assigned name, the routine returns NULL. */ const char *glp_get_col_name(glp_prob *lp, int j) { char *name; if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_name: j = %d; column number out of range\n" , j); name = lp->col[j]->name; return name; } /*********************************************************************** * NAME * * glp_get_row_type - retrieve row type * * SYNOPSIS * * int glp_get_row_type(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_type returns the type of i-th row, i.e. the * type of corresponding auxiliary variable, as follows: * * GLP_FR - free (unbounded) variable; * GLP_LO - variable with lower bound; * GLP_UP - variable with upper bound; * GLP_DB - double-bounded variable; * GLP_FX - fixed variable. */ int glp_get_row_type(glp_prob *lp, int i) { if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_type: i = %d; row number out of range\n", i); return lp->row[i]->type; } /*********************************************************************** * NAME * * glp_get_row_lb - retrieve row lower bound * * SYNOPSIS * * double glp_get_row_lb(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_lb returns the lower bound of i-th row, i.e. * the lower bound of corresponding auxiliary variable. However, if the * row has no lower bound, the routine returns -DBL_MAX. */ double glp_get_row_lb(glp_prob *lp, int i) { double lb; if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_lb: i = %d; row number out of range\n", i); switch (lp->row[i]->type) { case GLP_FR: case GLP_UP: lb = -DBL_MAX; break; case GLP_LO: case GLP_DB: case GLP_FX: lb = lp->row[i]->lb; break; default: xassert(lp != lp); } return lb; } /*********************************************************************** * NAME * * glp_get_row_ub - retrieve row upper bound * * SYNOPSIS * * double glp_get_row_ub(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_ub returns the upper bound of i-th row, i.e. * the upper bound of corresponding auxiliary variable. However, if the * row has no upper bound, the routine returns +DBL_MAX. */ double glp_get_row_ub(glp_prob *lp, int i) { double ub; if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_ub: i = %d; row number out of range\n", i); switch (lp->row[i]->type) { case GLP_FR: case GLP_LO: ub = +DBL_MAX; break; case GLP_UP: case GLP_DB: case GLP_FX: ub = lp->row[i]->ub; break; default: xassert(lp != lp); } return ub; } /*********************************************************************** * NAME * * glp_get_col_type - retrieve column type * * SYNOPSIS * * int glp_get_col_type(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_type returns the type of j-th column, i.e. * the type of corresponding structural variable, as follows: * * GLP_FR - free (unbounded) variable; * GLP_LO - variable with lower bound; * GLP_UP - variable with upper bound; * GLP_DB - double-bounded variable; * GLP_FX - fixed variable. */ int glp_get_col_type(glp_prob *lp, int j) { if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_type: j = %d; column number out of range\n" , j); return lp->col[j]->type; } /*********************************************************************** * NAME * * glp_get_col_lb - retrieve column lower bound * * SYNOPSIS * * double glp_get_col_lb(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_lb returns the lower bound of j-th column, * i.e. the lower bound of corresponding structural variable. However, * if the column has no lower bound, the routine returns -DBL_MAX. */ double glp_get_col_lb(glp_prob *lp, int j) { double lb; if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_lb: j = %d; column number out of range\n", j); switch (lp->col[j]->type) { case GLP_FR: case GLP_UP: lb = -DBL_MAX; break; case GLP_LO: case GLP_DB: case GLP_FX: lb = lp->col[j]->lb; break; default: xassert(lp != lp); } return lb; } /*********************************************************************** * NAME * * glp_get_col_ub - retrieve column upper bound * * SYNOPSIS * * double glp_get_col_ub(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_ub returns the upper bound of j-th column, * i.e. the upper bound of corresponding structural variable. However, * if the column has no upper bound, the routine returns +DBL_MAX. */ double glp_get_col_ub(glp_prob *lp, int j) { double ub; if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_ub: j = %d; column number out of range\n", j); switch (lp->col[j]->type) { case GLP_FR: case GLP_LO: ub = +DBL_MAX; break; case GLP_UP: case GLP_DB: case GLP_FX: ub = lp->col[j]->ub; break; default: xassert(lp != lp); } return ub; } /*********************************************************************** * NAME * * glp_get_obj_coef - retrieve obj. coefficient or constant term * * SYNOPSIS * * double glp_get_obj_coef(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_obj_coef returns the objective coefficient at * j-th structural variable (column) of the specified problem object. * * If the parameter j is zero, the routine returns the constant term * ("shift") of the objective function. */ double glp_get_obj_coef(glp_prob *lp, int j) { if (!(0 <= j && j <= lp->n)) xerror("glp_get_obj_coef: j = %d; column number out of range\n" , j); return j == 0 ? lp->c0 : lp->col[j]->coef; } /*********************************************************************** * NAME * * glp_get_num_nz - retrieve number of constraint coefficients * * SYNOPSIS * * int glp_get_num_nz(glp_prob *lp); * * RETURNS * * The routine glp_get_num_nz returns the number of (non-zero) elements * in the constraint matrix of the specified problem object. */ int glp_get_num_nz(glp_prob *lp) { int nnz = lp->nnz; return nnz; } /*********************************************************************** * NAME * * glp_get_mat_row - retrieve row of the constraint matrix * * SYNOPSIS * * int glp_get_mat_row(glp_prob *lp, int i, int ind[], double val[]); * * DESCRIPTION * * The routine glp_get_mat_row scans (non-zero) elements of i-th row * of the constraint matrix of the specified problem object and stores * their column indices and numeric values to locations ind[1], ..., * ind[len] and val[1], ..., val[len], respectively, where 0 <= len <= n * is the number of elements in i-th row, n is the number of columns. * * The parameter ind and/or val can be specified as NULL, in which case * corresponding information is not stored. * * RETURNS * * The routine glp_get_mat_row returns the length len, i.e. the number * of (non-zero) elements in i-th row. */ int glp_get_mat_row(glp_prob *lp, int i, int ind[], double val[]) { GLPAIJ *aij; int len; if (!(1 <= i && i <= lp->m)) xerror("glp_get_mat_row: i = %d; row number out of range\n", i); len = 0; for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) { len++; if (ind != NULL) ind[len] = aij->col->j; if (val != NULL) val[len] = aij->val; } xassert(len <= lp->n); return len; } /*********************************************************************** * NAME * * glp_get_mat_col - retrieve column of the constraint matrix * * SYNOPSIS * * int glp_get_mat_col(glp_prob *lp, int j, int ind[], double val[]); * * DESCRIPTION * * The routine glp_get_mat_col scans (non-zero) elements of j-th column * of the constraint matrix of the specified problem object and stores * their row indices and numeric values to locations ind[1], ..., * ind[len] and val[1], ..., val[len], respectively, where 0 <= len <= m * is the number of elements in j-th column, m is the number of rows. * * The parameter ind or/and val can be specified as NULL, in which case * corresponding information is not stored. * * RETURNS * * The routine glp_get_mat_col returns the length len, i.e. the number * of (non-zero) elements in j-th column. */ int glp_get_mat_col(glp_prob *lp, int j, int ind[], double val[]) { GLPAIJ *aij; int len; if (!(1 <= j && j <= lp->n)) xerror("glp_get_mat_col: j = %d; column number out of range\n", j); len = 0; for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) { len++; if (ind != NULL) ind[len] = aij->row->i; if (val != NULL) val[len] = aij->val; } xassert(len <= lp->m); return len; } /* eof */ igraph/src/glpspx02.c0000644000176000001440000030646212325527073014207 0ustar ripleyusers/* glpspx02.c (dual simplex method) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wcomment" #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpspx.h" #define GLP_DEBUG 1 #if 0 #define GLP_LONG_STEP 1 #endif struct csa { /* common storage area */ /*--------------------------------------------------------------*/ /* LP data */ int m; /* number of rows (auxiliary variables), m > 0 */ int n; /* number of columns (structural variables), n > 0 */ char *type; /* char type[1+m+n]; */ /* type[0] is not used; type[k], 1 <= k <= m+n, is the type of variable x[k]: GLP_FR - free variable GLP_LO - variable with lower bound GLP_UP - variable with upper bound GLP_DB - double-bounded variable GLP_FX - fixed variable */ double *lb; /* double lb[1+m+n]; */ /* lb[0] is not used; lb[k], 1 <= k <= m+n, is an lower bound of variable x[k]; if x[k] has no lower bound, lb[k] is zero */ double *ub; /* double ub[1+m+n]; */ /* ub[0] is not used; ub[k], 1 <= k <= m+n, is an upper bound of variable x[k]; if x[k] has no upper bound, ub[k] is zero; if x[k] is of fixed type, ub[k] is the same as lb[k] */ double *coef; /* double coef[1+m+n]; */ /* coef[0] is not used; coef[k], 1 <= k <= m+n, is an objective coefficient at variable x[k] */ /*--------------------------------------------------------------*/ /* original bounds of variables */ char *orig_type; /* char orig_type[1+m+n]; */ double *orig_lb; /* double orig_lb[1+m+n]; */ double *orig_ub; /* double orig_ub[1+m+n]; */ /*--------------------------------------------------------------*/ /* original objective function */ double *obj; /* double obj[1+n]; */ /* obj[0] is a constant term of the original objective function; obj[j], 1 <= j <= n, is an original objective coefficient at structural variable x[m+j] */ double zeta; /* factor used to scale original objective coefficients; its sign defines original optimization direction: zeta > 0 means minimization, zeta < 0 means maximization */ /*--------------------------------------------------------------*/ /* constraint matrix A; it has m rows and n columns and is stored by columns */ int *A_ptr; /* int A_ptr[1+n+1]; */ /* A_ptr[0] is not used; A_ptr[j], 1 <= j <= n, is starting position of j-th column in arrays A_ind and A_val; note that A_ptr[1] is always 1; A_ptr[n+1] indicates the position after the last element in arrays A_ind and A_val */ int *A_ind; /* int A_ind[A_ptr[n+1]]; */ /* row indices */ double *A_val; /* double A_val[A_ptr[n+1]]; */ /* non-zero element values */ #if 1 /* 06/IV-2009 */ /* constraint matrix A stored by rows */ int *AT_ptr; /* int AT_ptr[1+m+1]; /* AT_ptr[0] is not used; AT_ptr[i], 1 <= i <= m, is starting position of i-th row in arrays AT_ind and AT_val; note that AT_ptr[1] is always 1; AT_ptr[m+1] indicates the position after the last element in arrays AT_ind and AT_val */ int *AT_ind; /* int AT_ind[AT_ptr[m+1]]; */ /* column indices */ double *AT_val; /* double AT_val[AT_ptr[m+1]]; */ /* non-zero element values */ #endif /*--------------------------------------------------------------*/ /* basis header */ int *head; /* int head[1+m+n]; */ /* head[0] is not used; head[i], 1 <= i <= m, is the ordinal number of basic variable xB[i]; head[i] = k means that xB[i] = x[k] and i-th column of matrix B is k-th column of matrix (I|-A); head[m+j], 1 <= j <= n, is the ordinal number of non-basic variable xN[j]; head[m+j] = k means that xN[j] = x[k] and j-th column of matrix N is k-th column of matrix (I|-A) */ #if 1 /* 06/IV-2009 */ int *bind; /* int bind[1+m+n]; */ /* bind[0] is not used; bind[k], 1 <= k <= m+n, is the position of k-th column of the matrix (I|-A) in the matrix (B|N); that is, bind[k] = k' means that head[k'] = k */ #endif char *stat; /* char stat[1+n]; */ /* stat[0] is not used; stat[j], 1 <= j <= n, is the status of non-basic variable xN[j], which defines its active bound: GLP_NL - lower bound is active GLP_NU - upper bound is active GLP_NF - free variable GLP_NS - fixed variable */ /*--------------------------------------------------------------*/ /* matrix B is the basis matrix; it is composed from columns of the augmented constraint matrix (I|-A) corresponding to basic variables and stored in a factorized (invertable) form */ int valid; /* factorization is valid only if this flag is set */ BFD *bfd; /* BFD bfd[1:m,1:m]; */ /* factorized (invertable) form of the basis matrix */ #if 0 /* 06/IV-2009 */ /*--------------------------------------------------------------*/ /* matrix N is a matrix composed from columns of the augmented constraint matrix (I|-A) corresponding to non-basic variables except fixed ones; it is stored by rows and changes every time the basis changes */ int *N_ptr; /* int N_ptr[1+m+1]; */ /* N_ptr[0] is not used; N_ptr[i], 1 <= i <= m, is starting position of i-th row in arrays N_ind and N_val; note that N_ptr[1] is always 1; N_ptr[m+1] indicates the position after the last element in arrays N_ind and N_val */ int *N_len; /* int N_len[1+m]; */ /* N_len[0] is not used; N_len[i], 1 <= i <= m, is length of i-th row (0 to n) */ int *N_ind; /* int N_ind[N_ptr[m+1]]; */ /* column indices */ double *N_val; /* double N_val[N_ptr[m+1]]; */ /* non-zero element values */ #endif /*--------------------------------------------------------------*/ /* working parameters */ int phase; /* search phase: 0 - not determined yet 1 - search for dual feasible solution 2 - search for optimal solution */ glp_long tm_beg; /* time value at the beginning of the search */ int it_beg; /* simplex iteration count at the beginning of the search */ int it_cnt; /* simplex iteration count; it increases by one every time the basis changes */ int it_dpy; /* simplex iteration count at the most recent display output */ /*--------------------------------------------------------------*/ /* basic solution components */ double *bbar; /* double bbar[1+m]; */ /* bbar[0] is not used on phase I; on phase II it is the current value of the original objective function; bbar[i], 1 <= i <= m, is primal value of basic variable xB[i] (if xB[i] is free, its primal value is not updated) */ double *cbar; /* double cbar[1+n]; */ /* cbar[0] is not used; cbar[j], 1 <= j <= n, is reduced cost of non-basic variable xN[j] (if xN[j] is fixed, its reduced cost is not updated) */ /*--------------------------------------------------------------*/ /* the following pricing technique options may be used: GLP_PT_STD - standard ("textbook") pricing; GLP_PT_PSE - projected steepest edge; GLP_PT_DVX - Devex pricing (not implemented yet); in case of GLP_PT_STD the reference space is not used, and all steepest edge coefficients are set to 1 */ int refct; /* this count is set to an initial value when the reference space is defined and decreases by one every time the basis changes; once this count reaches zero, the reference space is redefined again */ char *refsp; /* char refsp[1+m+n]; */ /* refsp[0] is not used; refsp[k], 1 <= k <= m+n, is the flag which means that variable x[k] belongs to the current reference space */ double *gamma; /* double gamma[1+m]; */ /* gamma[0] is not used; gamma[i], 1 <= i <= n, is the steepest edge coefficient for basic variable xB[i]; if xB[i] is free, gamma[i] is not used and just set to 1 */ /*--------------------------------------------------------------*/ /* basic variable xB[p] chosen to leave the basis */ int p; /* index of the basic variable xB[p] chosen, 1 <= p <= m; if the set of eligible basic variables is empty (i.e. if the current basic solution is primal feasible within a tolerance) and thus no variable has been chosen, p is set to 0 */ double delta; /* change of xB[p] in the adjacent basis; delta > 0 means that xB[p] violates its lower bound and will increase to achieve it in the adjacent basis; delta < 0 means that xB[p] violates its upper bound and will decrease to achieve it in the adjacent basis */ /*--------------------------------------------------------------*/ /* pivot row of the simplex table corresponding to basic variable xB[p] chosen is the following vector: T' * e[p] = - N' * inv(B') * e[p] = - N' * rho, where B' is a matrix transposed to the current basis matrix, N' is a matrix, whose rows are columns of the matrix (I|-A) corresponding to non-basic non-fixed variables */ int trow_nnz; /* number of non-zero components, 0 <= nnz <= n */ int *trow_ind; /* int trow_ind[1+n]; */ /* trow_ind[0] is not used; trow_ind[t], 1 <= t <= nnz, is an index of non-zero component, i.e. trow_ind[t] = j means that trow_vec[j] != 0 */ double *trow_vec; /* int trow_vec[1+n]; */ /* trow_vec[0] is not used; trow_vec[j], 1 <= j <= n, is a numeric value of j-th component of the row */ double trow_max; /* infinity (maximum) norm of the row (max |trow_vec[j]|) */ int trow_num; /* number of significant non-zero components, which means that: |trow_vec[j]| >= eps for j in trow_ind[1,...,num], |tcol_vec[j]| < eps for j in trow_ind[num+1,...,nnz], where eps is a pivot tolerance */ /*--------------------------------------------------------------*/ #ifdef GLP_LONG_STEP /* 07/IV-2009 */ int nbps; /* number of breakpoints, 0 <= nbps <= n */ struct bkpt { int j; /* index of non-basic variable xN[j], 1 <= j <= n */ double t; /* value of dual ray parameter at breakpoint, t >= 0 */ double dz; /* dz = zeta(t = t[k]) - zeta(t = 0) */ } *bkpt; /* struct bkpt bkpt[1+n]; */ /* bkpt[0] is not used; bkpt[k], 1 <= k <= nbps, is k-th breakpoint of the dual objective */ #endif /*--------------------------------------------------------------*/ /* non-basic variable xN[q] chosen to enter the basis */ int q; /* index of the non-basic variable xN[q] chosen, 1 <= q <= n; if no variable has been chosen, q is set to 0 */ double new_dq; /* reduced cost of xN[q] in the adjacent basis (it is the change of lambdaB[p]) */ /*--------------------------------------------------------------*/ /* pivot column of the simplex table corresponding to non-basic variable xN[q] chosen is the following vector: T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], where B is the current basis matrix, N[q] is a column of the matrix (I|-A) corresponding to xN[q] */ int tcol_nnz; /* number of non-zero components, 0 <= nnz <= m */ int *tcol_ind; /* int tcol_ind[1+m]; */ /* tcol_ind[0] is not used; tcol_ind[t], 1 <= t <= nnz, is an index of non-zero component, i.e. tcol_ind[t] = i means that tcol_vec[i] != 0 */ double *tcol_vec; /* double tcol_vec[1+m]; */ /* tcol_vec[0] is not used; tcol_vec[i], 1 <= i <= m, is a numeric value of i-th component of the column */ /*--------------------------------------------------------------*/ /* working arrays */ double *work1; /* double work1[1+m]; */ double *work2; /* double work2[1+m]; */ double *work3; /* double work3[1+m]; */ double *work4; /* double work4[1+m]; */ }; static const double kappa = 0.10; /*********************************************************************** * alloc_csa - allocate common storage area * * This routine allocates all arrays in the common storage area (CSA) * and returns a pointer to the CSA. */ static struct csa *alloc_csa(glp_prob *lp) { struct csa *csa; int m = lp->m; int n = lp->n; int nnz = lp->nnz; csa = xmalloc(sizeof(struct csa)); xassert(m > 0 && n > 0); csa->m = m; csa->n = n; csa->type = xcalloc(1+m+n, sizeof(char)); csa->lb = xcalloc(1+m+n, sizeof(double)); csa->ub = xcalloc(1+m+n, sizeof(double)); csa->coef = xcalloc(1+m+n, sizeof(double)); csa->orig_type = xcalloc(1+m+n, sizeof(char)); csa->orig_lb = xcalloc(1+m+n, sizeof(double)); csa->orig_ub = xcalloc(1+m+n, sizeof(double)); csa->obj = xcalloc(1+n, sizeof(double)); csa->A_ptr = xcalloc(1+n+1, sizeof(int)); csa->A_ind = xcalloc(1+nnz, sizeof(int)); csa->A_val = xcalloc(1+nnz, sizeof(double)); #if 1 /* 06/IV-2009 */ csa->AT_ptr = xcalloc(1+m+1, sizeof(int)); csa->AT_ind = xcalloc(1+nnz, sizeof(int)); csa->AT_val = xcalloc(1+nnz, sizeof(double)); #endif csa->head = xcalloc(1+m+n, sizeof(int)); #if 1 /* 06/IV-2009 */ csa->bind = xcalloc(1+m+n, sizeof(int)); #endif csa->stat = xcalloc(1+n, sizeof(char)); #if 0 /* 06/IV-2009 */ csa->N_ptr = xcalloc(1+m+1, sizeof(int)); csa->N_len = xcalloc(1+m, sizeof(int)); csa->N_ind = NULL; /* will be allocated later */ csa->N_val = NULL; /* will be allocated later */ #endif csa->bbar = xcalloc(1+m, sizeof(double)); csa->cbar = xcalloc(1+n, sizeof(double)); csa->refsp = xcalloc(1+m+n, sizeof(char)); csa->gamma = xcalloc(1+m, sizeof(double)); csa->trow_ind = xcalloc(1+n, sizeof(int)); csa->trow_vec = xcalloc(1+n, sizeof(double)); #ifdef GLP_LONG_STEP /* 07/IV-2009 */ csa->bkpt = xcalloc(1+n, sizeof(struct bkpt)); #endif csa->tcol_ind = xcalloc(1+m, sizeof(int)); csa->tcol_vec = xcalloc(1+m, sizeof(double)); csa->work1 = xcalloc(1+m, sizeof(double)); csa->work2 = xcalloc(1+m, sizeof(double)); csa->work3 = xcalloc(1+m, sizeof(double)); csa->work4 = xcalloc(1+m, sizeof(double)); return csa; } /*********************************************************************** * init_csa - initialize common storage area * * This routine initializes all data structures in the common storage * area (CSA). */ static void init_csa(struct csa *csa, glp_prob *lp) { int m = csa->m; int n = csa->n; char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; double *coef = csa->coef; char *orig_type = csa->orig_type; double *orig_lb = csa->orig_lb; double *orig_ub = csa->orig_ub; double *obj = csa->obj; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; #if 1 /* 06/IV-2009 */ int *AT_ptr = csa->AT_ptr; int *AT_ind = csa->AT_ind; double *AT_val = csa->AT_val; #endif int *head = csa->head; #if 1 /* 06/IV-2009 */ int *bind = csa->bind; #endif char *stat = csa->stat; char *refsp = csa->refsp; double *gamma = csa->gamma; int i, j, k, loc; double cmax; /* auxiliary variables */ for (i = 1; i <= m; i++) { GLPROW *row = lp->row[i]; type[i] = (char)row->type; lb[i] = row->lb * row->rii; ub[i] = row->ub * row->rii; coef[i] = 0.0; } /* structural variables */ for (j = 1; j <= n; j++) { GLPCOL *col = lp->col[j]; type[m+j] = (char)col->type; lb[m+j] = col->lb / col->sjj; ub[m+j] = col->ub / col->sjj; coef[m+j] = col->coef * col->sjj; } /* original bounds of variables */ memcpy(&orig_type[1], &type[1], (m+n) * sizeof(char)); memcpy(&orig_lb[1], &lb[1], (m+n) * sizeof(double)); memcpy(&orig_ub[1], &ub[1], (m+n) * sizeof(double)); /* original objective function */ obj[0] = lp->c0; memcpy(&obj[1], &coef[m+1], n * sizeof(double)); /* factor used to scale original objective coefficients */ cmax = 0.0; for (j = 1; j <= n; j++) if (cmax < fabs(obj[j])) cmax = fabs(obj[j]); if (cmax == 0.0) cmax = 1.0; switch (lp->dir) { case GLP_MIN: csa->zeta = + 1.0 / cmax; break; case GLP_MAX: csa->zeta = - 1.0 / cmax; break; default: xassert(lp != lp); } #if 1 if (fabs(csa->zeta) < 1.0) csa->zeta *= 1000.0; #endif /* scale working objective coefficients */ for (j = 1; j <= n; j++) coef[m+j] *= csa->zeta; /* matrix A (by columns) */ loc = 1; for (j = 1; j <= n; j++) { GLPAIJ *aij; A_ptr[j] = loc; for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) { A_ind[loc] = aij->row->i; A_val[loc] = aij->row->rii * aij->val * aij->col->sjj; loc++; } } A_ptr[n+1] = loc; xassert(loc-1 == lp->nnz); #if 1 /* 06/IV-2009 */ /* matrix A (by rows) */ loc = 1; for (i = 1; i <= m; i++) { GLPAIJ *aij; AT_ptr[i] = loc; for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) { AT_ind[loc] = aij->col->j; AT_val[loc] = aij->row->rii * aij->val * aij->col->sjj; loc++; } } AT_ptr[m+1] = loc; xassert(loc-1 == lp->nnz); #endif /* basis header */ xassert(lp->valid); memcpy(&head[1], &lp->head[1], m * sizeof(int)); k = 0; for (i = 1; i <= m; i++) { GLPROW *row = lp->row[i]; if (row->stat != GLP_BS) { k++; xassert(k <= n); head[m+k] = i; stat[k] = (char)row->stat; } } for (j = 1; j <= n; j++) { GLPCOL *col = lp->col[j]; if (col->stat != GLP_BS) { k++; xassert(k <= n); head[m+k] = m + j; stat[k] = (char)col->stat; } } xassert(k == n); #if 1 /* 06/IV-2009 */ for (k = 1; k <= m+n; k++) bind[head[k]] = k; #endif /* factorization of matrix B */ csa->valid = 1, lp->valid = 0; csa->bfd = lp->bfd, lp->bfd = NULL; #if 0 /* 06/IV-2009 */ /* matrix N (by rows) */ alloc_N(csa); build_N(csa); #endif /* working parameters */ csa->phase = 0; csa->tm_beg = xtime(); csa->it_beg = csa->it_cnt = lp->it_cnt; csa->it_dpy = -1; /* reference space and steepest edge coefficients */ csa->refct = 0; memset(&refsp[1], 0, (m+n) * sizeof(char)); for (i = 1; i <= m; i++) gamma[i] = 1.0; return; } #if 1 /* copied from primal */ /*********************************************************************** * invert_B - compute factorization of the basis matrix * * This routine computes factorization of the current basis matrix B. * * If the operation is successful, the routine returns zero, otherwise * non-zero. */ static int inv_col(void *info, int i, int ind[], double val[]) { /* this auxiliary routine returns row indices and numeric values of non-zero elements of i-th column of the basis matrix */ struct csa *csa = info; int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; int k, len, ptr, t; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); #endif k = head[i]; /* B[i] is k-th column of (I|-A) */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* B[i] is k-th column of submatrix I */ len = 1; ind[1] = k; val[1] = 1.0; } else { /* B[i] is (k-m)-th column of submatrix (-A) */ ptr = A_ptr[k-m]; len = A_ptr[k-m+1] - ptr; memcpy(&ind[1], &A_ind[ptr], len * sizeof(int)); memcpy(&val[1], &A_val[ptr], len * sizeof(double)); for (t = 1; t <= len; t++) val[t] = - val[t]; } return len; } static int invert_B(struct csa *csa) { int ret; ret = bfd_factorize(csa->bfd, csa->m, NULL, inv_col, csa); csa->valid = (ret == 0); return ret; } #endif #if 1 /* copied from primal */ /*********************************************************************** * update_B - update factorization of the basis matrix * * This routine replaces i-th column of the basis matrix B by k-th * column of the augmented constraint matrix (I|-A) and then updates * the factorization of B. * * If the factorization has been successfully updated, the routine * returns zero, otherwise non-zero. */ static int update_B(struct csa *csa, int i, int k) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int ret; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* new i-th column of B is k-th column of I */ int ind[1+1]; double val[1+1]; ind[1] = k; val[1] = 1.0; xassert(csa->valid); ret = bfd_update_it(csa->bfd, i, 0, 1, ind, val); } else { /* new i-th column of B is (k-m)-th column of (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; double *val = csa->work1; int beg, end, ptr, len; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; len = 0; for (ptr = beg; ptr < end; ptr++) val[++len] = - A_val[ptr]; xassert(csa->valid); ret = bfd_update_it(csa->bfd, i, 0, len, &A_ind[beg-1], val); } csa->valid = (ret == 0); return ret; } #endif #if 1 /* copied from primal */ /*********************************************************************** * error_ftran - compute residual vector r = h - B * x * * This routine computes the residual vector r = h - B * x, where B is * the current basis matrix, h is the vector of right-hand sides, x is * the solution vector. */ static void error_ftran(struct csa *csa, double h[], double x[], double r[]) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; int i, k, beg, end, ptr; double temp; /* compute the residual vector: r = h - B * x = h - B[1] * x[1] - ... - B[m] * x[m], where B[1], ..., B[m] are columns of matrix B */ memcpy(&r[1], &h[1], m * sizeof(double)); for (i = 1; i <= m; i++) { temp = x[i]; if (temp == 0.0) continue; k = head[i]; /* B[i] is k-th column of (I|-A) */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* B[i] is k-th column of submatrix I */ r[k] -= temp; } else { /* B[i] is (k-m)-th column of submatrix (-A) */ beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) r[A_ind[ptr]] += A_val[ptr] * temp; } } return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * refine_ftran - refine solution of B * x = h * * This routine performs one iteration to refine the solution of * the system B * x = h, where B is the current basis matrix, h is the * vector of right-hand sides, x is the solution vector. */ static void refine_ftran(struct csa *csa, double h[], double x[]) { int m = csa->m; double *r = csa->work1; double *d = csa->work1; int i; /* compute the residual vector r = h - B * x */ error_ftran(csa, h, x, r); /* compute the correction vector d = inv(B) * r */ xassert(csa->valid); bfd_ftran(csa->bfd, d); /* refine the solution vector (new x) = (old x) + d */ for (i = 1; i <= m; i++) x[i] += d[i]; return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * error_btran - compute residual vector r = h - B'* x * * This routine computes the residual vector r = h - B'* x, where B' * is a matrix transposed to the current basis matrix, h is the vector * of right-hand sides, x is the solution vector. */ static void error_btran(struct csa *csa, double h[], double x[], double r[]) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; int i, k, beg, end, ptr; double temp; /* compute the residual vector r = b - B'* x */ for (i = 1; i <= m; i++) { /* r[i] := b[i] - (i-th column of B)'* x */ k = head[i]; /* B[i] is k-th column of (I|-A) */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif temp = h[i]; if (k <= m) { /* B[i] is k-th column of submatrix I */ temp -= x[k]; } else { /* B[i] is (k-m)-th column of submatrix (-A) */ beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) temp += A_val[ptr] * x[A_ind[ptr]]; } r[i] = temp; } return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * refine_btran - refine solution of B'* x = h * * This routine performs one iteration to refine the solution of the * system B'* x = h, where B' is a matrix transposed to the current * basis matrix, h is the vector of right-hand sides, x is the solution * vector. */ static void refine_btran(struct csa *csa, double h[], double x[]) { int m = csa->m; double *r = csa->work1; double *d = csa->work1; int i; /* compute the residual vector r = h - B'* x */ error_btran(csa, h, x, r); /* compute the correction vector d = inv(B') * r */ xassert(csa->valid); bfd_btran(csa->bfd, d); /* refine the solution vector (new x) = (old x) + d */ for (i = 1; i <= m; i++) x[i] += d[i]; return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * get_xN - determine current value of non-basic variable xN[j] * * This routine returns the current value of non-basic variable xN[j], * which is a value of its active bound. */ static double get_xN(struct csa *csa, int j) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif double *lb = csa->lb; double *ub = csa->ub; int *head = csa->head; char *stat = csa->stat; int k; double xN; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif switch (stat[j]) { case GLP_NL: /* x[k] is on its lower bound */ xN = lb[k]; break; case GLP_NU: /* x[k] is on its upper bound */ xN = ub[k]; break; case GLP_NF: /* x[k] is free non-basic variable */ xN = 0.0; break; case GLP_NS: /* x[k] is fixed non-basic variable */ xN = lb[k]; break; default: xassert(stat != stat); } return xN; } #endif #if 1 /* copied from primal */ /*********************************************************************** * eval_beta - compute primal values of basic variables * * This routine computes current primal values of all basic variables: * * beta = - inv(B) * N * xN, * * where B is the current basis matrix, N is a matrix built of columns * of matrix (I|-A) corresponding to non-basic variables, and xN is the * vector of current values of non-basic variables. */ static void eval_beta(struct csa *csa, double beta[]) { int m = csa->m; int n = csa->n; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; double *h = csa->work2; int i, j, k, beg, end, ptr; double xN; /* compute the right-hand side vector: h := - N * xN = - N[1] * xN[1] - ... - N[n] * xN[n], where N[1], ..., N[n] are columns of matrix N */ for (i = 1; i <= m; i++) h[i] = 0.0; for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* determine current value of xN[j] */ xN = get_xN(csa, j); if (xN == 0.0) continue; if (k <= m) { /* N[j] is k-th column of submatrix I */ h[k] -= xN; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] += xN * A_val[ptr]; } } /* solve system B * beta = h */ memcpy(&beta[1], &h[1], m * sizeof(double)); xassert(csa->valid); bfd_ftran(csa->bfd, beta); /* and refine the solution */ refine_ftran(csa, h, beta); return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * eval_pi - compute vector of simplex multipliers * * This routine computes the vector of current simplex multipliers: * * pi = inv(B') * cB, * * where B' is a matrix transposed to the current basis matrix, cB is * a subvector of objective coefficients at basic variables. */ static void eval_pi(struct csa *csa, double pi[]) { int m = csa->m; double *c = csa->coef; int *head = csa->head; double *cB = csa->work2; int i; /* construct the right-hand side vector cB */ for (i = 1; i <= m; i++) cB[i] = c[head[i]]; /* solve system B'* pi = cB */ memcpy(&pi[1], &cB[1], m * sizeof(double)); xassert(csa->valid); bfd_btran(csa->bfd, pi); /* and refine the solution */ refine_btran(csa, cB, pi); return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * eval_cost - compute reduced cost of non-basic variable xN[j] * * This routine computes the current reduced cost of non-basic variable * xN[j]: * * d[j] = cN[j] - N'[j] * pi, * * where cN[j] is the objective coefficient at variable xN[j], N[j] is * a column of the augmented constraint matrix (I|-A) corresponding to * xN[j], pi is the vector of simplex multipliers. */ static double eval_cost(struct csa *csa, double pi[], int j) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif double *coef = csa->coef; int *head = csa->head; int k; double dj; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif dj = coef[k]; if (k <= m) { /* N[j] is k-th column of submatrix I */ dj -= pi[k]; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) dj += A_val[ptr] * pi[A_ind[ptr]]; } return dj; } #endif #if 1 /* copied from primal */ /*********************************************************************** * eval_bbar - compute and store primal values of basic variables * * This routine computes primal values of all basic variables and then * stores them in the solution array. */ static void eval_bbar(struct csa *csa) { eval_beta(csa, csa->bbar); return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * eval_cbar - compute and store reduced costs of non-basic variables * * This routine computes reduced costs of all non-basic variables and * then stores them in the solution array. */ static void eval_cbar(struct csa *csa) { #ifdef GLP_DEBUG int m = csa->m; #endif int n = csa->n; #ifdef GLP_DEBUG int *head = csa->head; #endif double *cbar = csa->cbar; double *pi = csa->work3; int j; #ifdef GLP_DEBUG int k; #endif /* compute simplex multipliers */ eval_pi(csa, pi); /* compute and store reduced costs */ for (j = 1; j <= n; j++) { #ifdef GLP_DEBUG k = head[m+j]; /* x[k] = xN[j] */ xassert(1 <= k && k <= m+n); #endif cbar[j] = eval_cost(csa, pi, j); } return; } #endif /*********************************************************************** * reset_refsp - reset the reference space * * This routine resets (redefines) the reference space used in the * projected steepest edge pricing algorithm. */ static void reset_refsp(struct csa *csa) { int m = csa->m; int n = csa->n; int *head = csa->head; char *refsp = csa->refsp; double *gamma = csa->gamma; int i, k; xassert(csa->refct == 0); csa->refct = 1000; memset(&refsp[1], 0, (m+n) * sizeof(char)); for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ refsp[k] = 1; gamma[i] = 1.0; } return; } /*********************************************************************** * eval_gamma - compute steepest edge coefficients * * This routine computes the vector of steepest edge coefficients for * all basic variables (except free ones) using its direct definition: * * gamma[i] = eta[i] + sum alfa[i,j]^2, i = 1,...,m, * j in C * * where eta[i] = 1 means that xB[i] is in the current reference space, * and 0 otherwise; C is a set of non-basic non-fixed variables xN[j], * which are in the current reference space; alfa[i,j] are elements of * the current simplex table. * * NOTE: The routine is intended only for debugginig purposes. */ static void eval_gamma(struct csa *csa, double gamma[]) { int m = csa->m; int n = csa->n; char *type = csa->type; int *head = csa->head; char *refsp = csa->refsp; double *alfa = csa->work3; double *h = csa->work3; int i, j, k; /* gamma[i] := eta[i] (or 1, if xB[i] is free) */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (type[k] == GLP_FR) gamma[i] = 1.0; else gamma[i] = (refsp[k] ? 1.0 : 0.0); } /* compute columns of the current simplex table */ for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* skip column, if xN[j] is not in C */ if (!refsp[k]) continue; #ifdef GLP_DEBUG /* set C must not contain fixed variables */ xassert(type[k] != GLP_FX); #endif /* construct the right-hand side vector h = - N[j] */ for (i = 1; i <= m; i++) h[i] = 0.0; if (k <= m) { /* N[j] is k-th column of submatrix I */ h[k] = -1.0; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] = A_val[ptr]; } /* solve system B * alfa = h */ xassert(csa->valid); bfd_ftran(csa->bfd, alfa); /* gamma[i] := gamma[i] + alfa[i,j]^2 */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ if (type[k] != GLP_FR) gamma[i] += alfa[i] * alfa[i]; } } return; } /*********************************************************************** * chuzr - choose basic variable (row of the simplex table) * * This routine chooses basic variable xB[p] having largest weighted * bound violation: * * |r[p]| / sqrt(gamma[p]) = max |r[i]| / sqrt(gamma[i]), * i in I * * / lB[i] - beta[i], if beta[i] < lB[i] * | * r[i] = < 0, if lB[i] <= beta[i] <= uB[i] * | * \ uB[i] - beta[i], if beta[i] > uB[i] * * where beta[i] is primal value of xB[i] in the current basis, lB[i] * and uB[i] are lower and upper bounds of xB[i], I is a subset of * eligible basic variables, which significantly violates their bounds, * gamma[i] is the steepest edge coefficient. * * If |r[i]| is less than a specified tolerance, xB[i] is not included * in I and therefore ignored. * * If I is empty and no variable has been chosen, p is set to 0. */ static void chuzr(struct csa *csa, double tol_bnd) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; int *head = csa->head; double *bbar = csa->bbar; double *gamma = csa->gamma; int i, k, p; double delta, best, eps, ri, temp; /* nothing is chosen so far */ p = 0, delta = 0.0, best = 0.0; /* look through the list of basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* determine bound violation ri[i] */ ri = 0.0; if (type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX) { /* xB[i] has lower bound */ eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); if (bbar[i] < lb[k] - eps) { /* and significantly violates it */ ri = lb[k] - bbar[i]; } } if (type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX) { /* xB[i] has upper bound */ eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); if (bbar[i] > ub[k] + eps) { /* and significantly violates it */ ri = ub[k] - bbar[i]; } } /* if xB[i] is not eligible, skip it */ if (ri == 0.0) continue; /* xB[i] is eligible basic variable; choose one with largest weighted bound violation */ #ifdef GLP_DEBUG xassert(gamma[i] >= 0.0); #endif temp = gamma[i]; if (temp < DBL_EPSILON) temp = DBL_EPSILON; temp = (ri * ri) / temp; if (best < temp) p = i, delta = ri, best = temp; } /* store the index of basic variable xB[p] chosen and its change in the adjacent basis */ csa->p = p; csa->delta = delta; return; } #if 1 /* copied from primal */ /*********************************************************************** * eval_rho - compute pivot row of the inverse * * This routine computes the pivot (p-th) row of the inverse inv(B), * which corresponds to basic variable xB[p] chosen: * * rho = inv(B') * e[p], * * where B' is a matrix transposed to the current basis matrix, e[p] * is unity vector. */ static void eval_rho(struct csa *csa, double rho[]) { int m = csa->m; int p = csa->p; double *e = rho; int i; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); #endif /* construct the right-hand side vector e[p] */ for (i = 1; i <= m; i++) e[i] = 0.0; e[p] = 1.0; /* solve system B'* rho = e[p] */ xassert(csa->valid); bfd_btran(csa->bfd, rho); return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * refine_rho - refine pivot row of the inverse * * This routine refines the pivot row of the inverse inv(B) assuming * that it was previously computed by the routine eval_rho. */ static void refine_rho(struct csa *csa, double rho[]) { int m = csa->m; int p = csa->p; double *e = csa->work3; int i; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); #endif /* construct the right-hand side vector e[p] */ for (i = 1; i <= m; i++) e[i] = 0.0; e[p] = 1.0; /* refine solution of B'* rho = e[p] */ refine_btran(csa, e, rho); return; } #endif #if 1 /* 06/IV-2009 */ /*********************************************************************** * eval_trow - compute pivot row of the simplex table * * This routine computes the pivot row of the simplex table, which * corresponds to basic variable xB[p] chosen. * * The pivot row is the following vector: * * trow = T'* e[p] = - N'* inv(B') * e[p] = - N' * rho, * * where rho is the pivot row of the inverse inv(B) previously computed * by the routine eval_rho. * * Note that elements of the pivot row corresponding to fixed non-basic * variables are not computed. * * NOTES * * Computing pivot row of the simplex table is one of the most time * consuming operations, and for some instances it may take more than * 50% of the total solution time. * * In the current implementation there are two routines to compute the * pivot row. The routine eval_trow1 computes elements of the pivot row * as inner products of columns of the matrix N and the vector rho; it * is used when the vector rho is relatively dense. The routine * eval_trow2 computes the pivot row as a linear combination of rows of * the matrix N; it is used when the vector rho is relatively sparse. */ static void eval_trow1(struct csa *csa, double rho[]) { int m = csa->m; int n = csa->n; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; char *stat = csa->stat; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int j, k, beg, end, ptr, nnz; double temp; /* compute the pivot row as inner products of columns of the matrix N and vector rho: trow[j] = - rho * N[j] */ nnz = 0; for (j = 1; j <= n; j++) { if (stat[j] == GLP_NS) { /* xN[j] is fixed */ trow_vec[j] = 0.0; continue; } k = head[m+j]; /* x[k] = xN[j] */ if (k <= m) { /* N[j] is k-th column of submatrix I */ temp = - rho[k]; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ beg = A_ptr[k-m], end = A_ptr[k-m+1]; temp = 0.0; for (ptr = beg; ptr < end; ptr++) temp += rho[A_ind[ptr]] * A_val[ptr]; } if (temp != 0.0) trow_ind[++nnz] = j; trow_vec[j] = temp; } csa->trow_nnz = nnz; return; } static void eval_trow2(struct csa *csa, double rho[]) { int m = csa->m; int n = csa->n; int *AT_ptr = csa->AT_ptr; int *AT_ind = csa->AT_ind; double *AT_val = csa->AT_val; int *bind = csa->bind; char *stat = csa->stat; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int i, j, beg, end, ptr, nnz; double temp; /* clear the pivot row */ for (j = 1; j <= n; j++) trow_vec[j] = 0.0; /* compute the pivot row as a linear combination of rows of the matrix N: trow = - rho[1] * N'[1] - ... - rho[m] * N'[m] */ for (i = 1; i <= m; i++) { temp = rho[i]; if (temp == 0.0) continue; /* trow := trow - rho[i] * N'[i] */ j = bind[i] - m; /* x[i] = xN[j] */ if (j >= 1 && stat[j] != GLP_NS) trow_vec[j] -= temp; beg = AT_ptr[i], end = AT_ptr[i+1]; for (ptr = beg; ptr < end; ptr++) { j = bind[m + AT_ind[ptr]] - m; /* x[k] = xN[j] */ if (j >= 1 && stat[j] != GLP_NS) trow_vec[j] += temp * AT_val[ptr]; } } /* construct sparse pattern of the pivot row */ nnz = 0; for (j = 1; j <= n; j++) { if (trow_vec[j] != 0.0) trow_ind[++nnz] = j; } csa->trow_nnz = nnz; return; } static void eval_trow(struct csa *csa, double rho[]) { int m = csa->m; int i, nnz; double dens; /* determine the density of the vector rho */ nnz = 0; for (i = 1; i <= m; i++) if (rho[i] != 0.0) nnz++; dens = (double)nnz / (double)m; if (dens >= 0.20) { /* rho is relatively dense */ eval_trow1(csa, rho); } else { /* rho is relatively sparse */ eval_trow2(csa, rho); } return; } #endif /*********************************************************************** * sort_trow - sort pivot row of the simplex table * * This routine reorders the list of non-zero elements of the pivot * row to put significant elements, whose magnitude is not less than * a specified tolerance, in front of the list, and stores the number * of significant elements in trow_num. */ static void sort_trow(struct csa *csa, double tol_piv) { #ifdef GLP_DEBUG int n = csa->n; char *stat = csa->stat; #endif int nnz = csa->trow_nnz; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int j, num, pos; double big, eps, temp; /* compute infinity (maximum) norm of the row */ big = 0.0; for (pos = 1; pos <= nnz; pos++) { #ifdef GLP_DEBUG j = trow_ind[pos]; xassert(1 <= j && j <= n); xassert(stat[j] != GLP_NS); #endif temp = fabs(trow_vec[trow_ind[pos]]); if (big < temp) big = temp; } csa->trow_max = big; /* determine absolute pivot tolerance */ eps = tol_piv * (1.0 + 0.01 * big); /* move significant row components to the front of the list */ for (num = 0; num < nnz; ) { j = trow_ind[nnz]; if (fabs(trow_vec[j]) < eps) nnz--; else { num++; trow_ind[nnz] = trow_ind[num]; trow_ind[num] = j; } } csa->trow_num = num; return; } #ifdef GLP_LONG_STEP /* 07/IV-2009 */ static int ls_func(const void *p1_, const void *p2_) { const struct bkpt *p1 = p1_, *p2 = p2_; if (p1->t < p2->t) return -1; if (p1->t > p2->t) return +1; return 0; } static int ls_func1(const void *p1_, const void *p2_) { const struct bkpt *p1 = p1_, *p2 = p2_; if (p1->dz < p2->dz) return -1; if (p1->dz > p2->dz) return +1; return 0; } static void long_step(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; int *head = csa->head; char *stat = csa->stat; double *cbar = csa->cbar; double delta = csa->delta; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int trow_num = csa->trow_num; struct bkpt *bkpt = csa->bkpt; int j, k, kk, nbps, pos; double alfa, s, slope, dzmax; /* delta > 0 means that xB[p] violates its lower bound, so to increase the dual objective lambdaB[p] must increase; delta < 0 means that xB[p] violates its upper bound, so to increase the dual objective lambdaB[p] must decrease */ /* s := sign(delta) */ s = (delta > 0.0 ? +1.0 : -1.0); /* determine breakpoints of the dual objective */ nbps = 0; for (pos = 1; pos <= trow_num; pos++) { j = trow_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); xassert(stat[j] != GLP_NS); #endif /* if there is free non-basic variable, switch to the standard ratio test */ if (stat[j] == GLP_NF) { nbps = 0; goto done; } /* lambdaN[j] = ... - alfa * t - ..., where t = s * lambdaB[i] is the dual ray parameter, t >= 0 */ alfa = s * trow_vec[j]; #ifdef GLP_DEBUG xassert(alfa != 0.0); xassert(stat[j] == GLP_NL || stat[j] == GLP_NU); #endif if (alfa > 0.0 && stat[j] == GLP_NL || alfa < 0.0 && stat[j] == GLP_NU) { /* either lambdaN[j] >= 0 (if stat = GLP_NL) and decreases or lambdaN[j] <= 0 (if stat = GLP_NU) and increases; in both cases we have a breakpoint */ nbps++; #ifdef GLP_DEBUG xassert(nbps <= n); #endif bkpt[nbps].j = j; bkpt[nbps].t = cbar[j] / alfa; /* if (stat[j] == GLP_NL && cbar[j] < 0.0 || stat[j] == GLP_NU && cbar[j] > 0.0) xprintf("%d %g\n", stat[j], cbar[j]); */ /* if t is negative, replace it by exact zero (see comments in the routine chuzc) */ if (bkpt[nbps].t < 0.0) bkpt[nbps].t = 0.0; } } /* if there are less than two breakpoints, switch to the standard ratio test */ if (nbps < 2) { nbps = 0; goto done; } /* sort breakpoints by ascending the dual ray parameter, t */ qsort(&bkpt[1], nbps, sizeof(struct bkpt), ls_func); /* determine last breakpoint, at which the dual objective still greater than at t = 0 */ dzmax = 0.0; slope = fabs(delta); /* initial slope */ for (kk = 1; kk <= nbps; kk++) { if (kk == 1) bkpt[kk].dz = 0.0 + slope * (bkpt[kk].t - 0.0); else bkpt[kk].dz = bkpt[kk-1].dz + slope * (bkpt[kk].t - bkpt[kk-1].t); if (dzmax < bkpt[kk].dz) dzmax = bkpt[kk].dz; else if (bkpt[kk].dz < 0.05 * (1.0 + dzmax)) { nbps = kk - 1; break; } j = bkpt[kk].j; k = head[m+j]; /* x[k] = xN[j] */ if (type[k] == GLP_DB) slope -= fabs(trow_vec[j]) * (ub[k] - lb[k]); else { nbps = kk; break; } } /* if there are less than two breakpoints, switch to the standard ratio test */ if (nbps < 2) { nbps = 0; goto done; } /* sort breakpoints by ascending the dual change, dz */ qsort(&bkpt[1], nbps, sizeof(struct bkpt), ls_func1); /* for (kk = 1; kk <= nbps; kk++) xprintf("%d; t = %g; dz = %g\n", kk, bkpt[kk].t, bkpt[kk].dz); */ done: csa->nbps = nbps; return; } #endif /*********************************************************************** * chuzc - choose non-basic variable (column of the simplex table) * * This routine chooses non-basic variable xN[q], which being entered * in the basis keeps dual feasibility of the basic solution. * * The parameter rtol is a relative tolerance used to relax zero bounds * of reduced costs of non-basic variables. If rtol = 0, the routine * implements the standard ratio test. Otherwise, if rtol > 0, the * routine implements Harris' two-pass ratio test. In the latter case * rtol should be about three times less than a tolerance used to check * dual feasibility. */ static void chuzc(struct csa *csa, double rtol) { #ifdef GLP_DEBUG int m = csa->m; int n = csa->n; #endif char *stat = csa->stat; double *cbar = csa->cbar; #ifdef GLP_DEBUG int p = csa->p; #endif double delta = csa->delta; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int trow_num = csa->trow_num; int j, pos, q; double alfa, big, s, t, teta, tmax; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); #endif /* delta > 0 means that xB[p] violates its lower bound and goes to it in the adjacent basis, so lambdaB[p] is increasing from its lower zero bound; delta < 0 means that xB[p] violates its upper bound and goes to it in the adjacent basis, so lambdaB[p] is decreasing from its upper zero bound */ #ifdef GLP_DEBUG xassert(delta != 0.0); #endif /* s := sign(delta) */ s = (delta > 0.0 ? +1.0 : -1.0); /*** FIRST PASS ***/ /* nothing is chosen so far */ q = 0, teta = DBL_MAX, big = 0.0; /* walk through significant elements of the pivot row */ for (pos = 1; pos <= trow_num; pos++) { j = trow_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif alfa = s * trow_vec[j]; #ifdef GLP_DEBUG xassert(alfa != 0.0); #endif /* lambdaN[j] = ... - alfa * lambdaB[p] - ..., and due to s we need to consider only increasing lambdaB[p] */ if (alfa > 0.0) { /* lambdaN[j] is decreasing */ if (stat[j] == GLP_NL || stat[j] == GLP_NF) { /* lambdaN[j] has zero lower bound */ t = (cbar[j] + rtol) / alfa; } else { /* lambdaN[j] has no lower bound */ continue; } } else { /* lambdaN[j] is increasing */ if (stat[j] == GLP_NU || stat[j] == GLP_NF) { /* lambdaN[j] has zero upper bound */ t = (cbar[j] - rtol) / alfa; } else { /* lambdaN[j] has no upper bound */ continue; } } /* t is a change of lambdaB[p], on which lambdaN[j] reaches its zero bound (possibly relaxed); since the basic solution is assumed to be dual feasible, t has to be non-negative by definition; however, it may happen that lambdaN[j] slightly (i.e. within a tolerance) violates its zero bound, that leads to negative t; in the latter case, if xN[j] is chosen, negative t means that lambdaB[p] changes in wrong direction that may cause wrong results on updating reduced costs; thus, if t is negative, we should replace it by exact zero assuming that lambdaN[j] is exactly on its zero bound, and violation appears due to round-off errors */ if (t < 0.0) t = 0.0; /* apply minimal ratio test */ if (teta > t || teta == t && big < fabs(alfa)) q = j, teta = t, big = fabs(alfa); } /* the second pass is skipped in the following cases: */ /* if the standard ratio test is used */ if (rtol == 0.0) goto done; /* if no non-basic variable has been chosen on the first pass */ if (q == 0) goto done; /* if lambdaN[q] prevents lambdaB[p] from any change */ if (teta == 0.0) goto done; /*** SECOND PASS ***/ /* here tmax is a maximal change of lambdaB[p], on which the solution remains dual feasible within a tolerance */ #if 0 tmax = (1.0 + 10.0 * DBL_EPSILON) * teta; #else tmax = teta; #endif /* nothing is chosen so far */ q = 0, teta = DBL_MAX, big = 0.0; /* walk through significant elements of the pivot row */ for (pos = 1; pos <= trow_num; pos++) { j = trow_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif alfa = s * trow_vec[j]; #ifdef GLP_DEBUG xassert(alfa != 0.0); #endif /* lambdaN[j] = ... - alfa * lambdaB[p] - ..., and due to s we need to consider only increasing lambdaB[p] */ if (alfa > 0.0) { /* lambdaN[j] is decreasing */ if (stat[j] == GLP_NL || stat[j] == GLP_NF) { /* lambdaN[j] has zero lower bound */ t = cbar[j] / alfa; } else { /* lambdaN[j] has no lower bound */ continue; } } else { /* lambdaN[j] is increasing */ if (stat[j] == GLP_NU || stat[j] == GLP_NF) { /* lambdaN[j] has zero upper bound */ t = cbar[j] / alfa; } else { /* lambdaN[j] has no upper bound */ continue; } } /* (see comments for the first pass) */ if (t < 0.0) t = 0.0; /* t is a change of lambdaB[p], on which lambdaN[j] reaches its zero (lower or upper) bound; if t <= tmax, all reduced costs can violate their zero bounds only within relaxation tolerance rtol, so we can choose non-basic variable having largest influence coefficient to avoid possible numerical instability */ if (t <= tmax && big < fabs(alfa)) q = j, teta = t, big = fabs(alfa); } /* something must be chosen on the second pass */ xassert(q != 0); done: /* store the index of non-basic variable xN[q] chosen */ csa->q = q; /* store reduced cost of xN[q] in the adjacent basis */ csa->new_dq = s * teta; return; } #if 1 /* copied from primal */ /*********************************************************************** * eval_tcol - compute pivot column of the simplex table * * This routine computes the pivot column of the simplex table, which * corresponds to non-basic variable xN[q] chosen. * * The pivot column is the following vector: * * tcol = T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], * * where B is the current basis matrix, N[q] is a column of the matrix * (I|-A) corresponding to variable xN[q]. */ static void eval_tcol(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *head = csa->head; int q = csa->q; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; double *h = csa->tcol_vec; int i, k, nnz; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif k = head[m+q]; /* x[k] = xN[q] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* construct the right-hand side vector h = - N[q] */ for (i = 1; i <= m; i++) h[i] = 0.0; if (k <= m) { /* N[q] is k-th column of submatrix I */ h[k] = -1.0; } else { /* N[q] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] = A_val[ptr]; } /* solve system B * tcol = h */ xassert(csa->valid); bfd_ftran(csa->bfd, tcol_vec); /* construct sparse pattern of the pivot column */ nnz = 0; for (i = 1; i <= m; i++) { if (tcol_vec[i] != 0.0) tcol_ind[++nnz] = i; } csa->tcol_nnz = nnz; return; } #endif #if 1 /* copied from primal */ /*********************************************************************** * refine_tcol - refine pivot column of the simplex table * * This routine refines the pivot column of the simplex table assuming * that it was previously computed by the routine eval_tcol. */ static void refine_tcol(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *head = csa->head; int q = csa->q; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; double *h = csa->work3; int i, k, nnz; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif k = head[m+q]; /* x[k] = xN[q] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* construct the right-hand side vector h = - N[q] */ for (i = 1; i <= m; i++) h[i] = 0.0; if (k <= m) { /* N[q] is k-th column of submatrix I */ h[k] = -1.0; } else { /* N[q] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] = A_val[ptr]; } /* refine solution of B * tcol = h */ refine_ftran(csa, h, tcol_vec); /* construct sparse pattern of the pivot column */ nnz = 0; for (i = 1; i <= m; i++) { if (tcol_vec[i] != 0.0) tcol_ind[++nnz] = i; } csa->tcol_nnz = nnz; return; } #endif /*********************************************************************** * update_cbar - update reduced costs of non-basic variables * * This routine updates reduced costs of all (except fixed) non-basic * variables for the adjacent basis. */ static void update_cbar(struct csa *csa) { #ifdef GLP_DEBUG int n = csa->n; #endif double *cbar = csa->cbar; int trow_nnz = csa->trow_nnz; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int q = csa->q; double new_dq = csa->new_dq; int j, pos; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif /* set new reduced cost of xN[q] */ cbar[q] = new_dq; /* update reduced costs of other non-basic variables */ if (new_dq == 0.0) goto done; for (pos = 1; pos <= trow_nnz; pos++) { j = trow_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif if (j != q) cbar[j] -= trow_vec[j] * new_dq; } done: return; } /*********************************************************************** * update_bbar - update values of basic variables * * This routine updates values of all basic variables for the adjacent * basis. */ static void update_bbar(struct csa *csa) { #ifdef GLP_DEBUG int m = csa->m; int n = csa->n; #endif double *bbar = csa->bbar; int p = csa->p; double delta = csa->delta; int q = csa->q; int tcol_nnz = csa->tcol_nnz; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; int i, pos; double teta; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); xassert(1 <= q && q <= n); #endif /* determine the change of xN[q] in the adjacent basis */ #ifdef GLP_DEBUG xassert(tcol_vec[p] != 0.0); #endif teta = delta / tcol_vec[p]; /* set new primal value of xN[q] */ bbar[p] = get_xN(csa, q) + teta; /* update primal values of other basic variables */ if (teta == 0.0) goto done; for (pos = 1; pos <= tcol_nnz; pos++) { i = tcol_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); #endif if (i != p) bbar[i] += tcol_vec[i] * teta; } done: return; } /*********************************************************************** * update_gamma - update steepest edge coefficients * * This routine updates steepest-edge coefficients for the adjacent * basis. */ static void update_gamma(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; int *head = csa->head; char *refsp = csa->refsp; double *gamma = csa->gamma; int p = csa->p; int trow_nnz = csa->trow_nnz; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int q = csa->q; int tcol_nnz = csa->tcol_nnz; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; double *u = csa->work3; int i, j, k,pos; double gamma_p, eta_p, pivot, t, t1, t2; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); xassert(1 <= q && q <= n); #endif /* the basis changes, so decrease the count */ xassert(csa->refct > 0); csa->refct--; /* recompute gamma[p] for the current basis more accurately and compute auxiliary vector u */ #ifdef GLP_DEBUG xassert(type[head[p]] != GLP_FR); #endif gamma_p = eta_p = (refsp[head[p]] ? 1.0 : 0.0); for (i = 1; i <= m; i++) u[i] = 0.0; for (pos = 1; pos <= trow_nnz; pos++) { j = trow_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); xassert(type[k] != GLP_FX); #endif if (!refsp[k]) continue; t = trow_vec[j]; gamma_p += t * t; /* u := u + N[j] * delta[j] * trow[j] */ if (k <= m) { /* N[k] = k-j stolbec submatrix I */ u[k] += t; } else { /* N[k] = k-m-k stolbec (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) u[A_ind[ptr]] -= t * A_val[ptr]; } } xassert(csa->valid); bfd_ftran(csa->bfd, u); /* update gamma[i] for other basic variables (except xB[p] and free variables) */ pivot = tcol_vec[p]; #ifdef GLP_DEBUG xassert(pivot != 0.0); #endif for (pos = 1; pos <= tcol_nnz; pos++) { i = tcol_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); #endif k = head[i]; #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* skip xB[p] */ if (i == p) continue; /* skip free basic variable */ if (type[head[i]] == GLP_FR) { #ifdef GLP_DEBUG xassert(gamma[i] == 1.0); #endif continue; } /* compute gamma[i] for the adjacent basis */ t = tcol_vec[i] / pivot; t1 = gamma[i] + t * t * gamma_p + 2.0 * t * u[i]; t2 = (refsp[k] ? 1.0 : 0.0) + eta_p * t * t; gamma[i] = (t1 >= t2 ? t1 : t2); /* (though gamma[i] can be exact zero, because the reference space does not include non-basic fixed variables) */ if (gamma[i] < DBL_EPSILON) gamma[i] = DBL_EPSILON; } /* compute gamma[p] for the adjacent basis */ if (type[head[m+q]] == GLP_FR) gamma[p] = 1.0; else { gamma[p] = gamma_p / (pivot * pivot); if (gamma[p] < DBL_EPSILON) gamma[p] = DBL_EPSILON; } /* if xB[p], which becomes xN[q] in the adjacent basis, is fixed and belongs to the reference space, remove it from there, and change all gamma's appropriately */ k = head[p]; if (type[k] == GLP_FX && refsp[k]) { refsp[k] = 0; for (pos = 1; pos <= tcol_nnz; pos++) { i = tcol_ind[pos]; if (i == p) { if (type[head[m+q]] == GLP_FR) continue; t = 1.0 / tcol_vec[p]; } else { if (type[head[i]] == GLP_FR) continue; t = tcol_vec[i] / tcol_vec[p]; } gamma[i] -= t * t; if (gamma[i] < DBL_EPSILON) gamma[i] = DBL_EPSILON; } } return; } #if 1 /* copied from primal */ /*********************************************************************** * err_in_bbar - compute maximal relative error in primal solution * * This routine returns maximal relative error: * * max |beta[i] - bbar[i]| / (1 + |beta[i]|), * * where beta and bbar are, respectively, directly computed and the * current (updated) values of basic variables. * * NOTE: The routine is intended only for debugginig purposes. */ static double err_in_bbar(struct csa *csa) { int m = csa->m; double *bbar = csa->bbar; int i; double e, emax, *beta; beta = xcalloc(1+m, sizeof(double)); eval_beta(csa, beta); emax = 0.0; for (i = 1; i <= m; i++) { e = fabs(beta[i] - bbar[i]) / (1.0 + fabs(beta[i])); if (emax < e) emax = e; } xfree(beta); return emax; } #endif #if 1 /* copied from primal */ /*********************************************************************** * err_in_cbar - compute maximal relative error in dual solution * * This routine returns maximal relative error: * * max |cost[j] - cbar[j]| / (1 + |cost[j]|), * * where cost and cbar are, respectively, directly computed and the * current (updated) reduced costs of non-basic non-fixed variables. * * NOTE: The routine is intended only for debugginig purposes. */ static double err_in_cbar(struct csa *csa) { int m = csa->m; int n = csa->n; char *stat = csa->stat; double *cbar = csa->cbar; int j; double e, emax, cost, *pi; pi = xcalloc(1+m, sizeof(double)); eval_pi(csa, pi); emax = 0.0; for (j = 1; j <= n; j++) { if (stat[j] == GLP_NS) continue; cost = eval_cost(csa, pi, j); e = fabs(cost - cbar[j]) / (1.0 + fabs(cost)); if (emax < e) emax = e; } xfree(pi); return emax; } #endif /*********************************************************************** * err_in_gamma - compute maximal relative error in steepest edge cff. * * This routine returns maximal relative error: * * max |gamma'[j] - gamma[j]| / (1 + |gamma'[j]), * * where gamma'[j] and gamma[j] are, respectively, directly computed * and the current (updated) steepest edge coefficients for non-basic * non-fixed variable x[j]. * * NOTE: The routine is intended only for debugginig purposes. */ static double err_in_gamma(struct csa *csa) { int m = csa->m; char *type = csa->type; int *head = csa->head; double *gamma = csa->gamma; double *exact = csa->work4; int i; double e, emax, temp; eval_gamma(csa, exact); emax = 0.0; for (i = 1; i <= m; i++) { if (type[head[i]] == GLP_FR) { xassert(gamma[i] == 1.0); xassert(exact[i] == 1.0); continue; } temp = exact[i]; e = fabs(temp - gamma[i]) / (1.0 + fabs(temp)); if (emax < e) emax = e; } return emax; } /*********************************************************************** * change_basis - change basis header * * This routine changes the basis header to make it corresponding to * the adjacent basis. */ static void change_basis(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; int *head = csa->head; #if 1 /* 06/IV-2009 */ int *bind = csa->bind; #endif char *stat = csa->stat; int p = csa->p; double delta = csa->delta; int q = csa->q; int k; /* xB[p] leaves the basis, xN[q] enters the basis */ #ifdef GLP_DEBUG xassert(1 <= p && p <= m); xassert(1 <= q && q <= n); #endif /* xB[p] <-> xN[q] */ k = head[p], head[p] = head[m+q], head[m+q] = k; #if 1 /* 06/IV-2009 */ bind[head[p]] = p, bind[head[m+q]] = m + q; #endif if (type[k] == GLP_FX) stat[q] = GLP_NS; else if (delta > 0.0) { #ifdef GLP_DEBUG xassert(type[k] == GLP_LO || type[k] == GLP_DB); #endif stat[q] = GLP_NL; } else /* delta < 0.0 */ { #ifdef GLP_DEBUG xassert(type[k] == GLP_UP || type[k] == GLP_DB); #endif stat[q] = GLP_NU; } return; } /*********************************************************************** * check_feas - check dual feasibility of basic solution * * If the current basic solution is dual feasible within a tolerance, * this routine returns zero, otherwise it returns non-zero. */ static int check_feas(struct csa *csa, double tol_dj) { int m = csa->m; int n = csa->n; char *orig_type = csa->orig_type; int *head = csa->head; double *cbar = csa->cbar; int j, k; for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (cbar[j] < - tol_dj) if (orig_type[k] == GLP_LO || orig_type[k] == GLP_FR) return 1; if (cbar[j] > + tol_dj) if (orig_type[k] == GLP_UP || orig_type[k] == GLP_FR) return 1; } return 0; } /*********************************************************************** * set_aux_bnds - assign auxiliary bounds to variables * * This routine assigns auxiliary bounds to variables to construct an * LP problem solved on phase I. */ static void set_aux_bnds(struct csa *csa) { int m = csa->m; int n = csa->n; char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; char *orig_type = csa->orig_type; int *head = csa->head; char *stat = csa->stat; double *cbar = csa->cbar; int j, k; for (k = 1; k <= m+n; k++) { switch (orig_type[k]) { case GLP_FR: #if 0 type[k] = GLP_DB, lb[k] = -1.0, ub[k] = +1.0; #else /* to force free variables to enter the basis */ type[k] = GLP_DB, lb[k] = -1e3, ub[k] = +1e3; #endif break; case GLP_LO: type[k] = GLP_DB, lb[k] = 0.0, ub[k] = +1.0; break; case GLP_UP: type[k] = GLP_DB, lb[k] = -1.0, ub[k] = 0.0; break; case GLP_DB: case GLP_FX: type[k] = GLP_FX, lb[k] = ub[k] = 0.0; break; default: xassert(orig_type != orig_type); } } for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (type[k] == GLP_FX) stat[j] = GLP_NS; else if (cbar[j] >= 0.0) stat[j] = GLP_NL; else stat[j] = GLP_NU; } return; } /*********************************************************************** * set_orig_bnds - restore original bounds of variables * * This routine restores original types and bounds of variables and * determines statuses of non-basic variables assuming that the current * basis is dual feasible. */ static void set_orig_bnds(struct csa *csa) { int m = csa->m; int n = csa->n; char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; char *orig_type = csa->orig_type; double *orig_lb = csa->orig_lb; double *orig_ub = csa->orig_ub; int *head = csa->head; char *stat = csa->stat; double *cbar = csa->cbar; int j, k; memcpy(&type[1], &orig_type[1], (m+n) * sizeof(char)); memcpy(&lb[1], &orig_lb[1], (m+n) * sizeof(double)); memcpy(&ub[1], &orig_ub[1], (m+n) * sizeof(double)); for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif switch (type[k]) { case GLP_FR: stat[j] = GLP_NF; break; case GLP_LO: stat[j] = GLP_NL; break; case GLP_UP: stat[j] = GLP_NU; break; case GLP_DB: if (cbar[j] >= +DBL_EPSILON) stat[j] = GLP_NL; else if (cbar[j] <= -DBL_EPSILON) stat[j] = GLP_NU; else if (fabs(lb[k]) <= fabs(ub[k])) stat[j] = GLP_NL; else stat[j] = GLP_NU; break; case GLP_FX: stat[j] = GLP_NS; break; default: xassert(type != type); } } return; } /*********************************************************************** * check_stab - check numerical stability of basic solution * * If the current basic solution is dual feasible within a tolerance, * this routine returns zero, otherwise it returns non-zero. */ static int check_stab(struct csa *csa, double tol_dj) { int n = csa->n; char *stat = csa->stat; double *cbar = csa->cbar; int j; for (j = 1; j <= n; j++) { if (cbar[j] < - tol_dj) if (stat[j] == GLP_NL || stat[j] == GLP_NF) return 1; if (cbar[j] > + tol_dj) if (stat[j] == GLP_NU || stat[j] == GLP_NF) return 1; } return 0; } #if 1 /* copied from primal */ /*********************************************************************** * eval_obj - compute original objective function * * This routine computes the current value of the original objective * function. */ static double eval_obj(struct csa *csa) { int m = csa->m; int n = csa->n; double *obj = csa->obj; int *head = csa->head; double *bbar = csa->bbar; int i, j, k; double sum; sum = obj[0]; /* walk through the list of basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k > m) sum += obj[k-m] * bbar[i]; } /* walk through the list of non-basic variables */ for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k > m) sum += obj[k-m] * get_xN(csa, j); } return sum; } #endif /*********************************************************************** * display - display the search progress * * This routine displays some information about the search progress. */ static void display(struct csa *csa, const glp_smcp *parm, int spec) { int m = csa->m; int n = csa->n; double *coef = csa->coef; char *orig_type = csa->orig_type; int *head = csa->head; char *stat = csa->stat; int phase = csa->phase; double *bbar = csa->bbar; double *cbar = csa->cbar; int i, j, cnt; double sum; if (parm->msg_lev < GLP_MSG_ON) goto skip; if (parm->out_dly > 0 && 1000.0 * xdifftime(xtime(), csa->tm_beg) < parm->out_dly) goto skip; if (csa->it_cnt == csa->it_dpy) goto skip; if (!spec && csa->it_cnt % parm->out_frq != 0) goto skip; /* compute the sum of dual infeasibilities */ sum = 0.0; if (phase == 1) { for (i = 1; i <= m; i++) sum -= coef[head[i]] * bbar[i]; for (j = 1; j <= n; j++) sum -= coef[head[m+j]] * get_xN(csa, j); } else { for (j = 1; j <= n; j++) { if (cbar[j] < 0.0) if (stat[j] == GLP_NL || stat[j] == GLP_NF) sum -= cbar[j]; if (cbar[j] > 0.0) if (stat[j] == GLP_NU || stat[j] == GLP_NF) sum += cbar[j]; } } /* determine the number of basic fixed variables */ cnt = 0; for (i = 1; i <= m; i++) if (orig_type[head[i]] == GLP_FX) cnt++; if (csa->phase == 1) xprintf(" %6d: %24s infeas = %10.3e (%d)\n", csa->it_cnt, "", sum, cnt); else xprintf("|%6d: obj = %17.9e infeas = %10.3e (%d)\n", csa->it_cnt, eval_obj(csa), sum, cnt); csa->it_dpy = csa->it_cnt; skip: return; } #if 1 /* copied from primal */ /*********************************************************************** * store_sol - store basic solution back to the problem object * * This routine stores basic solution components back to the problem * object. */ static void store_sol(struct csa *csa, glp_prob *lp, int p_stat, int d_stat, int ray) { int m = csa->m; int n = csa->n; double zeta = csa->zeta; int *head = csa->head; char *stat = csa->stat; double *bbar = csa->bbar; double *cbar = csa->cbar; int i, j, k; #ifdef GLP_DEBUG xassert(lp->m == m); xassert(lp->n == n); #endif /* basis factorization */ #ifdef GLP_DEBUG xassert(!lp->valid && lp->bfd == NULL); xassert(csa->valid && csa->bfd != NULL); #endif lp->valid = 1, csa->valid = 0; lp->bfd = csa->bfd, csa->bfd = NULL; memcpy(&lp->head[1], &head[1], m * sizeof(int)); /* basic solution status */ lp->pbs_stat = p_stat; lp->dbs_stat = d_stat; /* objective function value */ lp->obj_val = eval_obj(csa); /* simplex iteration count */ lp->it_cnt = csa->it_cnt; /* unbounded ray */ lp->some = ray; /* basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { GLPROW *row = lp->row[k]; row->stat = GLP_BS; row->bind = i; row->prim = bbar[i] / row->rii; row->dual = 0.0; } else { GLPCOL *col = lp->col[k-m]; col->stat = GLP_BS; col->bind = i; col->prim = bbar[i] * col->sjj; col->dual = 0.0; } } /* non-basic variables */ for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { GLPROW *row = lp->row[k]; row->stat = stat[j]; row->bind = 0; #if 0 row->prim = get_xN(csa, j) / row->rii; #else switch (stat[j]) { case GLP_NL: row->prim = row->lb; break; case GLP_NU: row->prim = row->ub; break; case GLP_NF: row->prim = 0.0; break; case GLP_NS: row->prim = row->lb; break; default: xassert(stat != stat); } #endif row->dual = (cbar[j] * row->rii) / zeta; } else { GLPCOL *col = lp->col[k-m]; col->stat = stat[j]; col->bind = 0; #if 0 col->prim = get_xN(csa, j) * col->sjj; #else switch (stat[j]) { case GLP_NL: col->prim = col->lb; break; case GLP_NU: col->prim = col->ub; break; case GLP_NF: col->prim = 0.0; break; case GLP_NS: col->prim = col->lb; break; default: xassert(stat != stat); } #endif col->dual = (cbar[j] / col->sjj) / zeta; } } return; } #endif /*********************************************************************** * free_csa - deallocate common storage area * * This routine frees all the memory allocated to arrays in the common * storage area (CSA). */ static void free_csa(struct csa *csa) { xfree(csa->type); xfree(csa->lb); xfree(csa->ub); xfree(csa->coef); xfree(csa->orig_type); xfree(csa->orig_lb); xfree(csa->orig_ub); xfree(csa->obj); xfree(csa->A_ptr); xfree(csa->A_ind); xfree(csa->A_val); #if 1 /* 06/IV-2009 */ xfree(csa->AT_ptr); xfree(csa->AT_ind); xfree(csa->AT_val); #endif xfree(csa->head); #if 1 /* 06/IV-2009 */ xfree(csa->bind); #endif xfree(csa->stat); #if 0 /* 06/IV-2009 */ xfree(csa->N_ptr); xfree(csa->N_len); xfree(csa->N_ind); xfree(csa->N_val); #endif xfree(csa->bbar); xfree(csa->cbar); xfree(csa->refsp); xfree(csa->gamma); xfree(csa->trow_ind); xfree(csa->trow_vec); #ifdef GLP_LONG_STEP /* 07/IV-2009 */ xfree(csa->bkpt); #endif xfree(csa->tcol_ind); xfree(csa->tcol_vec); xfree(csa->work1); xfree(csa->work2); xfree(csa->work3); xfree(csa->work4); xfree(csa); return; } /*********************************************************************** * spx_dual - core LP solver based on the dual simplex method * * SYNOPSIS * * #include "glpspx.h" * int spx_dual(glp_prob *lp, const glp_smcp *parm); * * DESCRIPTION * * The routine spx_dual is a core LP solver based on the two-phase dual * simplex method. * * RETURNS * * 0 LP instance has been successfully solved. * * GLP_EOBJLL * Objective lower limit has been reached (maximization). * * GLP_EOBJUL * Objective upper limit has been reached (minimization). * * GLP_EITLIM * Iteration limit has been exhausted. * * GLP_ETMLIM * Time limit has been exhausted. * * GLP_EFAIL * The solver failed to solve LP instance. */ int spx_dual(glp_prob *lp, const glp_smcp *parm) { struct csa *csa; int binv_st = 2; /* status of basis matrix factorization: 0 - invalid; 1 - just computed; 2 - updated */ int bbar_st = 0; /* status of primal values of basic variables: 0 - invalid; 1 - just computed; 2 - updated */ int cbar_st = 0; /* status of reduced costs of non-basic variables: 0 - invalid; 1 - just computed; 2 - updated */ int rigorous = 0; /* rigorous mode flag; this flag is used to enable iterative refinement on computing pivot rows and columns of the simplex table */ int check = 0; int p_stat, d_stat, ret; /* allocate and initialize the common storage area */ csa = alloc_csa(lp); init_csa(csa, lp); if (parm->msg_lev >= GLP_MSG_DBG) xprintf("Objective scale factor = %g\n", csa->zeta); loop: /* main loop starts here */ /* compute factorization of the basis matrix */ if (binv_st == 0) { ret = invert_B(csa); if (ret != 0) { if (parm->msg_lev >= GLP_MSG_ERR) { xprintf("Error: unable to factorize the basis matrix (%d" ")\n", ret); xprintf("Sorry, basis recovery procedure not implemented" " yet\n"); } xassert(!lp->valid && lp->bfd == NULL); lp->bfd = csa->bfd, csa->bfd = NULL; lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; lp->obj_val = 0.0; lp->it_cnt = csa->it_cnt; lp->some = 0; ret = GLP_EFAIL; goto done; } csa->valid = 1; binv_st = 1; /* just computed */ /* invalidate basic solution components */ bbar_st = cbar_st = 0; } /* compute reduced costs of non-basic variables */ if (cbar_st == 0) { eval_cbar(csa); cbar_st = 1; /* just computed */ /* determine the search phase, if not determined yet */ if (csa->phase == 0) { if (check_feas(csa, 0.90 * parm->tol_dj) != 0) { /* current basic solution is dual infeasible */ /* start searching for dual feasible solution */ csa->phase = 1; set_aux_bnds(csa); } else { /* current basic solution is dual feasible */ /* start searching for optimal solution */ csa->phase = 2; set_orig_bnds(csa); } xassert(check_stab(csa, parm->tol_dj) == 0); /* some non-basic double-bounded variables might become fixed (on phase I) or vice versa (on phase II) */ #if 0 /* 06/IV-2009 */ build_N(csa); #endif csa->refct = 0; /* bounds of non-basic variables have been changed, so invalidate primal values */ bbar_st = 0; } /* make sure that the current basic solution remains dual feasible */ if (check_stab(csa, parm->tol_dj) != 0) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("Warning: numerical instability (dual simplex, p" "hase %s)\n", csa->phase == 1 ? "I" : "II"); #if 1 if (parm->meth == GLP_DUALP) { store_sol(csa, lp, GLP_UNDEF, GLP_UNDEF, 0); ret = GLP_EFAIL; goto done; } #endif /* restart the search */ csa->phase = 0; binv_st = 0; rigorous = 5; goto loop; } } xassert(csa->phase == 1 || csa->phase == 2); /* on phase I we do not need to wait until the current basic solution becomes primal feasible; it is sufficient to make sure that all reduced costs have correct signs */ if (csa->phase == 1 && check_feas(csa, parm->tol_dj) == 0) { /* the current basis is dual feasible; switch to phase II */ display(csa, parm, 1); csa->phase = 2; if (cbar_st != 1) { eval_cbar(csa); cbar_st = 1; } set_orig_bnds(csa); #if 0 /* 06/IV-2009 */ build_N(csa); #endif csa->refct = 0; bbar_st = 0; } /* compute primal values of basic variables */ if (bbar_st == 0) { eval_bbar(csa); if (csa->phase == 2) csa->bbar[0] = eval_obj(csa); bbar_st = 1; /* just computed */ } /* redefine the reference space, if required */ switch (parm->pricing) { case GLP_PT_STD: break; case GLP_PT_PSE: if (csa->refct == 0) reset_refsp(csa); break; default: xassert(parm != parm); } /* at this point the basis factorization and all basic solution components are valid */ xassert(binv_st && bbar_st && cbar_st); /* check accuracy of current basic solution components (only for debugging) */ if (check) { double e_bbar = err_in_bbar(csa); double e_cbar = err_in_cbar(csa); double e_gamma = (parm->pricing == GLP_PT_PSE ? err_in_gamma(csa) : 0.0); xprintf("e_bbar = %10.3e; e_cbar = %10.3e; e_gamma = %10.3e\n", e_bbar, e_cbar, e_gamma); xassert(e_bbar <= 1e-5 && e_cbar <= 1e-5 && e_gamma <= 1e-3); } /* if the objective has to be maximized, check if it has reached its lower limit */ if (csa->phase == 2 && csa->zeta < 0.0 && parm->obj_ll > -DBL_MAX && csa->bbar[0] <= parm->obj_ll) { if (bbar_st != 1 || cbar_st != 1) { if (bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); if (parm->msg_lev >= GLP_MSG_ALL) xprintf("OBJECTIVE LOWER LIMIT REACHED; SEARCH TERMINATED\n" ); store_sol(csa, lp, GLP_INFEAS, GLP_FEAS, 0); ret = GLP_EOBJLL; goto done; } /* if the objective has to be minimized, check if it has reached its upper limit */ if (csa->phase == 2 && csa->zeta > 0.0 && parm->obj_ul < +DBL_MAX && csa->bbar[0] >= parm->obj_ul) { if (bbar_st != 1 || cbar_st != 1) { if (bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); if (parm->msg_lev >= GLP_MSG_ALL) xprintf("OBJECTIVE UPPER LIMIT REACHED; SEARCH TERMINATED\n" ); store_sol(csa, lp, GLP_INFEAS, GLP_FEAS, 0); ret = GLP_EOBJUL; goto done; } /* check if the iteration limit has been exhausted */ if (parm->it_lim < INT_MAX && csa->it_cnt - csa->it_beg >= parm->it_lim) { if (csa->phase == 2 && bbar_st != 1 || cbar_st != 1) { if (csa->phase == 2 && bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); if (parm->msg_lev >= GLP_MSG_ALL) xprintf("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED\n"); switch (csa->phase) { case 1: d_stat = GLP_INFEAS; set_orig_bnds(csa); eval_bbar(csa); break; case 2: d_stat = GLP_FEAS; break; default: xassert(csa != csa); } store_sol(csa, lp, GLP_INFEAS, d_stat, 0); ret = GLP_EITLIM; goto done; } /* check if the time limit has been exhausted */ if (parm->tm_lim < INT_MAX && 1000.0 * xdifftime(xtime(), csa->tm_beg) >= parm->tm_lim) { if (csa->phase == 2 && bbar_st != 1 || cbar_st != 1) { if (csa->phase == 2 && bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); if (parm->msg_lev >= GLP_MSG_ALL) xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); switch (csa->phase) { case 1: d_stat = GLP_INFEAS; set_orig_bnds(csa); eval_bbar(csa); break; case 2: d_stat = GLP_FEAS; break; default: xassert(csa != csa); } store_sol(csa, lp, GLP_INFEAS, d_stat, 0); ret = GLP_ETMLIM; goto done; } /* display the search progress */ display(csa, parm, 0); /* choose basic variable xB[p] */ chuzr(csa, parm->tol_bnd); if (csa->p == 0) { if (bbar_st != 1 || cbar_st != 1) { if (bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); switch (csa->phase) { case 1: if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n"); set_orig_bnds(csa); eval_bbar(csa); p_stat = GLP_INFEAS, d_stat = GLP_NOFEAS; break; case 2: if (parm->msg_lev >= GLP_MSG_ALL) xprintf("OPTIMAL SOLUTION FOUND\n"); p_stat = d_stat = GLP_FEAS; break; default: xassert(csa != csa); } store_sol(csa, lp, p_stat, d_stat, 0); ret = 0; goto done; } /* compute pivot row of the simplex table */ { double *rho = csa->work4; eval_rho(csa, rho); if (rigorous) refine_rho(csa, rho); eval_trow(csa, rho); sort_trow(csa, parm->tol_bnd); } /* unlike primal simplex there is no need to check accuracy of the primal value of xB[p] (which might be computed using the pivot row), since bbar is a result of FTRAN */ #ifdef GLP_LONG_STEP /* 07/IV-2009 */ long_step(csa); if (csa->nbps > 0) { csa->q = csa->bkpt[csa->nbps].j; if (csa->delta > 0.0) csa->new_dq = + csa->bkpt[csa->nbps].t; else csa->new_dq = - csa->bkpt[csa->nbps].t; } else #endif /* choose non-basic variable xN[q] */ switch (parm->r_test) { case GLP_RT_STD: chuzc(csa, 0.0); break; case GLP_RT_HAR: chuzc(csa, 0.30 * parm->tol_dj); break; default: xassert(parm != parm); } if (csa->q == 0) { if (bbar_st != 1 || cbar_st != 1 || !rigorous) { if (bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; rigorous = 1; goto loop; } display(csa, parm, 1); switch (csa->phase) { case 1: if (parm->msg_lev >= GLP_MSG_ERR) xprintf("Error: unable to choose basic variable on ph" "ase I\n"); xassert(!lp->valid && lp->bfd == NULL); lp->bfd = csa->bfd, csa->bfd = NULL; lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; lp->obj_val = 0.0; lp->it_cnt = csa->it_cnt; lp->some = 0; ret = GLP_EFAIL; break; case 2: if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); store_sol(csa, lp, GLP_NOFEAS, GLP_FEAS, csa->head[csa->p]); ret = 0; break; default: xassert(csa != csa); } goto done; } /* check if the pivot element is acceptable */ { double piv = csa->trow_vec[csa->q]; double eps = 1e-5 * (1.0 + 0.01 * csa->trow_max); if (fabs(piv) < eps) { if (parm->msg_lev >= GLP_MSG_DBG) xprintf("piv = %.12g; eps = %g\n", piv, eps); if (!rigorous) { rigorous = 5; goto loop; } } } /* now xN[q] and xB[p] have been chosen anyhow */ /* compute pivot column of the simplex table */ eval_tcol(csa); if (rigorous) refine_tcol(csa); /* accuracy check based on the pivot element */ { double piv1 = csa->tcol_vec[csa->p]; /* more accurate */ double piv2 = csa->trow_vec[csa->q]; /* less accurate */ xassert(piv1 != 0.0); if (fabs(piv1 - piv2) > 1e-8 * (1.0 + fabs(piv1)) || !(piv1 > 0.0 && piv2 > 0.0 || piv1 < 0.0 && piv2 < 0.0)) { if (parm->msg_lev >= GLP_MSG_DBG) xprintf("piv1 = %.12g; piv2 = %.12g\n", piv1, piv2); if (binv_st != 1 || !rigorous) { if (binv_st != 1) binv_st = 0; rigorous = 5; goto loop; } /* (not a good idea; should be revised later) */ if (csa->tcol_vec[csa->p] == 0.0) { csa->tcol_nnz++; xassert(csa->tcol_nnz <= csa->m); csa->tcol_ind[csa->tcol_nnz] = csa->p; } csa->tcol_vec[csa->p] = piv2; } } /* update primal values of basic variables */ #ifdef GLP_LONG_STEP /* 07/IV-2009 */ if (csa->nbps > 0) { int kk, j, k; for (kk = 1; kk < csa->nbps; kk++) { if (csa->bkpt[kk].t >= csa->bkpt[csa->nbps].t) continue; j = csa->bkpt[kk].j; k = csa->head[csa->m + j]; xassert(csa->type[k] == GLP_DB); if (csa->stat[j] == GLP_NL) csa->stat[j] = GLP_NU; else csa->stat[j] = GLP_NL; } } bbar_st = 0; #else update_bbar(csa); if (csa->phase == 2) csa->bbar[0] += (csa->cbar[csa->q] / csa->zeta) * (csa->delta / csa->tcol_vec[csa->p]); bbar_st = 2; /* updated */ #endif /* update reduced costs of non-basic variables */ update_cbar(csa); cbar_st = 2; /* updated */ /* update steepest edge coefficients */ switch (parm->pricing) { case GLP_PT_STD: break; case GLP_PT_PSE: if (csa->refct > 0) update_gamma(csa); break; default: xassert(parm != parm); } /* update factorization of the basis matrix */ ret = update_B(csa, csa->p, csa->head[csa->m+csa->q]); if (ret == 0) binv_st = 2; /* updated */ else { csa->valid = 0; binv_st = 0; /* invalid */ } #if 0 /* 06/IV-2009 */ /* update matrix N */ del_N_col(csa, csa->q, csa->head[csa->m+csa->q]); if (csa->type[csa->head[csa->p]] != GLP_FX) add_N_col(csa, csa->q, csa->head[csa->p]); #endif /* change the basis header */ change_basis(csa); /* iteration complete */ csa->it_cnt++; if (rigorous > 0) rigorous--; goto loop; done: /* deallocate the common storage area */ free_csa(csa); /* return to the calling program */ return ret; } /* eof */ igraph/src/walktrap_graph.cpp0000644000176000001440000001423612325527074016072 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Pascal Pons The original copyright notice follows here. The FSF address was fixed by Tamas Nepusz */ // File: graph.cpp //----------------------------------------------------------------------------- // Walktrap v0.2 -- Finds community structure of networks using random walks // Copyright (C) 2004-2005 Pascal Pons // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA //----------------------------------------------------------------------------- // Author : Pascal Pons // Email : pascal.pons@gmail.com // Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html // Location : Paris, France // Time : June 2005 //----------------------------------------------------------------------------- // see readme.txt for more details #include #include #include #include #include // strlen #include "walktrap_graph.h" #include "igraph_interface.h" using namespace std; namespace igraph { namespace walktrap { bool operator<(const Edge& E1, const Edge& E2) { return(E1.neighbor < E2.neighbor); } Vertex::Vertex() { degree = 0; edges = 0; total_weight = 0.; } Vertex::~Vertex() { if(edges) delete[] edges; } Graph::Graph() { nb_vertices = 0; nb_edges = 0; vertices = 0; index = 0; total_weight = 0.; } Graph::~Graph () { if (vertices) delete[] vertices; } class Edge_list { public: int* V1; int* V2; float* W; int size; int size_max; void add(int v1, int v2, float w); Edge_list() { size = 0; size_max = 1024; V1 = new int[1024]; V2 = new int[1024]; W = new float[1024]; } ~Edge_list() { if(V1) delete[] V1; if(V2) delete[] V2; if(W) delete[] W; } }; void Edge_list::add(int v1, int v2, float w) { if(size == size_max) { int* tmp1 = new int[2*size_max]; int* tmp2 = new int[2*size_max]; float* tmp3 = new float[2*size_max]; for(int i = 0; i < size_max; i++) { tmp1[i] = V1[i]; tmp2[i] = V2[i]; tmp3[i] = W[i]; } delete[] V1; delete[] V2; delete[] W; V1 = tmp1; V2 = tmp2; W = tmp3; size_max *= 2; } V1[size] = v1; V2[size] = v2; W[size] = w; size++; } int Graph::convert_from_igraph(const igraph_t *graph, const igraph_vector_t *weights) { Graph &G=*this; int max_vertex=(int)igraph_vcount(graph)-1; long int no_of_edges=(long int)igraph_ecount(graph); long int i; long int deg; double w; Edge_list EL; for (i=0; i. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpenv.h" #include "glplib.h" /*********************************************************************** * NAME * * xlset - expand integer to long integer * * SYNOPSIS * * #include "glplib.h" * glp_long xlset(int x); * * RETURNS * * The routine xlset returns x expanded to long integer. */ glp_long xlset(int x) { glp_long t; t.lo = x, t.hi = (x >= 0 ? 0 : -1); return t; } /*********************************************************************** * NAME * * xlneg - negate long integer * * SYNOPSIS * * #include "glplib.h" * glp_long xlneg(glp_long x); * * RETURNS * * The routine xlneg returns the difference 0 - x. */ glp_long xlneg(glp_long x) { if (x.lo) x.lo = - x.lo, x.hi = ~x.hi; else x.hi = - x.hi; return x; } /*********************************************************************** * NAME * * xladd - add long integers * * SYNOPSIS * * #include "glplib.h" * glp_long xladd(glp_long x, glp_long y); * * RETURNS * * The routine xladd returns the sum x + y. */ glp_long xladd(glp_long x, glp_long y) { if ((unsigned int)x.lo <= 0xFFFFFFFF - (unsigned int)y.lo) x.lo += y.lo, x.hi += y.hi; else x.lo += y.lo, x.hi += y.hi + 1; return x; } /*********************************************************************** * NAME * * xlsub - subtract long integers * * SYNOPSIS * * #include "glplib.h" * glp_long xlsub(glp_long x, glp_long y); * * RETURNS * * The routine xlsub returns the difference x - y. */ glp_long xlsub(glp_long x, glp_long y) { return xladd(x, xlneg(y)); } /*********************************************************************** * NAME * * xlcmp - compare long integers * * SYNOPSIS * * #include "glplib.h" * int xlcmp(glp_long x, glp_long y); * * RETURNS * * The routine xlcmp returns the sign of the difference x - y. */ int xlcmp(glp_long x, glp_long y) { if (x.hi >= 0 && y.hi < 0) return +1; if (x.hi < 0 && y.hi >= 0) return -1; if ((unsigned int)x.hi < (unsigned int)y.hi) return -1; if ((unsigned int)x.hi > (unsigned int)y.hi) return +1; if ((unsigned int)x.lo < (unsigned int)y.lo) return -1; if ((unsigned int)x.lo > (unsigned int)y.lo) return +1; return 0; } /*********************************************************************** * NAME * * xlmul - multiply long integers * * SYNOPSIS * * #include "glplib.h" * glp_long xlmul(glp_long x, glp_long y); * * RETURNS * * The routine xlmul returns the product x * y. */ glp_long xlmul(glp_long x, glp_long y) { unsigned short xx[8], yy[4]; xx[4] = (unsigned short)x.lo; xx[5] = (unsigned short)(x.lo >> 16); xx[6] = (unsigned short)x.hi; xx[7] = (unsigned short)(x.hi >> 16); yy[0] = (unsigned short)y.lo; yy[1] = (unsigned short)(y.lo >> 16); yy[2] = (unsigned short)y.hi; yy[3] = (unsigned short)(y.hi >> 16); bigmul(4, 4, xx, yy); x.lo = (unsigned int)xx[0] | ((unsigned int)xx[1] << 16); x.hi = (unsigned int)xx[2] | ((unsigned int)xx[3] << 16); return x; } /*********************************************************************** * NAME * * xldiv - divide long integers * * SYNOPSIS * * #include "glplib.h" * glp_ldiv xldiv(glp_long x, glp_long y); * * RETURNS * * The routine xldiv returns a structure of type glp_ldiv containing * members quot (the quotient) and rem (the remainder), both of type * glp_long. */ glp_ldiv xldiv(glp_long x, glp_long y) { glp_ldiv t; int m, sx, sy; unsigned short xx[8], yy[4]; /* sx := sign(x) */ sx = (x.hi < 0); /* sy := sign(y) */ sy = (y.hi < 0); /* x := |x| */ if (sx) x = xlneg(x); /* y := |y| */ if (sy) y = xlneg(y); /* compute x div y and x mod y */ xx[0] = (unsigned short)x.lo; xx[1] = (unsigned short)(x.lo >> 16); xx[2] = (unsigned short)x.hi; xx[3] = (unsigned short)(x.hi >> 16); yy[0] = (unsigned short)y.lo; yy[1] = (unsigned short)(y.lo >> 16); yy[2] = (unsigned short)y.hi; yy[3] = (unsigned short)(y.hi >> 16); if (yy[3]) m = 4; else if (yy[2]) m = 3; else if (yy[1]) m = 2; else if (yy[0]) m = 1; else xerror("xldiv: divide by zero\n"); bigdiv(4 - m, m, xx, yy); /* remainder in x[0], x[1], ..., x[m-1] */ t.rem.lo = (unsigned int)xx[0], t.rem.hi = 0; if (m >= 2) t.rem.lo |= (unsigned int)xx[1] << 16; if (m >= 3) t.rem.hi = (unsigned int)xx[2]; if (m >= 4) t.rem.hi |= (unsigned int)xx[3] << 16; if (sx) t.rem = xlneg(t.rem); /* quotient in x[m], x[m+1], ..., x[4] */ t.quot.lo = (unsigned int)xx[m], t.quot.hi = 0; if (m <= 3) t.quot.lo |= (unsigned int)xx[m+1] << 16; if (m <= 2) t.quot.hi = (unsigned int)xx[m+2]; if (m <= 1) t.quot.hi |= (unsigned int)xx[m+3] << 16; if (sx ^ sy) t.quot = xlneg(t.quot); return t; } /*********************************************************************** * NAME * * xltod - convert long integer to double * * SYNOPSIS * * #include "glplib.h" * double xltod(glp_long x); * * RETURNS * * The routine xltod returns x converted to double. */ double xltod(glp_long x) { double s, z; if (x.hi >= 0) s = +1.0; else s = -1.0, x = xlneg(x); if (x.hi >= 0) z = 4294967296.0 * (double)x.hi + (double)(unsigned int)x.lo; else { xassert(x.hi == 0x80000000 && x.lo == 0x00000000); z = 9223372036854775808.0; /* 2^63 */ } return s * z; } char *xltoa(glp_long x, char *s) { /* convert long integer to character string */ static const char *d = "0123456789"; glp_ldiv t; int neg, len; if (x.hi >= 0) neg = 0; else neg = 1, x = xlneg(x); if (x.hi >= 0) { len = 0; while (!(x.hi == 0 && x.lo == 0)) { t = xldiv(x, xlset(10)); xassert(0 <= t.rem.lo && t.rem.lo <= 9); s[len++] = d[t.rem.lo]; x = t.quot; } if (len == 0) s[len++] = d[0]; if (neg) s[len++] = '-'; s[len] = '\0'; strrev(s); } else strcpy(s, "-9223372036854775808"); /* -2^63 */ return s; } /**********************************************************************/ #if 0 #include "glprng.h" #define N_TEST 1000000 /* number of tests */ static glp_long myrand(RNG *rand) { glp_long x; int k; k = rng_unif_rand(rand, 4); xassert(0 <= k && k <= 3); x.lo = rng_unif_rand(rand, 65536); if (k == 1 || k == 3) { x.lo <<= 16; x.lo += rng_unif_rand(rand, 65536); } if (k <= 1) x.hi = 0; else x.hi = rng_unif_rand(rand, 65536); if (k == 3) { x.hi <<= 16; x.hi += rng_unif_rand(rand, 65536); } if (rng_unif_rand(rand, 2)) x = xlneg(x); return x; } int main(void) { RNG *rand; glp_long x, y; glp_ldiv z; int test; rand = rng_create_rand(); for (test = 1; test <= N_TEST; test++) { x = myrand(rand); y = myrand(rand); if (y.lo == 0 && y.hi == 0) y.lo = 1; /* z.quot := x div y, z.rem := x mod y */ z = xldiv(x, y); /* x must be equal to y * z.quot + z.rem */ xassert(xlcmp(x, xladd(xlmul(y, z.quot), z.rem)) == 0); } xprintf("%d tests successfully passed\n", N_TEST); rng_delete_rand(rand); return 0; } #endif /* eof */ igraph/src/glpenv06.c0000644000176000001440000001116312325527073014160 0ustar ripleyusers/* glpenv06.c (standard time) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_time - determine current universal time * * SYNOPSIS * * glp_long glp_time(void); * * RETURNS * * The routine glp_time returns the current universal time (UTC), in * milliseconds, elapsed since 00:00:00 GMT January 1, 1970. */ static const int epoch = 2440588; /* = jday(1, 1, 1970) */ /* POSIX version ******************************************************/ #if defined(HAVE_SYS_TIME_H) && defined(HAVE_GETTIMEOFDAY) #include #include glp_long glp_time(void) { struct timeval tv; struct tm *tm; glp_long t; int j; gettimeofday(&tv, NULL); tm = gmtime(&tv.tv_sec); j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year); xassert(j >= 0); t = xlset(j - epoch); t = xlmul(t, xlset(24)); t = xladd(t, xlset(tm->tm_hour)); t = xlmul(t, xlset(60)); t = xladd(t, xlset(tm->tm_min)); t = xlmul(t, xlset(60)); t = xladd(t, xlset(tm->tm_sec)); t = xlmul(t, xlset(1000)); t = xladd(t, xlset(tv.tv_usec / 1000)); return t; } /* Windows version ****************************************************/ #elif defined(__WOE__) #include glp_long glp_time(void) { SYSTEMTIME st; glp_long t; int j; GetSystemTime(&st); j = jday(st.wDay, st.wMonth, st.wYear); xassert(j >= 0); t = xlset(j - epoch); t = xlmul(t, xlset(24)); t = xladd(t, xlset(st.wHour)); t = xlmul(t, xlset(60)); t = xladd(t, xlset(st.wMinute)); t = xlmul(t, xlset(60)); t = xladd(t, xlset(st.wSecond)); t = xlmul(t, xlset(1000)); t = xladd(t, xlset(st.wMilliseconds)); return t; } /* portable ISO C version *********************************************/ #else #include glp_long glp_time(void) { time_t timer; struct tm *tm; glp_long t; int j; timer = time(NULL); tm = gmtime(&timer); j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year); xassert(j >= 0); t = xlset(j - epoch); t = xlmul(t, xlset(24)); t = xladd(t, xlset(tm->tm_hour)); t = xlmul(t, xlset(60)); t = xladd(t, xlset(tm->tm_min)); t = xlmul(t, xlset(60)); t = xladd(t, xlset(tm->tm_sec)); t = xlmul(t, xlset(1000)); return t; } #endif /*********************************************************************** * NAME * * glp_difftime - compute difference between two time values * * SYNOPSIS * * double glp_difftime(glp_long t1, glp_long t0); * * RETURNS * * The routine glp_difftime returns the difference between two time * values t1 and t0, expressed in seconds. */ double glp_difftime(glp_long t1, glp_long t0) { return xltod(xlsub(t1, t0)) / 1000.0; } /**********************************************************************/ #if 0 int main(void) { glp_long t; glp_ldiv d; int ttt, ss, mm, hh, day, month, year; char s[50]; t = glp_time(); xprintf("t = %s\n", xltoa(t, s)); d = xldiv(t, xlset(1000)); ttt = d.rem.lo, t = d.quot; d = xldiv(t, xlset(60)); ss = d.rem.lo, t = d.quot; d = xldiv(t, xlset(60)); mm = d.rem.lo, t = d.quot; d = xldiv(t, xlset(24)); hh = d.rem.lo, t = d.quot; xassert(jdate(t.lo + epoch, &day, &month, &year) == 0); xprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d\n", year, month, day, hh, mm, ss, ttt); return 0; } #endif /* eof */ igraph/src/Light.h0000755000176000001440000000122312325527072013571 0ustar ripleyusers#ifndef LIGHT_H #define LIGHT_H #include "Point.h" #include "Color.h" #include using namespace std; namespace igraph { class Light { public: Light(); // creates a light at the origin Light(const Point& rLightPoint); ~Light(); const Point& LightPoint() const; void LightPoint(const Point& rLightPoint); double Intensity() const; void Intensity(double vIntensity); const Color& LightColor() const; void LightColor(const Color& rLightColor); private: Point mLightPoint; double mIntensity; // 0 to 1 Color mLightColor; }; typedef list LightList; typedef list::iterator LightListIterator; } // namespace igraph #endif igraph/src/walktrap.cpp0000644000176000001440000001464612325527074014716 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Pascal Pons The original copyright notice follows here. The FSF address was fixed by Tamas Nepusz */ // File: walktrap.cpp //----------------------------------------------------------------------------- // Walktrap v0.2 -- Finds community structure of networks using random walks // Copyright (C) 2004-2005 Pascal Pons // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA //----------------------------------------------------------------------------- // Author : Pascal Pons // Email : pascal.pons@gmail.com // Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html // Location : Paris, France // Time : June 2005 //----------------------------------------------------------------------------- // see readme.txt for more details #include "walktrap_graph.h" #include "walktrap_communities.h" #include #include #include #include #include #include "igraph_community.h" #include "igraph_components.h" #include "igraph_interface.h" #include "igraph_interrupt_internal.h" using namespace std; using namespace igraph::walktrap; /** * \function igraph_community_walktrap * * This function is the implementation of the Walktrap community * finding algorithm, see Pascal Pons, Matthieu Latapy: Computing * communities in large networks using random walks, * http://arxiv.org/abs/physics/0512106 * * * Currently the original C++ implementation is used in igraph, * see http://www-rp.lip6.fr/~latapy/PP/walktrap.html * I'm grateful to Matthieu Latapy and Pascal Pons for providing this * source code. * * * In contrast to the original implementation, isolated vertices are allowed * in the graph and they are assumed to have a single incident loop edge with * weight 1. * * \param graph The input graph, edge directions are ignored. * \param weights Numeric vector giving the weights of the edges. * If it is a NULL pointer then all edges will have equal * weights. The weights are expected to be positive. * \param steps Integer constant, the length of the random walks. * \param merges Pointer to a matrix, the merges performed by the * algorithm will be stored here (if not NULL). Each merge is a * row in a two-column matrix and contains the ids of the merged * clusters. Clusters are numbered from zero and cluster numbers * smaller than the number of nodes in the network belong to the * individual vertices as singleton clusters. In each step a new * cluster is created from two other clusters and its id will be * one larger than the largest cluster id so far. This means that * before the first merge we have \c n clusters (the number of * vertices in the graph) numbered from zero to \c n-1. The first * merge creates cluster \c n, the second cluster \c n+1, etc. * \param modularity Pointer to a vector. If not NULL then the * modularity score of the current clustering is stored here after * each merge operation. * \param membership Pointer to a vector. If not a NULL pointer, then * the membership vector corresponding to the maximal modularity * score is stored here. If it is not a NULL pointer, then neither * \p modularity nor \p merges may be NULL. * \return Error code. * * \sa \ref igraph_community_spinglass(), \ref * igraph_community_edge_betweenness(). * * Time complexity: O(|E||V|^2) in the worst case, O(|V|^2 log|V|) typically, * |V| is the number of vertices, |E| is the number of edges. * * \example examples/simple/walktrap.c */ int igraph_community_walktrap(const igraph_t *graph, const igraph_vector_t *weights, int steps, igraph_matrix_t *merges, igraph_vector_t *modularity, igraph_vector_t *membership) { long int no_of_nodes=(long int)igraph_vcount(graph); int length=steps; long max_memory=-1; if (membership && !(modularity && merges)) { IGRAPH_ERROR("Cannot calculate membership without modularity or merges", IGRAPH_EINVAL); } Graph* G = new Graph; if (G->convert_from_igraph(graph, weights)) IGRAPH_ERROR("Cannot convert igraph graph into walktrap format", IGRAPH_EINVAL); if (merges) { igraph_integer_t no; IGRAPH_CHECK(igraph_clusters(graph, /*membership=*/ 0, /*csize=*/ 0, &no, IGRAPH_WEAK)); IGRAPH_CHECK(igraph_matrix_resize(merges, no_of_nodes-no, 2)); } if (modularity) { IGRAPH_CHECK(igraph_vector_resize(modularity, no_of_nodes)); igraph_vector_null(modularity); } Communities C(G, length, max_memory, merges, modularity); while (!C.H->is_empty()) { IGRAPH_ALLOW_INTERRUPTION(); C.merge_nearest_communities(); } delete G; if (membership) { long int m=igraph_vector_which_max(modularity); IGRAPH_CHECK(igraph_community_to_membership(merges, no_of_nodes, /*steps=*/ m, membership, /*csize=*/ 0)); } return 0; } igraph/src/gengraph_powerlaw.cpp0000644000176000001440000001475612325527073016605 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // Pascalou ... #ifdef pascalou #define my_random() random() #define MY_RAND_MAX 0x7FFFFFFF #else #include "gengraph_definitions.h" #endif #include "gengraph_powerlaw.h" #include #include #include #include "igraph_error.h" namespace gengraph { // Destructor powerlaw::~powerlaw() { delete[] table; if(dt!=NULL) delete[] dt; } // Constructor powerlaw::powerlaw(double _alpha, int _mini, int _maxi) { alpha = _alpha; mini = _mini; maxi = _maxi; if(alpha<=2.0 && maxi<0) igraph_warningf("powerlaw exponent %f should be > 2 when no " "Maximum is specified", __FILE__, __LINE__, -1, alpha); if(alpha<=1.0 && maxi>=0) igraph_warningf("powerlaw exponent %f should be > 1", __FILE__, __LINE__, -1, alpha); if(maxi>=0 && mini>maxi) igraph_warningf("powerlaw max %d should be greater than min %d", __FILE__, __LINE__, -1, maxi, mini); table = new int[POWERLAW_TABLE]; tabulated = 0; dt = NULL; } // Sample int powerlaw::sample() { if(proba_big!=0 && test_proba(proba_big)) return int(floor(0.5+big_sample(random_float()))); int r=my_random(); // table[] contains integer from MY_RAND_MAX downto 0, in blocks. Search block... if(r>(MY_RAND_MAX>>max_dt)) return mini; int k=0; while(k=0) { a=b+1; if(a==tabulated-1) break; r<<=1; r+=random_bit(); } } // Now that we found the good block, run a dichotomy on this block [a,b] while(a=0 && k>maxi)) return 0.0; if(k>=mini+tabulated) return proba_big*(big_inv_sample(double(k)-0.5)-big_inv_sample(double(k)+0.5)); else { double div = table_mul; int prev_pos_in_table = k-mini-1; if(prev_pos_in_table<0) return (double(MY_RAND_MAX)+1.0-double(table[0]>>max_dt))*div; // what block are we in ? int k=0; while(k=mini; ) sum+=double(i)*proba(i); // add proba_big * integral(big_sample(t),t=0..1) if(proba_big!=0) sum += proba_big*((pow(_a+_b,_exp+1.0)-pow(_b,_exp+1.0))/(_a*(_exp+1.0)) +double(mini)-offset-sum); return sum; } // Median. Returns integer Med such that P(X<=Med) >= 1/2 int powerlaw::median() { if(proba_big>0.5) return int(floor(0.5+big_sample(1.0-0.5/proba_big))); double sum = 0.0; int i=mini; while(sum<0.5) sum+=proba(i++); return i-1; } void powerlaw::init_to_offset(double _offset, int _tabulated) { offset = _offset; tabulated = _tabulated; if(maxi>=0 && tabulated > maxi-mini) tabulated=maxi-mini+1; double sum = 0.0; double item = double(tabulated)+offset; // Compute sum of tabulated probabilities for(int i=tabulated; i--; ) sum += pow(item-=1.0, -alpha); // Compute others parameters : proba_big, table_mul, _a, _b, _exp if(maxi>0 && maxi<=mini+tabulated-1) { proba_big = 0; table_mul = inv_RANDMAX; } else { if(maxi<0) _b = 0.0; else _b = pow(double(maxi-mini)+0.5+offset, 1.0-alpha); _a = pow(double(tabulated)-0.5+offset,1.0-alpha) - _b; _exp = 1.0 / (1.0 - alpha); double sum_big = _a*(-_exp); proba_big = sum_big / (sum + sum_big); table_mul = inv_RANDMAX * sum / (sum + sum_big); } // How many delimiters will be necessary for the table ? max_dt = max(0,int(floor(alpha*log(double(tabulated))/log(2.0)))-6); if(dt!=NULL) delete[] dt; dt = new int[max_dt+1]; // Create table as decreasing integers from MY_RAND_MAX+1 (in virtual position -1) down to 0 // Every time the index crosses a delimiter, numbers get doubled. double ssum = 0; double mul = (double(MY_RAND_MAX)+1.0)*pow(2.0,max_dt)/sum; item = double(tabulated)+offset; int k = max_dt; dt[k--]=tabulated-1; for(int i=tabulated; --i>0; ) { table[i] = int(floor(0.5+ssum)); ssum += mul * pow(item-=1.0,-alpha); if(ssum>double(MY_RAND_MAX/2) && k>=0) { while((ssum*=0.5)>double(MY_RAND_MAX/2)) { mul*=0.5; dt[k--]=-1; }; mul*=0.5; dt[k--]=i-1; } } table[0] = int(floor(0.5+ssum)); max_dt = k+1; } void powerlaw::adjust_offset_mean(double _mean, double err, double factor) { // Set two bounds for offset double ol = offset; double oh = offset; if(mean()<_mean) { do { ol = oh; oh *= factor; init_to_offset(oh, tabulated); } while(mean()<_mean); } else { do { oh = ol; ol /= factor; init_to_offset(ol, tabulated); } while(mean()>_mean); } // Now, dichotomy while(fabs(oh-ol) > err*ol) { double oc = sqrt(oh*ol); init_to_offset(oc, tabulated); if(mean()<_mean) ol = oc; else oh = oc; } init_to_offset(sqrt(ol*oh), tabulated); } double powerlaw::init_to_mean(double _mean) { if(maxi>=0 && _mean >= 0.5*double((mini+maxi))) { igraph_errorf("Fatal error in powerlaw::init_to_mean(%f): " "Mean must be in ]min, (min+max)/2[ = ]%d, %d[", __FILE__, __LINE__, IGRAPH_EINVAL, _mean, mini, (mini+maxi)/2); return(-1.0); } init_to_offset(_mean-double(mini), 100); adjust_offset_mean(_mean, 0.01, 2); init_to_offset(offset, POWERLAW_TABLE); double eps = 1.0/(double(POWERLAW_TABLE)); adjust_offset_mean(_mean, eps*eps, 1.01); return offset; } } // namespace gengraph igraph/src/gengraph_degree_sequence.h0000644000176000001440000000435312325527073017525 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DEGREE_SEQUENCE_H #define DEGREE_SEQUENCE_H #include "igraph_types.h" #include "igraph_datatype.h" namespace gengraph { class degree_sequence { private: int n; int * deg; int total; public : // #vertices inline int size() { return n; }; inline int sum() { return total; }; inline int operator[](int i) { return deg[i]; }; inline int *seq() { return deg; }; inline void assign(int n0, int* d0) { n=n0; deg=d0; }; inline int dmax() { int dm = deg[0]; for(int i=1; idm) dm=deg[i]; return dm; } void make_even(int mini=-1, int maxi=-1); void sort(); void shuffle(); // raw constructor degree_sequence(int n, int *degs); // read-from-file constrictor degree_sequence(FILE *f, bool DISTRIB=true); // simple power-law constructor : Pk = int((x+k0)^(-exp),x=k..k+1), with k0 so that avg(X)=z degree_sequence(int n, double exp, int degmin, int degmax, double avg_degree=-1.0); // igraph constructor degree_sequence(const igraph_vector_t *out_seq); // destructor ~degree_sequence(); // unbind the deg[] vector (so that it doesn't get deleted when the class is destroyed) void detach(); // compute total number of arcs void compute_total(); // raw print (vertex by vertex) void print(); // distribution print (degree frequency) void print_cumul(); // is degree sequence realizable ? bool havelhakimi(); }; } // namespace gengraph #endif //DEGREE_SEQUENCE_H igraph/src/conversion.c0000644000176000001440000006237612325527073014720 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_conversion.h" #include "igraph_iterators.h" #include "igraph_interface.h" #include "igraph_attributes.h" #include "igraph_constructors.h" #include "igraph_types_internal.h" #include "igraph_sparsemat.h" #include "config.h" /** * \ingroup conversion * \function igraph_get_adjacency * \brief Returns the adjacency matrix of a graph * * * The result is an incidence matrix, it contains numbers greater * than one if there are multiple edges in the graph. * \param graph Pointer to the graph to convert * \param res Pointer to an initialized matrix object, it will be * resized if needed. * \param type Constant giving the type of the adjacency matrix to * create for undirected graphs. It is ignored for directed * graphs. Possible values: * \clist * \cli IGRAPH_GET_ADJACENCY_UPPER * the upper right triangle of the matrix is used. * \cli IGRAPH_GET_ADJACENCY_LOWER * the lower left triangle of the matrix is used. * \cli IGRAPH_GET_ADJACENCY_BOTH * the whole matrix is used, a symmetric matrix is returned. * \endclist * \param type eids Logical, if true, then the edges ids plus one * are stored in the adjacency matrix, instead of the number of * edges between the two vertices. (The plus one is needed, since * edge ids start from zero, and zero means no edge in this case.) * \return Error code: * \c IGRAPH_EINVAL invalid type argument. * * \sa igraph_get_adjacency_sparse if you want a sparse matrix representation * * Time complexity: O(|V||V|), * |V| is the * number of vertices in the graph. */ int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, igraph_bool_t eids) { igraph_eit_t edgeit; long int no_of_nodes=igraph_vcount(graph); igraph_bool_t directed=igraph_is_directed(graph); int retval=0; long int from, to; igraph_integer_t ffrom, fto; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); igraph_matrix_null(res); IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); if (directed) { while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); from=ffrom; to=fto; if (eids) { MATRIX(*res, from, to) = edge+1; } else { MATRIX(*res, from, to) += 1; } IGRAPH_EIT_NEXT(edgeit); } } else if (type==IGRAPH_GET_ADJACENCY_UPPER) { while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); from=ffrom; to=fto; if (to < from) { if (eids) { MATRIX(*res, to, from) = edge+1; } else { MATRIX(*res, to, from) += 1; } } else { if (eids) { MATRIX(*res, from, to) = edge+1; } else { MATRIX(*res, from, to) += 1; } } IGRAPH_EIT_NEXT(edgeit); } } else if (type==IGRAPH_GET_ADJACENCY_LOWER) { while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); from=ffrom; to=fto; if (to < from) { if (eids) { MATRIX(*res, from, to) = edge+1; } else { MATRIX(*res, from, to) += 1; } } else { if (eids) { MATRIX(*res, to, from) = edge+1; } else { MATRIX(*res, to, from) += 1; } } IGRAPH_EIT_NEXT(edgeit); } } else if (type==IGRAPH_GET_ADJACENCY_BOTH) { while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); from=ffrom; to=fto; if (eids) { MATRIX(*res, from, to) = edge+1; } else { MATRIX(*res, from, to) += 1; } if (from != to) { if (eids) { MATRIX(*res, to, from) = edge+1; } else { MATRIX(*res, to, from) += 1; } } IGRAPH_EIT_NEXT(edgeit); } } else { IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL); } igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(1); return retval; } /** * \ingroup conversion * \function igraph_get_adjacency_sparse * \brief Returns the adjacency matrix of a graph in sparse matrix format * * * The result is an incidence matrix, it contains numbers greater * than one if there are multiple edges in the graph. * \param graph Pointer to the graph to convert * \param res Pointer to an initialized sparse matrix object, it will be * resized if needed. * \param type Constant giving the type of the adjacency matrix to * create for undirected graphs. It is ignored for directed * graphs. Possible values: * \clist * \cli IGRAPH_GET_ADJACENCY_UPPER * the upper right triangle of the matrix is used. * \cli IGRAPH_GET_ADJACENCY_LOWER * the lower left triangle of the matrix is used. * \cli IGRAPH_GET_ADJACENCY_BOTH * the whole matrix is used, a symmetric matrix is returned. * \endclist * \return Error code: * \c IGRAPH_EINVAL invalid type argument. * * \sa igraph_get_adjacency if you would like to get a normal matrix * ( \type igraph_matrix_t ) * * Time complexity: O(|V||V|), * |V| is the * number of vertices in the graph. */ int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, igraph_get_adjacency_t type) { igraph_eit_t edgeit; long int no_of_nodes=igraph_vcount(graph); igraph_bool_t directed=igraph_is_directed(graph); int retval=0; long int from, to; igraph_integer_t ffrom, fto; igraph_spmatrix_null(res); IGRAPH_CHECK(igraph_spmatrix_resize(res, no_of_nodes, no_of_nodes)); IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); if (directed) { while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); from=ffrom; to=fto; igraph_spmatrix_add_e(res, from, to, 1); IGRAPH_EIT_NEXT(edgeit); } } else if (type==IGRAPH_GET_ADJACENCY_UPPER) { while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); from=ffrom; to=fto; if (to < from) { igraph_spmatrix_add_e(res, to, from, 1); } else { igraph_spmatrix_add_e(res, from, to, 1); } IGRAPH_EIT_NEXT(edgeit); } } else if (type==IGRAPH_GET_ADJACENCY_LOWER) { while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); from=ffrom; to=fto; if (to > from) { igraph_spmatrix_add_e(res, to, from, 1); } else { igraph_spmatrix_add_e(res, from, to, 1); } IGRAPH_EIT_NEXT(edgeit); } } else if (type==IGRAPH_GET_ADJACENCY_BOTH) { while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); from=ffrom; to=fto; igraph_spmatrix_add_e(res, from, to, 1); if (from != to) { igraph_spmatrix_add_e(res, to, from, 1); } IGRAPH_EIT_NEXT(edgeit); } } else { IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL); } igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(1); return retval; } /** * \ingroup conversion * \function igraph_get_edgelist * \brief Returns the list of edges in a graph * * The order of the edges is given by the edge ids. * \param graph Pointer to the graph object * \param res Pointer to an initialized vector object, it will be * resized. * \param bycol Logical, if true, the edges will be returned * columnwise, eg. the first edge is * res[0]->res[|E|], the second is * res[1]->res[|E|+1], etc. * \return Error code. * * Time complexity: O(|E|), the * number of edges in the graph. */ int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol) { igraph_eit_t edgeit; long int no_of_edges=igraph_ecount(graph); long int vptr=0; igraph_integer_t from, to; IGRAPH_CHECK(igraph_vector_resize(res, no_of_edges*2)); IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); if (bycol) { while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to); VECTOR(*res)[vptr]=from; VECTOR(*res)[vptr+no_of_edges]=to; vptr++; IGRAPH_EIT_NEXT(edgeit); } } else { while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to); VECTOR(*res)[vptr++]=from; VECTOR(*res)[vptr++]=to; IGRAPH_EIT_NEXT(edgeit); } } igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_to_directed * \brief Convert an undirected graph to a directed one * * * If the supplied graph is directed, this function does nothing. * \param graph The graph object to convert. * \param mode Constant, specifies the details of how exactly the * conversion is done. Possible values: \c * IGRAPH_TO_DIRECTED_ARBITRARY: the number of edges in the * graph stays the same, an arbitrarily directed edge is * created for each undirected edge; * \c IGRAPH_TO_DIRECTED_MUTUAL: two directed edges are * created for each undirected edge, one in each direction. * \return Error code. * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. */ int igraph_to_directed(igraph_t *graph, igraph_to_directed_t mode) { if (mode != IGRAPH_TO_DIRECTED_ARBITRARY && mode != IGRAPH_TO_DIRECTED_MUTUAL) { IGRAPH_ERROR("Cannot directed graph, invalid mode", IGRAPH_EINVAL); } if (igraph_is_directed(graph)) { return 0; } if (mode==IGRAPH_TO_DIRECTED_ARBITRARY) { igraph_t newgraph; igraph_vector_t edges; long int no_of_edges=igraph_ecount(graph); long int no_of_nodes=igraph_vcount(graph); long int size=no_of_edges*2; IGRAPH_VECTOR_INIT_FINALLY(&edges, size); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); igraph_vector_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1,1,1); IGRAPH_FINALLY_CLEAN(2); igraph_destroy(graph); *graph=newgraph; } else if (mode==IGRAPH_TO_DIRECTED_MUTUAL) { igraph_t newgraph; igraph_vector_t edges; igraph_vector_t index; long int no_of_edges=igraph_ecount(graph); long int no_of_nodes=igraph_vcount(graph); long int size=no_of_edges*4; long int i; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, size)); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges*4)); IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_edges*2); for (i=0; i * If the supplied graph is undirected, this function does nothing. * \param graph The graph object to convert. * \param mode Constant, specifies the details of how exactly the * conversion is done. Possible values: \c * IGRAPH_TO_UNDIRECTED_EACH: the number of edges remains * constant, an undirected edge is created for each directed * one, this version might create graphs with multiple edges; * \c IGRAPH_TO_UNDIRECTED_COLLAPSE: one undirected edge will * be created for each pair of vertices which are connected * with at least one directed edge, no multiple edges will be * created. \c IGRAPH_TO_UNDIRECTED_MUTUAL creates an undirected * edge for each pair of mutual edges in the directed graph. * Non-mutual edges are lost. This mode might create multiple * edges. * \param edge_comb What to do with the edge attributes. See the igraph * manual section about attributes for details. * \return Error code. * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. * * \example examples/simple/igraph_to_undirected.c */ int igraph_to_undirected(igraph_t *graph, igraph_to_undirected_t mode, const igraph_attribute_combination_t *edge_comb) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vector_t edges; igraph_t newgraph; igraph_bool_t attr=edge_comb && igraph_has_attribute_table(); if (mode != IGRAPH_TO_UNDIRECTED_EACH && mode != IGRAPH_TO_UNDIRECTED_COLLAPSE && mode != IGRAPH_TO_UNDIRECTED_MUTUAL) { IGRAPH_ERROR("Cannot undirect graph, invalid mode", IGRAPH_EINVAL); } if (!igraph_is_directed(graph)) { return 0; } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (mode==IGRAPH_TO_UNDIRECTED_EACH) { igraph_es_t es; igraph_eit_t eit; IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*2)); IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID)); IGRAPH_FINALLY(igraph_es_destroy, &es); IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); while (!IGRAPH_EIT_END(eit)) { long int edge=IGRAPH_EIT_GET(eit); igraph_integer_t from, to; igraph_edge(graph, (igraph_integer_t) edge, &from, &to); IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); IGRAPH_EIT_NEXT(eit); } igraph_eit_destroy(&eit); igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, IGRAPH_UNDIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); igraph_vector_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1,1,1); IGRAPH_FINALLY_CLEAN(2); igraph_destroy(graph); *graph=newgraph; } else if (mode==IGRAPH_TO_UNDIRECTED_COLLAPSE) { igraph_vector_t inadj, outadj; long int i; igraph_vector_t mergeinto; long int actedge=0; if (attr) { IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); } IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*2)); IGRAPH_VECTOR_INIT_FINALLY(&inadj, 0); IGRAPH_VECTOR_INIT_FINALLY(&outadj, 0); for (i=0; i 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * SCGlib : A C library for the spectral coarse graining of matrices * as described in the paper: Shrinking Matrices while preserving their * eigenpairs with Application to the Spectral Coarse Graining of Graphs. * Preprint available at * * Copyright (C) 2008 David Morton de Lachapelle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * DESCRIPTION * ----------- * The grouping function takes as argument 'nev' eigenvectors and * and tries to minimize the eigenpair shifts induced by the coarse * graining (Section 5 of the above reference). The eigenvectors are * stored in a 'nev'x'n' matrix 'v'. * The 'algo' parameter can take the following values * 1 -> Optimal method (sec. 5.3.1) * 2 -> Intervals+k-means (sec. 5.3.3) * 3 -> Intervals (sec. 5.3.2) * 4 -> Exact SCG (sec. 5.4.1--last paragraph) * 'nt' is a vector of length 'nev' giving either the size of the * partitions (if algo = 1) or the number of intervals to cut the * eigenvectors if algo = 2 or algo = 3. When algo = 4 this parameter * is ignored. 'maxiter' fixes the maximum number of iterations of * the k-means algorithm, and is only considered when algo = 2. * All the algorithms try to find a minimizing partition of * ||v_i-Pv_i|| where P is a problem-specific projector and v_i denotes * the eigenvectors stored in v. The final partition is worked out * as decribed in Method 1 of Section 5.4.2. * 'matrix' provides the type of SCG (i.e. the form of P). So far, * the options are those described in section 6, that is: * 1 -> Symmetric (sec. 6.1) * 2 -> Laplacian (sec. 6.2) * 3 -> Stochastic (sec. 6.3) * In the stochastic case, a valid distribution probability 'p' must be * provided. In all other cases, 'p' is ignored and can be set to NULL. * The group labels in the final partition are given in 'gr' as positive * consecutive integers starting from 0. */ #include "igraph_scg.h" #include "igraph_eigen.h" #include "igraph_interface.h" #include "igraph_structural.h" #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_memory.h" #include "scg_headers.h" #include "math.h" /** * \section about_scg * * * The SCG functions provide a framework, called Spectral Coarse Graining * (SCG), for reducing large graphs while preserving their * spectral-related features, that is features * closely related with the eigenvalues and eigenvectors of a graph * matrix (which for now can be the adjacency, the stochastic, or the * Laplacian matrix). * * * * Common examples of such features comprise the first-passage-time of * random walkers on Markovian graphs, thermodynamic properties of * lattice models in statistical physics (e.g. Ising model), and the * epidemic threshold of epidemic network models (SIR and SIS models). * * * * SCG differs from traditional clustering schemes by producing a * coarse-grained graph (not just a partition of * the vertices), representative of the original one. As shown in [1], * Principal Component Analysis can be viewed as a particular SCG, * called exact SCG, where the matrix to be * coarse-grained is the covariance matrix of some data set. * * * * SCG should be of interest to practitioners of various * fields dealing with problems where matrix eigenpairs play an important * role, as for instance is the case of dynamical processes on networks. * * *
SCG in brief * * The main idea of SCG is to operate on a matrix a shrinkage operation * specifically designed to preserve some of the matrix eigenpairs while * not altering other important matrix features (such as its structure). * Mathematically, this idea was expressed as follows. Consider a * (complex) n x n matrix M and form the product *
* M'=LMR*, *
* where n' < n and L, R are from C[n'xn]} and are such * that LR*=I[n'] (R* denotes the conjugate transpose of R). Under * these assumptions, it can be shown that P=R*L is an n'-rank * projector and that, if (lambda, v) is a (right) * eigenpair of M (i.e. Mv=lambda v} and P is orthogonal, there exists * an eigenvalue lambda' of M' such that *
* |lambda-lambda'| <= const ||e[P](v)|| * [1+O(||e[P](v)||2)], *
* where ||e[P](v)||=||v-Pv||. Hence, if P (or equivalently * L, R) is chosen so as to make ||e[P](v)|| as small as possible, one * can preserve to any desired level the original eigenvalue * lambda in the coarse-grained matrix M'; * under extra assumptions on M, this result can be generalized to * eigenvectors [1]. This leads to the following generic definition of a * SCG problem. *
* * * Given M (C[nxn]) and (lambda, v), a (right) eigenpair of M to be * preserved by the coarse graining, the problem is to find a projector * P' solving *
* min(||e[P](v)||, p in Omega), *
* where Omega is a set of projectors in C[nxn] described by some * ad hoc constraints c[1], ..., c[r] * (e.g. c[1]: P in R[nxn], c[2]: P=t(P), c[3]: P[i,j] >= 0}, etc). *
* * * Choosing pertinent constraints to solve the SCG problem is of great * importance in applications. For instance, in the absence of * constraints the SCG problem is solved trivially by * P'=vv* (v is assumed normalized). We have designed a particular * constraint, called homogeneous mixing, which * ensures that vertices belonging to the same group are merged * consistently from a physical point of view (see [1] for * details). Under this constraint the SCG problem reduces to finding * the partition of 1, ..., n (labeling the original vertices) * minimizing *
* ||e[P](v)||2 = * sum([v(i)-(Pv)(i)]2; * alpha=1,...,n', i in alpha), *
* where alpha denotes a group (i.e. a block) in a partition of * {1, ..., n}, and |alpha| is the number of elements in alpha. *
* * * If M is symmetric or stochastic, for instance, then it may be * desirable (or mandatory) to choose L, R so that M' is symmetric or * stochastic as well. This structural constraint * has led to the construction of particular semi-projectors for * symmetric [1], stochastic [3] and Laplacian [2] matrices, that are * made available. * * * * In short, the coarse graining of matrices and graphs involves: * \olist * \oli Retrieving a matrix or a graph matrix M from the * problem. * \oli Computing the eigenpairs of M to be preserved in the * coarse-grained graph or matrix. * \oli Setting some problem-specific constraints (e.g. dimension of * the coarse-grained object). * \oli Solving the constrained SCG problem, that is finding P'. * \oli Computing from P' two semi-projectors L' and R' * (e.g. following the method proposed in [1]). * \oli Working out the product M'=L'MR'* and, if needed, defining * from M' a coarse-grained graph. * \endolist * *
* *
Functions for performing SCG * * The main functions are \ref igraph_scg_adjacency(), \ref * igraph_scg_laplacian() and \ref igraph_scg_stochastic(). * These functions handle all the steps involved in the * Spectral Coarse Graining (SCG) of some particular matrices and graphs * as described above and in reference [1]. In more details, * they compute some prescribed eigenpairs of a matrix or a * graph matrix, (for now adjacency, Laplacian and stochastic matrices are * available), work out an optimal partition to preserve the eigenpairs, * and finally output a coarse-grained matrix or graph along with other * useful information. * * * * These steps can also be carried out independently: (1) Use * \ref igraph_get_adjacency(), \ref igraph_get_sparsemat(), * \ref igraph_laplacian(), \ref igraph_get_stochastic() or \ref * igraph_get_stochastic_sparsemat() to compute a matrix M. * (2) Work out some prescribed eigenpairs of M e.g. by * means of \ref igraph_arpack_rssolve() or \ref * igraph_arpack_rnsolve(). (3) Invoke one the four * algorithms of the function \ref igraph_scg_grouping() to get a * partition that will preserve the eigenpairs in the coarse-grained * matrix. (4) Compute the semi-projectors L and R using * \ref igraph_scg_semiprojectors() and from there the coarse-grained * matrix M'=LMR*. If necessary, construct a coarse-grained graph from * M' (e.g. as in [1]). * *
* *
References * * [1] D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, * Shrinking Matrices while Preserving their Eigenpairs with Application * to the Spectral Coarse Graining of Graphs. Submitted to * SIAM Journal on Matrix Analysis and * Applications, 2008. * http://people.epfl.ch/david.morton * * * [2] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining and * Synchronization in Oscillator Networks. * Physical Review Letters, * 100(17), 2008. * http://arxiv.org/abs/0708.2055 * * * [3] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining of Complex * Networks, Physical Review Letters, * 99(3), 2007. * http://arxiv.org/abs/0706.0812 * *
*/ /** * \function igraph_scg_grouping * \brief SCG problem solver * * This function solves the Spectral Coarse Graining (SCG) problem; * either exactly, or approximately but faster. * *
* The algorithm \c IGRAPH_SCG_OPTIMUM solves exactly the SCG problem * for each eigenvector in \p V. The running time of this algorithm is * O(max(nt) m^2) for the symmetric and laplacian matrix problems * It is O(m^3) for the stochastic problem. Here m is the number * of rows in \p V. In all three cases, the memory usage is O(m^2). * * * The algorithms \c IGRAPH_SCG_INTERV and \c IGRAPH_SCG_INTERV_KM solve * approximately the SCG problem by performing a (for now) constant * binning of the components of the eigenvectors, that is \p nt * VECTOR(nt_vec)[i]) constant-size bins are used to * partition V[,i]. When \p algo is \c * IGRAPH_SCG_INTERV_KM, the (Lloyd) k-means algorithm is * run on each partition obtained by \c IGRAPH_SCG_INTERV to improve * accuracy. * * * Once a minimizing partition (either exact or approximate) has been * found for each eigenvector, the final grouping is worked out as * follows: two vertices are grouped together in the final partition if * they are grouped together in each minimizing partition. In general the * size of the final partition is not known in advance when the number * of columns in \p V is larger than one. * * * Finally, the algorithm \c IGRAPH_SCG_EXACT groups the vertices with * equal components in each eigenvector. The last three algorithms * essentially have linear running time and memory load. * * \param V The matrix of eigenvectors to be preserved by coarse * graining, each column is an eigenvector. * \param groups Pointer to an initialized vector, the result of the * SCG is stored here. * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM, * it gives the number of groups to partition each eigenvector * separately. When \p algo is \c IGRAPH_SCG_INTERV or \c * IGRAPH_SCG_INTERV_KM, it gives the number of intervals to * partition each eigenvector. This is ignored when \p algo is \c * IGRAPH_SCG_EXACT. * \param nt_vec A numeric vector of length one or the length must * match the number of eigenvectors given in \p V, or a \c NULL * pointer. If not \c NULL, then this argument gives the number of * groups or intervals, and \p nt is ignored. Different number of * groups or intervals can be specified for each eigenvector. * \param mtype The type of semi-projectors used in the SCG. Possible * values are \c IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and * \c IGRAPH_SCG_LAPLACIAN. * \param algo The algorithm to solve the SCG problem. Possible * values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c * IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the * details about them above. * \param p A probability vector, or \c NULL. This argument must be * given if \p mtype is \c IGRAPH_SCG_STOCHASTIC, but it is ignored * otherwise. For the stochastic case it gives the stationary * probability distribution of a Markov chain, the one specified by * the graph/matrix under study. * \param maxiter A positive integer giving the number of iterations * of the k-means algorithm when \p algo is \c * IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable * (initial) value for this argument is 100. * \return Error code. * * Time complexity: see description above. * * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_laplacian(), \ref * igraph_scg_stochastic(). * * \example examples/simple/igraph_scg_grouping.c * \example examples/simple/igraph_scg_grouping2.c * \example examples/simple/igraph_scg_grouping3.c * \example examples/simple/igraph_scg_grouping4.c */ int igraph_scg_grouping(const igraph_matrix_t *V, igraph_vector_t *groups, igraph_integer_t nt, const igraph_vector_t *nt_vec, igraph_scg_matrix_t mtype, igraph_scg_algorithm_t algo, const igraph_vector_t *p, igraph_integer_t maxiter) { int no_of_nodes=(int) igraph_matrix_nrow(V); int nev=(int) igraph_matrix_ncol(V); igraph_matrix_int_t gr_mat; int i; if (nt_vec && igraph_vector_size(nt_vec) != 1 && igraph_vector_size(nt_vec) != nev) { IGRAPH_ERROR("Invalid length for interval specification", IGRAPH_EINVAL); } if (nt_vec && igraph_vector_size(nt_vec) == 1) { nt=(igraph_integer_t) VECTOR(*nt_vec)[0]; nt_vec=0; } if (!nt_vec && algo != IGRAPH_SCG_EXACT) { if (nt <= 1 || nt >= no_of_nodes) { IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); } } else if (algo != IGRAPH_SCG_EXACT) { igraph_real_t min, max; igraph_vector_minmax(nt_vec, &min, &max); if (min <= 1 || max >= no_of_nodes) { IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); } } if (mtype == IGRAPH_SCG_STOCHASTIC && !p) { IGRAPH_ERROR("`p' must be given for the stochastic matrix case", IGRAPH_EINVAL); } if (p && igraph_vector_size(p) != no_of_nodes) { IGRAPH_ERROR("Invalid `p' vector size", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_resize(groups, no_of_nodes)); #define INVEC(i) (nt_vec ? VECTOR(*nt_vec)[i] : nt) IGRAPH_CHECK(igraph_matrix_int_init(&gr_mat, no_of_nodes, nev)); IGRAPH_FINALLY(igraph_matrix_int_destroy, &gr_mat); switch (algo) { case IGRAPH_SCG_OPTIMUM: for (i=0; i * The symmetric semi-projectors are defined as *
* L[alpha,j] = R[alpha,j] = 1/sqrt(|alpha|) delta[alpha,gamma(j)], *
* the (row) Laplacian semi-projectors as *
* L[alpha,j] = 1/|alpha| delta[alpha,gamma(j)] *
* and *
* R[alpha,j] = delta[alpha,gamma(j)], *
* and the (row) stochastic semi-projectors as *
* L[alpha,j] = p[1][j] / sum(p[1][k]; k in gamma(j)) * delta[alpha,gamma(j)] *
* and *
* R[alpha,j] = delta[alpha,gamma(j)], *
* where p[1] is the (left) eigenvector associated with the * one-eigenvalue of the stochastic matrix. L and R are * defined in a symmetric way when \p norm is \c * IGRAPH_SCG_NORM_COL. All these semi-projectors verify various * properties described in the reference. * \param groups A vector of integers, giving the group label of every * vertex in the partition. Group labels should start at zero and * should be sequential. * \param mtype The type of semi-projectors. For now \c * IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and \c * IGRAP_SCG_LAPLACIAN are supported. * \param L If not a \c NULL pointer, then it must be a pointer to * an initialized matrix. The left semi-projector is stored here. * \param R If not a \c NULL pointer, then it must be a pointer to * an initialized matrix. The right semi-projector is stored here. * \param Lsparse If not a \c NULL pointer, then it must be a pointer * to an uninitialized sparse matrix. The left semi-projector is * stored here. * \param Rsparse If not a \c NULL pointer, then it must be a pointer * to an uninitialized sparse matrix. The right semi-projector is * stored here. * \param p \c NULL, or a probability vector of the same length as \p * groups. \p p is the stationary probability distribution of a * Markov chain when \p mtype is \c IGRAPH_SCG_STOCHASTIC. This * argument is ignored in all other cases. * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL. * Specifies whether the rows or the columns of the Laplacian * matrix sum up to zero, or whether the rows or the columns of the * stochastic matrix sum up to one. * \return Error code. * * Time complexity: TODO. * * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_stochastic() and * \ref igraph_scg_laplacian(), \ref igraph_scg_grouping(). * * \example examples/simple/igraph_scg_semiprojectors.c * \example examples/simple/igraph_scg_semiprojectors2.c * \example examples/simple/igraph_scg_semiprojectors3.c */ int igraph_scg_semiprojectors(const igraph_vector_t *groups, igraph_scg_matrix_t mtype, igraph_matrix_t *L, igraph_matrix_t *R, igraph_sparsemat_t *Lsparse, igraph_sparsemat_t *Rsparse, const igraph_vector_t *p, igraph_scg_norm_t norm) { int no_of_nodes=(int) igraph_vector_size(groups); int no_of_groups; igraph_real_t min, max; igraph_vector_minmax(groups, &min, &max); no_of_groups=(int) max+1; if (min < 0 || max >= no_of_nodes) { IGRAPH_ERROR("Invalid membership vector", IGRAPH_EINVAL); } if (mtype == IGRAPH_SCG_STOCHASTIC && !p) { IGRAPH_ERROR("`p' must be given for the stochastic matrix case", IGRAPH_EINVAL); } if (p && igraph_vector_size(p) != no_of_nodes) { IGRAPH_ERROR("Invalid `p' vector length, should match number of vertices", IGRAPH_EINVAL); } switch (mtype) { case IGRAPH_SCG_SYMMETRIC: IGRAPH_CHECK(igraph_i_scg_semiprojectors_sym(groups, L, R, Lsparse, Rsparse, no_of_groups, no_of_nodes)); break; case IGRAPH_SCG_LAPLACIAN: IGRAPH_CHECK(igraph_i_scg_semiprojectors_lap(groups, L, R, Lsparse, Rsparse, no_of_groups, no_of_nodes, norm)); break; case IGRAPH_SCG_STOCHASTIC: IGRAPH_CHECK(igraph_i_scg_semiprojectors_sto(groups, L, R, Lsparse, Rsparse, no_of_groups, no_of_nodes, p, norm)); break; } return 0; } /** * \function igraph_scg_norm_eps * Calculate SCG residuals * * Computes |v[i]-Pv[i]|, where v[i] is the i-th eigenvector in \p V * and P is the projector corresponding to the \p mtype argument. * * \param V The matrix of eigenvectors to be preserved by coarse * graining, each column is an eigenvector. * \param groups A vector of integers, giving the group label of every * vertex in the partition. Group labels should start at zero and * should be sequential. * \param eps Pointer to a real value, the result is stored here. * \param mtype The type of semi-projectors. For now \c * IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and \c * IGRAP_SCG_LAPLACIAN are supported. * \param p \c NULL, or a probability vector of the same length as \p * groups. \p p is the stationary probability distribution of a * Markov chain when \p mtype is \c IGRAPH_SCG_STOCHASTIC. This * argument is ignored in all other cases. * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL. * Specifies whether the rows or the columns of the Laplacian * matrix sum up to zero, or whether the rows or the columns of the * stochastic matrix sum up to one. * \return Error code. * * Time complexity: TODO. * * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_stochastic() and * \ref igraph_scg_laplacian(), \ref igraph_scg_grouping(), \ref * igraph_scg_semiprojectors(). */ int igraph_scg_norm_eps(const igraph_matrix_t *V, const igraph_vector_t *groups, igraph_vector_t *eps, igraph_scg_matrix_t mtype, const igraph_vector_t *p, igraph_scg_norm_t norm) { int no_of_nodes=(int) igraph_vector_size(groups); int no_of_groups; int no_of_vectors=(int) igraph_matrix_ncol(V); igraph_real_t min, max; igraph_sparsemat_t Lsparse, Rsparse, Lsparse2, Rsparse2, Rsparse3, proj; igraph_vector_t x, res; int k, i; if (igraph_matrix_nrow(V) != no_of_nodes) { IGRAPH_ERROR("Eigenvector length and group vector length do not match", IGRAPH_EINVAL); } igraph_vector_minmax(groups, &min, &max); no_of_groups=(int) max+1; if (min < 0 || max >= no_of_nodes) { IGRAPH_ERROR("Invalid membership vector", IGRAPH_EINVAL); } if (mtype == IGRAPH_SCG_STOCHASTIC && !p) { IGRAPH_ERROR("`p' must be given for the stochastic matrix case", IGRAPH_EINVAL); } if (p && igraph_vector_size(p) != no_of_nodes) { IGRAPH_ERROR("Invalid `p' vector length, should match number of vertices", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_scg_semiprojectors(groups, mtype, /* L= */ 0, /* R= */ 0, &Lsparse, &Rsparse, p, norm)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &Lsparse); IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse); IGRAPH_CHECK(igraph_sparsemat_compress(&Lsparse, &Lsparse2)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &Lsparse2); IGRAPH_CHECK(igraph_sparsemat_compress(&Rsparse, &Rsparse2)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse2); IGRAPH_CHECK(igraph_sparsemat_transpose(&Rsparse2, &Rsparse3, /*values=*/ 1)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse3); IGRAPH_CHECK(igraph_sparsemat_multiply(&Rsparse3, &Lsparse2, &proj)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &proj); IGRAPH_VECTOR_INIT_FINALLY(&res, no_of_nodes); IGRAPH_CHECK(igraph_vector_resize(eps, no_of_vectors)); for (k = 0; k < no_of_vectors; k++) { igraph_vector_view(&x, &MATRIX(*V, 0, k), no_of_nodes); igraph_vector_null(&res); IGRAPH_CHECK(igraph_sparsemat_gaxpy(&proj, &x, &res)); VECTOR(*eps)[k] = 0.0; for (i = 0; i < no_of_nodes; i++) { igraph_real_t di=MATRIX(*V, i, k) - VECTOR(res)[i]; VECTOR(*eps)[k] += di * di; } VECTOR(*eps)[k] = sqrt(VECTOR(*eps)[k]); } igraph_vector_destroy(&res); igraph_sparsemat_destroy(&proj); igraph_sparsemat_destroy(&Rsparse3); igraph_sparsemat_destroy(&Rsparse2); igraph_sparsemat_destroy(&Lsparse2); igraph_sparsemat_destroy(&Rsparse); igraph_sparsemat_destroy(&Lsparse); IGRAPH_FINALLY_CLEAN(7); return 0; } int igraph_i_matrix_laplacian(const igraph_matrix_t *matrix, igraph_matrix_t *mymatrix, igraph_scg_norm_t norm) { igraph_vector_t degree; int i, j, n=(int) igraph_matrix_nrow(matrix); IGRAPH_CHECK(igraph_matrix_resize(mymatrix, n, n)); IGRAPH_VECTOR_INIT_FINALLY(°ree, n); if (norm==IGRAPH_SCG_NORM_ROW) { IGRAPH_CHECK(igraph_matrix_rowsum(matrix, °ree)); } else { IGRAPH_CHECK(igraph_matrix_colsum(matrix, °ree)); } for (i=0; i= no_of_nodes) { IGRAPH_ERROR("Invalid eigenvectors given", IGRAPH_EINVAL); } if (!nt_vec && (nt <= 1 || nt >= no_of_nodes)) { IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); } if (nt_vec) { if (igraph_vector_size(nt_vec) != 1 && igraph_vector_size(nt_vec) != no_of_ev) { IGRAPH_ERROR("Invalid length for interval specification", IGRAPH_EINVAL); } igraph_vector_minmax(nt_vec, &min, &max); if (min <= 1 || max >= no_of_nodes) { IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); } } if (vectors && igraph_matrix_size(vectors) != 0 && (igraph_matrix_ncol(vectors) != no_of_ev || igraph_matrix_nrow(vectors) != no_of_nodes)) { IGRAPH_ERROR("Invalid eigenvector matrix size", IGRAPH_EINVAL); } if (vectors_cmplx && igraph_matrix_complex_size(vectors_cmplx) != 0 && (igraph_matrix_complex_ncol(vectors_cmplx) != no_of_ev || igraph_matrix_complex_nrow(vectors_cmplx) != no_of_nodes)) { IGRAPH_ERROR("Invalid eigenvector matrix size", IGRAPH_EINVAL); } if (groups && igraph_vector_size(groups) != 0 && igraph_vector_size(groups) != no_of_nodes) { IGRAPH_ERROR("Invalid `groups' vector size", IGRAPH_EINVAL); } if ( (scg_graph!=0) + (scg_matrix!=0) + (scg_sparsemat!=0) == 0 ) { IGRAPH_ERROR("No output is requested, please give at least one of " "`scg_graph', `scg_matrix' and `scg_sparsemat'", IGRAPH_EINVAL); } if (p && igraph_vector_size(p) != 0 && igraph_vector_size(p) != no_of_nodes) { IGRAPH_ERROR("Invalid `p' vector size", IGRAPH_EINVAL); } return 0; } /** * \function igraph_scg_adjacency * Spectral coarse graining, symmetric case. * * This function handles all the steps involved in the Spectral Coarse * Graining (SCG) of some matrices and graphs as described in the * reference below. * * \param graph The input graph. Exactly one of \p graph, \p matrix * and \p sparsemat must be given, the other two must be \c NULL * pointers. * \param matrix The input matrix. Exactly one of \p graph, \p matrix * and \p sparsemat must be given, the other two must be \c NULL * pointers. * \param sparsemat The input sparse matrix. Exactly one of \p graph, * \p matrix and \p sparsemat must be given, the other two must be * \c NULL pointers. * \param ev A vector of positive integers giving the indexes of the * eigenpairs to be preserved. 1 designates the eigenvalue with * largest algebraic value, 2 the one with second largest algebraic * value, etc. * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM, * it gives the number of groups to partition each eigenvector * separately. When \p algo is \c IGRAPH_SCG_INTERV or \c * IGRAPH_SCG_INTERV_KM, it gives the number of intervals to * partition each eigenvector. This is ignored when \p algo is \c * IGRAPH_SCG_EXACT. * \param nt_vec A numeric vector of length one or the length must * match the number of eigenvectors given in \p V, or a \c NULL * pointer. If not \c NULL, then this argument gives the number of * groups or intervals, and \p nt is ignored. Different number of * groups or intervals can be specified for each eigenvector. * \param algo The algorithm to solve the SCG problem. Possible * values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c * IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the * details about them above. * \param values If this is not \c NULL and the eigenvectors are * re-calculated, then the eigenvalues are stored here. * \param vectors If this is not \c NULL, and not a zero-length * matrix, then it is interpreted as the eigenvectors to use for * the coarse-graining. Otherwise the eigenvectors are * re-calculated, and they are stored here. (If this is not \c NULL.) * \param groups If this is not \c NULL, and not a zero-length vector, * then it is interpreted as the vector of group labels. (Group * labels are integers from zero and are sequential.) Otherwise * group labels are re-calculated and stored here, if this argument * is not a null pointer. * \param use_arpack Whether to use ARPACK for solving the * eigenproblem. Currently ARPACK is not implemented. * \param maxiter A positive integer giving the number of iterations * of the k-means algorithm when \p algo is \c * IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable * (initial) value for this argument is 100. * \param scg_graph If not a \c NULL pointer, then the coarse-grained * graph is returned here. * \param scg_matrix If not a \c NULL pointer, then it must be an * initialied matrix, and the coarse-grained matrix is returned * here. * \param scg_sparsemat If not a \c NULL pointer, then the coarse * grained matrix is returned here, in sparse matrix form. * \param L If not a \c NULL pointer, then it must be an initialized * matrix and the left semi-projector is returned here. * \param R If not a \c NULL pointer, then it must be an initialized * matrix and the right semi-projector is returned here. * \param Lsparse If not a \c NULL pointer, then the left * semi-projector is returned here. * \param Rsparse If not a \c NULL pointer, then the right * semi-projector is returned here. * \return Error code. * * Time complexity: TODO. * * \sa \ref igraph_scg_grouping(), \ref igraph_scg_semiprojectors(), * \ref igraph_scg_stochastic() and \ref igraph_scg_laplacian(). * * \example examples/simple/scg.c */ int igraph_scg_adjacency(const igraph_t *graph, const igraph_matrix_t *matrix, const igraph_sparsemat_t *sparsemat, const igraph_vector_t *ev, igraph_integer_t nt, const igraph_vector_t *nt_vec, igraph_scg_algorithm_t algo, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_t *groups, igraph_bool_t use_arpack, igraph_integer_t maxiter, igraph_t *scg_graph, igraph_matrix_t *scg_matrix, igraph_sparsemat_t *scg_sparsemat, igraph_matrix_t *L, igraph_matrix_t *R, igraph_sparsemat_t *Lsparse, igraph_sparsemat_t *Rsparse) { igraph_sparsemat_t *mysparsemat=(igraph_sparsemat_t*) sparsemat, real_sparsemat; int no_of_ev=(int) igraph_vector_size(ev); /* eigenvectors are calculated and returned */ igraph_bool_t do_vectors= vectors && igraph_matrix_size(vectors)==0; /* groups are calculated */ igraph_bool_t do_groups= !groups || igraph_vector_size(groups)==0; /* eigenvectors are not returned but must be calculated for groups */ igraph_bool_t tmp_vectors= !do_vectors && do_groups; /* need temporary vector for groups */ igraph_bool_t tmp_groups= !groups; igraph_matrix_t myvectors; igraph_vector_t mygroups; igraph_bool_t tmp_lsparse=!Lsparse, tmp_rsparse=!Rsparse; igraph_sparsemat_t myLsparse, myRsparse, tmpsparse, Rsparse_t; int no_of_nodes; igraph_real_t evmin, evmax; igraph_bool_t directed; /* --------------------------------------------------------------------*/ /* Argument checks */ IGRAPH_CHECK(igraph_i_scg_common_checks(graph, matrix, sparsemat, ev, nt, nt_vec, vectors, 0, groups, scg_graph, scg_matrix, scg_sparsemat, /*p=*/ 0, &evmin, &evmax)); if (graph) { no_of_nodes=igraph_vcount(graph); directed=igraph_is_directed(graph); } else if (matrix) { no_of_nodes=(int) igraph_matrix_nrow(matrix); directed=!igraph_matrix_is_symmetric(matrix); } else { no_of_nodes=(int) igraph_sparsemat_nrow(sparsemat); directed=!igraph_sparsemat_is_symmetric(sparsemat); } /* -------------------------------------------------------------------- */ /* Convert graph, if needed */ if (graph) { mysparsemat=&real_sparsemat; IGRAPH_CHECK(igraph_get_sparsemat(graph, mysparsemat)); IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat); } /* -------------------------------------------------------------------- */ /* Compute eigenpairs, if needed */ if (tmp_vectors) { vectors=&myvectors; IGRAPH_MATRIX_INIT_FINALLY(vectors, no_of_nodes, no_of_ev); } if (do_vectors || tmp_vectors) { igraph_arpack_options_t options; igraph_eigen_which_t which; igraph_matrix_t tmp; igraph_vector_t tmpev; igraph_vector_t tmpeval; int i; which.pos = IGRAPH_EIGEN_SELECT; which.il = (int) (no_of_nodes-evmax+1); which.iu = (int) (no_of_nodes-evmin+1); if (values) { IGRAPH_VECTOR_INIT_FINALLY(&tmpeval, 0); } IGRAPH_CHECK(igraph_matrix_init(&tmp, no_of_nodes, which.iu-which.il+1)); IGRAPH_FINALLY(igraph_matrix_destroy, &tmp); IGRAPH_CHECK(igraph_eigen_matrix_symmetric(matrix, mysparsemat, /* fun= */ 0, no_of_nodes, /* extra= */ 0, /* algorithm= */ use_arpack ? IGRAPH_EIGEN_ARPACK : IGRAPH_EIGEN_LAPACK, &which, &options, /*storage=*/ 0, values ? &tmpeval : 0, &tmp)); IGRAPH_VECTOR_INIT_FINALLY(&tmpev, no_of_ev); for (i=0; i #include #include using namespace prpack; using namespace std; void prpack_solver::initialize() { geg = NULL; gsg = NULL; sg = NULL; sccg = NULL; owns_bg = true; } prpack_solver::prpack_solver(const prpack_csc* g) { initialize(); TIME(read_time, bg = new prpack_base_graph(g)); } prpack_solver::prpack_solver(const prpack_int64_csc* g) { initialize(); TIME(read_time, bg = new prpack_base_graph(g)); } prpack_solver::prpack_solver(const prpack_csr* g) { initialize(); TIME(read_time, bg = new prpack_base_graph(g)); } prpack_solver::prpack_solver(const prpack_edge_list* g) { initialize(); TIME(read_time, bg = new prpack_base_graph(g)); } prpack_solver::prpack_solver(prpack_base_graph* g, bool owns_bg) { initialize(); this->owns_bg = owns_bg; TIME(read_time, bg = g); } prpack_solver::prpack_solver(const char* filename, const char* format, const bool weighted) { initialize(); TIME(read_time, bg = new prpack_base_graph(filename, format, weighted)); } prpack_solver::~prpack_solver() { if (owns_bg) { delete bg; } delete geg; delete gsg; delete sg; delete sccg; } int prpack_solver::get_num_vs() { return bg->num_vs; } prpack_result* prpack_solver::solve(const double alpha, const double tol, const char* method) { return solve(alpha, tol, NULL, NULL, method); } prpack_result* prpack_solver::solve( const double alpha, const double tol, const double* u, const double* v, const char* method) { double preprocess_time = 0; double compute_time = 0; prpack_result* ret = NULL; // decide which method to run string m; if (strcmp(method, "") != 0) m = string(method); else { if (bg->num_vs < 128) m = "ge"; else if (sccg != NULL) m = "sccgs"; else if (sg != NULL) m = "sg"; else m = "sccgs"; if (u != v) m += "_uv"; } // run the appropriate method if (m == "ge") { if (geg == NULL) { TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg)); } TIME(compute_time, ret = solve_via_ge( alpha, tol, geg->num_vs, geg->matrix, u)); } else if (m == "ge_uv") { if (geg == NULL) { TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg)); } TIME(compute_time, ret = solve_via_ge_uv( alpha, tol, geg->num_vs, geg->matrix, geg->d, u, v)); } else if (m == "gs") { if (gsg == NULL) { TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg)); } TIME(compute_time, ret = solve_via_gs( alpha, tol, gsg->num_vs, gsg->num_es, gsg->heads, gsg->tails, gsg->vals, gsg->ii, gsg->d, gsg->num_outlinks, u, v)); } else if (m == "gserr") { if (gsg == NULL) { TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg)); } TIME(compute_time, ret = solve_via_gs_err( alpha, tol, gsg->num_vs, gsg->num_es, gsg->heads, gsg->tails, gsg->ii, gsg->num_outlinks, u, v)); } else if (m == "sgs") { if (sg == NULL) { TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg)); } TIME(compute_time, ret = solve_via_schur_gs( alpha, tol, sg->num_vs, sg->num_no_in_vs, sg->num_no_out_vs, sg->num_es, sg->heads, sg->tails, sg->vals, sg->ii, sg->d, sg->num_outlinks, u, sg->encoding, sg->decoding)); } else if (m == "sgs_uv") { if (sg == NULL) { TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg)); } TIME(compute_time, ret = solve_via_schur_gs_uv( alpha, tol, sg->num_vs, sg->num_no_in_vs, sg->num_no_out_vs, sg->num_es, sg->heads, sg->tails, sg->vals, sg->ii, sg->d, sg->num_outlinks, u, v, sg->encoding, sg->decoding)); } else if (m == "sccgs") { if (sccg == NULL) { TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg)); } TIME(compute_time, ret = solve_via_scc_gs( alpha, tol, sccg->num_vs, sccg->num_es_inside, sccg->heads_inside, sccg->tails_inside, sccg->vals_inside, sccg->num_es_outside, sccg->heads_outside, sccg->tails_outside, sccg->vals_outside, sccg->ii, sccg->d, sccg->num_outlinks, u, sccg->num_comps, sccg->divisions, sccg->encoding, sccg->decoding)); } else if (m == "sccgs_uv") { if (sccg == NULL) { TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg)); } TIME(compute_time, ret = solve_via_scc_gs_uv( alpha, tol, sccg->num_vs, sccg->num_es_inside, sccg->heads_inside, sccg->tails_inside, sccg->vals_inside, sccg->num_es_outside, sccg->heads_outside, sccg->tails_outside, sccg->vals_outside, sccg->ii, sccg->d, sccg->num_outlinks, u, v, sccg->num_comps, sccg->divisions, sccg->encoding, sccg->decoding)); } else { // TODO: throw exception } ret->method = m.c_str(); ret->read_time = read_time; ret->preprocess_time = preprocess_time; ret->compute_time = compute_time; ret->num_vs = bg->num_vs; ret->num_es = bg->num_es; return ret; } // VARIOUS SOLVING METHODS //////////////////////////////////////////////////////////////////////// prpack_result* prpack_solver::solve_via_ge( const double alpha, const double tol, const int num_vs, const double* matrix, const double* uv) { prpack_result* ret = new prpack_result(); // initialize uv values const double uv_const = 1.0/num_vs; const int uv_exists = (uv) ? 1 : 0; uv = (uv) ? uv : &uv_const; // create matrix A double* A = new double[num_vs*num_vs]; for (int i = 0; i < num_vs*num_vs; ++i) A[i] = -alpha*matrix[i]; for (int i = 0; i < num_vs*num_vs; i += num_vs + 1) ++A[i]; // create vector b double* b = new double[num_vs]; for (int i = 0; i < num_vs; ++i) b[i] = uv[uv_exists*i]; // solve and normalize ge(num_vs, A, b); normalize(num_vs, b); // clean up and return delete[] A; ret->num_es_touched = -1; ret->x = b; return ret; } prpack_result* prpack_solver::solve_via_ge_uv( const double alpha, const double tol, const int num_vs, const double* matrix, const double* d, const double* u, const double* v) { prpack_result* ret = new prpack_result(); // initialize u and v values const double u_const = 1.0/num_vs; const double v_const = 1.0/num_vs; const int u_exists = (u) ? 1 : 0; const int v_exists = (v) ? 1 : 0; u = (u) ? u : &u_const; v = (v) ? v : &v_const; // create matrix A double* A = new double[num_vs*num_vs]; for (int i = 0; i < num_vs*num_vs; ++i) A[i] = -alpha*matrix[i]; for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) for (int j = 0; j < num_vs; ++j) A[inum_vs + j] -= alpha*u[u_exists*i]*d[j]; for (int i = 0; i < num_vs*num_vs; i += num_vs + 1) ++A[i]; // create vector b double* b = new double[num_vs]; for (int i = 0; i < num_vs; ++i) b[i] = (1 - alpha)*v[v_exists*i]; // solve ge(num_vs, A, b); // clean up and return delete[] A; ret->num_es_touched = -1; ret->x = b; return ret; } // Vanilla Gauss-Seidel. prpack_result* prpack_solver::solve_via_gs( const double alpha, const double tol, const int num_vs, const int num_es, const int* heads, const int* tails, const double* vals, const double* ii, const double* d, const double* num_outlinks, const double* u, const double* v) { prpack_result* ret = new prpack_result(); const bool weighted = vals != NULL; // initialize u and v values const double u_const = 1.0/num_vs; const double v_const = 1.0/num_vs; const int u_exists = (u) ? 1 : 0; const int v_exists = (v) ? 1 : 0; u = (u) ? u : &u_const; v = (v) ? v : &v_const; // initialize the eigenvector (and use personalization vector) double* x = new double[num_vs]; for (int i = 0; i < num_vs; ++i) x[i] = 0; // initialize delta double delta = 0; // run Gauss-Seidel ret->num_es_touched = 0; double err = 1, c = 0; do { if (weighted) { for (int i = 0; i < num_vs; ++i) { double new_val = 0; const int start_j = tails[i]; const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; for (int j = start_j; j < end_j; ++j) // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads[j]]*vals[j]; new_val = alpha*new_val + (1 - alpha)*v[v_exists*i]; delta -= alpha*x[i]*d[i]; new_val += delta*u[u_exists*i]; new_val /= 1 - alpha*(d[i]*u[u_exists*i] + (1 - d[i])*ii[i]); delta += alpha*new_val*d[i]; COMPENSATED_SUM(err, x[i] - new_val, c); x[i] = new_val; } } else { for (int i = 0; i < num_vs; ++i) { const double old_val = x[i]*num_outlinks[i]; double new_val = 0; const int start_j = tails[i]; const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; for (int j = start_j; j < end_j; ++j) // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads[j]]; new_val = alpha*new_val + (1 - alpha)*v[v_exists*i]; if (num_outlinks[i] < 0) { delta -= alpha*old_val; new_val += delta*u[u_exists*i]; new_val /= 1 - alpha*u[u_exists*i]; delta += alpha*new_val; } else { new_val += delta*u[u_exists*i]; new_val /= 1 - alpha*ii[i]; } COMPENSATED_SUM(err, old_val - new_val, c); x[i] = new_val/num_outlinks[i]; } } // update iteration index ret->num_es_touched += num_es; } while (err >= tol); // undo num_outlinks transformation if (!weighted) for (int i = 0; i < num_vs; ++i) x[i] *= num_outlinks[i]; // return results ret->x = x; return ret; } // Implement a gauss-seidel-like process with a strict error bound // we return a solution with 1-norm error less than tol. prpack_result* prpack_solver::solve_via_gs_err( const double alpha, const double tol, const int num_vs, const int num_es, const int* heads, const int* tails, const double* ii, const double* num_outlinks, const double* u, const double* v) { prpack_result* ret = new prpack_result(); // initialize u and v values const double u_const = 1.0/num_vs; const double v_const = 1.0/num_vs; const int u_exists = (u) ? 1 : 0; const int v_exists = (v) ? 1 : 0; u = (u) ? u : &u_const; v = (v) ? v : &v_const; // Note to Dave, we can't rescale v because we could be running this // same routine from multiple threads. // initialize the eigenvector (and use personalization vector) double* x = new double[num_vs]; for (int i = 0; i < num_vs; ++i) { x[i] = 0.; } // initialize delta double delta = 0.; // run Gauss-Seidel, note that we store x/deg[i] throughout this // iteration. long long maxedges = (long long)((double)num_es*std::min( log(tol)/log(alpha), (double)PRPACK_SOLVER_MAX_ITERS)); ret->num_es_touched = 0; double err=1., c = 0.; do { // iterate through vertices for (int i = 0; i < num_vs; ++i) { double old_val = x[i]*num_outlinks[i]; // adjust back to the "true" value. double new_val = 0.; int start_j = tails[i], end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; for (int j = start_j; j < end_j; ++j) { // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads[j]]; } new_val = alpha*new_val + alpha*ii[i]*old_val + (1.0-alpha)*v[v_exists*i]; new_val += delta*u[u_exists*i]; // add the dangling node adjustment if (num_outlinks[i] < 0) { delta += alpha*(new_val - old_val); } // note that new_val > old_val, but the fabs is just for COMPENSATED_SUM(err, -(new_val - old_val), c); x[i] = new_val/num_outlinks[i]; } // update iteration index ret->num_es_touched += num_es; } while (err >= tol && ret->num_es_touched < maxedges); if (err >= tol) { ret->converged = 0; } else { ret->converged = 1; } // undo num_outlinks transformation for (int i = 0; i < num_vs; ++i) x[i] *= num_outlinks[i]; // return results ret->x = x; return ret; } // Gauss-Seidel using the Schur complement to separate dangling nodes. prpack_result* prpack_solver::solve_via_schur_gs( const double alpha, const double tol, const int num_vs, const int num_no_in_vs, const int num_no_out_vs, const int num_es, const int* heads, const int* tails, const double* vals, const double* ii, const double* d, const double* num_outlinks, const double* uv, const int* encoding, const int* decoding, const bool should_normalize) { prpack_result* ret = new prpack_result(); const bool weighted = vals != NULL; // initialize uv values const double uv_const = 1.0/num_vs; const int uv_exists = (uv) ? 1 : 0; uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const; // initialize the eigenvector (and use personalization vector) double* x = new double[num_vs]; for (int i = 0; i < num_vs - num_no_out_vs; ++i) x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]); // run Gauss-Seidel for the top left part of (I - alpha*P)*x = uv ret->num_es_touched = 0; double err, c; do { // iterate through vertices int num_es_touched = 0; err = c = 0; #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64) for (int i = num_no_in_vs; i < num_vs - num_no_out_vs; ++i) { double new_val = 0; const int start_j = tails[i]; const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; if (weighted) { for (int j = start_j; j < end_j; ++j) // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads[j]]*vals[j]; COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c); new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); x[i] = new_val; } else { for (int j = start_j; j < end_j; ++j) // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads[j]]; COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c); new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); x[i] = new_val/num_outlinks[i]; } num_es_touched += end_j - start_j; } // update iteration index ret->num_es_touched += num_es_touched; } while (err/(1 - alpha) >= tol); // solve for the dangling nodes int num_es_touched = 0; #pragma omp parallel for reduction(+:num_es_touched) schedule(dynamic, 64) for (int i = num_vs - num_no_out_vs; i < num_vs; ++i) { x[i] = 0; const int start_j = tails[i]; const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; for (int j = start_j; j < end_j; ++j) x[i] += x[heads[j]]*((weighted) ? vals[j] : 1); x[i] = (alpha*x[i] + uv[uv_exists*i])/(1 - alpha*ii[i]); num_es_touched += end_j - start_j; } ret->num_es_touched += num_es_touched; // undo num_outlinks transformation if (!weighted) for (int i = 0; i < num_vs - num_no_out_vs; ++i) x[i] *= num_outlinks[i]; // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v if (should_normalize) normalize(num_vs, x); // return results ret->x = prpack_utils::permute(num_vs, x, decoding); delete[] x; if (uv_exists) delete[] uv; return ret; } prpack_result* prpack_solver::solve_via_schur_gs_uv( const double alpha, const double tol, const int num_vs, const int num_no_in_vs, const int num_no_out_vs, const int num_es, const int* heads, const int* tails, const double* vals, const double* ii, const double* d, const double* num_outlinks, const double* u, const double* v, const int* encoding, const int* decoding) { // solve uv = u prpack_result* ret_u = solve_via_schur_gs( alpha, tol, num_vs, num_no_in_vs, num_no_out_vs, num_es, heads, tails, vals, ii, d, num_outlinks, u, encoding, decoding, false); // solve uv = v prpack_result* ret_v = solve_via_schur_gs( alpha, tol, num_vs, num_no_in_vs, num_no_out_vs, num_es, heads, tails, vals, ii, d, num_outlinks, v, encoding, decoding, false); // combine the u and v cases return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v); } /** Gauss-Seidel using strongly connected components. * Notes: * If not weighted, then we store x[i] = "x[i]/outdegree" to * avoid additional arithmetic. We don't do this for the weighted * case because the adjustment may not be constant. */ prpack_result* prpack_solver::solve_via_scc_gs( const double alpha, const double tol, const int num_vs, const int num_es_inside, const int* heads_inside, const int* tails_inside, const double* vals_inside, const int num_es_outside, const int* heads_outside, const int* tails_outside, const double* vals_outside, const double* ii, const double* d, const double* num_outlinks, const double* uv, const int num_comps, const int* divisions, const int* encoding, const int* decoding, const bool should_normalize) { prpack_result* ret = new prpack_result(); const bool weighted = vals_inside != NULL; // initialize uv values const double uv_const = 1.0/num_vs; const int uv_exists = (uv) ? 1 : 0; uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const; // CHECK initialize the solution with one iteration of GS from x=0. double* x = new double[num_vs]; for (int i = 0; i < num_vs; ++i) x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]); // create x_outside double* x_outside = new double[num_vs]; // run Gauss-Seidel for (I - alpha*P)*x = uv ret->num_es_touched = 0; for (int comp_i = 0; comp_i < num_comps; ++comp_i) { const int start_comp = divisions[comp_i]; const int end_comp = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs; const bool parallelize = end_comp - start_comp > 512; // initialize relevant x_outside values for (int i = start_comp; i < end_comp; ++i) { x_outside[i] = 0; const int start_j = tails_outside[i]; const int end_j = (i + 1 != num_vs) ? tails_outside[i + 1] : num_es_outside; for (int j = start_j; j < end_j; ++j) x_outside[i] += x[heads_outside[j]]*((weighted) ? vals_outside[j] : 1.); ret->num_es_touched += end_j - start_j; } double err, c; do { int num_es_touched = 0; err = c = 0; if (parallelize) { // iterate through vertices #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64) for (int i = start_comp; i < end_comp; ++i) { double new_val = x_outside[i]; const int start_j = tails_inside[i]; const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside; if (weighted) { for (int j = start_j; j < end_j; ++j) { // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads_inside[j]]*vals_inside[j]; } COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c); x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); } else { for (int j = start_j; j < end_j; ++j) { // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads_inside[j]]; } COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c); x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i]; } num_es_touched += end_j - start_j; } } else { for (int i = start_comp; i < end_comp; ++i) { double new_val = x_outside[i]; const int start_j = tails_inside[i]; const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside; if (weighted) { for (int j = start_j; j < end_j; ++j) { // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads_inside[j]]*vals_inside[j]; } COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c); x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); } else { for (int j = start_j; j < end_j; ++j) { // TODO: might want to use compensation summation for large: end_j - start_j new_val += x[heads_inside[j]]; } COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c); x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i]; } num_es_touched += end_j - start_j; } } // update iteration index ret->num_es_touched += num_es_touched; } while (err/(1 - alpha) >= tol*(end_comp - start_comp)/num_vs); } // undo num_outlinks transformation if (!weighted) for (int i = 0; i < num_vs; ++i) x[i] *= num_outlinks[i]; // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v if (should_normalize) normalize(num_vs, x); // return results ret->x = prpack_utils::permute(num_vs, x, decoding); delete[] x; delete[] x_outside; if (uv_exists) delete[] uv; return ret; } prpack_result* prpack_solver::solve_via_scc_gs_uv( const double alpha, const double tol, const int num_vs, const int num_es_inside, const int* heads_inside, const int* tails_inside, const double* vals_inside, const int num_es_outside, const int* heads_outside, const int* tails_outside, const double* vals_outside, const double* ii, const double* d, const double* num_outlinks, const double* u, const double* v, const int num_comps, const int* divisions, const int* encoding, const int* decoding) { // solve uv = u prpack_result* ret_u = solve_via_scc_gs( alpha, tol, num_vs, num_es_inside, heads_inside, tails_inside, vals_inside, num_es_outside, heads_outside, tails_outside, vals_outside, ii, d, num_outlinks, u, num_comps, divisions, encoding, decoding, false); // solve uv = v prpack_result* ret_v = solve_via_scc_gs( alpha, tol, num_vs, num_es_inside, heads_inside, tails_inside, vals_inside, num_es_outside, heads_outside, tails_outside, vals_outside, ii, d, num_outlinks, v, num_comps, divisions, encoding, decoding, false); // combine u and v return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v); } // VARIOUS HELPER METHODS ///////////////////////////////////////////////////////////////////////// // Run Gaussian-Elimination (note: this changes A and returns the solution in b) void prpack_solver::ge(const int sz, double* A, double* b) { // put into triangular form for (int i = 0, isz = 0; i < sz; ++i, isz += sz) for (int k = 0, ksz = 0; k < i; ++k, ksz += sz) if (A[isz + k] != 0) { const double coeff = A[isz + k]/A[ksz + k]; A[isz + k] = 0; for (int j = k + 1; j < sz; ++j) A[isz + j] -= coeff*A[ksz + j]; b[i] -= coeff*b[k]; } // backwards substitution for (int i = sz - 1, isz = (sz - 1)*sz; i >= 0; --i, isz -= sz) { for (int j = i + 1; j < sz; ++j) b[i] -= A[isz + j]*b[j]; b[i] /= A[isz + i]; } } // Normalize a vector to sum to 1. void prpack_solver::normalize(const int length, double* x) { double norm = 0, c = 0; for (int i = 0; i < length; ++i) { COMPENSATED_SUM(norm, x[i], c); } norm = 1/norm; for (int i = 0; i < length; ++i) x[i] *= norm; } // Combine u and v results. prpack_result* prpack_solver::combine_uv( const int num_vs, const double* d, const double* num_outlinks, const int* encoding, const double alpha, const prpack_result* ret_u, const prpack_result* ret_v) { prpack_result* ret = new prpack_result(); const bool weighted = d != NULL; double delta_u = 0; double delta_v = 0; for (int i = 0; i < num_vs; ++i) { if ((weighted) ? (d[encoding[i]] == 1) : (num_outlinks[encoding[i]] < 0)) { delta_u += ret_u->x[i]; delta_v += ret_v->x[i]; } } const double s = ((1 - alpha)*alpha*delta_v)/(1 - alpha*delta_u); const double t = 1 - alpha; ret->x = new double[num_vs]; for (int i = 0; i < num_vs; ++i) ret->x[i] = s*ret_u->x[i] + t*ret_v->x[i]; ret->num_es_touched = ret_u->num_es_touched + ret_v->num_es_touched; // clean up and return delete ret_u; delete ret_v; return ret; } igraph/src/glpfhv.c0000644000176000001440000006553612325527073014022 0ustar ripleyusers/* glpfhv.c (LP basis factorization, FHV eta file version) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpfhv.h" #include "glpenv.h" #define xfault xerror /* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ #define M_MAX 100000000 /* = 100*10^6 */ /* maximal order of the basis matrix */ /*********************************************************************** * NAME * * fhv_create_it - create LP basis factorization * * SYNOPSIS * * #include "glpfhv.h" * FHV *fhv_create_it(void); * * DESCRIPTION * * The routine fhv_create_it creates a program object, which represents * a factorization of LP basis. * * RETURNS * * The routine fhv_create_it returns a pointer to the object created. */ FHV *fhv_create_it(void) { FHV *fhv; fhv = xmalloc(sizeof(FHV)); fhv->m_max = fhv->m = 0; fhv->valid = 0; fhv->luf = luf_create_it(); fhv->hh_max = 50; fhv->hh_nfs = 0; fhv->hh_ind = fhv->hh_ptr = fhv->hh_len = NULL; fhv->p0_row = fhv->p0_col = NULL; fhv->cc_ind = NULL; fhv->cc_val = NULL; fhv->upd_tol = 1e-6; fhv->nnz_h = 0; return fhv; } /*********************************************************************** * NAME * * fhv_factorize - compute LP basis factorization * * SYNOPSIS * * #include "glpfhv.h" * int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j, * int ind[], double val[]), void *info); * * DESCRIPTION * * The routine fhv_factorize computes the factorization of the basis * matrix B specified by the routine col. * * The parameter fhv specified the basis factorization data structure * created by the routine fhv_create_it. * * The parameter m specifies the order of B, m > 0. * * The formal routine col specifies the matrix B to be factorized. To * obtain j-th column of A the routine fhv_factorize calls the routine * col with the parameter j (1 <= j <= n). In response the routine col * should store row indices and numerical values of non-zero elements * of j-th column of B to locations ind[1,...,len] and val[1,...,len], * respectively, where len is the number of non-zeros in j-th column * returned on exit. Neither zero nor duplicate elements are allowed. * * The parameter info is a transit pointer passed to the routine col. * * RETURNS * * 0 The factorization has been successfully computed. * * FHV_ESING * The specified matrix is singular within the working precision. * * FHV_ECOND * The specified matrix is ill-conditioned. * * For more details see comments to the routine luf_factorize. * * ALGORITHM * * The routine fhv_factorize calls the routine luf_factorize (see the * module GLPLUF), which actually computes LU-factorization of the basis * matrix B in the form * * [B] = (F, V, P, Q), * * where F and V are such matrices that * * B = F * V, * * and P and Q are such permutation matrices that the matrix * * L = P * F * inv(P) * * is lower triangular with unity diagonal, and the matrix * * U = P * V * Q * * is upper triangular. * * In order to build the complete representation of the factorization * (see formula (1) in the file glpfhv.h) the routine fhv_factorize just * additionally sets H = I and P0 = P. */ int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j, int ind[], double val[]), void *info) { int ret; if (m < 1) xfault("fhv_factorize: m = %d; invalid parameter\n", m); if (m > M_MAX) xfault("fhv_factorize: m = %d; matrix too big\n", m); fhv->m = m; /* invalidate the factorization */ fhv->valid = 0; /* allocate/reallocate arrays, if necessary */ if (fhv->hh_ind == NULL) fhv->hh_ind = xcalloc(1+fhv->hh_max, sizeof(int)); if (fhv->hh_ptr == NULL) fhv->hh_ptr = xcalloc(1+fhv->hh_max, sizeof(int)); if (fhv->hh_len == NULL) fhv->hh_len = xcalloc(1+fhv->hh_max, sizeof(int)); if (fhv->m_max < m) { if (fhv->p0_row != NULL) xfree(fhv->p0_row); if (fhv->p0_col != NULL) xfree(fhv->p0_col); if (fhv->cc_ind != NULL) xfree(fhv->cc_ind); if (fhv->cc_val != NULL) xfree(fhv->cc_val); fhv->m_max = m + 100; fhv->p0_row = xcalloc(1+fhv->m_max, sizeof(int)); fhv->p0_col = xcalloc(1+fhv->m_max, sizeof(int)); fhv->cc_ind = xcalloc(1+fhv->m_max, sizeof(int)); fhv->cc_val = xcalloc(1+fhv->m_max, sizeof(double)); } /* try to factorize the basis matrix */ switch (luf_factorize(fhv->luf, m, col, info)) { case 0: break; case LUF_ESING: ret = FHV_ESING; goto done; case LUF_ECOND: ret = FHV_ECOND; goto done; default: xassert(fhv != fhv); } /* the basis matrix has been successfully factorized */ fhv->valid = 1; /* H := I */ fhv->hh_nfs = 0; /* P0 := P */ memcpy(&fhv->p0_row[1], &fhv->luf->pp_row[1], sizeof(int) * m); memcpy(&fhv->p0_col[1], &fhv->luf->pp_col[1], sizeof(int) * m); /* currently H has no factors */ fhv->nnz_h = 0; ret = 0; done: /* return to the calling program */ return ret; } /*********************************************************************** * NAME * * fhv_h_solve - solve system H*x = b or H'*x = b * * SYNOPSIS * * #include "glpfhv.h" * void fhv_h_solve(FHV *fhv, int tr, double x[]); * * DESCRIPTION * * The routine fhv_h_solve solves either the system H*x = b (if the * flag tr is zero) or the system H'*x = b (if the flag tr is non-zero), * where the matrix H is a component of the factorization specified by * the parameter fhv, H' is a matrix transposed to H. * * On entry the array x should contain elements of the right-hand side * vector b in locations x[1], ..., x[m], where m is the order of the * matrix H. On exit this array will contain elements of the solution * vector x in the same locations. */ void fhv_h_solve(FHV *fhv, int tr, double x[]) { int nfs = fhv->hh_nfs; int *hh_ind = fhv->hh_ind; int *hh_ptr = fhv->hh_ptr; int *hh_len = fhv->hh_len; int *sv_ind = fhv->luf->sv_ind; double *sv_val = fhv->luf->sv_val; int i, k, beg, end, ptr; double temp; if (!fhv->valid) xfault("fhv_h_solve: the factorization is not valid\n"); if (!tr) { /* solve the system H*x = b */ for (k = 1; k <= nfs; k++) { i = hh_ind[k]; temp = x[i]; beg = hh_ptr[k]; end = beg + hh_len[k] - 1; for (ptr = beg; ptr <= end; ptr++) temp -= sv_val[ptr] * x[sv_ind[ptr]]; x[i] = temp; } } else { /* solve the system H'*x = b */ for (k = nfs; k >= 1; k--) { i = hh_ind[k]; temp = x[i]; if (temp == 0.0) continue; beg = hh_ptr[k]; end = beg + hh_len[k] - 1; for (ptr = beg; ptr <= end; ptr++) x[sv_ind[ptr]] -= sv_val[ptr] * temp; } } return; } /*********************************************************************** * NAME * * fhv_ftran - perform forward transformation (solve system B*x = b) * * SYNOPSIS * * #include "glpfhv.h" * void fhv_ftran(FHV *fhv, double x[]); * * DESCRIPTION * * The routine fhv_ftran performs forward transformation, i.e. solves * the system B*x = b, where B is the basis matrix, x is the vector of * unknowns to be computed, b is the vector of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. */ void fhv_ftran(FHV *fhv, double x[]) { int *pp_row = fhv->luf->pp_row; int *pp_col = fhv->luf->pp_col; int *p0_row = fhv->p0_row; int *p0_col = fhv->p0_col; if (!fhv->valid) xfault("fhv_ftran: the factorization is not valid\n"); /* B = F*H*V, therefore inv(B) = inv(V)*inv(H)*inv(F) */ fhv->luf->pp_row = p0_row; fhv->luf->pp_col = p0_col; luf_f_solve(fhv->luf, 0, x); fhv->luf->pp_row = pp_row; fhv->luf->pp_col = pp_col; fhv_h_solve(fhv, 0, x); luf_v_solve(fhv->luf, 0, x); return; } /*********************************************************************** * NAME * * fhv_btran - perform backward transformation (solve system B'*x = b) * * SYNOPSIS * * #include "glpfhv.h" * void fhv_btran(FHV *fhv, double x[]); * * DESCRIPTION * * The routine fhv_btran performs backward transformation, i.e. solves * the system B'*x = b, where B' is a matrix transposed to the basis * matrix B, x is the vector of unknowns to be computed, b is the vector * of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. */ void fhv_btran(FHV *fhv, double x[]) { int *pp_row = fhv->luf->pp_row; int *pp_col = fhv->luf->pp_col; int *p0_row = fhv->p0_row; int *p0_col = fhv->p0_col; if (!fhv->valid) xfault("fhv_btran: the factorization is not valid\n"); /* B = F*H*V, therefore inv(B') = inv(F')*inv(H')*inv(V') */ luf_v_solve(fhv->luf, 1, x); fhv_h_solve(fhv, 1, x); fhv->luf->pp_row = p0_row; fhv->luf->pp_col = p0_col; luf_f_solve(fhv->luf, 1, x); fhv->luf->pp_row = pp_row; fhv->luf->pp_col = pp_col; return; } /*********************************************************************** * NAME * * fhv_update_it - update LP basis factorization * * SYNOPSIS * * #include "glpfhv.h" * int fhv_update_it(FHV *fhv, int j, int len, const int ind[], * const double val[]); * * DESCRIPTION * * The routine fhv_update_it updates the factorization of the basis * matrix B after replacing its j-th column by a new vector. * * The parameter j specifies the number of column of B, which has been * replaced, 1 <= j <= m, where m is the order of B. * * Row indices and numerical values of non-zero elements of the new * column of B should be placed in locations ind[1], ..., ind[len] and * val[1], ..., val[len], resp., where len is the number of non-zeros * in the column. Neither zero nor duplicate elements are allowed. * * RETURNS * * 0 The factorization has been successfully updated. * * FHV_ESING * The adjacent basis matrix is structurally singular, since after * changing j-th column of matrix V by the new column (see algorithm * below) the case k1 > k2 occured. * * FHV_ECHECK * The factorization is inaccurate, since after transforming k2-th * row of matrix U = P*V*Q, its diagonal element u[k2,k2] is zero or * close to zero, * * FHV_ELIMIT * Maximal number of H factors has been reached. * * FHV_EROOM * Overflow of the sparse vector area. * * In case of non-zero return code the factorization becomes invalid. * It should not be used until it has been recomputed with the routine * fhv_factorize. * * ALGORITHM * * The routine fhv_update_it is based on the transformation proposed by * Forrest and Tomlin. * * Let j-th column of the basis matrix B have been replaced by new * column B[j]. In order to keep the equality B = F*H*V j-th column of * matrix V should be replaced by the column inv(F*H)*B[j]. * * From the standpoint of matrix U = P*V*Q, replacement of j-th column * of matrix V is equivalent to replacement of k1-th column of matrix U, * where k1 is determined by permutation matrix Q. Thus, matrix U loses * its upper triangular form and becomes the following: * * 1 k1 k2 m * 1 x x * x x x x x x x * . x * x x x x x x x * k1 . . * x x x x x x x * . . * x x x x x x x * . . * . x x x x x x * . . * . . x x x x x * . . * . . . x x x x * k2 . . * . . . . x x x * . . . . . . . . x x * m . . . . . . . . . x * * where row index k2 corresponds to the lowest non-zero element of * k1-th column. * * The routine moves rows and columns k1+1, k1+2, ..., k2 of matrix U * by one position to the left and upwards and moves k1-th row and k1-th * column to position k2. As the result of such symmetric permutations * matrix U becomes the following: * * 1 k1 k2 m * 1 x x x x x x x * x x * . x x x x x x * x x * k1 . . x x x x x * x x * . . . x x x x * x x * . . . . x x x * x x * . . . . . x x * x x * . . . . . . x * x x * k2 . . x x x x x * x x * . . . . . . . . x x * m . . . . . . . . . x * * Then the routine performs gaussian elimination to eliminate elements * u[k2,k1], u[k2,k1+1], ..., u[k2,k2-1] using diagonal elements * u[k1,k1], u[k1+1,k1+1], ..., u[k2-1,k2-1] as pivots in the same way * as described in comments to the routine luf_factorize (see the module * GLPLUF). Note that actually all operations are performed on matrix V, * not on matrix U. During the elimination process the routine permutes * neither rows nor columns, so only k2-th row of matrix U is changed. * * To keep the main equality B = F*H*V, each time when the routine * applies elementary gaussian transformation to the transformed row of * matrix V (which corresponds to k2-th row of matrix U), it also adds * a new element (gaussian multiplier) to the current row-like factor * of matrix H, which corresponds to the transformed row of matrix V. */ int fhv_update_it(FHV *fhv, int j, int len, const int ind[], const double val[]) { int m = fhv->m; LUF *luf = fhv->luf; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; int *vr_cap = luf->vr_cap; double *vr_piv = luf->vr_piv; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *vc_cap = luf->vc_cap; int *pp_row = luf->pp_row; int *pp_col = luf->pp_col; int *qq_row = luf->qq_row; int *qq_col = luf->qq_col; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; double *work = luf->work; double eps_tol = luf->eps_tol; int *hh_ind = fhv->hh_ind; int *hh_ptr = fhv->hh_ptr; int *hh_len = fhv->hh_len; int *p0_row = fhv->p0_row; int *p0_col = fhv->p0_col; int *cc_ind = fhv->cc_ind; double *cc_val = fhv->cc_val; double upd_tol = fhv->upd_tol; int i, i_beg, i_end, i_ptr, j_beg, j_end, j_ptr, k, k1, k2, p, q, p_beg, p_end, p_ptr, ptr, ret; double f, temp; if (!fhv->valid) xfault("fhv_update_it: the factorization is not valid\n"); if (!(1 <= j && j <= m)) xfault("fhv_update_it: j = %d; column number out of range\n", j); /* check if the new factor of matrix H can be created */ if (fhv->hh_nfs == fhv->hh_max) { /* maximal number of updates has been reached */ fhv->valid = 0; ret = FHV_ELIMIT; goto done; } /* convert new j-th column of B to dense format */ for (i = 1; i <= m; i++) cc_val[i] = 0.0; for (k = 1; k <= len; k++) { i = ind[k]; if (!(1 <= i && i <= m)) xfault("fhv_update_it: ind[%d] = %d; row number out of rang" "e\n", k, i); if (cc_val[i] != 0.0) xfault("fhv_update_it: ind[%d] = %d; duplicate row index no" "t allowed\n", k, i); if (val[k] == 0.0) xfault("fhv_update_it: val[%d] = %g; zero element not allow" "ed\n", k, val[k]); cc_val[i] = val[k]; } /* new j-th column of V := inv(F * H) * (new B[j]) */ fhv->luf->pp_row = p0_row; fhv->luf->pp_col = p0_col; luf_f_solve(fhv->luf, 0, cc_val); fhv->luf->pp_row = pp_row; fhv->luf->pp_col = pp_col; fhv_h_solve(fhv, 0, cc_val); /* convert new j-th column of V to sparse format */ len = 0; for (i = 1; i <= m; i++) { temp = cc_val[i]; if (temp == 0.0 || fabs(temp) < eps_tol) continue; len++, cc_ind[len] = i, cc_val[len] = temp; } /* clear old content of j-th column of matrix V */ j_beg = vc_ptr[j]; j_end = j_beg + vc_len[j] - 1; for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) { /* get row index of v[i,j] */ i = sv_ind[j_ptr]; /* find v[i,j] in the i-th row */ i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; for (i_ptr = i_beg; sv_ind[i_ptr] != j; i_ptr++) /* nop */; xassert(i_ptr <= i_end); /* remove v[i,j] from the i-th row */ sv_ind[i_ptr] = sv_ind[i_end]; sv_val[i_ptr] = sv_val[i_end]; vr_len[i]--; } /* now j-th column of matrix V is empty */ luf->nnz_v -= vc_len[j]; vc_len[j] = 0; /* add new elements of j-th column of matrix V to corresponding row lists; determine indices k1 and k2 */ k1 = qq_row[j], k2 = 0; for (ptr = 1; ptr <= len; ptr++) { /* get row index of v[i,j] */ i = cc_ind[ptr]; /* at least one unused location is needed in i-th row */ if (vr_len[i] + 1 > vr_cap[i]) { if (luf_enlarge_row(luf, i, vr_len[i] + 10)) { /* overflow of the sparse vector area */ fhv->valid = 0; luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); ret = FHV_EROOM; goto done; } } /* add v[i,j] to i-th row */ i_ptr = vr_ptr[i] + vr_len[i]; sv_ind[i_ptr] = j; sv_val[i_ptr] = cc_val[ptr]; vr_len[i]++; /* adjust index k2 */ if (k2 < pp_col[i]) k2 = pp_col[i]; } /* capacity of j-th column (which is currently empty) should be not less than len locations */ if (vc_cap[j] < len) { if (luf_enlarge_col(luf, j, len)) { /* overflow of the sparse vector area */ fhv->valid = 0; luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); ret = FHV_EROOM; goto done; } } /* add new elements of matrix V to j-th column list */ j_ptr = vc_ptr[j]; memmove(&sv_ind[j_ptr], &cc_ind[1], len * sizeof(int)); memmove(&sv_val[j_ptr], &cc_val[1], len * sizeof(double)); vc_len[j] = len; luf->nnz_v += len; /* if k1 > k2, diagonal element u[k2,k2] of matrix U is zero and therefore the adjacent basis matrix is structurally singular */ if (k1 > k2) { fhv->valid = 0; ret = FHV_ESING; goto done; } /* perform implicit symmetric permutations of rows and columns of matrix U */ i = pp_row[k1], j = qq_col[k1]; for (k = k1; k < k2; k++) { pp_row[k] = pp_row[k+1], pp_col[pp_row[k]] = k; qq_col[k] = qq_col[k+1], qq_row[qq_col[k]] = k; } pp_row[k2] = i, pp_col[i] = k2; qq_col[k2] = j, qq_row[j] = k2; /* now i-th row of the matrix V is k2-th row of matrix U; since no pivoting is used, only this row will be transformed */ /* copy elements of i-th row of matrix V to the working array and remove these elements from matrix V */ for (j = 1; j <= m; j++) work[j] = 0.0; i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) { /* get column index of v[i,j] */ j = sv_ind[i_ptr]; /* store v[i,j] to the working array */ work[j] = sv_val[i_ptr]; /* find v[i,j] in the j-th column */ j_beg = vc_ptr[j]; j_end = j_beg + vc_len[j] - 1; for (j_ptr = j_beg; sv_ind[j_ptr] != i; j_ptr++) /* nop */; xassert(j_ptr <= j_end); /* remove v[i,j] from the j-th column */ sv_ind[j_ptr] = sv_ind[j_end]; sv_val[j_ptr] = sv_val[j_end]; vc_len[j]--; } /* now i-th row of matrix V is empty */ luf->nnz_v -= vr_len[i]; vr_len[i] = 0; /* create the next row-like factor of the matrix H; this factor corresponds to i-th (transformed) row */ fhv->hh_nfs++; hh_ind[fhv->hh_nfs] = i; /* hh_ptr[] will be set later */ hh_len[fhv->hh_nfs] = 0; /* up to (k2 - k1) free locations are needed to add new elements to the non-trivial row of the row-like factor */ if (luf->sv_end - luf->sv_beg < k2 - k1) { luf_defrag_sva(luf); if (luf->sv_end - luf->sv_beg < k2 - k1) { /* overflow of the sparse vector area */ fhv->valid = luf->valid = 0; luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); ret = FHV_EROOM; goto done; } } /* eliminate subdiagonal elements of matrix U */ for (k = k1; k < k2; k++) { /* v[p,q] = u[k,k] */ p = pp_row[k], q = qq_col[k]; /* this is the crucial point, where even tiny non-zeros should not be dropped */ if (work[q] == 0.0) continue; /* compute gaussian multiplier f = v[i,q] / v[p,q] */ f = work[q] / vr_piv[p]; /* perform gaussian transformation: (i-th row) := (i-th row) - f * (p-th row) in order to eliminate v[i,q] = u[k2,k] */ p_beg = vr_ptr[p]; p_end = p_beg + vr_len[p] - 1; for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) work[sv_ind[p_ptr]] -= f * sv_val[p_ptr]; /* store new element (gaussian multiplier that corresponds to p-th row) in the current row-like factor */ luf->sv_end--; sv_ind[luf->sv_end] = p; sv_val[luf->sv_end] = f; hh_len[fhv->hh_nfs]++; } /* set pointer to the current row-like factor of the matrix H (if no elements were added to this factor, it is unity matrix and therefore can be discarded) */ if (hh_len[fhv->hh_nfs] == 0) fhv->hh_nfs--; else { hh_ptr[fhv->hh_nfs] = luf->sv_end; fhv->nnz_h += hh_len[fhv->hh_nfs]; } /* store new pivot which corresponds to u[k2,k2] */ vr_piv[i] = work[qq_col[k2]]; /* new elements of i-th row of matrix V (which are non-diagonal elements u[k2,k2+1], ..., u[k2,m] of matrix U = P*V*Q) now are contained in the working array; add them to matrix V */ len = 0; for (k = k2+1; k <= m; k++) { /* get column index and value of v[i,j] = u[k2,k] */ j = qq_col[k]; temp = work[j]; /* if v[i,j] is close to zero, skip it */ if (fabs(temp) < eps_tol) continue; /* at least one unused location is needed in j-th column */ if (vc_len[j] + 1 > vc_cap[j]) { if (luf_enlarge_col(luf, j, vc_len[j] + 10)) { /* overflow of the sparse vector area */ fhv->valid = 0; luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); ret = FHV_EROOM; goto done; } } /* add v[i,j] to j-th column */ j_ptr = vc_ptr[j] + vc_len[j]; sv_ind[j_ptr] = i; sv_val[j_ptr] = temp; vc_len[j]++; /* also store v[i,j] to the auxiliary array */ len++, cc_ind[len] = j, cc_val[len] = temp; } /* capacity of i-th row (which is currently empty) should be not less than len locations */ if (vr_cap[i] < len) { if (luf_enlarge_row(luf, i, len)) { /* overflow of the sparse vector area */ fhv->valid = 0; luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); ret = FHV_EROOM; goto done; } } /* add new elements to i-th row list */ i_ptr = vr_ptr[i]; memmove(&sv_ind[i_ptr], &cc_ind[1], len * sizeof(int)); memmove(&sv_val[i_ptr], &cc_val[1], len * sizeof(double)); vr_len[i] = len; luf->nnz_v += len; /* updating is finished; check that diagonal element u[k2,k2] is not very small in absolute value among other elements in k2-th row and k2-th column of matrix U = P*V*Q */ /* temp = max(|u[k2,*]|, |u[*,k2]|) */ temp = 0.0; /* walk through k2-th row of U which is i-th row of V */ i = pp_row[k2]; i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) if (temp < fabs(sv_val[i_ptr])) temp = fabs(sv_val[i_ptr]); /* walk through k2-th column of U which is j-th column of V */ j = qq_col[k2]; j_beg = vc_ptr[j]; j_end = j_beg + vc_len[j] - 1; for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) if (temp < fabs(sv_val[j_ptr])) temp = fabs(sv_val[j_ptr]); /* check that u[k2,k2] is not very small */ if (fabs(vr_piv[i]) < upd_tol * temp) { /* the factorization seems to be inaccurate and therefore must be recomputed */ fhv->valid = 0; ret = FHV_ECHECK; goto done; } /* the factorization has been successfully updated */ ret = 0; done: /* return to the calling program */ return ret; } /*********************************************************************** * NAME * * fhv_delete_it - delete LP basis factorization * * SYNOPSIS * * #include "glpfhv.h" * void fhv_delete_it(FHV *fhv); * * DESCRIPTION * * The routine fhv_delete_it deletes LP basis factorization specified * by the parameter fhv and frees all memory allocated to this program * object. */ void fhv_delete_it(FHV *fhv) { luf_delete_it(fhv->luf); if (fhv->hh_ind != NULL) xfree(fhv->hh_ind); if (fhv->hh_ptr != NULL) xfree(fhv->hh_ptr); if (fhv->hh_len != NULL) xfree(fhv->hh_len); if (fhv->p0_row != NULL) xfree(fhv->p0_row); if (fhv->p0_col != NULL) xfree(fhv->p0_col); if (fhv->cc_ind != NULL) xfree(fhv->cc_ind); if (fhv->cc_val != NULL) xfree(fhv->cc_val); xfree(fhv); return; } /* eof */ igraph/src/foreign-pajek-lexer.l0000644000176000001440000001537712325372071016375 0ustar ripleyusers/* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-pajek-header.h" #include "foreign-pajek-parser.h" #define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); %} %option noyywrap %option prefix="igraph_pajek_yy" %option outfile="lex.yy.c" %option nounput %option noinput %option reentrant %option bison-bridge %option bison-locations digit [0-9] word [^ \t\r\n] %% [ \t]* { } %[^\n]*\n[\r]* { } %[^\n]*\r[\n]* { } \*[Nn][eE][Tt] { return NETWORKLINE; } \*[Nn][Ee][Tt][Ww][Oo][Rr][Kk] { return NETWORKLINE; } \*[Vv][Ee][Rr][Tt][Ii][Cc][Ee][Ss] { return VERTICESLINE; } \*[Aa][Rr][Cc][Ss] { return ARCSLINE; } \*[Ee][Dd][Gg][Ee][Ss] { return EDGESLINE; } \*[Aa][Rr][Cc][Ss][Ll][Ii][Ss][Tt] { return ARCSLISTLINE; } \*[Ee][Dd][Gg][Ee][Ss][Ll][Ii][Ss][Tt] { return EDGESLISTLINE; } \*[Mm][Aa][Tt][Rr][Ii][Xx] { return MATRIXLINE; } \n\r|\r\n|\n|\r { yyextra->mode=0; return NEWLINE; } \"[^\"]*\" { return QSTR; } \([^\)]*\) { return PSTR; } \-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } [Xx]_[Ff][Aa][Cc][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } } [Yy]_[Ff][Aa][Cc][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } } [Ii][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } } [Bb][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } } [Bb][Ww]/[ \t\n\r] { if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } } [Pp][Hh][Ii]/[ \t\n\r] { if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } } [Rr]/[ \t\n\r] { if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } } [Qq]/[ \t\n\r] { if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } } [Ff][Oo][Nn][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } } [Uu][Rr][Ll]/[ \t\n\r] { if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } } [Cc]/[ \t\n\r] { if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } } [Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } } [Ss]/[ \t\n\r] { if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } } [Aa]/[ \t\n\r] { if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } } [Ww]/[ \t\n\r] { if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } } [Hh]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } } [Hh]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } } [Aa]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } } [Aa]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } } [Kk]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } } [Kk]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } } [Aa][Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } } [Ll]/[ \t\n\r] { if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } } [Ll][Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } } [Ll][Pp][Hh][Ii]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LPHI; } else if (yyextra->mode==2) { return EP_LPHI; } else { return ALNUM; } } [Ll][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LC; } else if (yyextra->mode==2) { return EP_LC; } else { return ALNUM; } } [Ll][Rr]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LR; } else if (yyextra->mode==2) { return EP_LR; } else { return ALNUM; } } [Ll][Aa]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LA; } else if (yyextra->mode==2) { return EP_LA; } else { return ALNUM; } } [Ss][Ii][Zz][Ee]/[ \t\n\r] { if (yyextra->mode==1) { return VP_SIZE; } else if (yyextra->mode==2) { return EP_SIZE; } else { return ALNUM; } } [Ff][Oo][Ss]/[ \t\n\r] { if (yyextra->mode==1) { return VP_FOS; } else if (yyextra->mode==2) { return EP_FOS; } else { return ALNUM; } } {word}+ { return ALNUM; } <> { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return NEWLINE; } } %% igraph/src/cs_permute.c0000644000176000001440000000362512325527073014671 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* C = A(p,q) where p and q are permutations of 0..m-1 and 0..n-1. */ cs *cs_permute (const cs *A, const CS_INT *pinv, const CS_INT *q, CS_INT values) { CS_INT t, j, k, nz = 0, m, n, *Ap, *Ai, *Cp, *Ci ; CS_ENTRY *Cx, *Ax ; cs *C ; if (!CS_CSC (A)) return (NULL) ; /* check inputs */ m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; C = cs_spalloc (m, n, Ap [n], values && Ax != NULL, 0) ; /* alloc result */ if (!C) return (cs_done (C, NULL, NULL, 0)) ; /* out of memory */ Cp = C->p ; Ci = C->i ; Cx = C->x ; for (k = 0 ; k < n ; k++) { Cp [k] = nz ; /* column k of C is column q[k] of A */ j = q ? (q [k]) : k ; for (t = Ap [j] ; t < Ap [j+1] ; t++) { if (Cx) Cx [nz] = Ax [t] ; /* row i of A is row pinv[i] of C */ Ci [nz++] = pinv ? (pinv [Ai [t]]) : Ai [t] ; } } Cp [n] = nz ; /* finalize the last column of C */ return (cs_done (C, NULL, NULL, 1)) ; } igraph/src/separators.c0000644000176000001440000006361412325527074014713 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_separators.h" #include "igraph_memory.h" #include "igraph_adjlist.h" #include "igraph_dqueue.h" #include "igraph_vector.h" #include "igraph_interface.h" #include "igraph_flow.h" #include "igraph_flow_internal.h" #include "igraph_components.h" #include "igraph_structural.h" #include "igraph_constructors.h" #include "igraph_stack.h" #include "igraph_interrupt_internal.h" int igraph_i_is_separator(const igraph_t *graph, igraph_vit_t *vit, long int except, igraph_bool_t *res, igraph_vector_bool_t *removed, igraph_dqueue_t *Q, igraph_vector_t *neis, long int no_of_nodes) { long int start=0; if (IGRAPH_VIT_SIZE(*vit) >= no_of_nodes-1) { /* Just need to check that we really have n-1 vertices in it */ igraph_vector_bool_t hit; long int nohit=0; IGRAPH_CHECK(igraph_vector_bool_init(&hit, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &hit); for (IGRAPH_VIT_RESET(*vit); !IGRAPH_VIT_END(*vit); IGRAPH_VIT_NEXT(*vit)) { long int v=IGRAPH_VIT_GET(*vit); if (!VECTOR(hit)[v]) { nohit++; VECTOR(hit)[v] = 1; } } igraph_vector_bool_destroy(&hit); IGRAPH_FINALLY_CLEAN(1); if (nohit == no_of_nodes-1) { *res = 1; return 0; } } /* Remove the given vertices from the graph, do a breadth-first search and check the number of components */ if (except < 0) { for (IGRAPH_VIT_RESET(*vit); !IGRAPH_VIT_END(*vit); IGRAPH_VIT_NEXT(*vit)) { VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; } } else { /* There is an exception */ long int i; for (i=0, IGRAPH_VIT_RESET(*vit); iThis implementation first checks that the given * candidate is a separator, by calling \ref * igraph_is_separator(). If it is a separator, then it checks that * each subset of size n-1, where n is the size of the candidate, is * not a separator. * \param graph The input graph. It may be directed, but edge * directions are ignored. * \param candidate Pointer to a vector of long integers, the * candidate minimal separator. * \param res Pointer to a boolean variable, the result is stored * here. * \return Error code. * * Time complexity: O(n(|V|+|E|)), |V| is the number of vertices, |E| * is the number of edges, n is the number vertices in the candidate * separator. * * \example examples/simple/igraph_is_minimal_separator.c */ int igraph_is_minimal_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_bool_t removed; igraph_dqueue_t Q; igraph_vector_t neis; long int candsize; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, candidate, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); candsize=IGRAPH_VIT_SIZE(vit); IGRAPH_CHECK(igraph_vector_bool_init(&removed, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed); IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); /* Is it a separator at all? */ IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, -1, res, &removed, &Q, &neis, no_of_nodes)); if (!(*res)) { /* Not a separator at all, nothing to do, *res is already set */ } else if (candsize == 0) { /* Nothing to do, minimal, *res is already set */ } else { /* General case, we need to remove each vertex from 'candidate' * and check whether the remainder is a separator. If this is * false for all vertices, then 'candidate' is a minimal * separator. */ long int i; for (i=0, *res=0; iSee more about the implemented algorithm in * Anne Berry, Jean-Paul Bordat and Olivier Cogis: Generating All the * Minimal Separators of a Graph, In: Peter Widmayer, Gabriele Neyer * and Stephan Eidenbenz (editors): Graph-theoretic concepts in * computer science, 1665, 167--172, 1999. Springer. * * \param graph The input graph. It may be directed, but edge * directions are ignored. * \param separators An initialized pointer vector, the separators * are stored here. It is a list of pointers to igraph_vector_t * objects. Each vector will contain the ids of the vertices in * the separator. * To free all memory allocated for \c separators, you need call * \ref igraph_vector_destroy() and then \ref igraph_free() on * each element, before destroying the pointer vector itself. * \return Error code. * * Time complexity: O(n|V|^3), |V| is the number of vertices, n is the * number of separators. * * \example examples/simple/igraph_minimal_separators.c */ int igraph_all_minimal_st_separators(const igraph_t *graph, igraph_vector_ptr_t *separators) { /* * Some notes about the tricks used here. For finding the components * of the graph after removing some vertices, we do the * following. First we mark the vertices with the actual mark stamp * (mark), then run breadth-first search on the graph, but not * considering the marked vertices. Then we increase the mark. If * there is integer overflow here, then we zero out the mark and set * it to one. (We might as well just always zero it out.) * * For each separator the vertices are stored in vertex id order. * This facilitates the comparison of the separators when we find a * potential new candidate. * * To keep track of which separator we already used as a basis, we * keep a boolean vector (already_tried). The try_next pointer show * the next separator to try as a basis. */ long int no_of_nodes=igraph_vcount(graph); igraph_vector_t leaveout; igraph_vector_bool_t already_tried; long int try_next=0; unsigned long int mark=1; long int v; igraph_adjlist_t adjlist; igraph_vector_t components; igraph_dqueue_t Q; igraph_vector_t sorter; igraph_vector_ptr_clear(separators); IGRAPH_FINALLY(igraph_i_separators_free, separators); IGRAPH_CHECK(igraph_vector_init(&leaveout, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_destroy, &leaveout); IGRAPH_CHECK(igraph_vector_bool_init(&already_tried, 0)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &already_tried); IGRAPH_CHECK(igraph_vector_init(&components, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &components); IGRAPH_CHECK(igraph_vector_reserve(&components, no_of_nodes*2)); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); IGRAPH_CHECK(igraph_vector_init(&sorter, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &sorter); IGRAPH_CHECK(igraph_vector_reserve(&sorter, no_of_nodes)); /* --------------------------------------------------------------- * INITIALIZATION, we check whether the neighborhoods of the * vertices separate the graph. The ones that do will form the * initial basis. */ for (v=0; vThe implementation is based on the following paper: * Arkady Kanevsky: Finding all minimum-size separating vertex sets in * a graph, Networks 23, 533--541, 1993. * * \param graph The input graph, it may be directed, but edge * directions will be ignored. * \param separators An initialized pointer vector, the separators * are stored here. It is a list of pointers to igraph_vector_t * objects. Each vector will contain the ids of the vertices in * the separator. * To free all memory allocated for \c separators, you need call * \ref igraph_vector_destroy() and then \ref igraph_free() on * each element, before destroying the pointer vector itself. * \return Error code. * * Time complexity: TODO. * * \example examples/simple/igraph_minimum_size_separators.c */ int igraph_minimum_size_separators(const igraph_t *graph, igraph_vector_ptr_t *separators) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_integer_t conn; long int k; igraph_vector_t X; long int i, j; igraph_bool_t issepX; igraph_t Gbar; igraph_vector_t phi; igraph_t graph_copy; igraph_vector_t capacity; igraph_maxflow_stats_t stats; igraph_vector_ptr_clear(separators); IGRAPH_FINALLY(igraph_i_separators_free, separators); /* ---------------------------------------------------------------- */ /* 1 Find the vertex connectivity of 'graph' */ IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /* checks= */ 1)); k=conn; /* Special cases for low connectivity, two exits here! */ if (conn==0) { /* Nothing to do */ IGRAPH_FINALLY_CLEAN(1); /* separators */ return 0; } else if (conn==1) { igraph_vector_t ap; long int i, n; IGRAPH_VECTOR_INIT_FINALLY(&ap, 0); IGRAPH_CHECK(igraph_articulation_points(graph, &ap)); n=igraph_vector_size(&ap); IGRAPH_CHECK(igraph_vector_ptr_resize(separators, n)); igraph_vector_ptr_null(separators); for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_progress.h" #include "config.h" static IGRAPH_THREAD_LOCAL igraph_progress_handler_t *igraph_i_progress_handler=0; static IGRAPH_THREAD_LOCAL char igraph_i_progressmsg_buffer[1000]; /** * \function igraph_progress * Report progress * * Note that the usual way to report progress is the \ref IGRAPH_PROGRESS * macro, as that takes care of the return value of the progress * handler. * \param message A string describing the function or algorithm * that is reporting the progress. Current igraph functions * always use the name \p message argument if reporting from the * same function. * \param percent Numeric, the percentage that was completed by the * algorithm or function. * \param data User-defined data. Current igraph functions that * report progress pass a null pointer here. Users can * write their own progress handlers and functions with progress * reporting, and then pass some meaningfull context here. * \return If there is a progress handler installed and * it does not return \c IGRAPH_SUCCESS, then \c IGRAPH_INTERRUPTED * is returned. * * Time complexity: O(1). */ int igraph_progress(const char *message, igraph_real_t percent, void *data) { if (igraph_i_progress_handler) { if (igraph_i_progress_handler(message, percent, data) != IGRAPH_SUCCESS) return IGRAPH_INTERRUPTED; } return IGRAPH_SUCCESS; } /** * \function igraph_progressf * Report progress, printf-like version * * This is a more flexible version of \ref igraph_progress(), with * a printf-like template string. First the template string * is filled with the additional arguments and then \ref * igraph_progress() is called. * * Note that there is an upper limit for the length of * the \p message string, currently 1000 characters. * \param message A string describing the function or algorithm * that is reporting the progress. For this function this is a * template string, using the same syntax as the standard * \c libc \c printf function. * \param percent Numeric, the percentage that was completed by the * algorithm or function. * \param data User-defined data. Current igraph functions that * report progress pass a null pointer here. Users can * write their own progress handlers and functions with progress * reporting, and then pass some meaningfull context here. * \param ... Additional argument that were specified in the * \p message argument. * \return If there is a progress handler installed and * it does not return \c IGRAPH_SUCCESS, then \c IGRAPH_INTERRUPTED * is returned. * \return */ int igraph_progressf(const char *message, igraph_real_t percent, void *data, ...) { va_list ap; va_start(ap, data); vsnprintf(igraph_i_progressmsg_buffer, sizeof(igraph_i_progressmsg_buffer) / sizeof(char), message, ap); return igraph_progress(igraph_i_progressmsg_buffer, percent, data); } #ifndef USING_R /** * \function igraph_progress_handler_stderr * A simple predefined progress handler * * This simple progress handler first prints \p message, and then * the percentage complete value in a short message to standard error. * \param message A string describing the function or algorithm * that is reporting the progress. Current igraph functions * always use the name \p message argument if reporting from the * same function. * \param percent Numeric, the percentage that was completed by the * algorithm or function. * \param data User-defined data. Current igraph functions that * report progress pass a null pointer here. Users can * write their own progress handlers and functions with progress * reporting, and then pass some meaningfull context here. * \return This function always returns with \c IGRAPH_SUCCESS. * * Time complexity: O(1). */ int igraph_progress_handler_stderr(const char *message, igraph_real_t percent, void* data) { IGRAPH_UNUSED(data); fputs(message, stderr); fprintf(stderr, "%.1f percent ready\n", (double)percent); return 0; } #endif /** * \function igraph_set_progress_handler * Install a progress handler, or remove the current handler * * There is a single simple predefined progress handler: * \ref igraph_progress_handler_stderr(). * \param new_handler Pointer to a function of type * \ref igraph_progress_handler_t, the progress handler function to * install. To uninstall the current progress handler, this argument * can be a null pointer. * \return Pointer to the previously installed progress handler function. * * Time complexity: O(1). */ igraph_progress_handler_t * igraph_set_progress_handler(igraph_progress_handler_t new_handler) { igraph_progress_handler_t *previous_handler=igraph_i_progress_handler; igraph_i_progress_handler = new_handler; return previous_handler; } igraph/src/amd_info.c0000644000176000001440000001065612325527072014300 0ustar ripleyusers/* ========================================================================= */ /* === AMD_info ============================================================ */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* User-callable. Prints the output statistics for AMD. See amd.h * for details. If the Info array is not present, nothing is printed. */ #include "amd_internal.h" #define PRI(format,x) { if (x >= 0) { PRINTF ((format, x)) ; }} GLOBAL void AMD_info ( double Info [ ] ) { double n, ndiv, nmultsubs_ldl, nmultsubs_lu, lnz, lnzd ; PRINTF (("\nAMD version %d.%d.%d, %s, results:\n", AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE)) ; if (!Info) { return ; } n = Info [AMD_N] ; ndiv = Info [AMD_NDIV] ; nmultsubs_ldl = Info [AMD_NMULTSUBS_LDL] ; nmultsubs_lu = Info [AMD_NMULTSUBS_LU] ; lnz = Info [AMD_LNZ] ; lnzd = (n >= 0 && lnz >= 0) ? (n + lnz) : (-1) ; /* AMD return status */ PRINTF ((" status: ")) ; if (Info [AMD_STATUS] == AMD_OK) { PRINTF (("OK\n")) ; } else if (Info [AMD_STATUS] == AMD_OUT_OF_MEMORY) { PRINTF (("out of memory\n")) ; } else if (Info [AMD_STATUS] == AMD_INVALID) { PRINTF (("invalid matrix\n")) ; } else if (Info [AMD_STATUS] == AMD_OK_BUT_JUMBLED) { PRINTF (("OK, but jumbled\n")) ; } else { PRINTF (("unknown\n")) ; } /* statistics about the input matrix */ PRI (" n, dimension of A: %.20g\n", n); PRI (" nz, number of nonzeros in A: %.20g\n", Info [AMD_NZ]) ; PRI (" symmetry of A: %.4f\n", Info [AMD_SYMMETRY]) ; PRI (" number of nonzeros on diagonal: %.20g\n", Info [AMD_NZDIAG]) ; PRI (" nonzeros in pattern of A+A' (excl. diagonal): %.20g\n", Info [AMD_NZ_A_PLUS_AT]) ; PRI (" # dense rows/columns of A+A': %.20g\n", Info [AMD_NDENSE]) ; /* statistics about AMD's behavior */ PRI (" memory used, in bytes: %.20g\n", Info [AMD_MEMORY]) ; PRI (" # of memory compactions: %.20g\n", Info [AMD_NCMPA]) ; /* statistics about the ordering quality */ PRINTF (("\n" " The following approximate statistics are for a subsequent\n" " factorization of A(P,P) + A(P,P)'. They are slight upper\n" " bounds if there are no dense rows/columns in A+A', and become\n" " looser if dense rows/columns exist.\n\n")) ; PRI (" nonzeros in L (excluding diagonal): %.20g\n", lnz) ; PRI (" nonzeros in L (including diagonal): %.20g\n", lnzd) ; PRI (" # divide operations for LDL' or LU: %.20g\n", ndiv) ; PRI (" # multiply-subtract operations for LDL': %.20g\n", nmultsubs_ldl) ; PRI (" # multiply-subtract operations for LU: %.20g\n", nmultsubs_lu) ; PRI (" max nz. in any column of L (incl. diagonal): %.20g\n", Info [AMD_DMAX]) ; /* total flop counts for various factorizations */ if (n >= 0 && ndiv >= 0 && nmultsubs_ldl >= 0 && nmultsubs_lu >= 0) { PRINTF (("\n" " chol flop count for real A, sqrt counted as 1 flop: %.20g\n" " LDL' flop count for real A: %.20g\n" " LDL' flop count for complex A: %.20g\n" " LU flop count for real A (with no pivoting): %.20g\n" " LU flop count for complex A (with no pivoting): %.20g\n\n", n + ndiv + 2*nmultsubs_ldl, ndiv + 2*nmultsubs_ldl, 9*ndiv + 8*nmultsubs_ldl, ndiv + 2*nmultsubs_lu, 9*ndiv + 8*nmultsubs_lu)) ; } } igraph/src/cs_ereach.c0000644000176000001440000000373412325527073014440 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* find nonzero pattern of Cholesky L(k,1:k-1) using etree and triu(A(:,k)) */ CS_INT cs_ereach (const cs *A, CS_INT k, const CS_INT *parent, CS_INT *s, CS_INT *w) { CS_INT i, p, n, len, top, *Ap, *Ai ; if (!CS_CSC (A) || !parent || !s || !w) return (-1) ; /* check inputs */ top = n = A->n ; Ap = A->p ; Ai = A->i ; CS_MARK (w, k) ; /* mark node k as visited */ for (p = Ap [k] ; p < Ap [k+1] ; p++) { i = Ai [p] ; /* A(i,k) is nonzero */ if (i > k) continue ; /* only use upper triangular part of A */ for (len = 0 ; !CS_MARKED (w,i) ; i = parent [i]) /* traverse up etree*/ { s [len++] = i ; /* L(k,i) is nonzero */ CS_MARK (w, i) ; /* mark i as visited */ } while (len > 0) s [--top] = s [--len] ; /* push path onto stack */ } for (p = top ; p < n ; p++) CS_MARK (w, s [p]) ; /* unmark all nodes */ CS_MARK (w, k) ; /* unmark node k */ return (top) ; /* s [top..n-1] contains pattern of L(k,:)*/ } igraph/src/lad.c0000644000176000001440000014621112325527073013262 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The contents of this file was originally taken from the LAD homepage: http://liris.cnrs.fr/csolnon/LAD.html and then modified to fit better into igraph. Unfortunately LAD seems to have no version numbers. The files were apparently last changed on the 29th of June, 2010. The original copyright message follows here. The CeCILL-B V1 license is GPL compatible, because instead of V1, one can freely choose to use V2, and V2 is explicitly GPL compatible. */ /* This software has been written by Christine Solnon. It is distributed under the CeCILL-B FREE SOFTWARE LICENSE see http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html for more details */ /* Several modifications had to be made to the original LAD implementation to make it compile with non-C99-compliant compilers such as MSVC. In particular, I had to remove all the variable-sized arrays. -- Tamas Nepusz, 11 July 2013 */ #include #include #include #include #include #include #include "igraph_interface.h" #include "igraph_adjlist.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_memory.h" #include "igraph_matrix.h" #include "igraph_interrupt_internal.h" /* define boolean type as char */ #define true 1 #define false 0 #define bool char /* helper to allocate an array of given size */ #define ALLOC_ARRAY(VAR, SIZE, TYPE) { \ VAR = igraph_Calloc(SIZE, TYPE); \ if (VAR == 0) { \ IGRAPH_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, VAR); \ } /* ---------------------------------------------------------*/ /* Coming from graph.c */ /* ---------------------------------------------------------*/ typedef struct{ long int nbVertices; /* Number of vertices */ igraph_vector_t nbSucc; igraph_adjlist_t succ; igraph_matrix_char_t isEdge; } Tgraph; int igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { long int i, j, n; long int no_of_nodes=igraph_vcount(igraph); igraph_vector_int_t *neis; IGRAPH_VECTOR_INIT_FINALLY(&graph->nbSucc, no_of_nodes); IGRAPH_CHECK(igraph_degree(igraph, &graph->nbSucc, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); graph->nbVertices = no_of_nodes; IGRAPH_CHECK(igraph_adjlist_init(igraph, &graph->succ, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &graph->succ); IGRAPH_CHECK(igraph_matrix_char_init(&graph->isEdge, no_of_nodes, no_of_nodes)); IGRAPH_FINALLY(igraph_matrix_char_destroy, &graph->isEdge); for (i=0; isucc, i); n=igraph_vector_int_size(neis); for (j=0; jisEdge, i, v)) { IGRAPH_ERROR("LAD functions only work on simple graphs, " "simplify your graph", IGRAPH_EINVAL); } MATRIX(graph->isEdge, i, v) = 1; } } return 0; } /* ---------------------------------------------------------*/ /* Coming from domains.c */ /* ---------------------------------------------------------*/ typedef struct{ igraph_vector_int_t nbVal; /* nbVal[u] = number of values in D[u] */ igraph_vector_int_t firstVal; /* firstVal[u] = pos in val of the first value of D[u] */ igraph_vector_int_t val; /* val[firstVal[u]..firstVal[u]+nbVal[u]-1] = values of D[u] */ igraph_matrix_int_t posInVal; /* If v in D[u] then firstVal[u] <= posInVal[u][v] < firstVal[u]+nbVal[u] and val[posInVal[u][v]] = v otherwise posInVal[u][v] >= firstVal[u]+nbVal[u] */ int valSize; /* size of val */ igraph_matrix_int_t firstMatch; /* firstMatch[u][v] = pos in match of the first vertex of the covering matching of G_(u, v) */ igraph_vector_int_t matching; /* matching[firstMatch[u][v]..firstMatch[u][v]+nbSucc[u]-1] = covering matching of G_(u, v) */ int nextOutToFilter; /* position in toFilter of the next pattern node whose domain should be filtered (-1 if no domain to filter) */ int lastInToFilter; /* position in toFilter of the last pattern node whose domain should be filtered */ igraph_vector_int_t toFilter; /* contain all pattern nodes whose domain should be filtered */ igraph_vector_char_t markedToFilter; /* markedToFilter[u]=true if u is in toFilter; false otherwise */ igraph_vector_int_t globalMatchingP; /* globalMatchingP[u] = node of Gt matched to u in globalAllDiff(Np) */ igraph_vector_int_t globalMatchingT; /* globalMatchingT[v] = node of Gp matched to v in globalAllDiff(Np) or -1 if v is not matched */ } Tdomain; bool igraph_i_lad_toFilterEmpty(Tdomain* D) { /* return true if there is no more nodes in toFilter */ return (D->nextOutToFilter < 0); } void igraph_i_lad_resetToFilter(Tdomain *D) { /* empty to filter and unmark the vertices that are marked to be filtered */ igraph_vector_char_null(&D->markedToFilter); D->nextOutToFilter = -1; } int igraph_i_lad_nextToFilter(Tdomain* D, int size) { /* precondition: emptyToFilter = false remove a node from toFilter (FIFO) unmark this node and return it */ int u = VECTOR(D->toFilter)[D->nextOutToFilter]; VECTOR(D->markedToFilter)[u] = false; if (D->nextOutToFilter == D->lastInToFilter) { /* u was the last node in tofilter */ D->nextOutToFilter = -1; } else if (D->nextOutToFilter == size-1) { D->nextOutToFilter = 0; } else { D->nextOutToFilter++; } return u; } void igraph_i_lad_addToFilter(int u, Tdomain* D, int size) { /* if u is not marked, then add it to toFilter and mark it */ if (VECTOR(D->markedToFilter)[u]) { return; } VECTOR(D->markedToFilter)[u] = true; if (D->nextOutToFilter < 0) { D->lastInToFilter = 0; D->nextOutToFilter = 0; } else if (D->lastInToFilter == size-1) { D->lastInToFilter = 0; } else { D->lastInToFilter++; } VECTOR(D->toFilter)[D->lastInToFilter] = u; } bool igraph_i_lad_isInD(int u, int v, Tdomain* D) { /* returns true if v belongs to D(u); false otherwise */ return (MATRIX(D->posInVal, u, v) < VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]); } int igraph_i_lad_augmentingPath(int u, Tdomain* D, int nbV, bool* result) { /* return true if there exists an augmenting path starting from u and ending on a free vertex v in the bipartite directed graph G=(U, V, E) such that U=pattern nodes, V=target nodes, and E={(u, v), v in D(u)} U {(v, u), D->globalMatchingP[u]=v} update D-globalMatchingP and D->globalMatchingT consequently */ int *fifo, *pred; bool *marked; int nextIn = 0; int nextOut = 0; int i, v, v2, u2, j; /* Allocate memory */ ALLOC_ARRAY(fifo, nbV, int); ALLOC_ARRAY(pred, nbV, int); ALLOC_ARRAY(marked, nbV, bool); for (i=0; i < VECTOR(D->nbVal)[u]; i++) { v = VECTOR(D->val)[ VECTOR(D->firstVal)[u]+i ]; /* v in D(u) */ if (VECTOR(D->globalMatchingT)[v] < 0) { /* v is free => augmenting path found */ VECTOR(D->globalMatchingP)[u]=v; VECTOR(D->globalMatchingT)[v]=u; *result = true; goto cleanup; } /* v is not free => add it to fifo */ pred[v] = u; fifo[nextIn++] = v; marked[v] = true; } while (nextOut < nextIn) { u2 = VECTOR(D->globalMatchingT)[fifo[nextOut++]]; for (i=0; i < VECTOR(D->nbVal)[u2]; i++) { v = VECTOR(D->val)[ VECTOR(D->firstVal)[u2]+i ]; /* v in D(u2) */ if (VECTOR(D->globalMatchingT)[v] < 0) { /* v is free => augmenting path found */ j=0; while (u2 != u) { /* update global matching wrt path */ if (j>100) { IGRAPH_ERROR("LAD failed", IGRAPH_EINTERNAL); } j++; v2 = VECTOR(D->globalMatchingP)[u2]; VECTOR(D->globalMatchingP)[u2]=v; VECTOR(D->globalMatchingT)[v]=u2; v = v2; u2 = pred[v]; } VECTOR(D->globalMatchingP)[u]=v; VECTOR(D->globalMatchingT)[v]=u; *result = true; goto cleanup; } if (!marked[v]) { /* v is not free and not marked => add it to fifo */ pred[v] = u2; fifo[nextIn++] = v; marked[v] = true; } } } cleanup: igraph_free(fifo); igraph_free(pred); igraph_free(marked); IGRAPH_FINALLY_CLEAN(3); return 0; } int igraph_i_lad_removeAllValuesButOne(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* Gt, bool* result) { /* remove all values but v from D(u) and add all successors of u in toFilter return false if an inconsistency is detected wrt to global all diff */ int j, oldPos, newPos; igraph_vector_int_t *uneis=igraph_adjlist_get(&Gp->succ, u); int n=(int) igraph_vector_int_size(uneis); /* add all successors of u in toFilter */ for (j=0; jnbVertices)); } /* remove all values but v from D[u] */ oldPos = MATRIX(D->posInVal, u, v); newPos = VECTOR(D->firstVal)[u]; VECTOR(D->val)[oldPos] = VECTOR(D->val)[newPos]; VECTOR(D->val)[newPos] = v; MATRIX(D->posInVal, u, VECTOR(D->val)[newPos]) = newPos; MATRIX(D->posInVal, u, VECTOR(D->val)[oldPos]) = oldPos; VECTOR(D->nbVal)[u] = 1; /* update global matchings that support the global all different constraint */ if (VECTOR(D->globalMatchingP)[u] != v) { VECTOR(D->globalMatchingT)[ VECTOR(D->globalMatchingP)[u] ]=-1; VECTOR(D->globalMatchingP)[u] = -1; IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result)); } else { *result = true; } return 0; } int igraph_i_lad_removeValue(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* Gt, bool* result) { /* remove v from D(u) and add all successors of u in toFilter return false if an inconsistency is detected wrt global all diff */ int j; igraph_vector_int_t *uneis=igraph_adjlist_get(&Gp->succ, u); int n=(int) igraph_vector_int_size(uneis); int oldPos, newPos; /* add all successors of u in toFilter */ for (j=0; jnbVertices)); } /* remove v from D[u] */ oldPos = MATRIX(D->posInVal, u, v); VECTOR(D->nbVal)[u]--; newPos = VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]; VECTOR(D->val)[oldPos] = VECTOR(D->val)[newPos]; VECTOR(D->val)[newPos] = v; MATRIX(D->posInVal, u, VECTOR(D->val)[oldPos]) = oldPos; MATRIX(D->posInVal, u, VECTOR(D->val)[newPos]) = newPos; /* update global matchings that support the global all different constraint */ if (VECTOR(D->globalMatchingP)[u] == v) { VECTOR(D->globalMatchingP)[u] = -1; VECTOR(D->globalMatchingT)[v] = -1; IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result)); } else { *result = true; } return 0; } int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, int *invalid) { /* for each u in toBeMatched[0..nb-1], match u to D->val[D->firstVal[u] and filter domains of other non matched vertices wrt FC(Edges) and FC(diff) (this is not mandatory, as LAD is stronger than FC(Edges) and GAC(allDiff) is stronger than FC(diff), but this speeds up the solution process). return false if an inconsistency is detected by FC(Edges) or FC(diff); true otherwise; */ int j, u, v, u2, oldNbVal; igraph_vector_int_t *vneis; bool result; while (nb>0) { u = VECTOR(*toBeMatched)[--nb]; v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; vneis = igraph_adjlist_get(&Gt->succ, v); /* match u to v */ for (u2=0; u2nbVertices; u2++) { if (u != u2) { oldNbVal = VECTOR(D->nbVal)[u2]; if (igraph_i_lad_isInD(u2, v, D)) { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, v, D, Gp, Gt, &result)); if (!result) { *invalid = 1 ; return 0; } } if (MATRIX(Gp->isEdge, u, u2)) { /* remove from D[u2] vertices which are not adjacent to v */ j = VECTOR(D->firstVal)[u2]; while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) { if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) { j++; } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); if (!result) { *invalid = 1; return 0; } } } } else if (induced) { /* (u, u2) is not an edge => remove neighbors of v from D[u2] */ if (VECTOR(D->nbVal)[u2] < VECTOR(Gt->nbSucc)[v]) { j = VECTOR(D->firstVal)[u2]; while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) { if (!MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) { j++; } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); if (!result) { *invalid = 1; return 0; } } } } else { for (j=0; jnbSucc)[v]; j++) { if (igraph_i_lad_isInD(u2, (int) VECTOR(*vneis)[j], D)) { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, (int) VECTOR(*vneis)[j], D, Gp, Gt, &result)); if (!result) { *invalid = 1; return 0; } } } } } if (VECTOR(D->nbVal)[u2] == 0) { *invalid = 1; /* D[u2] is empty */ return 0; } if ((VECTOR(D->nbVal)[u2] == 1) && (oldNbVal > 1)) { VECTOR(*toBeMatched)[nb++]=u2; } } } } *invalid = 0; return 0; } bool igraph_i_lad_matchVertex(int u, bool induced, Tdomain* D, Tgraph* Gp, Tgraph *Gt) { int invalid; /* match u to D->val[D->firstVal[u]] and filter domains of other non matched vertices wrt FC(Edges) and FC(diff) (this is not mandatory, as LAD is stronger than FC(Edges) and GAC(allDiff) is stronger than FC(diff), but this speeds up the solution process). return false if an inconsistency is detected by FC(Edges) or FC(diff); true otherwise; */ igraph_vector_int_t toBeMatched; igraph_vector_int_init(&toBeMatched, Gp->nbVertices); IGRAPH_FINALLY(igraph_vector_int_destroy, &toBeMatched); VECTOR(toBeMatched)[0]=u; igraph_i_lad_matchVertices(1, &toBeMatched, induced, D, Gp, Gt, &invalid); igraph_vector_int_destroy(&toBeMatched); IGRAPH_FINALLY_CLEAN(1); return invalid ? false : true; } int igraph_i_lad_qcompare (void const *a, void const *b) { /* function used by the qsort function */ int pa = *((int*)a) - *((int*)b); return pa; } bool igraph_i_lad_compare(int size_mu, int* mu, int size_mv, int* mv) { /* return true if for every element u of mu there exists a different element v of mv such that u <= v; return false otherwise */ int i, j; qsort(mu, (size_t) size_mu, sizeof(int), igraph_i_lad_qcompare); qsort(mv, (size_t) size_mv, sizeof(int), igraph_i_lad_qcompare); i = size_mv-1; for (j=size_mu-1; j>=0; j--) { if (mu[j]>mv[i]) { return false; } i--; } return true; } int igraph_i_lad_initDomains(bool initialDomains, igraph_vector_ptr_t *domains, Tdomain* D, Tgraph* Gp, Tgraph* Gt, int *empty) { /* for every pattern node u, initialize D(u) with every vertex v such that for every neighbor u' of u there exists a different neighbor v' of v such that degree(u) <= degree(v) if initialDomains, then filter initial domains wrt compatibilities given in file return false if a domain is empty and true otherwise */ int *val; bool *dom; int *mu, *mv; int matchingSize, u, v, i, j; igraph_vector_t *vec; igraph_vector_t *Gp_uneis; igraph_vector_t *Gt_vneis; val = igraph_Calloc(Gp->nbVertices*Gt->nbVertices, int); if (val == 0) { IGRAPH_ERROR("cannot allocated 'val' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); } dom = igraph_Calloc(Gt->nbVertices, bool); if (dom == 0) { igraph_free(val); IGRAPH_ERROR("cannot allocated 'dom' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_vector_int_init(&D->globalMatchingP, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &D->globalMatchingP); igraph_vector_int_fill(&D->globalMatchingP, -1L); IGRAPH_CHECK(igraph_vector_int_init(&D->globalMatchingT, Gt->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &D->globalMatchingT); igraph_vector_int_fill(&D->globalMatchingT, -1L); IGRAPH_CHECK(igraph_vector_int_init(&D->nbVal, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &D->nbVal); IGRAPH_CHECK(igraph_vector_int_init(&D->firstVal, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &D->firstVal); IGRAPH_CHECK(igraph_matrix_int_init(&D->posInVal, Gp->nbVertices, Gt->nbVertices)); IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->posInVal); IGRAPH_CHECK(igraph_matrix_int_init(&D->firstMatch, Gp->nbVertices, Gt->nbVertices)); IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->firstMatch); IGRAPH_CHECK(igraph_vector_char_init(&D->markedToFilter, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_char_destroy, &D->markedToFilter); IGRAPH_CHECK(igraph_vector_int_init(&D->toFilter, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &D->toFilter); D->valSize = 0; matchingSize = 0; for (u=0; unbVertices; u++) { igraph_vector_int_t *Gp_uneis=igraph_adjlist_get(&Gp->succ, u); if (initialDomains) { /* read the list of target vertices which are compatible with u */ vec=VECTOR(*domains)[u]; i=(int) igraph_vector_size(vec); memset(dom, false, sizeof(bool)*(size_t)(Gt->nbVertices)); for (j=0; jmarkedToFilter)[u] = true; VECTOR(D->toFilter)[u] = u; VECTOR(D->nbVal)[u] = 0; VECTOR(D->firstVal)[u] = D->valSize; for (v=0; vnbVertices; v++) { igraph_vector_int_t *Gt_vneis=igraph_adjlist_get(&Gt->succ, v); if ((initialDomains) && (!dom[v])) { /* v not in D(u) */ MATRIX(D->posInVal, u, v) = (int) (VECTOR(D->firstVal)[u] + Gt->nbVertices); } else { MATRIX(D->firstMatch, u, v) = matchingSize; matchingSize += VECTOR(Gp->nbSucc)[u]; if (VECTOR(Gp->nbSucc)[u] <= VECTOR(Gt->nbSucc)[v]) { mu = igraph_Calloc((long int) VECTOR(Gp->nbSucc)[u], int); if (mu == 0) { igraph_free(val); igraph_free(dom); IGRAPH_ERROR("cannot allocate 'mu' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); } mv = igraph_Calloc((long int) VECTOR(Gt->nbSucc)[v], int); if (mv == 0) { igraph_free(mu); igraph_free(val); igraph_free(dom); IGRAPH_ERROR("cannot allocate 'mv' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); } for (i=0; inbSucc)[u]; i++) { mu[i]=(int) VECTOR(Gp->nbSucc)[(long int) VECTOR(*Gp_uneis)[i]]; } for (i=0; inbSucc)[v]; i++) { mv[i]=(int) VECTOR(Gt->nbSucc)[(long int) VECTOR(*Gt_vneis)[i]]; } if (igraph_i_lad_compare((int) VECTOR(Gp->nbSucc)[u], mu, (int) VECTOR(Gt->nbSucc)[v], mv)==1) { val[D->valSize] = v; VECTOR(D->nbVal)[u]++; MATRIX(D->posInVal, u, v) = D->valSize++; } else { /* v not in D(u) */ MATRIX(D->posInVal, u, v) = (int)(VECTOR(D->firstVal)[u] + Gt->nbVertices); } igraph_free(mu); mu = 0; igraph_free(mv); mv = 0; } else { /* v not in D(u) */ MATRIX(D->posInVal, u, v) = (int) (VECTOR(D->firstVal)[u] + Gt->nbVertices); } } } if (VECTOR(D->nbVal)[u] == 0) { *empty = 1; /* empty domain */ igraph_free(val); igraph_free(dom); return 0; } } IGRAPH_CHECK(igraph_vector_int_init(&D->val, D->valSize)); IGRAPH_FINALLY(igraph_vector_int_destroy, &D->val); for (i=0; ivalSize; i++) { VECTOR(D->val)[i] = val[i]; } IGRAPH_CHECK(igraph_vector_int_init(&D->matching, matchingSize)); IGRAPH_FINALLY(igraph_vector_int_destroy, &D->matching); igraph_vector_int_fill(&D->matching, -1); D->nextOutToFilter = 0; D->lastInToFilter = (int) (Gp->nbVertices-1); *empty=0; igraph_free(val); igraph_free(dom); return 0; } /* ---------------------------------------------------------*/ /* Coming from allDiff.c */ /* ---------------------------------------------------------*/ #define white 0 #define grey 1 #define black 2 #define toBeDeleted 3 #define deleted 4 void igraph_i_lad_addToDelete(int u, int* list, int* nb, int* marked) { if (marked[u]sizeOfV) { *invalid = 1; /* trivial case of infeasibility */ return 0; } ALLOC_ARRAY(matchedWithV, sizeOfV, int); ALLOC_ARRAY(nbPred, sizeOfV, int); ALLOC_ARRAY(pred, sizeOfV*sizeOfU, int); ALLOC_ARRAY(nbSucc, sizeOfU, int); ALLOC_ARRAY(succ, sizeOfU*sizeOfV, int); ALLOC_ARRAY(listV, sizeOfV, int); ALLOC_ARRAY(listU, sizeOfU, int); ALLOC_ARRAY(listDV, sizeOfV, int); ALLOC_ARRAY(listDU, sizeOfU, int); ALLOC_ARRAY(markedV, sizeOfV, int); ALLOC_ARRAY(markedU, sizeOfU, int); ALLOC_ARRAY(unmatched, sizeOfU, int); ALLOC_ARRAY(posInUnmatched, sizeOfU, int); IGRAPH_CHECK(igraph_vector_int_init(&path, 0)); IGRAPH_FINALLY(igraph_vector_int_destroy, &path); /* initialize matchedWithV and unmatched */ memset(matchedWithV, -1, (size_t)sizeOfV*sizeof(int)); for (u=0; u= 0) { matchedWithV[VECTOR(*matchedWithU)[u]]=u; } else { posInUnmatched[u]=nbUnmatched; unmatched[nbUnmatched++]=u; } } /* try to match unmatched vertices of U with free vertices of V */ j=0; while (j= 0)); i++) { } if (i == VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]) { j++; /* no free vertex for u */ } else { v=VECTOR(*adj)[i]; /* v is free => match u with v */ VECTOR(*matchedWithU)[u]=v; matchedWithV[v]=u; unmatched[j]=unmatched[--nbUnmatched]; posInUnmatched[unmatched[j]]=j; } } while (nbUnmatched > 0) { /* Try to increase the number of matched vertices */ /* step 1 : build the DAG */ memset(markedU, white, (size_t) sizeOfU*sizeof(int)); memset(nbSucc, 0, (size_t) sizeOfU*sizeof(int)); memset(markedV, white, (size_t) sizeOfV*sizeof(int)); memset(nbPred, 0, (size_t) sizeOfV*sizeof(int)); /* first layer of the DAG from the free nodes of U */ nbV=0; for (j=0; j0)) { /* build next layer from nodes of V to nodes of U */ nbU=0; for (i=0; i0)) { /* v is the final node of an augmenting path */ IGRAPH_CHECK(igraph_vector_int_resize(&path, 1)); VECTOR(path)[0]=v; nbDV=0; nbDU=0; igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV); do{ u=pred[v*sizeOfU + 0]; /* (u, v) belongs to the augmenting path */ IGRAPH_CHECK(igraph_vector_int_push_back(&path, u)); igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU); if (VECTOR(*matchedWithU)[u]!=-1) { /* u is not the initial node of the augmenting path */ v=VECTOR(*matchedWithU)[u]; /* (v, u) belongs to the augmenting path */ IGRAPH_CHECK(igraph_vector_int_push_back(&path, v)); igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV); } } while (VECTOR(*matchedWithU)[u]!=-1); /* delete nodes of listDV and listDU */ while ((nbDV>0) || (nbDU>0)) { while (nbDV>0) { /* delete v */ v=listDV[--nbDV]; markedV[v]=deleted; u=matchedWithV[v]; if (u!=-1) { igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU); } for (i=0; i0) { /* delete u */ u = listDU[--nbDU]; markedU[u]=deleted; v=VECTOR(*matchedWithU)[u]; if (v!=-1) { igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV); } j=0; for (i=0; i1) { u=igraph_vector_int_pop_back(&path); v=igraph_vector_int_pop_back(&path); w=matchedWithV[v]; /* match v with u instead of v with w */ VECTOR(*matchedWithU)[u]=v; matchedWithV[v]=u; } } } } *invalid=0; cleanup: /* Free the allocated arrays */ igraph_vector_int_destroy(&path); igraph_free(posInUnmatched); igraph_free(unmatched); igraph_free(markedU); igraph_free(markedV); igraph_free(listDU); igraph_free(listDV); igraph_free(listU); igraph_free(listV); igraph_free(succ); igraph_free(nbSucc); igraph_free(pred); igraph_free(nbPred); igraph_free(matchedWithV); IGRAPH_FINALLY_CLEAN(14); return 0; } void igraph_i_lad_DFS(int nbU, int nbV, int u, bool* marked, int* nbSucc, int* succ, igraph_vector_int_t * matchedWithU, int* order, int* nb) { /* perform a depth first search, starting from u, in the bipartite graph Go=(U, V, E) such that U = vertices of Gp V = vertices of Gt E = { (u, matchedWithU[u]) / u is a vertex of Gp } U { (v, u) / v is a vertex of D[u] which is not matched to v} Given a vertex v of Gt, nbSucc[v]=number of successors of v and succ[v]=list of successors of v. order[nb^out+1..nb^in] contains the vertices discovered by the DFS */ int i; int v=VECTOR(*matchedWithU)[u]; /* the only one predecessor of v is u */ marked[u]=true; if (v >= 0) { for (i=0; i number it */ order[*nb]=u; (*nb)--; } int igraph_i_lad_SCC(int nbU, int nbV, int* numV, int* numU, int* nbSucc, int* succ, int* nbPred, int* pred, igraph_vector_int_t * matchedWithU, igraph_vector_int_t * matchedWithV) { /* postrelation: numV[v]==numU[u] iff they belong to the same strongly connected component in the bipartite graph Go=(U, V, E) such that U = vertices of Gp V = vertices of Gt E = { (u, matchedWithU[u]) / u is a vertex of Gp } U { (v, u) / v is a vertex of D[u] which is not matched to v} Given a vertex v of Gt, nbSucc[v]=number of sucessors of v and succ[v]=list of successors of v */ int *order; bool *marked; int *fifo; int u, v, i, j, k, nbSCC, nb; /* Allocate memory */ ALLOC_ARRAY(order, nbU, int); ALLOC_ARRAY(marked, nbU, bool); ALLOC_ARRAY(fifo, nbV, int); /* Order vertices of Gp wrt DFS */ nb=nbU-1; for (u=0; u0) { v=fifo[--k]; u=VECTOR(*matchedWithV)[v]; if (u!=-1) { numU[u]=nbSCC; for (j=0; jglobalMatchingP is an all different matching of the pattern vertices postcondition: filter domains wrt GAC(allDiff) return false if an inconsistency is detected; true otherwise Build the bipartite directed graph Go=(U, V, E) such that E = { (u, v) / u is a vertex of Gp which is matched to v (i.e., v=D->globalMatchingP[u])} U { (v, u) / v is a vertex of Gt which is in D(u) but is not matched to u} */ int *nbPred; /* nbPred[u] = nb of predecessors of u in Go */ int *pred; /* pred[u][i] = ith predecessor of u in Go */ int *nbSucc; /* nbSucc[v] = nb of successors of v in Go */ int *succ; /* succ[v][i] = ith successor of v in Go */ int u, v, i, w, oldNbVal, nbToMatch; int *numV, *numU; igraph_vector_int_t toMatch; bool *used; int *list; int nb=0; bool result; /* Allocate memory */ ALLOC_ARRAY(nbPred, Gp->nbVertices, int); ALLOC_ARRAY(pred, Gp->nbVertices*Gt->nbVertices, int); ALLOC_ARRAY(nbSucc, Gt->nbVertices, int); ALLOC_ARRAY(succ, Gt->nbVertices*Gp->nbVertices, int); ALLOC_ARRAY(numV, Gt->nbVertices, int); ALLOC_ARRAY(numU, Gp->nbVertices, int); ALLOC_ARRAY(used, Gp->nbVertices*Gt->nbVertices, bool); ALLOC_ARRAY(list, Gt->nbVertices, int); IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch); for (u=0; unbVertices; u++) { for (i=0; i < VECTOR(D->nbVal)[u]; i++) { v=VECTOR(D->val)[ VECTOR(D->firstVal)[u]+i ]; /* v in D(u) */ used[u*Gt->nbVertices+v]=false; if (v != VECTOR(D->globalMatchingP)[u]) { pred[u*Gt->nbVertices + (nbPred[u]++)]=v; succ[v*Gp->nbVertices + (nbSucc[v]++)]=u; } } } /* mark as used all edges of paths starting from free vertices */ for (v=0; vnbVertices; v++) { if (VECTOR(D->globalMatchingT)[v] < 0) { /* v is free */ list[nb++]=v; numV[v]=true; } } while (nb>0) { v=list[--nb]; for (i=0; inbVertices + i]; used[u*Gt->nbVertices+v]=true; if (numU[u]==false) { numU[u]=true; w=VECTOR(D->globalMatchingP)[u]; used[u*Gt->nbVertices+w]=true; if (numV[w]==false) { list[nb++]=w; numV[w]=true; } } } } /* look for strongly connected components in Go */ IGRAPH_CHECK( igraph_i_lad_SCC((int)(Gp->nbVertices), (int)(Gt->nbVertices), numV, numU, nbSucc, succ, nbPred, pred, &D->globalMatchingP, &D->globalMatchingT)); /* remove v from D[u] if (u, v) is not marked as used and u and v are not in the same SCC and D->globalMatchingP[u] != v */ nbToMatch = 0; for (u=0; unbVertices; u++) { oldNbVal = VECTOR(D->nbVal)[u]; for (i=0; i < VECTOR(D->nbVal)[u]; i++) { v=VECTOR(D->val)[ VECTOR(D->firstVal)[u]+i ]; /* v in D(u) */ if ((!used[u*Gt->nbVertices+v]) && (numV[v]!=numU[u]) && (VECTOR(D->globalMatchingP)[u]!=v)) { IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result)); if (!result) { *invalid = 1; /* Yes, this is ugly. */ goto cleanup; } } } if (VECTOR(D->nbVal)[u] == 0) { *invalid = 1; /* Yes, this is ugly. */ goto cleanup; } if ((oldNbVal>1) && (VECTOR(D->nbVal)[u]==1)) { VECTOR(toMatch)[nbToMatch++] = u; } } IGRAPH_CHECK(igraph_i_lad_matchVertices(nbToMatch, &toMatch, induced, D, Gp, Gt, invalid)); cleanup: igraph_vector_int_destroy(&toMatch); igraph_free(list); igraph_free(used); igraph_free(numU); igraph_free(numV); igraph_free(succ); igraph_free(nbSucc); igraph_free(pred); igraph_free(nbPred); IGRAPH_FINALLY_CLEAN(9); return 0; } /* ---------------------------------------------------------*/ /* Coming from lad.c */ /* ---------------------------------------------------------*/ int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* Gt, bool *result) { /* return true if G_(u, v) has a adj(u)-covering matching; false otherwise */ int u2, v2, i, j; int nbMatched = 0; igraph_vector_int_t *Gp_uneis=igraph_adjlist_get(&Gp->succ, u); int *num, *numInv; igraph_vector_int_t nbComp; igraph_vector_int_t firstComp; igraph_vector_int_t comp; int nbNum=0; int posInComp=0; igraph_vector_int_t matchedWithU; int invalid; /* special case when u has only 1 adjacent node => no need to call Hopcroft and Karp */ if (VECTOR(Gp->nbSucc)[u]==1) { u2 = (int) VECTOR(*Gp_uneis)[0]; /* u2 is the only node adjacent to u */ v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ]; if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) { *result = true; return 0; } /* look for a support of edge (u, u2) for v */ for (i=VECTOR(D->firstVal)[u2]; i < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; i++) { if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[i])) { VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ] = VECTOR(D->val)[i]; *result = true; return 0; } } *result = false; return 0; } /* general case (when u has more than 1 adjacent node) */ for (i=0; inbSucc)[u]; i++) { /* remove from the matching of G_(u, v) edges which no longer belong to G_(u, v) */ u2 = (int) VECTOR(*Gp_uneis)[i]; v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v)+i]; if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) { nbMatched++; } } if (nbMatched == VECTOR(Gp->nbSucc)[u]) { *result = true; return 0; } /* The matching still covers adj(u) */ /* Allocate memory */ ALLOC_ARRAY(num, Gt->nbVertices, int); ALLOC_ARRAY(numInv, Gt->nbVertices, int); /* Build the bipartite graph let U be the set of nodes adjacent to u let V be the set of nodes that are adjacent to v, and that belong to domains of nodes of U */ /* nbComp[u]=number of elements of V that are compatible with u */ IGRAPH_CHECK(igraph_vector_int_init(&nbComp, (long int) VECTOR(Gp->nbSucc)[u])); IGRAPH_FINALLY(igraph_vector_int_destroy, &nbComp); IGRAPH_CHECK(igraph_vector_int_init(&firstComp, (long int) VECTOR(Gp->nbSucc)[u])); IGRAPH_FINALLY(igraph_vector_int_destroy, &firstComp); /* comp[firstComp[u]..firstComp[u]+nbComp[u]-1] = nodes of Gt that are compatible with u */ IGRAPH_CHECK(igraph_vector_int_init(&comp, (long int) (VECTOR(Gp->nbSucc)[u] * Gt->nbVertices))); IGRAPH_FINALLY(igraph_vector_int_destroy, &comp); IGRAPH_CHECK(igraph_vector_int_init(&matchedWithU, (long int) VECTOR(Gp->nbSucc)[u])); IGRAPH_FINALLY(igraph_vector_int_destroy, &matchedWithU); memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(int)); for (i=0; inbSucc)[u]; i++) { u2 = (int) VECTOR(*Gp_uneis)[i]; /* u2 is adjacent to u */ /* search for all nodes v2 in D[u2] which are adjacent to v */ VECTOR(nbComp)[i]=0; VECTOR(firstComp)[i]=posInComp; if (VECTOR(D->nbVal)[u2] > VECTOR(Gt->nbSucc)[v]) { for (j=VECTOR(D->firstVal)[u2]; j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; j++) { v2 = VECTOR(D->val)[j]; /* v2 belongs to D[u2] */ if (MATRIX(Gt->isEdge, v, v2)) { /* v2 is a successor of v */ if (num[v2]<0) { /* v2 has not yet been added to V */ num[v2]=nbNum; numInv[nbNum++]=v2; } VECTOR(comp)[posInComp++]=num[v2]; VECTOR(nbComp)[i]++; } } } else { igraph_vector_int_t *Gt_vneis=igraph_adjlist_get(&Gt->succ, v); for (j=0; jnbSucc)[v]; j++) { v2 = (int) VECTOR(*Gt_vneis)[j]; /* v2 is a successor of v */ if (igraph_i_lad_isInD(u2, v2, D)) { /* v2 belongs to D[u2] */ if (num[v2]<0) { /* v2 has not yet been added to V */ num[v2]=nbNum; numInv[nbNum++]=v2; } VECTOR(comp)[posInComp++]=num[v2]; VECTOR(nbComp)[i]++; } } } if (VECTOR(nbComp)[i]==0) { *result = false; /* u2 has no compatible vertex in succ[v] */ goto cleanup; } /* u2 is matched to v2 in the matching that supports (u, v) */ v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v)+i]; if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) { VECTOR(matchedWithU)[i]=num[v2]; } else { VECTOR(matchedWithU)[i]=-1; } } /* Call Hopcroft Karp to update the matching */ IGRAPH_CHECK( igraph_i_lad_updateMatching((int) VECTOR(Gp->nbSucc)[u], nbNum, &nbComp, &firstComp, &comp, &matchedWithU, &invalid) ); if (invalid) { *result = false; goto cleanup; } for (i=0; inbSucc)[u]; i++) { VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v)+i] = numInv[ VECTOR(matchedWithU)[i] ]; } *result = true; cleanup: igraph_free(numInv); igraph_free(num); igraph_vector_int_destroy(&matchedWithU); igraph_vector_int_destroy(&comp); igraph_vector_int_destroy(&firstComp); igraph_vector_int_destroy(&nbComp); IGRAPH_FINALLY_CLEAN(6); return 0; } /* ---------------------------------------------------------*/ /* Coming from main.c */ /* ---------------------------------------------------------*/ int igraph_i_lad_filter(bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, bool *result) { /* filter domains of all vertices in D->toFilter wrt LAD and ensure GAC(allDiff) return false if some domain becomes empty; true otherwise */ int u, v, i, oldNbVal; int invalid; bool result2; while (!igraph_i_lad_toFilterEmpty(D)) { while (!igraph_i_lad_toFilterEmpty(D)) { u=igraph_i_lad_nextToFilter(D, (int) (Gp->nbVertices)); oldNbVal = VECTOR(D->nbVal)[u]; i = VECTOR(D->firstVal)[u]; while (i < VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]) { /* for every target node v in D(u), check if G_(u, v) has a covering matching */ v=VECTOR(D->val)[i]; IGRAPH_CHECK(igraph_i_lad_checkLAD(u, v, D, Gp, Gt, &result2)); if (result2) { i++; } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result2)); if (!result2) { *result = false; return 0; } } } if ((VECTOR(D->nbVal)[u]==1) && (oldNbVal>1) && (!igraph_i_lad_matchVertex(u, induced, D, Gp, Gt))) { *result = false; return 0; } if (VECTOR(D->nbVal)[u]==0) { *result = false; return 0; } } igraph_i_lad_ensureGACallDiff(induced, Gp, Gt, D, &invalid); if (invalid) { *result = false; return 0; } } *result = true; return 0; } int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, int *invalid, igraph_bool_t *iso, igraph_vector_t *map, igraph_vector_ptr_t *maps, int *nbNodes, int *nbFail, int *nbSol, clock_t *begin) { /* if firstSol then search for the first solution; otherwise search for all solutions if induced then search for induced subgraphs; otherwise search for partial subgraphs return false if CPU time limit exceeded before the search is completed, return true otherwise */ int u, v, minDom, i; int* nbVal; int* globalMatching; clock_t end=clock(); igraph_vector_t *vec; int* val; bool result; (*nbNodes)++; if ( (double)(end - *begin) / CLOCKS_PER_SEC >= timeLimit) { /* CPU time limit exceeded */ IGRAPH_ERROR("LAD CPU time exceeded", IGRAPH_CPUTIME); } /* Allocate memory */ ALLOC_ARRAY(nbVal, Gp->nbVertices, int); ALLOC_ARRAY(globalMatching, Gp->nbVertices, int); IGRAPH_CHECK(igraph_i_lad_filter(induced, D, Gp, Gt, &result)); if (!result) { /* filtering has detected an inconsistency */ (*nbFail)++; igraph_i_lad_resetToFilter(D); *invalid=0; goto cleanup; } /* The current node of the search tree is consistent wrt to LAD and GAC(allDiff) Save domain sizes and global all different matching and search for the non matched vertex minDom with smallest domain */ minDom=-1; for (u=0; unbVertices; u++) { nbVal[u]=VECTOR(D->nbVal)[u]; if ((nbVal[u]>1) && ((minDom<0) || (nbVal[u]globalMatchingP)[u]; } if (minDom==-1) { /* All vertices are matched => Solution found */ if (iso) { *iso = 1; } (*nbSol)++; if (map && igraph_vector_size(map)==0) { IGRAPH_CHECK(igraph_vector_resize(map, Gp->nbVertices)); for (u=0; unbVertices; u++) { VECTOR(*map)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; } } if (maps) { vec=igraph_Calloc(1, igraph_vector_t); if (!vec) { IGRAPH_ERROR("LAD failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vec); IGRAPH_CHECK(igraph_vector_init(vec, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_destroy, vec); for (u=0; unbVertices; u++) { VECTOR(*vec)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; } IGRAPH_CHECK(igraph_vector_ptr_push_back(maps, vec)); IGRAPH_FINALLY_CLEAN(2); } igraph_i_lad_resetToFilter(D); *invalid=0; goto cleanup; } /* save the domain of minDom to iterate on its values */ ALLOC_ARRAY(val, VECTOR(D->nbVal)[minDom], int); for (i=0; i < VECTOR(D->nbVal)[minDom]; i++) { val[i]=VECTOR(D->val)[ VECTOR(D->firstVal)[minDom]+i ]; } /* branch on minDom=v, for every target node v in D(u) */ for(i=0; ((iglobalMatchingT, -1); for (u=0; unbVertices; u++) { VECTOR(D->nbVal)[u] = nbVal[u]; VECTOR(D->globalMatchingP)[u] = globalMatching[u]; VECTOR(D->globalMatchingT)[globalMatching[u]] = u; } } *invalid=0; igraph_free(val); IGRAPH_FINALLY_CLEAN(1); cleanup: igraph_free(globalMatching); igraph_free(nbVal); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_subisomorphic_lad * Check subgraph isomorphism with the LAD algorithm * * Check whether \p pattern is isomorphic to a subgraph os \p target. * The original LAD implementation by Christine Solnon was used as the * basis of this code. * * * See more about at http://liris.cnrs.fr/csolnon/LAD.html and in * Christine Solnon: AllDifferent-based Filtering for Subgraph * Isomorphism. Artificial Intelligence, 174(12-13):850-864, August * 2010, Elsevier * * \param pattern The smaller graph, it can be directed or undirected. * \param target The bigger graph, it can be directed or undirected. * \param domains A pointer vector, or a null pointer. If a pointer * vector, then it must contain pointers to \c igraph_vector_t * objects and the length of the vector must match the number of * vertices in the \p pattern graph. For each vertex, the ids of * the compatible vertices in the target graph are listed. * \param iso Pointer to a boolean, or a null pointer. If not a null * pointer, then the boolean is set to TRUE (1) if a subgraph * isomorphism is found, and to FALSE (0) otherwise. * \param map Pointer to a vector or a null pointer. If not a null * pointer and a subgraph isomorphism is found, the matching * vertices from the target graph are listed here, for each vertex * (in vertex id order) from the pattern graph. * \param maps Pointer vector or a null pointer. If not a null * pointer, then all subgraph isomorphisms are stored in the * pointer vector, in \c igraph_vector_t objects. * \param induced Boolean, whether to search for induced matching * subgraphs. * \param time_limit Processor time limit in seconds. Supply zero * here for no limit. If the time limit is over, then the function * signals an error. * \return Error code * * \sa \ref igraph_subisomorphic_vf2() for the VF2 algorithm. * * Time complexity: exponential. * * \example examples/simple/igraph_subisomorphic_lad.c */ int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, igraph_vector_ptr_t *domains, igraph_bool_t *iso, igraph_vector_t *map, igraph_vector_ptr_t *maps, igraph_bool_t induced, int time_limit) { bool firstSol = maps == 0; bool initialDomains = domains != 0; Tgraph Gp, Gt; Tdomain D; int invalidDomain; int u, nbToMatch = 0; igraph_vector_int_t toMatch; /* Number of nodes in the search tree */ int nbNodes=0; /* number of failed nodes in the search tree */ int nbFail=0; /* number of solutions found */ int nbSol=0; /* reusable structure to get CPU time usage */ clock_t begin=clock(); if (!iso && !map && !maps) { IGRAPH_ERROR("Please give least one of `iso', `map' or `maps'", IGRAPH_EINVAL); } if (time_limit<=0) { time_limit = INT_MAX; } igraph_i_lad_createGraph(pattern, &Gp); igraph_i_lad_createGraph(target, &Gt); if (iso) { *iso = 0; } if (map) { igraph_vector_clear(map); } if (maps) { igraph_vector_ptr_clear(maps); } if (Gp.nbVertices > Gt.nbVertices) { goto exit3; } IGRAPH_CHECK(igraph_i_lad_initDomains(initialDomains, domains, &D, &Gp, &Gt, &invalidDomain)); if (invalidDomain) { goto exit2; } IGRAPH_CHECK(igraph_i_lad_updateMatching((int) (Gp.nbVertices), (int) (Gt.nbVertices), &D.nbVal, &D.firstVal, &D.val, &D.globalMatchingP, &invalidDomain)); if (invalidDomain) { goto exit; } IGRAPH_CHECK(igraph_i_lad_ensureGACallDiff((char) induced, &Gp, &Gt, &D, &invalidDomain)); if (invalidDomain) { goto exit; } for (u=0; u 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_layout.h" #include "igraph_random.h" #include "igraph_memory.h" #include "igraph_iterators.h" #include "igraph_interface.h" #include "igraph_adjlist.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_paths.h" #include "igraph_structural.h" #include "igraph_visitor.h" #include "igraph_topology.h" #include "igraph_components.h" #include "igraph_types_internal.h" #include "igraph_dqueue.h" #include "igraph_arpack.h" #include "igraph_blas.h" #include "igraph_centrality.h" #include "igraph_eigen.h" #include "config.h" #include #include "igraph_math.h" /** * \section about_layouts * * Layout generator functions (or at least most of them) try to place the * vertices and edges of a graph on a 2D plane or in 3D space in a way * which visually pleases the human eye. * * They take a graph object and a number of parameters as arguments * and return an \type igraph_matrix_t, in which each row gives the * coordinates of a vertex. */ /** * \ingroup layout * \function igraph_layout_random * \brief Places the vertices uniform randomly on a plane. * * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized as needed. * \return Error code. The current implementation always returns with * success. * * Time complexity: O(|V|), the * number of vertices. */ int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { long int no_of_nodes=igraph_vcount(graph); long int i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); RNG_BEGIN(); for (i=0; i * * Time complexity: O(|V|), the number of vertices. */ int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { long int no_of_nodes=igraph_vcount(graph); long int i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); RNG_BEGIN(); for (i=0; i * The algorithm was described in the following paper: * Distributing many points on a sphere by E.B. Saff and * A.B.J. Kuijlaars, \emb Mathematical Intelligencer \eme 19.1 (1997) * 5--11. * * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized as needed. * \return Error code. The current implementation always returns with * success. * * Added in version 0.2. * * Time complexity: O(|V|), the number of vertices in the graph. */ int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) { long int no_of_nodes=igraph_vcount(graph); long int i; igraph_real_t h; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); if (no_of_nodes != 0) { MATRIX(*res, 0, 0)=M_PI; MATRIX(*res, 0, 1)=0; } for (i=1; i=2) { MATRIX(*res, no_of_nodes-1, 0)=0; MATRIX(*res, no_of_nodes-1, 1)=0; } for (i=0; i * This is a force-directed layout, see Fruchterman, T.M.J. and * Reingold, E.M.: Graph Drawing by Force-directed Placement. * Software -- Practice and Experience, 21/11, 1129--1164, * 1991. * This function was ported from the SNA R package. * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized as needed. * \param niter The number of iterations to do. A reasonable * default value is 500. * \param maxdelta The maximum distance to move a vertex in an * iteration. A reasonable default value is the number of * vertices. * \param area The area parameter of the algorithm. A reasonable * default is the square of the number of vertices. * \param coolexp The cooling exponent of the simulated annealing. * A reasonable default is 1.5. * \param repulserad Determines the radius at which * vertex-vertex repulsion cancels out attraction of * adjacent vertices. A reasonable default is \p area * times the number of vertices. * \param use_seed Logical, if true the supplied values in the * \p res argument are used as an initial layout, if * false a random initial layout is used. * \param weight Pointer to a vector containing edge weights, * the attraction along the edges will be multiplied by these. * It will be ignored if it is a null-pointer. * \param minx Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote x \endquote coordinate for every vertex. * \param maxx Same as \p minx, but the maximum \quote x \endquote * coordinates. * \param miny Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote y \endquote coordinate for every vertex. * \param maxy Same as \p miny, but the maximum \quote y \endquote * coordinates. * \return Error code. * * Time complexity: O(|V|^2) in each * iteration, |V| is the number of * vertices in the graph. */ int igraph_layout_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_bool_t use_seed, const igraph_vector_t *weight, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy) { igraph_real_t frk,t,ded,xd,yd; igraph_real_t rf,af; long int i,j,k; long int no_of_nodes=igraph_vcount(graph); igraph_matrix_t dxdy=IGRAPH_MATRIX_NULL; igraph_eit_t edgeit; igraph_integer_t from, to; if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } if (minx && igraph_vector_size(minx) != no_of_nodes) { IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); } if (maxx && igraph_vector_size(maxx) != no_of_nodes) { IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); } if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); } if (miny && igraph_vector_size(miny) != no_of_nodes) { IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); } if (maxy && igraph_vector_size(maxy) != no_of_nodes) { IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); } if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); if (!use_seed) { IGRAPH_CHECK(igraph_layout_random(graph, res)); } IGRAPH_MATRIX_INIT_FINALLY(&dxdy, no_of_nodes, 2); IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); frk=sqrt(area/no_of_nodes); for(i=niter;i>0;i--) { /* Report progress in approx. every 100th step */ if (i%10 == 0) IGRAPH_PROGRESS("Fruchterman-Reingold layout: ", 100.0-100.0*i/niter, NULL); /* Set the temperature (maximum move/iteration) */ t=maxdelta*pow(i/(double)niter,coolexp); /* Clear the deltas */ igraph_matrix_null(&dxdy); /* Increment deltas for each undirected pair */ for(j=0;jt){ /* Dampen to t */ ded=t/ded; MATRIX(dxdy, j, 0)*=ded; MATRIX(dxdy, j, 1)*=ded; } MATRIX(*res, j, 0)+=MATRIX(dxdy, j, 0); /* Update positions */ MATRIX(*res, j, 1)+=MATRIX(dxdy, j, 1); if (minx && MATRIX(*res, j, 0) < VECTOR(*minx)[j]) { MATRIX(*res, j, 0) = VECTOR(*minx)[j]; } else if (maxx && MATRIX(*res, j, 0) > VECTOR(*maxx)[j]) { MATRIX(*res, j, 0) = VECTOR(*maxx)[j]; } if (miny && MATRIX(*res, j, 1) < VECTOR(*miny)[j]) { MATRIX(*res, j, 1) = VECTOR(*miny)[j]; } else if (maxy && MATRIX(*res, j, 1) > VECTOR(*maxy)[j]) { MATRIX(*res, j, 1) = VECTOR(*maxy)[j]; } } } IGRAPH_PROGRESS("Fruchterman-Reingold layout: ", 100.0, NULL); igraph_eit_destroy(&edgeit); igraph_matrix_destroy(&dxdy); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_layout_fruchterman_reingold_3d * \brief 3D Fruchterman-Reingold algorithm. * * This is the 3D version of the force based * Fruchterman-Reingold layout (see \ref * igraph_layout_fruchterman_reingold for the 2D version * * * This function was ported from the SNA R package. * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized as needed. * \param niter The number of iterations to do. A reasonable * default value is 500. * \param maxdelta The maximum distance to move a vertex in an * iteration. A reasonable default value is the number of * vertices. * \param volume The volume parameter of the algorithm. A reasonable * default is the number of vertices^3. * \param coolexp The cooling exponent of the simulated annealing. * A reasonable default is 1.5. * \param repulserad Determines the radius at which * vertex-vertex repulsion cancels out attraction of * adjacent vertices. A reasonable default is \p volume * times the number of vertices. * \param use_seed Logical, if true the supplied values in the * \p res argument are used as an initial layout, if * false a random initial layout is used. * \param weight Pointer to a vector containing edge weights, * the attraction along the edges will be multiplied by these. * It will be ignored if it is a null-pointer. * \param minx Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote x \endquote coordinate for every vertex. * \param maxx Same as \p minx, but the maximum \quote x \endquote * coordinates. * \param miny Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote y \endquote coordinate for every vertex. * \param maxy Same as \p miny, but the maximum \quote y \endquote * coordinates. * \param minz Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote z \endquote coordinate for every vertex. * \param maxz Same as \p minz, but the maximum \quote z \endquote * coordinates. * \return Error code. * * Added in version 0.2. * * Time complexity: O(|V|^2) in each * iteration, |V| is the number of * vertices in the graph. * */ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t volume, igraph_real_t coolexp, igraph_real_t repulserad, igraph_bool_t use_seed, const igraph_vector_t *weight, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, const igraph_vector_t *minz, const igraph_vector_t *maxz) { igraph_real_t frk, t, ded, xd, yd, zd; igraph_matrix_t dxdydz; igraph_real_t rf, af; long int i, j, k; long int no_of_nodes=igraph_vcount(graph); igraph_eit_t edgeit; igraph_integer_t from, to; if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } if (minx && igraph_vector_size(minx) != no_of_nodes) { IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); } if (maxx && igraph_vector_size(maxx) != no_of_nodes) { IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); } if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); } if (miny && igraph_vector_size(miny) != no_of_nodes) { IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); } if (maxy && igraph_vector_size(maxy) != no_of_nodes) { IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); } if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); } if (minz && igraph_vector_size(minz) != no_of_nodes) { IGRAPH_ERROR("Invalid minz vector length", IGRAPH_EINVAL); } if (maxz && igraph_vector_size(maxz) != no_of_nodes) { IGRAPH_ERROR("Invalid maxz vector length", IGRAPH_EINVAL); } if (minz && maxz && !igraph_vector_all_le(minz, maxz)) { IGRAPH_ERROR("minz must not be greater than maxz", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_init(&dxdydz, no_of_nodes, 3)); IGRAPH_FINALLY(igraph_matrix_destroy, &dxdydz); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); if (!use_seed) { IGRAPH_CHECK(igraph_layout_random_3d(graph, res)); } IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); frk=pow(volume/(double)no_of_nodes,1.0/3.0); /*Define the F-R constant*/ /*Run the annealing loop*/ for(i=niter;i>=0;i--){ if (i%10 == 0) IGRAPH_PROGRESS("3D Fruchterman-Reingold layout: ", 100.0-100.0*i/niter, NULL); /*Set the temperature (maximum move/iteration)*/ t=maxdelta*pow(i/(double)niter,coolexp); /*Clear the deltas*/ igraph_matrix_null(&dxdydz); /*Increment deltas for each undirected pair*/ for(j=0;jt){ /*Dampen to t*/ ded=t/ded; MATRIX(dxdydz, j, 0)*=ded; MATRIX(dxdydz, j, 1)*=ded; MATRIX(dxdydz, j, 2)*=ded; } MATRIX(*res, j, 0)+=MATRIX(dxdydz, j, 0); /*Update positions*/ MATRIX(*res, j, 1)+=MATRIX(dxdydz, j, 1); MATRIX(*res, j, 2)+=MATRIX(dxdydz, j, 2); if (minx && MATRIX(*res, j, 0) < VECTOR(*minx)[j]) { MATRIX(*res, j, 0) = VECTOR(*minx)[j]; } else if (maxx && MATRIX(*res, j, 0) > VECTOR(*maxx)[j]) { MATRIX(*res, j, 0) = VECTOR(*maxx)[j]; } if (miny && MATRIX(*res, j, 1) < VECTOR(*miny)[j]) { MATRIX(*res, j, 1) = VECTOR(*miny)[j]; } else if (maxy && MATRIX(*res, j, 1) > VECTOR(*maxy)[j]) { MATRIX(*res, j, 1) = VECTOR(*maxy)[j]; } if (minz && MATRIX(*res, j, 2) < VECTOR(*minz)[j]) { MATRIX(*res, j, 2) = VECTOR(*minz)[j]; } else if (maxz && MATRIX(*res, j, 2) > VECTOR(*maxz)[j]) { MATRIX(*res, j, 2) = VECTOR(*maxz)[j]; } } } IGRAPH_PROGRESS("3D Fruchterman-Reingold layout: ", 100.0, NULL); igraph_matrix_destroy(&dxdydz); igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \ingroup layout * \function igraph_layout_kamada_kawai * \brief Places the vertices on a plane according the Kamada-Kawai algorithm. * * * This is a force directed layout, see Kamada, T. and Kawai, S.: An * Algorithm for Drawing General Undirected Graphs. Information * Processing Letters, 31/1, 7--15, 1989. * This function was ported from the SNA R package. * \param graph A graph object. * \param res Pointer to an initialized matrix object. This will * contain the result (x-positions in column zero and * y-positions in column one) and will be resized if needed. * \param niter The number of iterations to perform. A reasonable * default value is 1000. * \param sigma Sets the base standard deviation of position * change proposals. A reasonable default value is the * number of vertices / 4. * \param initemp Sets the initial temperature for the annealing. * A reasonable default value is 10. * \param coolexp The cooling exponent of the annealing. * A reasonable default value is 0.99. * \param kkconst The Kamada-Kawai vertex attraction constant. * Typical value: (number of vertices)^2 * \param use_seed Boolean, whether to use the values supplied in the * \p res argument as the initial configuration. If zero then a * random initial configuration is used. * \param minx Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote x \endquote coordinate for every vertex. * \param maxx Same as \p minx, but the maximum \quote x \endquote * coordinates. * \param miny Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote y \endquote coordinate for every vertex. * \param maxy Same as \p miny, but the maximum \quote y \endquote * coordinates. * \return Error code. * * Time complexity: O(|V|^2) for each * iteration, |V| is the number of * vertices in the graph. */ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t sigma, igraph_real_t initemp, igraph_real_t coolexp, igraph_real_t kkconst, igraph_bool_t use_seed, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy) { igraph_real_t temp, candx, candy, dx, dy; igraph_real_t dpot, odis, ndis, osqd, nsqd; long int n=igraph_vcount(graph); int i,j,k; igraph_matrix_t elen; if (minx && igraph_vector_size(minx) != n) { IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); } if (maxx && igraph_vector_size(maxx) != n) { IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); } if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); } if (miny && igraph_vector_size(miny) != n) { IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); } if (maxy && igraph_vector_size(maxy) != n) { IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); } if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); } #define CHECK_BOUNDS(x) do { \ if (minx && MATRIX(*res, (x), 0) < VECTOR(*minx)[(x)]) { \ MATRIX(*res, (x), 0) = VECTOR(*minx)[(x)]; \ } else if (maxx && MATRIX(*res, (x), 0) > VECTOR(*maxx)[(x)]) { \ MATRIX(*res, (x), 0) = VECTOR(*maxx)[(x)]; \ } \ if (miny && MATRIX(*res, (x), 1) < VECTOR(*miny)[(x)]) { \ MATRIX(*res, (x), 1) = VECTOR(*miny)[(x)]; \ } else if (maxy && MATRIX(*res, (x), 1) > VECTOR(*maxy)[(x)]) { \ MATRIX(*res, (x), 1) = VECTOR(*maxy)[(x)]; \ } \ } while (0) /* Calculate elen, initial x & y */ RNG_BEGIN(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 2)); IGRAPH_MATRIX_INIT_FINALLY(&elen, n, n); IGRAPH_CHECK(igraph_shortest_paths(graph, &elen, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL)); /* Scan the distance matrix and introduce an upper limit. * This helps with disconnected graphs. */ temp = 0.0; for (i=0; i temp) temp = MATRIX(elen, i, j); } } for (i=0; i temp) MATRIX(elen, i, j) = temp; } } /* Initialize the layout if needed */ if (!use_seed) { for (i=0; i * This function was ported from the SNA R package. * \param graph A graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized if needed. * \param niter The number of iterations to perform. A reasonable * default value is 1000. * \param sigma Sets the base standard deviation of position * change proposals. A reasonable default value is the * number of vertices / 4. * \param initemp Sets the initial temperature for the annealing. * A reasonable default value is 10. * \param coolexp The cooling exponent of the annealing. * A reasonable default value is 0.99. * \param kkconst The Kamada-Kawai vertex attraction constant. * Typical value: (number of vertices)^2 * \param use_seed Boolean, whether to use the values cupplied in the \p res * argument as the initial configuration. If zero then a random initial * configuration is used. * \param fixz Logical, whether to fix the third coordinate of the input * matrix. * \return Error code. * * Added in version 0.2. * * Time complexity: O(|V|^2) for each * iteration, |V| is the number of * vertices in the graph. */ int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t sigma, igraph_real_t initemp, igraph_real_t coolexp, igraph_real_t kkconst, igraph_bool_t use_seed, igraph_bool_t fixz, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, const igraph_vector_t *minz, const igraph_vector_t *maxz) { igraph_real_t temp, candx, candy, candz; igraph_real_t dpot, odis, ndis, osqd, nsqd; long int i,j,k; long int no_of_nodes=igraph_vcount(graph); igraph_matrix_t elen; if (minx && igraph_vector_size(minx) != no_of_nodes) { IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); } if (maxx && igraph_vector_size(maxx) != no_of_nodes) { IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); } if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); } if (miny && igraph_vector_size(miny) != no_of_nodes) { IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); } if (maxy && igraph_vector_size(maxy) != no_of_nodes) { IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); } if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); } if (minz && igraph_vector_size(minz) != no_of_nodes) { IGRAPH_ERROR("Invalid minz vector length", IGRAPH_EINVAL); } if (maxz && igraph_vector_size(maxz) != no_of_nodes) { IGRAPH_ERROR("Invalid maxz vector length", IGRAPH_EINVAL); } if (minz && maxz && !igraph_vector_all_le(minz, maxz)) { IGRAPH_ERROR("minz must not be greater than maxz", IGRAPH_EINVAL); } #define CHECK_BOUNDS(x) do { \ if (minx && MATRIX(*res, (x), 0) < VECTOR(*minx)[(x)]) { \ MATRIX(*res, (x), 0) = VECTOR(*minx)[(x)]; \ } else if (maxx && MATRIX(*res, (x), 0) > VECTOR(*maxx)[(x)]) { \ MATRIX(*res, (x), 0) = VECTOR(*maxx)[(x)]; \ } \ if (miny && MATRIX(*res, (x), 1) < VECTOR(*miny)[(x)]) { \ MATRIX(*res, (x), 1) = VECTOR(*miny)[(x)]; \ } else if (maxy && MATRIX(*res, (x), 1) > VECTOR(*maxy)[(x)]) { \ MATRIX(*res, (x), 1) = VECTOR(*maxy)[(x)]; \ } \ if (minz && MATRIX(*res, (x), 2) < VECTOR(*minz)[(x)]) { \ MATRIX(*res, (x), 2) = VECTOR(*minz)[(x)]; \ } else if (maxz && MATRIX(*res, (x), 2) > VECTOR(*maxz)[(x)]) { \ MATRIX(*res, (x), 2) = VECTOR(*maxz)[(x)]; \ } \ } while (0) RNG_BEGIN(); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); IGRAPH_MATRIX_INIT_FINALLY(&elen, no_of_nodes, no_of_nodes); IGRAPH_CHECK(igraph_shortest_paths(graph, &elen, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL)); /* Scan the distance matrix and introduce an upper limit. * This helps with disconnected graphs. */ temp = 0.0; for (i=0; i temp) temp = MATRIX(elen, i, j); } } for (i=0; i temp) MATRIX(elen, i, j) = temp; } } if (!use_seed) { for (i=0; i * This is a layout generator similar to the Large Graph Layout * algorithm and program * (http://lgl.sourceforge.net/). But unlike LGL, this * version uses a Fruchterman-Reingold style simulated annealing * algorithm for placing the vertices. The speedup is achieved by * placing the vertices on a grid and calculating the repulsion only * for vertices which are closer to each other than a limit. * * \param graph The (initialized) graph object to place. * \param res Pointer to an initialized matrix object to hold the * result. It will be resized if needed. * \param maxit The maximum number of cooling iterations to perform * for each layout step. A reasonable default is 150. * \param maxdelta The maximum length of the move allowed for a vertex * in a single iteration. A reasonable default is the number of * vertices. * \param area This parameter gives the area of the square on which * the vertices will be placed. A reasonable default value is the * number of vertices squared. * \param coolexp The cooling exponent. A reasonable default value is * 1.5. * \param repulserad Determines the radius at which vertex-vertex * repulsion cancels out attraction of adjacent vertices. A * reasonable default value is \p area times the number of vertices. * \param cellsize The size of the grid cells, one side of the * square. A reasonable default value is the fourth root of * \p area (or the square root of the number of vertices if \p area * is also left at its default value). * \param proot The root vertex, this is placed first, its neighbors * in the first iteration, second neighbors in the second, etc. If * negative then a random vertex is chosen. * \return Error code. * * Added in version 0.2. * * Time complexity: ideally O(dia*maxit*(|V|+|E|)), |V| is the number * of vertices, * dia is the diameter of the graph, worst case complexity is still * O(dia*maxit*(|V|^2+|E|)), this is the case when all vertices happen to be * in the same grid cell. */ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t maxit, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_integer_t proot) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_t mst; long int root; long int no_of_layers, actlayer=0; igraph_vector_t vids; igraph_vector_t layers; igraph_vector_t parents; igraph_vector_t edges; igraph_2dgrid_t grid; igraph_vector_t eids; igraph_vector_t forcex; igraph_vector_t forcey; igraph_real_t frk=sqrt(area/no_of_nodes); igraph_real_t H_n=0; IGRAPH_CHECK(igraph_minimum_spanning_tree_unweighted(graph, &mst)); IGRAPH_FINALLY(igraph_destroy, &mst); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); /* Determine the root vertex, random pick right now */ if (proot < 0) { root=RNG_INTEGER(0, no_of_nodes-1); } else { root=proot; } /* Assign the layers */ IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); IGRAPH_VECTOR_INIT_FINALLY(&layers, 0); IGRAPH_VECTOR_INIT_FINALLY(&parents, 0); IGRAPH_CHECK(igraph_i_bfs(&mst, (igraph_integer_t) root, IGRAPH_ALL, &vids, &layers, &parents)); no_of_layers=igraph_vector_size(&layers)-1; /* We don't need the mst any more */ igraph_destroy(&mst); igraph_empty(&mst, 0, IGRAPH_UNDIRECTED); /* to make finalization work */ IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges)); IGRAPH_VECTOR_INIT_FINALLY(&eids, 0); IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes); /* Place the vertices randomly */ IGRAPH_CHECK(igraph_layout_random(graph, res)); igraph_matrix_scale(res, 1e6); /* This is the grid for calculating the vertices near to a given vertex */ IGRAPH_CHECK(igraph_2dgrid_init(&grid, res, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize)); IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid); /* Place the root vertex */ igraph_2dgrid_add(&grid, root, 0, 0); for (actlayer=1; actlayer epsilon) { long int jj; igraph_real_t t=maxdelta*pow((maxit-it)/(double)maxit, coolexp); long int vid, nei; IGRAPH_PROGRESS("Large graph layout", 100.0*((actlayer-1.0)/(no_of_layers-1.0)+((float)it)/(maxit*(no_of_layers-1.0))), 0); /* init */ igraph_vector_null(&forcex); igraph_vector_null(&forcey); maxchange=0; /* attractive "forces" along the edges */ for (jj=0; jj t) { ded=t/ded; fx*=ded; fy *=ded; } igraph_2dgrid_move(&grid, vvid, fx, fy); if (fx > maxchange) { maxchange=fx; } if (fy > maxchange) { maxchange=fy; } } it++; /* printf("%li iterations, maxchange: %f\n", it, (double)maxchange); */ } } IGRAPH_PROGRESS("Large graph layout", 100.0, 0); igraph_destroy(&mst); igraph_vector_destroy(&vids); igraph_vector_destroy(&layers); igraph_vector_destroy(&parents); igraph_vector_destroy(&edges); igraph_2dgrid_destroy(&grid); igraph_vector_destroy(&eids); igraph_vector_destroy(&forcex); igraph_vector_destroy(&forcey); IGRAPH_FINALLY_CLEAN(9); return 0; } /** * \function igraph_layout_grid_fruchterman_reingold * \brief Force based layout generator for large graphs. * * * This algorithm is the same as the Fruchterman-Reingold layout * generator, but it partitions the 2d space to a grid and and vertex * repulsion is calculated only for vertices nearby. * * \param graph The graph object. * \param res The result, the coordinates in a matrix. The parameter * should point to an initialized matrix object and will be resized. * \param niter The number of iterations to do. A reasonable * default value is 500. * \param maxdelta The maximum distance to move a vertex in an * iteration. A reasonable default value is the number of * vertices. * \param area The area parameter of the algorithm. A reasonable * default is the square of the number of vertices. * \param coolexp The cooling exponent of the simulated annealing. * A reasonable default is 1.5. * \param repulserad Determines the radius at which * vertex-vertex repulsion cancels out attraction of * adjacent vertices. A reasonable default is \p area * times the number of vertices. * \param cellsize The size of the grid cells. A reasonable default is * the fourth root of \p area (or the square root of the * number of vertices if \p area is also left at its default * value) * \param use_seed Logical, if true, the coordinates passed in \p res * (should have the appropriate size) will be used for the first * iteration. * \param weight Pointer to a vector containing edge weights, * the attraction along the edges will be multiplied by these. * It will be ignored if it is a null-pointer. * \return Error code. * * Added in version 0.2. * * Time complexity: ideally (constant number of vertices in each cell) * O(niter*(|V|+|E|)), in the worst case O(niter*(|V|^2+|E|)). */ int igraph_layout_grid_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_bool_t use_seed, const igraph_vector_t *weight) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_2dgrid_t grid; igraph_vector_t forcex; igraph_vector_t forcey; long int i, it=0; igraph_2dgrid_iterator_t vidit; igraph_real_t frk=sqrt(area/no_of_nodes); if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes); /* initial layout */ if (!use_seed) { IGRAPH_CHECK(igraph_layout_random(graph, res)); igraph_matrix_scale(res, sqrt(area/M_PI)); } /* make grid */ IGRAPH_CHECK(igraph_2dgrid_init(&grid, res, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize)); IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid); /* place vertices on grid */ for (i=0; i t) { ded=t/ded; fx*=ded; fy *=ded; } igraph_2dgrid_move(&grid, vvid, fx, fy); } it++; } /* it= 0) { continue; } MATRIX(*res, neighbor, 1)=actdist+1; IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); IGRAPH_CHECK(igraph_dqueue_push(&q, actdist+1)); vdata[neighbor].parent = actnode; vdata[neighbor].level = actdist+1; } } /* Step 2: postorder tree traversal, determines the appropriate X * offsets for every node */ igraph_i_layout_reingold_tilford_postorder(vdata, root, no_of_nodes); /* Step 3: calculate real coordinates based on X offsets */ igraph_i_layout_reingold_tilford_calc_coords(vdata, res, root, no_of_nodes, vdata[root].offset); igraph_dqueue_destroy(&q); igraph_adjlist_destroy(&allneis); igraph_free(vdata); IGRAPH_FINALLY_CLEAN(3); IGRAPH_PROGRESS("Reingold-Tilford tree layout", 100.0, NULL); return 0; } int igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata, igraph_matrix_t *res, long int node, long int vcount, igraph_real_t xpos) { long int i; MATRIX(*res, node, 0) = xpos; for (i=0; i= 0) { /* Now we will follow the right contour of leftroot and the * left contour of the subtree rooted at i */ long lnode, rnode; igraph_real_t loffset, roffset, minsep, rootsep; lnode = leftroot; rnode = i; minsep = 1; rootsep = vdata[leftroot].offset + minsep; loffset = 0; roffset = minsep; /*printf(" Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep);*/ while ((lnode >= 0) && (rnode >= 0)) { /* Step to the next level on the right contour of the left subtree */ if (vdata[lnode].right_contour >= 0) { loffset += vdata[lnode].offset_follow_rc; lnode = vdata[lnode].right_contour; } else { /* Left subtree ended there. The right contour of the left subtree * will continue to the next step on the right subtree. */ if (vdata[rnode].left_contour >= 0) { /*printf(" Left subtree ended, continuing left subtree's left and right contour on right subtree (node %ld)\n", vdata[rnode].left_contour);*/ vdata[lnode].left_contour = vdata[rnode].left_contour; vdata[lnode].right_contour = vdata[rnode].left_contour; vdata[lnode].offset_follow_lc = vdata[lnode].offset_follow_rc = (roffset-loffset)+vdata[rnode].offset_follow_lc; /*printf(" vdata[lnode].offset_follow_* = %.4f\n", vdata[lnode].offset_follow_lc);*/ } lnode = -1; } /* Step to the next level on the left contour of the right subtree */ if (vdata[rnode].left_contour >= 0) { roffset += vdata[rnode].offset_follow_lc; rnode = vdata[rnode].left_contour; } else { /* Right subtree ended here. The left contour of the right * subtree will continue to the next step on the left subtree. * Note that lnode has already been advanced here */ if (lnode >= 0) { /*printf(" Right subtree ended, continuing right subtree's left and right contour on left subtree (node %ld)\n", lnode);*/ vdata[rnode].left_contour = lnode; vdata[rnode].right_contour = lnode; vdata[rnode].offset_follow_lc = vdata[rnode].offset_follow_rc = (loffset-roffset); /* loffset has also been increased earlier */ /*printf(" vdata[rnode].offset_follow_* = %.4f\n", vdata[rnode].offset_follow_lc);*/ } rnode = -1; } /*printf(" Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep);*/ /* Push subtrees away if necessary */ if ((lnode >= 0) && (rnode >= 0) && (roffset - loffset < minsep)) { /*printf(" Pushing right subtree away by %lf\n", minsep-roffset+loffset);*/ rootsep += minsep-roffset+loffset; roffset = loffset+minsep; } } /*printf(" Offset of subtree with root node %d will be %lf\n", i, rootsep);*/ vdata[i].offset = rootsep; vdata[node].right_contour = i; vdata[node].offset_follow_rc = rootsep; avg = (avg*j)/(j+1) + rootsep/(j+1); leftrootidx=j; leftroot=i; } else { leftrootidx=j; leftroot=i; vdata[node].left_contour=i; vdata[node].right_contour=i; vdata[node].offset_follow_lc = 0.0; vdata[node].offset_follow_rc = 0.0; avg = vdata[i].offset; } j++; } } /*printf("Shifting node to be centered above children. Shift amount: %lf\n", avg);*/ vdata[node].offset_follow_lc -= avg; vdata[node].offset_follow_rc -= avg; for (i=0, j=0; i * Arranges the nodes in a tree where the given node is used as the root. * The tree is directed downwards and the parents are centered above its * children. For the exact algorithm, see: * * * Reingold, E and Tilford, J: Tidier drawing of trees. * IEEE Trans. Softw. Eng., SE-7(2):223--228, 1981 * * * If the given graph is not a tree, a breadth-first search is executed * first to obtain a possible spanning tree. * * \param graph The graph object. * \param res The result, the coordinates in a matrix. The parameter * should point to an initialized matrix object and will be resized. * \param mode Specifies which edges to consider when building the tree. * If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN * then only the incoming edges of a parent are considered. If it is * \c IGRAPH_ALL then all edges are used (this was the behavior in * igraph 0.5 and before). This parameter also influences how the root * vertices are calculated, if they are not given. See the \p roots parameter. * \param roots The index of the root vertex or root vertices. * If this is a non-empty vector then the supplied vertex ids are used * as the roots of the trees (or a single tree if the graph is connected). * If it is a null pointer of a pointer to an empty vector, then the root * vertices are automatically calculated based on topological sorting, * performed with the opposite mode than the \p mode argument. * After the vertices have been sorted, one is selected from each component. * \param rootlevel This argument can be useful when drawing forests which are * not trees (i.e. they are unconnected and have tree components). It specifies * the level of the root vertices for every tree in the forest. It is only * considered if not a null pointer and the \p roots argument is also given * (and it is not a null pointer of an empty vector). * \return Error code. * * Added in version 0.2. * * \sa \ref igraph_layout_reingold_tilford_circular(). * * \example examples/simple/igraph_layout_reingold_tilford.c */ int igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, const igraph_vector_t *roots, const igraph_vector_t *rootlevel) { long int no_of_nodes_orig=igraph_vcount(graph); long int no_of_nodes=no_of_nodes_orig; long int real_root; igraph_t extended; const igraph_t *pextended=graph; igraph_vector_t myroots; const igraph_vector_t *proots=roots; igraph_neimode_t mode2; /* TODO: possible speedup could be achieved if we use a table for storing * the children of each node in the tree. (Now the implementation uses a * single array containing the parent of each node and a node's children * are determined by looking for other nodes that have this node as parent) */ if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } if (mode==IGRAPH_IN) { mode2=IGRAPH_OUT; } else if (mode==IGRAPH_OUT) { mode2=IGRAPH_IN; } else { mode2=mode; } if ( (!roots || igraph_vector_size(roots)==0) && rootlevel && igraph_vector_size(rootlevel) != 0 ) { IGRAPH_WARNING("Reingold-Tilford layout: 'rootlevel' ignored"); } /* ----------------------------------------------------------------------- */ /* If root vertices are not given, then do a topological sort and take the last element from every component for directed graphs, or select the vertex with the maximum degree from each component for undirected graphs */ if (!roots || igraph_vector_size(roots)==0) { igraph_vector_t order, membership; igraph_integer_t no_comps; long int i, noseen=0; IGRAPH_VECTOR_INIT_FINALLY(&myroots, 0); IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&membership, no_of_nodes); if (igraph_is_directed(graph) && mode != IGRAPH_ALL) { IGRAPH_CHECK(igraph_topological_sorting(graph, &order, mode2)); IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ 0, &no_comps, IGRAPH_STRONG)); } else { IGRAPH_CHECK(igraph_sort_vertex_ids_by_degree(graph, &order, igraph_vss_all(), IGRAPH_ALL, 0, IGRAPH_ASCENDING, 0)); IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ 0, &no_comps, IGRAPH_WEAK)); } IGRAPH_CHECK(igraph_vector_resize(&myroots, no_comps)); igraph_vector_null(&myroots); proots=&myroots; for (i=no_of_nodes-1; noseen < no_comps && i>=0; i--) { long int v=(long int) VECTOR(order)[i]; long int mem=(long int) VECTOR(membership)[v]; if (VECTOR(myroots)[mem]==0) { noseen += 1; VECTOR(myroots)[mem]=v+1; } } for (i=0; i 0 && igraph_vector_size(roots) > 1) { /* ----------------------------------------------------------------------- */ /* Many roots were given to us, check 'rootlevel' */ long int plus_levels=0; long int i; if (igraph_vector_size(roots) != igraph_vector_size(rootlevel)) { IGRAPH_ERROR("Reingold-Tilford: 'roots' and 'rootlevel' lengths differ", IGRAPH_EINVAL); } /* check if there is one which is not zero */ for (i=0; i=no_of_nodes) { IGRAPH_ERROR("invalid vertex id", IGRAPH_EINVVID); } } else { igraph_vector_t newedges; long int no_of_newedges=igraph_vector_size(proots); long int i; real_root=no_of_nodes; /* Make copy if needed */ if (pextended == graph) { pextended=&extended; IGRAPH_CHECK(igraph_copy(&extended, graph)); IGRAPH_FINALLY(igraph_destroy, &extended); } IGRAPH_VECTOR_INIT_FINALLY(&newedges, no_of_newedges*2); IGRAPH_CHECK(igraph_add_vertices(&extended, 1, 0)); for (i=0; i * This layout is almost the same as \ref igraph_layout_reingold_tilford(), but * the tree is drawn in a circular way, with the root vertex in the center. * * \param graph The graph object. * \param res The result, the coordinates in a matrix. The parameter * should point to an initialized matrix object and will be resized. * \param mode Specifies which edges to consider when building the tree. * If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN * then only the incoming edges of a parent are considered. If it is * \c IGRAPH_ALL then all edges are used (this was the behavior in * igraph 0.5 and before). This parameter also influences how the root * vertices are calculated, if they are not given. See the \p roots parameter. * \param roots The index of the root vertex or root vertices. * If this is a non-empty vector then the supplied vertex ids are used * as the roots of the trees (or a single tree if the graph is connected). * If it is a null pointer of a pointer to an empty vector, then the root * vertices are automatically calculated based on topological sorting, * performed with the opposite mode than the \p mode argument. * After the vertices have been sorted, one is selected from each component. * \param rootlevel This argument can be useful when drawing forests which are * not trees (i.e. they are unconnected and have tree components). It specifies * the level of the root vertices for every tree in the forest. It is only * considered if not a null pointer and the \p roots argument is also given * (and it is not a null pointer of an empty vector). Note that if you supply * a null pointer here and the graph has multiple components, all of the root * vertices will be mapped to the origin of the coordinate system, which does * not really make sense. * \return Error code. * * \sa \ref igraph_layout_reingold_tilford(). */ int igraph_layout_reingold_tilford_circular(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, const igraph_vector_t *roots, const igraph_vector_t *rootlevel) { long int no_of_nodes=igraph_vcount(graph); long int i; igraph_real_t ratio=2*M_PI*(no_of_nodes-1.0)/no_of_nodes; igraph_real_t minx, maxx; IGRAPH_CHECK(igraph_layout_reingold_tilford(graph, res, mode, roots, rootlevel)); if (no_of_nodes == 0) return 0; minx = maxx = MATRIX(*res, 0, 0); for (i=1; i maxx) maxx=MATRIX(*res, i, 0); if (MATRIX(*res, i, 0) < minx) minx=MATRIX(*res, i, 0); } ratio /= (maxx-minx); for (i=0; i this_node.x // other_node.y > this_node.y // the force will be on this_node away from other_node // the proportion (distance/y_distance) is equal to the proportion // (directed_force/y_force), as the two triangles are similar. // therefore, the magnitude of y_force = (directed_force*y_distance)/distance // the sign of y_force is negative, away from other_node igraph_real_t x_distance, y_distance; y_distance = MATRIX(*pos, other_node, 1)-MATRIX(*pos, this_node, 1); if (y_distance < 0) { y_distance = -y_distance; } *y = -1 * ((directed_force * y_distance) / distance); // the x component works in exactly the same way. x_distance = MATRIX(*pos, other_node, 0)-MATRIX(*pos, this_node, 0); if (x_distance < 0) { x_distance = -x_distance; } *x = -1 * ((directed_force * x_distance) / distance); // Now we need to reverse the polarity of our answers based on the falsness // of our assumptions. if (MATRIX(*pos, other_node, 0) < MATRIX(*pos, this_node, 0)) { *x = *x * -1; } if (MATRIX(*pos, other_node, 1) < MATRIX(*pos, this_node, 1)) { *y = *y * -1; } return 0; } int igraph_i_apply_electrical_force(const igraph_matrix_t *pos, igraph_vector_t *pending_forces_x, igraph_vector_t *pending_forces_y, long int other_node, long int this_node, igraph_real_t node_charge, igraph_real_t distance) { igraph_real_t directed_force = COULOMBS_CONSTANT * ((node_charge * node_charge)/(distance * distance)); igraph_real_t x_force, y_force; igraph_i_determine_electric_axal_forces(pos, &x_force, &y_force, directed_force, distance, other_node, this_node); VECTOR(*pending_forces_x)[this_node] += x_force; VECTOR(*pending_forces_y)[this_node] += y_force; VECTOR(*pending_forces_x)[other_node] -= x_force; VECTOR(*pending_forces_y)[other_node] -= y_force; return 0; } int igraph_i_determine_spring_axal_forces(const igraph_matrix_t *pos, igraph_real_t *x, igraph_real_t *y, igraph_real_t directed_force, igraph_real_t distance, int spring_length, long int other_node, long int this_node) { // if the spring is just the right size, the forces will be 0, so we can // skip the computation. // // if the spring is too long, our forces will be identical to those computed // by determine_electrical_axal_forces() (this_node will be pulled toward // other_node). // // if the spring is too short, our forces will be the opposite of those // computed by determine_electrical_axal_forces() (this_node will be pushed // away from other_node) // // finally, since both nodes are movable, only one-half of the total force // should be applied to each node, so half the forces for our answer. if (distance == spring_length) { *x = 0.0; *y = 0.0; } else { igraph_i_determine_electric_axal_forces(pos, x, y, directed_force, distance, other_node, this_node); if (distance < spring_length) { *x = -1 * *x; *y = -1 * *y; } *x = 0.5 * *x; *y = 0.5 * *y; } return 0; } int igraph_i_apply_spring_force(const igraph_matrix_t *pos, igraph_vector_t *pending_forces_x, igraph_vector_t *pending_forces_y, long int other_node, long int this_node, int spring_length, igraph_real_t spring_constant) { // determined using Hooke's Law: // force = -kx // where: // k = spring constant // x = displacement from ideal length in meters igraph_real_t distance, displacement, directed_force, x_force, y_force; distance = igraph_i_distance_between(pos, other_node, this_node); // let's protect ourselves from division by zero by ignoring two nodes that // happen to be in the same place. Since we separate all nodes before we // work on any of them, this will only happen in extremely rare circumstances, // and when it does, electrical force will probably push one or both of them // one way or another anyway. if (distance == 0.0) { return 0; } displacement = distance - spring_length; if (displacement < 0) { displacement = -displacement; } directed_force = -1 * spring_constant * displacement; // remember, this is force directed away from the spring; // a negative number is back towards the spring (or, in our case, back towards // the other node) // get the force that should be applied to >this< node igraph_i_determine_spring_axal_forces(pos, &x_force, &y_force, directed_force, distance, spring_length, other_node, this_node); VECTOR(*pending_forces_x)[this_node] += x_force; VECTOR(*pending_forces_y)[this_node] += y_force; VECTOR(*pending_forces_x)[other_node] -= x_force; VECTOR(*pending_forces_y)[other_node] -= y_force; return 0; } int igraph_i_move_nodes(igraph_matrix_t *pos, const igraph_vector_t *pending_forces_x, const igraph_vector_t *pending_forces_y, igraph_real_t node_mass, igraph_real_t max_sa_movement) { // Since each iteration is isolated, time is constant at 1. // Therefore: // Force effects acceleration. // acceleration (d(velocity)/time) = velocity // velocity (d(displacement)/time) = displacement // displacement = acceleration // determined using Newton's second law: // sum(F) = ma // therefore: // acceleration = force / mass // velocity = force / mass // displacement = force / mass long int this_node, no_of_nodes=igraph_vector_size(pending_forces_x); for (this_node=0; this_node < no_of_nodes; this_node++) { igraph_real_t x_movement, y_movement; x_movement = VECTOR(*pending_forces_x)[this_node] / node_mass; if (x_movement > max_sa_movement) { x_movement = max_sa_movement; } else if (x_movement < -max_sa_movement) { x_movement = -max_sa_movement; } y_movement = VECTOR(*pending_forces_y)[this_node] / node_mass; if (y_movement > max_sa_movement) { y_movement = max_sa_movement; } else if (y_movement < -max_sa_movement) { y_movement = -max_sa_movement; } MATRIX(*pos, this_node, 0) += x_movement; MATRIX(*pos, this_node, 1) += y_movement; } return 0; } /** * \function igraph_layout_graphopt * \brief Optimizes vertex layout via the graphopt algorithm. * * * This is a port of the graphopt layout algorithm by Michael Schmuhl. * graphopt version 0.4.1 was rewritten in C and the support for * layers was removed (might be added later) and a code was a bit * reorganized to avoid some unnecessary steps is the node charge (see below) * is zero. * * * graphopt uses physical analogies for defining attracting and repelling * forces among the vertices and then the physical system is simulated * until it reaches an equilibrium. (There is no simulated annealing or * anything like that, so a stable fixed point is not guaranteed.) * * * See also http://www.schmuhl.org/graphopt/ for the original graphopt. * \param graph The input graph. * \param res Pointer to an initialized matrix, the result will be stored here * and its initial contents is used the starting point of the simulation * if the \p use_seed argument is true. Note that in this case the * matrix should have the proper size, otherwise a warning is issued and * the supplied values are ignored. If no starting positions are given * (or they are invalid) then a random staring position is used. * The matrix will be resized if needed. * \param niter Integer constant, the number of iterations to perform. * Should be a couple of hundred in general. If you have a large graph * then you might want to only do a few iterations and then check the * result. If it is not good enough you can feed it in again in * the \p res argument. The original graphopt default if 500. * \param node_charge The charge of the vertices, used to calculate electric * repulsion. The original graphopt default is 0.001. * \param node_mass The mass of the vertices, used for the spring forces. * The original graphopt defaults to 30. * \param spring_length The length of the springs, an integer number. * The original graphopt defaults to zero. * \param spring_constant The spring constant, the original graphopt defaults * to one. * \param max_sa_movement Real constant, it gives the maximum amount of movement * allowed in a single step along a single axis. The original graphopt * default is 5. * \param use_seed Logical scalar, whether to use the positions in \p res as * a starting configuration. See also \p res above. * \return Error code. * * Time complexity: O(n (|V|^2+|E|) ), n is the number of iterations, * |V| is the number of vertices, |E| the number * of edges. If \p node_charge is zero then it is only O(n|E|). */ int igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t node_charge, igraph_real_t node_mass, igraph_real_t spring_length, igraph_real_t spring_constant, igraph_real_t max_sa_movement, igraph_bool_t use_seed) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); int my_spring_length=(int) spring_length; igraph_vector_t pending_forces_x, pending_forces_y; /* Set a flag to calculate (or not) the electrical forces that the nodes */ /* apply on each other based on if both node types' charges are zero. */ igraph_bool_t apply_electric_charges= (node_charge!=0); long int this_node, other_node, edge; igraph_real_t distance; long int i; IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_x, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_y, no_of_nodes); if (use_seed) { if (igraph_matrix_nrow(res) != no_of_nodes || igraph_matrix_ncol(res) != 2) { IGRAPH_WARNING("Invalid size for initial matrix, starting from random layout"); IGRAPH_CHECK(igraph_layout_random(graph, res)); } } else { IGRAPH_CHECK(igraph_layout_random(graph, res)); } IGRAPH_PROGRESS("Graphopt layout", 0, NULL); for(i=niter;i>0;i--) { /* Report progress in approx. every 100th step */ if (i%10 == 0) { IGRAPH_PROGRESS("Graphopt layout", 100.0-100.0*i/niter, NULL); } /* Clear pending forces on all nodes */ igraph_vector_null(&pending_forces_x); igraph_vector_null(&pending_forces_y); // Apply electrical force applied by all other nodes if (apply_electric_charges) { // Iterate through all nodes for (this_node = 0; this_node < no_of_nodes; this_node++) { IGRAPH_ALLOW_INTERRUPTION(); for (other_node = this_node + 1; other_node < no_of_nodes; other_node++) { distance = igraph_i_distance_between(res, this_node, other_node); // let's protect ourselves from division by zero by ignoring // two nodes that happen to be in the same place. Since we // separate all nodes before we work on any of them, this // will only happen in extremely rare circumstances, and when // it does, springs will probably pull them apart anyway. // also, if we are more than 50 away, the electric force // will be negligible. // ***** may not always be desirable **** if ((distance != 0.0) && (distance < 500.0)) { // if (distance != 0.0) { // Apply electrical force from node(counter2) on // node(counter) igraph_i_apply_electrical_force(res, &pending_forces_x, &pending_forces_y, other_node, this_node, node_charge, distance); } } } } // Apply force from springs for (edge = 0; edge < no_of_edges; edge++) { long int tthis_node=IGRAPH_FROM(graph, edge); long int oother_node=IGRAPH_TO(graph, edge); // Apply spring force on both nodes igraph_i_apply_spring_force(res, &pending_forces_x, &pending_forces_y, oother_node, tthis_node, my_spring_length, spring_constant); } // Effect the movement of the nodes based on all pending forces igraph_i_move_nodes(res, &pending_forces_x, &pending_forces_y, node_mass, max_sa_movement); } IGRAPH_PROGRESS("Graphopt layout", 100, NULL); igraph_vector_destroy(&pending_forces_y); igraph_vector_destroy(&pending_forces_x); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r, igraph_real_t cx, igraph_real_t cy, igraph_real_t startr, igraph_real_t killr); int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y, igraph_real_t *r); int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y, igraph_real_t *z, igraph_real_t *r); /** * \function igraph_layout_merge_dla * \brief Merge multiple layouts by using a DLA algorithm * * * First each layout is covered by a circle. Then the layout of the * largest graph is placed at the origin. Then the other layouts are * placed by the DLA algorithm, larger ones first and smaller ones * last. * \param thegraphs Pointer vector containing the graph object of * which the layouts will be merged. * \param coords Pointer vector containing matrix objects with the 2d * layouts of the graphs in \p thegraphs. * \param res Pointer to an initialized matrix object, the result will * be stored here. It will be resized if needed. * \return Error code. * * Added in version 0.2. This function is experimental. * * * Time complexity: TODO. */ int igraph_layout_merge_dla(igraph_vector_ptr_t *thegraphs, igraph_vector_ptr_t *coords, igraph_matrix_t *res) { long int graphs=igraph_vector_ptr_size(coords); igraph_vector_t sizes; igraph_vector_t x, y, r; igraph_vector_t nx, ny, nr; long int allnodes=0; long int i, j; long int actg; igraph_i_layout_mergegrid_t grid; long int jpos=0; igraph_real_t minx, maxx, miny, maxy; igraph_real_t area=0; igraph_real_t maxr=0; long int respos; /* Graphs are currently not used, only the coordinates */ IGRAPH_UNUSED(thegraphs); IGRAPH_VECTOR_INIT_FINALLY(&sizes, graphs); IGRAPH_VECTOR_INIT_FINALLY(&x, graphs); IGRAPH_VECTOR_INIT_FINALLY(&y, graphs); IGRAPH_VECTOR_INIT_FINALLY(&r, graphs); IGRAPH_VECTOR_INIT_FINALLY(&nx, graphs); IGRAPH_VECTOR_INIT_FINALLY(&ny, graphs); IGRAPH_VECTOR_INIT_FINALLY(&nr, graphs); RNG_BEGIN(); for (i=0; i maxr) { maxr=VECTOR(r)[i]; } igraph_i_layout_sphere_2d(mat, igraph_vector_e_ptr(&nx, i), igraph_vector_e_ptr(&ny, i), igraph_vector_e_ptr(&nr, i)); } igraph_vector_order2(&sizes); /* largest first */ /* 0. create grid */ minx=miny=-sqrt(5*area); maxx=maxy=sqrt(5*area); igraph_i_layout_mergegrid_init(&grid, minx, maxx, 200, miny, maxy, 200); IGRAPH_FINALLY(igraph_i_layout_mergegrid_destroy, &grid); /* fprintf(stderr, "Ok, starting DLA\n"); */ /* 1. place the largest */ actg=(long int) VECTOR(sizes)[jpos++]; igraph_i_layout_merge_place_sphere(&grid, 0, 0, VECTOR(r)[actg], actg); IGRAPH_PROGRESS("Merging layouts via DLA", 0.0, NULL); while (jposxmax) { xmax=MATRIX(*coords,i,0); } if (MATRIX(*coords,i,1) < ymin) { ymin=MATRIX(*coords,i,1); } else if (MATRIX(*coords,i,1)>ymax) { ymax=MATRIX(*coords,i,1); } } *x=(xmin+xmax)/2; *y=(ymin+ymax)/2; *r=sqrt( (xmax-xmin)*(xmax-xmin)+(ymax-ymin)*(ymax-ymin) ) / 2; return 0; } int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y, igraph_real_t *z, igraph_real_t *r) { long int nodes=igraph_matrix_nrow(coords); long int i; igraph_real_t xmin, xmax, ymin, ymax, zmin, zmax; xmin=xmax=MATRIX(*coords,0,0); ymin=ymax=MATRIX(*coords,0,1); zmin=zmax=MATRIX(*coords,0,2); for (i=1; ixmax) { xmax=MATRIX(*coords,i,0); } if (MATRIX(*coords,i,1) < ymin) { ymin=MATRIX(*coords,i,1); } else if (MATRIX(*coords,i,1)>ymax) { ymax=MATRIX(*coords,i,1); } if (MATRIX(*coords,i,2) < zmin) { zmin=MATRIX(*coords,i,2); } else if (MATRIX(*coords,i,2)>zmax) { zmax=MATRIX(*coords,i,2); } } *x=(xmin+xmax)/2; *y=(ymin+ymax)/2; *z=(zmin+zmax)/2; *r=sqrt( (xmax-xmin)*(xmax-xmin)+(ymax-ymin)*(ymax-ymin)+ (zmax-zmin)*(zmax-zmin) ) / 2; return 0; } #define DIST(x,y) (sqrt(pow((x)-cx,2)+pow((y)-cy,2))) int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r, igraph_real_t cx, igraph_real_t cy, igraph_real_t startr, igraph_real_t killr) { long int sp=-1; igraph_real_t angle, len; long int steps=0; /* The graph is not used, only its coordinates */ IGRAPH_UNUSED(actg); while (sp < 0) { /* start particle */ do { steps++; angle=RNG_UNIF(0,2*M_PI); len=RNG_UNIF(.5*startr, startr); *x=cx+len*cos(angle); *y=cy+len*sin(angle); sp=igraph_i_layout_mergegrid_get_sphere(grid, *x, *y, r); } while (sp >= 0); while (sp < 0 && DIST(*x,*y) * This layout requires a distance matrix, where the intersection of * row i and column j specifies the desired distance between vertex i * and vertex j. The algorithm will try to place the vertices in a * space having a given number of dimensions in a way that approximates * the distance relations prescribed in the distance matrix. igraph * uses the classical multidimensional scaling by Torgerson; for more * details, see Cox & Cox: Multidimensional Scaling (1994), Chapman * and Hall, London. * * * If the input graph is disconnected, igraph will decompose it * first into its subgraphs, lay out the subgraphs one by one * using the appropriate submatrices of the distance matrix, and * then merge the layouts using \ref igraph_layout_merge_dla. * Since \ref igraph_layout_merge_dla works for 2D layouts only, * you cannot run the MDS layout on disconnected graphs for * more than two dimensions. * * \param graph A graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized if needed. * \param dist The distance matrix. It must be symmetric and this * function does not check whether the matrix is indeed * symmetric. Results are unspecified if you pass a non-symmetric * matrix here. You can set this parameter to null; in this * case, the shortest path lengths between vertices will be * used as distances. * \param dim The number of dimensions in the embedding space. For * 2D layouts, supply 2 here. * \param options This argument is currently ignored, it was used for * ARPACK, but LAPACK is used now for calculating the eigenvectors. * \return Error code. * * Added in version 0.6. * * * Time complexity: usually around O(|V|^2 dim). */ int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, const igraph_matrix_t *dist, long int dim, igraph_arpack_options_t *options) { long int i, no_of_nodes=igraph_vcount(graph); igraph_matrix_t m; igraph_bool_t conn; RNG_BEGIN(); /* Check the distance matrix */ if (dist && (igraph_matrix_nrow(dist) != no_of_nodes || igraph_matrix_ncol(dist) != no_of_nodes)) { IGRAPH_ERROR("invalid distance matrix size", IGRAPH_EINVAL); } /* Check the number of dimensions */ if (dim <= 1) { IGRAPH_ERROR("dim must be positive", IGRAPH_EINVAL); } if (dim > no_of_nodes) { IGRAPH_ERROR("dim must be less than the number of nodes", IGRAPH_EINVAL); } /* Copy or obtain the distance matrix */ if (dist == 0) { IGRAPH_CHECK(igraph_matrix_init(&m, no_of_nodes, no_of_nodes)); IGRAPH_FINALLY(igraph_matrix_destroy, &m); IGRAPH_CHECK(igraph_shortest_paths(graph, &m, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL)); } else { IGRAPH_CHECK(igraph_matrix_copy(&m, dist)); IGRAPH_FINALLY(igraph_matrix_destroy, &m); /* Make sure that the diagonal contains zeroes only */ for (i = 0; i < no_of_nodes; i++) MATRIX(m, i, i) = 0.0; } /* Check whether the graph is connected */ IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (conn) { /* Yes, it is, just do the MDS */ IGRAPH_CHECK(igraph_i_layout_mds_single(graph, res, &m, dim)); } else { /* The graph is not connected, lay out the components one by one */ igraph_vector_ptr_t layouts; igraph_vector_t comp, vertex_order; igraph_t subgraph; igraph_matrix_t *layout; igraph_matrix_t dist_submatrix; igraph_bool_t *seen_vertices; long int j, n, processed_vertex_count = 0; IGRAPH_VECTOR_INIT_FINALLY(&comp, 0); IGRAPH_VECTOR_INIT_FINALLY(&vertex_order, no_of_nodes); IGRAPH_CHECK(igraph_vector_ptr_init(&layouts, 0)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &layouts); igraph_vector_ptr_set_item_destructor(&layouts, (igraph_finally_func_t*)igraph_matrix_destroy); IGRAPH_CHECK(igraph_matrix_init(&dist_submatrix, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &dist_submatrix); seen_vertices = igraph_Calloc(no_of_nodes, igraph_bool_t); if (seen_vertices == 0) IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, seen_vertices); for (i = 0; i < no_of_nodes; i++) { if (seen_vertices[i]) continue; /* This is a vertex whose component we did not lay out so far */ IGRAPH_CHECK(igraph_subcomponent(graph, &comp, i, IGRAPH_ALL)); /* Take the subgraph */ IGRAPH_CHECK(igraph_induced_subgraph(graph, &subgraph, igraph_vss_vector(&comp), IGRAPH_SUBGRAPH_AUTO)); IGRAPH_FINALLY(igraph_destroy, &subgraph); /* Calculate the submatrix of the distances */ IGRAPH_CHECK(igraph_matrix_select_rows_cols(&m, &dist_submatrix, &comp, &comp)); /* Allocate a new matrix for storing the layout */ layout = igraph_Calloc(1, igraph_matrix_t); if (layout == 0) IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, layout); IGRAPH_CHECK(igraph_matrix_init(layout, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, layout); /* Lay out the subgraph */ IGRAPH_CHECK(igraph_i_layout_mds_single(&subgraph, layout, &dist_submatrix, dim)); /* Store the layout */ IGRAPH_CHECK(igraph_vector_ptr_push_back(&layouts, layout)); IGRAPH_FINALLY_CLEAN(2); /* ownership of layout taken by layouts */ /* Free the newly created subgraph */ igraph_destroy(&subgraph); IGRAPH_FINALLY_CLEAN(1); /* Mark all the vertices in the component as visited */ n = igraph_vector_size(&comp); for (j = 0; j < n; j++) { seen_vertices[(long int)VECTOR(comp)[j]] = 1; VECTOR(vertex_order)[(long int)VECTOR(comp)[j]] = processed_vertex_count++; } } /* Merge the layouts - reusing dist_submatrix here */ IGRAPH_CHECK(igraph_layout_merge_dla(0, &layouts, &dist_submatrix)); /* Reordering the rows of res to match the original graph */ IGRAPH_CHECK(igraph_matrix_select_rows(&dist_submatrix, res, &vertex_order)); igraph_free(seen_vertices); igraph_matrix_destroy(&dist_submatrix); igraph_vector_ptr_destroy_all(&layouts); igraph_vector_destroy(&vertex_order); igraph_vector_destroy(&comp); IGRAPH_FINALLY_CLEAN(5); } RNG_END(); igraph_matrix_destroy(&m); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * \function igraph_layout_bipartite * Simple layout for bipartite graphs * * The layout is created by first placing the vertices in two rows, * according to their types. Then the positions within the rows are * optimized to minimize edge crossings, by calling \ref * igraph_layout_sugiyama(). * * \param graph The input graph. * \param types A boolean vector containing ones and zeros, the vertex * types. Its length must match the number of vertices in the graph. * \param res Pointer to an initialized matrix, the result, the x and * y coordinates are stored here. * \param hgap The preferred minimum horizontal gap between vertices * in the same layer (i.e. vertices of the same type). * \param vgap The distance between layers. * \param maxiter Maximum number of iterations in the crossing * minimization stage. 100 is a reasonable default; if you feel * that you have too many edge crossings, increase this. * \return Error code. * * \sa \ref igraph_layout_sugiyama(). */ int igraph_layout_bipartite(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, igraph_real_t hgap, igraph_real_t vgap, long int maxiter) { long int i, no_of_nodes=igraph_vcount(graph); igraph_vector_t layers; if (igraph_vector_bool_size(types) != no_of_nodes) { IGRAPH_ERROR("Invalid vertex type vector size", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&layers, no_of_nodes); for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Jörg Reichardt The original copyright notice follows here */ /*************************************************************************** NetRoutines.cpp - description ------------------- begin : Tue Oct 28 2003 copyright : (C) 2003 by Joerg Reichardt email : reichardt@mitte ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include "NetRoutines.h" #include "NetDataTypes.h" #include "igraph_types.h" #include "igraph_interface.h" #include "igraph_conversion.h" int igraph_i_read_network(const igraph_t *graph, const igraph_vector_t *weights, network *net, igraph_bool_t use_weights, unsigned int states) { double av_k=0.0, sum_weight=0.0, min_weight=1e60, max_weight=-1e60; unsigned long min_k=999999999, max_k=0; long max_index=0; char name[255]; NNode *node1,*node2; DLList_Iter iter; igraph_vector_t edgelist; long int no_of_edges=(long int)igraph_ecount(graph); long int ii; char *empty=new char[1]; empty[0]='\0'; IGRAPH_VECTOR_INIT_FINALLY(&edgelist, no_of_edges*2); IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0 /* rowwise */)); for (ii=0; iinode_list->Push(new NNode(i,0,net->link_list, empty,states)); max_index=i1; } if (max_indexnode_list->Push(new NNode(i,0,net->link_list, empty,states)); max_index=i2; } node1=net->node_list->Get(i1-1); sprintf(name,"%li",i1); node1->Set_Name(name); node2=net->node_list->Get(i2-1); sprintf(name,"%li",i2); node2->Set_Name(name); node1->Connect_To(node2,Links); if (Linksmax_weight) max_weight=Links; sum_weight+=Links; } IGRAPH_FINALLY_CLEAN(1); igraph_vector_destroy(&edgelist); node1=iter.First(net->node_list); while (!iter.End()) { if (node1->Get_Degree()>max_k) max_k=node1->Get_Degree(); if (node1->Get_Degree()Get_Degree(); av_k+=node1->Get_Degree(); node1=iter.Next(); } net->av_k=av_k/double(net->node_list->Size()); net->sum_weights=sum_weight; net->av_weight=sum_weight/double(net->link_list->Size()); net->min_k=min_k; net->max_k=max_k; net->min_weight=min_weight; net->max_weight=max_weight; net->sum_bids=0; net->min_bids=0; net->max_bids=0; delete [] empty; return 0; } //############################################################################################################### void reduce_cliques(DLList*> *global_cluster_list, FILE *file) { unsigned long size; ClusterList *c_cur, *largest_c=0; DLList*> *subsets; DLList_Iter*> c_iter, sub_iter; DLList_Iter iter; NNode *n_cur; if (!(global_cluster_list->Size())) return; //wir suchen den groessten Cluster c_cur=c_iter.First(global_cluster_list); size=0; while (!(c_iter.End())) { if (c_cur->Size()>size) { size=c_cur->Size(); largest_c=c_cur; } c_cur=c_iter.Next(); } // printf("Groesster Cluster hat %u Elemente.\n",largest_c->Size()); //Schauen, ob es Teilmengen gibt, die ebenfalls gefunden wurden subsets=new DLList*>(); c_cur=c_iter.First(global_cluster_list); while (!(c_iter.End())) { if ((*c_cur<*largest_c || *c_cur==*largest_c) && c_cur!=largest_c) //alle echten Teilcluster von largest_c und die doppelten { subsets->Push(c_cur); } c_cur=c_iter.Next(); } // die gefundenen Subsets werden aus der cluster_liste geloescht while (subsets->Size()) { global_cluster_list->fDelete(subsets->Pop()); } delete subsets; // Dann schreiben wir den groessten Cluster in das File fprintf(file,"Energie: %1.12f Nodes:%3lu - ",largest_c->Get_Energy(),largest_c->Size()); n_cur=iter.First(largest_c); while (!(iter.End())) { fprintf(file,"%s",n_cur->Get_Name()); n_cur=iter.Next(); if (n_cur) fprintf(file,", "); } fprintf(file,"\n"); //Schliesslich schmeissen wir noch den eben gefundenen groessten Cluster raus global_cluster_list->fDelete(largest_c); //und dann geht es von vorn mit der Reduzierten ClusterListe los reduce_cliques(global_cluster_list, file); } //################################################################################## void reduce_cliques2(network *net, bool only_double, long marker) { unsigned long size; ClusterList *c_cur, *largest_c=0; DLList_Iter*> c_iter; do { //wir suchen den groessten, nicht markierten Cluster size=0; c_cur=c_iter.First(net->cluster_list); while (!(c_iter.End())) { if ((c_cur->Size()>size) && (c_cur->Get_Marker()!=marker)) { size=c_cur->Size(); largest_c=c_cur; } c_cur=c_iter.Next(); } // printf("Groesster Cluster hat %u Elemente.\n",largest_c->Size()); //Schauen, ob es Teilmengen gibt, die ebenfalls gefunden wurden c_cur=c_iter.First(net->cluster_list); while (!(c_iter.End())) { if (((!only_double && (*c_cur<*largest_c)) || (*c_cur==*largest_c)) && (c_cur!=largest_c)) //alle echten Teilcluster von largest_c und die doppelten { net->cluster_list->fDelete(c_cur); while (c_cur->Get_Candidates()->Size()) c_cur->Get_Candidates()->Pop(); while (c_cur->Size()) c_cur->Pop(); // die knoten aber nicht loeschen!! delete c_cur; // nicht vergessen, die global geloeschte Clusterliste zu loeschen } c_cur=c_iter.Next(); } //Schliesslich markieren wir noch den eben gefundenen groessten Cluster largest_c->Set_Marker(marker); } while (size); } //################################################################################################## unsigned long iterate_nsf_hierarchy(NNode *parent, unsigned long depth,FILE *file) { NNode* next_node; unsigned long newdepth, maxdepth; bool first=true; DLList_Iter *iter; maxdepth=newdepth=depth; iter=new DLList_Iter; next_node=iter->First(parent->Get_Neighbours()); while (!(iter->End())) { if (next_node->Get_Marker()>parent->Get_Marker()) // wir gehen nach unten { if (first) fprintf(file,",("); // eine Neue Klammer auf if (first) fprintf(file,"%s",next_node->Get_Name()); // nur vor dem ersten kein Komma else fprintf(file,",%s",next_node->Get_Name()); // sonst immer mit Komma first=false; newdepth=iterate_nsf_hierarchy(next_node,depth+1, file); if (maxdepthNext(); } if (!first) fprintf(file,")"); //hat es ueberhaupt einen gegeben? //dann klamer zu! delete iter; return maxdepth; } //################################################################ void clear_all_markers(network *net) { DLList_Iter iter; NNode *n_cur; n_cur=iter.First(net->node_list); while (!iter.End()) { n_cur->Set_Marker(0); n_cur=iter.Next(); } } igraph/src/igraph_structural.h0000644000176000001440000001410212325527073016262 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_STRUCTURAL_H #define IGRAPH_STRUCTURAL_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" #include "igraph_datatype.h" #include "igraph_iterators.h" #include "igraph_attributes.h" #include "igraph_sparsemat.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Basic query functions */ /* -------------------------------------------------- */ int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); /* -------------------------------------------------- */ /* Structural properties */ /* -------------------------------------------------- */ int igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights); int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_t *mst); int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, const igraph_vector_t *weights); int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vid, igraph_neimode_t mode); int igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode); int igraph_subgraph(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids); int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl, igraph_vector_t *map, igraph_vector_t *invmap); int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl); int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, const igraph_es_t eids, igraph_bool_t delete_vertices); int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_bool_t loops, const igraph_attribute_combination_t *edge_comb); int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, igraph_bool_t ignore_loops, igraph_reciprocity_t mode); int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); int igraph_density(const igraph_t *graph, igraph_real_t *res, igraph_bool_t loops); int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es); int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, igraph_vector_t *circle); int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); int igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, igraph_neimode_t mode, const igraph_vector_t *roots, igraph_vector_t *vertex_index); int igraph_is_mutual(igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); int igraph_maximum_cardinality_search(const igraph_t *graph, igraph_vector_t *alpha, igraph_vector_t *alpham1); int igraph_is_chordal(const igraph_t *graph, const igraph_vector_t *alpha, const igraph_vector_t *alpham1, igraph_bool_t *chordal, igraph_vector_t *fill_in, igraph_t *newgraph); int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vs_t vids, igraph_vector_t *knn, igraph_vector_t *knnk, const igraph_vector_t *weights); int igraph_contract_vertices(igraph_t *graph, const igraph_vector_t *mapping, const igraph_attribute_combination_t *vertex_comb); int igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure); int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights, igraph_fas_algorithm_t algo); int igraph_diversity(igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *res, const igraph_vs_t vs); /* -------------------------------------------------- */ /* Spectral Properties */ /* -------------------------------------------------- */ int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, igraph_bool_t normalized, const igraph_vector_t *weights); /* -------------------------------------------------- */ /* Internal functions, may change any time */ /* -------------------------------------------------- */ int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights, igraph_vector_t *layering); int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights, igraph_vector_t *layering); __END_DECLS #endif igraph/src/revolver_ml_cit.c0000644000176000001440000031474712325527074015731 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_revolver.h" #include "igraph_memory.h" #include "igraph_random.h" #include "config.h" #include "igraph_math.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_structural.h" #include "igraph_nongraph.h" #include "igraph_stack.h" #include /* This data structure holds the context of the degree based optimizer. */ typedef struct igraph_i_revolver_ml_D_data_t { igraph_scalar_function_t *A; igraph_vector_function_t *dA; const igraph_t *graph; long int no_of_nodes; igraph_vector_t A_vect; /* Temporary storage */ igraph_vector_ptr_t dA_vects; /* Temporary storage */ igraph_integer_t maxdegree; igraph_vector_long_t degree; igraph_vector_t neis; igraph_vector_t dS; /* Temporary storage */ igraph_vector_t par1; /* More tmp storage */ igraph_vector_t tmpgrad; /* More... */ igraph_vector_t lastparam; /* The parameter values used last time */ igraph_real_t lastf; /* The evaluated function value */ igraph_vector_t lastgrad; /* The evaluated gradient */ const igraph_vector_t *filter; } igraph_i_revolver_ml_D_data_t; /* Evaluate the objective function and calculate its gradient too. */ int igraph_i_revolver_ml_D_eval(const igraph_vector_t *par, igraph_i_revolver_ml_D_data_t *data) { long int no_of_edges=0; igraph_real_t sum=0.0; long int t, i; int dim=(int) igraph_vector_size(par); igraph_vector_t *grad=&data->lastgrad; igraph_real_t S=0.0; /* sum N_i^t A(p,x) */ /* Init */ igraph_vector_long_null(&data->degree); igraph_vector_null(&data->dS); igraph_vector_null(grad); /* Calculate all possible A and dA values and store them in A_vect & dA_vects */ for (t=0; t<=data->maxdegree; t++) { VECTOR(data->par1)[0] = t; VECTOR(data->A_vect)[t] = data->A(&data->par1, par, 0); data->dA(&data->par1, par, &data->tmpgrad, 0); for (i=0; idA_vects)[i]; VECTOR(*v)[t] = VECTOR(data->tmpgrad)[i]; } } for (t=0; tno_of_nodes; t++) { long int n, nneis; IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_CHECK(igraph_neighbors(data->graph, &data->neis, (igraph_integer_t) t, IGRAPH_OUT)); nneis=igraph_vector_size(&data->neis); if (! data->filter || VECTOR(*data->filter)[t] != 0) { /* Update sum(s) */ for (n=0; nneis)[n]; long int x=VECTOR(data->degree)[to]; sum -= log( VECTOR(data->A_vect)[x] ); sum += log( S ); for (i=0; idA_vects)[i]; VECTOR(*grad)[i] -= VECTOR(*v)[x] / VECTOR(data->A_vect)[x]; VECTOR(*grad)[i] += VECTOR(data->dS)[i] / S; } no_of_edges++; } } /* Update S, data->dS */ for (n=0; nneis)[n]; long int x=VECTOR(data->degree)[to]; VECTOR(data->degree)[to] += 1; S += VECTOR(data->A_vect)[x+1]; S -= VECTOR(data->A_vect)[x]; for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += VECTOR(*v)[x+1]; VECTOR(data->dS)[i] -= VECTOR(*v)[x]; } } S += VECTOR(data->A_vect)[0]; for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += VECTOR(*v)[0]; } } igraph_vector_update(&data->lastparam, par); data->lastf=sum / no_of_edges; for (i=0; ilastgrad); i++) { VECTOR(data->lastgrad)[i] /= no_of_edges; } return 0; } /* This function gives the value of the objective function at the supplied parameter vector. Called by the optimizer. */ igraph_real_t igraph_i_revolver_ml_D_f(const igraph_vector_t *par, const igraph_vector_t *garbage, void* extra) { igraph_i_revolver_ml_D_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_D_eval(par, data); } return data->lastf; } /* This function gives the gradient of the objective function at the supplied parameter vector. Called by the optimizer. */ void igraph_i_revolver_ml_D_df(const igraph_vector_t *par, const igraph_vector_t *garbage, igraph_vector_t *res, void *extra) { igraph_i_revolver_ml_D_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_D_eval(par, data); } igraph_vector_update(res, &data->lastgrad); } void igraph_i_revolver_ml_D_free(igraph_vector_ptr_t *ptr) { long int i, n=igraph_vector_ptr_size(ptr); for (i=0; ilastgrad; igraph_real_t S=0.0; long int no_of_edges=0; /* Init */ igraph_vector_long_null(&data->degree); igraph_vector_null(&data->dS); igraph_vector_null(grad); for (i=0; inocats; i++) { for (j=0; jmaxdegree+1; j++) { long int k; VECTOR(data->par1)[0]=i; VECTOR(data->par1)[1]=j; MATRIX(data->A_vect, i, j) = data->A(&data->par1, par, 0); data->dA(&data->par1, par, &data->tmpgrad, 0); for (k=0; kdA_vects)[k]; MATRIX(*m, i, j)=VECTOR(data->tmpgrad)[k]; } } } for (t=0; tno_of_nodes; t++) { long int n, nneis; long int tcat=(long int) VECTOR(*data->cats)[t]; IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_CHECK(igraph_neighbors(data->graph, &data->neis, (igraph_integer_t) t, IGRAPH_OUT)); nneis=igraph_vector_size(&data->neis); if (! data->filter || VECTOR(*data->filter)[t]) { /* Update sum(s) */ for (n=0; nneis)[n]; long int x=(long int) VECTOR(*data->cats)[to]; long int y=VECTOR(data->degree)[to]; /* CHECK_VALID(x,y); */ sum -= log( MATRIX(data->A_vect, x, y) ); sum += log( S ); for (i=0; idA_vects)[i]; VECTOR(*grad)[i] -= MATRIX(*m, x, y) / MATRIX(data->A_vect, x, y); VECTOR(*grad)[i] += VECTOR(data->dS)[i] / S; } no_of_edges++; } } /* Update D, data->dS */ for (n=0; nneis)[n]; long int x=(long int) VECTOR(*data->cats)[to]; long int y=VECTOR(data->degree)[to]; VECTOR(data->degree)[to] += 1; S += MATRIX(data->A_vect, x, y+1); S -= MATRIX(data->A_vect, x, y); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += MATRIX(*m, x, y+1); VECTOR(data->dS)[i] -= MATRIX(*m, x, y); } } /* New vertex */ S += MATRIX(data->A_vect, tcat, 0); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += MATRIX(*m, tcat, 0); } } igraph_vector_update(&data->lastparam, par); data->lastf=sum / no_of_edges; for (i=0; ilastgrad); i++) { VECTOR(data->lastgrad)[i] /= no_of_edges; } return 0.0; } igraph_real_t igraph_i_revolver_ml_DE_f(const igraph_vector_t *par, const igraph_vector_t *garbage, void *extra) { igraph_i_revolver_ml_DE_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_DE_eval(par, data); } if (!igraph_finite(data->lastf)) { IGRAPH_WARNING("Target function evaluated to non-finite value."); } /* printf("eval ("); */ /* for (i=0; ilastf); */ return data->lastf; } void igraph_i_revolver_ml_DE_df(const igraph_vector_t *par, const igraph_vector_t *garbage, igraph_vector_t *res, void *extra) { igraph_i_revolver_ml_DE_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_DE_eval(par, data); } igraph_vector_update(res, &data->lastgrad); /* printf("derivative ("); */ /* for (i=0; iA_valid,(X),(Y))) { \ int i; \ VECTOR(data->par1)[0]=(X); VECTOR(data->par1)[1]=(Y); \ MATRIX(data->A_vect, (X), (Y)) = data->A(&data->par1, par, 0); \ data->dA(&data->par1, 0, &data->tmpgrad, 0); \ for (i=0; idA_vects)[i]; \ MATRIX(*m, (X), (Y)) = VECTOR(data->tmpgrad)[i]; \ } \ } int igraph_i_revolver_ml_AD_eval(const igraph_vector_t *par, igraph_i_revolver_ml_AD_data_t *data) { igraph_real_t sum=0.0; long int t, i, j; int dim=(int) igraph_vector_size(par); igraph_vector_t *grad=&data->lastgrad; igraph_real_t S=0.0; long int agebins=data->agebins; long int binwidth=data->no_of_nodes/agebins+1; long int no_of_edges=0; /* Init */ igraph_vector_long_null(&data->degree); igraph_vector_null(&data->dS); igraph_vector_null(grad); igraph_matrix_bool_null(&data->A_valid); for (i=0; imaxdegree+1; i++) { for (j=0; jpar1)[0]=(i); VECTOR(data->par1)[1]=(j); MATRIX(data->A_vect, (i), (j)) = data->A(&data->par1, par, 0); data->dA(&data->par1, par, &data->tmpgrad, 0); for (k=0; kdA_vects)[k]; MATRIX(*m, (i), (j)) = VECTOR(data->tmpgrad)[k]; } } } for (t=0; tno_of_nodes; t++) { long int n, nneis; IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_CHECK(igraph_neighbors(data->graph, &data->neis, (igraph_integer_t) t, IGRAPH_OUT)); nneis=igraph_vector_size(&data->neis); if (! data->filter || VECTOR(*data->filter)[t]) { /* Update sum(s) */ for (n=0; nneis)[n]; long int x=VECTOR(data->degree)[to]; long int y=(t-to)/binwidth; /* CHECK_VALID(x,y); */ sum -= log( MATRIX(data->A_vect, x, y) ); sum += log( S ); for (i=0; idA_vects)[i]; VECTOR(*grad)[i] -= MATRIX(*m, x, y) / MATRIX(data->A_vect, x, y); VECTOR(*grad)[i] += VECTOR(data->dS)[i] / S; } no_of_edges++; } } /* Update S, data->dS */ for (n=0; nneis)[n]; long int x=VECTOR(data->degree)[to]; long int y=(t-to)/binwidth; /* CHECK_VALID(x+1,y); /\* (x,y) already checked *\/ */ VECTOR(data->degree)[to] += 1; S += MATRIX(data->A_vect, x+1, y); S -= MATRIX(data->A_vect, x, y); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += MATRIX(*m, x+1, y); VECTOR(data->dS)[i] -= MATRIX(*m, x, y); } } /* New vertex */ /* CHECK_VALID(0,0); */ S += MATRIX(data->A_vect, 0, 0); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += MATRIX(*m, 0, 0); } /* Aging */ for (j=1; t-binwidth*j+1>=0; j++) { long int shnode=t-binwidth*j+1; long int deg=VECTOR(data->degree)[shnode]; /* CHECK_VALID(deg, j-1); */ /* CHECK_VALID(deg, j); */ S += MATRIX(data->A_vect, deg, j); S -= MATRIX(data->A_vect, deg, j-1); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += MATRIX(*m, deg, j); VECTOR(data->dS)[i] -= MATRIX(*m, deg, j-1); } } } igraph_vector_update(&data->lastparam, par); data->lastf=sum / no_of_edges; for (i=0; ilastgrad); i++) { VECTOR(data->lastgrad)[i] /= no_of_edges; } return 0.0; } igraph_real_t igraph_i_revolver_ml_AD_f(const igraph_vector_t *par, const igraph_vector_t *garbage, void *extra) { igraph_i_revolver_ml_AD_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_AD_eval(par, data); } if (!igraph_finite(data->lastf)) { IGRAPH_WARNING("Target function evaluated to non-finite value."); } /* printf("eval ("); */ /* for (i=0; ilastf); */ return data->lastf; } void igraph_i_revolver_ml_AD_df(const igraph_vector_t *par, const igraph_vector_t *garbage, igraph_vector_t *res, void *extra) { igraph_i_revolver_ml_AD_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_AD_eval(par, data); } igraph_vector_update(res, &data->lastgrad); /* printf("derivative ("); */ /* for (i=0; i parscale ? (p1+a)*log(age/parscale)*p2 : 0; VECTOR(*res)[3]= age < parscale ? (p1+a)*log(age/parscale)*p2 : 0; VECTOR(*res)[4]=-(p1+a)*(exponent-1)*pow(age/parscale, exponent-2)* age/parscale/parscale; /* printf("deriv at %f %f, %f %f %f %f %f: %f %f %f %f %f\n", deg, age, */ /* alpha, a, paralpha, parbeta, parscale, */ /* VECTOR(*res)[0], VECTOR(*res)[1], VECTOR(*res)[2], VECTOR(*res)[3], */ /* VECTOR(*res)[4]); */ } int igraph_revolver_ml_AD_dpareto_eval(const igraph_t *graph, igraph_real_t alpha, igraph_real_t a, igraph_real_t paralpha, igraph_real_t parbeta, igraph_real_t parscale, igraph_real_t *value, igraph_vector_t *deriv, int agebins, const igraph_vector_t *filter) { igraph_vector_t res; int ret; igraph_integer_t fncount, grcount; IGRAPH_VECTOR_INIT_FINALLY(&res, 5); VECTOR(res)[0] = alpha; VECTOR(res)[1] = a; VECTOR(res)[2] = paralpha; VECTOR(res)[3] = parbeta; VECTOR(res)[4] = parscale; ret=igraph_revolver_ml_AD(graph, &res, value, 0, 0, 0, igraph_i_revolver_ml_AD_dpareto_f, igraph_i_revolver_ml_AD_dpareto_df, agebins, filter, &fncount, &grcount, deriv); igraph_vector_destroy(&res); IGRAPH_FINALLY_CLEAN(1); return ret; } int igraph_revolver_ml_AD_dpareto(const igraph_t *graph, igraph_real_t *alpha, igraph_real_t *a, igraph_real_t *paralpha, igraph_real_t *parbeta, igraph_real_t *parscale, igraph_real_t *Fmin, igraph_real_t abstol, igraph_real_t reltol, int maxit, int agebins, const igraph_vector_t *filter, igraph_integer_t *fncount, igraph_integer_t *grcount) { igraph_vector_t res; int ret; IGRAPH_VECTOR_INIT_FINALLY(&res, 5); VECTOR(res)[0] = *alpha; VECTOR(res)[1] = *a; VECTOR(res)[2] = *paralpha; VECTOR(res)[3] = *parbeta; VECTOR(res)[4] = *parscale; ret=igraph_revolver_ml_AD(graph, &res, Fmin, abstol, reltol, maxit, igraph_i_revolver_ml_AD_dpareto_f, igraph_i_revolver_ml_AD_dpareto_df, agebins, filter, fncount, grcount, 0); *alpha=VECTOR(res)[0]; *a=VECTOR(res)[1]; *paralpha=VECTOR(res)[2]; *parbeta=VECTOR(res)[3]; *parscale=VECTOR(res)[4]; igraph_vector_destroy(&res); IGRAPH_FINALLY_CLEAN(1); return ret; } /*------------------------------------------------------------------*/ typedef struct igraph_i_revolver_ml_ADE_data_t { igraph_scalar_function_t *A; igraph_vector_function_t *dA; const igraph_t *graph; const igraph_vector_t *cats; long int no_of_nodes; igraph_array3_t A_vect; /* Temporary storage */ igraph_vector_ptr_t dA_vects; /* Temporary storage */ igraph_integer_t maxdegree; igraph_integer_t nocats; igraph_vector_long_t degree; igraph_vector_t neis; igraph_vector_t dS; /* Temporary storage */ igraph_vector_t par1; /* More tmp storage */ igraph_vector_t tmpgrad; /* More... */ int agebins; igraph_vector_t lastparam; /* The parameter values used last time */ igraph_real_t lastf; /* The evaluated function value */ igraph_vector_t lastgrad; /* The evaluated gradient */ const igraph_vector_t *filter; } igraph_i_revolver_ml_ADE_data_t; int igraph_i_revolver_ml_ADE_eval(const igraph_vector_t *par, igraph_i_revolver_ml_ADE_data_t *data) { igraph_real_t sum=0.; long int t, i, j, c; int dim=(int) igraph_vector_size(par); igraph_vector_t *grad=&data->lastgrad; igraph_real_t S=0.0; long int agebins=data->agebins; long int binwidth=data->no_of_nodes/agebins+1; long int no_of_edges=0; /* Init */ igraph_vector_long_null(&data->degree); igraph_vector_null(&data->dS); igraph_vector_null(grad); for (c=0; cnocats; c++) { for (i=0; imaxdegree+1; i++) { for (j=0; jpar1)[0]=c; VECTOR(data->par1)[1]=i; VECTOR(data->par1)[2]=j; ARRAY3(data->A_vect, c, (i), (j)) = data->A(&data->par1, par, 0); data->dA(&data->par1, par, &data->tmpgrad, 0); for (k=0; kdA_vects)[k]; ARRAY3(*m, c, i, j) = VECTOR(data->tmpgrad)[k]; } } } } for (t=0; tno_of_nodes; t++) { long int n, nneis, shnode; long int tcat=(long int) VECTOR(*data->cats)[t]; IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_CHECK(igraph_neighbors(data->graph, &data->neis, (igraph_integer_t) t, IGRAPH_OUT)); nneis=igraph_vector_size(&data->neis); if (! data->filter || VECTOR(*data->filter)[t]) { /* Update sum(s) */ for (n=0; nneis)[n]; long int x=(long int) VECTOR(*data->cats)[to]; long int y=VECTOR(data->degree)[to]; long int z=(t-to)/binwidth; /* CHECK_VALID(x,y); */ sum -= log( ARRAY3(data->A_vect, x, y, z) ); sum += log( S ); for (i=0; idA_vects)[i]; VECTOR(*grad)[i] -= ARRAY3(*m, x, y, z) / ARRAY3(data->A_vect, x, y, z); VECTOR(*grad)[i] += VECTOR(data->dS)[i] / S; } no_of_edges++; } } /* Update S, data->dS */ for (n=0; nneis)[n]; long int x=(long int) VECTOR(*data->cats)[to]; long int y=VECTOR(data->degree)[to]; long int z=(t-to)/binwidth; /* CHECK_VALID(x+1,y); /\* (x,y) already checked *\/ */ VECTOR(data->degree)[to] += 1; S += ARRAY3(data->A_vect, x, y+1, z); S -= ARRAY3(data->A_vect, x, y, z); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += ARRAY3(*m, x, y+1, z); VECTOR(data->dS)[i] -= ARRAY3(*m, x, y, z); } } /* New vertex */ /* CHECK_VALID(0,0); */ S += ARRAY3(data->A_vect, tcat, 0, 0); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += ARRAY3(*m, tcat, 0, 0); } /* Aging */ for (j=1, shnode=t-binwidth+1; shnode>=0; j++, shnode-=binwidth) { long int cat=(long int) VECTOR(*data->cats)[shnode]; long int deg=VECTOR(data->degree)[shnode]; /* CHECK_VALID(deg, j-1); */ /* CHECK_VALID(deg, j); */ S += ARRAY3(data->A_vect, cat, deg, j); S -= ARRAY3(data->A_vect, cat, deg, j-1); for (i=0; idA_vects)[i]; VECTOR(data->dS)[i] += ARRAY3(*m, cat, deg, j); VECTOR(data->dS)[i] -= ARRAY3(*m, cat, deg, j-1); } } } igraph_vector_update(&data->lastparam, par); data->lastf=sum / no_of_edges; for (i=0; ilastgrad); i++) { VECTOR(data->lastgrad)[i] /= no_of_edges; } return 0.0; } igraph_real_t igraph_i_revolver_ml_ADE_f(const igraph_vector_t *par, const igraph_vector_t *garbage, void *extra) { igraph_i_revolver_ml_ADE_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_ADE_eval(par, data); } if (!igraph_finite(data->lastf)) { IGRAPH_WARNING("Target function evaluated to non-finite value."); } /* printf("eval ("); */ /* for (i=0; ilastf); */ return data->lastf; } void igraph_i_revolver_ml_ADE_df(const igraph_vector_t *par, const igraph_vector_t *garbage, igraph_vector_t *res, void *extra) { igraph_i_revolver_ml_ADE_data_t *data=extra; if (!igraph_vector_all_e(par, &data->lastparam)) { igraph_i_revolver_ml_ADE_eval(par, data); } igraph_vector_update(res, &data->lastgrad); /* printf("derivative ("); */ /* for (i=0; i parscale ? c * (p1+a)*log(age/parscale)*p2 : 0; VECTOR(*res)[3]= age < parscale ? c * (p1+a)*log(age/parscale)*p2 : 0; VECTOR(*res)[4]= c * -(p1+a)*(exponent-1)*pow(age/parscale, exponent-2)* age/parscale/parscale; VECTOR(*res)[4+cat] = (p1+a) * p2; /* printf("deriv at %f %f, %f %f %f %f %f: %f %f %f %f %f\n", deg, age, */ /* alpha, a, paralpha, parbeta, parscale, */ /* VECTOR(*res)[0], VECTOR(*res)[1], VECTOR(*res)[2], VECTOR(*res)[3], */ /* VECTOR(*res)[4]); */ } int igraph_revolver_ml_ADE_dpareto_eval(const igraph_t *graph, const igraph_vector_t *cats, igraph_real_t alpha, igraph_real_t a, igraph_real_t paralpha, igraph_real_t parbeta, igraph_real_t parscale, const igraph_vector_t *coeffs, igraph_real_t *value, igraph_vector_t *deriv, int agebins, const igraph_vector_t *filter) { igraph_vector_t res; int ret, i; igraph_integer_t fncount, grcount; IGRAPH_VECTOR_INIT_FINALLY(&res, 5+igraph_vector_size(coeffs)); VECTOR(res)[0] = alpha; VECTOR(res)[1] = a; VECTOR(res)[2] = paralpha; VECTOR(res)[3] = parbeta; VECTOR(res)[4] = parscale; for (i=0; i=0; j++, shnode-=binwidth) { long int cat=(long int) VECTOR(*cats)[shnode]; long int deg=VECTOR(degree)[shnode]; for (i=0; i maxdelta) { maxdelta=diff; } } } if (maxdelta < delta) { break; } /* Switch kernels */ actkernel=1-actkernel; fromkernel=kernels[actkernel]; tokernel=kernels[1-actkernel]; IGRAPH_PROGRESS("ML Revolver d", 100*(it+1)/niter, NULL); } /* it=0; i--) { j=RNG_INTEGER(0,i); tmp=VECTOR(*v)[i]; VECTOR(*v)[i] = VECTOR(*v)[j]; VECTOR(*v)[j] = tmp; } return 0; } int igraph_revolver_ml_f(const igraph_t *graph, igraph_integer_t niter, igraph_vector_t *kernel, igraph_vector_t *cites, igraph_real_t delta, igraph_real_t *logprob, igraph_real_t *logmax) { long int no_of_nodes=igraph_vcount(graph); long int it, t; igraph_vector_long_t ptk; igraph_vector_t *mycites, vmycites; igraph_vector_int_t *neis, *neis2; igraph_adjlist_t outadjlist, inadjlist; igraph_vector_long_t marked; igraph_vector_t vmykernel; igraph_vector_t *kernels[]={ kernel, &vmykernel }; long int actkernel=0; igraph_vector_t *fromkernel=kernels[actkernel], *tokernel=kernels[1-actkernel]; igraph_vector_t perm; IGRAPH_VECTOR_INIT_FINALLY(&perm, 0); IGRAPH_CHECK(igraph_vector_reserve(&perm, no_of_nodes)); IGRAPH_CHECK(igraph_vector_long_init(&ptk, 2)); IGRAPH_FINALLY(igraph_vector_long_destroy, &ptk); IGRAPH_CHECK(igraph_adjlist_init(graph, &outadjlist, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &outadjlist); IGRAPH_CHECK(igraph_adjlist_init(graph, &inadjlist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &inadjlist); IGRAPH_VECTOR_INIT_FINALLY(&vmykernel, 2); IGRAPH_CHECK(igraph_vector_long_init(&marked, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &marked); if (cites) { IGRAPH_CHECK(igraph_vector_resize(cites, 2)); igraph_vector_null(cites); mycites=cites; } else { IGRAPH_VECTOR_INIT_FINALLY(&vmycites, 2); mycites=&vmycites; } IGRAPH_CHECK(igraph_vector_resize(kernel, 2)); igraph_vector_fill(kernel, 1); IGRAPH_PROGRESS("ML revolver f", 0, NULL); RNG_BEGIN(); for (it=0; it= t) { break; } if (VECTOR(marked)[nei] != t+1) { VECTOR(marked)[nei] = t+1; VECTOR(ptk)[0] -= 1; VECTOR(ptk)[1] += 1; } } neis2=igraph_adjlist_get(&outadjlist, to); nneis2=igraph_vector_int_size(neis2); for (j=0; j no_of_nodes ? no_of_nodes : maxdegree * maxdegree)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_CHECK(igraph_vector_long_init(°ree, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, °ree); IGRAPH_CHECK(igraph_matrix_long_init(&ptk, 2, maxdegree+1)); IGRAPH_FINALLY(igraph_matrix_long_destroy, &ptk); IGRAPH_CHECK(igraph_adjlist_init(graph, &outadjlist, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &outadjlist); IGRAPH_CHECK(igraph_adjlist_init(graph, &inadjlist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &inadjlist); IGRAPH_MATRIX_INIT_FINALLY(&vmykernel, 3, maxdegree+1); IGRAPH_CHECK(igraph_vector_long_init(&marked, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &marked); if (cites) { IGRAPH_CHECK(igraph_matrix_resize(cites, 3, maxdegree+1)); igraph_matrix_null(cites); mycites=cites; } else { IGRAPH_MATRIX_INIT_FINALLY(&vmycites, 3, maxdegree+1); mycites=&vmycites; } IGRAPH_CHECK(igraph_matrix_resize(kernel, 3, maxdegree+1)); igraph_matrix_fill(kernel, 1); IGRAPH_PROGRESS("ML revolver df", 0, NULL); RNG_BEGIN(); for (it=0; it 0) { MATRIX(ptk, 0, deg-1) += 1; MATRIX(ptk, 1, deg-1) = 0; } else { MATRIX(ptk, 0, -deg-1) -= 1; MATRIX(ptk, 1, -deg-1) = 0; } } S2=S3; for (e=0; e actmaxdegree) { actmaxdegree ++; } MATRIX(ptk, x, y) -= 1; /* won't be cited again by this vertex */ S1 -= MATRIX(*fromkernel, 0, y); S1 += MATRIX(*fromkernel, 0, y+1); S3 -= MATRIX(*fromkernel, 1, y); S3 += MATRIX(*fromkernel, 1, y+1); S2 -= MATRIX(*fromkernel, x+1, y); if (x==0) { igraph_stack_push(&stack, y+2); } else { igraph_stack_push(&stack, -y-1); igraph_stack_push(&stack, y+2); } /* neighbors of 'to' */ neis2=igraph_adjlist_get(&inadjlist, to); nneis2=igraph_vector_int_size(neis2); for (j=0; j= t) { break; } if (VECTOR(marked)[nei] != t+1) { long int neideg=VECTOR(degree)[nei]; VECTOR(marked)[nei] = t+1; MATRIX(ptk, 0, neideg) -= 1; MATRIX(ptk, 1, neideg) += 1; S2 -= MATRIX(*fromkernel, 1, neideg) - MATRIX(*fromkernel, 2, neideg); igraph_stack_push(&stack, neideg+1); } } neis2=igraph_adjlist_get(&outadjlist, to); nneis2=igraph_vector_int_size(neis2); for (j=0; j=0; j++) { long int shnode=t-binwidth*j+1; long int deg=VECTOR(degree)[shnode]; MATRIX(ptk, deg, j) += 1; MATRIX(ptk, deg, j-1) -= 1; S += MATRIX(*fromkernel, deg, j); S -= MATRIX(*fromkernel, deg, j-1); } MATRIX(ptk, 0, 0) += 1; S += MATRIX(*fromkernel, 0, 0); } /* t < no_of_nodes */ /* Mk/sum */ maxdelta=0.0; for (i=0; i<=maxdegree; i++) { for (j=0; j maxdelta){ maxdelta=diff; } } } if (maxdelta < delta) { break; } /* Switch kernels */ actkernel=1-actkernel; fromkernel=kernels[actkernel]; tokernel=kernels[1-actkernel]; IGRAPH_PROGRESS("ML Revolver d", 100*(it+1)/niter, NULL); } /* it maxdelta) { maxdelta=diff; } } } } if (maxdelta < delta) { break; } /* Switch kernels */ actkernel=1-actkernel; fromkernel=kernels[actkernel]; tokernel=kernels[1-actkernel]; IGRAPH_PROGRESS("ML Revolver de", 100*(it+1)/niter, NULL); } /* it=0; j++) { long int shnode=t-binwidth*j+1; long int cat=(long int) VECTOR(*cats)[shnode]; long int deg=VECTOR(degree)[shnode]; ARRAY3(ptk, cat, deg, j) += 1; ARRAY3(ptk, cat, deg, j-1) -= 1; S += ARRAY3(*fromkernel, cat, deg, j); S -= ARRAY3(*fromkernel, cat, deg, j-1); } ARRAY3(ptk, tcat, 0, 0) += 1; S += ARRAY3(*fromkernel, tcat, 0, 0); } /* t < no_of_nodes */ /* Mk/sum */ maxdelta=0.0; for (i=0; i maxdelta) { maxdelta=diff; } } } } if (maxdelta < delta) { break; } /* Switch kernels */ actkernel=1-actkernel; fromkernel=kernels[actkernel]; tokernel=kernels[1-actkernel]; IGRAPH_PROGRESS("ML Revolver d", 100*(it+1)/niter, NULL); } /* it=0; k++) { long int shnode=t+1-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); nneis=igraph_vector_size(&neis); for (i=0; i maxdelta) { maxdelta=diff; } } if (maxdelta < delta) { break; } /* Switch kernels */ actkernel=1-actkernel; fromkernel=kernels[actkernel]; tokernel=kernels[1-actkernel]; IGRAPH_PROGRESS("ML Revolver l", 100*(it+1)/niter, NULL); } /* it < niter */ /* Switch kernels if needed */ if (fromkernel != kernel) { igraph_vector_update(kernel, fromkernel); } if (!cites) { igraph_vector_destroy(&vmycites); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&vmykernel); igraph_vector_long_destroy(&lastcit); igraph_vector_destroy(&neis); igraph_vector_long_destroy(&ptk); IGRAPH_FINALLY_CLEAN(4); return 0; } /* -----------------------------------------------------------*/ int igraph_revolver_probs_d(const igraph_t *graph, const igraph_vector_t *kernel, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting, igraph_bool_t pntk) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vector_long_t degree; igraph_vector_t neis; long int t; igraph_real_t S=0.0; igraph_vector_long_t ntk; long int ntksize=igraph_vector_size(kernel); IGRAPH_CHECK(igraph_vector_long_init(°ree, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, °ree); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (pntk) { IGRAPH_CHECK(igraph_vector_long_init(&ntk, ntksize)); IGRAPH_FINALLY(igraph_vector_long_destroy, &ntk); } if (logprobs) { IGRAPH_CHECK(igraph_vector_resize(logprobs, no_of_edges)); } if (logciting) { IGRAPH_CHECK(igraph_vector_resize(logciting, no_of_nodes)); igraph_vector_null(logciting); } if (logcited) { IGRAPH_CHECK(igraph_vector_resize(logcited, no_of_nodes)); igraph_vector_null(logcited); } for (t=0; t=0; j++) { long int shnode=t-binwidth*j+1; long int deg=VECTOR(degree)[shnode]; if (pntk) { MATRIX(ntk, deg, j) += 1; MATRIX(ntk, deg, j-1) -= 1; } S += MATRIX(*kernel, deg, j); S -= MATRIX(*kernel, deg, j-1); } if (pntk) { MATRIX(ntk, 0, 0) += 1; } S += MATRIX(*kernel, 0, 0); } /* t < no_of_nodes */ if (pntk) { igraph_matrix_long_destroy(&ntk); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&neis); igraph_vector_long_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_revolver_probs_de(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *cats, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vector_long_t degree; igraph_vector_t neis; long int t; igraph_real_t S=0.0; IGRAPH_CHECK(igraph_vector_long_init(°ree, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, °ree); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (logprobs) { IGRAPH_CHECK(igraph_vector_resize(logprobs, no_of_edges)); } if (logcited) { IGRAPH_CHECK(igraph_vector_resize(logcited, no_of_nodes)); igraph_vector_null(logcited); } if (logciting) { IGRAPH_CHECK(igraph_vector_resize(logciting, no_of_nodes)); igraph_vector_null(logciting); } for (t=0; t=0; j++) { long int shnode=t-binwidth*j+1; long int cat=(long int) VECTOR(*cats)[shnode]; long int deg=VECTOR(degree)[shnode]; S += ARRAY3(*kernel, cat, deg, j); S -= ARRAY3(*kernel, cat, deg, j-1); } S += ARRAY3(*kernel, (long int) VECTOR(*cats)[t], 0, 0); } /* t < no_of_nodes */ igraph_vector_destroy(&neis); igraph_vector_long_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_revolver_probs_ADE(const igraph_t *graph, igraph_scalar_function_t *A_fun, const igraph_matrix_t *par, const igraph_vector_t *cats, const igraph_vector_t *gcats, int agebins, igraph_vector_t *logprobs, igraph_vector_t *logcited, igraph_vector_t *logciting) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vector_long_t degree; igraph_vector_t neis; igraph_vector_t S; igraph_vector_t gpar; igraph_vector_t var; int parlen=(int) igraph_matrix_nrow(par); int no_gcats=(int) igraph_matrix_ncol(par); long int t, i, j; long int binwidth=no_of_nodes/agebins+1; IGRAPH_CHECK(igraph_vector_long_init(°ree, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, °ree); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&S, no_gcats); IGRAPH_VECTOR_INIT_FINALLY(&var, 3); if (logprobs) { IGRAPH_CHECK(igraph_vector_resize(logprobs, no_of_edges)); } if (logcited) { IGRAPH_CHECK(igraph_vector_resize(logcited, no_of_nodes)); igraph_vector_null(logcited); } if (logciting) { IGRAPH_CHECK(igraph_vector_resize(logciting, no_of_nodes)); igraph_vector_null(logciting); } for (t=0; t=0; j++) { long int shnode=t-binwidth*j+1; VECTOR(var)[0] = VECTOR(*cats)[shnode]; VECTOR(var)[1] = VECTOR(degree)[shnode]; VECTOR(var)[2] = j; for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_nongraph.h" #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_interrupt_internal.h" #include "igraph_types_internal.h" #include "config.h" #include "plfit/error.h" #include "plfit/plfit.h" #include #include #include /** * \ingroup nongraph * \function igraph_running_mean * \brief Calculates the running mean of a vector. * * * The running mean is defined by the mean of the * previous \p binwidth values. * \param data The vector containing the data. * \param res The vector containing the result. This should be * initialized before calling this function and will be * resized. * \param binwidth Integer giving the width of the bin for the running * mean calculation. * \return Error code. * * Time complexity: O(n), * n is the length of * the data vector. */ int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, igraph_integer_t binwidth) { double sum=0; long int i; /* Check */ if (igraph_vector_size(data) < binwidth) { IGRAPH_ERROR("Vector too short for this binwidth", IGRAPH_EINVAL); } /* Memory for result */ IGRAPH_CHECK(igraph_vector_resize(res, (long int)(igraph_vector_size(data)-binwidth+1))); /* Initial bin */ for (i=0; i * The convex hull is determined by the Graham scan algorithm. * See the following reference for details: * * * Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford * Stein. Introduction to Algorithms, Second Edition. MIT Press and * McGraw-Hill, 2001. ISBN 0262032937. Pages 949-955 of section 33.3: * Finding the convex hull. * * \param data vector containing the coordinates. The length of the * vector must be even, since it contains X-Y coordinate pairs. * \param resverts the vector containing the result, e.g. the vector of * vertex indices used as the corners of the convex hull. Supply * \c NULL here if you are only interested in the coordinates of * the convex hull corners. * \param rescoords the matrix containing the coordinates of the selected * corner vertices. Supply \c NULL here if you are only interested in * the vertex indices. * \return Error code: * \c IGRAPH_ENOMEM: not enough memory * * Time complexity: O(n log(n)) where n is the number of vertices * * \example examples/simple/igraph_convex_hull.c */ int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, igraph_matrix_t *rescoords) { igraph_integer_t no_of_nodes; long int i, pivot_idx=0, last_idx, before_last_idx, next_idx, j; igraph_real_t* angles; igraph_vector_t stack; igraph_indheap_t order; igraph_real_t px, py, cp; no_of_nodes=(igraph_integer_t) igraph_matrix_nrow(data); if (igraph_matrix_ncol(data) != 2) { IGRAPH_ERROR("matrix must have 2 columns", IGRAPH_EINVAL); } if (no_of_nodes == 0) { if (resverts != 0) { IGRAPH_CHECK(igraph_vector_resize(resverts, 0)); } if (rescoords != 0) { IGRAPH_CHECK(igraph_matrix_resize(rescoords, 0, 2)); } /**************************** this is an exit here *********/ return 0; } angles=igraph_Calloc(no_of_nodes, igraph_real_t); if (!angles) IGRAPH_ERROR("not enough memory for angle array", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, angles); IGRAPH_VECTOR_INIT_FINALLY(&stack, 0); /* Search for the pivot vertex */ for (i=1; i= 0 && j > 2) { igraph_vector_pop_back(&stack); j--; last_idx=(long int) VECTOR(stack)[j-1]; before_last_idx=(long int) VECTOR(stack)[j-2]; cp=(MATRIX(*data, last_idx, 0)-MATRIX(*data, before_last_idx, 0))* (MATRIX(*data, next_idx, 1)-MATRIX(*data, before_last_idx, 1))- (MATRIX(*data, next_idx, 0)-MATRIX(*data, before_last_idx, 0))* (MATRIX(*data, last_idx, 1)-MATRIX(*data, before_last_idx, 1)); } IGRAPH_CHECK(igraph_vector_push_back(&stack, next_idx)); j++; } } } /* Create result vector */ if (resverts != 0) { igraph_vector_clear(resverts); IGRAPH_CHECK(igraph_vector_append(resverts, &stack)); } if (rescoords != 0) { igraph_matrix_select_rows(data, rescoords, &stack); } /* Free everything */ igraph_vector_destroy(&stack); igraph_indheap_destroy(&order); IGRAPH_FINALLY_CLEAN(2); return 0; } static const char* igraph_i_plfit_error_message = 0; static void igraph_i_plfit_error_handler_store(const char *reason, const char *file, int line, int plfit_errno) { igraph_i_plfit_error_message = reason; } /** * \ingroup nongraph * \function igraph_power_law_fit * \brief Fits a power-law distribution to a vector of numbers * * This function fits a power-law distribution to a vector containing samples * from a distribution (that is assumed to follow a power-law of course). In * a power-law distribution, it is generally assumed that P(X=x) is * proportional to x-alpha, where x is a positive number and alpha * is greater than 1. In many real-world cases, the power-law behaviour kicks * in only above a threshold value \em xmin. The goal of this functions is to * determine \em alpha if \em xmin is given, or to determine \em xmin and the * corresponding value of \em alpha. * * * The function uses the maximum likelihood principle to determine \em alpha * for a given \em xmin; in other words, the function will return the \em alpha * value for which the probability of drawing the given sample is the highest. * When \em xmin is not given in advance, the algorithm will attempt to find * the optimal \em xmin value for which the p-value of a Kolmogorov-Smirnov * test between the fitted distribution and the original sample is the largest. * The function uses the method of Clauset, Shalizi and Newman to calculate the * parameters of the fitted distribution. See the following reference for * details: * * * Aaron Clauset, Cosma R .Shalizi and Mark E.J. Newman: Power-law * distributions in empirical data. SIAM Review 51(4):661-703, 2009. * * \param data vector containing the samples for which a power-law distribution * is to be fitted. Note that you have to provide the \em samples, * not the probability density function or the cumulative * distribution function. For example, if you wish to fit * a power-law to the degrees of a graph, you can use the output of * \ref igraph_degree directly as an input argument to * \ref igraph_power_law_fit * \param result the result of the fitting algorithm. See \ref igraph_plfit_result_t * for more details. * \param xmin the minimum value in the sample vector where the power-law * behaviour is expected to kick in. Samples smaller than \c xmin * will be ignored by the algoritm. Pass zero here if you want to * include all the samples. If \c xmin is negative, the algorithm * will attempt to determine its best value automatically. * \param force_continuous assume that the samples in the \c data argument come * from a continuous distribution even if the sample vector * contains integer values only (by chance). If this argument is * false, igraph will assume a continuous distribution if at least * one sample is non-integer and assume a discrete distribution * otherwise. * \return Error code: * \c IGRAPH_ENOMEM: not enough memory * \c IGRAPH_EINVAL: one of the arguments is invalid * \c IGRAPH_EOVERFLOW: overflow during the fitting process * \c IGRAPH_EUNDERFLOW: underflow during the fitting process * \c IGRAPH_FAILURE: the underlying algorithm signaled a failure * without returning a more specific error code * * Time complexity: in the continuous case, O(n log(n)) if \c xmin is given. * In the discrete case, the time complexity is dominated by the complexity of * the underlying L-BFGS algorithm that is used to optimize alpha. If \c xmin * is not given, the time complexity is multiplied by the number of unique * samples in the input vector (although it should be faster in practice). * * \example examples/simple/igraph_power_law_fit.c */ int igraph_power_law_fit(const igraph_vector_t* data, igraph_plfit_result_t* result, igraph_real_t xmin, igraph_bool_t force_continuous) { plfit_error_handler_t* plfit_stored_error_handler; plfit_result_t plfit_result; plfit_continuous_options_t cont_options; plfit_discrete_options_t disc_options; igraph_bool_t discrete = force_continuous ? 0 : 1; igraph_bool_t finite_size_correction; int retval; size_t i, n; n = (size_t) igraph_vector_size(data); finite_size_correction = (n < 50); if (discrete) { /* Does the vector contain discrete values only? */ for (i = 0; i < n; i++) { if ((long int)(VECTOR(*data)[i]) != VECTOR(*data)[i]) { discrete = 0; break; } } } plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); if (discrete) { plfit_discrete_options_init(&disc_options); disc_options.finite_size_correction = (plfit_bool_t) finite_size_correction; if (xmin >= 0) { retval = plfit_estimate_alpha_discrete(VECTOR(*data), n, xmin, &disc_options, &plfit_result); } else { retval = plfit_discrete(VECTOR(*data), n, &disc_options, &plfit_result); } } else { plfit_continuous_options_init(&cont_options); cont_options.finite_size_correction = (plfit_bool_t) finite_size_correction; if (xmin >= 0) { retval = plfit_estimate_alpha_continuous(VECTOR(*data), n, xmin, &cont_options, &plfit_result); } else { retval = plfit_continuous(VECTOR(*data), n, &cont_options, &plfit_result); } } plfit_set_error_handler(plfit_stored_error_handler); switch (retval) { case PLFIT_FAILURE: IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_FAILURE); break; case PLFIT_EINVAL: IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EINVAL); break; case PLFIT_UNDRFLOW: IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EUNDERFLOW); break; case PLFIT_OVERFLOW: IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EOVERFLOW); break; case PLFIT_ENOMEM: IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_ENOMEM); break; default: break; } if (result) { result->continuous = !discrete; result->alpha = plfit_result.alpha; result->xmin = plfit_result.xmin; result->L = plfit_result.L; result->D = plfit_result.D; result->p = plfit_result.p; } return 0; } /** * Internal function, floating point division * Used only in compilers not supporting INFINITY and HUGE_VAL to create * infinity values */ double igraph_i_fdiv(const double a, const double b) { return a / b; } igraph/src/attributes.c0000644000176000001440000002713212325527072014707 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_attributes.h" #include "igraph_memory.h" #include "config.h" #include #include /* Should you ever want to have a thread-local attribute handler table, prepend * IGRAPH_THREAD_LOCAL to the following declaration */ igraph_attribute_table_t *igraph_i_attribute_table=0; int igraph_i_attribute_init(igraph_t *graph, void *attr) { graph->attr=0; if (igraph_i_attribute_table) { return igraph_i_attribute_table->init(graph, attr); } else { return 0; } } void igraph_i_attribute_destroy(igraph_t *graph) { if (igraph_i_attribute_table) { igraph_i_attribute_table->destroy(graph); } } int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->copy(to, from, ga, va, ea); } else { return 0; } } int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->add_vertices(graph, nv, attr); } else { return 0; } } int igraph_i_attribute_permute_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx); } else { return 0; } } int igraph_i_attribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->combine_vertices(graph, newgraph, merges, comb); } else { return 0; } } int igraph_i_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, void *attr) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->add_edges(graph, edges, attr); } else { return 0; } } int igraph_i_attribute_permute_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->permute_edges(graph, newgraph, idx); } else { return 0; } } int igraph_i_attribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->combine_edges(graph, newgraph, merges, comb); } else { return 0; } } int igraph_i_attribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, igraph_vector_t *gtypes, igraph_strvector_t *vnames, igraph_vector_t *vtypes, igraph_strvector_t *enames, igraph_vector_t *etypes) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_info(graph, gnames, gtypes, vnames, vtypes, enames, etypes); } else { return 0; } } igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->has_attr(graph, type, name); } else { return 0; } } int igraph_i_attribute_gettype(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->gettype(graph, type, elemtype, name); } else { return 0; } } int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, const char *name, igraph_vector_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_numeric_graph_attr(graph, name, value); } else { return 0; } } int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_numeric_vertex_attr(graph, name, vs, value); } else { return 0; } } int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_numeric_edge_attr(graph, name, es, value); } else { return 0; } } int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, const char *name, igraph_strvector_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_string_graph_attr(graph, name, value); } else { return 0; } } int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_strvector_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_string_vertex_attr(graph, name, vs, value); } else { return 0; } } int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_strvector_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_string_edge_attr(graph, name, es, value); } else { return 0; } } int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, const char *name, igraph_vector_bool_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_bool_graph_attr(graph, name, value); } else { return 0; } } int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_bool_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_bool_vertex_attr(graph, name, vs, value); } else { return 0; } } int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_bool_edge_attr(graph, name, es, value); } else { return 0; } } /** * \function igraph_i_set_attribute_table * \brief Attach an attribute table. * * This function attaches attribute handling code to the igraph library. * Note that the attribute handler table is \em not thread-local even if * igraph is compiled in thread-local mode. In the vast majority of cases, * this is not a significant restriction. * * \param table Pointer to an \ref igraph_attribute_table_t object * containing the functions for attribute manipulation. Supply \c * NULL here if you don't want attributes. * \return Pointer to the old attribute handling table. * * Time complexity: O(1). */ igraph_attribute_table_t * igraph_i_set_attribute_table(const igraph_attribute_table_t * table) { igraph_attribute_table_t *old=igraph_i_attribute_table; igraph_i_attribute_table=(igraph_attribute_table_t*) table; return old; } igraph_bool_t igraph_has_attribute_table() { return igraph_i_attribute_table != 0; } int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0)); return 0; } void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) { long int i, n=igraph_vector_ptr_size(&comb->list); for (i=0; ilist)[i]; if (rec->name) { igraph_Free(rec->name); } igraph_Free(rec); } igraph_vector_ptr_destroy(&comb->list); } int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t type, void *func) { long int i, n=igraph_vector_ptr_size(&comb->list); /* Search, in case it is already there */ for (i=0; ilist)[i]; const char *n=r->name; if ( (!name && !n) || (name && n && !strcmp(n, name)) ) { r->type=type; r->func=func; break; } } if (i==n) { /* This is a new attribute name */ igraph_attribute_combination_record_t *rec= igraph_Calloc(1, igraph_attribute_combination_record_t); if (!rec) { IGRAPH_ERROR("Cannot create attribute combination data", IGRAPH_ENOMEM); } if (!name) { rec->name=0; } else { rec->name=strdup(name); } rec->type=type; rec->func=func; IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec)); } return 0; } int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, const char *name) { long int i, n=igraph_vector_ptr_size(&comb->list); /* Search, in case it is already there */ for (i=0; ilist)[i]; const char *n=r->name; if ( (!name && !n) || (name && n && !strcmp(n, name)) ) { break; } } if (i!=n) { igraph_attribute_combination_record_t *r=VECTOR(comb->list)[i]; if (r->name) { igraph_Free(r->name); } igraph_Free(r); igraph_vector_ptr_remove(&comb->list, i); } else { /* It is not there, we don't do anything */ } return 0; } int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t *type, void **func) { long int i, def=-1, len=igraph_vector_ptr_size(&comb->list); for (i=0; ilist)[i]; const char *n=rec->name; if ( (!name && !n) || (name && n && !strcmp(n, name)) ) { *type=rec->type; *func=rec->func; return 0; } if (!n) { def=i; } } if (def==-1) { /* Did not find anything */ *type=IGRAPH_ATTRIBUTE_COMBINE_DEFAULT; *func=0; } else { igraph_attribute_combination_record_t *rec=VECTOR(comb->list)[def]; *type=rec->type; *func=rec->func; } return 0; } int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) { va_list ap; IGRAPH_CHECK(igraph_attribute_combination_init(comb)); va_start(ap, comb); while (1) { void *func=0; igraph_attribute_combination_type_t type; const char *name; name=va_arg(ap, const char *); if (name == IGRAPH_NO_MORE_ATTRIBUTES) { break; } type=(igraph_attribute_combination_type_t)va_arg(ap, int); if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) { func=va_arg(ap, void*); } if (strlen(name)==0) { name=0; } IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func)); } va_end(ap); return 0; } igraph/src/unit_limiter.h0000755000176000001440000000021112325527074015224 0ustar ripleyusers#ifndef ZERO_TO_ONE_H #define ZERO_TO_ONE_H namespace igraph { double unit_limiter(double vUnitDouble); } // namespace igraph #endif igraph/src/glpmpl02.c0000644000176000001440000013072212325527073014157 0ustar ripleyusers/* glpmpl02.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #define _GLPSTD_STDIO #include "glpenv.h" #include "glpmpl.h" /**********************************************************************/ /* * * PROCESSING DATA SECTION * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- create_slice - create slice. -- -- This routine creates a slice, which initially has no components. */ SLICE *create_slice(MPL *mpl) { SLICE *slice; xassert(mpl == mpl); slice = NULL; return slice; } /*---------------------------------------------------------------------- -- expand_slice - append new component to slice. -- -- This routine expands slice appending to it either a given symbol or -- null component, which becomes the last component of the slice. */ SLICE *expand_slice ( MPL *mpl, SLICE *slice, /* destroyed */ SYMBOL *sym /* destroyed */ ) { SLICE *tail, *temp; /* create a new component */ tail = dmp_get_atom(mpl->tuples, sizeof(SLICE)); tail->sym = sym; tail->next = NULL; /* and append it to the component list */ if (slice == NULL) slice = tail; else { for (temp = slice; temp->next != NULL; temp = temp->next); temp->next = tail; } return slice; } /*---------------------------------------------------------------------- -- slice_dimen - determine dimension of slice. -- -- This routine returns dimension of slice, which is number of all its -- components including null ones. */ int slice_dimen ( MPL *mpl, SLICE *slice /* not changed */ ) { SLICE *temp; int dim; xassert(mpl == mpl); dim = 0; for (temp = slice; temp != NULL; temp = temp->next) dim++; return dim; } /*---------------------------------------------------------------------- -- slice_arity - determine arity of slice. -- -- This routine returns arity of slice, i.e. number of null components -- (indicated by asterisks) in the slice. */ int slice_arity ( MPL *mpl, SLICE *slice /* not changed */ ) { SLICE *temp; int arity; xassert(mpl == mpl); arity = 0; for (temp = slice; temp != NULL; temp = temp->next) if (temp->sym == NULL) arity++; return arity; } /*---------------------------------------------------------------------- -- fake_slice - create fake slice of all asterisks. -- -- This routine creates a fake slice of given dimension, which contains -- asterisks in all components. Zero dimension is allowed. */ SLICE *fake_slice(MPL *mpl, int dim) { SLICE *slice; slice = create_slice(mpl); while (dim-- > 0) slice = expand_slice(mpl, slice, NULL); return slice; } /*---------------------------------------------------------------------- -- delete_slice - delete slice. -- -- This routine deletes specified slice. */ void delete_slice ( MPL *mpl, SLICE *slice /* destroyed */ ) { SLICE *temp; while (slice != NULL) { temp = slice; slice = temp->next; if (temp->sym != NULL) delete_symbol(mpl, temp->sym); xassert(sizeof(SLICE) == sizeof(TUPLE)); dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE)); } return; } /*---------------------------------------------------------------------- -- is_number - check if current token is number. -- -- If the current token is a number, this routine returns non-zero. -- Otherwise zero is returned. */ int is_number(MPL *mpl) { return mpl->token == T_NUMBER; } /*---------------------------------------------------------------------- -- is_symbol - check if current token is symbol. -- -- If the current token is suitable to be a symbol, the routine returns -- non-zero. Otherwise zero is returned. */ int is_symbol(MPL *mpl) { return mpl->token == T_NUMBER || mpl->token == T_SYMBOL || mpl->token == T_STRING; } /*---------------------------------------------------------------------- -- is_literal - check if current token is given symbolic literal. -- -- If the current token is given symbolic literal, this routine returns -- non-zero. Otherwise zero is returned. -- -- This routine is used on processing the data section in the same way -- as the routine is_keyword on processing the model section. */ int is_literal(MPL *mpl, char *literal) { return is_symbol(mpl) && strcmp(mpl->image, literal) == 0; } /*---------------------------------------------------------------------- -- read_number - read number. -- -- This routine reads the current token, which must be a number, and -- returns its numeric value. */ double read_number(MPL *mpl) { double num; xassert(is_number(mpl)); num = mpl->value; get_token(mpl /* */); return num; } /*---------------------------------------------------------------------- -- read_symbol - read symbol. -- -- This routine reads the current token, which must be a symbol, and -- returns its symbolic value. */ SYMBOL *read_symbol(MPL *mpl) { SYMBOL *sym; xassert(is_symbol(mpl)); if (is_number(mpl)) sym = create_symbol_num(mpl, mpl->value); else sym = create_symbol_str(mpl, create_string(mpl, mpl->image)); get_token(mpl /* */); return sym; } /*---------------------------------------------------------------------- -- read_slice - read slice. -- -- This routine reads slice using the syntax: -- -- ::= [ ] -- ::= ( ) -- ::= -- ::= , -- ::= -- ::= * -- -- The bracketed form of slice is used for members of multi-dimensional -- objects while the parenthesized form is used for elemental sets. */ SLICE *read_slice ( MPL *mpl, char *name, /* not changed */ int dim ) { SLICE *slice; int close; xassert(name != NULL); switch (mpl->token) { case T_LBRACKET: close = T_RBRACKET; break; case T_LEFT: xassert(dim > 0); close = T_RIGHT; break; default: xassert(mpl != mpl); } if (dim == 0) error(mpl, "%s cannot be subscripted", name); get_token(mpl /* ( | [ */); /* read slice components */ slice = create_slice(mpl); for (;;) { /* the current token must be a symbol or asterisk */ if (is_symbol(mpl)) slice = expand_slice(mpl, slice, read_symbol(mpl)); else if (mpl->token == T_ASTERISK) { slice = expand_slice(mpl, slice, NULL); get_token(mpl /* * */); } else error(mpl, "number, symbol, or asterisk missing where expec" "ted"); /* check a token that follows the symbol */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == close) break; else error(mpl, "syntax error in slice"); } /* number of slice components must be the same as the appropriate dimension */ if (slice_dimen(mpl, slice) != dim) { switch (close) { case T_RBRACKET: error(mpl, "%s must have %d subscript%s, not %d", name, dim, dim == 1 ? "" : "s", slice_dimen(mpl, slice)); break; case T_RIGHT: error(mpl, "%s has dimension %d, not %d", name, dim, slice_dimen(mpl, slice)); break; default: xassert(close != close); } } get_token(mpl /* ) | ] */); return slice; } /*---------------------------------------------------------------------- -- select_set - select set to saturate it with elemental sets. -- -- This routine selects set to saturate it with elemental sets provided -- in the data section. */ SET *select_set ( MPL *mpl, char *name /* not changed */ ) { SET *set; AVLNODE *node; xassert(name != NULL); node = avl_find_node(mpl->tree, name); if (node == NULL || avl_get_node_type(node) != A_SET) error(mpl, "%s not a set", name); set = (SET *)avl_get_node_link(node); if (set->assign != NULL || set->gadget != NULL) error(mpl, "%s needs no data", name); set->data = 1; return set; } /*---------------------------------------------------------------------- -- simple_format - read set data block in simple format. -- -- This routine reads set data block using the syntax: -- -- ::= , , ... , -- -- where are used to construct a complete n-tuple, which is -- included in elemental set assigned to the set member. Commae between -- symbols are optional and may be omitted anywhere. -- -- Number of components in the slice must be the same as dimension of -- n-tuples in elemental sets assigned to the set members. To construct -- complete n-tuple the routine replaces null positions in the slice by -- corresponding . -- -- If the slice contains at least one null position, the current token -- must be symbol. Otherwise, the routine reads no symbols to construct -- the n-tuple, so the current token is not checked. */ void simple_format ( MPL *mpl, SET *set, /* not changed */ MEMBER *memb, /* modified */ SLICE *slice /* not changed */ ) { TUPLE *tuple; SLICE *temp; SYMBOL *sym, *with = NULL; xassert(set != NULL); xassert(memb != NULL); xassert(slice != NULL); xassert(set->dimen == slice_dimen(mpl, slice)); xassert(memb->value.set->dim == set->dimen); if (slice_arity(mpl, slice) > 0) xassert(is_symbol(mpl)); /* read symbols and construct complete n-tuple */ tuple = create_tuple(mpl); for (temp = slice; temp != NULL; temp = temp->next) { if (temp->sym == NULL) { /* substitution is needed; read symbol */ if (!is_symbol(mpl)) { int lack = slice_arity(mpl, temp); /* with cannot be null due to assertion above */ xassert(with != NULL); if (lack == 1) error(mpl, "one item missing in data group beginning " "with %s", format_symbol(mpl, with)); else error(mpl, "%d items missing in data group beginning " "with %s", lack, format_symbol(mpl, with)); } sym = read_symbol(mpl); if (with == NULL) with = sym; } else { /* copy symbol from the slice */ sym = copy_symbol(mpl, temp->sym); } /* append the symbol to the n-tuple */ tuple = expand_tuple(mpl, tuple, sym); /* skip optional comma *between* */ if (temp->next != NULL && mpl->token == T_COMMA) get_token(mpl /* , */); } /* add constructed n-tuple to elemental set */ check_then_add(mpl, memb->value.set, tuple); return; } /*---------------------------------------------------------------------- -- matrix_format - read set data block in matrix format. -- -- This routine reads set data block using the syntax: -- -- ::= ... := -- +/- +/- ... +/- -- +/- +/- ... +/- -- . . . . . . . . . . . -- +/- +/- ... +/- -- -- where are symbols that denote rows of the matrix, -- are symbols that denote columns of the matrix, "+" and "-" indicate -- whether corresponding n-tuple needs to be included in the elemental -- set or not, respectively. -- -- Number of the slice components must be the same as dimension of the -- elemental set. The slice must have two null positions. To construct -- complete n-tuple for particular element of the matrix the routine -- replaces first null position of the slice by the corresponding -- (or , if the flag tr is on) and second null position by the -- corresponding (or by , if the flag tr is on). */ void matrix_format ( MPL *mpl, SET *set, /* not changed */ MEMBER *memb, /* modified */ SLICE *slice, /* not changed */ int tr ) { SLICE *list, *col, *temp; TUPLE *tuple; SYMBOL *row; xassert(set != NULL); xassert(memb != NULL); xassert(slice != NULL); xassert(set->dimen == slice_dimen(mpl, slice)); xassert(memb->value.set->dim == set->dimen); xassert(slice_arity(mpl, slice) == 2); /* read the matrix heading that contains column symbols (there may be no columns at all) */ list = create_slice(mpl); while (mpl->token != T_ASSIGN) { /* read column symbol and append it to the column list */ if (!is_symbol(mpl)) error(mpl, "number, symbol, or := missing where expected"); list = expand_slice(mpl, list, read_symbol(mpl)); } get_token(mpl /* := */); /* read zero or more rows that contain matrix data */ while (is_symbol(mpl)) { /* read row symbol (if the matrix has no columns, row symbols are just ignored) */ row = read_symbol(mpl); /* read the matrix row accordingly to the column list */ for (col = list; col != NULL; col = col->next) { int which = 0; /* check indicator */ if (is_literal(mpl, "+")) ; else if (is_literal(mpl, "-")) { get_token(mpl /* - */); continue; } else { int lack = slice_dimen(mpl, col); if (lack == 1) error(mpl, "one item missing in data group beginning " "with %s", format_symbol(mpl, row)); else error(mpl, "%d items missing in data group beginning " "with %s", lack, format_symbol(mpl, row)); } /* construct complete n-tuple */ tuple = create_tuple(mpl); for (temp = slice; temp != NULL; temp = temp->next) { if (temp->sym == NULL) { /* substitution is needed */ switch (++which) { case 1: /* substitute in the first null position */ tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, tr ? col->sym : row)); break; case 2: /* substitute in the second null position */ tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, tr ? row : col->sym)); break; default: xassert(which != which); } } else { /* copy symbol from the slice */ tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, temp->sym)); } } xassert(which == 2); /* add constructed n-tuple to elemental set */ check_then_add(mpl, memb->value.set, tuple); get_token(mpl /* + */); } /* delete the row symbol */ delete_symbol(mpl, row); } /* delete the column list */ delete_slice(mpl, list); return; } /*---------------------------------------------------------------------- -- set_data - read set data. -- -- This routine reads set data using the syntax: -- -- ::= set ; -- ::= set [ ] ; -- ::= -- ::= -- ::= , := -- ::= , ( ) -- ::= , -- ::= , : -- ::= , (tr) -- ::= , (tr) : -- -- Commae in are optional and may be omitted anywhere. */ void set_data(MPL *mpl) { SET *set; TUPLE *tuple; MEMBER *memb; SLICE *slice; int tr = 0; xassert(is_literal(mpl, "set")); get_token(mpl /* set */); /* symbolic name of set must follows the keyword 'set' */ if (!is_symbol(mpl)) error(mpl, "set name missing where expected"); /* select the set to saturate it with data */ set = select_set(mpl, mpl->image); get_token(mpl /* */); /* read optional subscript list, which identifies member of the set to be read */ tuple = create_tuple(mpl); if (mpl->token == T_LBRACKET) { /* subscript list is specified */ if (set->dim == 0) error(mpl, "%s cannot be subscripted", set->name); get_token(mpl /* [ */); /* read symbols and construct subscript list */ for (;;) { if (!is_symbol(mpl)) error(mpl, "number or symbol missing where expected"); tuple = expand_tuple(mpl, tuple, read_symbol(mpl)); if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_RBRACKET) break; else error(mpl, "syntax error in subscript list"); } if (set->dim != tuple_dimen(mpl, tuple)) error(mpl, "%s must have %d subscript%s rather than %d", set->name, set->dim, set->dim == 1 ? "" : "s", tuple_dimen(mpl, tuple)); get_token(mpl /* ] */); } else { /* subscript list is not specified */ if (set->dim != 0) error(mpl, "%s must be subscripted", set->name); } /* there must be no member with the same subscript list */ if (find_member(mpl, set->array, tuple) != NULL) error(mpl, "%s%s already defined", set->name, format_tuple(mpl, '[', tuple)); /* add new member to the set and assign it empty elemental set */ memb = add_member(mpl, set->array, tuple); memb->value.set = create_elemset(mpl, set->dimen); /* create an initial fake slice of all asterisks */ slice = fake_slice(mpl, set->dimen); /* read zero or more data assignments */ for (;;) { /* skip optional comma */ if (mpl->token == T_COMMA) get_token(mpl /* , */); /* process assignment element */ if (mpl->token == T_ASSIGN) { /* assignment ligature is non-significant element */ get_token(mpl /* := */); } else if (mpl->token == T_LEFT) { /* left parenthesis begins either new slice or "transpose" indicator */ int is_tr; get_token(mpl /* ( */); is_tr = is_literal(mpl, "tr"); unget_token(mpl /* ( */); if (is_tr) goto left; /* delete the current slice and read new one */ delete_slice(mpl, slice); slice = read_slice(mpl, set->name, set->dimen); /* each new slice resets the "transpose" indicator */ tr = 0; /* if the new slice is 0-ary, formally there is one 0-tuple (in the simple format) that follows it */ if (slice_arity(mpl, slice) == 0) simple_format(mpl, set, memb, slice); } else if (is_symbol(mpl)) { /* number or symbol begins data in the simple format */ simple_format(mpl, set, memb, slice); } else if (mpl->token == T_COLON) { /* colon begins data in the matrix format */ if (slice_arity(mpl, slice) != 2) err1: error(mpl, "slice currently used must specify 2 asterisk" "s, not %d", slice_arity(mpl, slice)); get_token(mpl /* : */); /* read elemental set data in the matrix format */ matrix_format(mpl, set, memb, slice, tr); } else if (mpl->token == T_LEFT) left: { /* left parenthesis begins the "transpose" indicator, which is followed by data in the matrix format */ get_token(mpl /* ( */); if (!is_literal(mpl, "tr")) err2: error(mpl, "transpose indicator (tr) incomplete"); if (slice_arity(mpl, slice) != 2) goto err1; get_token(mpl /* tr */); if (mpl->token != T_RIGHT) goto err2; get_token(mpl /* ) */); /* in this case the colon is optional */ if (mpl->token == T_COLON) get_token(mpl /* : */); /* set the "transpose" indicator */ tr = 1; /* read elemental set data in the matrix format */ matrix_format(mpl, set, memb, slice, tr); } else if (mpl->token == T_SEMICOLON) { /* semicolon terminates the data block */ get_token(mpl /* ; */); break; } else error(mpl, "syntax error in set data block"); } /* delete the current slice */ delete_slice(mpl, slice); return; } /*---------------------------------------------------------------------- -- select_parameter - select parameter to saturate it with data. -- -- This routine selects parameter to saturate it with data provided in -- the data section. */ PARAMETER *select_parameter ( MPL *mpl, char *name /* not changed */ ) { PARAMETER *par; AVLNODE *node; xassert(name != NULL); node = avl_find_node(mpl->tree, name); if (node == NULL || avl_get_node_type(node) != A_PARAMETER) error(mpl, "%s not a parameter", name); par = (PARAMETER *)avl_get_node_link(node); if (par->assign != NULL) error(mpl, "%s needs no data", name); if (par->data) error(mpl, "%s already provided with data", name); par->data = 1; return par; } /*---------------------------------------------------------------------- -- set_default - set default parameter value. -- -- This routine sets default value for specified parameter. */ void set_default ( MPL *mpl, PARAMETER *par, /* not changed */ SYMBOL *altval /* destroyed */ ) { xassert(par != NULL); xassert(altval != NULL); if (par->option != NULL) error(mpl, "default value for %s already specified in model se" "ction", par->name); xassert(par->defval == NULL); par->defval = altval; return; } /*---------------------------------------------------------------------- -- read_value - read value and assign it to parameter member. -- -- This routine reads numeric or symbolic value from the input stream -- and assigns to new parameter member specified by its n-tuple, which -- (the member) is created and added to the parameter array. */ MEMBER *read_value ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* destroyed */ ) { MEMBER *memb; xassert(par != NULL); xassert(is_symbol(mpl)); /* there must be no member with the same n-tuple */ if (find_member(mpl, par->array, tuple) != NULL) error(mpl, "%s%s already defined", par->name, format_tuple(mpl, '[', tuple)); /* create new parameter member with given n-tuple */ memb = add_member(mpl, par->array, tuple); /* read value and assigns it to the new parameter member */ switch (par->type) { case A_NUMERIC: case A_INTEGER: case A_BINARY: if (!is_number(mpl)) error(mpl, "%s requires numeric data", par->name); memb->value.num = read_number(mpl); break; case A_SYMBOLIC: memb->value.sym = read_symbol(mpl); break; default: xassert(par != par); } return memb; } /*---------------------------------------------------------------------- -- plain_format - read parameter data block in plain format. -- -- This routine reads parameter data block using the syntax: -- -- ::= , , ... , , -- -- where are used to determine a complete subscript list for -- parameter member, is a numeric or symbolic value assigned to -- the parameter member. Commae between data items are optional and may -- be omitted anywhere. -- -- Number of components in the slice must be the same as dimension of -- the parameter. To construct the complete subscript list the routine -- replaces null positions in the slice by corresponding . */ void plain_format ( MPL *mpl, PARAMETER *par, /* not changed */ SLICE *slice /* not changed */ ) { TUPLE *tuple; SLICE *temp; SYMBOL *sym, *with = NULL; xassert(par != NULL); xassert(par->dim == slice_dimen(mpl, slice)); xassert(is_symbol(mpl)); /* read symbols and construct complete subscript list */ tuple = create_tuple(mpl); for (temp = slice; temp != NULL; temp = temp->next) { if (temp->sym == NULL) { /* substitution is needed; read symbol */ if (!is_symbol(mpl)) { int lack = slice_arity(mpl, temp) + 1; xassert(with != NULL); xassert(lack > 1); error(mpl, "%d items missing in data group beginning wit" "h %s", lack, format_symbol(mpl, with)); } sym = read_symbol(mpl); if (with == NULL) with = sym; } else { /* copy symbol from the slice */ sym = copy_symbol(mpl, temp->sym); } /* append the symbol to the subscript list */ tuple = expand_tuple(mpl, tuple, sym); /* skip optional comma */ if (mpl->token == T_COMMA) get_token(mpl /* , */); } /* read value and assign it to new parameter member */ if (!is_symbol(mpl)) { xassert(with != NULL); error(mpl, "one item missing in data group beginning with %s", format_symbol(mpl, with)); } read_value(mpl, par, tuple); return; } /*---------------------------------------------------------------------- -- tabular_format - read parameter data block in tabular format. -- -- This routine reads parameter data block using the syntax: -- -- ::= ... := -- ... -- ... -- . . . . . . . . . . . -- ... -- -- where are symbols that denote rows of the table, -- are symbols that denote columns of the table, are numeric -- or symbolic values assigned to the corresponding parameter members. -- If is specified as single point, no value is provided. -- -- Number of components in the slice must be the same as dimension of -- the parameter. The slice must have two null positions. To construct -- complete subscript list for particular the routine replaces -- the first null position of the slice by the corresponding (or -- , if the flag tr is on) and the second null position by the -- corresponding (or by , if the flag tr is on). */ void tabular_format ( MPL *mpl, PARAMETER *par, /* not changed */ SLICE *slice, /* not changed */ int tr ) { SLICE *list, *col, *temp; TUPLE *tuple; SYMBOL *row; xassert(par != NULL); xassert(par->dim == slice_dimen(mpl, slice)); xassert(slice_arity(mpl, slice) == 2); /* read the table heading that contains column symbols (the table may have no columns) */ list = create_slice(mpl); while (mpl->token != T_ASSIGN) { /* read column symbol and append it to the column list */ if (!is_symbol(mpl)) error(mpl, "number, symbol, or := missing where expected"); list = expand_slice(mpl, list, read_symbol(mpl)); } get_token(mpl /* := */); /* read zero or more rows that contain tabular data */ while (is_symbol(mpl)) { /* read row symbol (if the table has no columns, these symbols are just ignored) */ row = read_symbol(mpl); /* read values accordingly to the column list */ for (col = list; col != NULL; col = col->next) { int which = 0; /* if the token is single point, no value is provided */ if (is_literal(mpl, ".")) { get_token(mpl /* . */); continue; } /* construct complete subscript list */ tuple = create_tuple(mpl); for (temp = slice; temp != NULL; temp = temp->next) { if (temp->sym == NULL) { /* substitution is needed */ switch (++which) { case 1: /* substitute in the first null position */ tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, tr ? col->sym : row)); break; case 2: /* substitute in the second null position */ tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, tr ? row : col->sym)); break; default: xassert(which != which); } } else { /* copy symbol from the slice */ tuple = expand_tuple(mpl, tuple, copy_symbol(mpl, temp->sym)); } } xassert(which == 2); /* read value and assign it to new parameter member */ if (!is_symbol(mpl)) { int lack = slice_dimen(mpl, col); if (lack == 1) error(mpl, "one item missing in data group beginning " "with %s", format_symbol(mpl, row)); else error(mpl, "%d items missing in data group beginning " "with %s", lack, format_symbol(mpl, row)); } read_value(mpl, par, tuple); } /* delete the row symbol */ delete_symbol(mpl, row); } /* delete the column list */ delete_slice(mpl, list); return; } /*---------------------------------------------------------------------- -- tabbing_format - read parameter data block in tabbing format. -- -- This routine reads parameter data block using the syntax: -- -- ::= , ... , , := , -- , ... , , , ... , , -- , ... , , , ... , , -- . . . . . . . . . . . . . . . . . -- , ... , , , ... , -- ::= -- ::= : -- -- where are names of parameters (all the parameters must be -- subscripted and have identical dimensions), are symbols -- used to define subscripts of parameter members, are numeric -- or symbolic values assigned to the corresponding parameter members. -- Optional may specify a simple set, in which case n-tuples -- built of for each row of the data table (i.e. subscripts -- of parameter members) are added to the specified set. Commae between -- data items are optional and may be omitted anywhere. -- -- If the parameter altval is not NULL, it specifies a default value -- provided for all the parameters specified in the data block. */ void tabbing_format ( MPL *mpl, SYMBOL *altval /* not changed */ ) { SET *set = NULL; PARAMETER *par; SLICE *list, *col; TUPLE *tuple; int next_token, j, dim = 0; char *last_name = NULL; /* read the optional */ if (is_symbol(mpl)) { get_token(mpl /* */); next_token = mpl->token; unget_token(mpl /* */); if (next_token == T_COLON) { /* select the set to saturate it with data */ set = select_set(mpl, mpl->image); /* the set must be simple (i.e. not set of sets) */ if (set->dim != 0) error(mpl, "%s must be a simple set", set->name); /* and must not be defined yet */ if (set->array->head != NULL) error(mpl, "%s already defined", set->name); /* add new (the only) member to the set and assign it empty elemental set */ add_member(mpl, set->array, NULL)->value.set = create_elemset(mpl, set->dimen); last_name = set->name, dim = set->dimen; get_token(mpl /* */); xassert(mpl->token == T_COLON); get_token(mpl /* : */); } } /* read the table heading that contains parameter names */ list = create_slice(mpl); while (mpl->token != T_ASSIGN) { /* there must be symbolic name of parameter */ if (!is_symbol(mpl)) error(mpl, "parameter name or := missing where expected"); /* select the parameter to saturate it with data */ par = select_parameter(mpl, mpl->image); /* the parameter must be subscripted */ if (par->dim == 0) error(mpl, "%s not a subscripted parameter", mpl->image); /* the set (if specified) and all the parameters in the data block must have identical dimension */ if (dim != 0 && par->dim != dim) { xassert(last_name != NULL); error(mpl, "%s has dimension %d while %s has dimension %d", last_name, dim, par->name, par->dim); } /* set default value for the parameter (if specified) */ if (altval != NULL) set_default(mpl, par, copy_symbol(mpl, altval)); /* append the parameter to the column list */ list = expand_slice(mpl, list, (SYMBOL *)par); last_name = par->name, dim = par->dim; get_token(mpl /* */); /* skip optional comma */ if (mpl->token == T_COMMA) get_token(mpl /* , */); } if (slice_dimen(mpl, list) == 0) error(mpl, "at least one parameter name required"); get_token(mpl /* := */); /* skip optional comma */ if (mpl->token == T_COMMA) get_token(mpl /* , */); /* read rows that contain tabbing data */ while (is_symbol(mpl)) { /* read subscript list */ tuple = create_tuple(mpl); for (j = 1; j <= dim; j++) { /* read j-th subscript */ if (!is_symbol(mpl)) { int lack = slice_dimen(mpl, list) + dim - j + 1; xassert(tuple != NULL); xassert(lack > 1); error(mpl, "%d items missing in data group beginning wit" "h %s", lack, format_symbol(mpl, tuple->sym)); } /* read and append j-th subscript to the n-tuple */ tuple = expand_tuple(mpl, tuple, read_symbol(mpl)); /* skip optional comma *between* */ if (j < dim && mpl->token == T_COMMA) get_token(mpl /* , */); } /* if the set is specified, add to it new n-tuple, which is a copy of the subscript list just read */ if (set != NULL) check_then_add(mpl, set->array->head->value.set, copy_tuple(mpl, tuple)); /* skip optional comma between and */ if (mpl->token == T_COMMA) get_token(mpl /* , */); /* read values accordingly to the column list */ for (col = list; col != NULL; col = col->next) { /* if the token is single point, no value is provided */ if (is_literal(mpl, ".")) { get_token(mpl /* . */); continue; } /* read value and assign it to new parameter member */ if (!is_symbol(mpl)) { int lack = slice_dimen(mpl, col); xassert(tuple != NULL); if (lack == 1) error(mpl, "one item missing in data group beginning " "with %s", format_symbol(mpl, tuple->sym)); else error(mpl, "%d items missing in data group beginning " "with %s", lack, format_symbol(mpl, tuple->sym)); } read_value(mpl, (PARAMETER *)col->sym, copy_tuple(mpl, tuple)); /* skip optional comma preceding the next value */ if (col->next != NULL && mpl->token == T_COMMA) get_token(mpl /* , */); } /* delete the original subscript list */ delete_tuple(mpl, tuple); /* skip optional comma (only if there is next data group) */ if (mpl->token == T_COMMA) { get_token(mpl /* , */); if (!is_symbol(mpl)) unget_token(mpl /* , */); } } /* delete the column list (it contains parameters, not symbols, so nullify it before) */ for (col = list; col != NULL; col = col->next) col->sym = NULL; delete_slice(mpl, list); return; } /*---------------------------------------------------------------------- -- parameter_data - read parameter data. -- -- This routine reads parameter data using the syntax: -- -- ::= param : ; -- ::= param -- ; -- ::= -- ::= -- ::= default -- ::= -- ::= , := -- ::= , [ ] -- ::= , -- ::= , : -- ::= , (tr) -- ::= , (tr) : -- -- Commae in are optional and may be omitted anywhere. */ void parameter_data(MPL *mpl) { PARAMETER *par; SYMBOL *altval = NULL; SLICE *slice; int tr = 0; xassert(is_literal(mpl, "param")); get_token(mpl /* param */); /* read optional default value */ if (is_literal(mpl, "default")) { get_token(mpl /* default */); if (!is_symbol(mpl)) error(mpl, "default value missing where expected"); altval = read_symbol(mpl); /* if the default value follows the keyword 'param', the next token must be only the colon */ if (mpl->token != T_COLON) error(mpl, "colon missing where expected"); } /* being used after the keyword 'param' or the optional default value the colon begins data in the tabbing format */ if (mpl->token == T_COLON) { get_token(mpl /* : */); /* skip optional comma */ if (mpl->token == T_COMMA) get_token(mpl /* , */); /* read parameter data in the tabbing format */ tabbing_format(mpl, altval); /* on reading data in the tabbing format the default value is always copied, so delete the original symbol */ if (altval != NULL) delete_symbol(mpl, altval); /* the next token must be only semicolon */ if (mpl->token != T_SEMICOLON) error(mpl, "symbol, number, or semicolon missing where expe" "cted"); get_token(mpl /* ; */); goto done; } /* in other cases there must be symbolic name of parameter, which follows the keyword 'param' */ if (!is_symbol(mpl)) error(mpl, "parameter name missing where expected"); /* select the parameter to saturate it with data */ par = select_parameter(mpl, mpl->image); get_token(mpl /* */); /* read optional default value */ if (is_literal(mpl, "default")) { get_token(mpl /* default */); if (!is_symbol(mpl)) error(mpl, "default value missing where expected"); altval = read_symbol(mpl); /* set default value for the parameter */ set_default(mpl, par, altval); } /* create initial fake slice of all asterisks */ slice = fake_slice(mpl, par->dim); /* read zero or more data assignments */ for (;;) { /* skip optional comma */ if (mpl->token == T_COMMA) get_token(mpl /* , */); /* process current assignment */ if (mpl->token == T_ASSIGN) { /* assignment ligature is non-significant element */ get_token(mpl /* := */); } else if (mpl->token == T_LBRACKET) { /* left bracket begins new slice; delete the current slice and read new one */ delete_slice(mpl, slice); slice = read_slice(mpl, par->name, par->dim); /* each new slice resets the "transpose" indicator */ tr = 0; } else if (is_symbol(mpl)) { /* number or symbol begins data in the plain format */ plain_format(mpl, par, slice); } else if (mpl->token == T_COLON) { /* colon begins data in the tabular format */ if (par->dim == 0) err1: error(mpl, "%s not a subscripted parameter", par->name); if (slice_arity(mpl, slice) != 2) err2: error(mpl, "slice currently used must specify 2 asterisk" "s, not %d", slice_arity(mpl, slice)); get_token(mpl /* : */); /* read parameter data in the tabular format */ tabular_format(mpl, par, slice, tr); } else if (mpl->token == T_LEFT) { /* left parenthesis begins the "transpose" indicator, which is followed by data in the tabular format */ get_token(mpl /* ( */); if (!is_literal(mpl, "tr")) err3: error(mpl, "transpose indicator (tr) incomplete"); if (par->dim == 0) goto err1; if (slice_arity(mpl, slice) != 2) goto err2; get_token(mpl /* tr */); if (mpl->token != T_RIGHT) goto err3; get_token(mpl /* ) */); /* in this case the colon is optional */ if (mpl->token == T_COLON) get_token(mpl /* : */); /* set the "transpose" indicator */ tr = 1; /* read parameter data in the tabular format */ tabular_format(mpl, par, slice, tr); } else if (mpl->token == T_SEMICOLON) { /* semicolon terminates the data block */ get_token(mpl /* ; */); break; } else error(mpl, "syntax error in parameter data block"); } /* delete the current slice */ delete_slice(mpl, slice); done: return; } /*---------------------------------------------------------------------- -- data_section - read data section. -- -- This routine reads data section using the syntax: -- -- ::= -- ::= ; -- ::= -- ::= -- -- Reading data section is terminated by either the keyword 'end' or -- the end of file. */ void data_section(MPL *mpl) { while (!(mpl->token == T_EOF || is_literal(mpl, "end"))) { if (is_literal(mpl, "set")) set_data(mpl); else if (is_literal(mpl, "param")) parameter_data(mpl); else error(mpl, "syntax error in data section"); } return; } /* eof */ igraph/src/dstatn.f0000644000176000001440000000272412325527073014022 0ustar ripleyusersc c %---------------------------------------------% c | Initialize statistic and timing information | c | for nonsymmetric Arnoldi code. | c %---------------------------------------------% c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\SCCS Information: @(#) c FILE: statn.F SID: 2.4 DATE OF SID: 4/20/96 RELEASE: 2 c subroutine igraphdstatn c c %--------------------------------% c | See stat.doc for documentation | c %--------------------------------% c include 'stat.h' c c %-----------------------% c | Executable Statements | c %-----------------------% c nopx = 0 nbx = 0 nrorth = 0 nitref = 0 nrstrt = 0 c tnaupd = 0.0D+0 tnaup2 = 0.0D+0 tnaitr = 0.0D+0 tneigh = 0.0D+0 tngets = 0.0D+0 tnapps = 0.0D+0 tnconv = 0.0D+0 titref = 0.0D+0 tgetv0 = 0.0D+0 trvec = 0.0D+0 c c %----------------------------------------------------% c | User time including reverse communication overhead | c %----------------------------------------------------% c tmvopx = 0.0D+0 tmvbx = 0.0D+0 c return c c c %---------------% c | End of igraphdstatn | c %---------------% c end igraph/src/heap.pmt0000644000176000001440000002171212325372072014010 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include #define PARENT(x) (((x)+1)/2-1) #define LEFTCHILD(x) (((x)+1)*2-1) #define RIGHTCHILD(x) (((x)+1)*2) /** * \ingroup heap * \function igraph_heap_init * \brief Initializes an empty heap object. * * Creates an empty heap, but allocates size for some elements. * \param h Pointer to an uninitialized heap object. * \param alloc_size Number of elements to allocate memory for. * \return Error code. * * Time complexity: O(\p alloc_size), assuming memory allocation is a * linear operation. */ int FUNCTION(igraph_heap,init)(TYPE(igraph_heap)* h, long int alloc_size) { if (alloc_size <= 0 ) { alloc_size=1; } h->stor_begin=igraph_Calloc(alloc_size, BASE); if (h->stor_begin==0) { IGRAPH_ERROR("heap init failed", IGRAPH_ENOMEM); } h->stor_end=h->stor_begin + alloc_size; h->end=h->stor_begin; h->destroy=1; return 0; } /** * \ingroup heap * \function igraph_heap_init_array * \brief Build a heap from an array. * * Initializes a heap object from an array, the heap is also * built of course (constructor). * \param h Pointer to an uninitialized heap object. * \param data Pointer to an array of base data type. * \param len The length of the array at \p data. * \return Error code. * * Time complexity: O(n), the number of elements in the heap. */ int FUNCTION(igraph_heap,init_array)(TYPE(igraph_heap) *h, BASE* data, long int len) { h->stor_begin=igraph_Calloc(len, BASE); if (h->stor_begin==0) { IGRAPH_ERROR("heap init from array failed", IGRAPH_ENOMEM); } h->stor_end=h->stor_begin+len; h->end=h->stor_end; h->destroy=1; memcpy(h->stor_begin, data, (size_t) len*sizeof(igraph_real_t)); FUNCTION(igraph_heap,i_build) (h->stor_begin, h->end-h->stor_begin, 0); return 0; } /** * \ingroup heap * \function igraph_heap_destroy * \brief Destroys an initialized heap object. * * \param h The heap object. * * Time complexity: O(1). */ void FUNCTION(igraph_heap,destroy)(TYPE(igraph_heap)* h) { if (h->destroy) { if (h->stor_begin != 0) { igraph_Free(h->stor_begin); h->stor_begin=0; } } } /** * \ingroup heap * \function igraph_heap_empty * \brief Decides whether a heap object is empty. * * \param h The heap object. * \return \c TRUE if the heap is empty, \c FALSE otherwise. * * TIme complexity: O(1). */ igraph_bool_t FUNCTION(igraph_heap,empty)(TYPE(igraph_heap)* h) { assert(h != NULL); assert(h->stor_begin != NULL); return h->stor_begin == h->end; } /** * \ingroup heap * \function igraph_heap_push * \brief Add an element. * * Adds an element to the heap. * \param h The heap object. * \param elem The element to add. * \return Error code. * * Time complexity: O(log n), n is the number of elements in the * heap if no reallocation is needed, O(n) otherwise. It is ensured * that n push operations are performed in O(n log n) time. */ int FUNCTION(igraph_heap,push)(TYPE(igraph_heap)* h, BASE elem) { assert(h != NULL); assert(h->stor_begin != NULL); /* full, allocate more storage */ if (h->stor_end == h->end) { long int new_size = FUNCTION(igraph_heap,size)(h) * 2; if (new_size == 0) { new_size = 1; } IGRAPH_CHECK(FUNCTION(igraph_heap,reserve)(h, new_size)); } *(h->end) = elem; h->end += 1; /* maintain heap */ FUNCTION(igraph_heap,i_shift_up)(h->stor_begin, FUNCTION(igraph_heap,size)(h), FUNCTION(igraph_heap,size)(h)-1); return 0; } /** * \ingroup heap * \function igraph_heap_top * \brief Top element. * * For maximum heaps this is the largest, for minimum heaps the * smallest element of the heap. * \param h The heap object. * \return The top element. * * Time complexity: O(1). */ BASE FUNCTION(igraph_heap,top)(TYPE(igraph_heap)* h) { assert(h != NULL); assert(h->stor_begin != NULL); assert(h->stor_begin != h->end); return h->stor_begin[0]; } /** * \ingroup heap * \function igraph_heap_delete_top * \brief Return and removes the top element * * Removes and returns the top element of the heap. For maximum heaps * this is the largest, for minimum heaps the smallest element. * \param h The heap object. * \return The top element. * * Time complexity: O(log n), n is the number of elements in the * heap. */ BASE FUNCTION(igraph_heap,delete_top)(TYPE(igraph_heap)* h) { BASE tmp; assert(h != NULL); assert(h->stor_begin != NULL); tmp=h->stor_begin[0]; FUNCTION(igraph_heap,i_switch)(h->stor_begin, 0, FUNCTION(igraph_heap,size)(h)-1); h->end -= 1; FUNCTION(igraph_heap,i_sink)(h->stor_begin, h->end-h->stor_begin, 0); return tmp; } /** * \ingroup heap * \function igraph_heap_size * \brief Number of elements * * Gives the number of elements in a heap. * \param h The heap object. * \return The number of elements in the heap. * * Time complexity: O(1). */ long int FUNCTION(igraph_heap,size)(TYPE(igraph_heap)* h) { assert(h != NULL); assert(h->stor_begin != NULL); return h->end - h->stor_begin; } /** * \ingroup heap * \function igraph_heap_reserve * \brief Allocate more memory * * Allocates memory for future use. The size of the heap is * unchanged. If the heap is larger than the \p size parameter then * nothing happens. * \param h The heap object. * \param size The number of elements to allocate memory for. * \return Error code. * * Time complexity: O(\p size) if \p size is larger than the current * number of elements. O(1) otherwise. */ int FUNCTION(igraph_heap,reserve)(TYPE(igraph_heap)* h, long int size) { long int actual_size=FUNCTION(igraph_heap,size)(h); BASE *tmp; assert(h != NULL); assert(h->stor_begin != NULL); if (size <= actual_size) { return 0; } tmp=igraph_Realloc(h->stor_begin, (size_t) size, BASE); if (tmp==0) { IGRAPH_ERROR("heap reserve failed", IGRAPH_ENOMEM); } h->stor_begin=tmp; h->stor_end=h->stor_begin + size; h->end=h->stor_begin+actual_size; return 0; } /** * \ingroup heap * \brief Build a heap, this should not be called directly. */ void FUNCTION(igraph_heap,i_build)(BASE* arr, long int size, long int head) { if (RIGHTCHILD(head) < size) { /* both subtrees */ FUNCTION(igraph_heap,i_build)(arr, size, LEFTCHILD(head) ); FUNCTION(igraph_heap,i_build)(arr, size, RIGHTCHILD(head)); FUNCTION(igraph_heap,i_sink)(arr, size, head); } else if (LEFTCHILD(head) < size) { /* only left */ FUNCTION(igraph_heap,i_build)(arr, size, LEFTCHILD(head)); FUNCTION(igraph_heap,i_sink)(arr, size, head); } else { /* none */ } } /** * \ingroup heap * \brief Shift an element upwards in a heap, this should not be * called directly. */ void FUNCTION(igraph_heap,i_shift_up)(BASE* arr, long int size, long int elem) { if (elem==0 || arr[elem] HEAPLESS arr[PARENT(elem)]) { /* at the top */ } else { FUNCTION(igraph_heap,i_switch)(arr, elem, PARENT(elem)); FUNCTION(igraph_heap,i_shift_up)(arr, size, PARENT(elem)); } } /** * \ingroup heap * \brief Moves an element down in a heap, this function should not be * called directly. */ void FUNCTION(igraph_heap,i_sink)(BASE* arr, long int size, long int head) { if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || arr[LEFTCHILD(head)] HEAPMOREEQ arr[RIGHTCHILD(head)]) { /* sink to the left if needed */ if (arr[head] HEAPLESS arr[LEFTCHILD(head)]) { FUNCTION(igraph_heap,i_switch)(arr, head, LEFTCHILD(head)); FUNCTION(igraph_heap,i_sink)(arr, size, LEFTCHILD(head)); } } else { /* sink to the right */ if (arr[head] HEAPLESS arr[RIGHTCHILD(head)]) { FUNCTION(igraph_heap,i_switch)(arr, head, RIGHTCHILD(head)); FUNCTION(igraph_heap,i_sink)(arr, size, RIGHTCHILD(head)); } } } /** * \ingroup heap * \brief Switches two elements in a heap, this function should not be * called directly. */ void FUNCTION(igraph_heap,i_switch)(BASE* arr, long int e1, long int e2) { if (e1!=e2) { BASE tmp=arr[e1]; arr[e1]=arr[e2]; arr[e2]=tmp; } } igraph/src/foreign-pajek-header.h0000644000176000001440000000255212325527073016475 0ustar ripleyusers/* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge MA, 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_vector.h" #include "igraph_types_internal.h" typedef struct { void *scanner; int eof; char errmsg[300]; igraph_vector_t *vector; igraph_bool_t directed; int vcount, vcount2; int actfrom; int actto; int mode; /* 0: general, 1: vertex, 2: edge */ igraph_trie_t *vertex_attribute_names; igraph_vector_ptr_t *vertex_attributes; igraph_trie_t *edge_attribute_names; igraph_vector_ptr_t *edge_attributes; int vertexid; int actvertex; int actedge; } igraph_i_pajek_parsedata_t; igraph/src/igraph_version.h0000644000176000001440000000207712325527073015547 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_VERSION_H #define IGRAPH_VERSION_H #define IGRAPH_VERSION "0.7.1" int igraph_version(const char **version_string, int *major, int *minor, int *subminor); #endif igraph/src/igraph_lapack_internal.h0000644000176000001440000001333312325527073017206 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LAPACK_INTERNAL_H #define LAPACK_INTERNAL_H /* Note: only files calling the LAPACK routines directly need to include this header. */ #include "igraph_types.h" #include "config.h" #ifndef INTERNAL_LAPACK #define igraphdgeevx_ dgeevx_ #define igraphdgeev_ dgeev_ #define igraphdgebak_ dgebak_ #define igraphxerbla_ xerbla_ #define igraphdgebal_ dgebal_ #define igraphdisnan_ disnan_ #define igraphdlaisnan_ dlaisnan_ #define igraphdgehrd_ dgehrd_ #define igraphdgehd2_ dgehd2_ #define igraphdlarf_ dlarf_ #define igraphiladlc_ iladlc_ #define igraphiladlr_ iladlr_ #define igraphdlarfg_ dlarfg_ #define igraphdlapy2_ dlapy2_ #define igraphdlahr2_ dlahr2_ #define igraphdlacpy_ dlacpy_ #define igraphdlarfb_ dlarfb_ #define igraphilaenv_ ilaenv_ #define igraphieeeck_ ieeeck_ #define igraphiparmq_ iparmq_ #define igraphdhseqr_ dhseqr_ #define igraphdlahqr_ dlahqr_ #define igraphdlabad_ dlabad_ #define igraphdlanv2_ dlanv2_ #define igraphdlaqr0_ dlaqr0_ #define igraphdlaqr3_ dlaqr3_ #define igraphdlaqr4_ dlaqr4_ #define igraphdlaqr2_ dlaqr2_ #define igraphdlaset_ dlaset_ #define igraphdormhr_ dormhr_ #define igraphdormqr_ dormqr_ #define igraphdlarft_ dlarft_ #define igraphdorm2r_ dorm2r_ #define igraphdtrexc_ dtrexc_ #define igraphdlaexc_ dlaexc_ #define igraphdlange_ dlange_ #define igraphdlassq_ dlassq_ #define igraphdlarfx_ dlarfx_ #define igraphdlartg_ dlartg_ #define igraphdlasy2_ dlasy2_ #define igraphdlaqr5_ dlaqr5_ #define igraphdlaqr1_ dlaqr1_ #define igraphdlascl_ dlascl_ #define igraphdorghr_ dorghr_ #define igraphdorgqr_ dorgqr_ #define igraphdorg2r_ dorg2r_ #define igraphdtrevc_ dtrevc_ #define igraphdlaln2_ dlaln2_ #define igraphdladiv_ dladiv_ #define igraphdsyevr_ dsyevr_ #define igraphdlansy_ dlansy_ #define igraphdormtr_ dormtr_ #define igraphdormql_ dormql_ #define igraphdorm2l_ dorm2l_ #define igraphdstebz_ dstebz_ #define igraphdlaebz_ dlaebz_ #define igraphdstein_ dstein_ #define igraphdlagtf_ dlagtf_ #define igraphdlagts_ dlagts_ #define igraphdlarnv_ dlarnv_ #define igraphdlaruv_ dlaruv_ #define igraphdstemr_ dstemr_ #define igraphdlae2_ dlae2_ #define igraphdlaev2_ dlaev2_ #define igraphdlanst_ dlanst_ #define igraphdlarrc_ dlarrc_ #define igraphdlarre_ dlarre_ #define igraphdlarra_ dlarra_ #define igraphdlarrb_ dlarrb_ #define igraphdlaneg_ dlaneg_ #define igraphdlarrd_ dlarrd_ #define igraphdlarrk_ dlarrk_ #define igraphdlasq2_ dlasq2_ #define igraphdlasq3_ dlasq3_ #define igraphdlasq4_ dlasq4_ #define igraphdlasq5_ dlasq5_ #define igraphdlasq6_ dlasq6_ #define igraphdlasrt_ dlasrt_ #define igraphdlarrj_ dlarrj_ #define igraphdlarrr_ dlarrr_ #define igraphdlarrv_ dlarrv_ #define igraphdlar1v_ dlar1v_ #define igraphdlarrf_ dlarrf_ #define igraphdsterf_ dsterf_ #define igraphdsytrd_ dsytrd_ #define igraphdlatrd_ dlatrd_ #define igraphdsytd2_ dsytd2_ #define igraphdlanhs_ dlanhs_ #define igraphdgeqr2_ dgeqr2_ #define igraphdtrsen_ dtrsen_ #define igraphdlacn2_ dlacn2_ #define igraphdtrsyl_ dtrsyl_ #define igraphdlasr_ dlasr_ #define igraphdsteqr_ dsteqr_ #define igraphdgesv_ dgesv_ #define igraphdgetrf_ dgetrf_ #define igraphdgetf2_ dgetf2_ #define igraphdlaswp_ dlaswp_ #define igraphdgetrs_ dgetrs_ #define igraphlen_trim_ len_trim_ #define igraph_dlamc1_ dlamc1_ #define igraph_dlamc2_ dlamc2_ #define igraph_dlamc3_ dlamc3_ #define igraph_dlamc4_ dlamc4_ #define igraph_dlamc5_ dlamc5_ #endif int igraphdgetrf_(int *m, int *n, igraph_real_t *a, int *lda, int *ipiv, int *info); int igraphdgetrs_(char *trans, int *n, int *nrhs, igraph_real_t *a, int *lda, int *ipiv, igraph_real_t *b, int *ldb, int *info); int igraphdgesv_(int *n, int *nrhs, igraph_real_t *a, int *lda, int *ipiv, igraph_real_t *b, int *ldb, int *info); igraph_real_t igraphdlapy2_(igraph_real_t *x, igraph_real_t *y); int igraphdsyevr_(char *jobz, char *range, char *uplo, int *n, igraph_real_t *a, int *lda, igraph_real_t *vl, igraph_real_t *vu, int * il, int *iu, igraph_real_t *abstol, int *m, igraph_real_t *w, igraph_real_t *z, int *ldz, int *isuppz, igraph_real_t *work, int *lwork, int *iwork, int *liwork, int *info); int igraphdgeev_(char *jobvl, char *jobvr, int *n, igraph_real_t *a, int *lda, igraph_real_t *wr, igraph_real_t *wi, igraph_real_t *vl, int *ldvl, igraph_real_t *vr, int *ldvr, igraph_real_t *work, int *lwork, int *info); int igraphdgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense, int *n, igraph_real_t *a, int *lda, igraph_real_t *wr, igraph_real_t *wi, igraph_real_t *vl, int *ldvl, igraph_real_t *vr, int *ldvr, int *ilo, int *ihi, igraph_real_t *scale, igraph_real_t *abnrm, igraph_real_t *rconde, igraph_real_t *rcondv, igraph_real_t *work, int *lwork, int *iwork, int *info); int igraphdgehrd_(int *n, int *ilo, int *ihi, igraph_real_t *A, int *lda, igraph_real_t *tau, igraph_real_t *work, int *lwork, int *info); #endif igraph/src/walktrap_communities.h0000644000176000001440000001507712325527074016776 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Pascal Pons The original copyright notice follows here. The FSF address was fixed by Tamas Nepusz */ // File: communities.h //----------------------------------------------------------------------------- // Walktrap v0.2 -- Finds community structure of networks using random walks // Copyright (C) 2004-2005 Pascal Pons // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA //----------------------------------------------------------------------------- // Author : Pascal Pons // Email : pascal.pons@gmail.com // Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html // Location : Paris, France // Time : June 2005 //----------------------------------------------------------------------------- // see readme.txt for more details #ifndef COMMUNITIES_H #define COMMUNITIES_H #include "walktrap_graph.h" #include "walktrap_heap.h" #include "igraph_community.h" #include "config.h" namespace igraph { namespace walktrap { class Communities; class Probabilities { public: static IGRAPH_THREAD_LOCAL float* tmp_vector1; // static IGRAPH_THREAD_LOCAL float* tmp_vector2; // static IGRAPH_THREAD_LOCAL int* id; // static IGRAPH_THREAD_LOCAL int* vertices1; // static IGRAPH_THREAD_LOCAL int* vertices2; // static IGRAPH_THREAD_LOCAL int current_id; // static IGRAPH_THREAD_LOCAL Communities* C; // pointer to all the communities static IGRAPH_THREAD_LOCAL int length; // length of the random walks int size; // number of probabilities stored int* vertices; // the vertices corresponding to the stored probabilities, 0 if all the probabilities are stored float* P; // the probabilities long memory(); // the memory (in Bytes) used by the object double compute_distance(const Probabilities* P2) const; // compute the squared distance r^2 between this probability vector and P2 Probabilities(int community); // compute the probability vector of a community Probabilities(int community1, int community2); // merge the probability vectors of two communities in a new one // the two communities must have their probability vectors stored ~Probabilities(); // destructor }; class Community { public: Neighbor* first_neighbor; // first item of the list of adjacent communities Neighbor* last_neighbor; // last item of the list of adjacent communities int this_community; // number of this community int first_member; // number of the first vertex of the community int last_member; // number of the last vertex of the community int size; // number of members of the community Probabilities* P; // the probability vector, 0 if not stored. float sigma; // sigma(C) of the community float internal_weight; // sum of the weight of the internal edges float total_weight; // sum of the weight of all the edges of the community (an edge between two communities is a half-edge for each community) int sub_communities[2]; // the two sub sommunities, -1 if no sub communities; int sub_community_of; // number of the community in which this community has been merged // 0 if the community is active // -1 if the community is not used void merge(Community &C1, Community &C2); // create a new community by merging C1 an C2 void add_neighbor(Neighbor* N); void remove_neighbor(Neighbor* N); float min_delta_sigma(); // compute the minimal delta sigma among all the neighbors of this community Community(); // create an empty community ~Community(); // destructor }; class Communities { private: long max_memory; // size in Byte of maximal memory usage, -1 for no limit igraph_matrix_t *merges; long int mergeidx; igraph_vector_t *modularity; public: long memory_used; // in bytes Min_delta_sigma_heap* min_delta_sigma; // the min delta_sigma of the community with a saved probability vector (for memory management) Graph* G; // the graph int* members; // the members of each community represented as a chained list. // a community points to the first_member the array which contains // the next member (-1 = end of the community) Neighbor_heap* H; // the distances between adjacent communities. Community* communities; // array of the communities int nb_communities; // number of valid communities int nb_active_communities; // number of active communities Communities(Graph* G, int random_walks_length = 3, long max_memory = -1, igraph_matrix_t *merges=0, igraph_vector_t *modularity=0); // Constructor ~Communities(); // Destructor void merge_communities(Neighbor* N); // create a community by merging two existing communities double merge_nearest_communities(); double compute_delta_sigma(int c1, int c2); // compute delta_sigma(c1,c2) void remove_neighbor(Neighbor* N); void add_neighbor(Neighbor* N); void update_neighbor(Neighbor* N, float new_delta_sigma); void manage_memory(); }; } } /* end of namespaces */ #endif igraph/src/igraph_datatype.h0000644000176000001440000000603712325527073015675 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_DATATYPE_H #define IGRAPH_DATATYPE_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_vector.h" __BEGIN_DECLS /** * \ingroup internal * \struct igraph_t * \brief The internal data structure for storing graphs. * * It is simple and efficient. It has the following members: * - n The number of vertices, reduntant. * - directed Whether the graph is directed. * - from The first column of the edge list. * - to The second column of the edge list. * - oi The index of the edge list by the first column. Thus * the first edge according to this order goes from * \c from[oi[0]] to \c to[oi[0]]. The length of * this vector is the same as the number of edges in the graph. * - ii The index of the edge list by the second column. * The length of this vector is the same as the number of edges. * - os Contains pointers to the edgelist (\c from * and \c to for every vertex. The first edge \em from * vertex \c v is edge no. \c from[oi[os[v]]] if * \c os[v]is
This is basically the same as os, but this time * for the incoming edges. * * For undirected graph, the same edge list is stored, ie. an * undirected edge is stored only once, and for checking whether there * is an undirected edge from \c v1 to \c v2 one * should search for both \c from=v1, \c to=v2 and * \c from=v2, \c to=v1. * * The storage requirements for a graph with \c |V| vertices * and \c |E| edges is \c O(|E|+|V|). */ typedef struct igraph_s { igraph_integer_t n; igraph_bool_t directed; igraph_vector_t from; igraph_vector_t to; igraph_vector_t oi; igraph_vector_t ii; igraph_vector_t os; igraph_vector_t is; void *attr; } igraph_t; __END_DECLS #endif igraph/src/foreign-graphml.c0000644000176000001440000014622112325527073015604 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_foreign.h" #include "config.h" #include /* isnan */ #include "igraph_math.h" #include "igraph_attributes.h" #include "igraph_interface.h" #include "igraph_types_internal.h" #include /* isspace */ #include #include "igraph_memory.h" #include /* va_start & co */ #if HAVE_LIBXML == 1 #include #include xmlEntity blankEntityStruct = { #ifndef XML_WITHOUT_CORBA 0, #endif XML_ENTITY_DECL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, XML_EXTERNAL_GENERAL_PARSED_ENTITY, 0, 0, 0, 0, 0, 1 }; xmlEntityPtr blankEntity = &blankEntityStruct; #define GRAPHML_PARSE_ERROR(state, msg) do { \ if (state->successful) { \ igraph_error(msg, __FILE__, __LINE__, IGRAPH_PARSEERROR); \ igraph_i_graphml_sax_handler_error(state, msg); \ } \ return; \ } while (1) #define GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) do { \ if (state->successful) { \ igraph_error(msg, __FILE__, __LINE__, code); \ igraph_i_graphml_sax_handler_error(state, msg); \ } \ return; \ } while (1) /* TODO: proper error handling */ typedef struct igraph_i_graphml_attribute_record_t { const char *id; /* GraphML id */ enum { I_GRAPHML_BOOLEAN, I_GRAPHML_INTEGER, I_GRAPHML_LONG, I_GRAPHML_FLOAT, I_GRAPHML_DOUBLE, I_GRAPHML_STRING, I_GRAPHML_UNKNOWN_TYPE } type; /* GraphML type */ igraph_attribute_record_t record; } igraph_i_graphml_attribute_record_t; struct igraph_i_graphml_parser_state { enum { START, INSIDE_GRAPHML, INSIDE_GRAPH, INSIDE_NODE, INSIDE_EDGE, INSIDE_KEY, INSIDE_DEFAULT, INSIDE_DATA, FINISH, UNKNOWN, ERROR } st; igraph_t *g; igraph_trie_t node_trie; igraph_strvector_t edgeids; igraph_vector_t edgelist; igraph_vector_int_t prev_state_stack; unsigned int unknown_depth; int index; igraph_bool_t successful, edges_directed, destroyed; igraph_trie_t v_names; igraph_vector_ptr_t v_attrs; igraph_trie_t e_names; igraph_vector_ptr_t e_attrs; igraph_trie_t g_names; igraph_vector_ptr_t g_attrs; xmlChar *data_key; igraph_attribute_elemtype_t data_type; char *error_message; char *data_char; }; static void igraph_i_report_unhandled_attribute_target(const char* target, const char* file, int line) { igraph_warningf("Attribute target '%s' is not handled; ignoring corresponding " "attribute specifications", file, line, 0, target); } igraph_bool_t igraph_i_graphml_parse_boolean(const char* char_data) { int value; if (char_data == 0) return 0; if (!strcasecmp("true", char_data)) return 1; if (!strcasecmp("yes", char_data)) return 1; if (!strcasecmp("false", char_data)) return 0; if (!strcasecmp("no", char_data)) return 0; if (sscanf(char_data, "%d", &value) == 0) return 0; return value != 0; } void igraph_i_graphml_destroy_state(struct igraph_i_graphml_parser_state* state) { long int i; if (state->destroyed) return; state->destroyed=1; /* this is the easy part */ igraph_trie_destroy(&state->node_trie); igraph_strvector_destroy(&state->edgeids); igraph_trie_destroy(&state->v_names); igraph_trie_destroy(&state->e_names); igraph_trie_destroy(&state->g_names); igraph_vector_destroy(&state->edgelist); igraph_vector_int_destroy(&state->prev_state_stack); if (state->error_message) { free(state->error_message); } if (state->data_key) { free(state->data_key); } if (state->data_char) { free(state->data_char); } for (i=0; iv_attrs); i++) { igraph_i_graphml_attribute_record_t *rec=VECTOR(state->v_attrs)[i]; if (rec->record.type==IGRAPH_ATTRIBUTE_NUMERIC) { if (rec->record.value != 0) { igraph_vector_destroy((igraph_vector_t*)rec->record.value); igraph_Free(rec->record.value); } } else if (rec->record.type==IGRAPH_ATTRIBUTE_STRING) { if (rec->record.value != 0) { igraph_strvector_destroy((igraph_strvector_t*)rec->record.value); igraph_Free(rec->record.value); } } else if (rec->record.type==IGRAPH_ATTRIBUTE_BOOLEAN) { if (rec->record.value != 0) { igraph_vector_bool_destroy((igraph_vector_bool_t*)rec->record.value); igraph_Free(rec->record.value); } } if (rec->id != 0) igraph_Free(rec->id); if (rec->record.name != 0) igraph_Free(rec->record.name); igraph_Free(rec); } for (i=0; ie_attrs); i++) { igraph_i_graphml_attribute_record_t *rec=VECTOR(state->e_attrs)[i]; if (rec->record.type==IGRAPH_ATTRIBUTE_NUMERIC) { if (rec->record.value != 0) { igraph_vector_destroy((igraph_vector_t*)rec->record.value); igraph_Free(rec->record.value); } } else if (rec->record.type==IGRAPH_ATTRIBUTE_STRING) { if (rec->record.value != 0) { igraph_strvector_destroy((igraph_strvector_t*)rec->record.value); igraph_Free(rec->record.value); } } else if (rec->record.type==IGRAPH_ATTRIBUTE_BOOLEAN) { if (rec->record.value != 0) { igraph_vector_bool_destroy((igraph_vector_bool_t*)rec->record.value); igraph_Free(rec->record.value); } } if (rec->id != 0) igraph_Free(rec->id); if (rec->record.name != 0) igraph_Free(rec->record.name); igraph_Free(rec); } for (i=0; ig_attrs); i++) { igraph_i_graphml_attribute_record_t *rec=VECTOR(state->g_attrs)[i]; if (rec->record.type==IGRAPH_ATTRIBUTE_NUMERIC) { if (rec->record.value != 0) { igraph_vector_destroy((igraph_vector_t*)rec->record.value); igraph_Free(rec->record.value); } } else if (rec->record.type==IGRAPH_ATTRIBUTE_STRING) { if (rec->record.value != 0) { igraph_strvector_destroy((igraph_strvector_t*)rec->record.value); igraph_Free(rec->record.value); } } else if (rec->record.type==IGRAPH_ATTRIBUTE_BOOLEAN) { if (rec->record.value != 0) { igraph_vector_bool_destroy((igraph_vector_bool_t*)rec->record.value); igraph_Free(rec->record.value); } } if (rec->id != 0) igraph_Free(rec->id); if (rec->record.name != 0) igraph_Free(rec->record.name); igraph_Free(rec); } igraph_vector_ptr_destroy(&state->v_attrs); igraph_vector_ptr_destroy(&state->e_attrs); igraph_vector_ptr_destroy(&state->g_attrs); IGRAPH_FINALLY_CLEAN(1); } void igraph_i_graphml_sax_handler_error(void *state0, const char* msg, ...) { struct igraph_i_graphml_parser_state *state= (struct igraph_i_graphml_parser_state*)state0; va_list ap; va_start(ap, msg); if (state->error_message == 0) state->error_message=igraph_Calloc(4096, char); state->successful=0; state->st=ERROR; vsnprintf(state->error_message, 4096, msg, ap); va_end(ap); } xmlEntityPtr igraph_i_graphml_sax_handler_get_entity(void *state0, const xmlChar* name) { xmlEntityPtr predef = xmlGetPredefinedEntity(name); IGRAPH_UNUSED(state0); if (predef != NULL) return predef; IGRAPH_WARNING("unknown XML entity found\n"); return blankEntity; } void igraph_i_graphml_handle_unknown_start_tag(struct igraph_i_graphml_parser_state *state) { if (state->st != UNKNOWN) { igraph_vector_int_push_back(&state->prev_state_stack, state->st); state->st=UNKNOWN; state->unknown_depth=1; } else { state->unknown_depth++; } } void igraph_i_graphml_sax_handler_start_document(void *state0) { struct igraph_i_graphml_parser_state *state= (struct igraph_i_graphml_parser_state*)state0; int ret; state->st=START; state->successful=1; state->edges_directed=0; state->destroyed=0; state->data_key=0; state->error_message=0; state->data_char=0; state->unknown_depth=0; ret=igraph_vector_int_init(&state->prev_state_stack, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } ret=igraph_vector_int_reserve(&state->prev_state_stack, 32); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_vector_int_destroy, &state->prev_state_stack); ret=igraph_vector_ptr_init(&state->v_attrs, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_vector_ptr_destroy, &state->v_attrs); ret=igraph_vector_ptr_init(&state->e_attrs, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_vector_ptr_destroy, &state->e_attrs); ret=igraph_vector_ptr_init(&state->g_attrs, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_vector_ptr_destroy, &state->g_attrs); ret=igraph_vector_init(&state->edgelist, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_vector_destroy, &state->edgelist); ret=igraph_trie_init(&state->node_trie, 1); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_trie_destroy, &state->node_trie); ret=igraph_strvector_init(&state->edgeids, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_strvector_destroy, &state->edgeids); ret=igraph_trie_init(&state->v_names, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_trie_destroy, &state->v_names); ret=igraph_trie_init(&state->e_names, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_trie_destroy, &state->e_names); ret=igraph_trie_init(&state->g_names, 0); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } IGRAPH_FINALLY(igraph_trie_destroy, &state->g_names); IGRAPH_FINALLY_CLEAN(10); IGRAPH_FINALLY(igraph_i_graphml_destroy_state, state); } void igraph_i_graphml_sax_handler_end_document(void *state0) { struct igraph_i_graphml_parser_state *state= (struct igraph_i_graphml_parser_state*)state0; long i, l; int r; igraph_attribute_record_t idrec, eidrec; const char *idstr="id"; igraph_bool_t already_has_vertex_id=0, already_has_edge_id=0; if (!state->successful) return; if (state->index<0) { igraph_vector_ptr_t vattr, eattr, gattr; long int esize=igraph_vector_ptr_size(&state->e_attrs); const void **tmp; r=igraph_vector_ptr_init(&vattr, igraph_vector_ptr_size(&state->v_attrs)+1); if (r) { igraph_error("Cannot parse GraphML file", __FILE__, __LINE__, r); igraph_i_graphml_sax_handler_error(state, "Cannot parse GraphML file"); return; } IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattr); if (igraph_strvector_size(&state->edgeids) != 0) { esize++; } r=igraph_vector_ptr_init(&eattr, esize); if (r) { igraph_error("Cannot parse GraphML file", __FILE__, __LINE__, r); igraph_i_graphml_sax_handler_error(state, "Cannot parse GraphML file"); return; } IGRAPH_FINALLY(igraph_vector_ptr_destroy, &eattr); r=igraph_vector_ptr_init(&gattr, igraph_vector_ptr_size(&state->g_attrs)); if (r) { igraph_error("Cannot parse GraphML file", __FILE__, __LINE__, r); igraph_i_graphml_sax_handler_error(state, "Cannot parse GraphML file"); return; } IGRAPH_FINALLY(igraph_vector_ptr_destroy, &gattr); for (i=0; iv_attrs); i++) { igraph_i_graphml_attribute_record_t *graphmlrec= VECTOR(state->v_attrs)[i]; igraph_attribute_record_t *rec=&graphmlrec->record; /* Check that the name of the vertex attribute is not 'id'. If it is then we cannot the complimentary 'id' attribute. */ if (! strcmp(rec->name, idstr)) { already_has_vertex_id=1; } if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec=(igraph_vector_t*)rec->value; long int origsize=igraph_vector_size(vec); long int nodes=igraph_trie_size(&state->node_trie); igraph_vector_resize(vec, nodes); for (l=origsize; ltype == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strvec=(igraph_strvector_t*)rec->value; long int origsize=igraph_strvector_size(strvec); long int nodes=igraph_trie_size(&state->node_trie); igraph_strvector_resize(strvec, nodes); for (l=origsize; ltype == IGRAPH_ATTRIBUTE_BOOLEAN) { igraph_vector_bool_t *boolvec=(igraph_vector_bool_t*)rec->value; long int origsize=igraph_vector_bool_size(boolvec); long int nodes=igraph_trie_size(&state->node_trie); igraph_vector_bool_resize(boolvec, nodes); for (l=origsize; lnode_trie, (const igraph_strvector_t **)tmp); VECTOR(vattr)[i]=&idrec; } else { igraph_vector_ptr_pop_back(&vattr); IGRAPH_WARNING("Could not add vertex ids, " "there is already an 'id' vertex attribute"); } for (i=0; ie_attrs); i++) { igraph_i_graphml_attribute_record_t *graphmlrec= VECTOR(state->e_attrs)[i]; igraph_attribute_record_t *rec=&graphmlrec->record; if (! strcmp(rec->name, idstr)) { already_has_edge_id=1; } if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec=(igraph_vector_t*)rec->value; long int origsize=igraph_vector_size(vec); long int edges=igraph_vector_size(&state->edgelist)/2; igraph_vector_resize(vec, edges); for (l=origsize; ltype == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strvec=(igraph_strvector_t*)rec->value; long int origsize=igraph_strvector_size(strvec); long int edges=igraph_vector_size(&state->edgelist)/2; igraph_strvector_resize(strvec, edges); for (l=origsize; ltype == IGRAPH_ATTRIBUTE_BOOLEAN) { igraph_vector_bool_t *boolvec=(igraph_vector_bool_t*)rec->value; long int origsize=igraph_vector_bool_size(boolvec); long int edges=igraph_vector_size(&state->edgelist)/2; igraph_vector_bool_resize(boolvec, edges); for (l=origsize; ledgeids) != 0) { if (!already_has_edge_id) { long int origsize=igraph_strvector_size(&state->edgeids); eidrec.name=idstr; eidrec.type=IGRAPH_ATTRIBUTE_STRING; igraph_strvector_resize(&state->edgeids, igraph_vector_size(&state->edgelist)/2); for (; origsize < igraph_strvector_size(&state->edgeids); origsize++) { igraph_strvector_set(&state->edgeids, origsize, ""); } eidrec.value=&state->edgeids; VECTOR(eattr)[(long int)igraph_vector_ptr_size(&eattr)-1]=&eidrec; } else { igraph_vector_ptr_pop_back(&eattr); IGRAPH_WARNING("Could not add edge ids, " "there is already an 'id' edge attribute"); } } for (i=0; ig_attrs); i++) { igraph_i_graphml_attribute_record_t *graphmlrec= VECTOR(state->g_attrs)[i]; igraph_attribute_record_t *rec=&graphmlrec->record; if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec=(igraph_vector_t*)rec->value; long int origsize=igraph_vector_size(vec); igraph_vector_resize(vec, 1); for (l=origsize; l<1; l++) { VECTOR(*vec)[l]=IGRAPH_NAN; } } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strvec=(igraph_strvector_t*)rec->value; long int origsize=igraph_strvector_size(strvec); igraph_strvector_resize(strvec, 1); for (l=origsize; l<1; l++) { igraph_strvector_set(strvec, l, ""); } } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { igraph_vector_bool_t *boolvec=(igraph_vector_bool_t*)rec->value; long int origsize=igraph_vector_bool_size(boolvec); igraph_vector_bool_resize(boolvec, 1); for (l=origsize; l<1; l++) { VECTOR(*boolvec)[l]=0; } } VECTOR(gattr)[i]=rec; } igraph_empty_attrs(state->g, 0, state->edges_directed, &gattr); igraph_add_vertices(state->g, (igraph_integer_t) igraph_trie_size(&state->node_trie), &vattr); igraph_add_edges(state->g, &state->edgelist, &eattr); igraph_vector_ptr_destroy(&vattr); igraph_vector_ptr_destroy(&eattr); igraph_vector_ptr_destroy(&gattr); IGRAPH_FINALLY_CLEAN(3); } igraph_i_graphml_destroy_state(state); } #define toXmlChar(a) (BAD_CAST(a)) #define fromXmlChar(a) ((char *)(a)) /* not the most elegant way... */ void igraph_i_graphml_add_attribute_key(const xmlChar** attrs, struct igraph_i_graphml_parser_state *state) { xmlChar **it; igraph_trie_t *trie=0; igraph_vector_ptr_t *ptrvector=0; long int id; unsigned short int skip=0; int ret; igraph_i_graphml_attribute_record_t *rec; if (!state->successful) return; rec = igraph_Calloc(1, igraph_i_graphml_attribute_record_t); if (rec==0) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->type = I_GRAPHML_UNKNOWN_TYPE; for (it=(xmlChar**)attrs; *it; it+=2) { if (xmlStrEqual(*it, toXmlChar("id"))) { const char *id=(const char*)(*(it+1)); rec->id=strdup(id); } else if (xmlStrEqual(*it, toXmlChar("attr.name"))) { const char *name=fromXmlChar(*(it+1)); rec->record.name=strdup(name); } else if (xmlStrEqual(*it, toXmlChar("attr.type"))) { if (xmlStrEqual(*(it+1), (xmlChar*)"boolean")) { rec->type=I_GRAPHML_BOOLEAN; rec->record.type=IGRAPH_ATTRIBUTE_BOOLEAN; } else if (xmlStrEqual(*(it+1), toXmlChar("string"))) { rec->type=I_GRAPHML_STRING; rec->record.type=IGRAPH_ATTRIBUTE_STRING; } else if (xmlStrEqual(*(it+1), toXmlChar("float"))) { rec->type=I_GRAPHML_FLOAT; rec->record.type=IGRAPH_ATTRIBUTE_NUMERIC; } else if (xmlStrEqual(*(it+1), toXmlChar("double"))) { rec->type=I_GRAPHML_DOUBLE; rec->record.type=IGRAPH_ATTRIBUTE_NUMERIC; } else if (xmlStrEqual(*(it+1), toXmlChar("int"))) { rec->type=I_GRAPHML_INTEGER; rec->record.type=IGRAPH_ATTRIBUTE_NUMERIC; } else if (xmlStrEqual(*(it+1), toXmlChar("long"))) { rec->type=I_GRAPHML_LONG; rec->record.type=IGRAPH_ATTRIBUTE_NUMERIC; } else { GRAPHML_PARSE_ERROR(state, "Cannot parse GraphML file, unknown attribute type"); } } else if (xmlStrEqual(*it, toXmlChar("for"))) { /* graph, vertex or edge attribute? */ if (xmlStrEqual(*(it+1), toXmlChar("graph"))) { trie=&state->g_names; ptrvector=&state->g_attrs; } else if (xmlStrEqual(*(it+1), toXmlChar("node"))) { trie=&state->v_names; ptrvector=&state->v_attrs; } else if (xmlStrEqual(*(it+1), toXmlChar("edge"))) { trie=&state->e_names; ptrvector=&state->e_attrs; } else if (xmlStrEqual(*(it+1), toXmlChar("graphml"))) { igraph_i_report_unhandled_attribute_target("graphml", __FILE__, __LINE__); skip=1; } else if (xmlStrEqual(*(it+1), toXmlChar("hyperedge"))) { igraph_i_report_unhandled_attribute_target("hyperedge", __FILE__, __LINE__); skip=1; } else if (xmlStrEqual(*(it+1), toXmlChar("port"))) { igraph_i_report_unhandled_attribute_target("port", __FILE__, __LINE__); skip=1; } else if (xmlStrEqual(*(it+1), toXmlChar("endpoint"))) { igraph_i_report_unhandled_attribute_target("endpoint", __FILE__, __LINE__); skip=1; } else if (xmlStrEqual(*(it+1), toXmlChar("all"))) { /* TODO: we should handle this */ igraph_i_report_unhandled_attribute_target("all", __FILE__, __LINE__); skip=1; } else { GRAPHML_PARSE_ERROR(state, "Cannot parse GraphML file, unknown value in the 'for' attribute of a tag"); } } } /* throw an error if there is no ID; this is a clear violation of the GraphML * DTD */ if (rec->id == 0) { GRAPHML_PARSE_ERROR(state, "Found tag with no 'id' attribute"); } /* in case of a missing attr.name attribute, use the id as the attribute name */ if (rec->record.name == 0) { rec->record.name=strdup(rec->id); } /* if the attribute type is missing, throw an error */ if (!skip && rec->type == I_GRAPHML_UNKNOWN_TYPE) { igraph_warningf("Ignoring because of a missing or unknown 'attr.type' attribute", __FILE__, __LINE__, 0, rec->id); skip = 1; } /* if the value of the 'for' attribute was unknown, throw an error */ if (!skip && trie == 0) { GRAPHML_PARSE_ERROR(state, "Cannot parse GraphML file, missing 'for' attribute in a tag"); } /* if the code above requested skipping the attribute, free everything and * return */ if (skip) { igraph_free(rec); IGRAPH_FINALLY_CLEAN(1); return; } /* add to trie, attribues */ igraph_trie_get(trie, rec->id, &id); if (id != igraph_trie_size(trie)-1) { GRAPHML_PARSE_ERROR(state, "Cannot parse GraphML file, duplicate attribute"); } ret=igraph_vector_ptr_push_back(ptrvector, rec); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot read GraphML file", ret); } /* Ownership of 'rec' is now taken by ptrvector so we can clean the * finally stack */ IGRAPH_FINALLY_CLEAN(1); /* rec */ /* create the attribute values */ switch (rec->record.type) { igraph_vector_t *vec; igraph_vector_bool_t *boolvec; igraph_strvector_t *strvec; case IGRAPH_ATTRIBUTE_BOOLEAN: boolvec=igraph_Calloc(1, igraph_vector_bool_t); if (boolvec==0) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); } rec->record.value=boolvec; igraph_vector_bool_init(boolvec, 0); break; case IGRAPH_ATTRIBUTE_NUMERIC: vec=igraph_Calloc(1, igraph_vector_t); if (vec==0) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); } rec->record.value=vec; igraph_vector_init(vec, 0); break; case IGRAPH_ATTRIBUTE_STRING: strvec=igraph_Calloc(1, igraph_strvector_t); if (strvec==0) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); } rec->record.value=strvec; igraph_strvector_init(strvec, 0); break; default: break; } } void igraph_i_graphml_attribute_data_setup(struct igraph_i_graphml_parser_state *state, const xmlChar **attrs, igraph_attribute_elemtype_t type) { xmlChar **it; if (!state->successful) return; for (it=(xmlChar**)attrs; *it; it+=2) { if (xmlStrEqual(*it, toXmlChar("key"))) { if (state->data_key) { free(state->data_key); } state->data_key=xmlStrdup(*(it+1)); if (state->data_char) { free(state->data_char); } state->data_char=0; state->data_type=type; } else { /* ignore */ } } } void igraph_i_graphml_attribute_data_add(struct igraph_i_graphml_parser_state *state, const xmlChar *data, int len) { long int data_char_new_start=0; if (!state->successful) return; if (state->data_char) { data_char_new_start=(long int) strlen(state->data_char); state->data_char=igraph_Realloc(state->data_char, (size_t)(data_char_new_start+len+1), char); } else { state->data_char=igraph_Calloc((size_t) len+1, char); } if (state->data_char==0) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); } memcpy(state->data_char+data_char_new_start, data, (size_t) len*sizeof(xmlChar)); state->data_char[data_char_new_start+len]='\0'; } void igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parser_state *state) { const char *key=fromXmlChar(state->data_key); igraph_attribute_elemtype_t type=state->data_type; igraph_trie_t *trie=0; igraph_vector_ptr_t *ptrvector=0; igraph_i_graphml_attribute_record_t *graphmlrec; igraph_attribute_record_t *rec; long int recid, id=0; int ret; switch (type) { case IGRAPH_ATTRIBUTE_GRAPH: trie=&state->g_names; ptrvector=&state->g_attrs; id=0; break; case IGRAPH_ATTRIBUTE_VERTEX: trie=&state->v_names; ptrvector=&state->v_attrs; id=igraph_trie_size(&state->node_trie)-1; /* hack */ break; case IGRAPH_ATTRIBUTE_EDGE: trie=&state->e_names; ptrvector=&state->e_attrs; id=igraph_vector_size(&state->edgelist)/2-1; /* hack */ break; default: /* impossible */ break; } igraph_trie_check(trie, key, &recid); if (recid < 0) { /* no such attribute key, issue a warning */ igraph_warningf( "unknown attribute key '%s' in a tag, ignoring attribute", __FILE__, __LINE__, 0, key ); igraph_Free(state->data_char); return; } graphmlrec=VECTOR(*ptrvector)[recid]; rec=&graphmlrec->record; switch (rec->type) { igraph_vector_bool_t *boolvec; igraph_vector_t *vec; igraph_strvector_t *strvec; igraph_real_t num; long int s, i; case IGRAPH_ATTRIBUTE_BOOLEAN: boolvec=(igraph_vector_bool_t *)rec->value; s=igraph_vector_bool_size(boolvec); if (id >= s) { ret=igraph_vector_bool_resize(boolvec, id+1); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } for (i=s; idata_char); break; case IGRAPH_ATTRIBUTE_NUMERIC: vec=(igraph_vector_t *)rec->value; s=igraph_vector_size(vec); if (id >= s) { ret=igraph_vector_resize(vec, id+1); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } for (i=s; idata_char) sscanf(state->data_char, "%lf", &num); else num=0; VECTOR(*vec)[id]=num; break; case IGRAPH_ATTRIBUTE_STRING: strvec=(igraph_strvector_t *)rec->value; s=igraph_strvector_size(strvec); if (id >= s) { ret=igraph_strvector_resize(strvec, id+1); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } for (i=s;idata_char) ret=igraph_strvector_set(strvec, id, (char*)state->data_char); else ret=igraph_strvector_set(strvec, id, ""); if (ret) { GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); } break; default: break; } if (state->data_char) { igraph_Free(state->data_char); } } void igraph_i_graphml_sax_handler_start_element(void *state0, const xmlChar* name, const xmlChar** attrs) { struct igraph_i_graphml_parser_state *state= (struct igraph_i_graphml_parser_state*)state0; xmlChar** it; long int id1, id2; if (!state->successful) return; switch (state->st) { case START: /* If we are in the START state and received a graphml tag, * change to INSIDE_GRAPHML state. Otherwise, change to UNKNOWN. */ if (xmlStrEqual(name, toXmlChar("graphml"))) state->st=INSIDE_GRAPHML; else igraph_i_graphml_handle_unknown_start_tag(state); break; case INSIDE_GRAPHML: /* If we are in the INSIDE_GRAPHML state and received a graph tag, * change to INSIDE_GRAPH state if the state->index counter reached * zero (this is to handle multiple graphs in the same file). * Otherwise, change to UNKNOWN. */ if (xmlStrEqual(name, toXmlChar("graph"))) { if (state->index==0) { state->st=INSIDE_GRAPH; for (it=(xmlChar**)attrs; *it; it+=2) { if (xmlStrEqual(*it, toXmlChar("edgedefault"))) { if (xmlStrEqual(*(it+1), toXmlChar("directed"))) state->edges_directed=1; else if (xmlStrEqual(*(it+1), toXmlChar("undirected"))) state->edges_directed=0; } } } state->index--; } else if (xmlStrEqual(name, toXmlChar("key"))) { igraph_i_graphml_add_attribute_key(attrs, state); state->st=INSIDE_KEY; } else igraph_i_graphml_handle_unknown_start_tag(state); break; case INSIDE_KEY: /* If we are in the INSIDE_KEY state, check for default tag */ if (xmlStrEqual(name, toXmlChar("default"))) state->st=INSIDE_DEFAULT; else igraph_i_graphml_handle_unknown_start_tag(state); break; case INSIDE_DEFAULT: /* If we are in the INSIDE_DEFAULT state, every further tag will be unknown */ igraph_i_graphml_handle_unknown_start_tag(state); break; case INSIDE_GRAPH: /* If we are in the INSIDE_GRAPH state, check for node and edge tags */ if (xmlStrEqual(name, toXmlChar("edge"))) { id1=-1; id2=-1; for (it=(xmlChar**)attrs; *it; it+=2) { if (xmlStrEqual(*it, toXmlChar("source"))) { igraph_trie_get(&state->node_trie, fromXmlChar(*(it+1)), &id1); } if (xmlStrEqual(*it, toXmlChar("target"))) { igraph_trie_get(&state->node_trie, fromXmlChar(*(it+1)), &id2); } if (xmlStrEqual(*it, toXmlChar("id"))) { long int edges=igraph_vector_size(&state->edgelist)/2+1; long int origsize=igraph_strvector_size(&state->edgeids); igraph_strvector_resize(&state->edgeids, edges); for (;origsize < edges-1; origsize++) { igraph_strvector_set(&state->edgeids, origsize, ""); } igraph_strvector_set(&state->edgeids, edges-1, fromXmlChar(*(it+1))); } } if (id1>=0 && id2>=0) { igraph_vector_push_back(&state->edgelist, id1); igraph_vector_push_back(&state->edgelist, id2); } else { igraph_i_graphml_sax_handler_error(state, "Edge with missing source or target encountered"); return; } state->st=INSIDE_EDGE; } else if (xmlStrEqual(name, toXmlChar("node"))) { for (it=(xmlChar**)attrs; *it; it+=2) { if (xmlStrEqual(*it, toXmlChar("id"))) { it++; igraph_trie_get(&state->node_trie, fromXmlChar(*it), &id1); break; } } state->st=INSIDE_NODE; } else if (xmlStrEqual(name, toXmlChar("data"))) { igraph_i_graphml_attribute_data_setup(state, attrs, IGRAPH_ATTRIBUTE_GRAPH); igraph_vector_int_push_back(&state->prev_state_stack, state->st); state->st=INSIDE_DATA; } else igraph_i_graphml_handle_unknown_start_tag(state); break; case INSIDE_NODE: if (xmlStrEqual(name, toXmlChar("data"))) { igraph_i_graphml_attribute_data_setup(state, attrs, IGRAPH_ATTRIBUTE_VERTEX); igraph_vector_int_push_back(&state->prev_state_stack, state->st); state->st=INSIDE_DATA; } break; case INSIDE_EDGE: if (xmlStrEqual(name, toXmlChar("data"))) { igraph_i_graphml_attribute_data_setup(state, attrs, IGRAPH_ATTRIBUTE_EDGE); igraph_vector_int_push_back(&state->prev_state_stack, state->st); state->st=INSIDE_DATA; } break; case INSIDE_DATA: /* We do not expect any new tags within a tag */ igraph_i_graphml_handle_unknown_start_tag(state); break; case UNKNOWN: igraph_i_graphml_handle_unknown_start_tag(state); break; default: break; } } void igraph_i_graphml_sax_handler_end_element(void *state0, const xmlChar* name) { struct igraph_i_graphml_parser_state *state= (struct igraph_i_graphml_parser_state*)state0; if (!state->successful) return; IGRAPH_UNUSED(name); switch (state->st) { case INSIDE_GRAPHML: state->st=FINISH; break; case INSIDE_GRAPH: state->st=INSIDE_GRAPHML; break; case INSIDE_KEY: state->st=INSIDE_GRAPHML; break; case INSIDE_DEFAULT: state->st=INSIDE_KEY; break; case INSIDE_NODE: state->st=INSIDE_GRAPH; break; case INSIDE_EDGE: state->st=INSIDE_GRAPH; break; case INSIDE_DATA: igraph_i_graphml_attribute_data_finish(state); state->st = igraph_vector_int_pop_back(&state->prev_state_stack); break; case UNKNOWN: state->unknown_depth--; if (!state->unknown_depth) { state->st = igraph_vector_int_pop_back(&state->prev_state_stack); } break; default: break; } } void igraph_i_graphml_sax_handler_chars(void* state0, const xmlChar* ch, int len) { struct igraph_i_graphml_parser_state *state= (struct igraph_i_graphml_parser_state*)state0; if (!state->successful) return; switch (state->st) { case INSIDE_KEY: case INSIDE_DEFAULT: break; case INSIDE_DATA: igraph_i_graphml_attribute_data_add(state, ch, len); break; default: /* just ignore it */ break; } } static xmlSAXHandler igraph_i_graphml_sax_handler={ NULL, NULL, NULL, NULL, NULL, igraph_i_graphml_sax_handler_get_entity, NULL, NULL, NULL, NULL, NULL, NULL, igraph_i_graphml_sax_handler_start_document, igraph_i_graphml_sax_handler_end_document, igraph_i_graphml_sax_handler_start_element, igraph_i_graphml_sax_handler_end_element, NULL, igraph_i_graphml_sax_handler_chars, NULL, NULL, NULL, igraph_i_graphml_sax_handler_error, igraph_i_graphml_sax_handler_error, igraph_i_graphml_sax_handler_error, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL }; #endif int igraph_i_xml_escape(char* src, char** dest) { long int destlen=0; char *s, *d; for (s=src; *s; s++, destlen++) { if (*s == '&') destlen += 4; else if (*s == '<') destlen += 3; else if (*s == '>') destlen += 3; else if (*s == '"') destlen += 5; else if (*s == '\'') destlen += 5; } *dest=igraph_Calloc(destlen+1, char); if (!*dest) IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); for (s=src, d=*dest; *s; s++, d++) { switch (*s) { case '&': strcpy(d, "&"); d+=4; break; case '<': strcpy(d, "<"); d+=3; break; case '>': strcpy(d, ">"); d+=3; break; case '"': strcpy(d, """); d+=5; break; case '\'': strcpy(d, "'"); d+=5; break; default: *d = *s; } } *d=0; return 0; } /** * \ingroup loadsave * \function igraph_read_graph_graphml * \brief Reads a graph from a GraphML file. * * * GraphML is an XML-based file format for representing various types of * graphs. Currently only the most basic import functionality is implemented * in igraph: it can read GraphML files without nested graphs and hyperedges. * Attributes of the graph are loaded only if an attribute interface * is attached, ie. if you use igraph from R or Python. * * * Graph attribute names are taken from the \c attr.name attributes of the * \c key tags in the GraphML file. Since \c attr.name is not mandatory, * igraph will fall back to the \c id attribute of the \c key tag if * \c attr.name is missing. * * \param graph Pointer to an uninitialized graph object. * \param instream A stream, it should be readable. * \param index If the GraphML file contains more than one graph, the one * specified by this index will be loaded. Indices start from * zero, so supply zero here if your GraphML file contains only * a single graph. * * \return Error code: * \c IGRAPH_PARSEERROR: if there is a * problem reading the file, or the file is syntactically * incorrect. * \c IGRAPH_UNIMPLEMENTED: the GraphML functionality was disabled * at compile-time * * \example examples/simple/graphml.c */ int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { #if HAVE_LIBXML == 1 xmlParserCtxtPtr ctxt; struct igraph_i_graphml_parser_state state; int res; char buffer[4096]; if (index<0) IGRAPH_ERROR("Graph index must be non-negative", IGRAPH_EINVAL); xmlInitParser(); /* Create a progressive parser context */ state.g=graph; state.index=index<0?0:index; res=(int) fread(buffer, 1, 4096, instream); ctxt=xmlCreatePushParserCtxt(&igraph_i_graphml_sax_handler, &state, buffer, res, NULL); /* ctxt=xmlCreateIOParserCtxt(&igraph_i_graphml_sax_handler, &state, */ /* igraph_i_libxml2_read_callback, */ /* igraph_i_libxml2_close_callback, */ /* instream, XML_CHAR_ENCODING_NONE); */ if (ctxt==NULL) IGRAPH_ERROR("Can't create progressive parser context", IGRAPH_PARSEERROR); /* Parse the file */ while ((res=(int) fread(buffer, 1, 4096, instream))>0) { xmlParseChunk(ctxt, buffer, res, 0); if (!state.successful) break; } xmlParseChunk(ctxt, buffer, res, 1); /* Free the context */ xmlFreeParserCtxt(ctxt); if (!state.successful) { if (state.error_message != 0) IGRAPH_ERROR(state.error_message, IGRAPH_PARSEERROR); else IGRAPH_ERROR("Malformed GraphML file", IGRAPH_PARSEERROR); } if (state.index>=0) IGRAPH_ERROR("Graph index was too large", IGRAPH_EINVAL); return 0; #else IGRAPH_ERROR("GraphML support is disabled", IGRAPH_UNIMPLEMENTED); #endif } /** * \ingroup loadsave * \function igraph_write_graph_graphml * \brief Writes the graph to a file in GraphML format * * * GraphML is an XML-based file format for representing various types of * graphs. See the GraphML Primer (http://graphml.graphdrawing.org/primer/graphml-primer.html) * for detailed format description. * * \param graph The graph to write. * \param outstream The stream object to write to, it should be * writable. * \param prefixattr Logical value, whether to put a prefix in front of the * attribute names to ensure uniqueness if the graph has vertex and * edge (or graph) attributes with the same name. * \return Error code: * \c IGRAPH_EFILE if there is an error * writing the file. * * Time complexity: O(|V|+|E|) otherwise. All * file operations are expected to have time complexity * O(1). * * \example examples/simple/graphml.c */ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, igraph_bool_t prefixattr) { int ret; igraph_integer_t l, vc; igraph_eit_t it; igraph_strvector_t gnames, vnames, enames; igraph_vector_t gtypes, vtypes, etypes; long int i; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; const char *gprefix= prefixattr ? "g_" : ""; const char *vprefix= prefixattr ? "v_" : ""; const char *eprefix= prefixattr ? "e_" : ""; ret=fprintf(outstream, "\n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); ret=fprintf(outstream, "\n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); ret=fprintf(outstream, "\n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); /* dump the elements if any */ IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); IGRAPH_VECTOR_INIT_FINALLY(>ypes, 0); IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); igraph_i_attribute_get_info(graph, &gnames, >ypes, &vnames, &vtypes, &enames, &etypes); /* graph attributes */ for (i=0; i\n", gprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { ret=fprintf(outstream, " \n", gprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { ret=fprintf(outstream, " \n", gprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } igraph_Free(name_escaped); } /* vertex attributes */ for (i=0; i\n", vprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { ret=fprintf(outstream, " \n", vprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { ret=fprintf(outstream, " \n", vprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } igraph_Free(name_escaped); } /* edge attributes */ for (i=0; i\n", eprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { ret=fprintf(outstream, " \n", eprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { ret=fprintf(outstream, " \n", eprefix, name_escaped, name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } igraph_Free(name_escaped); } ret=fprintf(outstream, " \n", (igraph_is_directed(graph)?"directed":"undirected")); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); /* Write the graph atributes before anything else */ for (i=0; i%g\n", gprefix, name_escaped, VECTOR(numv)[0]); igraph_Free(name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { char *s, *s_escaped; igraph_strvector_get(&gnames, i, &name); IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); ret=fprintf(outstream, " ", gprefix, name_escaped); igraph_Free(name_escaped); IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); igraph_strvector_get(&strv, 0, &s); IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped)); ret=fprintf(outstream, "%s", s_escaped); igraph_Free(s_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); ret=fprintf(outstream, "\n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { igraph_strvector_get(&gnames, i, &name); IGRAPH_CHECK(igraph_i_attribute_get_bool_graph_attr(graph, name, &boolv)); IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); ret=fprintf(outstream, " %s\n", gprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); igraph_Free(name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } /* Let's dump the nodes first */ vc=igraph_vcount(graph); for (l=0; l\n", (long)l); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); for (i=0; i%g\n", vprefix, name_escaped, VECTOR(numv)[0]); igraph_Free(name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { char *s, *s_escaped; igraph_strvector_get(&vnames, i, &name); IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); ret=fprintf(outstream, " ", vprefix, name_escaped); igraph_Free(name_escaped); IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1(l), &strv)); igraph_strvector_get(&strv, 0, &s); IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped)); ret=fprintf(outstream, "%s", s_escaped); igraph_Free(s_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); ret=fprintf(outstream, "\n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { igraph_strvector_get(&vnames, i, &name); IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name, igraph_vss_1(l), &boolv)); IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); ret=fprintf(outstream, " %s\n", vprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); igraph_Free(name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } ret=fprintf(outstream, " \n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } /* Now the edges */ IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); while (!IGRAPH_EIT_END(it)) { igraph_integer_t from, to; char *name, *name_escaped; long int edge=IGRAPH_EIT_GET(it); igraph_edge(graph, (igraph_integer_t) edge, &from, &to); ret=fprintf(outstream, " \n", (long int)from, (long int)to); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); for (i=0; i%g
\n", eprefix, name_escaped, VECTOR(numv)[0]); igraph_Free(name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) { char *s, *s_escaped; igraph_strvector_get(&enames, i, &name); IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); ret=fprintf(outstream, " ", eprefix, name_escaped); igraph_Free(name_escaped); IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, igraph_ess_1((igraph_integer_t) edge), &strv)); igraph_strvector_get(&strv, 0, &s); IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped)); ret=fprintf(outstream, "%s", s_escaped); igraph_Free(s_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); ret=fprintf(outstream, "\n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { igraph_strvector_get(&enames, i, &name); IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name, igraph_ess_1((igraph_integer_t) edge), &boolv)); IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); ret=fprintf(outstream, " %s\n", eprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); igraph_Free(name_escaped); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } ret=fprintf(outstream, " \n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); IGRAPH_EIT_NEXT(it); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); ret=fprintf(outstream, " \n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); fprintf(outstream, "\n"); if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); igraph_strvector_destroy(&gnames); igraph_strvector_destroy(&vnames); igraph_strvector_destroy(&enames); igraph_vector_destroy(>ypes); igraph_vector_destroy(&vtypes); igraph_vector_destroy(&etypes); igraph_vector_destroy(&numv); igraph_strvector_destroy(&strv); igraph_vector_bool_destroy(&boolv); IGRAPH_FINALLY_CLEAN(9); return 0; } igraph/src/clustertool.cpp0000644000176000001440000005627412325527072015451 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Joerg Reichardt The original copyright notice follows here */ /*************************************************************************** main.cpp - description ------------------- begin : Tue Jul 13 11:26:47 CEST 2004 copyright : (C) 2004 by email : ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "NetDataTypes.h" #include "NetRoutines.h" #include "pottsmodel_2.h" #include "igraph_community.h" #include "igraph_error.h" #include "igraph_random.h" #include "igraph_math.h" #include "igraph_interface.h" #include "igraph_components.h" #include "igraph_interrupt_internal.h" int igraph_i_community_spinglass_orig(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, igraph_real_t stoptemp, igraph_real_t coolfact, igraph_spincomm_update_t update_rule, igraph_real_t gamma); int igraph_i_community_spinglass_negative(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, igraph_real_t stoptemp, igraph_real_t coolfact, igraph_spincomm_update_t update_rule, igraph_real_t gamma, /* igraph_matrix_t *adhesion, */ /* igraph_matrix_t *normalised_adhesion, */ /* igraph_real_t *polarization, */ igraph_real_t gamma_minus); /** * \function igraph_community_spinglass * \brief Community detection based on statistical mechanics * * This function implements the community structure detection * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. * The algorithm is described in their paper: Statistical Mechanics of * Community Detection, http://arxiv.org/abs/cond-mat/0603718 . * * From version 0.6 igraph also supports an extension to * the algorithm that allows negative edge weights. This is described * in V.A. Traag and Jeroen Bruggeman: Community detection in networks * with positive and negative links, http://arxiv.org/abs/0811.2329 . * \param graph The input graph, it may be directed but the direction * of the edge is not used in the algorithm. * \param weights The vector giving the edge weights, it may be \c NULL, * in which case all edges are weighted equally. Edge weights * should be positive, altough this is not tested. * \param modularity Pointer to a real number, if not \c NULL then the * modularity score of the solution will be stored here. This is the * gereralized modularity that simplifies to the one defined in * M. E. J. Newman and M. Girvan, Phys. Rev. E 69, 026113 (2004), * if the gamma parameter is one. * \param temperature Pointer to a real number, if not \c NULL then * the temperature at the end of the algorithm will be stored * here. * \param membership Pointer to an initialized vector or \c NULL. If * not \c NULL then the result of the clustering will be stored * here, for each vertex the number of its cluster is given, the * first cluster is numbered zero. The vector will be resized as * needed. * \param csize Pointer to an initialized vector or \c NULL. If not \c * NULL then the sizes of the clusters will stored here in cluster * number order. The vector will be resized as needed. * \param spins Integer giving the number of spins, ie. the maximum * number of clusters. Usually it is not a program to give a high * number here, the default was 25 in the original code. Even if * the number of spins is high the number of clusters in the * result might small. * \param parupdate A logical constant, whether to update all spins in * parallel. The default for this argument was \c FALSE (ie. 0) in * the original code. It is not implemented in the \c * IGRAPH_SPINCOMM_INP_NEG implementation. * \param starttemp Real number, the temperature at the start. The * value of this argument was 1.0 in the original code. * \param stoptemp Real number, the algorithm stops at this * temperature. The default was 0.01 in the original code. * \param coolfact Real number, the coolinf factor for the simulated * annealing. The default was 0.99 in the original code. * \param update_rule The type of the update rule. Possible values: \c * IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c * IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defined * the null model based on which the actual clustering is done. If * this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph * (ie. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the * configuration model is used. The configuration means that the * baseline for the clustering is a random graph with the same * degree distribution as the input graph. * \param gamma Real number. The gamma parameter of the * algorithm. This defined the weight of the missing and existing * links in the quality function for the clustering. The default * value in the original code was 1.0, which is equal weight to * missing and existing edges. Smaller values make the existing * links contibute more to the energy function which is minimized * in the algorithm. Bigger values make the missing links more * important. (If my understanding is correct.) * \param implementation Constant, chooses between the two * implementations of the spin-glass algorithm that are included * in igraph. \c IGRAPH_SPINCOMM_IMP_ORIG selects the original * implementation, this is faster, \c IGRAPH_SPINCOMM_INP_NEG selects * a new implementation by Vincent Traag that allows negative edge * weights. * \param gamma_minus Real number. Parameter for the \c * IGRAPH_SPINCOMM_IMP_NEG implementation. This * specifies the balance between the importance of present and * non-present negative weighted edges in a community. Smaller values of * \p gamma_minus lead to communities with lesser * negative intra-connectivity. * If this argument is set to zero, the algorithm reduces to a graph * coloring algorithm, using the number of spins as the number of * colors. * \return Error code. * * \sa igraph_community_spinglass_single() for calculating the community * of a single vertex. * * Time complexity: TODO. * * \example examples/simple/spinglass.c */ int igraph_community_spinglass(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, igraph_real_t stoptemp, igraph_real_t coolfact, igraph_spincomm_update_t update_rule, igraph_real_t gamma, /* the rest is for the NegSpin implementation */ igraph_spinglass_implementation_t implementation, /* igraph_matrix_t *adhesion, */ /* igraph_matrix_t *normalised_adhesion, */ /* igraph_real_t *polarization, */ igraph_real_t gamma_minus) { switch (implementation) { case IGRAPH_SPINCOMM_IMP_ORIG: return igraph_i_community_spinglass_orig(graph, weights, modularity, temperature, membership, csize, spins, parupdate, starttemp, stoptemp, coolfact, update_rule, gamma); break; case IGRAPH_SPINCOMM_IMP_NEG: return igraph_i_community_spinglass_negative(graph, weights, modularity, temperature, membership, csize, spins, parupdate, starttemp, stoptemp, coolfact, update_rule, gamma, /* adhesion, normalised_adhesion, */ /* polarization, */ gamma_minus); break; default: IGRAPH_ERROR("Unknown `implementation' in spinglass community finding", IGRAPH_EINVAL); } return 0; } int igraph_i_community_spinglass_orig(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, igraph_real_t stoptemp, igraph_real_t coolfact, igraph_spincomm_update_t update_rule, igraph_real_t gamma) { unsigned long changes, runs; igraph_bool_t use_weights=0; bool zeroT; double kT, acc, prob; ClusterList *cl_cur; network *net; PottsModel *pm; /* Check arguments */ if (spins < 2 || spins > 500) { IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL); } if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); } if (weights) { if (igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } use_weights=1; } if (coolfact < 0 || coolfact>=1.0) { IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL); } if (gamma < 0.0) { IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL); } if (starttemp/stoptemp<1.0) { IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp", IGRAPH_EINVAL); } /* Check whether we have a single component */ igraph_bool_t conn; IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (!conn) { IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); } net = new network; net->node_list =new DL_Indexed_List(); net->link_list =new DL_Indexed_List(); net->cluster_list=new DL_Indexed_List*>(); /* Transform the igraph_t */ IGRAPH_CHECK(igraph_i_read_network(graph, weights, net, use_weights, 0)); prob=2.0*net->sum_weights/double(net->node_list->Size()) /double(net->node_list->Size()-1); pm=new PottsModel(net,(unsigned int)spins,update_rule); /* initialize the random number generator */ RNG_BEGIN(); if ((stoptemp==0.0) && (starttemp==0.0)) zeroT=true; else zeroT=false; if (!zeroT) kT=pm->FindStartTemp(gamma, prob, starttemp); else kT=stoptemp; /* assign random initial configuration */ pm->assign_initial_conf(-1); runs=0; changes=1; while (changes>0 && (kT/stoptemp>1.0 || (zeroT && runs<150))) { IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ runs++; if (!zeroT) { kT*=coolfact; if (parupdate) { changes=pm->HeatBathParallelLookup(gamma, prob, kT, 50); } else { acc=pm->HeatBathLookup(gamma, prob, kT, 50); if (acc<(1.0-1.0/double(spins))*0.01) { changes=0; } else { changes=1; } } } else { if (parupdate) { changes=pm->HeatBathParallelLookupZeroTemp(gamma, prob, 50); } else { acc=pm->HeatBathLookupZeroTemp(gamma, prob, 50); /* less than 1 percent acceptance ratio */ if (acc<(1.0-1.0/double(spins))*0.01) { changes=0; } else { changes=1; } } } } /* while loop */ pm->WriteClusters(modularity, temperature, csize, membership, kT, gamma); while (net->link_list->Size()) delete net->link_list->Pop(); while (net->node_list->Size()) delete net->node_list->Pop(); while (net->cluster_list->Size()) { cl_cur=net->cluster_list->Pop(); while (cl_cur->Size()) cl_cur->Pop(); delete cl_cur; } delete net->link_list; delete net->node_list; delete net->cluster_list; RNG_END(); delete net; delete pm; return 0; } /** * \function igraph_community_spinglass_single * \brief Community of a single node based on statistical mechanics * * This function implements the community structure detection * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. It is * described in their paper: Statistical Mechanics of * Community Detection, http://arxiv.org/abs/cond-mat/0603718 . * * * This function calculates the community of a single vertex without * calculating all the communities in the graph. * * \param graph The input graph, it may be directed but the direction * of the edges is not used in the algorithm. * \param weights Pointer to a vector with the weights of the edges. * Alternatively \c NULL can be supplied to have the same weight * for every edge. * \param vertex The vertex id of the vertex of which ths community is * calculated. * \param community Pointer to an initialized vector, the result, the * ids of the vertices in the community of the input vertex will be * stored here. The vector will be resized as needed. * \param cohesion Pointer to a real variable, if not \c NULL the * cohesion index of the community will be stored here. * \param adhesion Pointer to a real variable, if not \c NULL the * adhesion index of the community will be stored here. * \param inner_links Pointer to an integer, if not \c NULL the * number of edges within the community is stored here. * \param outer_links Pointer to an integer, if not \c NULL the * number of edges between the community and the rest of the graph * will be stored here. * \param spins The number of spins to use, this can be higher than * the actual number of clusters in the network, in which case some * clusters will contain zero vertices. * \param update_rule The type of the update rule. Possible values: \c * IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c * IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defined * the null model based on which the actual clustering is done. If * this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph * (ie. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the * configuration model is used. The configuration means that the * baseline for the clustering is a random graph with the same * degree distribution as the input graph. * \param gamma Real number. The gamma parameter of the * algorithm. This defined the weight of the missing and existing * links in the quality function for the clustering. The default * value in the original code was 1.0, which is equal weight to * missing and existing edges. Smaller values make the existing * links contibute more to the energy function which is minimized * in the algorithm. Bigger values make the missing links more * important. (If my understanding is correct.) * \return Error code. * * \sa igraph_community_spinglass() for the traditional version of the * algorithm. * * Time complexity: TODO. */ int igraph_community_spinglass_single(const igraph_t *graph, const igraph_vector_t *weights, igraph_integer_t vertex, igraph_vector_t *community, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, igraph_integer_t *outer_links, igraph_integer_t spins, igraph_spincomm_update_t update_rule, igraph_real_t gamma) { igraph_bool_t use_weights=0; double prob; ClusterList *cl_cur; network *net; PottsModel *pm; char startnode[255]; /* Check arguments */ if (spins < 2 || spins > 500) { IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL); } if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); } if (weights) { if (igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } use_weights=1; } if (gamma < 0.0) { IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL); } if (vertex < 0 || vertex > igraph_vcount(graph)) { IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL); } /* Check whether we have a single component */ igraph_bool_t conn; IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (!conn) { IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); } net = new network; net->node_list =new DL_Indexed_List(); net->link_list =new DL_Indexed_List(); net->cluster_list=new DL_Indexed_List*>(); /* Transform the igraph_t */ IGRAPH_CHECK(igraph_i_read_network(graph, weights, net, use_weights, 0)); prob=2.0*net->sum_weights/double(net->node_list->Size()) /double(net->node_list->Size()-1); pm=new PottsModel(net,(unsigned int)spins,update_rule); /* initialize the random number generator */ RNG_BEGIN(); /* to be exected, if we want to find the community around a particular node*/ /* the initial conf is needed, because otherwise, the degree of the nodes is not in the weight property, stupid!!! */ pm->assign_initial_conf(-1); snprintf(startnode, 255, "%li", (long int)vertex+1); pm->FindCommunityFromStart(gamma, prob, startnode, community, cohesion, adhesion, inner_links, outer_links); while (net->link_list->Size()) delete net->link_list->Pop(); while (net->node_list->Size()) delete net->node_list->Pop(); while (net->cluster_list->Size()) { cl_cur=net->cluster_list->Pop(); while (cl_cur->Size()) cl_cur->Pop(); delete cl_cur; } delete net->link_list; delete net->node_list; delete net->cluster_list; RNG_END(); delete net; delete pm; return 0; } int igraph_i_community_spinglass_negative(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, igraph_real_t stoptemp, igraph_real_t coolfact, igraph_spincomm_update_t update_rule, igraph_real_t gamma, /* igraph_matrix_t *adhesion, */ /* igraph_matrix_t *normalised_adhesion, */ /* igraph_real_t *polarization, */ igraph_real_t gamma_minus) { unsigned long changes, runs; igraph_bool_t use_weights=0; bool zeroT; double kT, acc; ClusterList *cl_cur; network *net; PottsModelN *pm; igraph_real_t d_n; igraph_real_t d_p; /* Check arguments */ if (parupdate) { IGRAPH_ERROR("Parallel spin update not implemented with " "negative gamma", IGRAPH_UNIMPLEMENTED); } if (spins < 2 || spins > 500) { IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL); } if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); } if (weights) { if (igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } use_weights=1; } if (coolfact < 0 || coolfact>=1.0) { IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL); } if (gamma < 0.0) { IGRAPH_ERROR("Invalid gamma value", IGRAPH_EINVAL); } if (starttemp/stoptemp<1.0) { IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp", IGRAPH_EINVAL); } /* Check whether we have a single component */ igraph_bool_t conn; IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (!conn) { IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); } igraph_vector_minmax(weights, &d_n, &d_p); if (d_n > 0) { d_n=0; } if (d_p < 0) { d_p=0; } d_n = -d_n; net = new network; net->node_list =new DL_Indexed_List(); net->link_list =new DL_Indexed_List(); net->cluster_list=new DL_Indexed_List*>(); /* Transform the igraph_t */ IGRAPH_CHECK(igraph_i_read_network(graph, weights, net, use_weights, 0)); bool directed = igraph_is_directed(graph); pm=new PottsModelN(net,(unsigned int)spins, directed); /* initialize the random number generator */ RNG_BEGIN(); if ((stoptemp==0.0) && (starttemp==0.0)) zeroT=true; else zeroT=false; //Begin at a high enough temperature kT=pm->FindStartTemp(gamma, gamma_minus, starttemp); /* assign random initial configuration */ pm->assign_initial_conf(true); runs=0; changes=1; acc = 0; while (changes>0 && (kT/stoptemp>1.0 || (zeroT && runs<150))) { IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ runs++; kT = kT*coolfact; acc=pm->HeatBathLookup(gamma, gamma_minus, kT, 50); if (acc<(1.0-1.0/double(spins))*0.001) changes=0; else changes=1; } /* while loop */ /* These are needed, otherwise 'modularity' is not calculated */ igraph_matrix_t adhesion, normalized_adhesion; igraph_real_t polarization; IGRAPH_MATRIX_INIT_FINALLY(&adhesion, 0, 0); IGRAPH_MATRIX_INIT_FINALLY(&normalized_adhesion, 0, 0); pm->WriteClusters(modularity, temperature, csize, membership, &adhesion, &normalized_adhesion, &polarization, kT, d_p, d_n, gamma, gamma_minus); igraph_matrix_destroy(&normalized_adhesion); igraph_matrix_destroy(&adhesion); IGRAPH_FINALLY_CLEAN(2); while (net->link_list->Size()) delete net->link_list->Pop(); while (net->node_list->Size()) delete net->node_list->Pop(); while (net->cluster_list->Size()) { cl_cur=net->cluster_list->Pop(); while (cl_cur->Size()) cl_cur->Pop(); delete cl_cur; } RNG_END(); return 0; } igraph/src/foreign-gml-lexer.l0000644000176000001440000000616212325372071016052 0ustar ripleyusers/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-gml-header.h" #include "foreign-gml-parser.h" #define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); %} %option noyywrap %option prefix="igraph_gml_yy" %option outfile="lex.yy.c" %option nounput %option noinput %option reentrant %option bison-bridge %option bison-locations digit [0-9] whitespace [ \r\n\t] %% ^#[^\n\r]*[\n]|[\r] { /* comments ignored */ } \"[^\"]*\" { return STRING; } \-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } [a-zA-Z_][a-zA-Z_0-9]* { return KEYWORD; } \[ { return LISTOPEN; } \] { return LISTCLOSE; } \n\r|\r\n|\r|\n { } {whitespace} { /* other whitespace ignored */ } <> { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return EOFF; } } %% igraph/src/glpnet08.c0000644000176000001440000001654512325527073014171 0ustar ripleyusers/* glpnet08.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Two subroutines sub() and wclique() below are intended to find a * maximum weight clique in a given undirected graph. These subroutines * are slightly modified version of the program WCLIQUE developed by * Patric Ostergard and based * on ideas from the article "P. R. J. Ostergard, A new algorithm for * the maximum-weight clique problem, submitted for publication", which * in turn is a generalization of the algorithm for unweighted graphs * presented in "P. R. J. Ostergard, A fast algorithm for the maximum * clique problem, submitted for publication". * * USED WITH PERMISSION OF THE AUTHOR OF THE ORIGINAL CODE. * * Changes were made by Andrew Makhorin . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif #include "glpenv.h" #include "glpnet.h" /*********************************************************************** * NAME * * wclique - find maximum weight clique with Ostergard's algorithm * * SYNOPSIS * * int wclique(int n, const int w[], const unsigned char a[], * int ind[]); * * DESCRIPTION * * The routine wclique finds a maximum weight clique in an undirected * graph with Ostergard's algorithm. * * INPUT PARAMETERS * * n is the number of vertices, n > 0. * * w[i], i = 1,...,n, is a weight of vertex i. * * a[*] is the strict (without main diagonal) lower triangle of the * graph adjacency matrix in packed format. * * OUTPUT PARAMETER * * ind[k], k = 1,...,size, is the number of a vertex included in the * clique found, 1 <= ind[k] <= n, where size is the number of vertices * in the clique returned on exit. * * RETURNS * * The routine returns the clique size, i.e. the number of vertices in * the clique. */ struct csa { /* common storage area */ int n; /* number of vertices */ const int *wt; /* int wt[0:n-1]; */ /* weights */ const unsigned char *a; /* adjacency matrix (packed lower triangle without main diag.) */ int record; /* weight of best clique */ int rec_level; /* number of vertices in best clique */ int *rec; /* int rec[0:n-1]; */ /* best clique so far */ int *clique; /* int clique[0:n-1]; */ /* table for pruning */ int *set; /* int set[0:n-1]; */ /* current clique */ }; #define n (csa->n) #define wt (csa->wt) #define a (csa->a) #define record (csa->record) #define rec_level (csa->rec_level) #define rec (csa->rec) #define clique (csa->clique) #define set (csa->set) #if 0 static int is_edge(struct csa *csa, int i, int j) { /* if there is arc (i,j), the routine returns true; otherwise false; 0 <= i, j < n */ int k; xassert(0 <= i && i < n); xassert(0 <= j && j < n); if (i == j) return 0; if (i < j) k = i, i = j, j = k; k = (i * (i - 1)) / 2 + j; return a[k / CHAR_BIT] & (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); } #else #define is_edge(csa, i, j) ((i) == (j) ? 0 : \ (i) > (j) ? is_edge1(i, j) : is_edge1(j, i)) #define is_edge1(i, j) is_edge2(((i) * ((i) - 1)) / 2 + (j)) #define is_edge2(k) (a[(k) / CHAR_BIT] & \ (unsigned char)(1 << ((CHAR_BIT - 1) - (k) % CHAR_BIT))) #endif static void sub(struct csa *csa, int ct, int table[], int level, int weight, int l_weight) { int i, j, k, curr_weight, left_weight, *p1, *p2, *newtable; newtable = xcalloc(n, sizeof(int)); if (ct <= 0) { /* 0 or 1 elements left; include these */ if (ct == 0) { set[level++] = table[0]; weight += l_weight; } if (weight > record) { record = weight; rec_level = level; for (i = 0; i < level; i++) rec[i] = set[i]; } goto done; } for (i = ct; i >= 0; i--) { if ((level == 0) && (i < ct)) goto done; k = table[i]; if ((level > 0) && (clique[k] <= (record - weight))) goto done; /* prune */ set[level] = k; curr_weight = weight + wt[k]; l_weight -= wt[k]; if (l_weight <= (record - curr_weight)) goto done; /* prune */ p1 = newtable; p2 = table; left_weight = 0; while (p2 < table + i) { j = *p2++; if (is_edge(csa, j, k)) { *p1++ = j; left_weight += wt[j]; } } if (left_weight <= (record - curr_weight)) continue; sub(csa, p1 - newtable - 1, newtable, level + 1, curr_weight, left_weight); } done: xfree(newtable); return; } int wclique(int _n, const int w[], const unsigned char _a[], int ind[]) { struct csa _csa, *csa = &_csa; int i, j, p, max_wt, max_nwt, wth, *used, *nwt, *pos; glp_long timer; n = _n; xassert(n > 0); wt = &w[1]; a = _a; record = 0; rec_level = 0; rec = &ind[1]; clique = xcalloc(n, sizeof(int)); set = xcalloc(n, sizeof(int)); used = xcalloc(n, sizeof(int)); nwt = xcalloc(n, sizeof(int)); pos = xcalloc(n, sizeof(int)); /* start timer */ timer = xtime(); /* order vertices */ for (i = 0; i < n; i++) { nwt[i] = 0; for (j = 0; j < n; j++) if (is_edge(csa, i, j)) nwt[i] += wt[j]; } for (i = 0; i < n; i++) used[i] = 0; for (i = n-1; i >= 0; i--) { max_wt = -1; max_nwt = -1; for (j = 0; j < n; j++) { if ((!used[j]) && ((wt[j] > max_wt) || (wt[j] == max_wt && nwt[j] > max_nwt))) { max_wt = wt[j]; max_nwt = nwt[j]; p = j; } } pos[i] = p; used[p] = 1; for (j = 0; j < n; j++) if ((!used[j]) && (j != p) && (is_edge(csa, p, j))) nwt[j] -= wt[p]; } /* main routine */ wth = 0; for (i = 0; i < n; i++) { wth += wt[pos[i]]; sub(csa, i, pos, 0, 0, wth); clique[pos[i]] = record; if (xdifftime(xtime(), timer) >= 5.0 - 0.001) { /* print current record and reset timer */ xprintf("level = %d (%d); best = %d\n", i+1, n, record); timer = xtime(); } } xfree(clique); xfree(set); xfree(used); xfree(nwt); xfree(pos); /* return the solution found */ for (i = 1; i <= rec_level; i++) ind[i]++; return rec_level; } #undef n #undef wt #undef a #undef record #undef rec_level #undef rec #undef clique #undef set /* eof */ igraph/src/infomap_Greedy.h0000644000176000001440000000413112325527073015451 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GREEDY_H #define GREEDY_H #include #include #include #include #include "igraph_random.h" #include "infomap_Node.h" #include "infomap_FlowGraph.h" class Greedy { public: Greedy(FlowGraph * fgraph); // initialise les attributs par rapport au graph ~Greedy(); void setMove(int *moveTo); //virtual void determMove(int *moveTo); bool optimize(); //virtual void move(bool &moved); void apply(bool sort); //virtual void level(Node ***, bool sort); void tune(void); /**************************************************************************/ FlowGraph * graph; int Nnode; double exit; double exitFlow; double exit_log_exit; double size_log_size; double nodeSize_log_nodeSize; double codeLength; double alpha,beta; // local copy of fgraph alpha, beta (=alpha - Nnode = graph->Nnode;1) vector node_index; // module number of each node int Nempty; vector mod_empty; vector mod_exit; // version tmp de node vector mod_size; vector mod_danglingSize; vector mod_teleportWeight; vector mod_members; }; void delete_Greedy(Greedy *greedy); #endif igraph/src/glpios05.c0000644000176000001440000002373412325527073014170 0ustar ripleyusers/* glpios05.c (Gomory's mixed integer cut generator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpios.h" /*********************************************************************** * NAME * * ios_gmi_gen - generate Gomory's mixed integer cuts. * * SYNOPSIS * * #include "glpios.h" * void ios_gmi_gen(glp_tree *tree, IOSPOOL *pool); * * DESCRIPTION * * The routine ios_gmi_gen generates Gomory's mixed integer cuts for * the current point and adds them to the cut pool. */ #define MAXCUTS 50 /* maximal number of cuts to be generated for one round */ struct worka { /* Gomory's cut generator working area */ int *ind; /* int ind[1+n]; */ double *val; /* double val[1+n]; */ double *phi; /* double phi[1+m+n]; */ }; #define f(x) ((x) - floor(x)) /* compute fractional part of x */ static void gen_cut(glp_tree *tree, struct worka *worka, int j) { /* this routine tries to generate Gomory's mixed integer cut for specified structural variable x[m+j] of integer kind, which is basic and has fractional value in optimal solution to current LP relaxation */ glp_prob *mip = tree->mip; int m = mip->m; int n = mip->n; int *ind = worka->ind; double *val = worka->val; double *phi = worka->phi; int i, k, len, kind, stat; double lb, ub, alfa, beta, ksi, phi1, rhs; /* compute row of the simplex tableau, which (row) corresponds to specified basic variable xB[i] = x[m+j]; see (23) */ len = glp_eval_tab_row(mip, m+j, ind, val); /* determine beta[i], which a value of xB[i] in optimal solution to current LP relaxation; note that this value is the same as if it would be computed with formula (27); it is assumed that beta[i] is fractional enough */ beta = mip->col[j]->prim; /* compute cut coefficients phi and right-hand side rho, which correspond to formula (30); dense format is used, because rows of the simplex tableau is usually dense */ for (k = 1; k <= m+n; k++) phi[k] = 0.0; rhs = f(beta); /* initial value of rho; see (28), (32) */ for (j = 1; j <= len; j++) { /* determine original number of non-basic variable xN[j] */ k = ind[j]; xassert(1 <= k && k <= m+n); /* determine the kind, bounds and current status of xN[j] in optimal solution to LP relaxation */ if (k <= m) { /* auxiliary variable */ GLPROW *row = mip->row[k]; kind = GLP_CV; lb = row->lb; ub = row->ub; stat = row->stat; } else { /* structural variable */ GLPCOL *col = mip->col[k-m]; kind = col->kind; lb = col->lb; ub = col->ub; stat = col->stat; } /* xN[j] cannot be basic */ xassert(stat != GLP_BS); /* determine row coefficient ksi[i,j] at xN[j]; see (23) */ ksi = val[j]; /* if ksi[i,j] is too large in the magnitude, do not generate the cut */ if (fabs(ksi) > 1e+05) goto fini; /* if ksi[i,j] is too small in the magnitude, skip it */ if (fabs(ksi) < 1e-10) goto skip; /* compute row coefficient alfa[i,j] at y[j]; see (26) */ switch (stat) { case GLP_NF: /* xN[j] is free (unbounded) having non-zero ksi[i,j]; do not generate the cut */ goto fini; case GLP_NL: /* xN[j] has active lower bound */ alfa = - ksi; break; case GLP_NU: /* xN[j] has active upper bound */ alfa = + ksi; break; case GLP_NS: /* xN[j] is fixed; skip it */ goto skip; default: xassert(stat != stat); } /* compute cut coefficient phi'[j] at y[j]; see (21), (28) */ switch (kind) { case GLP_IV: /* y[j] is integer */ if (fabs(alfa - floor(alfa + 0.5)) < 1e-10) { /* alfa[i,j] is close to nearest integer; skip it */ goto skip; } else if (f(alfa) <= f(beta)) phi1 = f(alfa); else phi1 = (f(beta) / (1.0 - f(beta))) * (1.0 - f(alfa)); break; case GLP_CV: /* y[j] is continuous */ if (alfa >= 0.0) phi1 = + alfa; else phi1 = (f(beta) / (1.0 - f(beta))) * (- alfa); break; default: xassert(kind != kind); } /* compute cut coefficient phi[j] at xN[j] and update right- hand side rho; see (31), (32) */ switch (stat) { case GLP_NL: /* xN[j] has active lower bound */ phi[k] = + phi1; rhs += phi1 * lb; break; case GLP_NU: /* xN[j] has active upper bound */ phi[k] = - phi1; rhs -= phi1 * ub; break; default: xassert(stat != stat); } skip: ; } /* now the cut has the form sum_k phi[k] * x[k] >= rho, where cut coefficients are stored in the array phi in dense format; x[1,...,m] are auxiliary variables, x[m+1,...,m+n] are struc- tural variables; see (30) */ /* eliminate auxiliary variables in order to express the cut only through structural variables; see (33) */ for (i = 1; i <= m; i++) { GLPROW *row; GLPAIJ *aij; if (fabs(phi[i]) < 1e-10) continue; /* auxiliary variable x[i] has non-zero cut coefficient */ row = mip->row[i]; /* x[i] cannot be fixed */ xassert(row->type != GLP_FX); /* substitute x[i] = sum_j a[i,j] * x[m+j] */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) phi[m+aij->col->j] += phi[i] * aij->val; } /* convert the final cut to sparse format and substitute fixed (structural) variables */ len = 0; for (j = 1; j <= n; j++) { GLPCOL *col; if (fabs(phi[m+j]) < 1e-10) continue; /* structural variable x[m+j] has non-zero cut coefficient */ col = mip->col[j]; if (col->type == GLP_FX) { /* eliminate x[m+j] */ rhs -= phi[m+j] * col->lb; } else { len++; ind[len] = j; val[len] = phi[m+j]; } } if (fabs(rhs) < 1e-12) rhs = 0.0; /* if the cut inequality seems to be badly scaled, reject it to avoid numeric difficulties */ for (k = 1; k <= len; k++) { if (fabs(val[k]) < 1e-03) goto fini; if (fabs(val[k]) > 1e+03) goto fini; } /* add the cut to the cut pool for further consideration */ #if 0 ios_add_cut_row(tree, pool, GLP_RF_GMI, len, ind, val, GLP_LO, rhs); #else glp_ios_add_row(tree, NULL, GLP_RF_GMI, 0, len, ind, val, GLP_LO, rhs); #endif fini: return; } struct var { int j; double f; }; static int fcmp(const void *p1, const void *p2) { const struct var *v1 = p1, *v2 = p2; if (v1->f > v2->f) return -1; if (v1->f < v2->f) return +1; return 0; } void ios_gmi_gen(glp_tree *tree) { /* main routine to generate Gomory's cuts */ glp_prob *mip = tree->mip; int m = mip->m; int n = mip->n; struct var *var; int k, nv, j, size; struct worka _worka, *worka = &_worka; /* allocate working arrays */ var = xcalloc(1+n, sizeof(struct var)); worka->ind = xcalloc(1+n, sizeof(int)); worka->val = xcalloc(1+n, sizeof(double)); worka->phi = xcalloc(1+m+n, sizeof(double)); /* build the list of integer structural variables, which are basic and have fractional value in optimal solution to current LP relaxation */ nv = 0; for (j = 1; j <= n; j++) { GLPCOL *col = mip->col[j]; double frac; if (col->kind != GLP_IV) continue; if (col->type == GLP_FX) continue; if (col->stat != GLP_BS) continue; frac = f(col->prim); if (!(0.05 <= frac && frac <= 0.95)) continue; /* add variable to the list */ nv++, var[nv].j = j, var[nv].f = frac; } /* order the list by descending fractionality */ qsort(&var[1], nv, sizeof(struct var), fcmp); /* try to generate cuts by one for each variable in the list, but not more than MAXCUTS cuts */ size = glp_ios_pool_size(tree); for (k = 1; k <= nv; k++) { if (glp_ios_pool_size(tree) - size >= MAXCUTS) break; gen_cut(tree, worka, var[k].j); } /* free working arrays */ xfree(var); xfree(worka->ind); xfree(worka->val); xfree(worka->phi); return; } /* eof */ igraph/src/foreign-gml-header.h0000644000176000001440000000176712325527073016171 0ustar ripleyusers/* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge MA, 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_gml_tree.h" typedef struct { void *scanner; int eof; char errmsg[300]; igraph_gml_tree_t *tree; } igraph_i_gml_parsedata_t; igraph/src/glpapi03.c0000644000176000001440000001215712325527073014142 0ustar ripleyusers/* glpapi03.c (row and column searching routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" /*********************************************************************** * NAME * * glp_create_index - create the name index * * SYNOPSIS * * void glp_create_index(glp_prob *lp); * * DESCRIPTION * * The routine glp_create_index creates the name index for the * specified problem object. The name index is an auxiliary data * structure, which is intended to quickly (i.e. for logarithmic time) * find rows and columns by their names. * * This routine can be called at any time. If the name index already * exists, the routine does nothing. */ void glp_create_index(glp_prob *lp) { GLPROW *row; GLPCOL *col; int i, j; /* create row name index */ if (lp->r_tree == NULL) { lp->r_tree = avl_create_tree(avl_strcmp, NULL); for (i = 1; i <= lp->m; i++) { row = lp->row[i]; xassert(row->node == NULL); if (row->name != NULL) { row->node = avl_insert_node(lp->r_tree, row->name); avl_set_node_link(row->node, row); } } } /* create column name index */ if (lp->c_tree == NULL) { lp->c_tree = avl_create_tree(avl_strcmp, NULL); for (j = 1; j <= lp->n; j++) { col = lp->col[j]; xassert(col->node == NULL); if (col->name != NULL) { col->node = avl_insert_node(lp->c_tree, col->name); avl_set_node_link(col->node, col); } } } return; } /*********************************************************************** * NAME * * glp_find_row - find row by its name * * SYNOPSIS * * int glp_find_row(glp_prob *lp, const char *name); * * RETURNS * * The routine glp_find_row returns the ordinal number of a row, * which is assigned (by the routine glp_set_row_name) the specified * symbolic name. If no such row exists, the routine returns 0. */ int glp_find_row(glp_prob *lp, const char *name) { AVLNODE *node; int i = 0; if (lp->r_tree == NULL) xerror("glp_find_row: row name index does not exist\n"); if (!(name == NULL || name[0] == '\0' || strlen(name) > 255)) { node = avl_find_node(lp->r_tree, name); if (node != NULL) i = ((GLPROW *)avl_get_node_link(node))->i; } return i; } /*********************************************************************** * NAME * * glp_find_col - find column by its name * * SYNOPSIS * * int glp_find_col(glp_prob *lp, const char *name); * * RETURNS * * The routine glp_find_col returns the ordinal number of a column, * which is assigned (by the routine glp_set_col_name) the specified * symbolic name. If no such column exists, the routine returns 0. */ int glp_find_col(glp_prob *lp, const char *name) { AVLNODE *node; int j = 0; if (lp->c_tree == NULL) xerror("glp_find_col: column name index does not exist\n"); if (!(name == NULL || name[0] == '\0' || strlen(name) > 255)) { node = avl_find_node(lp->c_tree, name); if (node != NULL) j = ((GLPCOL *)avl_get_node_link(node))->j; } return j; } /*********************************************************************** * NAME * * glp_delete_index - delete the name index * * SYNOPSIS * * void glp_delete_index(glp_prob *lp); * * DESCRIPTION * * The routine glp_delete_index deletes the name index previously * created by the routine glp_create_index and frees the memory * allocated to this auxiliary data structure. * * This routine can be called at any time. If the name index does not * exist, the routine does nothing. */ void glp_delete_index(glp_prob *lp) { int i, j; /* delete row name index */ if (lp->r_tree != NULL) { for (i = 1; i <= lp->m; i++) lp->row[i]->node = NULL; avl_delete_tree(lp->r_tree), lp->r_tree = NULL; } /* delete column name index */ if (lp->c_tree != NULL) { for (j = 1; j <= lp->n; j++) lp->col[j]->node = NULL; avl_delete_tree(lp->c_tree), lp->c_tree = NULL; } return; } /* eof */ igraph/src/prpack.cpp0000644000176000001440000000642312325527074014343 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "prpack.h" #include "prpack/prpack_igraph_graph.h" #include "prpack/prpack_solver.h" #include "igraph_error.h" using namespace prpack; using namespace std; /* * PRPACK-based implementation of \c igraph_personalized_pagerank. * * See \c igraph_personalized_pagerank for the documentation of the parameters. */ int igraph_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vector_t *reset, const igraph_vector_t *weights) { long int i, no_of_nodes = igraph_vcount(graph), nodes_to_calc; igraph_vit_t vit; double* u = 0; double* v = 0; const prpack_result* res; if (reset) { /* Normalize reset vector so the sum is 1 */ double reset_sum = igraph_vector_sum(reset); if (igraph_vector_min(reset) < 0) { IGRAPH_ERROR("the reset vector must not contain negative elements", IGRAPH_EINVAL); } if (reset_sum == 0) { IGRAPH_ERROR("the sum of the elements in the reset vector must not be zero", IGRAPH_EINVAL); } // Construct the personalization vector v = new double[no_of_nodes]; for (i = 0; i < no_of_nodes; i++) { v[i] = VECTOR(*reset)[i] / reset_sum; } } // Construct and run the solver prpack_igraph_graph prpack_graph(graph, weights, directed); prpack_solver solver(&prpack_graph, false); res = solver.solve(damping, 1e-10, u, v, ""); // Check whether the solver converged // TODO: this is commented out because some of the solvers do not implement it yet /* if (!res->converged) { IGRAPH_WARNING("PRPACK solver failed to converge. Results may be inaccurate."); } */ // Fill the result vector IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc=IGRAPH_VIT_SIZE(vit); IGRAPH_CHECK(igraph_vector_resize(vector, nodes_to_calc)); for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { VECTOR(*vector)[i] = res->x[i]; } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); // TODO: can we get the eigenvalue? We'll just fake it until we can. if (value) { *value = 1.0; } delete res; return IGRAPH_SUCCESS; } igraph/src/dnconv.f0000644000176000001440000001003712325527073014010 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdnconv c c\Description: c Convergence testing for the nonsymmetric Arnoldi eigenvalue routine. c c\Usage: c call igraphdnconv c ( N, RITZR, RITZI, BOUNDS, TOL, NCONV ) c c\Arguments c N Integer. (INPUT) c Number of Ritz values to check for convergence. c c RITZR, Double precision arrays of length N. (INPUT) c RITZI Real and imaginary parts of the Ritz values to be checked c for convergence. c BOUNDS Double precision array of length N. (INPUT) c Ritz estimates for the Ritz values in RITZR and RITZI. c c TOL Double precision scalar. (INPUT) c Desired backward error for a Ritz value to be considered c "converged". c c NCONV Integer scalar. (OUTPUT) c Number of "converged" Ritz values. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c igraphsecond ARPACK utility routine for timing. c dlamch LAPACK routine that determines machine constants. c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.1' c c\SCCS Information: @(#) c FILE: nconv.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 c c\Remarks c 1. xxxx c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdnconv (n, ritzr, ritzi, bounds, tol, nconv) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c integer n, nconv Double precision & tol c c %-----------------% c | Array Arguments | c %-----------------% Double precision & ritzr(n), ritzi(n), bounds(n) c c %---------------% c | Local Scalars | c %---------------% c integer i Double precision & temp, eps23 c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlapy2, dlamch external dlapy2, dlamch c %-----------------------% c | Executable Statements | c %-----------------------% c c %-------------------------------------------------------------% c | Convergence test: unlike in the symmetric code, I am not | c | using things like refined error bounds and gap condition | c | because I don't know the exact equivalent concept. | c | | c | Instead the i-th Ritz value is considered "converged" when: | c | | c | bounds(i) .le. ( TOL * | ritz | ) | c | | c | for some appropriate choice of norm. | c %-------------------------------------------------------------% c call igraphsecond (t0) c c %---------------------------------% c | Get machine dependent constant. | c %---------------------------------% c eps23 = dlamch('Epsilon-Machine') eps23 = eps23**(2.0D+0 / 3.0D+0) c nconv = 0 do 20 i = 1, n temp = max( eps23, dlapy2( ritzr(i), ritzi(i) ) ) if (bounds(i) .le. tol*temp) nconv = nconv + 1 20 continue c call igraphsecond (t1) tnconv = tnconv + (t1 - t0) c return c c %---------------% c | End of igraphdnconv | c %---------------% c end igraph/src/igraph_pmt.h0000644000176000001440000000715412325527073014663 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define CONCAT2x(a,b) a ## _ ## b #define CONCAT2(a,b) CONCAT2x(a,b) #define CONCAT3x(a,b,c) a ## _ ## b ## _ ## c #define CONCAT3(a,b,c) CONCAT3x(a,b,c) #define CONCAT4x(a,b,c,d) a ## _ ## b ## _ ## c ## _ ## d #define CONCAT4(a,b,c,d) CONCAT4x(a,b,c,d) #if defined(BASE_IGRAPH_REAL) #define BASE igraph_real_t #define SHORT #define OUT_FORMAT "%G" #define PRINTFUNC(val) igraph_real_printf(val) #define FPRINTFUNC(file, val) igraph_real_fprintf(file, val) #define ZERO 0.0 #define ONE 1.0 #define MULTIPLICITY 1 #elif defined(BASE_LONG) #define BASE long #define SHORT long #define OUT_FORMAT "%ld" #define ZERO 0L #define ONE 1L #define MULTIPLICITY 1 #elif defined(BASE_CHAR) #define BASE char #define SHORT char #define OUT_FORMAT "%d" #define ZERO 0 #define ONE 1 #define MULTIPLICITY 1 #elif defined(BASE_BOOL) #define BASE igraph_bool_t #define SHORT bool #define OUT_FORMAT "%d" #define ZERO 0 #define ONE 1 #define MULTIPLICITY 1 #elif defined(BASE_INT) #define BASE int #define SHORT int #define OUT_FORMAT "%d" #define ZERO 0 #define ONE 1 #define MULTIPLICITY 1 #elif defined(BASE_LIMB) #define BASE limb_t #define SHORT limb #define ZERO 0 #define ONE 1 #define MULTIPLICITY 1 #define UNSIGNED 1 #elif defined(BASE_PTR) #define BASE void* #define SHORT ptr #define ZERO 0 #define MULTIPLICITY 1 #elif defined(BASE_COMPLEX) #undef complex #define BASE igraph_complex_t #define SHORT complex #define ZERO igraph_complex(0,0) #define ONE {{1.0,0.0}} #define MULTIPLICITY 2 #define NOTORDERED 1 #define NOABS 1 #define SUM(a,b,c) ((a) = igraph_complex_add((b),(c))) #define DIFF(a,b,c) ((a) = igraph_complex_sub((b),(c))) #define PROD(a,b,c) ((a) = igraph_complex_mul((b),(c))) #define DIV(a,b,c) ((a) = igraph_complex_div((b),(c))) #define EQ(a,b) IGRAPH_COMPLEX_EQ((a),(b)) #define SQ(a) IGRAPH_REAL(igraph_complex_mul((a),(a))) #else #error unknown BASE_ directive #endif #if defined(BASE_IGRAPH_REAL) # define FUNCTION(dir,name) CONCAT2(dir,name) # define TYPE(dir) CONCAT2(dir,t) #elif defined(BASE_BOOL) /* Special case because stdbool.h defines bool as a macro to _Bool which would * screw things up */ # define FUNCTION(a,c) CONCAT3x(a,bool,c) # define TYPE(dir) CONCAT3x(dir,bool,t) #else # define FUNCTION(a,c) CONCAT3(a,SHORT,c) # define TYPE(dir) CONCAT3(dir,SHORT,t) #endif #if defined(HEAP_TYPE_MIN) #define HEAPMORE < #define HEAPMOREEQ <= #define HEAPLESS > #define HEAPLESSEQ >= #undef FUNCTION #undef TYPE #if defined(BASE_IGRAPH_REAL) #define FUNCTION(dir,name) CONCAT3(dir,min,name) #define TYPE(dir) CONCAT3(dir,min,t) #else #define FUNCTION(a,c) CONCAT4(a,min,SHORT,c) #define TYPE(dir) CONCAT4(dir,min,SHORT,t) #endif #endif #if defined(HEAP_TYPE_MAX) #define HEAPMORE > #define HEAPMOREEQ >= #define HEAPLESS < #define HEAPLESSEQ <= #endif igraph/src/igraph_hashtable.c0000644000176000001440000001000712325527073016000 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_memory.h" #include "igraph_error.h" #include "config.h" #include int igraph_hashtable_init(igraph_hashtable_t *ht) { IGRAPH_CHECK(igraph_trie_init(&ht->keys, 1)); IGRAPH_FINALLY(igraph_trie_destroy, &ht->keys); IGRAPH_CHECK(igraph_strvector_init(&ht->elements, 0)); IGRAPH_FINALLY(igraph_trie_destroy, &ht->elements); IGRAPH_CHECK(igraph_strvector_init(&ht->defaults, 0)); IGRAPH_FINALLY_CLEAN(2); return 0; } void igraph_hashtable_destroy(igraph_hashtable_t *ht) { igraph_trie_destroy(&ht->keys); igraph_strvector_destroy(&ht->elements); igraph_strvector_destroy(&ht->defaults); } /* Note: may leave the hash table in an inconsistent state if a new element is added, but this is not a big problem, since while the defaults, or the defaults plus the elements may contain more elements than the keys trie, but the data is always retrieved based on the trie */ int igraph_hashtable_addset(igraph_hashtable_t *ht, const char *key, const char *def, const char *elem){ long int size=igraph_trie_size(&ht->keys); long int newid; IGRAPH_CHECK(igraph_trie_get(&ht->keys, key, &newid)); if (newid==size) { /* this is a new element */ IGRAPH_CHECK(igraph_strvector_resize(&ht->defaults, newid+1)); IGRAPH_CHECK(igraph_strvector_resize(&ht->elements, newid+1)); IGRAPH_CHECK(igraph_strvector_set(&ht->defaults, newid, def)); IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, elem)); } else { /* set an already existing element */ IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, elem)); } return 0; } /* Previous comment also applies here */ int igraph_hashtable_addset2(igraph_hashtable_t *ht, const char *key, const char *def, const char *elem, int elemlen) { long int size=igraph_trie_size(&ht->keys); long int newid; char *tmp; IGRAPH_CHECK(igraph_trie_get(&ht->keys, key, &newid)); tmp=igraph_Calloc(elemlen+1, char); if (tmp==0) { IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp); strncpy(tmp, elem, elemlen); tmp[elemlen]='\0'; if (newid==size) { IGRAPH_CHECK(igraph_strvector_resize(&ht->defaults, newid+1)); IGRAPH_CHECK(igraph_strvector_resize(&ht->elements, newid+1)); IGRAPH_CHECK(igraph_strvector_set(&ht->defaults, newid, def)); IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, tmp)); } else { IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, tmp)); } igraph_Free(tmp); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_hashtable_get(igraph_hashtable_t *ht, const char *key, char **elem) { long int newid; IGRAPH_CHECK(igraph_trie_get(&ht->keys, key, &newid)); igraph_strvector_get(&ht->elements, newid, elem); return 0; } int igraph_hashtable_reset(igraph_hashtable_t *ht) { igraph_strvector_destroy(&ht->elements); IGRAPH_CHECK(igraph_strvector_copy(&ht->elements, &ht->defaults)); return 0; } int igraph_hashtable_getkeys(igraph_hashtable_t *ht, const igraph_strvector_t **sv) { return igraph_trie_getkeys(&ht->keys, sv); } igraph/src/glpenv02.c0000644000176000001440000000410512325527073014152 0ustar ripleyusers/* glpenv02.c (thread local storage) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpenv.h" static void *tls = NULL; /* in a re-entrant version of the package this variable must be placed in the Thread Local Storage (TLS) */ /*********************************************************************** * NAME * * tls_set_ptr - store global pointer in TLS * * SYNOPSIS * * #include "glpenv.h" * void tls_set_ptr(void *ptr); * * DESCRIPTION * * The routine tls_set_ptr stores a pointer specified by the parameter * ptr in the Thread Local Storage (TLS). */ void tls_set_ptr(void *ptr) { tls = ptr; return; } /*********************************************************************** * NAME * * tls_get_ptr - retrieve global pointer from TLS * * SYNOPSIS * * #include "glpenv.h" * void *tls_get_ptr(void); * * RETURNS * * The routine tls_get_ptr returns a pointer previously stored by the * routine tls_set_ptr. If the latter has not been called yet, NULL is * returned. */ void *tls_get_ptr(void) { void *ptr; ptr = tls; return ptr; } /* eof */ igraph/src/prpack_igraph_graph.cpp0000644000176000001440000000702112325527074017051 0ustar ripleyusers#include "prpack_igraph_graph.h" #include #include using namespace prpack; using namespace std; #ifdef PRPACK_IGRAPH_SUPPORT prpack_igraph_graph::prpack_igraph_graph(const igraph_t* g, const igraph_vector_t* weights, igraph_bool_t directed) { const igraph_bool_t treat_as_directed = igraph_is_directed(g) && directed; igraph_es_t es; igraph_eit_t eit; igraph_vector_t neis; long int i, j, eid, sum, temp, num_ignored_es; int *p_head, *p_head_copy; double* p_weight; // Get the number of vertices and edges. For undirected graphs, we add // an edge in both directions. num_vs = igraph_vcount(g); num_es = igraph_ecount(g); num_self_es = 0; if (!treat_as_directed) { num_es *= 2; } // Allocate memory for heads and tails p_head = heads = new int[num_es]; tails = new int[num_vs]; memset(tails, 0, num_vs * sizeof(tails[0])); // Allocate memory for weights if needed if (weights != 0) { p_weight = vals = new double[num_es]; } // Count the number of ignored edges (those with negative or zero weight) num_ignored_es = 0; if (treat_as_directed) { // Select all the edges and iterate over them by the source vertices es = igraph_ess_all(IGRAPH_EDGEORDER_TO); // Add the edges igraph_eit_create(g, es, &eit); while (!IGRAPH_EIT_END(eit)) { eid = IGRAPH_EIT_GET(eit); IGRAPH_EIT_NEXT(eit); // Handle the weight if (weights != 0) { // Does this edge have zero or negative weight? if (VECTOR(*weights)[eid] <= 0) { // Ignore it. num_ignored_es++; continue; } *p_weight = VECTOR(*weights)[eid]; ++p_weight; } *p_head = IGRAPH_FROM(g, eid); ++p_head; ++tails[IGRAPH_TO(g, eid)]; if (IGRAPH_FROM(g, eid) == IGRAPH_TO(g, eid)) { ++num_self_es; } } igraph_eit_destroy(&eit); } else { // Select all the edges and iterate over them by the target vertices igraph_vector_init(&neis, 0); for (i = 0; i < num_vs; i++) { igraph_incident(g, &neis, i, IGRAPH_ALL); temp = igraph_vector_size(&neis); // TODO: should loop edges be added in both directions? p_head_copy = p_head; for (j = 0; j < temp; j++) { if (weights != 0) { if (VECTOR(*weights)[(long int)VECTOR(neis)[j]] <= 0) { // Ignore num_ignored_es++; continue; } *p_weight = VECTOR(*weights)[(long int)VECTOR(neis)[j]]; ++p_weight; } *p_head = IGRAPH_OTHER(g, VECTOR(neis)[j], i); if (i == *p_head) { num_self_es++; } ++p_head; } tails[i] = p_head - p_head_copy; } igraph_vector_destroy(&neis); } // Decrease num_es by the number of ignored edges num_es -= num_ignored_es; // Finalize the tails vector for (i = 0, sum = 0; i < num_vs; ++i) { temp = sum; sum += tails[i]; tails[i] = temp; } // Normalize the weights normalize_weights(); // Debug /* printf("Heads:"); for (i = 0; i < num_es; ++i) { printf(" %d", heads[i]); } printf("\n"); printf("Tails:"); for (i = 0; i < num_vs; ++i) { printf(" %d", tails[i]); } printf("\n"); if (vals) { printf("Vals:"); for (i = 0; i < num_es; ++i) { printf(" %.4f", vals[i]); } printf("\n"); } printf("===========================\n"); */ } // PRPACK_IGRAPH_SUPPORT #endif igraph/src/community.c0000644000176000001440000035527012325527072014554 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_community.h" #include "igraph_constructors.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_arpack.h" #include "igraph_arpack_internal.h" #include "igraph_adjlist.h" #include "igraph_interface.h" #include "igraph_interrupt_internal.h" #include "igraph_components.h" #include "igraph_dqueue.h" #include "igraph_progress.h" #include "igraph_stack.h" #include "igraph_spmatrix.h" #include "igraph_statusbar.h" #include "igraph_types_internal.h" #include "igraph_conversion.h" #include "igraph_centrality.h" #include "config.h" #include #include int igraph_i_rewrite_membership_vector(igraph_vector_t *membership) { long int no=(long int) igraph_vector_max(membership)+1; igraph_vector_t idx; long int realno=0; long int i; long int len=igraph_vector_size(membership); IGRAPH_VECTOR_INIT_FINALLY(&idx, no); for (i=0; i=0; i--) { long int edge=(long int) VECTOR(*edges)[i]; long int from=IGRAPH_FROM(graph, edge); long int to=IGRAPH_TO(graph, edge); long int c1=(long int) VECTOR(mymembership)[from]; long int c2=(long int) VECTOR(mymembership)[to]; igraph_real_t actmod; long int j; if (c1 != c2) { /* this is a merge */ if (res) { MATRIX(*res, midx, 0)=c1; MATRIX(*res, midx, 1)=c2; } if (bridges) { VECTOR(*bridges)[midx]=i+1; } /* The new cluster has id no_of_nodes+midx+1 */ for (j=0; j maxmod) { maxmod=actmod; if (membership) { igraph_vector_update(membership, &mymembership); } } } midx++; } } if (membership) { IGRAPH_CHECK(igraph_i_rewrite_membership_vector(membership)); } igraph_vector_destroy(&mymembership); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_community_eb_get_merges * \brief Calculating the merges, ie. the dendrogram for an edge betweenness community structure * * * This function is handy if you have a sequence of edge which are * gradually removed from the network and you would like to know how * the network falls apart into separate components. The edge sequence * may come from the \ref igraph_community_edge_betweenness() * function, but this is not necessary. Note that \ref * igraph_community_edge_betweenness can also calculate the * dendrogram, via its \p merges argument. * * \param graph The input graph. * \param edges Vector containing the edges to be removed from the * network, all edges are expected to appear exactly once in the * vector. * \param weights An optional vector containing edge weights. If null, * the unweighted modularity scores will be calculated. If not null, * the weighted modularity scores will be calculated. Ignored if both * \p modularity and \p membership are nulls. * \param res Pointer to an initialized matrix, if not NULL then the * dendrogram will be stored here, in the same form as for the \ref * igraph_community_walktrap() function: the matrix has two columns * and each line is a merge given by the ids of the merged * components. The component ids are number from zero and * component ids smaller than the number of vertices in the graph * belong to individual vertices. The non-trivial components * containing at least two vertices are numbered from \c n, \c n is * the number of vertices in the graph. So if the first line * contains \c a and \c b that means that components \c a and \c b * are merged into component \c n, the second line creates * component \c n+1, etc. The matrix will be resized as needed. * \param bridges Pointer to an initialized vector or NULL. If not * null then the index of the edge removals which split the network * will be stored here. The vector will be resized as needed. * \param modularity If not a null pointer, then the modularity values * for the different divisions, corresponding to the merges matrix, * will be stored here. * \param membership If not a null pointer, then the membership vector * for the best division (in terms of modularity) will be stored * here. * \return Error code. * * \sa \ref igraph_community_edge_betweenness(). * * Time complexity: O(|E|+|V|log|V|), |V| is the number of vertices, * |E| is the number of edges. */ int igraph_community_eb_get_merges(const igraph_t *graph, const igraph_vector_t *edges, const igraph_vector_t *weights, igraph_matrix_t *res, igraph_vector_t *bridges, igraph_vector_t *modularity, igraph_vector_t *membership) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t ptr; long int i, midx=0; igraph_integer_t no_comps; if (membership || modularity) { return igraph_i_community_eb_get_merges2(graph, edges, weights, res, bridges, modularity, membership); } IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, IGRAPH_WEAK)); IGRAPH_VECTOR_INIT_FINALLY(&ptr, no_of_nodes*2-1); if (res) { IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes-no_comps, 2)); } if (bridges) { IGRAPH_CHECK(igraph_vector_resize(bridges, no_of_nodes-no_comps)); } for (i=igraph_vector_size(edges)-1; i>=0; i--) { igraph_integer_t edge=(igraph_integer_t) VECTOR(*edges)[i]; igraph_integer_t from, to, c1, c2, idx; igraph_edge(graph, edge, &from, &to); idx=from+1; while (VECTOR(ptr)[idx-1] != 0) { idx=(igraph_integer_t) VECTOR(ptr)[idx-1]; } c1=idx-1; idx=to+1; while (VECTOR(ptr)[idx-1] != 0) { idx=(igraph_integer_t) VECTOR(ptr)[idx-1]; } c2=idx-1; if (c1 != c2) { /* this is a merge */ if (res) { MATRIX(*res, midx, 0)=c1; MATRIX(*res, midx, 1)=c2; } if (bridges) { VECTOR(*bridges)[midx]=i+1; } VECTOR(ptr)[c1]=no_of_nodes+midx+1; VECTOR(ptr)[c2]=no_of_nodes+midx+1; VECTOR(ptr)[from]=no_of_nodes+midx+1; VECTOR(ptr)[to]=no_of_nodes+midx+1; midx++; } } igraph_vector_destroy(&ptr); IGRAPH_FINALLY_CLEAN(1); return 0; } /* Find the smallest active element in the vector */ long int igraph_i_vector_which_max_not_null(const igraph_vector_t *v, const char *passive) { long int which, i=0, size=igraph_vector_size(v); igraph_real_t max; while (passive[i]) { i++; } which=i; max=VECTOR(*v)[which]; for (i++; i max) { max=elem; which=i; } } return which; } /** * \function igraph_community_edge_betweenness * \brief Community finding based on edge betweenness * * Community structure detection based on the betweenness of the edges * in the network. The algorithm was invented by M. Girvan and * M. Newman, see: M. Girvan and M. E. J. Newman: Community structure in * social and biological networks, Proc. Nat. Acad. Sci. USA 99, 7821-7826 * (2002). * * * The idea is that the betweenness of the edges connecting two * communities is typically high, as many of the shortest paths * between nodes in separate communities go through them. So we * gradually remove the edge with highest betweenness from the * network, and recalculate edge betweenness after every removal. * This way sooner or later the network falls off to two components, * then after a while one of these components falls off to two smaller * components, etc. until all edges are removed. This is a divisive * hierarchical approach, the result is a dendrogram. * \param graph The input graph. * \param result Pointer to an initialized vector, the result will be * stored here, the ids of the removed edges in the order of their * removal. It will be resized as needed. It may be NULL if * the edge IDs are not needed by the caller. * \param edge_betweenness Pointer to an initialized vector or * NULL. In the former case the edge betweenness of the removed * edge is stored here. The vector will be resized as needed. * \param merges Pointer to an initialized matrix or NULL. If not NULL * then merges performed by the algorithm are stored here. Even if * this is a divisive algorithm, we can replay it backwards and * note which two clusters were merged. Clusters are numbered from * zero, see the \p merges argument of \ref * igraph_community_walktrap() for details. The matrix will be * resized as needed. * \param bridges Pointer to an initialized vector of NULL. If not * NULL then all edge removals which separated the network into * more components are marked here. * \param modularity If not a null pointer, then the modularity values * of the different divisions are stored here, in the order * corresponding to the merge matrix. The modularity values will * take weights into account if \p weights is not null. * \param membership If not a null pointer, then the membership vector, * corresponding to the highest modularity value, is stored here. * \param directed Logical constant, whether to calculate directed * betweenness (ie. directed paths) for directed graphs. It is * ignored for undirected graphs. * \param weights An optional vector containing edge weights. If null, * the unweighted edge betweenness scores will be calculated and * used. If not null, the weighted edge betweenness scores will be * calculated and used. * \return Error code. * * \sa \ref igraph_community_eb_get_merges(), \ref * igraph_community_spinglass(), \ref igraph_community_walktrap(). * * Time complexity: O(|V||E|^2), as the betweenness calculation requires * O(|V||E|) and we do it |E|-1 times. * * \example examples/simple/igraph_community_edge_betweenness.c */ int igraph_community_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, igraph_vector_t *edge_betweenness, igraph_matrix_t *merges, igraph_vector_t *bridges, igraph_vector_t *modularity, igraph_vector_t *membership, igraph_bool_t directed, const igraph_vector_t *weights) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); double *distance, *tmpscore; unsigned long long int *nrgeo; long int source, i, e; igraph_inclist_t elist_out, elist_in, fathers; igraph_inclist_t *elist_out_p, *elist_in_p; igraph_vector_t *neip; long int neino; igraph_vector_t eb; long int maxedge, pos; igraph_integer_t from, to; igraph_bool_t result_owned = 0; igraph_stack_t stack=IGRAPH_STACK_NULL; igraph_real_t steps, steps_done; char *passive; /* Needed only for the unweighted case */ igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; /* Needed only for the weighted case */ igraph_2wheap_t heap; if (result == 0) { result = igraph_Calloc(1, igraph_vector_t); if (result == 0) IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, result); IGRAPH_VECTOR_INIT_FINALLY(result, 0); result_owned = 1; } directed=directed && igraph_is_directed(graph); if (directed) { IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN)); IGRAPH_FINALLY(igraph_inclist_destroy, &elist_in); elist_out_p=&elist_out; elist_in_p=&elist_in; } else { IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); elist_out_p=elist_in_p=&elist_out; } distance=igraph_Calloc(no_of_nodes, double); if (distance==0) { IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, distance); nrgeo=igraph_Calloc(no_of_nodes, unsigned long long int); if (nrgeo==0) { IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nrgeo); tmpscore=igraph_Calloc(no_of_nodes, double); if (tmpscore==0) { IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmpscore); if (weights == 0) { IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); } else { if (igraph_vector_min(weights) <= 0) { IGRAPH_ERROR("weights must be strictly positive", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_2wheap_init(&heap, no_of_nodes)); IGRAPH_FINALLY(igraph_2wheap_destroy, &heap); IGRAPH_CHECK(igraph_inclist_init_empty(&fathers, (igraph_integer_t) no_of_nodes)); IGRAPH_FINALLY(igraph_inclist_destroy, &fathers); } IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); if (edge_betweenness) { IGRAPH_CHECK(igraph_vector_resize(edge_betweenness, no_of_edges)); VECTOR(*edge_betweenness)[no_of_edges-1]=0; } IGRAPH_VECTOR_INIT_FINALLY(&eb, no_of_edges); passive=igraph_Calloc(no_of_edges, char); if (!passive) { IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, passive); /* Estimate the number of steps to be taken. * It is assumed that one iteration is O(|E||V|), but |V| is constant * anyway, so we will have approximately |E|^2 / 2 steps, and one * iteration of the outer loop advances the step counter by the number * of remaining edges at that iteration. */ steps = no_of_edges / 2.0 * (no_of_edges+1); steps_done = 0; for (e=0; e * Many community detection algorithms return with a \em merges * matrix, \ref igraph_community_walktrap() and \ref * igraph_community_edge_betweenness() are two examples. The matrix * contains the merge operations performed while mapping the * hierarchical structure of a network. If the matrix has \c n-1 rows, * where \c n is the number of vertices in the graph, then it contains * the hierarchical structure of the whole network and it is called a * dendrogram. * * * This function performs \p steps merge operations as prescribed by * the \p merges matrix and returns the current state of the network. * * * If \p merges is not a complete dendrogram, it is possible to * take \p steps steps if \p steps is not bigger than the number * lines in \p merges. * \param merges The two-column matrix containing the merge * operations. See \ref igraph_community_walktrap() for the * detailed syntax. * \param nodes The number of leaf nodes in the dendrogram * \param steps Integer constant, the number of steps to take. * \param membership Pointer to an initialized vector, the membership * results will be stored here, if not NULL. The vector will be * resized as needed. * \param csize Pointer to an initialized vector, or NULL. If not NULL * then the sizes of the components will be stored here, the vector * will be resized as needed. * * \sa \ref igraph_community_walktrap(), \ref * igraph_community_edge_betweenness(), \ref * igraph_community_fastgreedy() for community structure detection * algorithms. * * Time complexity: O(|V|), the number of vertices in the graph. */ int igraph_community_to_membership(const igraph_matrix_t *merges, igraph_integer_t nodes, igraph_integer_t steps, igraph_vector_t *membership, igraph_vector_t *csize) { long int no_of_nodes=nodes; long int components=no_of_nodes-steps; long int i, found=0; igraph_vector_t tmp; if (steps > igraph_matrix_nrow(merges)) { IGRAPH_ERROR("`steps' to big or `merges' matrix too short", IGRAPH_EINVAL); } if (membership) { IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); igraph_vector_null(membership); } if (csize) { IGRAPH_CHECK(igraph_vector_resize(csize, components)); igraph_vector_null(csize); } IGRAPH_VECTOR_INIT_FINALLY(&tmp, steps); for (i=steps-1; i>=0; i--) { long int c1=(long int) MATRIX(*merges, i, 0); long int c2=(long int) MATRIX(*merges, i, 1); /* new component? */ if (VECTOR(tmp)[i]==0) { found++; VECTOR(tmp)[i]=found; } if (c1 * Modularity on weighted graphs is also meaningful. When taking edge * weights into account, `Aij' becomes the weight of the corresponding * edge (or 0 if there is no edge), `ki' is the total weight of edges * incident on vertex `i', `kj' is the total weight of edges incident * on vertex `j' and `m' is the total weight of all edges. * * * See also Clauset, A.; Newman, M. E. J.; Moore, C. Finding * community structure in very large networks, Physical Review E, * 2004, 70, 066111. * \param graph The input graph. * \param membership Numeric vector which gives the type of each * vertex, ie. the component to which it belongs. * It does not have to be consecutive, i.e. empty communities are * allowed. * \param modularity Pointer to a real number, the result will be * stored here. * \param weights Weight vector or NULL if no weights are specified. * \return Error code. * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. */ int igraph_modularity(const igraph_t *graph, const igraph_vector_t *membership, igraph_real_t *modularity, const igraph_vector_t *weights) { igraph_vector_t e, a; long int types=(long int) igraph_vector_max(membership)+1; long int no_of_edges=igraph_ecount(graph); long int i; igraph_integer_t from, to; igraph_real_t m; long int c1, c2; if (igraph_vector_size(membership) < igraph_vcount(graph)) { IGRAPH_ERROR("cannot calculate modularity, membership vector too short", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&e, types); IGRAPH_VECTOR_INIT_FINALLY(&a, types); if (weights) { if (igraph_vector_size(weights) < no_of_edges) IGRAPH_ERROR("cannot calculate modularity, weight vector too short", IGRAPH_EINVAL); m=igraph_vector_sum(weights); for (i=0; i 0) { for (i=0; i * The function documented in these section implements the * leading eigenvector method developed by Mark Newman and * published in MEJ Newman: Finding community structure using the * eigenvectors of matrices, Phys Rev E 74:036104 (2006). * * * The heart of the method is the definition of the modularity matrix, * B, which is B=A-P, A being the adjacency matrix of the (undirected) * network, and P contains the probability that certain edges are * present according to the configuration model In * other words, a Pij element of P is the probability that there is an * edge between vertices i and j in a random network in which the * degrees of all vertices are the same as in the input graph. * * * The leading eigenvector method works by calculating the eigenvector * of the modularity matrix for the largest positive eigenvalue and * then separating vertices into two community based on the sign of * the corresponding element in the eigenvector. If all elements in * the eigenvector are of the same sign that means that the network * has no underlying community structure. * Check Newman's paper to understand why this is a good method for * detecting community structure. * * * The leading eigenvector community structure detection method is * implemented in \ref igraph_community_leading_eigenvector(). * After the initial split, the following splits are done in a * way to optimize modularity regarding to the original network. * * * * \example examples/simple/igraph_community_leading_eigenvector.c * */ typedef struct igraph_i_community_leading_eigenvector_data_t { igraph_vector_t *idx; igraph_vector_t *idx2; igraph_adjlist_t *adjlist; igraph_inclist_t *inclist; igraph_vector_t *tmp; long int no_of_edges; igraph_vector_t *mymembership; long int comm; const igraph_vector_t *weights; const igraph_t *graph; igraph_vector_t *strength; igraph_real_t sumweights; } igraph_i_community_leading_eigenvector_data_t; int igraph_i_community_leading_eigenvector(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_community_leading_eigenvector_data_t *data=extra; long int j, k, nlen, size=n; igraph_vector_t *idx=data->idx; igraph_vector_t *idx2=data->idx2; igraph_vector_t *tmp=data->tmp; igraph_adjlist_t *adjlist=data->adjlist; igraph_real_t ktx, ktx2; long int no_of_edges=data->no_of_edges; igraph_vector_t *mymembership=data->mymembership; long int comm=data->comm; /* Ax */ for (j=0; jidx; igraph_vector_t *idx2=data->idx2; igraph_vector_t *tmp=data->tmp; igraph_adjlist_t *adjlist=data->adjlist; igraph_real_t ktx, ktx2; long int no_of_edges=data->no_of_edges; igraph_vector_t *mymembership=data->mymembership; long int comm=data->comm; /* Ax */ for (j=0; jidx; igraph_vector_t *idx2=data->idx2; igraph_vector_t *tmp=data->tmp; igraph_inclist_t *inclist=data->inclist; igraph_real_t ktx, ktx2; igraph_vector_t *mymembership=data->mymembership; long int comm=data->comm; const igraph_vector_t *weights=data->weights; const igraph_t *graph=data->graph; igraph_vector_t *strength=data->strength; igraph_real_t sw=data->sumweights; /* Ax */ for (j=0; jidx; igraph_vector_t *idx2=data->idx2; igraph_vector_t *tmp=data->tmp; igraph_inclist_t *inclist=data->inclist; igraph_real_t ktx, ktx2; igraph_vector_t *mymembership=data->mymembership; long int comm=data->comm; const igraph_vector_t *weights=data->weights; const igraph_t *graph=data->graph; igraph_vector_t *strength=data->strength; igraph_real_t sw=data->sumweights; /* Ax */ for (j=0; jp communities, * then these are numbered from zero to p-1. The * first line of the matrix contains the first merge * (which is in reality the last split) of two communities into * community p, the merge in the second line forms * community p+1, etc. The matrix should be * initialized before calling and will be resized as needed. * This argument is ignored of it is \c NULL. * \param membership The membership of the vertices after all the * splits were performed will be stored here. The vector must be * initialized before calling and will be resized as needed. * This argument is ignored if it is \c NULL. This argument can * also be used to supply a starting configuration for the community * finding, in the format of a membership vector. In this case the * \p start argument must be set to 1. * \param steps The maximum number of steps to perform. It might * happen that some component (or the whole network) has no * underlying community structure and no further steps can be * done. If you want as many steps as possible then supply the * number of vertices in the network here. * \param options The options for ARPACK. \c n is always * overwritten. \c ncv is set to at least 4. * \param modularity If not a null pointer, then it must be a pointer * to a real number and the modularity score of the final division * is stored here. * \param start Boolean, whether to use the community structure given * in the \p membership argument as a starting point. * \param eigenvalues Pointer to an initialized vector or a null * pointer. If not a null pointer, then the eigenvalues calculated * along the community structure detection are stored here. The * non-positive eigenvalues, that do not result a split, are stored * as well. * \param eigenvectors If not a null pointer, then the eigenvectors * that are calculated in each step of the algorithm, are stored here, * in a pointer vector. Each eigenvector is stored in an * \ref igraph_vector_t object. The user is responsible of * deallocating the memory that belongs to the individual vectors, * by calling first \ref igraph_vector_destroy(), and then * free() on them. * \param history Pointer to an initialized vector or a null pointer. * If not a null pointer, then a trace of the algorithm is stored * here, encoded numerically. The various operations: * \clist * \cli IGRAPH_LEVC_HIST_START_FULL * Start the algorithm from an initial state where each connected * component is a separate community. * \cli IGRAPH_LEVC_HIST_START_GIVEN * Start the algorithm from a given community structure. The next * value in the vector contains the initial number of * communities. * \cli IGRAPH_LEVC_HIST_SPLIT * Split a community into two communities. The id of the splitted * community is given in the next element of the history vector. * The id of the first new community is the same as the id of the * splitted community. The id of the second community equals to * the number of communities before the split. * \cli IGRAPH_LEVC_HIST_FAILED * Tried to split a community, but it was not worth it, as it * does not result in a bigger modularity value. The id of the * community is given in the next element of the vector. * \endclist * \param callback A null pointer or a function of type \ref * igraph_community_leading_eigenvector_callback_t. If given, this * callback function is called after each eigenvector/eigenvalue * calculation. If the callback returns a non-zero value, then the * community finding algorithm stops. See the arguments passed to * the callback at the documentation of \ref * igraph_community_leading_eigenvector_callback_t. * \param callback_extra Extra argument to pass to the callback * function. * \return Error code. * * \sa \ref igraph_community_walktrap() and \ref * igraph_community_spinglass() for other community structure * detection methods. * * Time complexity: O(|E|+|V|^2*steps), |V| is the number of vertices, * |E| the number of edges, steps the number of splits * performed. */ int igraph_community_leading_eigenvector(const igraph_t *graph, const igraph_vector_t *weights, igraph_matrix_t *merges, igraph_vector_t *membership, igraph_integer_t steps, igraph_arpack_options_t *options, igraph_real_t *modularity, igraph_bool_t start, igraph_vector_t *eigenvalues, igraph_vector_ptr_t *eigenvectors, igraph_vector_t *history, igraph_community_leading_eigenvector_callback_t *callback, void *callback_extra) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_dqueue_t tosplit; igraph_vector_t idx, idx2, mymerges; igraph_vector_t strength, tmp; long int staken=0; igraph_adjlist_t adjlist; igraph_inclist_t inclist; long int i, j, k, l; long int communities; igraph_vector_t vmembership, *mymembership=membership; igraph_i_community_leading_eigenvector_data_t extra; igraph_arpack_storage_t storage; igraph_real_t mod=0; igraph_arpack_function_t *arpcb1 = weights ? igraph_i_community_leading_eigenvector_weighted : igraph_i_community_leading_eigenvector; igraph_arpack_function_t *arpcb2 = weights ? igraph_i_community_leading_eigenvector2_weighted : igraph_i_community_leading_eigenvector2; igraph_real_t sumweights=0.0; if (weights && no_of_edges != igraph_vector_size(weights)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } if (start && !membership) { IGRAPH_ERROR("Cannot start from given configuration if memberships " "missing", IGRAPH_EINVAL); } if (start && membership && igraph_vector_size(membership) != no_of_nodes) { IGRAPH_ERROR("Wrong length for vector of predefined memberships", IGRAPH_EINVAL); } if (start && membership && igraph_vector_max(membership) >= no_of_nodes) { IGRAPH_WARNING("Too many communities in membership start vector"); } if (igraph_is_directed(graph)) { IGRAPH_WARNING("This method was developed for undirected graphs"); } if (steps < 0 || steps > no_of_nodes-1) { steps=(igraph_integer_t) no_of_nodes-1; } if (!membership) { mymembership=&vmembership; IGRAPH_VECTOR_INIT_FINALLY(mymembership, 0); } IGRAPH_VECTOR_INIT_FINALLY(&mymerges, 0); IGRAPH_CHECK(igraph_vector_reserve(&mymerges, steps*2)); IGRAPH_VECTOR_INIT_FINALLY(&idx, 0); if (eigenvalues) { igraph_vector_clear(eigenvalues); } if (eigenvectors) { igraph_vector_ptr_clear(eigenvectors); IGRAPH_FINALLY(igraph_i_levc_free, eigenvectors); } IGRAPH_STATUS("Starting leading eigenvector method.\n", 0); if (!start) { /* Calculate the weakly connected components in the graph and use them as * an initial split */ IGRAPH_CHECK(igraph_clusters(graph, mymembership, &idx, 0, IGRAPH_WEAK)); communities = igraph_vector_size(&idx); IGRAPH_STATUSF(("Starting from %li component(s).\n", 0, communities)); if (history) { IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_START_FULL)); } } else { /* Just create the idx vector for the given membership vector */ communities=(long int) igraph_vector_max(mymembership)+1; IGRAPH_STATUSF(("Starting from given membership vector with %li " "communities.\n", 0, communities)); if (history) { IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_START_GIVEN)); IGRAPH_CHECK(igraph_vector_push_back(history, communities)); } IGRAPH_CHECK(igraph_vector_resize(&idx, communities)); igraph_vector_null(&idx); for (i=0; i 2) { igraph_dqueue_push(&tosplit, i); } } for (i=1; incv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ options->start = 0; options->which[0]='L'; options->which[1]='A'; /* Memory for ARPACK */ /* We are allocating memory for 20 eigenvectors since options->ncv won't be * larger than 20 when using automatic mode in igraph_arpack_rssolve */ IGRAPH_CHECK(igraph_arpack_storage_init(&storage, (int) no_of_nodes, 20, (int) no_of_nodes, 1)); IGRAPH_FINALLY(igraph_arpack_storage_destroy, &storage); extra.idx=&idx; extra.idx2=&idx2; extra.tmp=&tmp; extra.adjlist=&adjlist; extra.inclist=&inclist; extra.weights=weights; extra.sumweights=sumweights; extra.graph=graph; extra.strength=&strength; extra.no_of_edges=no_of_edges; extra.mymembership=mymembership; while (!igraph_dqueue_empty(&tosplit) && staken < steps) { long int comm=(long int) igraph_dqueue_pop_back(&tosplit); /* depth first search */ long int size=0; igraph_real_t tmpev; IGRAPH_STATUSF(("Trying to split community %li... ", 0, comm)); IGRAPH_ALLOW_INTERRUPTION(); for (i=0; in=(int) size-1; options->info=0; options->nev=1; options->ldv=0; options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ options->nconv = 0; options->lworkl = 0; /* we surely have enough space */ extra.comm=comm; /* We try calling the solver twice, once from a random starting point, once from a fixed one. This is because for some hard cases it tends to fail. We need to suppress error handling for the first call. */ { int i; igraph_error_handler_t *errh= igraph_set_error_handler(igraph_i_error_handler_none); igraph_warning_handler_t *warnh= igraph_set_warning_handler(igraph_warning_handler_ignore); igraph_arpack_rssolve(arpcb2, &extra, options, &storage, /*values=*/ 0, /*vectors=*/ 0); igraph_set_error_handler(errh); igraph_set_warning_handler(warnh); if (options->nconv < 1) { /* Call again, from a fixed starting point */ options->start=1; options->info=0; options->ncv=0; options->lworkl = 0; /* we surely have enough space */ for (i=0; i < options->n ; i++) { storage.resid[i] = 1; } IGRAPH_CHECK(igraph_arpack_rssolve(arpcb2, &extra, options, &storage, /*values=*/ 0, /*vectors=*/ 0)); options->start=0; } } if (options->nconv < 1) { IGRAPH_ERROR("ARPACK did not converge", IGRAPH_ARPACK_FAILED); } tmpev=storage.d[0]; /* Now we do the original eigenproblem, again, twice if needed */ options->n=(int) size; options->info=0; options->nev=1; options->ldv=0; options->nconv=0; options->lworkl = 0; /* we surely have enough space */ options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ { int i; igraph_error_handler_t *errh= igraph_set_error_handler(igraph_i_error_handler_none); igraph_arpack_rssolve(arpcb1, &extra, options, &storage, /*values=*/ 0, /*vectors=*/ 0); igraph_set_error_handler(errh); if (options->nconv < 1) { /* Call again from a fixed starting point */ options->start=1; options->info=0; options->ncv=0; options->lworkl = 0; /* we surely have enough space */ for (i=0; i < options->n; i++) { storage.resid[i] = 1; } IGRAPH_CHECK(igraph_arpack_rssolve(arpcb1, &extra, options, &storage, /*values=*/ 0, /*vectors=*/ 0)); options->start=0; } } if (options->nconv < 1) { IGRAPH_ERROR("ARPACK did not converge", IGRAPH_ARPACK_FAILED); } /* Ok, we have the leading eigenvector of the modularity matrix*/ /* ---------------------------------------------------------------*/ /* To avoid numeric errors */ if (fabs(storage.d[0]) < 1e-8) { storage.d[0] = 0; } /* We replace very small (in absolute value) elements of the leading eigenvector with zero, to get the same result, consistently.*/ for (i=0; i 1) { IGRAPH_CHECK(igraph_dqueue_push(&tosplit, communities-1)); } if (size-l > 1) { IGRAPH_CHECK(igraph_dqueue_push(&tosplit, comm)); } } igraph_arpack_storage_destroy(&storage); IGRAPH_FINALLY_CLEAN(1); if (!weights) { igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); } else { igraph_inclist_destroy(&inclist); igraph_vector_destroy(&strength); IGRAPH_FINALLY_CLEAN(2); } igraph_dqueue_destroy(&tosplit); igraph_vector_destroy(&tmp); igraph_vector_destroy(&idx2); IGRAPH_FINALLY_CLEAN(3); IGRAPH_STATUS("Done.\n", 0); /* reform the mymerges vector */ if (merges) { igraph_vector_null(&idx); l=igraph_vector_size(&mymerges); k=communities; j=0; IGRAPH_CHECK(igraph_matrix_resize(merges, l/2, 2)); for (i=l; i>0; i-=2) { long int from=(long int) VECTOR(mymerges)[i-1]; long int to=(long int) VECTOR(mymerges)[i-2]; MATRIX(*merges, j, 0)=VECTOR(mymerges)[i-2]; MATRIX(*merges, j, 1)=VECTOR(mymerges)[i-1]; if (VECTOR(idx)[from]!=0) { MATRIX(*merges, j, 1)=VECTOR(idx)[from]-1; } if (VECTOR(idx)[to]!=0) { MATRIX(*merges, j, 0)=VECTOR(idx)[to]-1; } VECTOR(idx)[to]=++k; j++; } } if (eigenvectors) { IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&idx); igraph_vector_destroy(&mymerges); IGRAPH_FINALLY_CLEAN(2); if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, mymembership, modularity, weights)); } if (!membership) { igraph_vector_destroy(mymembership); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_le_community_to_membership * Vertex membership from the leading eigenvector community structure * * This function creates a membership vector from the * result of \ref igraph_community_leading_eigenvector(), * It takes \c membership * and performs \c steps merges, according to the supplied * \c merges matrix. * \param merges The matrix defining the merges to make. * This is usually from the output of the leading eigenvector community * structure detection routines. * \param steps The number of steps to make according to \c merges. * \param membership Initially the starting membership vector, * on output the resulting membership vector, after performing \c steps merges. * \param csize Optionally the sizes of the communities is stored here, * if this is not a null pointer, but an initialized vector. * \return Error code. * * Time complexity: O(|V|), the number of vertices. */ int igraph_le_community_to_membership(const igraph_matrix_t *merges, igraph_integer_t steps, igraph_vector_t *membership, igraph_vector_t *csize) { long int no_of_nodes=igraph_vector_size(membership); igraph_vector_t fake_memb; long int components, i; if (igraph_matrix_nrow(merges) < steps) { IGRAPH_ERROR("`steps' to big or `merges' matrix too short", IGRAPH_EINVAL); } components=(long int) igraph_vector_max(membership)+1; if (components > no_of_nodes) { IGRAPH_ERROR("Invalid membership vector, too many components", IGRAPH_EINVAL); } if (steps >= components) { IGRAPH_ERROR("Cannot make `steps' steps from supplied membership vector", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&fake_memb, components); /* Check membership vector */ for (i=0; i * Weights are taken into account as follows: when the new label of node * i is determined, the algorithm iterates over all edges incident on * node i and calculate the total weight of edges leading to other * nodes with label 0, 1, 2, ..., k-1 (where k is the number of possible * labels). The new label of node i will then be the label whose edges * (among the ones incident on node i) have the highest total weight. * * \param graph The input graph, should be undirected to make sense. * \param membership The membership vector, the result is returned here. * For each vertex it gives the ID of its community (label). * \param weights The weight vector, it should contain a positive * weight for all the edges. * \param initial The initial state. If NULL, every vertex will have * a different label at the beginning. Otherwise it must be a vector * with an entry for each vertex. Non-negative values denote different * labels, negative entries denote vertices without labels. * \param fixed Boolean vector denoting which labels are fixed. Of course * this makes sense only if you provided an initial state, otherwise * this element will be ignored. Also note that vertices without labels * cannot be fixed. * \param modularity If not a null pointer, then it must be a pointer * to a real number. The modularity score of the detected community * structure is stored here. * \return Error code. * * Time complexity: O(m+n) * * \example examples/simple/igraph_community_label_propagation.c */ int igraph_community_label_propagation(const igraph_t *graph, igraph_vector_t *membership, const igraph_vector_t *weights, const igraph_vector_t *initial, igraph_vector_bool_t *fixed, igraph_real_t *modularity) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); long int no_of_not_fixed_nodes=no_of_nodes; long int i, j, k; igraph_adjlist_t al; igraph_inclist_t il; igraph_bool_t running = 1; igraph_vector_t label_counters, dominant_labels, nonzero_labels, node_order; /* The implementation uses a trick to avoid negative array indexing: * elements of the membership vector are increased by 1 at the start * of the algorithm; this to allow us to denote unlabeled vertices * (if any) by zeroes. The membership vector is shifted back in the end */ /* Do some initial checks */ if (fixed && igraph_vector_bool_size(fixed) != no_of_nodes) { IGRAPH_ERROR("Invalid fixed labeling vector length", IGRAPH_EINVAL); } if (weights) { if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } else if (igraph_vector_min(weights) < 0) { IGRAPH_ERROR("Weights must be non-negative", IGRAPH_EINVAL); } } if (fixed && !initial) { IGRAPH_WARNING("Ignoring fixed vertices as no initial labeling given"); } IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); if (initial) { if (igraph_vector_size(initial) != no_of_nodes) { IGRAPH_ERROR("Invalid initial labeling vector length", IGRAPH_EINVAL); } /* Check if the labels used are valid, initialize membership vector */ for (i=0; i no_of_nodes) { IGRAPH_ERROR("elements of the initial labeling vector must be between 0 and |V|-1", IGRAPH_EINVAL); } if (i <= 0) { IGRAPH_ERROR("at least one vertex must be labeled in the initial labeling", IGRAPH_EINVAL); } } else { for (i=0; i 0) { /* Select randomly from the dominant labels */ k = RNG_INTEGER(0, igraph_vector_size(&dominant_labels)-1); k = (long int) VECTOR(dominant_labels)[k]; /* Check if the _current_ label of the node is also dominant */ if (VECTOR(label_counters)[(long)VECTOR(*membership)[v1]]!=max_count) { /* Nope, we need at least one more iteration */ running = 1; } VECTOR(*membership)[v1] = k; } /* Clear the nonzero elements in label_counters */ num_neis = igraph_vector_size(&nonzero_labels); for (j = 0; j < num_neis; j++) { VECTOR(label_counters)[(long int)VECTOR(nonzero_labels)[j]] = 0; } } } RNG_END(); /* Shift back the membership vector, permute labels in increasing order */ /* We recycle label_counters here :) */ igraph_vector_fill(&label_counters, -1); j = 0; for (i=0; i= 0) { if (VECTOR(label_counters)[k] == -1) { /* We have seen this label for the first time */ VECTOR(label_counters)[k] = j; k = j; j++; } else { k = (long int) VECTOR(label_counters)[k]; } } else { /* This is an unlabeled vertex */ } VECTOR(*membership)[i] = k; } if (weights) igraph_inclist_destroy(&il); else igraph_adjlist_destroy(&al); IGRAPH_FINALLY_CLEAN(1); if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, membership, modularity, weights)); } igraph_vector_destroy(&node_order); igraph_vector_destroy(&label_counters); igraph_vector_destroy(&dominant_labels); igraph_vector_destroy(&nonzero_labels); IGRAPH_FINALLY_CLEAN(4); return 0; } /********************************************************************/ /* Structure storing a community */ typedef struct { igraph_integer_t size; /* Size of the community */ igraph_real_t weight_inside; /* Sum of edge weights inside community */ igraph_real_t weight_all; /* Sum of edge weights starting/ending in the community */ } igraph_i_multilevel_community; /* Global community list structure */ typedef struct { long int communities_no, vertices_no; /* Number of communities, number of vertices */ igraph_real_t weight_sum; /* Sum of edges weight in the whole graph */ igraph_i_multilevel_community *item; /* List of communities */ igraph_vector_t *membership; /* Community IDs */ igraph_vector_t *weights; /* Graph edge weights */ } igraph_i_multilevel_community_list; /* Computes the modularity of a community partitioning */ igraph_real_t igraph_i_multilevel_community_modularity( const igraph_i_multilevel_community_list *communities) { igraph_real_t result = 0; long int i; igraph_real_t m = communities->weight_sum; for (i = 0; i < communities->vertices_no; i++) { if (communities->item[i].size > 0) { result += (communities->item[i].weight_inside - communities->item[i].weight_all*communities->item[i].weight_all/m)/m; } } return result; } typedef struct { long int from; long int to; long int id; } igraph_i_multilevel_link; int igraph_i_multilevel_link_cmp(const void *a, const void *b) { long int r = (((igraph_i_multilevel_link*)a)->from - ((igraph_i_multilevel_link*)b)->from); if (r != 0) return (int) r; return (int) (((igraph_i_multilevel_link*)a)->to - ((igraph_i_multilevel_link*)b)->to); } /* removes multiple edges and returns new edge id's for each edge in |E|log|E| */ int igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_t *eids) { long int ecount = igraph_ecount(graph); long int i, l = -1, last_from = -1, last_to = -1; igraph_bool_t directed = igraph_is_directed(graph); igraph_integer_t from, to; igraph_vector_t edges; igraph_i_multilevel_link *links; /* Make sure there's enough space in eids to store the new edge IDs */ IGRAPH_CHECK(igraph_vector_resize(eids, ecount)); links = igraph_Calloc(ecount, igraph_i_multilevel_link); if (links == 0) { IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, links); for (i = 0; i < ecount; i++) { igraph_edge(graph, (igraph_integer_t) i, &from, &to); links[i].from = from; links[i].to = to; links[i].id = i; } qsort((void*)links, (size_t) ecount, sizeof(igraph_i_multilevel_link), igraph_i_multilevel_link_cmp); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); for (i = 0; i < ecount; i++) { if (links[i].from == last_from && links[i].to == last_to) { VECTOR(*eids)[links[i].id] = l; continue; } last_from = links[i].from; last_to = links[i].to; igraph_vector_push_back(&edges, last_from); igraph_vector_push_back(&edges, last_to); l++; VECTOR(*eids)[links[i].id] = l; } free(links); IGRAPH_FINALLY_CLEAN(1); igraph_destroy(graph); IGRAPH_CHECK(igraph_create(graph, &edges, igraph_vcount(graph), directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } typedef struct { long int community; igraph_real_t weight; } igraph_i_multilevel_community_link; int igraph_i_multilevel_community_link_cmp(const void *a, const void *b) { return (int) (((igraph_i_multilevel_community_link*)a)->community - ((igraph_i_multilevel_community_link*)b)->community); } /** * Given a graph, a community structure and a vertex ID, this method * calculates: * * - edges: the list of edge IDs that are incident on the vertex * - weight_all: the total weight of these edges * - weight_inside: the total weight of edges that stay within the same * community where the given vertex is right now, excluding loop edges * - weight_loop: the total weight of loop edges * - links_community and links_weight: together these two vectors list the * communities incident on this vertex and the total weight of edges * pointing to these communities */ int igraph_i_multilevel_community_links(const igraph_t *graph, const igraph_i_multilevel_community_list *communities, igraph_integer_t vertex, igraph_vector_t *edges, igraph_real_t *weight_all, igraph_real_t *weight_inside, igraph_real_t *weight_loop, igraph_vector_t *links_community, igraph_vector_t *links_weight) { long int i, n, last = -1, c = -1; igraph_real_t weight = 1; long int to, to_community; long int community = (long int) VECTOR(*(communities->membership))[(long int)vertex]; igraph_i_multilevel_community_link *links; *weight_all = *weight_inside = *weight_loop = 0; igraph_vector_clear(links_community); igraph_vector_clear(links_weight); /* Get the list of incident edges */ igraph_incident(graph, edges, vertex, IGRAPH_ALL); n = igraph_vector_size(edges); links = igraph_Calloc(n, igraph_i_multilevel_community_link); if (links == 0) { IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, links); for (i = 0; i < n; i++) { long int eidx = (long int) VECTOR(*edges)[i]; weight = VECTOR(*communities->weights)[eidx]; to = IGRAPH_OTHER(graph, eidx, vertex); *weight_all += weight; if (to == vertex) { *weight_loop += weight; links[i].community = community; links[i].weight = 0; continue; } to_community = (long int)VECTOR(*(communities->membership))[to]; if (community == to_community) *weight_inside += weight; /* debug("Link %ld (C: %ld) <-> %ld (C: %ld)\n", vertex, community, to, to_community); */ links[i].community = to_community; links[i].weight = weight; } /* Sort links by community ID and merge the same */ qsort((void*)links, (size_t) n, sizeof(igraph_i_multilevel_community_link), igraph_i_multilevel_community_link_cmp); for (i = 0; i < n; i++) { to_community = links[i].community; if (to_community != last) { igraph_vector_push_back(links_community, to_community); igraph_vector_push_back(links_weight, links[i].weight); last = to_community; c++; } else { VECTOR(*links_weight)[c] += links[i].weight; } } igraph_free(links); IGRAPH_FINALLY_CLEAN(1); return 0; } igraph_real_t igraph_i_multilevel_community_modularity_gain( const igraph_i_multilevel_community_list *communities, igraph_integer_t community, igraph_integer_t vertex, igraph_real_t weight_all, igraph_real_t weight_inside) { IGRAPH_UNUSED(vertex); return weight_inside - communities->item[(long int)community].weight_all*weight_all/communities->weight_sum; } /* Shrinks communities into single vertices, keeping all the edges. * This method is internal because it destroys the graph in-place and * creates a new one -- this is fine for the multilevel community * detection where a copy of the original graph is used anyway. * The membership vector will also be rewritten by the underlying * igraph_membership_reindex call */ int igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_t *membership) { igraph_vector_t edges; long int no_of_nodes = igraph_vcount(graph); long int no_of_edges = igraph_ecount(graph); igraph_bool_t directed = igraph_is_directed(graph); long int i; igraph_eit_t eit; if (no_of_nodes == 0) return 0; if (igraph_vector_size(membership) < no_of_nodes) { IGRAPH_ERROR("cannot shrink graph, membership vector too short", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); IGRAPH_CHECK(igraph_reindex_membership(membership, 0)); /* Create the new edgelist */ igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit); IGRAPH_FINALLY(igraph_eit_destroy, &eit); i = 0; while (!IGRAPH_EIT_END(eit)) { igraph_integer_t from, to; IGRAPH_CHECK(igraph_edge(graph, IGRAPH_EIT_GET(eit), &from, &to)); VECTOR(edges)[i++] = VECTOR(*membership)[(long int) from]; VECTOR(edges)[i++] = VECTOR(*membership)[(long int) to]; IGRAPH_EIT_NEXT(eit); } igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); /* Create the new graph */ igraph_destroy(graph); no_of_nodes = (long int) igraph_vector_max(membership)+1; IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup communities * \function igraph_i_community_multilevel_step * \brief Performs a single step of the multi-level modularity optimization method * * This function implements a single step of the multi-level modularity optimization * algorithm for finding community structure, see VD Blondel, J-L Guillaume, * R Lambiotte and E Lefebvre: Fast unfolding of community hierarchies in large * networks, http://arxiv.org/abs/0803.0476 for the details. * * This function was contributed by Tom Gregorovic. * * \param graph The input graph. It must be an undirected graph. * \param weights Numeric vector containing edge weights. If \c NULL, every edge * has equal weight. The weights are expected to be non-negative. * \param membership The membership vector, the result is returned here. * For each vertex it gives the ID of its community. * \param modularity The modularity of the partition is returned here. * \c NULL means that the modularity is not needed. * \return Error code. * * Time complexity: in average near linear on sparse graphs. */ int igraph_i_community_multilevel_step(igraph_t *graph, igraph_vector_t *weights, igraph_vector_t *membership, igraph_real_t *modularity) { long int i, j; long int vcount = igraph_vcount(graph); long int ecount = igraph_ecount(graph); igraph_integer_t ffrom, fto; igraph_real_t q, pass_q; int pass; igraph_bool_t changed = 0; igraph_vector_t links_community; igraph_vector_t links_weight; igraph_vector_t edges; igraph_vector_t temp_membership; igraph_i_multilevel_community_list communities; /* Initial sanity checks on the input parameters */ if (igraph_is_directed(graph)) { IGRAPH_ERROR("multi-level community detection works for undirected graphs only", IGRAPH_UNIMPLEMENTED); } if (igraph_vector_size(weights) < igraph_ecount(graph)) IGRAPH_ERROR("multi-level community detection: weight vector too short", IGRAPH_EINVAL); if (igraph_vector_any_smaller(weights, 0)) IGRAPH_ERROR("weights must be positive", IGRAPH_EINVAL); /* Initialize data structures */ IGRAPH_VECTOR_INIT_FINALLY(&links_community, 0); IGRAPH_VECTOR_INIT_FINALLY(&links_weight, 0); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&temp_membership, vcount); IGRAPH_CHECK(igraph_vector_resize(membership, vcount)); /* Initialize list of communities from graph vertices */ communities.vertices_no = vcount; communities.communities_no = vcount; communities.weights = weights; communities.weight_sum = 2 * igraph_vector_sum(weights); communities.membership = membership; communities.item = igraph_Calloc(vcount, igraph_i_multilevel_community); if (communities.item == 0) { IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, communities.item); /* Still initializing the communities data structure */ for (i=0; i < vcount; i++) { VECTOR(*communities.membership)[i] = i; communities.item[i].size = 1; communities.item[i].weight_inside = 0; communities.item[i].weight_all = 0; } /* Some more initialization :) */ for (i = 0; i < ecount; i++) { igraph_real_t weight = 1; igraph_edge(graph, (igraph_integer_t) i, &ffrom, &fto); weight = VECTOR(*weights)[i]; communities.item[(long int) ffrom].weight_all += weight; communities.item[(long int) fto].weight_all += weight; if (ffrom == fto) communities.item[(long int) ffrom].weight_inside += 2*weight; } q = igraph_i_multilevel_community_modularity(&communities); pass = 1; do { /* Pass begin */ long int temp_communities_no = communities.communities_no; pass_q = q; changed = 0; /* Save the current membership, it will be restored in case of worse result */ IGRAPH_CHECK(igraph_vector_update(&temp_membership, communities.membership)); for (i = 0; i < vcount; i++) { /* Exclude vertex from its current community */ igraph_real_t weight_all = 0; igraph_real_t weight_inside = 0; igraph_real_t weight_loop = 0; igraph_real_t max_q_gain = 0; igraph_real_t max_weight; long int old_id, new_id, n; igraph_i_multilevel_community_links(graph, &communities, (igraph_integer_t) i, &edges, &weight_all, &weight_inside, &weight_loop, &links_community, &links_weight); old_id = (long int)VECTOR(*(communities.membership))[i]; new_id = old_id; /* Update old community */ igraph_vector_set(communities.membership, i, -1); communities.item[old_id].size--; if (communities.item[old_id].size == 0) {communities.communities_no--;} communities.item[old_id].weight_all -= weight_all; communities.item[old_id].weight_inside -= 2*weight_inside + weight_loop; /* debug("Remove %ld all: %lf Inside: %lf\n", i, -weight_all, -2*weight_inside + weight_loop); */ /* Find new community to join with the best modification gain */ max_q_gain = 0; max_weight = weight_inside; n = igraph_vector_size(&links_community); for (j = 0; j < n; j++) { long int c = (long int) VECTOR(links_community)[j]; igraph_real_t w = VECTOR(links_weight)[j]; igraph_real_t q_gain = igraph_i_multilevel_community_modularity_gain(&communities, (igraph_integer_t) c, (igraph_integer_t) i, weight_all, w); /* debug("Link %ld -> %ld weight: %lf gain: %lf\n", i, c, (double) w, (double) q_gain); */ if (q_gain > max_q_gain) { new_id = c; max_q_gain = q_gain; max_weight = w; } } /* debug("Added vertex %ld to community %ld (gain %lf).\n", i, new_id, (double) max_q_gain); */ /* Add vertex to "new" community and update it */ igraph_vector_set(communities.membership, i, new_id); if (communities.item[new_id].size == 0) {communities.communities_no++;} communities.item[new_id].size++; communities.item[new_id].weight_all += weight_all; communities.item[new_id].weight_inside += 2*max_weight + weight_loop; if (new_id != old_id) { changed++; } } q = igraph_i_multilevel_community_modularity(&communities); if (changed && (q > pass_q)) { /* debug("Pass %d (changed: %d) Communities: %ld Modularity from %lf to %lf\n", pass, changed, communities.communities_no, (double) pass_q, (double) q); */ pass++; } else { /* No changes or the modularity became worse, restore last membership */ IGRAPH_CHECK(igraph_vector_update(communities.membership, &temp_membership)); communities.communities_no = temp_communities_no; break; } IGRAPH_ALLOW_INTERRUPTION(); } while (changed && (q > pass_q)); /* Pass end */ if (modularity) { *modularity = q; } /* debug("Result Communities: %ld Modularity: %lf\n", communities.communities_no, (double) q); */ IGRAPH_CHECK(igraph_reindex_membership(membership, 0)); /* Shrink the nodes of the graph according to the present community structure * and simplify the resulting graph */ /* TODO: check if we really need to copy temp_membership */ IGRAPH_CHECK(igraph_vector_update(&temp_membership, membership)); IGRAPH_CHECK(igraph_i_multilevel_shrink(graph, &temp_membership)); igraph_vector_destroy(&temp_membership); IGRAPH_FINALLY_CLEAN(1); /* Update edge weights after shrinking and simplification */ /* Here we reuse the edges vector as we don't need the previous contents anymore */ /* TODO: can we use igraph_simplify here? */ IGRAPH_CHECK(igraph_i_multilevel_simplify_multiple(graph, &edges)); /* We reuse the links_weight vector to store the old edge weights */ IGRAPH_CHECK(igraph_vector_update(&links_weight, weights)); igraph_vector_fill(weights, 0); for (i = 0; i < ecount; i++) { VECTOR(*weights)[(long int)VECTOR(edges)[i]] += VECTOR(links_weight)[i]; } igraph_free(communities.item); igraph_vector_destroy(&links_community); igraph_vector_destroy(&links_weight); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(4); return 0; } /** * \ingroup communities * \function igraph_community_multilevel * \brief Finding community structure by multi-level optimization of modularity * * This function implements the multi-level modularity optimization * algorithm for finding community structure, see * VD Blondel, J-L Guillaume, R Lambiotte and E Lefebvre: Fast unfolding of * community hierarchies in large networks, J Stat Mech P10008 (2008) * for the details (preprint: http://arxiv.org/abs/arXiv:0803.0476). * * It is based on the modularity measure and a hierarchical approach. * Initially, each vertex is assigned to a community on its own. In every step, * vertices are re-assigned to communities in a local, greedy way: each vertex * is moved to the community with which it achieves the highest contribution to * modularity. When no vertices can be reassigned, each community is considered * a vertex on its own, and the process starts again with the merged communities. * The process stops when there is only a single vertex left or when the modularity * cannot be increased any more in a step. * * This function was contributed by Tom Gregorovic. * * \param graph The input graph. It must be an undirected graph. * \param weights Numeric vector containing edge weights. If \c NULL, every edge * has equal weight. The weights are expected to be non-negative. * \param membership The membership vector, the result is returned here. * For each vertex it gives the ID of its community. The vector * must be initialized and it will be resized accordingly. * \param memberships Numeric matrix that will contain the membership * vector after each level, if not \c NULL. It must be initialized and * it will be resized accordingly. * \param modularity Numeric vector that will contain the modularity score * after each level, if not \c NULL. It must be initialized and it * will be resized accordingly. * \return Error code. * * Time complexity: in average near linear on sparse graphs. * * \example examples/simple/igraph_community_multilevel.c */ int igraph_community_multilevel(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *membership, igraph_matrix_t *memberships, igraph_vector_t *modularity) { igraph_t g; igraph_vector_t w, m, level_membership; igraph_real_t prev_q = -1, q = -1; int i, level = 1; long int vcount = igraph_vcount(graph); /* Make a copy of the original graph, we will do the merges on the copy */ IGRAPH_CHECK(igraph_copy(&g, graph)); IGRAPH_FINALLY(igraph_destroy, &g); if (weights) { IGRAPH_CHECK(igraph_vector_copy(&w, weights)); IGRAPH_FINALLY(igraph_vector_destroy, &w); } else { IGRAPH_VECTOR_INIT_FINALLY(&w, igraph_ecount(&g)); igraph_vector_fill(&w, 1); } IGRAPH_VECTOR_INIT_FINALLY(&m, vcount); IGRAPH_VECTOR_INIT_FINALLY(&level_membership, vcount); if (memberships || membership) { /* Put each vertex in its own community */ for (i = 0; i < vcount; i++) { VECTOR(level_membership)[i] = i; } } if (memberships) { /* Resize the membership matrix to have vcount columns and no rows */ IGRAPH_CHECK(igraph_matrix_resize(memberships, 0, vcount)); } if (modularity) { /* Clear the modularity vector */ igraph_vector_clear(modularity); } while (1) { /* Remember the previous modularity and vertex count, do a single step */ igraph_integer_t step_vcount = igraph_vcount(&g); prev_q = q; IGRAPH_CHECK(igraph_i_community_multilevel_step(&g, &w, &m, &q)); /* Were there any merges? If not, we have to stop the process */ if (igraph_vcount(&g) == step_vcount || q < prev_q) break; if (memberships || membership) { for (i = 0; i < vcount; i++) { /* Readjust the membership vector */ VECTOR(level_membership)[i] = VECTOR(m)[(long int) VECTOR(level_membership)[i]]; } } if (modularity) { /* If we have to return the modularity scores, add it to the modularity vector */ IGRAPH_CHECK(igraph_vector_push_back(modularity, q)); } if (memberships) { /* If we have to return the membership vectors at each level, store the new * membership vector */ IGRAPH_CHECK(igraph_matrix_add_rows(memberships, 1)); IGRAPH_CHECK(igraph_matrix_set_row(memberships, &level_membership, level - 1)); } /* debug("Level: %d Communities: %ld Modularity: %f\n", level, (long int) igraph_vcount(&g), (double) q); */ /* Increase the level counter */ level++; } /* It might happen that there are no merges, so every vertex is in its own community. We still might want the modularity score for that. */ if (modularity && igraph_vector_size(modularity) == 0) { igraph_vector_t tmp; igraph_real_t mod; int i; IGRAPH_VECTOR_INIT_FINALLY(&tmp, vcount); for (i=0; i * References: * * * Meila M: Comparing clusterings by the variation of information. * In: Schölkopf B, Warmuth MK (eds.). Learning Theory and Kernel Machines: * 16th Annual Conference on Computational Learning Theory and 7th Kernel * Workshop, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer * Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1. * * * Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure * identification. J Stat Mech P09008, 2005. * * * van Dongen S: Performance criteria for graph clustering and Markov cluster * experiments. Technical Report INS-R0012, National Research Institute for * Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. * * * Rand WM: Objective criteria for the evaluation of clustering methods. * J Am Stat Assoc 66(336):846-850, 1971. * * * Hubert L and Arabie P: Comparing partitions. Journal of Classification * 2:193-218, 1985. * * \param comm1 the membership vector of the first community structure * \param comm2 the membership vector of the second community structure * \param result the result is stored here. * \param method the comparison method to use. \c IGRAPH_COMMCMP_VI * selects the variation of information (VI) metric of * Meila (2003), \c IGRAPH_COMMCMP_NMI selects the * normalized mutual information measure proposed by * Danon et al (2005), \c IGRAPH_COMMCMP_SPLIT_JOIN * selects the split-join distance of van Dongen (2000), * \c IGRAPH_COMMCMP_RAND selects the unadjusted Rand * index (1971) and \c IGRAPH_COMMCMP_ADJUSTED_RAND * selects the adjusted Rand index. * * \return Error code. * * Time complexity: O(n log(n)). */ int igraph_compare_communities(const igraph_vector_t *comm1, const igraph_vector_t *comm2, igraph_real_t* result, igraph_community_comparison_t method) { igraph_vector_t c1, c2; if (igraph_vector_size(comm1) != igraph_vector_size(comm2)) { IGRAPH_ERROR("community membership vectors have different lengths", IGRAPH_EINVAL); } /* Copy and reindex membership vectors to make sure they are continuous */ IGRAPH_CHECK(igraph_vector_copy(&c1, comm1)); IGRAPH_FINALLY(igraph_vector_destroy, &c1); IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); IGRAPH_FINALLY(igraph_vector_destroy, &c2); IGRAPH_CHECK(igraph_reindex_membership(&c1, 0)); IGRAPH_CHECK(igraph_reindex_membership(&c2, 0)); switch (method) { case IGRAPH_COMMCMP_VI: IGRAPH_CHECK(igraph_i_compare_communities_vi(&c1, &c2, result)); break; case IGRAPH_COMMCMP_NMI: IGRAPH_CHECK(igraph_i_compare_communities_nmi(&c1, &c2, result)); break; case IGRAPH_COMMCMP_SPLIT_JOIN: { igraph_integer_t d12, d21; IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, &d12, &d21)); *result = d12 + d21; } break; case IGRAPH_COMMCMP_RAND: case IGRAPH_COMMCMP_ADJUSTED_RAND: IGRAPH_CHECK(igraph_i_compare_communities_rand(&c1, &c2, result, method == IGRAPH_COMMCMP_ADJUSTED_RAND)); break; default: IGRAPH_ERROR("unknown community comparison method", IGRAPH_EINVAL); } /* Clean up everything */ igraph_vector_destroy(&c1); igraph_vector_destroy(&c2); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \ingroup communities * \function igraph_split_join_distance * \brief Calculates the split-join distance of two community structures * * The split-join distance between partitions A and B is the sum of the * projection distance of A from B and the projection distance of B from * A. The projection distance is an asymmetric measure and it is defined * as follows: * * * First, each set in partition A is evaluated against all sets in partition * B. For each set in partition A, the best matching set in partition B is * found and the overlap size is calculated. (Matching is quantified by the * size of the overlap between the two sets). Then, the maximal overlap sizes * for each set in A are summed together and subtracted from the number of * elements in A. * * * The split-join distance will be returned in two arguments, \c distance12 * will contain the projection distance of the first partition from the * second, while \c distance21 will be the projection distance of the second * partition from the first. This makes it easier to detect whether a * partition is a subpartition of the other, since in this case, the * corresponding distance will be zero. * * * Reference: * * * van Dongen S: Performance criteria for graph clustering and Markov cluster * experiments. Technical Report INS-R0012, National Research Institute for * Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. * * \param comm1 the membership vector of the first community structure * \param comm2 the membership vector of the second community structure * \param distance12 pointer to an \c igraph_integer_t, the projection distance * of the first community structure from the second one will be * returned here. * \param distance21 pointer to an \c igraph_integer_t, the projection distance * of the second community structure from the first one will be * returned here. * \return Error code. * * \see \ref igraph_compare_communities() with the \c IGRAPH_COMMCMP_SPLIT_JOIN * method if you are not interested in the individual distances but only the sum * of them. * * Time complexity: O(n log(n)). */ int igraph_split_join_distance(const igraph_vector_t *comm1, const igraph_vector_t *comm2, igraph_integer_t *distance12, igraph_integer_t *distance21) { igraph_vector_t c1, c2; if (igraph_vector_size(comm1) != igraph_vector_size(comm2)) { IGRAPH_ERROR("community membership vectors have different lengths", IGRAPH_EINVAL); } /* Copy and reindex membership vectors to make sure they are continuous */ IGRAPH_CHECK(igraph_vector_copy(&c1, comm1)); IGRAPH_FINALLY(igraph_vector_destroy, &c1); IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); IGRAPH_FINALLY(igraph_vector_destroy, &c2); IGRAPH_CHECK(igraph_reindex_membership(&c1, 0)); IGRAPH_CHECK(igraph_reindex_membership(&c2, 0)); IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, distance12, distance21)); /* Clean up everything */ igraph_vector_destroy(&c1); igraph_vector_destroy(&c2); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * Calculates the entropy and the mutual information for two reindexed community * membership vectors v1 and v2. This is needed by both Meila's and Danon's * community comparison measure. */ int igraph_i_entropy_and_mutual_information(const igraph_vector_t* v1, const igraph_vector_t* v2, double* h1, double* h2, double* mut_inf) { long int i, n = igraph_vector_size(v1); long int k1 = (long int)igraph_vector_max(v1)+1; long int k2 = (long int)igraph_vector_max(v2)+1; double *p1, *p2; igraph_spmatrix_t m; igraph_spmatrix_iter_t mit; p1 = igraph_Calloc(k1, double); if (p1 == 0) { IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, p1); p2 = igraph_Calloc(k2, double); if (p2 == 0) { IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, p2); /* Calculate the entropy of v1 */ *h1 = 0.0; for (i = 0; i < n; i++) p1[(long int)VECTOR(*v1)[i]]++; for (i = 0; i < k1; i++) { p1[i] /= n; *h1 -= p1[i] * log(p1[i]); } /* Calculate the entropy of v2 */ *h2 = 0.0; for (i = 0; i < n; i++) p2[(long int)VECTOR(*v2)[i]]++; for (i = 0; i < k2; i++) { p2[i] /= n; *h2 -= p2[i] * log(p2[i]); } /* We will only need the logs of p1 and p2 from now on */ for (i = 0; i < k1; i++) { p1[i] = log(p1[i]); } for (i = 0; i < k2; i++) { p2[i] = log(p2[i]); } /* Calculate the mutual information of v1 and v2 */ *mut_inf = 0.0; IGRAPH_CHECK(igraph_spmatrix_init(&m, k1, k2)); IGRAPH_FINALLY(igraph_spmatrix_destroy, &m); for (i = 0; i < n; i++) { IGRAPH_CHECK(igraph_spmatrix_add_e(&m, (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1)); } IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m)); IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); while (!igraph_spmatrix_iter_end(&mit)) { double p = mit.value / n; *mut_inf += p * (log(p) - p1[mit.ri] - p2[mit.ci]); igraph_spmatrix_iter_next(&mit); } igraph_spmatrix_iter_destroy(&mit); igraph_spmatrix_destroy(&m); free(p1); free(p2); IGRAPH_FINALLY_CLEAN(4); return 0; } /** * Implementation of the normalized mutual information (NMI) measure of * Danon et al. This function assumes that the community membership * vectors have already been normalized using igraph_reindex_communities(). * * * Reference: Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community * structure identification. J Stat Mech P09008, 2005. * * * Time complexity: O(n log(n)) */ int igraph_i_compare_communities_nmi(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_real_t* result) { double h1, h2, mut_inf; IGRAPH_CHECK(igraph_i_entropy_and_mutual_information(v1, v2, &h1, &h2, &mut_inf)); if (h1 == 0 && h2 == 0) *result = 1; else *result = 2 * mut_inf / (h1 + h2); return IGRAPH_SUCCESS; } /** * Implementation of the variation of information metric (VI) of * Meila et al. This function assumes that the community membership * vectors have already been normalized using igraph_reindex_communities(). * * * Reference: Meila M: Comparing clusterings by the variation of information. * In: Schölkopf B, Warmuth MK (eds.). Learning Theory and Kernel Machines: * 16th Annual Conference on Computational Learning Theory and 7th Kernel * Workshop, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer * Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1. * * * Time complexity: O(n log(n)) */ int igraph_i_compare_communities_vi(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_real_t* result) { double h1, h2, mut_inf; IGRAPH_CHECK(igraph_i_entropy_and_mutual_information(v1, v2, &h1, &h2, &mut_inf)); *result = h1 + h2 - 2*mut_inf; return IGRAPH_SUCCESS; } /** * \brief Calculates the confusion matrix for two clusterings. * * * This function assumes that the community membership vectors have already * been normalized using igraph_reindex_communities(). * * * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1 * and k2 are the number of clusters in each of the clusterings. */ int igraph_i_confusion_matrix(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_spmatrix_t *m) { long int k1 = (long int)igraph_vector_max(v1)+1; long int k2 = (long int)igraph_vector_max(v2)+1; long int i, n = igraph_vector_size(v1); IGRAPH_CHECK(igraph_spmatrix_resize(m, k1, k2)); for (i = 0; i < n; i++) { IGRAPH_CHECK(igraph_spmatrix_add_e(m, (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1)); } return IGRAPH_SUCCESS; } /** * Implementation of the split-join distance of van Dongen. * * * This function assumes that the community membership vectors have already * been normalized using igraph_reindex_communities(). * * * Reference: van Dongen S: Performance criteria for graph clustering and Markov * cluster experiments. Technical Report INS-R0012, National Research Institute * for Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. * * * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1 * and k2 are the number of clusters in each of the clusterings. */ int igraph_i_split_join_distance(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_integer_t* distance12, igraph_integer_t* distance21) { long int n = igraph_vector_size(v1); igraph_vector_t rowmax, colmax; igraph_spmatrix_t m; igraph_spmatrix_iter_t mit; /* Calculate the confusion matrix */ IGRAPH_CHECK(igraph_spmatrix_init(&m, 1, 1)); IGRAPH_FINALLY(igraph_spmatrix_destroy, &m); IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &m)); /* Initialize vectors that will store the row/columnwise maxima */ IGRAPH_VECTOR_INIT_FINALLY(&rowmax, igraph_spmatrix_nrow(&m)); IGRAPH_VECTOR_INIT_FINALLY(&colmax, igraph_spmatrix_ncol(&m)); /* Find the row/columnwise maxima */ IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m)); IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); while (!igraph_spmatrix_iter_end(&mit)) { if (mit.value > VECTOR(rowmax)[mit.ri]) VECTOR(rowmax)[mit.ri] = mit.value; if (mit.value > VECTOR(colmax)[mit.ci]) VECTOR(colmax)[mit.ci] = mit.value; igraph_spmatrix_iter_next(&mit); } igraph_spmatrix_iter_destroy(&mit); IGRAPH_FINALLY_CLEAN(1); /* Calculate the distances */ *distance12 = (igraph_integer_t) (n - igraph_vector_sum(&rowmax)); *distance21 = (igraph_integer_t) (n - igraph_vector_sum(&colmax)); igraph_vector_destroy(&rowmax); igraph_vector_destroy(&colmax); igraph_spmatrix_destroy(&m); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } /** * Implementation of the adjusted and unadjusted Rand indices. * * * This function assumes that the community membership vectors have already * been normalized using igraph_reindex_communities(). * * * References: * * * Rand WM: Objective criteria for the evaluation of clustering methods. J Am * Stat Assoc 66(336):846-850, 1971. * * * Hubert L and Arabie P: Comparing partitions. Journal of Classification * 2:193-218, 1985. * * * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1 * and k2 are the number of clusters in each of the clusterings. */ int igraph_i_compare_communities_rand(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_real_t *result, igraph_bool_t adjust) { igraph_spmatrix_t m; igraph_spmatrix_iter_t mit; igraph_vector_t rowsums, colsums; long int i, nrow, ncol; double rand, n; double frac_pairs_in_1, frac_pairs_in_2; /* Calculate the confusion matrix */ IGRAPH_CHECK(igraph_spmatrix_init(&m, 1, 1)); IGRAPH_FINALLY(igraph_spmatrix_destroy, &m); IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &m)); /* The unadjusted Rand index is defined as (a+d) / (a+b+c+d), where: * * - a is the number of pairs in the same cluster both in v1 and v2. This * equals the sum of n(i,j) choose 2 for all i and j. * * - b is the number of pairs in the same cluster in v1 and in different * clusters in v2. This is sum n(i,*) choose 2 for all i minus a. * n(i,*) is the number of elements in cluster i in v1. * * - c is the number of pairs in the same cluster in v2 and in different * clusters in v1. This is sum n(*,j) choose 2 for all j minus a. * n(*,j) is the number of elements in cluster j in v2. * * - d is (n choose 2) - a - b - c. * * Therefore, a+d = (n choose 2) - b - c * = (n choose 2) - sum (n(i,*) choose 2) * - sum (n(*,j) choose 2) * + 2 * sum (n(i,j) choose 2). * * Since a+b+c+d = (n choose 2) and this goes in the denominator, we can * just as well start dividing each term in a+d by (n choose 2), which * yields: * * 1 - sum( n(i,*)/n * (n(i,*)-1)/(n-1) ) * - sum( n(*,i)/n * (n(*,i)-1)/(n-1) ) * + sum( n(i,j)/n * (n(i,j)-1)/(n-1) ) * 2 */ /* Calculate row and column sums */ nrow = igraph_spmatrix_nrow(&m); ncol = igraph_spmatrix_ncol(&m); n = igraph_vector_size(v1) + 0.0; IGRAPH_VECTOR_INIT_FINALLY(&rowsums, nrow); IGRAPH_VECTOR_INIT_FINALLY(&colsums, ncol); IGRAPH_CHECK(igraph_spmatrix_rowsums(&m, &rowsums)); IGRAPH_CHECK(igraph_spmatrix_colsums(&m, &colsums)); /* Start calculating the unadjusted Rand index */ rand = 0.0; IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m)); IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); while (!igraph_spmatrix_iter_end(&mit)) { rand += (mit.value / n) * (mit.value-1) / (n-1); igraph_spmatrix_iter_next(&mit); } igraph_spmatrix_iter_destroy(&mit); IGRAPH_FINALLY_CLEAN(1); frac_pairs_in_1 = frac_pairs_in_2 = 0.0; for (i = 0; i < nrow; i++) { frac_pairs_in_1 += (VECTOR(rowsums)[i] / n) * (VECTOR(rowsums)[i]-1) / (n-1); } for (i = 0; i < ncol; i++) { frac_pairs_in_2 += (VECTOR(colsums)[i] / n) * (VECTOR(colsums)[i]-1) / (n-1); } rand = 1.0 + 2 * rand - frac_pairs_in_1 - frac_pairs_in_2; if (adjust) { double expected = frac_pairs_in_1 * frac_pairs_in_2 + (1-frac_pairs_in_1) * (1-frac_pairs_in_2); rand = (rand - expected) / (1 - expected); } igraph_vector_destroy(&rowsums); igraph_vector_destroy(&colsums); igraph_spmatrix_destroy(&m); IGRAPH_FINALLY_CLEAN(3); *result = rand; return IGRAPH_SUCCESS; } igraph/src/config.h.in0000644000176000001440000000515112325372070014371 0ustar ripleyusers/* src/config.h.in. Generated from configure.in by autoheader. */ /* Define to 1 if you have the `expm1' function. */ #undef HAVE_EXPM1 /* Define to 1 if you have the `finite' function. */ #undef HAVE_FINITE /* Define to 1 if you have the `fmin' function. */ #undef HAVE_FMIN /* Define to 1 if using the GNU fortran compiler */ #undef HAVE_GFORTRAN /* Define to 1 if you have the GLPK library */ #undef HAVE_GLPK /* Define to 1 if you have the GMP library */ #undef HAVE_GMP /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the libxml2 libraries installed */ #undef HAVE_LIBXML /* Define to 1 if you have the `log1p' function. */ #undef HAVE_LOG1P /* Define to 1 if you have the `log2' function. */ #undef HAVE_LOG2 /* Define to 1 if you have the `logbl' function. */ #undef HAVE_LOGBL /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `rint' function. */ #undef HAVE_RINT /* Define to 1 if you have the `rintf' function. */ #undef HAVE_RINTF /* Define to 1 if you have the `round' function. */ #undef HAVE_ROUND /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `stpcpy' function. */ #undef HAVE_STPCPY /* Define to 1 if the stpcpy function has a signature */ #undef HAVE_STPCPY_SIGNATURE /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the sys/times.h header */ #undef HAVE_TIMES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* We don't care about thread-local storage in R */ #undef IGRAPH_THREAD_LOCAL /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS igraph/src/lbfgs.c0000644000176000001440000012036012325527073013614 0ustar ripleyusers/* * Limited memory BFGS (L-BFGS). * * Copyright (c) 1990, Jorge Nocedal * Copyright (c) 2007-2010 Naoaki Okazaki * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* $Id: lbfgs.c 65 2010-01-29 12:19:16Z naoaki $ */ /* This library is a C port of the FORTRAN implementation of Limited-memory Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) method written by Jorge Nocedal. The original FORTRAN source code is available at: http://www.ece.northwestern.edu/~nocedal/lbfgs.html The L-BFGS algorithm is described in: - Jorge Nocedal. Updating Quasi-Newton Matrices with Limited Storage. Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. - Dong C. Liu and Jorge Nocedal. On the limited memory BFGS method for large scale optimization. Mathematical Programming B, Vol. 45, No. 3, pp. 503-528, 1989. The line search algorithms used in this implementation are described in: - John E. Dennis and Robert B. Schnabel. Numerical Methods for Unconstrained Optimization and Nonlinear Equations, Englewood Cliffs, 1983. - Jorge J. More and David J. Thuente. Line search algorithm with guaranteed sufficient decrease. ACM Transactions on Mathematical Software (TOMS), Vol. 20, No. 3, pp. 286-307, 1994. This library also implements Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) method presented in: - Galen Andrew and Jianfeng Gao. Scalable training of L1-regularized log-linear models. In Proceedings of the 24th International Conference on Machine Learning (ICML 2007), pp. 33-40, 2007. I would like to thank the original author, Jorge Nocedal, who has been distributing the effieicnt and explanatory implementation in an open source licence. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif/*HAVE_CONFIG_H*/ #ifndef _MSC_VER #include #endif #include #include #include #include "lbfgs.h" #ifdef _MSC_VER #define inline __inline typedef unsigned int uint32_t; #endif/*_MSC_VER*/ #if defined(USE_SSE) && defined(__SSE2__) && LBFGS_FLOAT == 64 /* Use SSE2 optimization for 64bit double precision. */ #include "arithmetic_sse_double.h" #elif defined(USE_SSE) && defined(__SSE__) && LBFGS_FLOAT == 32 /* Use SSE optimization for 32bit float precision. */ #include "arithmetic_sse_float.h" #else /* No CPU specific optimization. */ #include "arithmetic_ansi.h" #endif #define min2(a, b) ((a) <= (b) ? (a) : (b)) #define max2(a, b) ((a) >= (b) ? (a) : (b)) #define max3(a, b, c) max2(max2((a), (b)), (c)); #define is_aligned(p, bytes) \ (((uintptr_t)(const void*)(p)) % (bytes) == 0) struct tag_callback_data { int n; void *instance; lbfgs_evaluate_t proc_evaluate; lbfgs_progress_t proc_progress; }; typedef struct tag_callback_data callback_data_t; struct tag_iteration_data { lbfgsfloatval_t alpha; lbfgsfloatval_t *s; /* [n] */ lbfgsfloatval_t *y; /* [n] */ lbfgsfloatval_t ys; /* vecdot(y, s) */ }; typedef struct tag_iteration_data iteration_data_t; static const lbfgs_parameter_t _defparam = { 6, 1e-5, 0, 1e-5, 0, LBFGS_LINESEARCH_DEFAULT, 40, 1e-20, 1e20, 1e-4, 0.9, 0.9, 1.0e-16, 0.0, 0, -1, }; /* Forward function declarations. */ typedef int (*line_search_proc)( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *f, lbfgsfloatval_t *g, lbfgsfloatval_t *s, lbfgsfloatval_t *stp, const lbfgsfloatval_t* xp, const lbfgsfloatval_t* gp, lbfgsfloatval_t *wa, callback_data_t *cd, const lbfgs_parameter_t *param ); static int line_search_backtracking( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *f, lbfgsfloatval_t *g, lbfgsfloatval_t *s, lbfgsfloatval_t *stp, const lbfgsfloatval_t* xp, const lbfgsfloatval_t* gp, lbfgsfloatval_t *wa, callback_data_t *cd, const lbfgs_parameter_t *param ); static int line_search_backtracking_owlqn( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *f, lbfgsfloatval_t *g, lbfgsfloatval_t *s, lbfgsfloatval_t *stp, const lbfgsfloatval_t* xp, const lbfgsfloatval_t* gp, lbfgsfloatval_t *wp, callback_data_t *cd, const lbfgs_parameter_t *param ); static int line_search_morethuente( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *f, lbfgsfloatval_t *g, lbfgsfloatval_t *s, lbfgsfloatval_t *stp, const lbfgsfloatval_t* xp, const lbfgsfloatval_t* gp, lbfgsfloatval_t *wa, callback_data_t *cd, const lbfgs_parameter_t *param ); static int update_trial_interval( lbfgsfloatval_t *x, lbfgsfloatval_t *fx, lbfgsfloatval_t *dx, lbfgsfloatval_t *y, lbfgsfloatval_t *fy, lbfgsfloatval_t *dy, lbfgsfloatval_t *t, lbfgsfloatval_t *ft, lbfgsfloatval_t *dt, const lbfgsfloatval_t tmin, const lbfgsfloatval_t tmax, int *brackt ); static lbfgsfloatval_t owlqn_x1norm( const lbfgsfloatval_t* x, const int start, const int n ); static void owlqn_pseudo_gradient( lbfgsfloatval_t* pg, const lbfgsfloatval_t* x, const lbfgsfloatval_t* g, const int n, const lbfgsfloatval_t c, const int start, const int end ); static void owlqn_project( lbfgsfloatval_t* d, const lbfgsfloatval_t* sign, const int start, const int end ); #if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) static int round_out_variables(int n) { n += 7; n /= 8; n *= 8; return n; } #endif/*defined(USE_SSE)*/ lbfgsfloatval_t* lbfgs_malloc(int n) { #if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) n = round_out_variables(n); #endif/*defined(USE_SSE)*/ return (lbfgsfloatval_t*)vecalloc(sizeof(lbfgsfloatval_t) * (size_t) n); } void lbfgs_free(lbfgsfloatval_t *x) { vecfree(x); } void lbfgs_parameter_init(lbfgs_parameter_t *param) { memcpy(param, &_defparam, sizeof(*param)); } int lbfgs( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *ptr_fx, lbfgs_evaluate_t proc_evaluate, lbfgs_progress_t proc_progress, void *instance, lbfgs_parameter_t *_param ) { int ret; int i, j, k, ls, end, bound; lbfgsfloatval_t step; /* Constant parameters and their default values. */ lbfgs_parameter_t param = (_param != NULL) ? (*_param) : _defparam; const int m = param.m; lbfgsfloatval_t *xp = NULL; lbfgsfloatval_t *g = NULL, *gp = NULL, *pg = NULL; lbfgsfloatval_t *d = NULL, *w = NULL, *pf = NULL; iteration_data_t *lm = NULL, *it = NULL; lbfgsfloatval_t ys, yy; lbfgsfloatval_t xnorm, gnorm, beta; lbfgsfloatval_t fx = 0.; lbfgsfloatval_t rate = 0.; line_search_proc linesearch = line_search_morethuente; /* Construct a callback data. */ callback_data_t cd; cd.n = n; cd.instance = instance; cd.proc_evaluate = proc_evaluate; cd.proc_progress = proc_progress; #if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) /* Round out the number of variables. */ n = round_out_variables(n); #endif/*defined(USE_SSE)*/ /* Check the input parameters for errors. */ if (n <= 0) { return LBFGSERR_INVALID_N; } #if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) if (n % 8 != 0) { return LBFGSERR_INVALID_N_SSE; } if (!is_aligned(x, 16)) { return LBFGSERR_INVALID_X_SSE; } #endif/*defined(USE_SSE)*/ if (param.epsilon < 0.) { return LBFGSERR_INVALID_EPSILON; } if (param.past < 0) { return LBFGSERR_INVALID_TESTPERIOD; } if (param.delta < 0.) { return LBFGSERR_INVALID_DELTA; } if (param.min_step < 0.) { return LBFGSERR_INVALID_MINSTEP; } if (param.max_step < param.min_step) { return LBFGSERR_INVALID_MAXSTEP; } if (param.ftol < 0.) { return LBFGSERR_INVALID_FTOL; } if (param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE || param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE) { if (param.wolfe <= param.ftol || 1. <= param.wolfe) { return LBFGSERR_INVALID_WOLFE; } } if (param.gtol < 0.) { return LBFGSERR_INVALID_GTOL; } if (param.xtol < 0.) { return LBFGSERR_INVALID_XTOL; } if (param.max_linesearch <= 0) { return LBFGSERR_INVALID_MAXLINESEARCH; } if (param.orthantwise_c < 0.) { return LBFGSERR_INVALID_ORTHANTWISE; } if (param.orthantwise_start < 0 || n < param.orthantwise_start) { return LBFGSERR_INVALID_ORTHANTWISE_START; } if (param.orthantwise_end < 0) { param.orthantwise_end = n; } if (n < param.orthantwise_end) { return LBFGSERR_INVALID_ORTHANTWISE_END; } if (param.orthantwise_c != 0.) { switch (param.linesearch) { case LBFGS_LINESEARCH_BACKTRACKING: linesearch = line_search_backtracking_owlqn; break; default: /* Only the backtracking method is available. */ return LBFGSERR_INVALID_LINESEARCH; } } else { switch (param.linesearch) { case LBFGS_LINESEARCH_MORETHUENTE: linesearch = line_search_morethuente; break; case LBFGS_LINESEARCH_BACKTRACKING_ARMIJO: case LBFGS_LINESEARCH_BACKTRACKING_WOLFE: case LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE: linesearch = line_search_backtracking; break; default: return LBFGSERR_INVALID_LINESEARCH; } } /* Allocate working space. */ xp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); g = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); gp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); d = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); w = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); if (xp == NULL || g == NULL || gp == NULL || d == NULL || w == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; } if (param.orthantwise_c != 0.) { /* Allocate working space for OW-LQN. */ pg = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); if (pg == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; } } /* Allocate limited memory storage. */ lm = (iteration_data_t*)vecalloc((size_t) m * sizeof(iteration_data_t)); if (lm == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; } /* Initialize the limited memory. */ for (i = 0;i < m;++i) { it = &lm[i]; it->alpha = 0; it->ys = 0; it->s = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); it->y = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); if (it->s == NULL || it->y == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; } } /* Allocate an array for storing previous values of the objective function. */ if (0 < param.past) { pf = (lbfgsfloatval_t*)vecalloc((size_t) param.past * sizeof(lbfgsfloatval_t)); } /* Evaluate the function value and its gradient. */ fx = cd.proc_evaluate(cd.instance, x, g, cd.n, 0); if (0. != param.orthantwise_c) { /* Compute the L1 norm of the variable and add it to the object value. */ xnorm = owlqn_x1norm(x, param.orthantwise_start, param.orthantwise_end); fx += xnorm * param.orthantwise_c; owlqn_pseudo_gradient( pg, x, g, n, param.orthantwise_c, param.orthantwise_start, param.orthantwise_end ); } /* Store the initial value of the objective function. */ if (pf != NULL) { pf[0] = fx; } /* Compute the direction; we assume the initial hessian matrix H_0 as the identity matrix. */ if (param.orthantwise_c == 0.) { vecncpy(d, g, n); } else { vecncpy(d, pg, n); } /* Make sure that the initial variables are not a minimizer. */ vec2norm(&xnorm, x, n); if (param.orthantwise_c == 0.) { vec2norm(&gnorm, g, n); } else { vec2norm(&gnorm, pg, n); } if (xnorm < 1.0) xnorm = 1.0; if (gnorm / xnorm <= param.epsilon) { ret = LBFGS_ALREADY_MINIMIZED; goto lbfgs_exit; } /* Compute the initial step: step = 1.0 / sqrt(vecdot(d, d, n)) */ vec2norminv(&step, d, n); k = 1; end = 0; for (;;) { /* Store the current position and gradient vectors. */ veccpy(xp, x, n); veccpy(gp, g, n); /* Search for an optimal step. */ if (param.orthantwise_c == 0.) { ls = linesearch(n, x, &fx, g, d, &step, xp, gp, w, &cd, ¶m); } else { ls = linesearch(n, x, &fx, g, d, &step, xp, pg, w, &cd, ¶m); owlqn_pseudo_gradient( pg, x, g, n, param.orthantwise_c, param.orthantwise_start, param.orthantwise_end ); } if (ls < 0) { /* Revert to the previous point. */ veccpy(x, xp, n); veccpy(g, gp, n); ret = ls; goto lbfgs_exit; } /* Compute x and g norms. */ vec2norm(&xnorm, x, n); if (param.orthantwise_c == 0.) { vec2norm(&gnorm, g, n); } else { vec2norm(&gnorm, pg, n); } /* Report the progress. */ if (cd.proc_progress) { if ((ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls))) { goto lbfgs_exit; } } /* Convergence test. The criterion is given by the following formula: |g(x)| / \max(1, |x|) < \epsilon */ if (xnorm < 1.0) xnorm = 1.0; if (gnorm / xnorm <= param.epsilon) { /* Convergence. */ ret = LBFGS_SUCCESS; break; } /* Test for stopping criterion. The criterion is given by the following formula: (f(past_x) - f(x)) / f(x) < \delta */ if (pf != NULL) { /* We don't test the stopping criterion while k < past. */ if (param.past <= k) { /* Compute the relative improvement from the past. */ rate = (pf[k % param.past] - fx) / fx; /* The stopping criterion. */ if (rate < param.delta) { ret = LBFGS_STOP; break; } } /* Store the current value of the objective function. */ pf[k % param.past] = fx; } if (param.max_iterations != 0 && param.max_iterations < k+1) { /* Maximum number of iterations. */ ret = LBFGSERR_MAXIMUMITERATION; break; } /* Update vectors s and y: s_{k+1} = x_{k+1} - x_{k} = \step * d_{k}. y_{k+1} = g_{k+1} - g_{k}. */ it = &lm[end]; vecdiff(it->s, x, xp, n); vecdiff(it->y, g, gp, n); /* Compute scalars ys and yy: ys = y^t \cdot s = 1 / \rho. yy = y^t \cdot y. Notice that yy is used for scaling the hessian matrix H_0 (Cholesky factor). */ vecdot(&ys, it->y, it->s, n); vecdot(&yy, it->y, it->y, n); it->ys = ys; /* Recursive formula to compute dir = -(H \cdot g). This is described in page 779 of: Jorge Nocedal. Updating Quasi-Newton Matrices with Limited Storage. Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. */ bound = (m <= k) ? m : k; ++k; end = (end + 1) % m; /* Compute the steepest direction. */ if (param.orthantwise_c == 0.) { /* Compute the negative of gradients. */ vecncpy(d, g, n); } else { vecncpy(d, pg, n); } j = end; for (i = 0;i < bound;++i) { j = (j + m - 1) % m; /* if (--j == -1) j = m-1; */ it = &lm[j]; /* \alpha_{j} = \rho_{j} s^{t}_{j} \cdot q_{k+1}. */ vecdot(&it->alpha, it->s, d, n); it->alpha /= it->ys; /* q_{i} = q_{i+1} - \alpha_{i} y_{i}. */ vecadd(d, it->y, -it->alpha, n); } vecscale(d, ys / yy, n); for (i = 0;i < bound;++i) { it = &lm[j]; /* \beta_{j} = \rho_{j} y^t_{j} \cdot \gamma_{i}. */ vecdot(&beta, it->y, d, n); beta /= it->ys; /* \gamma_{i+1} = \gamma_{i} + (\alpha_{j} - \beta_{j}) s_{j}. */ vecadd(d, it->s, it->alpha - beta, n); j = (j + 1) % m; /* if (++j == m) j = 0; */ } /* Constrain the search direction for orthant-wise updates. */ if (param.orthantwise_c != 0.) { for (i = param.orthantwise_start;i < param.orthantwise_end;++i) { if (d[i] * pg[i] >= 0) { d[i] = 0; } } } /* Now the search direction d is ready. We try step = 1 first. */ step = 1.0; } lbfgs_exit: /* Return the final value of the objective function. */ if (ptr_fx != NULL) { *ptr_fx = fx; } vecfree(pf); /* Free memory blocks used by this function. */ if (lm != NULL) { for (i = 0;i < m;++i) { vecfree(lm[i].s); vecfree(lm[i].y); } vecfree(lm); } vecfree(pg); vecfree(w); vecfree(d); vecfree(gp); vecfree(g); vecfree(xp); return ret; } static int line_search_backtracking( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *f, lbfgsfloatval_t *g, lbfgsfloatval_t *s, lbfgsfloatval_t *stp, const lbfgsfloatval_t* xp, const lbfgsfloatval_t* gp, lbfgsfloatval_t *wp, callback_data_t *cd, const lbfgs_parameter_t *param ) { int count = 0; lbfgsfloatval_t width, dg; lbfgsfloatval_t finit, dginit = 0., dgtest; const lbfgsfloatval_t dec = 0.5, inc = 2.1; /* Check the input parameters for errors. */ if (*stp <= 0.) { return LBFGSERR_INVALIDPARAMETERS; } /* Compute the initial gradient in the search direction. */ vecdot(&dginit, g, s, n); /* Make sure that s points to a descent direction. */ if (0 < dginit) { return LBFGSERR_INCREASEGRADIENT; } /* The initial value of the objective function. */ finit = *f; dgtest = param->ftol * dginit; for (;;) { veccpy(x, xp, n); vecadd(x, s, *stp, n); /* Evaluate the function and gradient values. */ *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); ++count; if (*f > finit + *stp * dgtest) { width = dec; } else { /* The sufficient decrease condition (Armijo condition). */ if (param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) { /* Exit with the Armijo condition. */ return count; } /* Check the Wolfe condition. */ vecdot(&dg, g, s, n); if (dg < param->wolfe * dginit) { width = inc; } else { if(param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE) { /* Exit with the regular Wolfe condition. */ return count; } /* Check the strong Wolfe condition. */ if(dg > -param->wolfe * dginit) { width = dec; } else { /* Exit with the strong Wolfe condition. */ return count; } } } if (*stp < param->min_step) { /* The step is the minimum value. */ return LBFGSERR_MINIMUMSTEP; } if (*stp > param->max_step) { /* The step is the maximum value. */ return LBFGSERR_MAXIMUMSTEP; } if (param->max_linesearch <= count) { /* Maximum number of iteration. */ return LBFGSERR_MAXIMUMLINESEARCH; } (*stp) *= width; } } static int line_search_backtracking_owlqn( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *f, lbfgsfloatval_t *g, lbfgsfloatval_t *s, lbfgsfloatval_t *stp, const lbfgsfloatval_t* xp, const lbfgsfloatval_t* gp, lbfgsfloatval_t *wp, callback_data_t *cd, const lbfgs_parameter_t *param ) { int i, count = 0; lbfgsfloatval_t width = 0.5, norm = 0.; lbfgsfloatval_t finit = *f, dgtest; /* Check the input parameters for errors. */ if (*stp <= 0.) { return LBFGSERR_INVALIDPARAMETERS; } /* Choose the orthant for the new point. */ for (i = 0;i < n;++i) { wp[i] = (xp[i] == 0.) ? -gp[i] : xp[i]; } for (;;) { /* Update the current point. */ veccpy(x, xp, n); vecadd(x, s, *stp, n); /* The current point is projected onto the orthant. */ owlqn_project(x, wp, param->orthantwise_start, param->orthantwise_end); /* Evaluate the function and gradient values. */ *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); /* Compute the L1 norm of the variables and add it to the object value. */ norm = owlqn_x1norm(x, param->orthantwise_start, param->orthantwise_end); *f += norm * param->orthantwise_c; ++count; dgtest = 0.; for (i = 0;i < n;++i) { dgtest += (x[i] - xp[i]) * gp[i]; } if (*f <= finit + param->ftol * dgtest) { /* The sufficient decrease condition. */ return count; } if (*stp < param->min_step) { /* The step is the minimum value. */ return LBFGSERR_MINIMUMSTEP; } if (*stp > param->max_step) { /* The step is the maximum value. */ return LBFGSERR_MAXIMUMSTEP; } if (param->max_linesearch <= count) { /* Maximum number of iteration. */ return LBFGSERR_MAXIMUMLINESEARCH; } (*stp) *= width; } } static int line_search_morethuente( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *f, lbfgsfloatval_t *g, lbfgsfloatval_t *s, lbfgsfloatval_t *stp, const lbfgsfloatval_t* xp, const lbfgsfloatval_t* gp, lbfgsfloatval_t *wa, callback_data_t *cd, const lbfgs_parameter_t *param ) { int count = 0; int brackt, stage1, uinfo = 0; lbfgsfloatval_t dg; lbfgsfloatval_t stx, fx, dgx; lbfgsfloatval_t sty, fy, dgy; lbfgsfloatval_t fxm, dgxm, fym, dgym, fm, dgm; lbfgsfloatval_t finit, ftest1, dginit, dgtest; lbfgsfloatval_t width, prev_width; lbfgsfloatval_t stmin, stmax; /* Check the input parameters for errors. */ if (*stp <= 0.) { return LBFGSERR_INVALIDPARAMETERS; } /* Compute the initial gradient in the search direction. */ vecdot(&dginit, g, s, n); /* Make sure that s points to a descent direction. */ if (0 < dginit) { return LBFGSERR_INCREASEGRADIENT; } /* Initialize local variables. */ brackt = 0; stage1 = 1; finit = *f; dgtest = param->ftol * dginit; width = param->max_step - param->min_step; prev_width = 2.0 * width; /* The variables stx, fx, dgx contain the values of the step, function, and directional derivative at the best step. The variables sty, fy, dgy contain the value of the step, function, and derivative at the other endpoint of the interval of uncertainty. The variables stp, f, dg contain the values of the step, function, and derivative at the current step. */ stx = sty = 0.; fx = fy = finit; dgx = dgy = dginit; for (;;) { /* Set the minimum and maximum steps to correspond to the present interval of uncertainty. */ if (brackt) { stmin = min2(stx, sty); stmax = max2(stx, sty); } else { stmin = stx; stmax = *stp + 4.0 * (*stp - stx); } /* Clip the step in the range of [stpmin, stpmax]. */ if (*stp < param->min_step) *stp = param->min_step; if (param->max_step < *stp) *stp = param->max_step; /* If an unusual termination is to occur then let stp be the lowest point obtained so far. */ if ((brackt && ((*stp <= stmin || stmax <= *stp) || param->max_linesearch <= count + 1 || uinfo != 0)) || (brackt && (stmax - stmin <= param->xtol * stmax))) { *stp = stx; } /* Compute the current value of x: x <- x + (*stp) * s. */ veccpy(x, xp, n); vecadd(x, s, *stp, n); /* Evaluate the function and gradient values. */ *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); vecdot(&dg, g, s, n); ftest1 = finit + *stp * dgtest; ++count; /* Test for errors and convergence. */ if (brackt && ((*stp <= stmin || stmax <= *stp) || uinfo != 0)) { /* Rounding errors prevent further progress. */ return LBFGSERR_ROUNDING_ERROR; } if (*stp == param->max_step && *f <= ftest1 && dg <= dgtest) { /* The step is the maximum value. */ return LBFGSERR_MAXIMUMSTEP; } if (*stp == param->min_step && (ftest1 < *f || dgtest <= dg)) { /* The step is the minimum value. */ return LBFGSERR_MINIMUMSTEP; } if (brackt && (stmax - stmin) <= param->xtol * stmax) { /* Relative width of the interval of uncertainty is at most xtol. */ return LBFGSERR_WIDTHTOOSMALL; } if (param->max_linesearch <= count) { /* Maximum number of iteration. */ return LBFGSERR_MAXIMUMLINESEARCH; } if (*f <= ftest1 && fabs(dg) <= param->gtol * (-dginit)) { /* The sufficient decrease condition and the directional derivative condition hold. */ return count; } /* In the first stage we seek a step for which the modified function has a nonpositive value and nonnegative derivative. */ if (stage1 && *f <= ftest1 && min2(param->ftol, param->gtol) * dginit <= dg) { stage1 = 0; } /* A modified function is used to predict the step only if we have not obtained a step for which the modified function has a nonpositive function value and nonnegative derivative, and if a lower function value has been obtained but the decrease is not sufficient. */ if (stage1 && ftest1 < *f && *f <= fx) { /* Define the modified function and derivative values. */ fm = *f - *stp * dgtest; fxm = fx - stx * dgtest; fym = fy - sty * dgtest; dgm = dg - dgtest; dgxm = dgx - dgtest; dgym = dgy - dgtest; /* Call update_trial_interval() to update the interval of uncertainty and to compute the new step. */ uinfo = update_trial_interval( &stx, &fxm, &dgxm, &sty, &fym, &dgym, stp, &fm, &dgm, stmin, stmax, &brackt ); /* Reset the function and gradient values for f. */ fx = fxm + stx * dgtest; fy = fym + sty * dgtest; dgx = dgxm + dgtest; dgy = dgym + dgtest; } else { /* Call update_trial_interval() to update the interval of uncertainty and to compute the new step. */ uinfo = update_trial_interval( &stx, &fx, &dgx, &sty, &fy, &dgy, stp, f, &dg, stmin, stmax, &brackt ); } /* Force a sufficient decrease in the interval of uncertainty. */ if (brackt) { if (0.66 * prev_width <= fabs(sty - stx)) { *stp = stx + 0.5 * (sty - stx); } prev_width = width; width = fabs(sty - stx); } } return LBFGSERR_LOGICERROR; } /** * Define the local variables for computing minimizers. */ #define USES_MINIMIZER \ lbfgsfloatval_t a, d, gamma, theta, p, q, r, s; /** * Find a minimizer of an interpolated cubic function. * @param cm The minimizer of the interpolated cubic. * @param u The value of one point, u. * @param fu The value of f(u). * @param du The value of f'(u). * @param v The value of another point, v. * @param fv The value of f(v). * @param du The value of f'(v). */ #define CUBIC_MINIMIZER(cm, u, fu, du, v, fv, dv) \ d = (v) - (u); \ theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \ p = fabs(theta); \ q = fabs(du); \ r = fabs(dv); \ s = max3(p, q, r); \ /* gamma = s*sqrt((theta/s)**2 - (du/s) * (dv/s)) */ \ a = theta / s; \ gamma = s * sqrt(a * a - ((du) / s) * ((dv) / s)); \ if ((v) < (u)) gamma = -gamma; \ p = gamma - (du) + theta; \ q = gamma - (du) + gamma + (dv); \ r = p / q; \ (cm) = (u) + r * d; /** * Find a minimizer of an interpolated cubic function. * @param cm The minimizer of the interpolated cubic. * @param u The value of one point, u. * @param fu The value of f(u). * @param du The value of f'(u). * @param v The value of another point, v. * @param fv The value of f(v). * @param du The value of f'(v). * @param xmin The maximum value. * @param xmin The minimum value. */ #define CUBIC_MINIMIZER2(cm, u, fu, du, v, fv, dv, xmin, xmax) \ d = (v) - (u); \ theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \ p = fabs(theta); \ q = fabs(du); \ r = fabs(dv); \ s = max3(p, q, r); \ /* gamma = s*sqrt((theta/s)**2 - (du/s) * (dv/s)) */ \ a = theta / s; \ gamma = s * sqrt(max2(0, a * a - ((du) / s) * ((dv) / s))); \ if ((u) < (v)) gamma = -gamma; \ p = gamma - (dv) + theta; \ q = gamma - (dv) + gamma + (du); \ r = p / q; \ if (r < 0. && gamma != 0.) { \ (cm) = (v) - r * d; \ } else if (a < 0) { \ (cm) = (xmax); \ } else { \ (cm) = (xmin); \ } /** * Find a minimizer of an interpolated quadratic function. * @param qm The minimizer of the interpolated quadratic. * @param u The value of one point, u. * @param fu The value of f(u). * @param du The value of f'(u). * @param v The value of another point, v. * @param fv The value of f(v). */ #define QUARD_MINIMIZER(qm, u, fu, du, v, fv) \ a = (v) - (u); \ (qm) = (u) + (du) / (((fu) - (fv)) / a + (du)) / 2 * a; /** * Find a minimizer of an interpolated quadratic function. * @param qm The minimizer of the interpolated quadratic. * @param u The value of one point, u. * @param du The value of f'(u). * @param v The value of another point, v. * @param dv The value of f'(v). */ #define QUARD_MINIMIZER2(qm, u, du, v, dv) \ a = (u) - (v); \ (qm) = (v) + (dv) / ((dv) - (du)) * a; /** * Update a safeguarded trial value and interval for line search. * * The parameter x represents the step with the least function value. * The parameter t represents the current step. This function assumes * that the derivative at the point of x in the direction of the step. * If the bracket is set to true, the minimizer has been bracketed in * an interval of uncertainty with endpoints between x and y. * * @param x The pointer to the value of one endpoint. * @param fx The pointer to the value of f(x). * @param dx The pointer to the value of f'(x). * @param y The pointer to the value of another endpoint. * @param fy The pointer to the value of f(y). * @param dy The pointer to the value of f'(y). * @param t The pointer to the value of the trial value, t. * @param ft The pointer to the value of f(t). * @param dt The pointer to the value of f'(t). * @param tmin The minimum value for the trial value, t. * @param tmax The maximum value for the trial value, t. * @param brackt The pointer to the predicate if the trial value is * bracketed. * @retval int Status value. Zero indicates a normal termination. * * @see * Jorge J. More and David J. Thuente. Line search algorithm with * guaranteed sufficient decrease. ACM Transactions on Mathematical * Software (TOMS), Vol 20, No 3, pp. 286-307, 1994. */ static int update_trial_interval( lbfgsfloatval_t *x, lbfgsfloatval_t *fx, lbfgsfloatval_t *dx, lbfgsfloatval_t *y, lbfgsfloatval_t *fy, lbfgsfloatval_t *dy, lbfgsfloatval_t *t, lbfgsfloatval_t *ft, lbfgsfloatval_t *dt, const lbfgsfloatval_t tmin, const lbfgsfloatval_t tmax, int *brackt ) { int bound; int dsign = fsigndiff(dt, dx); lbfgsfloatval_t mc; /* minimizer of an interpolated cubic. */ lbfgsfloatval_t mq; /* minimizer of an interpolated quadratic. */ lbfgsfloatval_t newt; /* new trial value. */ USES_MINIMIZER; /* for CUBIC_MINIMIZER and QUARD_MINIMIZER. */ /* Check the input parameters for errors. */ if (*brackt) { if (*t <= min2(*x, *y) || max2(*x, *y) <= *t) { /* The trival value t is out of the interval. */ return LBFGSERR_OUTOFINTERVAL; } if (0. <= *dx * (*t - *x)) { /* The function must decrease from x. */ return LBFGSERR_INCREASEGRADIENT; } if (tmax < tmin) { /* Incorrect tmin and tmax specified. */ return LBFGSERR_INCORRECT_TMINMAX; } } /* Trial value selection. */ if (*fx < *ft) { /* Case 1: a higher function value. The minimum is brackt. If the cubic minimizer is closer to x than the quadratic one, the cubic one is taken, else the average of the minimizers is taken. */ *brackt = 1; bound = 1; CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); QUARD_MINIMIZER(mq, *x, *fx, *dx, *t, *ft); if (fabs(mc - *x) < fabs(mq - *x)) { newt = mc; } else { newt = mc + 0.5 * (mq - mc); } } else if (dsign) { /* Case 2: a lower function value and derivatives of opposite sign. The minimum is brackt. If the cubic minimizer is closer to x than the quadratic (secant) one, the cubic one is taken, else the quadratic one is taken. */ *brackt = 1; bound = 0; CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); if (fabs(mc - *t) > fabs(mq - *t)) { newt = mc; } else { newt = mq; } } else if (fabs(*dt) < fabs(*dx)) { /* Case 3: a lower function value, derivatives of the same sign, and the magnitude of the derivative decreases. The cubic minimizer is only used if the cubic tends to infinity in the direction of the minimizer or if the minimum of the cubic is beyond t. Otherwise the cubic minimizer is defined to be either tmin or tmax. The quadratic (secant) minimizer is also computed and if the minimum is brackt then the the minimizer closest to x is taken, else the one farthest away is taken. */ bound = 1; CUBIC_MINIMIZER2(mc, *x, *fx, *dx, *t, *ft, *dt, tmin, tmax); QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); if (*brackt) { if (fabs(*t - mc) < fabs(*t - mq)) { newt = mc; } else { newt = mq; } } else { if (fabs(*t - mc) > fabs(*t - mq)) { newt = mc; } else { newt = mq; } } } else { /* Case 4: a lower function value, derivatives of the same sign, and the magnitude of the derivative does not decrease. If the minimum is not brackt, the step is either tmin or tmax, else the cubic minimizer is taken. */ bound = 0; if (*brackt) { CUBIC_MINIMIZER(newt, *t, *ft, *dt, *y, *fy, *dy); } else if (*x < *t) { newt = tmax; } else { newt = tmin; } } /* Update the interval of uncertainty. This update does not depend on the new step or the case analysis above. - Case a: if f(x) < f(t), x <- x, y <- t. - Case b: if f(t) <= f(x) && f'(t)*f'(x) > 0, x <- t, y <- y. - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, x <- t, y <- x. */ if (*fx < *ft) { /* Case a */ *y = *t; *fy = *ft; *dy = *dt; } else { /* Case c */ if (dsign) { *y = *x; *fy = *fx; *dy = *dx; } /* Cases b and c */ *x = *t; *fx = *ft; *dx = *dt; } /* Clip the new trial value in [tmin, tmax]. */ if (tmax < newt) newt = tmax; if (newt < tmin) newt = tmin; /* Redefine the new trial value if it is close to the upper bound of the interval. */ if (*brackt && bound) { mq = *x + 0.66 * (*y - *x); if (*x < *y) { if (mq < newt) newt = mq; } else { if (newt < mq) newt = mq; } } /* Return the new trial value. */ *t = newt; return 0; } static lbfgsfloatval_t owlqn_x1norm( const lbfgsfloatval_t* x, const int start, const int n ) { int i; lbfgsfloatval_t norm = 0.; for (i = start;i < n;++i) { norm += fabs(x[i]); } return norm; } static void owlqn_pseudo_gradient( lbfgsfloatval_t* pg, const lbfgsfloatval_t* x, const lbfgsfloatval_t* g, const int n, const lbfgsfloatval_t c, const int start, const int end ) { int i; /* Compute the negative of gradients. */ for (i = 0;i < start;++i) { pg[i] = g[i]; } /* Compute the psuedo-gradients. */ for (i = start;i < end;++i) { if (x[i] < 0.) { /* Differentiable. */ pg[i] = g[i] - c; } else if (0. < x[i]) { /* Differentiable. */ pg[i] = g[i] + c; } else { if (g[i] < -c) { /* Take the right partial derivative. */ pg[i] = g[i] + c; } else if (c < g[i]) { /* Take the left partial derivative. */ pg[i] = g[i] - c; } else { pg[i] = 0.; } } } for (i = end;i < n;++i) { pg[i] = g[i]; } } static void owlqn_project( lbfgsfloatval_t* d, const lbfgsfloatval_t* sign, const int start, const int end ) { int i; for (i = start;i < end;++i) { if (d[i] * sign[i] <= 0) { d[i] = 0; } } } igraph/src/dsaup2.f0000644000176000001440000010001212325527073013710 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsaup2 c c\Description: c Intermediate level interface called by igraphdsaupd. c c\Usage: c call igraphdsaup2 c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, c ISHIFT, MXITER, V, LDV, H, LDH, RITZ, BOUNDS, Q, LDQ, WORKL, c IPNTR, WORKD, INFO ) c c\Arguments c c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdsaupd. c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdsaupd. c c NP Integer. (INPUT/OUTPUT) c Contains the number of implicit shifts to apply during c each Arnoldi/Lanczos iteration. c If ISHIFT=1, NP is adjusted dynamically at each iteration c to accelerate convergence and prevent stagnation. c This is also roughly equal to the number of matrix-vector c products (involving the operator OP) per Arnoldi iteration. c The logic for adjusting is contained within the current c subroutine. c If ISHIFT=0, NP is the number of shifts the user needs c to provide via reverse comunication. 0 < NP < NCV-NEV. c NP may be less than NCV-NEV since a leading block of the current c upper Tridiagonal matrix has split off and contains "unwanted" c Ritz values. c Upon termination of the IRA iteration, NP contains the number c of "converged" wanted Ritz values. c c IUPD Integer. (INPUT) c IUPD .EQ. 0: use explicit restart instead implicit update. c IUPD .NE. 0: use implicit update. c c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) c The Lanczos basis vectors. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c H Double precision (NEV+NP) by 2 array. (OUTPUT) c H is used to store the generated symmetric tridiagonal matrix c The subdiagonal is stored in the first column of H starting c at H(2,1). The main diagonal is stored in the igraphsecond column c of H starting at H(1,2). If igraphdsaup2 converges store the c B-norm of the final residual vector in H(1,1). c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c RITZ Double precision array of length NEV+NP. (OUTPUT) c RITZ(1:NEV) contains the computed Ritz values of OP. c c BOUNDS Double precision array of length NEV+NP. (OUTPUT) c BOUNDS(1:NEV) contain the error bounds corresponding to RITZ. c c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) c Private (replicated) work array used to accumulate the c rotation in the shift application step. c c LDQ Integer. (INPUT) c Leading dimension of Q exactly as declared in the calling c program. c c WORKL Double precision array of length at least 3*(NEV+NP). (INPUT/WORKSPACE) c Private (replicated) array on each PE or array allocated on c the front end. It is used in the computation of the c tridiagonal eigenvalue problem, the calculation and c application of the shifts and convergence checking. c If ISHIFT .EQ. O and IDO .EQ. 3, the first NP locations c of WORKL are used in reverse communication to hold the user c supplied shifts. c c IPNTR Integer array of length 3. (OUTPUT) c Pointer to mark the starting locations in the WORKD for c vectors used by the Lanczos iteration. c ------------------------------------------------------------- c IPNTR(1): pointer to the current operand vector X. c IPNTR(2): pointer to the current result vector Y. c IPNTR(3): pointer to the vector B * X when used in one of c the spectral transformation modes. X is the current c operand. c ------------------------------------------------------------- c c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) c Distributed array to be used in the basic Lanczos iteration c for reverse communication. The user should not use WORKD c as temporary workspace during the iteration !!!!!!!!!! c See Data Distribution Note in igraphdsaupd. c c INFO Integer. (INPUT/OUTPUT) c If INFO .EQ. 0, a randomly initial residual vector is used. c If INFO .NE. 0, RESID contains the initial residual vector, c possibly from a previous run. c Error flag on output. c = 0: Normal return. c = 1: All possible eigenvalues of OP has been found. c NP returns the size of the invariant subspace c spanning the operator OP. c = 2: No shifts could be applied. c = -8: Error return from trid. eigenvalue calculation; c This should never happen. c = -9: Starting vector is zero. c = -9999: Could not build an Lanczos factorization. c Size that was built in returned in NP. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, c 1980. c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", c Computer Physics Communications, 53 (1989), pp 169-179. c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to c Implement the Spectral Transformation", Math. Comp., 48 (1987), c pp 663-673. c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", c SIAM J. Matr. Anal. Apps., January (1993). c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines c for Updating the QR decomposition", ACM TOMS, December 1990, c Volume 16 Number 4, pp 369-377. c c\Routines called: c igraphdgetv0 ARPACK initial vector generation routine. c igraphdsaitr ARPACK Lanczos factorization routine. c igraphdsapps ARPACK application of implicit shifts routine. c igraphdsconv ARPACK convergence of Ritz values routine. c igraphdseigt ARPACK compute Ritz values and error bounds routine. c igraphdsgets ARPACK reorder Ritz values and error bounds routine. c igraphdsortr ARPACK sorting routine. c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdvout ARPACK utility routine that prints vectors. c dlamch LAPACK routine that determines machine constants. c dcopy Level 1 BLAS that copies one vector to another. c ddot Level 1 BLAS that computes the scalar product of two vectors. c dnrm2 Level 1 BLAS that computes the norm of a vector. c dscal Level 1 BLAS that scales a vector. c dswap Level 1 BLAS that swaps two vectors. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c 12/15/93: Version ' 2.4' c xx/xx/95: Version ' 2.4'. (R.B. Lehoucq) c c\SCCS Information: @(#) c FILE: saup2.F SID: 2.6 DATE OF SID: 8/16/96 RELEASE: 2 c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsaup2 & ( ido, bmat, n, which, nev, np, tol, resid, mode, iupd, & ishift, mxiter, v, ldv, h, ldh, ritz, bounds, & q, ldq, workl, ipntr, workd, info ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat*1, which*2 integer ido, info, ishift, iupd, ldh, ldq, ldv, mxiter, & n, mode, nev, np Double precision & tol c c %-----------------% c | Array Arguments | c %-----------------% c integer ipntr(3) Double precision & bounds(nev+np), h(ldh,2), q(ldq,nev+np), resid(n), & ritz(nev+np), v(ldv,nev+np), workd(3*n), & workl(3*(nev+np)) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c character wprime*2 logical cnorm, getv0, initv, update, ushift integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, & np0, nptemp, nevd2, nevm2, kp(3) Double precision & rnorm, temp, eps23 save cnorm, getv0, initv, update, ushift, & iter, kplusp, msglvl, nconv, nev0, np0, & rnorm, eps23 c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, igraphdgetv0, igraphdsaitr, dscal, & igraphdsconv, igraphdseigt, igraphdsgets, & igraphdsapps, igraphdsortr, igraphdvout, igraphivout, & igraphsecond, dswap c c %--------------------% c | External Functions | c %--------------------% c Double precision & ddot, dnrm2, dlamch external ddot, dnrm2, dlamch c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic min c c %-----------------------% c | Executable Statements | c %-----------------------% c if (ido .eq. 0) then c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = msaup2 c c %---------------------------------% c | Set machine dependent constant. | c %---------------------------------% c eps23 = dlamch('Epsilon-Machine') eps23 = eps23**(2.0D+0/3.0D+0) c c %-------------------------------------% c | nev0 and np0 are integer variables | c | hold the initial values of NEV & NP | c %-------------------------------------% c nev0 = nev np0 = np c c %-------------------------------------% c | kplusp is the bound on the largest | c | Lanczos factorization built. | c | nconv is the current number of | c | "converged" eigenvlues. | c | iter is the counter on the current | c | iteration step. | c %-------------------------------------% c kplusp = nev0 + np0 nconv = 0 iter = 0 c c %--------------------------------------------% c | Set flags for computing the first NEV steps | c | of the Lanczos factorization. | c %--------------------------------------------% c getv0 = .true. update = .false. ushift = .false. cnorm = .false. c if (info .ne. 0) then c c %--------------------------------------------% c | User provides the initial residual vector. | c %--------------------------------------------% c initv = .true. info = 0 else initv = .false. end if end if c c %---------------------------------------------% c | Get a possibly random starting vector and | c | force it into the range of the operator OP. | c %---------------------------------------------% c 10 continue c if (getv0) then call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, & rnorm, ipntr, workd, info) c if (ido .ne. 99) go to 9000 c if (rnorm .eq. zero) then c c %-----------------------------------------% c | The initial vector is zero. Error exit. | c %-----------------------------------------% c info = -9 go to 1200 end if getv0 = .false. ido = 0 end if c c %------------------------------------------------------------% c | Back from reverse communication: continue with update step | c %------------------------------------------------------------% c if (update) go to 20 c c %-------------------------------------------% c | Back from computing user specified shifts | c %-------------------------------------------% c if (ushift) go to 50 c c %-------------------------------------% c | Back from computing residual norm | c | at the end of the current iteration | c %-------------------------------------% c if (cnorm) go to 100 c c %----------------------------------------------------------% c | Compute the first NEV steps of the Lanczos factorization | c %----------------------------------------------------------% c call igraphdsaitr (ido, bmat, n, 0, nev0, mode, resid, rnorm, v, & ldv, h, ldh, ipntr, workd, info) c c %---------------------------------------------------% c | ido .ne. 99 implies use of reverse communication | c | to compute operations involving OP and possibly B | c %---------------------------------------------------% c if (ido .ne. 99) go to 9000 c if (info .gt. 0) then c c %-----------------------------------------------------% c | igraphdsaitr was unable to build an Lanczos factorization | c | of length NEV0. INFO is returned with the size of | c | the factorization built. Exit main loop. | c %-----------------------------------------------------% c np = info mxiter = iter info = -9999 go to 1200 end if c c %--------------------------------------------------------------% c | | c | M A I N LANCZOS I T E R A T I O N L O O P | c | Each iteration implicitly restarts the Lanczos | c | factorization in place. | c | | c %--------------------------------------------------------------% c 1000 continue c iter = iter + 1 c if (msglvl .gt. 0) then call igraphivout (logfil, 1, iter, ndigit, & '_saup2: **** Start of major iteration number ****') end if if (msglvl .gt. 1) then call igraphivout (logfil, 1, nev, ndigit, & '_saup2: The length of the current Lanczos factorization') call igraphivout (logfil, 1, np, ndigit, & '_saup2: Extend the Lanczos factorization by') end if c c %------------------------------------------------------------% c | Compute NP additional steps of the Lanczos factorization. | c %------------------------------------------------------------% c ido = 0 20 continue update = .true. c call igraphdsaitr (ido, bmat, n, nev, np, mode, resid, rnorm, & v, ldv, h, ldh, ipntr, workd, info) c c %---------------------------------------------------% c | ido .ne. 99 implies use of reverse communication | c | to compute operations involving OP and possibly B | c %---------------------------------------------------% c if (ido .ne. 99) go to 9000 c if (info .gt. 0) then c c %-----------------------------------------------------% c | igraphdsaitr was unable to build an Lanczos factorization | c | of length NEV0+NP0. INFO is returned with the size | c | of the factorization built. Exit main loop. | c %-----------------------------------------------------% c np = info mxiter = iter info = -9999 go to 1200 end if update = .false. c if (msglvl .gt. 1) then call igraphdvout (logfil, 1, rnorm, ndigit, & '_saup2: Current B-norm of residual for factorization') end if c c %--------------------------------------------------------% c | Compute the eigenvalues and corresponding error bounds | c | of the current symmetric tridiagonal matrix. | c %--------------------------------------------------------% c call igraphdseigt (rnorm, kplusp, h, ldh, ritz, bounds, workl, & ierr) c if (ierr .ne. 0) then info = -8 go to 1200 end if c c %----------------------------------------------------% c | Make a copy of eigenvalues and corresponding error | c | bounds obtained from _seigt. | c %----------------------------------------------------% c call dcopy(kplusp, ritz, 1, workl(kplusp+1), 1) call dcopy(kplusp, bounds, 1, workl(2*kplusp+1), 1) c c %---------------------------------------------------% c | Select the wanted Ritz values and their bounds | c | to be used in the convergence test. | c | The selection is based on the requested number of | c | eigenvalues instead of the current NEV and NP to | c | prevent possible misconvergence. | c | * Wanted Ritz values := RITZ(NP+1:NEV+NP) | c | * Shifts := RITZ(1:NP) := WORKL(1:NP) | c %---------------------------------------------------% c nev = nev0 np = np0 call igraphdsgets (ishift, which, nev, np, ritz, bounds, workl) c c %-------------------% c | Convergence test. | c %-------------------% c call dcopy (nev, bounds(np+1), 1, workl(np+1), 1) call igraphdsconv (nev, ritz(np+1), workl(np+1), tol, nconv) c if (msglvl .gt. 2) then kp(1) = nev kp(2) = np kp(3) = nconv call igraphivout (logfil, 3, kp, ndigit, & '_saup2: NEV, NP, NCONV are') call igraphdvout (logfil, kplusp, ritz, ndigit, & '_saup2: The eigenvalues of H') call igraphdvout (logfil, kplusp, bounds, ndigit, & '_saup2: Ritz estimates of the current NCV Ritz values') end if c c %---------------------------------------------------------% c | Count the number of unwanted Ritz values that have zero | c | Ritz estimates. If any Ritz estimates are equal to zero | c | then a leading block of H of order equal to at least | c | the number of Ritz values with zero Ritz estimates has | c | split off. None of these Ritz values may be removed by | c | shifting. Decrease NP the number of shifts to apply. If | c | no shifts may be applied, then prepare to exit | c %---------------------------------------------------------% c nptemp = np do 30 j=1, nptemp if (bounds(j) .eq. zero) then np = np - 1 nev = nev + 1 end if 30 continue c if ( (nconv .ge. nev0) .or. & (iter .gt. mxiter) .or. & (np .eq. 0) ) then c c %------------------------------------------------% c | Prepare to exit. Put the converged Ritz values | c | and corresponding bounds in RITZ(1:NCONV) and | c | BOUNDS(1:NCONV) respectively. Then sort. Be | c | careful when NCONV > NP since we don't want to | c | swap overlapping locations. | c %------------------------------------------------% c if (which .eq. 'BE') then c c %-----------------------------------------------------% c | Both ends of the spectrum are requested. | c | Sort the eigenvalues into algebraically decreasing | c | order first then swap low end of the spectrum next | c | to high end in appropriate locations. | c | NOTE: when np < floor(nev/2) be careful not to swap | c | overlapping locations. | c %-----------------------------------------------------% c wprime = 'SA' call igraphdsortr (wprime, .true., kplusp, ritz, bounds) nevd2 = nev / 2 nevm2 = nev - nevd2 if ( nev .gt. 1 ) then call dswap ( min(nevd2,np), ritz(nevm2+1), 1, & ritz( max(kplusp-nevd2+1,kplusp-np+1) ), 1) call dswap ( min(nevd2,np), bounds(nevm2+1), 1, & bounds( max(kplusp-nevd2+1,kplusp-np)+1 ), 1) end if c else c c %--------------------------------------------------% c | LM, SM, LA, SA case. | c | Sort the eigenvalues of H into the an order that | c | is opposite to WHICH, and apply the resulting | c | order to BOUNDS. The eigenvalues are sorted so | c | that the wanted part are always within the first | c | NEV locations. | c %--------------------------------------------------% c if (which .eq. 'LM') wprime = 'SM' if (which .eq. 'SM') wprime = 'LM' if (which .eq. 'LA') wprime = 'SA' if (which .eq. 'SA') wprime = 'LA' c call igraphdsortr (wprime, .true., kplusp, ritz, bounds) c end if c c %--------------------------------------------------% c | Scale the Ritz estimate of each Ritz value | c | by 1 / max(eps23,magnitude of the Ritz value). | c %--------------------------------------------------% c do 35 j = 1, nev0 temp = max( eps23, abs(ritz(j)) ) bounds(j) = bounds(j)/temp 35 continue c c %----------------------------------------------------% c | Sort the Ritz values according to the scaled Ritz | c | esitmates. This will push all the converged ones | c | towards the front of ritzr, ritzi, bounds | c | (in the case when NCONV < NEV.) | c %----------------------------------------------------% c wprime = 'LA' call igraphdsortr(wprime, .true., nev0, bounds, ritz) c c %----------------------------------------------% c | Scale the Ritz estimate back to its original | c | value. | c %----------------------------------------------% c do 40 j = 1, nev0 temp = max( eps23, abs(ritz(j)) ) bounds(j) = bounds(j)*temp 40 continue c c %--------------------------------------------------% c | Sort the "converged" Ritz values again so that | c | the "threshold" values and their associated Ritz | c | estimates appear at the appropriate position in | c | ritz and bound. | c %--------------------------------------------------% c if (which .eq. 'BE') then c c %------------------------------------------------% c | Sort the "converged" Ritz values in increasing | c | order. The "threshold" values are in the | c | middle. | c %------------------------------------------------% c wprime = 'LA' call igraphdsortr(wprime, .true., nconv, ritz, bounds) c else c c %----------------------------------------------% c | In LM, SM, LA, SA case, sort the "converged" | c | Ritz values according to WHICH so that the | c | "threshold" value appears at the front of | c | ritz. | c %----------------------------------------------% call igraphdsortr(which, .true., nconv, ritz, bounds) c end if c c %------------------------------------------% c | Use h( 1,1 ) as storage to communicate | c | rnorm to _seupd if needed | c %------------------------------------------% c h(1,1) = rnorm c if (msglvl .gt. 1) then call igraphdvout (logfil, kplusp, ritz, ndigit, & '_saup2: Sorted Ritz values.') call igraphdvout (logfil, kplusp, bounds, ndigit, & '_saup2: Sorted ritz estimates.') end if c c %------------------------------------% c | Max iterations have been exceeded. | c %------------------------------------% c if (iter .gt. mxiter .and. nconv .lt. nev) info = 1 c c %---------------------% c | No shifts to apply. | c %---------------------% c if (np .eq. 0 .and. nconv .lt. nev0) info = 2 c np = nconv go to 1100 c else if (nconv .lt. nev .and. ishift .eq. 1) then c c %---------------------------------------------------% c | Do not have all the requested eigenvalues yet. | c | To prevent possible stagnation, adjust the number | c | of Ritz values and the shifts. | c %---------------------------------------------------% c nevbef = nev nev = nev + min (nconv, np/2) if (nev .eq. 1 .and. kplusp .ge. 6) then nev = kplusp / 2 else if (nev .eq. 1 .and. kplusp .gt. 2) then nev = 2 end if np = kplusp - nev c c %---------------------------------------% c | If the size of NEV was just increased | c | resort the eigenvalues. | c %---------------------------------------% c if (nevbef .lt. nev) & call igraphdsgets (ishift, which, nev, np, ritz, bounds, & workl) c end if c if (msglvl .gt. 0) then call igraphivout (logfil, 1, nconv, ndigit, & '_saup2: no. of "converged" Ritz values at this iter.') if (msglvl .gt. 1) then kp(1) = nev kp(2) = np call igraphivout (logfil, 2, kp, ndigit, & '_saup2: NEV and NP are') call igraphdvout (logfil, nev, ritz(np+1), ndigit, & '_saup2: "wanted" Ritz values.') call igraphdvout (logfil, nev, bounds(np+1), ndigit, & '_saup2: Ritz estimates of the "wanted" values ') end if end if c if (ishift .eq. 0) then c c %-----------------------------------------------------% c | User specified shifts: reverse communication to | c | compute the shifts. They are returned in the first | c | NP locations of WORKL. | c %-----------------------------------------------------% c ushift = .true. ido = 3 go to 9000 end if c 50 continue c c %------------------------------------% c | Back from reverse communication; | c | User specified shifts are returned | c | in WORKL(1:*NP) | c %------------------------------------% c ushift = .false. c c c %---------------------------------------------------------% c | Move the NP shifts to the first NP locations of RITZ to | c | free up WORKL. This is for the non-exact shift case; | c | in the exact shift case, igraphdsgets already handles this. | c %---------------------------------------------------------% c if (ishift .eq. 0) call dcopy (np, workl, 1, ritz, 1) c if (msglvl .gt. 2) then call igraphivout (logfil, 1, np, ndigit, & '_saup2: The number of shifts to apply ') call igraphdvout (logfil, np, workl, ndigit, & '_saup2: shifts selected') if (ishift .eq. 1) then call igraphdvout (logfil, np, bounds, ndigit, & '_saup2: corresponding Ritz estimates') end if end if c c %---------------------------------------------------------% c | Apply the NP0 implicit shifts by QR bulge chasing. | c | Each shift is applied to the entire tridiagonal matrix. | c | The first 2*N locations of WORKD are used as workspace. | c | After igraphdsapps is done, we have a Lanczos | c | factorization of length NEV. | c %---------------------------------------------------------% c call igraphdsapps (n, nev, np, ritz, v, ldv, h, ldh, resid, & q, ldq, workd) c c %---------------------------------------------% c | Compute the B-norm of the updated residual. | c | Keep B*RESID in WORKD(1:N) to be used in | c | the first step of the next call to igraphdsaitr. | c %---------------------------------------------% c cnorm = .true. call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, resid, 1, workd(n+1), 1) ipntr(1) = n + 1 ipntr(2) = 1 ido = 2 c c %----------------------------------% c | Exit in order to compute B*RESID | c %----------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd, 1) end if c 100 continue c c %----------------------------------% c | Back from reverse communication; | c | WORKD(1:N) := B*RESID | c %----------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c if (bmat .eq. 'G') then rnorm = ddot (n, resid, 1, workd, 1) rnorm = sqrt(abs(rnorm)) else if (bmat .eq. 'I') then rnorm = dnrm2(n, resid, 1) end if cnorm = .false. 130 continue c if (msglvl .gt. 2) then call igraphdvout (logfil, 1, rnorm, ndigit, & '_saup2: B-norm of residual for NEV factorization') call igraphdvout (logfil, nev, h(1,2), ndigit, & '_saup2: main diagonal of compressed H matrix') call igraphdvout (logfil, nev-1, h(2,1), ndigit, & '_saup2: subdiagonal of compressed H matrix') end if c go to 1000 c c %---------------------------------------------------------------% c | | c | E N D O F M A I N I T E R A T I O N L O O P | c | | c %---------------------------------------------------------------% c 1100 continue c mxiter = iter nev = nconv c 1200 continue ido = 99 c c %------------% c | Error exit | c %------------% c call igraphsecond (t1) tsaup2 = t1 - t0 c 9000 continue return c c %---------------% c | End of igraphdsaup2 | c %---------------% c end igraph/src/cs_multiply.c0000644000176000001440000000464712325527073015074 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* C = A*B */ cs *cs_multiply (const cs *A, const cs *B) { CS_INT p, j, nz = 0, anz, *Cp, *Ci, *Bp, m, n, bnz, *w, values, *Bi ; CS_ENTRY *x, *Bx, *Cx ; cs *C ; if (!CS_CSC (A) || !CS_CSC (B)) return (NULL) ; /* check inputs */ if (A->n != B->m) return (NULL) ; m = A->m ; anz = A->p [A->n] ; n = B->n ; Bp = B->p ; Bi = B->i ; Bx = B->x ; bnz = Bp [n] ; w = cs_calloc (m, sizeof (CS_INT)) ; /* get workspace */ values = (A->x != NULL) && (Bx != NULL) ; x = values ? cs_malloc (m, sizeof (CS_ENTRY)) : NULL ; /* get workspace */ C = cs_spalloc (m, n, anz + bnz, values, 0) ; /* allocate result */ if (!C || !w || (values && !x)) return (cs_done (C, w, x, 0)) ; Cp = C->p ; for (j = 0 ; j < n ; j++) { if (nz + m > C->nzmax && !cs_sprealloc (C, 2*(C->nzmax)+m)) { return (cs_done (C, w, x, 0)) ; /* out of memory */ } Ci = C->i ; Cx = C->x ; /* C->i and C->x may be reallocated */ Cp [j] = nz ; /* column j of C starts here */ for (p = Bp [j] ; p < Bp [j+1] ; p++) { nz = cs_scatter (A, Bi [p], Bx ? Bx [p] : 1, w, x, j+1, C, nz) ; } if (values) for (p = Cp [j] ; p < nz ; p++) Cx [p] = x [Ci [p]] ; } Cp [n] = nz ; /* finalize the last column of C */ cs_sprealloc (C, 0) ; /* remove extra space from C */ return (cs_done (C, w, x, 1)) ; /* success; free workspace, return C */ } igraph/src/vector_ptr.c0000644000176000001440000004610112325527074014707 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_vector_ptr.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include /** * \section about_igraph_vector_ptr_objects Pointer vectors * (igraph_vector_ptr_t) * * The \type igraph_vector_ptr_t data type is very similar to * the \type igraph_vector_t type, but it stores generic pointers instead of * real numbers. * * This type has the same space complexity as \type * igraph_vector_t, and most implemented operations work the same way * as for \type igraph_vector_t. * * This type is mostly used to pass to or receive from a set of * graphs to some \a igraph functions, such as \ref * igraph_decompose(), which decomposes a graph to connected * components. * * The same \ref VECTOR macro used for ordinary vectors can be * used for pointer vectors as well, please note that a typeless * generic pointer will be provided by this macro and you may need to * cast it to a specific pointer before starting to work with it. * * Pointer vectors may have an associated item destructor function * which takes a pointer and returns nothing. The item destructor will * be called on each item in the pointer vector when it is destroyed by * \ref igraph_vector_ptr_destroy() or \ref igraph_vector_ptr_destroy_all(), * or when its elements are freed by \ref igraph_vector_ptr_free_all(). * Note that the semantics of an item destructor does not coincide with * C++ destructors; for instance, when a pointer vector is resized to a * smaller size, the extra items will \em not be destroyed automatically! * Nevertheless, item destructors may become handy in many cases; for * instance, a vector of graphs generated by \ref igraph_decompose() can * be destroyed with a single call to \ref igraph_vector_ptr_destroy_all() * if the item destructor is set to \ref igraph_destroy(). */ /** * \ingroup vectorptr * \function igraph_vector_ptr_init * \brief Initialize a pointer vector (constructor). * * * This is the constructor of the pointer vector data type. All * pointer vectors constructed this way should be destroyed via * calling \ref igraph_vector_ptr_destroy(). * \param v Pointer to an uninitialized * igraph_vector_ptr_t object, to be created. * \param size Integer, the size of the pointer vector. * \return Error code: * \c IGRAPH_ENOMEM if out of memory * * Time complexity: operating system dependent, the amount of \quote * time \endquote required to allocate \p size elements. */ int igraph_vector_ptr_init (igraph_vector_ptr_t* v, int long size) { long int alloc_size= size > 0 ? size : 1; assert(v != NULL); if (size < 0) { size=0; } v->stor_begin=igraph_Calloc(alloc_size, void*); if (v->stor_begin==0) { IGRAPH_ERROR("vector ptr init failed", IGRAPH_ENOMEM); } v->stor_end=v->stor_begin + alloc_size; v->end=v->stor_begin+size; v->item_destructor=0; return 0; } /** */ const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, void *const *data, long int length) { igraph_vector_ptr_t *v2=(igraph_vector_ptr_t*) v; v2->stor_begin=(void **)data; v2->stor_end=(void**)data+length; v2->end=v2->stor_end; v2->item_destructor=0; return v; } /** * \ingroup vectorptr * \function igraph_vector_ptr_destroy * \brief Destroys a pointer vector. * * * The destructor for pointer vectors. * \param v Pointer to the pointer vector to destroy. * * Time complexity: operating system dependent, the \quote time * \endquote required to deallocate O(n) bytes, n is the number of * elements allocated for the pointer vector (not necessarily the * number of elements in the vector). */ void igraph_vector_ptr_destroy (igraph_vector_ptr_t* v) { assert(v != 0); if (v->stor_begin != 0) { igraph_Free(v->stor_begin); v->stor_begin = NULL; } } void igraph_i_vector_ptr_call_item_destructor_all(igraph_vector_ptr_t* v) { void **ptr; if (v->item_destructor != 0) { for (ptr=v->stor_begin; ptrend; ptr++) { if (*ptr != 0) v->item_destructor(*ptr); } } } /** * \ingroup vectorptr * \function igraph_vector_ptr_free_all * \brief Frees all the elements of a pointer vector. * * If an item destructor is set for this pointer vector, this function will * first call the destructor on all elements of the vector and then * free all the elements using free(). If an item destructor is not set, * the elements will simply be freed. * * \param v Pointer to the pointer vector whose elements will be freed. * * Time complexity: operating system dependent, the \quote time * \endquote required to call the destructor n times and then * deallocate O(n) pointers, each pointing to a memory area of * arbitrary size. n is the number of elements in the pointer vector. */ void igraph_vector_ptr_free_all (igraph_vector_ptr_t* v) { void **ptr; assert(v != 0); assert(v->stor_begin != 0); igraph_i_vector_ptr_call_item_destructor_all(v); for (ptr=v->stor_begin; ptrend; ptr++) { igraph_Free(*ptr); } } /** * \ingroup vectorptr * \function igraph_vector_ptr_destroy_all * \brief Frees all the elements and destroys the pointer vector. * * This function is equivalent to \ref igraph_vector_ptr_free_all() * followed by \ref igraph_vector_ptr_destroy(). * * \param v Pointer to the pointer vector to destroy. * * Time complexity: operating system dependent, the \quote time * \endquote required to deallocate O(n) pointers, each pointing to * a memory area of arbitrary size, plus the \quote time \endquote * required to deallocate O(n) bytes, n being the number of elements * allocated for the pointer vector (not necessarily the number of * elements in the vector). */ void igraph_vector_ptr_destroy_all (igraph_vector_ptr_t* v) { assert(v != 0); assert(v->stor_begin != 0); igraph_vector_ptr_free_all(v); igraph_vector_ptr_set_item_destructor(v, 0); igraph_vector_ptr_destroy(v); } /** * \ingroup vectorptr * \brief Reserves memory for a pointer vector for later use. * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_vector_ptr_reserve (igraph_vector_ptr_t* v, long int size) { long int actual_size=igraph_vector_ptr_size(v); void **tmp; assert(v != NULL); assert(v->stor_begin != NULL); if (size <= igraph_vector_ptr_size(v)) { return 0; } tmp=igraph_Realloc(v->stor_begin, (size_t) size, void*); if (tmp==0) { IGRAPH_ERROR("vector ptr reserve failed", IGRAPH_ENOMEM); } v->stor_begin=tmp; v->stor_end=v->stor_begin + size; v->end=v->stor_begin+actual_size; return 0; } /** * \ingroup vectorptr * \brief Decides whether the pointer vector is empty. */ igraph_bool_t igraph_vector_ptr_empty (const igraph_vector_ptr_t* v) { assert(v != NULL); assert(v->stor_begin != NULL); return v->stor_begin == v->end; } /** * \ingroup vectorptr * \function igraph_vector_ptr_size * \brief Gives the number of elements in the pointer vector. * * \param v The pointer vector object. * \return The size of the object, ie. the number of pointers stored. * * Time complexity: O(1). */ long int igraph_vector_ptr_size (const igraph_vector_ptr_t* v) { assert(v != NULL); /* assert(v->stor_begin != NULL); */ /* TODO */ return v->end - v->stor_begin; } /** * \ingroup vectorptr * \function igraph_vector_ptr_clear * \brief Removes all elements from a pointer vector. * * * This function resizes a pointer to vector to zero length. Note that * the pointed objects are \em not deallocated, you should call * free() on them, or make sure that their allocated memory is freed * in some other way, you'll get memory leaks otherwise. If you have * set up an item destructor earlier, the destructor will be called * on every element. * * * Note that the current implementation of this function does * \em not deallocate the memory required for storing the * pointers, so making a pointer vector smaller this way does not give * back any memory. This behavior might change in the future. * \param v The pointer vector to clear. * * Time complexity: O(1). */ void igraph_vector_ptr_clear (igraph_vector_ptr_t* v) { assert(v != NULL); assert(v->stor_begin != NULL); igraph_i_vector_ptr_call_item_destructor_all(v); v->end = v->stor_begin; } /** * \ingroup vectorptr * \function igraph_vector_ptr_push_back * \brief Appends an element to the back of a pointer vector. * * \param v The pointer vector. * \param e The new element to include in the pointer vector. * \return Error code. * \sa igraph_vector_push_back() for the corresponding operation of * the ordinary vector type. * * Time complexity: O(1) or O(n), n is the number of elements in the * vector. The pointer vector implementation ensures that n subsequent * push_back operations need O(n) time to complete. */ int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e) { assert(v != NULL); assert(v->stor_begin != NULL); /* full, allocate more storage */ if (v->stor_end == v->end) { long int new_size = igraph_vector_ptr_size(v) * 2; if (new_size == 0) { new_size = 1; } IGRAPH_CHECK(igraph_vector_ptr_reserve(v, new_size)); } *(v->end) = e; v->end += 1; return 0; } void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v) { void *tmp; assert(v != NULL); assert(v->stor_begin != NULL); assert(v->stor_begin != v->end); tmp=*(v->end); v->end -= 1; return tmp; } /** * \ingroup vectorptr * \function igraph_vector_ptr_insert * \brief Inserts a single element into a pointer vector. * * Note that this function does not do range checking. Insertion will shift the * elements from the position given to the end of the vector one position to the * right, and the new element will be inserted in the empty space created at * the given position. The size of the vector will increase by one. * * \param v The pointer vector object. * \param pos The position where the new element is inserted. * \param e The inserted element */ int igraph_vector_ptr_insert(igraph_vector_ptr_t* v, long int pos, void* e) { long int size = igraph_vector_ptr_size(v); IGRAPH_CHECK(igraph_vector_ptr_resize(v, size+1)); if (posstor_begin+pos+1, v->stor_begin+pos, sizeof(void*) * (size_t) (size-pos)); } v->stor_begin[pos] = e; return 0; } /** * \ingroup vectorptr * \function igraph_vector_ptr_e * \brief Access an element of a pointer vector. * * \param v Pointer to a pointer vector. * \param pos The index of the pointer to return. * \return The pointer at \p pos position. * * Time complexity: O(1). */ void* igraph_vector_ptr_e (const igraph_vector_ptr_t* v, long int pos) { assert(v != NULL); assert(v->stor_begin != NULL); return * (v->stor_begin + pos); } /** * \ingroup vectorptr * \function igraph_vector_ptr_set * \brief Assign to an element of a pointer vector. * * \param v Pointer to a pointer vector. * \param pos The index of the pointer to update. * \param value The new pointer to set in the vector. * * Time complexity: O(1). */ void igraph_vector_ptr_set (igraph_vector_ptr_t* v, long int pos, void* value) { assert(v != NULL); assert(v->stor_begin != NULL); *(v->stor_begin + pos) = value; } /** * \ingroup vectorptr * \brief Set all elements of a pointer vector to the NULL pointer. */ void igraph_vector_ptr_null (igraph_vector_ptr_t* v) { assert(v != NULL); assert(v->stor_begin != NULL); if (igraph_vector_ptr_size(v)>0) { memset(v->stor_begin, 0, sizeof(void*) * (size_t) igraph_vector_ptr_size(v)); } } /** * \ingroup vectorptr * \function igraph_vector_ptr_resize * \brief Resizes a pointer vector. * * * Note that if a vector is made smaller the pointed object are not * deallocated by this function and the item destructor is not called * on the extra elements. * * \param v A pointer vector. * \param newsize The new size of the pointer vector. * \return Error code. * * Time complexity: O(1) if the vector if made smaller. Operating * system dependent otherwise, the amount of \quote time \endquote * needed to allocate the memory for the vector elements. */ int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize) { IGRAPH_CHECK(igraph_vector_ptr_reserve(v, newsize)); v->end = v->stor_begin+newsize; return 0; } /** * \ingroup vectorptr * \brief Initializes a pointer vector from an array (constructor). * * \return Error code: * \c IGRAPH_ENOMEM if out of memory */ int igraph_vector_ptr_init_copy(igraph_vector_ptr_t *v, void* *data, long int length) { v->stor_begin=igraph_Calloc(length, void*); if (v->stor_begin==0) { IGRAPH_ERROR("cannot init ptr vector from array", IGRAPH_ENOMEM); } v->stor_end=v->stor_begin+length; v->end=v->stor_end; v->item_destructor=0; memcpy(v->stor_begin, data, (size_t) length * sizeof(void*)); return 0; } /** * \ingroup vectorptr * \brief Copy the contents of a pointer vector to a regular C array. */ void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to) { assert(v != NULL); assert(v->stor_begin != NULL); if (v->end != v->stor_begin) { memcpy(to, v->stor_begin, sizeof(void*) * (size_t) (v->end - v->stor_begin)); } } /** * \ingroup vectorptr * \function igraph_vector_ptr_copy * \brief Copy a pointer vector (constructor). * * * This function creates a pointer vector by copying another one. This * is shallow copy, only the pointers in the vector will be copied. * * * It is potentially dangerous to copy a pointer vector with an associated * item destructor. The copied vector will inherit the item destructor, * which may cause problems when both vectors are destroyed as the items * might get destroyed twice. Make sure you know what you are doing when * copying a pointer vector with an item destructor, or unset the item * destructor on one of the vectors later. * * \param to Pointer to an uninitialized pointer vector object. * \param from A pointer vector object. * \return Error code: * \c IGRAPH_ENOMEM if out of memory * * Time complexity: O(n) if allocating memory for n elements can be * done in O(n) time. */ int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { assert(from != NULL); /* assert(from->stor_begin != NULL); */ /* TODO */ to->stor_begin=igraph_Calloc(igraph_vector_ptr_size(from), void*); if (to->stor_begin==0) { IGRAPH_ERROR("cannot copy ptr vector", IGRAPH_ENOMEM); } to->stor_end=to->stor_begin+igraph_vector_ptr_size(from); to->end=to->stor_end; to->item_destructor=from->item_destructor; memcpy(to->stor_begin, from->stor_begin, (size_t) igraph_vector_ptr_size(from)*sizeof(void*)); return 0; } /** * \ingroup vectorptr * \brief Remove an element from a pointer vector. */ void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos) { assert(v != NULL); assert(v->stor_begin != NULL); if (pos+1stor_begin+pos, v->stor_begin+pos+1, sizeof(void*) * (size_t) (igraph_vector_ptr_size(v)-pos-1)); } v->end--; } /** * \ingroup vectorptr * \brief Sort the pointer vector based on an external comparison function * * Sometimes it is necessary to sort the pointers in the vector based on * the property of the element being referenced by the pointer. This * function allows us to sort the vector based on an arbitrary external * comparison function which accepts two \c void* pointers \c p1 and \c p2 * and returns an integer less than, equal to or greater than zero if the * first argument is considered to be respectively less than, equal to, or * greater than the second. \c p1 and \c p2 will point to the pointer in the * vector, so they have to be double-dereferenced if one wants to get access * to the underlying object the address of which is stored in \c v . */ void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int (*compar)(const void*, const void*)) { qsort(v->stor_begin, (size_t) igraph_vector_ptr_size(v), sizeof(void*), compar); } int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v, const igraph_vector_int_t *idx) { void **tmp; int i, n=igraph_vector_int_size(idx); tmp=igraph_Calloc(n, void*); if (!tmp) { IGRAPH_ERROR("Cannot index pointer vector", IGRAPH_ENOMEM); } for (i=0; istor_begin); v->stor_begin = tmp; v->stor_end = v->end = tmp + n; return 0; } int igraph_vector_ptr_append (igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { long int origsize=igraph_vector_ptr_size(to); long int othersize=igraph_vector_ptr_size(from); long int i; IGRAPH_CHECK(igraph_vector_ptr_resize(to, origsize+othersize)); for (i=0; istor_begin[origsize]=from->stor_begin[i]; } return 0; } /** * \ingroup vectorptr * \function igraph_vector_ptr_set_item_destructor * \brief Sets the item destructor for this pointer vector. * * The item destructor is a function which will be called on every non-null * pointer stored in this vector when \ref igraph_vector_ptr_destroy(), * igraph_vector_ptr_destroy_all() or \ref igraph_vector_ptr_free_all() * is called. * * \return The old item destructor. * * Time complexity: O(1). */ igraph_finally_func_t* igraph_vector_ptr_set_item_destructor( igraph_vector_ptr_t *v, igraph_finally_func_t *func) { igraph_finally_func_t* result = v->item_destructor; v->item_destructor = func; return result; } /** * \ingroup vectorptr * \function igraph_vector_ptr_get_item_destructor * \brief Gets the current item destructor for this pointer vector. * * The item destructor is a function which will be called on every non-null * pointer stored in this vector when \ref igraph_vector_ptr_destroy(), * igraph_vector_ptr_destroy_all() or \ref igraph_vector_ptr_free_all() * is called. * * \return The current item destructor. * * Time complexity: O(1). */ igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v) { assert(v != 0); return v->item_destructor; } igraph/src/bliss_utils.cc0000644000176000001440000000267312325527072015223 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include "bliss_utils.hh" #include "bliss_bignum.hh" using namespace std; #include #if defined(BLISS_USE_GMP) namespace igraph { int BigNum::tostring(char **str) { *str=igraph_Calloc(mpz_sizeinbase(v, 10)+2, char); if (! *str) { IGRAPH_ERROR("Cannot convert big number to string", IGRAPH_ENOMEM); } mpz_get_str(*str, 10, v); return 0; } } #else namespace igraph { int BigNum::tostring(char **str) { int size=static_cast( (log(abs(v))/log(10.0))+4 ); *str=igraph_Calloc(size, char ); if (! *str) { IGRAPH_ERROR("Cannot convert big number to string", IGRAPH_ENOMEM); } std::stringstream ss; ss << v; strncpy(*str, ss.str().c_str(), size); return 0; } } #endif igraph/src/igraph_transitivity.h0000644000176000001440000000456412325527073016636 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_TRANSITIVITY_H #define IGRAPH_TRANSITIVITY_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_datatype.h" #include "igraph_constants.h" #include "igraph_iterators.h" __BEGIN_DECLS int igraph_transitivity_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode); int igraph_transitivity_local_undirected(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode); int igraph_transitivity_local_undirected1(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode); int igraph_transitivity_local_undirected2(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode); int igraph_transitivity_local_undirected4(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode); int igraph_transitivity_avglocal_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode); int igraph_transitivity_barrat(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, const igraph_vector_t *weights, const igraph_transitivity_mode_t mode); __END_DECLS #endif igraph/src/array.pmt0000644000176000001440000000477512325372072014223 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" int FUNCTION(igraph_array3,init)(TYPE(igraph_array3) *a, long int n1, long int n2, long int n3) { int ret; ret=FUNCTION(igraph_vector,init)(&a->data, n1*n2*n3); a->n1=n1; a->n2=n2; a->n3=n3; a->n1n2=n1*n2; return ret; } void FUNCTION(igraph_array3,destroy)(TYPE(igraph_array3) *a) { FUNCTION(igraph_vector,destroy)(&a->data); } long int FUNCTION(igraph_array3,size)(const TYPE(igraph_array3) *a) { return (a->n1n2) * (a->n3); } long int FUNCTION(igraph_array3,n)(const TYPE(igraph_array3) *a, long int idx) { switch (idx) { case 1: return a->n1; break; case 2: return a->n2; break; case 3: return a->n3; break; } return 0; } int FUNCTION(igraph_array3,resize)(TYPE(igraph_array3) *a, long int n1, long int n2, long int n3) { int ret=FUNCTION(igraph_vector,resize)(&a->data, n1*n2*n3); a->n1=n1; a->n2=n2; a->n3=n3; a->n1n2=n1*n2; return ret; } void FUNCTION(igraph_array3,null)(TYPE(igraph_array3) *a) { FUNCTION(igraph_vector,null)(&a->data); } BASE FUNCTION(igraph_array3,sum)(const TYPE(igraph_array3) *a) { return FUNCTION(igraph_vector,sum)(&a->data); } void FUNCTION(igraph_array3,scale)(TYPE(igraph_array3) *a, BASE by) { FUNCTION(igraph_vector,scale)(&a->data, by); } void FUNCTION(igraph_array3,fill)(TYPE(igraph_array3) *a, BASE e) { FUNCTION(igraph_vector,fill)(&a->data, e); } int FUNCTION(igraph_array3,update)(TYPE(igraph_array3) *to, const TYPE(igraph_array3) *from) { IGRAPH_CHECK(FUNCTION(igraph_array3,resize)(to, from->n1, from->n2, from->n3)); FUNCTION(igraph_vector,update)(&to->data, &from->data); return 0; } igraph/src/infomap_FlowGraph.h0000644000176000001440000000402112325527073016121 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FLOWGRAPH_H #define FLOWGRAPH_H #include #include #include "igraph_interface.h" #include "infomap_Node.h" class FlowGraph{ private: void init(int n, const igraph_vector_t *nodeWeights); public: FlowGraph(int n); FlowGraph(int n, const igraph_vector_t *nodeWeights); FlowGraph(FlowGraph * fgraph); FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members); FlowGraph(const igraph_t * graph, const igraph_vector_t *e_weights, const igraph_vector_t *v_weights); ~FlowGraph(); void swap(FlowGraph * fgraph); void initiate(); void eigenvector(); void calibrate(); void back_to(FlowGraph * fgraph); /*************************************************************************/ Node **node; int Nnode; double alpha,beta; int Ndanglings; vector danglings; // id of dangling nodes double exit; // double exitFlow; // double exit_log_exit; // double size_log_size; // double nodeSize_log_nodeSize; // \sum_{v in V} p log(p) double codeLength; }; void delete_FlowGraph(FlowGraph *fgraph); #endif igraph/src/dnaupd.f0000644000176000001440000006614512325527073014007 0ustar ripleyusersc\BeginDoc c c\Name: igraphdnaupd c c\Description: c Reverse communication interface for the Implicitly Restarted Arnoldi c iteration. This subroutine computes approximations to a few eigenpairs c of a linear operator "OP" with respect to a semi-inner product defined by c a symmetric positive semi-definite real matrix B. B may be the identity c matrix. NOTE: If the linear operator "OP" is real and symmetric c with respect to the real positive semi-definite symmetric matrix B, c i.e. B*OP = (OP')*B, then subroutine ssaupd should be used instead. c c The computed approximate eigenvalues are called Ritz values and c the corresponding approximate eigenvectors are called Ritz vectors. c c igraphdnaupd is usually called iteratively to solve one of the c following problems: c c Mode 1: A*x = lambda*x. c ===> OP = A and B = I. c c Mode 2: A*x = lambda*M*x, M symmetric positive definite c ===> OP = inv[M]*A and B = M. c ===> (If M can be factored see remark 3 below) c c Mode 3: A*x = lambda*M*x, M symmetric semi-definite c ===> OP = Real_Part{ inv[A - sigma*M]*M } and B = M. c ===> shift-and-invert mode (in real arithmetic) c If OP*x = amu*x, then c amu = 1/2 * [ 1/(lambda-sigma) + 1/(lambda-conjg(sigma)) ]. c Note: If sigma is real, i.e. imaginary part of sigma is zero; c Real_Part{ inv[A - sigma*M]*M } == inv[A - sigma*M]*M c amu == 1/(lambda-sigma). c c Mode 4: A*x = lambda*M*x, M symmetric semi-definite c ===> OP = Imaginary_Part{ inv[A - sigma*M]*M } and B = M. c ===> shift-and-invert mode (in real arithmetic) c If OP*x = amu*x, then c amu = 1/2i * [ 1/(lambda-sigma) - 1/(lambda-conjg(sigma)) ]. c c Both mode 3 and 4 give the same enhancement to eigenvalues close to c the (complex) shift sigma. However, as lambda goes to infinity, c the operator OP in mode 4 dampens the eigenvalues more strongly than c does OP defined in mode 3. c c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v c should be accomplished either by a direct method c using a sparse matrix factorization and solving c c [A - sigma*M]*w = v or M*w = v, c c or through an iterative method for solving these c systems. If an iterative method is used, the c convergence test must be more stringent than c the accuracy requirements for the eigenvalue c approximations. c c\Usage: c call igraphdnaupd c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, c IPNTR, WORKD, WORKL, LWORKL, INFO ) c c\Arguments c IDO Integer. (INPUT/OUTPUT) c Reverse communication flag. IDO must be zero on the first c call to igraphdnaupd. IDO will be set internally to c indicate the type of operation to be performed. Control is c then given back to the calling routine which has the c responsibility to carry out the requested operation and call c igraphdnaupd with the result. The operand is given in c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). c ------------------------------------------------------------- c IDO = 0: first call to the reverse communication interface c IDO = -1: compute Y = OP * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c This is for the initialization phase to force the c starting vector into the range of OP. c IDO = 1: compute Y = OP * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c In mode 3 and 4, the vector B * X is already c available in WORKD(ipntr(3)). It does not c need to be recomputed in forming OP * X. c IDO = 2: compute Y = B * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c IDO = 3: compute the IPARAM(8) real and imaginary parts c of the shifts where INPTR(14) is the pointer c into WORKL for placing the shifts. See Remark c 5 below. c IDO = 99: done c ------------------------------------------------------------- c c BMAT Character*1. (INPUT) c BMAT specifies the type of the matrix B that defines the c semi-inner product for the operator OP. c BMAT = 'I' -> standard eigenvalue problem A*x = lambda*x c BMAT = 'G' -> generalized eigenvalue problem A*x = lambda*B*x c c N Integer. (INPUT) c Dimension of the eigenproblem. c c WHICH Character*2. (INPUT) c 'LM' -> want the NEV eigenvalues of largest magnitude. c 'SM' -> want the NEV eigenvalues of smallest magnitude. c 'LR' -> want the NEV eigenvalues of largest real part. c 'SR' -> want the NEV eigenvalues of smallest real part. c 'LI' -> want the NEV eigenvalues of largest imaginary part. c 'SI' -> want the NEV eigenvalues of smallest imaginary part. c c NEV Integer. (INPUT) c Number of eigenvalues of OP to be computed. 0 < NEV < N-1. c c TOL Double precision scalar. (INPUT) c Stopping criterion: the relative accuracy of the Ritz value c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)) c where ABS(RITZ(I)) is the magnitude when RITZ(I) is complex. c DEFAULT = DLAMCH('EPS') (machine precision as computed c by the LAPACK auxiliary subroutine DLAMCH). c c RESID Double precision array of length N. (INPUT/OUTPUT) c On INPUT: c If INFO .EQ. 0, a random initial residual vector is used. c If INFO .NE. 0, RESID contains the initial residual vector, c possibly from a previous run. c On OUTPUT: c RESID contains the final residual vector. c c NCV Integer. (INPUT) c Number of columns of the matrix V. NCV must satisfy the two c inequalities 2 <= NCV-NEV and NCV <= N. c This will indicate how many Arnoldi vectors are generated c at each iteration. After the startup phase in which NEV c Arnoldi vectors are generated, the algorithm generates c approximately NCV-NEV Arnoldi vectors at each subsequent update c iteration. Most of the cost in generating each Arnoldi vector is c in the matrix-vector operation OP*x. c NOTE: 2 <= NCV-NEV in order that complex conjugate pairs of Ritz c values are kept together. (See remark 4 below) c c V Double precision array N by NCV. (OUTPUT) c Contains the final set of Arnoldi basis vectors. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling program. c c IPARAM Integer array of length 11. (INPUT/OUTPUT) c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. c The shifts selected at each iteration are used to restart c the Arnoldi iteration in an implicit fashion. c ------------------------------------------------------------- c ISHIFT = 0: the shifts are provided by the user via c reverse communication. The real and imaginary c parts of the NCV eigenvalues of the Hessenberg c matrix H are returned in the part of the WORKL c array corresponding to RITZR and RITZI. See remark c 5 below. c ISHIFT = 1: exact shifts with respect to the current c Hessenberg matrix H. This is equivalent to c restarting the iteration with a starting vector c that is a linear combination of approximate Schur c vectors associated with the "wanted" Ritz values. c ------------------------------------------------------------- c c IPARAM(2) = No longer referenced. c c IPARAM(3) = MXITER c On INPUT: maximum number of Arnoldi update iterations allowed. c On OUTPUT: actual number of Arnoldi update iterations taken. c c IPARAM(4) = NB: blocksize to be used in the recurrence. c The code currently works only for NB = 1. c c IPARAM(5) = NCONV: number of "converged" Ritz values. c This represents the number of Ritz values that satisfy c the convergence criterion. c c IPARAM(6) = IUPD c No longer referenced. Implicit restarting is ALWAYS used. c c IPARAM(7) = MODE c On INPUT determines what type of eigenproblem is being solved. c Must be 1,2,3,4; See under \Description of igraphdnaupd for the c four modes available. c c IPARAM(8) = NP c When ido = 3 and the user provides shifts through reverse c communication (IPARAM(1)=0), igraphdnaupd returns NP, the number c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark c 5 below. c c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, c OUTPUT: NUMOP = total number of OP*x operations, c NUMOPB = total number of B*x operations if BMAT='G', c NUMREO = total number of steps of re-orthogonalization. c c IPNTR Integer array of length 14. (OUTPUT) c Pointer to mark the starting locations in the WORKD and WORKL c arrays for matrices/vectors used by the Arnoldi iteration. c ------------------------------------------------------------- c IPNTR(1): pointer to the current operand vector X in WORKD. c IPNTR(2): pointer to the current result vector Y in WORKD. c IPNTR(3): pointer to the vector B * X in WORKD when used in c the shift-and-invert mode. c IPNTR(4): pointer to the next available location in WORKL c that is untouched by the program. c IPNTR(5): pointer to the NCV by NCV upper Hessenberg matrix c H in WORKL. c IPNTR(6): pointer to the real part of the ritz value array c RITZR in WORKL. c IPNTR(7): pointer to the imaginary part of the ritz value array c RITZI in WORKL. c IPNTR(8): pointer to the Ritz estimates in array WORKL associated c with the Ritz values located in RITZR and RITZI in WORKL. c c IPNTR(14): pointer to the NP shifts in WORKL. See Remark 5 below. c c Note: IPNTR(9:13) is only referenced by igraphdneupd. See Remark 2 below. c c IPNTR(9): pointer to the real part of the NCV RITZ values of the c original system. c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of c the original system. c IPNTR(11): pointer to the NCV corresponding error bounds. c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular c Schur matrix for H. c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors c of the upper Hessenberg matrix H. Only referenced by c igraphdneupd if RVEC = .TRUE. See Remark 2 below. c ------------------------------------------------------------- c c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) c Distributed array to be used in the basic Arnoldi iteration c for reverse communication. The user should not use WORKD c as temporary workspace during the iteration. Upon termination c WORKD(1:N) contains B*RESID(1:N). If an invariant subspace c associated with the converged Ritz values is desired, see remark c 2 below, subroutine igraphdneupd uses this output. c See Data Distribution Note below. c c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) c Private (replicated) array on each PE or array allocated on c the front end. See Data Distribution Note below. c c LWORKL Integer. (INPUT) c LWORKL must be at least 3*NCV**2 + 6*NCV. c c INFO Integer. (INPUT/OUTPUT) c If INFO .EQ. 0, a randomly initial residual vector is used. c If INFO .NE. 0, RESID contains the initial residual vector, c possibly from a previous run. c Error flag on output. c = 0: Normal exit. c = 1: Maximum number of iterations taken. c All possible eigenvalues of OP has been found. IPARAM(5) c returns the number of wanted converged Ritz values. c = 2: No longer an informational error. Deprecated starting c with release 2 of ARPACK. c = 3: No shifts could be applied during a cycle of the c Implicitly restarted Arnoldi iteration. One possibility c is to increase the size of NCV relative to NEV. c See remark 4 below. c = -1: N must be positive. c = -2: NEV must be positive. c = -3: NCV-NEV >= 2 and less than or equal to N. c = -4: The maximum number of Arnoldi update iteration c must be greater than zero. c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' c = -6: BMAT must be one of 'I' or 'G'. c = -7: Length of private work array is not sufficient. c = -8: Error return from LAPACK eigenvalue calculation; c = -9: Starting vector is zero. c = -10: IPARAM(7) must be 1,2,3,4. c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. c = -12: IPARAM(1) must be equal to 0 or 1. c = -9999: Could not build an Arnoldi factorization. c IPARAM(5) returns the size of the current Arnoldi c factorization. c c\Remarks c 1. The computed Ritz values are approximate eigenvalues of OP. The c selection of WHICH should be made with this in mind when c Mode = 3 and 4. After convergence, approximate eigenvalues of the c original problem may be obtained with the ARPACK subroutine igraphdneupd. c c 2. If a basis for the invariant subspace corresponding to the converged Ritz c values is needed, the user must call igraphdneupd immediately following c completion of igraphdnaupd. This is new starting with release 2 of ARPACK. c c 3. If M can be factored into a Cholesky factorization M = LL' c then Mode = 2 should not be selected. Instead one should use c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular c linear systems should be solved with L and L' rather c than computing inverses. After convergence, an approximate c eigenvector z of the original problem is recovered by solving c L'z = x where x is a Ritz vector of OP. c c 4. At present there is no a-priori analysis to guide the selection c of NCV relative to NEV. The only formal requrement is that NCV > NEV + 2. c However, it is recommended that NCV .ge. 2*NEV+1. If many problems of c the same type are to be solved, one should experiment with increasing c NCV while keeping NEV fixed for a given test problem. This will c usually decrease the required number of OP*x operations but it c also increases the work and storage required to maintain the orthogonal c basis vectors. The optimal "cross-over" with respect to CPU time c is problem dependent and must be determined empirically. c See Chapter 8 of Reference 2 for further information. c c 5. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the c NP = IPARAM(8) real and imaginary parts of the shifts in locations c real part imaginary part c ----------------------- -------------- c 1 WORKL(IPNTR(14)) WORKL(IPNTR(14)+NP) c 2 WORKL(IPNTR(14)+1) WORKL(IPNTR(14)+NP+1) c . . c . . c . . c NP WORKL(IPNTR(14)+NP-1) WORKL(IPNTR(14)+2*NP-1). c c Only complex conjugate pairs of shifts may be applied and the pairs c must be placed in consecutive locations. The real part of the c eigenvalues of the current upper Hessenberg matrix are located in c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1) and the imaginary part c in WORKL(IPNTR(7)) through WORKL(IPNTR(7)+NCV-1). They are ordered c according to the order defined by WHICH. The complex conjugate c pairs are kept together and the associated Ritz estimates are located in c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). c c----------------------------------------------------------------------- c c\Data Distribution Note: c c Fortran-D syntax: c ================ c Double precision resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) c decompose d1(n), d2(n,ncv) c align resid(i) with d1(i) c align v(i,j) with d2(i,j) c align workd(i) with d1(i) range (1:n) c align workd(i) with d1(i-n) range (n+1:2*n) c align workd(i) with d1(i-2*n) range (2*n+1:3*n) c distribute d1(block), d2(block,:) c replicated workl(lworkl) c c Cray MPP syntax: c =============== c Double precision resid(n), v(ldv,ncv), workd(n,3), workl(lworkl) c shared resid(block), v(block,:), workd(block,:) c replicated workl(lworkl) c c CM2/CM5 syntax: c ============== c c----------------------------------------------------------------------- c c include 'ex-nonsym.doc' c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for c Real Matrices", Linear Algebra and its Applications, vol 88/89, c pp 575-595, (1987). c c\Routines called: c igraphdnaup2 ARPACK routine that implements the Implicitly Restarted c Arnoldi Iteration. c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdvout ARPACK utility routine that prints vectors. c dlamch LAPACK routine that determines machine constants. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c 12/16/93: Version '1.1' c c\SCCS Information: @(#) c FILE: naupd.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 c c\Remarks c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdnaupd & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, & ipntr, workd, workl, lworkl, info ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat*1, which*2 integer ido, info, ldv, lworkl, n, ncv, nev Double precision & tol c c %-----------------% c | Array Arguments | c %-----------------% c integer iparam(11), ipntr(14) Double precision & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c integer bounds, ierr, ih, iq, ishift, iupd, iw, & ldh, ldq, levec, mode, msglvl, mxiter, nb, & nev0, next, np, ritzi, ritzr, j save bounds, ih, iq, ishift, iupd, iw, ldh, ldq, & levec, mode, msglvl, mxiter, nb, nev0, next, & np, ritzi, ritzr c c %----------------------% c | External Subroutines | c %----------------------% c external igraphdnaup2, igraphdvout, igraphivout, & igraphsecond, igraphdstatn c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlamch external dlamch c c %-----------------------% c | Executable Statements | c %-----------------------% c if (ido .eq. 0) then c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphdstatn call igraphsecond (t0) msglvl = mnaupd c c %----------------% c | Error checking | c %----------------% c ierr = 0 ishift = iparam(1) levec = iparam(2) mxiter = iparam(3) nb = iparam(4) c c %--------------------------------------------% c | Revision 2 performs only implicit restart. | c %--------------------------------------------% c iupd = 1 mode = iparam(7) c if (n .le. 0) then ierr = -1 else if (nev .le. 0) then ierr = -2 else if (ncv .le. nev+1 .or. ncv .gt. n) then ierr = -3 else if (mxiter .le. 0) then ierr = -4 else if (which .ne. 'LM' .and. & which .ne. 'SM' .and. & which .ne. 'LR' .and. & which .ne. 'SR' .and. & which .ne. 'LI' .and. & which .ne. 'SI') then ierr = -5 else if (bmat .ne. 'I' .and. bmat .ne. 'G') then ierr = -6 else if (lworkl .lt. 3*ncv**2 + 6*ncv) then ierr = -7 else if (mode .lt. 1 .or. mode .gt. 5) then ierr = -10 else if (mode .eq. 1 .and. bmat .eq. 'G') then ierr = -11 else if (ishift .lt. 0 .or. ishift .gt. 1) then ierr = -12 end if c c %------------% c | Error Exit | c %------------% c if (ierr .ne. 0) then info = ierr ido = 99 go to 9000 end if c c %------------------------% c | Set default parameters | c %------------------------% c if (nb .le. 0) nb = 1 if (tol .le. zero) tol = dlamch('EpsMach') c c %----------------------------------------------% c | NP is the number of additional steps to | c | extend the length NEV Lanczos factorization. | c | NEV0 is the local variable designating the | c | size of the invariant subspace desired. | c %----------------------------------------------% c np = ncv - nev nev0 = nev c c %-----------------------------% c | Zero out internal workspace | c %-----------------------------% c do 10 j = 1, 3*ncv**2 + 6*ncv workl(j) = zero 10 continue c c %-------------------------------------------------------------% c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | c | etc... and the remaining workspace. | c | Also update pointer to be used on output. | c | Memory is laid out as follows: | c | workl(1:ncv*ncv) := generated Hessenberg matrix | c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | c | parts of ritz values | c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | c | workl(ncv*ncv+3*ncv+1:2*ncv*ncv+3*ncv) := rotation matrix Q | c | workl(2*ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) := workspace | c | The final workspace is needed by subroutine igraphdneigh called | c | by igraphdnaup2. Subroutine igraphdneigh calls LAPACK routines for | c | calculating eigenvalues and the last row of the eigenvector | c | matrix. | c %-------------------------------------------------------------% c ldh = ncv ldq = ncv ih = 1 ritzr = ih + ldh*ncv ritzi = ritzr + ncv bounds = ritzi + ncv iq = bounds + ncv iw = iq + ldq*ncv next = iw + ncv**2 + 3*ncv c ipntr(4) = next ipntr(5) = ih ipntr(6) = ritzr ipntr(7) = ritzi ipntr(8) = bounds ipntr(14) = iw c end if c c %-------------------------------------------------------% c | Carry out the Implicitly restarted Arnoldi Iteration. | c %-------------------------------------------------------% c call igraphdnaup2 & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritzr), & workl(ritzi), workl(bounds), workl(iq), ldq, workl(iw), & ipntr, workd, info ) c c %--------------------------------------------------% c | ido .ne. 99 implies use of reverse communication | c | to compute operations involving OP or shifts. | c %--------------------------------------------------% c if (ido .eq. 3) iparam(8) = np if (ido .ne. 99) go to 9000 c iparam(3) = mxiter iparam(5) = np iparam(9) = nopx iparam(10) = nbx iparam(11) = nrorth c c %------------------------------------% c | Exit if there was an informational | c | error within igraphdnaup2. | c %------------------------------------% c if (info .lt. 0) go to 9000 if (info .eq. 2) info = 3 c if (msglvl .gt. 0) then call igraphivout (logfil, 1, mxiter, ndigit, & '_naupd: Number of update iterations taken') call igraphivout (logfil, 1, np, ndigit, & '_naupd: Number of wanted "converged" Ritz values') call igraphdvout (logfil, np, workl(ritzr), ndigit, & '_naupd: Real part of the final Ritz values') call igraphdvout (logfil, np, workl(ritzi), ndigit, & '_naupd: Imaginary part of the final Ritz values') call igraphdvout (logfil, np, workl(bounds), ndigit, & '_naupd: Associated Ritz estimates') end if c call igraphsecond (t1) tnaupd = t1 - t0 c c 9000 continue c return c c %---------------% c | End of igraphdnaupd | c %---------------% c end igraph/src/glplpx03.c0000644000176000001440000002127112325527073014171 0ustar ripleyusers/* glplpx03.c (OPB format) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Author: Oscar Gustafsson . * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #define _GLPSTD_ERRNO #define _GLPSTD_STDIO #include "glpapi.h" #if 0 /* 24/XII-2009; by mao */ #include "glpipp.h" #endif /*---------------------------------------------------------------------- -- lpx_write_pb - write problem data in (normalized) OPB format. -- -- *Synopsis* -- -- #include "glplpx.h" -- int lpx_write_pb(LPX *lp, const char *fname, int normalized, -- int binarize); -- -- *Description* -- -- The routine lpx_write_pb writes problem data in OPB format -- to an output text file whose name is the character string fname. -- If normalized is non-zero the output will be generated in a -- normalized form with sequentially numbered variables, x1, x2 etc. -- If binarize, any integer variable will be repalzec by binary ones, -- see ipp_binarize -- -- *Returns* -- -- If the operation was successful, the routine returns zero. Otherwise -- the routine prints an error message and returns non-zero. */ #if 1 /* 24/XII-2009; by mao (disabled, because IPP was removed) */ int lpx_write_pb(LPX *lp, const char *fname, int normalized, int binarize) { xassert(lp == lp); xassert(fname == fname); xassert(normalized == normalized); xassert(binarize == binarize); xprintf("lpx_write_pb: sorry, currently this operation is not ava" "ilable\n"); return 1; } #else int lpx_write_pb(LPX *lp, const char *fname, int normalized, int binarize) { FILE* fp; int m,n,i,j,k,o,nonfree=0, obj_dir, dbl, *ndx, row_type, emptylhs=0; double coeff, *val, bound, constant/*=0.0*/; char* objconstname = "dummy_one"; char* emptylhsname = "dummy_zero"; /* Variables needed for possible binarization */ /*LPX* tlp;*/ IPP *ipp = NULL; /*tlp=lp;*/ if(binarize) /* Transform integer variables to binary ones */ { ipp = ipp_create_wksp(); ipp_load_orig(ipp, lp); ipp_binarize(ipp); lp = ipp_build_prob(ipp); } fp = fopen(fname, "w"); if(fp!= NULL) { xprintf( "lpx_write_pb: writing problem in %sOPB format to `%s'...\n", (normalized?"normalized ":""), fname); m = glp_get_num_rows(lp); n = glp_get_num_cols(lp); for(i=1;i<=m;i++) { switch(glp_get_row_type(lp,i)) { case GLP_LO: case GLP_UP: case GLP_FX: { nonfree += 1; break; } case GLP_DB: { nonfree += 2; break; } } } constant=glp_get_obj_coef(lp,0); fprintf(fp,"* #variables = %d #constraints = %d\n", n + (constant == 0?1:0), nonfree + (constant == 0?1:0)); /* Objective function */ obj_dir = glp_get_obj_dir(lp); fprintf(fp,"min: "); for(i=1;i<=n;i++) { coeff = glp_get_obj_coef(lp,i); if(coeff != 0.0) { if(obj_dir == GLP_MAX) coeff=-coeff; if(normalized) fprintf(fp, " %d x%d", (int)coeff, i); else fprintf(fp, " %d*%s", (int)coeff, glp_get_col_name(lp,i)); } } if(constant) { if(normalized) fprintf(fp, " %d x%d", (int)constant, n+1); else fprintf(fp, " %d*%s", (int)constant, objconstname); } fprintf(fp,";\n"); if(normalized && !binarize) /* Name substitution */ { fprintf(fp,"* Variable name substitution:\n"); for(j=1;j<=n;j++) { fprintf(fp, "* x%d = %s\n", j, glp_get_col_name(lp,j)); } if(constant) fprintf(fp, "* x%d = %s\n", n+1, objconstname); } ndx = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); /* Constraints */ for(j=1;j<=m;j++) { row_type=glp_get_row_type(lp,j); if(row_type!=GLP_FR) { if(row_type == GLP_DB) { dbl=2; row_type = GLP_UP; } else { dbl=1; } k=glp_get_mat_row(lp, j, ndx, val); for(o=1;o<=dbl;o++) { if(o==2) { row_type = GLP_LO; } if(k==0) /* Empty LHS */ { emptylhs = 1; if(normalized) { fprintf(fp, "0 x%d ", n+2); } else { fprintf(fp, "0*%s ", emptylhsname); } } for(i=1;i<=k;i++) { if(val[i] != 0.0) { if(normalized) { fprintf(fp, "%d x%d ", (row_type==GLP_UP)?(-(int)val[i]):((int)val[i]), ndx[i]); } else { fprintf(fp, "%d*%s ", (int)val[i], glp_get_col_name(lp,ndx[i])); } } } switch(row_type) { case GLP_LO: { fprintf(fp, ">="); bound = glp_get_row_lb(lp,j); break; } case GLP_UP: { if(normalized) { fprintf(fp, ">="); bound = -glp_get_row_ub(lp,j); } else { fprintf(fp, "<="); bound = glp_get_row_ub(lp,j); } break; } case GLP_FX: { fprintf(fp, "="); bound = glp_get_row_lb(lp,j); break; } } fprintf(fp," %d;\n",(int)bound); } } } xfree(ndx); xfree(val); if(constant) { xprintf( "lpx_write_pb: adding constant objective function variable\n"); if(normalized) fprintf(fp, "1 x%d = 1;\n", n+1); else fprintf(fp, "1*%s = 1;\n", objconstname); } if(emptylhs) { xprintf( "lpx_write_pb: adding dummy variable for empty left-hand si" "de constraint\n"); if(normalized) fprintf(fp, "1 x%d = 0;\n", n+2); else fprintf(fp, "1*%s = 0;\n", emptylhsname); } } else { xprintf("Problems opening file for writing: %s\n", fname); return(1); } fflush(fp); if (ferror(fp)) { xprintf("lpx_write_pb: can't write to `%s' - %s\n", fname, strerror(errno)); goto fail; } fclose(fp); if(binarize) { /* delete the resultant problem object */ if (lp != NULL) lpx_delete_prob(lp); /* delete MIP presolver workspace */ if (ipp != NULL) ipp_delete_wksp(ipp); /*lp=tlp;*/ } return 0; fail: if (fp != NULL) fclose(fp); return 1; } #endif /* eof */ igraph/src/glpmpl05.c0000644000176000001440000005344612325527073014171 0ustar ripleyusers/* glpmpl05.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Authors: Andrew Makhorin * Heinrich Schuchardt * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #define _GLPSTD_STDIO #define _GLPSTD_TIME #include "glpmpl.h" double fn_gmtime(MPL *mpl) { /* obtain the current calendar time (UTC) */ time_t timer; struct tm *tm; int j; time(&timer); if (timer == (time_t)(-1)) err: error(mpl, "gmtime(); unable to obtain current calendar time"); tm = gmtime(&timer); if (tm == NULL) goto err; j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year); if (j < 0) goto err; return (((double)(j - jday(1, 1, 1970)) * 24.0 + (double)tm->tm_hour) * 60.0 + (double)tm->tm_min) * 60.0 + (double)tm->tm_sec; } static char *week[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; static char *moon[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static void error1(MPL *mpl, const char *str, const char *s, const char *fmt, const char *f, const char *msg) { xprintf("Input string passed to str2time:\n"); xprintf("%s\n", str); xprintf("%*s\n", (s - str) + 1, "^"); xprintf("Format string passed to str2time:\n"); xprintf("%s\n", fmt); xprintf("%*s\n", (f - fmt) + 1, "^"); error(mpl, "%s", msg); /* no return */ } double fn_str2time(MPL *mpl, const char *str, const char *fmt) { /* convert character string to the calendar time */ int j, year, month, day, hh, mm, ss, zone; const char *s, *f; year = month = day = hh = mm = ss = -1, zone = INT_MAX; s = str; for (f = fmt; *f != '\0'; f++) { if (*f == '%') { f++; if (*f == 'b' || *f == 'h') { /* the abbreviated month name */ int k; char *name; if (month >= 0) error1(mpl, str, s, fmt, f, "month multiply specified" ); while (*s == ' ') s++; for (month = 1; month <= 12; month++) { name = moon[month-1]; for (k = 0; k <= 2; k++) { if (toupper((unsigned char)s[k]) != toupper((unsigned char)name[k])) goto next; } s += 3; for (k = 3; name[k] != '\0'; k++) { if (toupper((unsigned char)*s) != toupper((unsigned char)name[k])) break; s++; } break; next: ; } if (month > 12) error1(mpl, str, s, fmt, f, "abbreviated month name m" "issing or invalid"); } else if (*f == 'd') { /* the day of the month as a decimal number (01..31) */ if (day >= 0) error1(mpl, str, s, fmt, f, "day multiply specified"); while (*s == ' ') s++; if (!('0' <= *s && *s <= '9')) error1(mpl, str, s, fmt, f, "day missing or invalid"); day = (*s++) - '0'; if ('0' <= *s && *s <= '9') day = 10 * day + ((*s++) - '0'); if (!(1 <= day && day <= 31)) error1(mpl, str, s, fmt, f, "day out of range"); } else if (*f == 'H') { /* the hour as a decimal number, using a 24-hour clock (00..23) */ if (hh >= 0) error1(mpl, str, s, fmt, f, "hour multiply specified") ; while (*s == ' ') s++; if (!('0' <= *s && *s <= '9')) error1(mpl, str, s, fmt, f, "hour missing or invalid") ; hh = (*s++) - '0'; if ('0' <= *s && *s <= '9') hh = 10 * hh + ((*s++) - '0'); if (!(0 <= hh && hh <= 23)) error1(mpl, str, s, fmt, f, "hour out of range"); } else if (*f == 'm') { /* the month as a decimal number (01..12) */ if (month >= 0) error1(mpl, str, s, fmt, f, "month multiply specified" ); while (*s == ' ') s++; if (!('0' <= *s && *s <= '9')) error1(mpl, str, s, fmt, f, "month missing or invalid" ); month = (*s++) - '0'; if ('0' <= *s && *s <= '9') month = 10 * month + ((*s++) - '0'); if (!(1 <= month && month <= 12)) error1(mpl, str, s, fmt, f, "month out of range"); } else if (*f == 'M') { /* the minute as a decimal number (00..59) */ if (mm >= 0) error1(mpl, str, s, fmt, f, "minute multiply specifie" "d"); while (*s == ' ') s++; if (!('0' <= *s && *s <= '9')) error1(mpl, str, s, fmt, f, "minute missing or invali" "d"); mm = (*s++) - '0'; if ('0' <= *s && *s <= '9') mm = 10 * mm + ((*s++) - '0'); if (!(0 <= mm && mm <= 59)) error1(mpl, str, s, fmt, f, "minute out of range"); } else if (*f == 'S') { /* the second as a decimal number (00..60) */ if (ss >= 0) error1(mpl, str, s, fmt, f, "second multiply specifie" "d"); while (*s == ' ') s++; if (!('0' <= *s && *s <= '9')) error1(mpl, str, s, fmt, f, "second missing or invali" "d"); ss = (*s++) - '0'; if ('0' <= *s && *s <= '9') ss = 10 * ss + ((*s++) - '0'); if (!(0 <= ss && ss <= 60)) error1(mpl, str, s, fmt, f, "second out of range"); } else if (*f == 'y') { /* the year without a century as a decimal number (00..99); the values 00 to 68 mean the years 2000 to 2068 while the values 69 to 99 mean the years 1969 to 1999 */ if (year >= 0) error1(mpl, str, s, fmt, f, "year multiply specified") ; while (*s == ' ') s++; if (!('0' <= *s && *s <= '9')) error1(mpl, str, s, fmt, f, "year missing or invalid") ; year = (*s++) - '0'; if ('0' <= *s && *s <= '9') year = 10 * year + ((*s++) - '0'); year += (year >= 69 ? 1900 : 2000); } else if (*f == 'Y') { /* the year as a decimal number, using the Gregorian calendar */ if (year >= 0) error1(mpl, str, s, fmt, f, "year multiply specified") ; while (*s == ' ') s++; if (!('0' <= *s && *s <= '9')) error1(mpl, str, s, fmt, f, "year missing or invalid") ; year = 0; for (j = 1; j <= 4; j++) { if (!('0' <= *s && *s <= '9')) break; year = 10 * year + ((*s++) - '0'); } if (!(1 <= year && year <= 4000)) error1(mpl, str, s, fmt, f, "year out of range"); } else if (*f == 'z') { /* time zone offset in the form zhhmm */ int z, hh, mm; if (zone != INT_MAX) error1(mpl, str, s, fmt, f, "time zone offset multipl" "y specified"); while (*s == ' ') s++; if (*s == 'Z') { z = hh = mm = 0, s++; goto skip; } if (*s == '+') z = +1, s++; else if (*s == '-') z = -1, s++; else error1(mpl, str, s, fmt, f, "time zone offset sign mi" "ssing"); hh = 0; for (j = 1; j <= 2; j++) { if (!('0' <= *s && *s <= '9')) err1: error1(mpl, str, s, fmt, f, "time zone offset valu" "e incomplete or invalid"); hh = 10 * hh + ((*s++) - '0'); } if (hh > 23) err2: error1(mpl, str, s, fmt, f, "time zone offset value o" "ut of range"); if (*s == ':') { s++; if (!('0' <= *s && *s <= '9')) goto err1; } mm = 0; if (!('0' <= *s && *s <= '9')) goto skip; for (j = 1; j <= 2; j++) { if (!('0' <= *s && *s <= '9')) goto err1; mm = 10 * mm + ((*s++) - '0'); } if (mm > 59) goto err2; skip: zone = z * (60 * hh + mm); } else if (*f == '%') { /* literal % character */ goto test; } else error1(mpl, str, s, fmt, f, "invalid conversion specifie" "r"); } else if (*f == ' ') ; else test: { /* check a matching character in the input string */ if (*s != *f) error1(mpl, str, s, fmt, f, "character mismatch"); s++; } } if (year < 0) year = 1970; if (month < 0) month = 1; if (day < 0) day = 1; if (hh < 0) hh = 0; if (mm < 0) mm = 0; if (ss < 0) ss = 0; if (zone == INT_MAX) zone = 0; j = jday(day, month, year); xassert(j >= 0); return (((double)(j - jday(1, 1, 1970)) * 24.0 + (double)hh) * 60.0 + (double)mm) * 60.0 + (double)ss - 60.0 * (double)zone; } static void error2(MPL *mpl, const char *fmt, const char *f, const char *msg) { xprintf("Format string passed to time2str:\n"); xprintf("%s\n", fmt); xprintf("%*s\n", (f - fmt) + 1, "^"); error(mpl, "%s", msg); /* no return */ } static int weekday(int j) { /* determine weekday number (1 = Mon, ..., 7 = Sun) */ return (j + jday(1, 1, 1970)) % 7 + 1; } static int firstday(int year) { /* determine the first day of the first week for a specified year according to ISO 8601 */ int j; /* if 1 January is Monday, Tuesday, Wednesday or Thursday, it is in week 01; if 1 January is Friday, Saturday or Sunday, it is in week 52 or 53 of the previous year */ j = jday(1, 1, year) - jday(1, 1, 1970); switch (weekday(j)) { case 1: /* 1 Jan is Mon */ j += 0; break; case 2: /* 1 Jan is Tue */ j -= 1; break; case 3: /* 1 Jan is Wed */ j -= 2; break; case 4: /* 1 Jan is Thu */ j -= 3; break; case 5: /* 1 Jan is Fri */ j += 3; break; case 6: /* 1 Jan is Sat */ j += 2; break; case 7: /* 1 Jan is Sun */ j += 1; break; default: xassert(j != j); } /* the first day of the week must be Monday */ xassert(weekday(j) == 1); return j; } void fn_time2str(MPL *mpl, char *str, double t, const char *fmt) { /* convert the calendar time to character string */ int j, year, month, day, hh, mm, ss, len; double temp; const char *f; char buf[MAX_LENGTH+1]; if (!(-62135596800.0 <= t && t <= 64092211199.0)) error(mpl, "time2str(%.*g,...); argument out of range", DBL_DIG, t); t = floor(t + 0.5); temp = fabs(t) / 86400.0; j = (int)floor(temp); if (t < 0.0) { if (temp == floor(temp)) j = - j; else j = - (j + 1); } xassert(jdate(j + jday(1, 1, 1970), &day, &month, &year) == 0); ss = (int)(t - 86400.0 * (double)j); xassert(0 <= ss && ss < 86400); mm = ss / 60, ss %= 60; hh = mm / 60, mm %= 60; len = 0; for (f = fmt; *f != '\0'; f++) { if (*f == '%') { f++; if (*f == 'a') { /* the abbreviated weekday name */ memcpy(buf, week[weekday(j)-1], 3), buf[3] = '\0'; } else if (*f == 'A') { /* the full weekday name */ strcpy(buf, week[weekday(j)-1]); } else if (*f == 'b' || *f == 'h') { /* the abbreviated month name */ memcpy(buf, moon[month-1], 3), buf[3] = '\0'; } else if (*f == 'B') { /* the full month name */ strcpy(buf, moon[month-1]); } else if (*f == 'C') { /* the century of the year */ sprintf(buf, "%02d", year / 100); } else if (*f == 'd') { /* the day of the month as a decimal number (01..31) */ sprintf(buf, "%02d", day); } else if (*f == 'D') { /* the date using the format %m/%d/%y */ sprintf(buf, "%02d/%02d/%02d", month, day, year % 100); } else if (*f == 'e') { /* the day of the month like with %d, but padded with blank (1..31) */ sprintf(buf, "%2d", day); } else if (*f == 'F') { /* the date using the format %Y-%m-%d */ sprintf(buf, "%04d-%02d-%02d", year, month, day); } else if (*f == 'g') { /* the year corresponding to the ISO week number, but without the century (range 00 through 99); this has the same format and value as %y, except that if the ISO week number (see %V) belongs to the previous or next year, that year is used instead */ int iso; if (j < firstday(year)) iso = year - 1; else if (j < firstday(year + 1)) iso = year; else iso = year + 1; sprintf(buf, "%02d", iso % 100); } else if (*f == 'G') { /* the year corresponding to the ISO week number; this has the same format and value as %Y, excepth that if the ISO week number (see %V) belongs to the previous or next year, that year is used instead */ int iso; if (j < firstday(year)) iso = year - 1; else if (j < firstday(year + 1)) iso = year; else iso = year + 1; sprintf(buf, "%04d", iso); } else if (*f == 'H') { /* the hour as a decimal number, using a 24-hour clock (00..23) */ sprintf(buf, "%02d", hh); } else if (*f == 'I') { /* the hour as a decimal number, using a 12-hour clock (01..12) */ sprintf(buf, "%02d", hh == 0 ? 12 : hh <= 12 ? hh : hh - 12); } else if (*f == 'j') { /* the day of the year as a decimal number (001..366) */ sprintf(buf, "%03d", jday(day, month, year) - jday(1, 1, year) + 1); } else if (*f == 'k') { /* the hour as a decimal number, using a 24-hour clock like %H, but padded with blank (0..23) */ sprintf(buf, "%2d", hh); } else if (*f == 'l') { /* the hour as a decimal number, using a 12-hour clock like %I, but padded with blank (1..12) */ sprintf(buf, "%2d", hh == 0 ? 12 : hh <= 12 ? hh : hh - 12); } else if (*f == 'm') { /* the month as a decimal number (01..12) */ sprintf(buf, "%02d", month); } else if (*f == 'M') { /* the minute as a decimal number (00..59) */ sprintf(buf, "%02d", mm); } else if (*f == 'p') { /* either AM or PM, according to the given time value; noon is treated as PM and midnight as AM */ strcpy(buf, hh <= 11 ? "AM" : "PM"); } else if (*f == 'P') { /* either am or pm, according to the given time value; noon is treated as pm and midnight as am */ strcpy(buf, hh <= 11 ? "am" : "pm"); } else if (*f == 'r') { /* the calendar time using the format %I:%M:%S %p */ sprintf(buf, "%02d:%02d:%02d %s", hh == 0 ? 12 : hh <= 12 ? hh : hh - 12, mm, ss, hh <= 11 ? "AM" : "PM"); } else if (*f == 'R') { /* the hour and minute using the format %H:%M */ sprintf(buf, "%02d:%02d", hh, mm); } else if (*f == 'S') { /* the second as a decimal number (00..59) */ sprintf(buf, "%02d", ss); } else if (*f == 'T') { /* the time of day using the format %H:%M:%S */ sprintf(buf, "%02d:%02d:%02d", hh, mm, ss); } else if (*f == 'u') { /* the day of the week as a decimal number (1..7), Monday being 1 */ sprintf(buf, "%d", weekday(j)); } else if (*f == 'U') { /* the week number of the current year as a decimal number (range 00 through 53), starting with the first Sunday as the first day of the first week; days preceding the first Sunday in the year are considered to be in week 00 */ #if 1 /* 09/I-2009 */ #undef sun /* causes compilation error in SunOS */ #endif int sun; /* sun = the first Sunday of the year */ sun = jday(1, 1, year) - jday(1, 1, 1970); sun += (7 - weekday(sun)); sprintf(buf, "%02d", (j + 7 - sun) / 7); } else if (*f == 'V') { /* the ISO week number as a decimal number (range 01 through 53); ISO weeks start with Monday and end with Sunday; week 01 of a year is the first week which has the majority of its days in that year; week 01 of a year can contain days from the previous year; the week before week 01 of a year is the last week (52 or 53) of the previous year even if it contains days from the new year */ int iso; if (j < firstday(year)) iso = j - firstday(year - 1); else if (j < firstday(year + 1)) iso = j - firstday(year); else iso = j - firstday(year + 1); sprintf(buf, "%02d", iso / 7 + 1); } else if (*f == 'w') { /* the day of the week as a decimal number (0..6), Sunday being 0 */ sprintf(buf, "%d", weekday(j) % 7); } else if (*f == 'W') { /* the week number of the current year as a decimal number (range 00 through 53), starting with the first Monday as the first day of the first week; days preceding the first Monday in the year are considered to be in week 00 */ int mon; /* mon = the first Monday of the year */ mon = jday(1, 1, year) - jday(1, 1, 1970); mon += (8 - weekday(mon)) % 7; sprintf(buf, "%02d", (j + 7 - mon) / 7); } else if (*f == 'y') { /* the year without a century as a decimal number (00..99) */ sprintf(buf, "%02d", year % 100); } else if (*f == 'Y') { /* the year as a decimal number, using the Gregorian calendar */ sprintf(buf, "%04d", year); } else if (*f == '%') { /* a literal % character */ buf[0] = '%', buf[1] = '\0'; } else error2(mpl, fmt, f, "invalid conversion specifier"); } else buf[0] = *f, buf[1] = '\0'; if (len + strlen(buf) > MAX_LENGTH) error(mpl, "time2str; output string length exceeds %d chara" "cters", MAX_LENGTH); memcpy(str+len, buf, strlen(buf)); len += strlen(buf); } str[len] = '\0'; return; } /* eof */ igraph/src/Point.h0000755000176000001440000000147312325527072013622 0ustar ripleyusers/** this is a simple generic class representing a 3d point with a name. it also defines the PointList type, which is a linked list of Points */ #ifndef POINT_H #define POINT_H #include using namespace std; namespace igraph { class Point { public: Point(); // creates a point at the origin with name 0 Point(double vX, double vY, double vZ, int vName); Point(double vX, double vY, double vZ); ~Point(); double X() const; void X(double vX); double Y() const; void Y(double vY); double Z() const; void Z(double vZ); int Name() const; void Name(int vName); double Distance(const Point& rPoint) const; bool operator==(const Point& vRhs) const; private: double mX, mY, mZ; int mName; }; typedef list PointList; typedef list::iterator PointListIterator; } // namespace igraph #endif igraph/src/heap.c0000644000176000001440000006675712325527073013457 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include "igraph_math.h" #include #include /* memcpy & co. */ #include #define PARENT(x) (((x)+1)/2-1) #define LEFTCHILD(x) (((x)+1)*2-1) #define RIGHTCHILD(x) (((x)+1)*2) /** * \ingroup indheap * \brief Initializes an indexed heap (constructor). * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_indheap_init (igraph_indheap_t* h, long int alloc_size) { if (alloc_size <= 0 ) { alloc_size=1; } h->stor_begin=igraph_Calloc(alloc_size, igraph_real_t); if (h->stor_begin==0) { h->index_begin=0; IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); } h->index_begin=igraph_Calloc(alloc_size, long int); if (h->index_begin==0) { igraph_Free(h->stor_begin); h->stor_begin=0; IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); } h->stor_end=h->stor_begin + alloc_size; h->end=h->stor_begin; h->destroy=1; return 0; } int igraph_indheap_clear(igraph_indheap_t *h) { h->end=h->stor_begin; return 0; } /** * \ingroup indheap * \brief Initializes and build an indexed heap from a C array (constructor). * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_indheap_init_array (igraph_indheap_t *h, igraph_real_t* data, long int len) { long int i; h->stor_begin=igraph_Calloc(len, igraph_real_t); if (h->stor_begin==0) { h->index_begin=0; IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); } h->index_begin=igraph_Calloc(len, long int); if (h->index_begin==0) { igraph_Free(h->stor_begin); h->stor_begin=0; IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); } h->stor_end=h->stor_begin+len; h->end=h->stor_end; h->destroy=1; memcpy(h->stor_begin, data, (size_t) len*sizeof(igraph_real_t)); for (i=0; iindex_begin[i]=i+1; } igraph_indheap_i_build (h, 0); return 0; } /** * \ingroup indheap * \brief Destroys an initialized indexed heap. */ void igraph_indheap_destroy (igraph_indheap_t* h) { assert(h != 0); if (h->destroy) { if (h->stor_begin != 0) { igraph_Free(h->stor_begin); h->stor_begin=0; } if (h->index_begin != 0) { igraph_Free(h->index_begin); h->index_begin=0; } } } /** * \ingroup indheap * \brief Checks whether a heap is empty. */ igraph_bool_t igraph_indheap_empty (igraph_indheap_t* h) { assert(h != 0); assert(h->stor_begin != 0); return h->stor_begin == h->end; } /** * \ingroup indheap * \brief Adds an element to an indexed heap. */ int igraph_indheap_push (igraph_indheap_t* h, igraph_real_t elem) { assert(h != 0); assert(h->stor_begin != 0); /* full, allocate more storage */ if (h->stor_end == h->end) { long int new_size = igraph_indheap_size(h) * 2; if (new_size == 0) { new_size = 1; } IGRAPH_CHECK(igraph_indheap_reserve(h, new_size)); } *(h->end) = elem; h->end += 1; *(h->index_begin+igraph_indheap_size(h)-1)=igraph_indheap_size(h)-1; /* maintain indheap */ igraph_indheap_i_shift_up(h, igraph_indheap_size(h)-1); return 0; } /** * \ingroup indheap * \brief Adds an element to an indexed heap with a given index. */ int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_real_t elem) { assert(h != 0); assert(h->stor_begin != 0); /* full, allocate more storage */ if (h->stor_end == h->end) { long int new_size = igraph_indheap_size(h) * 2; if (new_size == 0) { new_size = 1; } IGRAPH_CHECK(igraph_indheap_reserve(h, new_size)); } *(h->end) = elem; h->end += 1; *(h->index_begin+igraph_indheap_size(h)-1)=idx; /* maintain indheap */ igraph_indheap_i_shift_up(h, igraph_indheap_size(h)-1); return 0; } /** * \ingroup indheap * \brief Modifies an element in an indexed heap. */ int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem) { long int i, n; assert(h != 0); assert(h->stor_begin != 0); n = igraph_indheap_size(h); for (i=0; iindex_begin[i] == idx) { h->stor_begin[i] = elem; break; } if (i == n) return 0; /* maintain indheap */ igraph_indheap_i_build(h, 0); return 0; } /** * \ingroup indheap * \brief Returns the largest element in an indexed heap. */ igraph_real_t igraph_indheap_max (igraph_indheap_t* h) { assert(h != NULL); assert(h->stor_begin != NULL); assert(h->stor_begin != h->end); return h->stor_begin[0]; } /** * \ingroup indheap * \brief Removes the largest element from an indexed heap. */ igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h) { igraph_real_t tmp; assert(h != NULL); assert(h->stor_begin != NULL); tmp=h->stor_begin[0]; igraph_indheap_i_switch(h, 0, igraph_indheap_size(h)-1); h->end -= 1; igraph_indheap_i_sink(h, 0); return tmp; } /** * \ingroup indheap * \brief Gives the number of elements in an indexed heap. */ long int igraph_indheap_size (igraph_indheap_t* h) { assert(h != 0); assert(h->stor_begin != 0); return h->end - h->stor_begin; } /** * \ingroup indheap * \brief Reserves more memory for an indexed heap. * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_indheap_reserve (igraph_indheap_t* h, long int size) { long int actual_size=igraph_indheap_size(h); igraph_real_t *tmp1; long int *tmp2; assert(h != 0); assert(h->stor_begin != 0); if (size <= actual_size) { return 0; } tmp1=igraph_Calloc(size, igraph_real_t); if (tmp1==0) { IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp1); /* TODO: hack */ tmp2=igraph_Calloc(size, long int); if (tmp2==0) { IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp2); memcpy(tmp1, h->stor_begin, (size_t) actual_size*sizeof(igraph_real_t)); memcpy(tmp2, h->index_begin, (size_t) actual_size*sizeof(long int)); igraph_Free(h->stor_begin); igraph_Free(h->index_begin); h->stor_begin=tmp1; h->index_begin=tmp2; h->stor_end=h->stor_begin + size; h->end=h->stor_begin+actual_size; IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \ingroup indheap * \brief Returns the index of the largest element in an indexed heap. */ long int igraph_indheap_max_index(igraph_indheap_t *h) { assert(h != 0); assert(h->stor_begin != 0); return h->index_begin[0]; } /** * \ingroup indheap * \brief Builds an indexed heap, this function should not be called * directly. */ void igraph_indheap_i_build(igraph_indheap_t* h, long int head) { long int size=igraph_indheap_size(h); if (RIGHTCHILD(head) < size) { /* both subtrees */ igraph_indheap_i_build(h, LEFTCHILD(head) ); igraph_indheap_i_build(h, RIGHTCHILD(head)); igraph_indheap_i_sink(h, head); } else if (LEFTCHILD(head) < size) { /* only left */ igraph_indheap_i_build(h, LEFTCHILD(head)); igraph_indheap_i_sink(h, head); } else { /* none */ } } /** * \ingroup indheap * \brief Moves an element up in the heap, don't call this function * directly. */ void igraph_indheap_i_shift_up(igraph_indheap_t *h, long int elem) { if (elem==0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) { /* at the top */ } else { igraph_indheap_i_switch(h, elem, PARENT(elem)); igraph_indheap_i_shift_up(h, PARENT(elem)); } } /** * \ingroup indheap * \brief Moves an element down in the heap, don't call this function * directly. */ void igraph_indheap_i_sink(igraph_indheap_t* h, long int head) { long int size=igraph_indheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || h->stor_begin[LEFTCHILD(head)]>=h->stor_begin[RIGHTCHILD(head)]) { /* sink to the left if needed */ if (h->stor_begin[head] < h->stor_begin[LEFTCHILD(head)]) { igraph_indheap_i_switch(h, head, LEFTCHILD(head)); igraph_indheap_i_sink(h, LEFTCHILD(head)); } } else { /* sink to the right */ if (h->stor_begin[head] < h->stor_begin[RIGHTCHILD(head)]) { igraph_indheap_i_switch(h, head, RIGHTCHILD(head)); igraph_indheap_i_sink(h, RIGHTCHILD(head)); } } } /** * \ingroup indheap * \brief Switches two elements in a heap, don't call this function * directly. */ void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2) { if (e1!=e2) { igraph_real_t tmp=h->stor_begin[e1]; h->stor_begin[e1]=h->stor_begin[e2]; h->stor_begin[e2]=tmp; tmp=h->index_begin[e1]; h->index_begin[e1]=h->index_begin[e2]; h->index_begin[e2]=(long int) tmp; } } /** * \ingroup doubleindheap * \brief Initializes an empty doubly indexed heap object (constructor). * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_d_indheap_init (igraph_d_indheap_t* h, long int alloc_size) { if (alloc_size <= 0 ) { alloc_size=1; } h->stor_begin=igraph_Calloc(alloc_size, igraph_real_t); if (h->stor_begin==0) { h->index_begin=0; h->index2_begin=0; IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); } h->stor_end=h->stor_begin + alloc_size; h->end=h->stor_begin; h->destroy=1; h->index_begin=igraph_Calloc(alloc_size, long int); if (h->index_begin==0) { igraph_Free(h->stor_begin); h->stor_begin=0; h->index2_begin=0; IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); } h->index2_begin=igraph_Calloc(alloc_size, long int); if (h->index2_begin==0) { igraph_Free(h->stor_begin); igraph_Free(h->index_begin); h->stor_begin=0; h->index_begin=0; IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); } return 0; } /** * \ingroup doubleindheap * \brief Destroys an initialized doubly indexed heap object. */ void igraph_d_indheap_destroy (igraph_d_indheap_t* h) { assert(h != 0); if (h->destroy) { if (h->stor_begin != 0) { igraph_Free(h->stor_begin); h->stor_begin=0; } if (h->index_begin != 0) { igraph_Free(h->index_begin); h->index_begin=0; } if (h->index2_begin != 0) { igraph_Free(h->index2_begin); h->index2_begin=0; } } } /** * \ingroup doubleindheap * \brief Decides whether a heap is empty. */ igraph_bool_t igraph_d_indheap_empty (igraph_d_indheap_t* h) { assert(h != 0); assert(h->stor_begin != 0); return h->stor_begin == h->end; } /** * \ingroup doubleindheap * \brief Adds an element to the heap. */ int igraph_d_indheap_push (igraph_d_indheap_t* h, igraph_real_t elem, long int idx, long int idx2) { assert(h != 0); assert(h->stor_begin != 0); /* full, allocate more storage */ if (h->stor_end == h->end) { long int new_size = igraph_d_indheap_size(h) * 2; if (new_size == 0) { new_size = 1; } IGRAPH_CHECK(igraph_d_indheap_reserve(h, new_size)); } *(h->end) = elem; h->end += 1; *(h->index_begin+igraph_d_indheap_size(h)-1)=idx ; *(h->index2_begin+igraph_d_indheap_size(h)-1)=idx2 ; /* maintain d_indheap */ igraph_d_indheap_i_shift_up(h, igraph_d_indheap_size(h)-1); return 0; } /** * \ingroup doubleindheap * \brief Returns the largest element in the heap. */ igraph_real_t igraph_d_indheap_max (igraph_d_indheap_t* h) { assert(h != NULL); assert(h->stor_begin != NULL); assert(h->stor_begin != h->end); return h->stor_begin[0]; } /** * \ingroup doubleindheap * \brief Removes the largest element from the heap. */ igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t* h) { igraph_real_t tmp; assert(h != NULL); assert(h->stor_begin != NULL); tmp=h->stor_begin[0]; igraph_d_indheap_i_switch(h, 0, igraph_d_indheap_size(h)-1); h->end -= 1; igraph_d_indheap_i_sink(h, 0); return tmp; } /** * \ingroup doubleindheap * \brief Gives the number of elements in the heap. */ long int igraph_d_indheap_size (igraph_d_indheap_t* h) { assert(h != 0); assert(h->stor_begin != 0); return h->end - h->stor_begin; } /** * \ingroup doubleindheap * \brief Allocates memory for a heap. * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_d_indheap_reserve (igraph_d_indheap_t* h, long int size) { long int actual_size=igraph_d_indheap_size(h); igraph_real_t *tmp1; long int *tmp2, *tmp3; assert(h != 0); assert(h->stor_begin != 0); if (size <= actual_size) { return 0; } tmp1=igraph_Calloc(size, igraph_real_t); if (tmp1==0) { IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp1); /* TODO: hack */ tmp2=igraph_Calloc(size, long int); if (tmp2==0) { IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp2); /* TODO: hack */ tmp3=igraph_Calloc(size, long int); if (tmp3==0) { IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp3); /* TODO: hack */ memcpy(tmp1, h->stor_begin, (size_t) actual_size*sizeof(igraph_real_t)); memcpy(tmp2, h->index_begin, (size_t) actual_size*sizeof(long int)); memcpy(tmp3, h->index2_begin, (size_t) actual_size*sizeof(long int)); igraph_Free(h->stor_begin); igraph_Free(h->index_begin); igraph_Free(h->index2_begin); h->stor_begin=tmp1; h->stor_end=h->stor_begin + size; h->end=h->stor_begin+actual_size; h->index_begin=tmp2; h->index2_begin=tmp3; IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \ingroup doubleindheap * \brief Gives the indices of the maximal element in the heap. */ void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int *idx2) { assert(h != 0); assert(h->stor_begin != 0); (*idx)=h->index_begin[0]; (*idx2)=h->index2_begin[0]; } /** * \ingroup doubleindheap * \brief Builds the heap, don't call it directly. */ void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head) { long int size=igraph_d_indheap_size(h); if (RIGHTCHILD(head) < size) { /* both subtrees */ igraph_d_indheap_i_build(h, LEFTCHILD(head) ); igraph_d_indheap_i_build(h, RIGHTCHILD(head)); igraph_d_indheap_i_sink(h, head); } else if (LEFTCHILD(head) < size) { /* only left */ igraph_d_indheap_i_build(h, LEFTCHILD(head)); igraph_d_indheap_i_sink(h, head); } else { /* none */ } } /** * \ingroup doubleindheap * \brief Moves an element up in the heap, don't call it directly. */ void igraph_d_indheap_i_shift_up(igraph_d_indheap_t *h, long int elem) { if (elem==0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) { /* at the top */ } else { igraph_d_indheap_i_switch(h, elem, PARENT(elem)); igraph_d_indheap_i_shift_up(h, PARENT(elem)); } } /** * \ingroup doubleindheap * \brief Moves an element down in the heap, don't call it directly. */ void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head) { long int size=igraph_d_indheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || h->stor_begin[LEFTCHILD(head)]>=h->stor_begin[RIGHTCHILD(head)]) { /* sink to the left if needed */ if (h->stor_begin[head] < h->stor_begin[LEFTCHILD(head)]) { igraph_d_indheap_i_switch(h, head, LEFTCHILD(head)); igraph_d_indheap_i_sink(h, LEFTCHILD(head)); } } else { /* sink to the right */ if (h->stor_begin[head] < h->stor_begin[RIGHTCHILD(head)]) { igraph_d_indheap_i_switch(h, head, RIGHTCHILD(head)); igraph_d_indheap_i_sink(h, RIGHTCHILD(head)); } } } /** * \ingroup doubleindheap * \brief Switches two elements in the heap, don't call it directly. */ void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2) { if (e1!=e2) { long int tmpi; igraph_real_t tmp=h->stor_begin[e1]; h->stor_begin[e1]=h->stor_begin[e2]; h->stor_begin[e2]=tmp; tmpi=h->index_begin[e1]; h->index_begin[e1]=h->index_begin[e2]; h->index_begin[e2]=tmpi; tmpi=h->index2_begin[e1]; h->index2_begin[e1]=h->index2_begin[e2]; h->index2_begin[e2]=tmpi; } } /*************************************************/ #undef PARENT #undef LEFTCHILD #undef RIGHTCHILD #define PARENT(x) ((x)/2) #define LEFTCHILD(x) ((x)*2+1) #define RIGHTCHILD(x) ((x)*2) #define INACTIVE IGRAPH_INFINITY #define UNDEFINED 0.0 #define INDEXINC 1 void igraph_i_cutheap_switch(igraph_i_cutheap_t *ch, long int hidx1, long int hidx2) { if (hidx1 != hidx2) { long int idx1=(long int) VECTOR(ch->index)[hidx1]; long int idx2=(long int) VECTOR(ch->index)[hidx2]; igraph_real_t tmp=VECTOR(ch->heap)[hidx1]; VECTOR(ch->heap)[hidx1]=VECTOR(ch->heap)[hidx2]; VECTOR(ch->heap)[hidx2]=tmp; VECTOR(ch->index)[hidx1]=idx2; VECTOR(ch->index)[hidx2]=idx1; VECTOR(ch->hptr)[idx1] = hidx2+INDEXINC; VECTOR(ch->hptr)[idx2] = hidx1+INDEXINC; } } void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, long int hidx) { long int size=igraph_vector_size(&ch->heap); if (LEFTCHILD(hidx) >= size) { /* leaf node */ } else if (RIGHTCHILD(hidx) == size || VECTOR(ch->heap)[LEFTCHILD(hidx)] >= VECTOR(ch->heap)[RIGHTCHILD(hidx)]) { /* sink to the left if needed */ if (VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[LEFTCHILD(hidx)]) { igraph_i_cutheap_switch(ch, hidx, LEFTCHILD(hidx)); igraph_i_cutheap_sink(ch, LEFTCHILD(hidx)); } } else { /* sink to the right */ if (VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[RIGHTCHILD(hidx)]) { igraph_i_cutheap_switch(ch, hidx, RIGHTCHILD(hidx)); igraph_i_cutheap_sink(ch, RIGHTCHILD(hidx)); } } } void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, long int hidx) { if (hidx==0 || VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[PARENT(hidx)]) { /* at the top */ } else { igraph_i_cutheap_switch(ch, hidx, PARENT(hidx)); igraph_i_cutheap_shift_up(ch, PARENT(hidx)); } } int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes) { ch->dnodes=nodes; IGRAPH_VECTOR_INIT_FINALLY(&ch->heap, nodes); /* all zero */ IGRAPH_CHECK(igraph_vector_init_seq(&ch->index, 0, nodes-1)); IGRAPH_FINALLY(igraph_vector_destroy, &ch->index); IGRAPH_CHECK(igraph_vector_init_seq(&ch->hptr, INDEXINC, nodes+INDEXINC-1)); IGRAPH_FINALLY_CLEAN(2); return 0; } void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch) { igraph_vector_destroy(&ch->hptr); igraph_vector_destroy(&ch->index); igraph_vector_destroy(&ch->heap); } igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch) { return igraph_vector_empty(&ch->heap); } /* Number of active vertices */ igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch) { return (igraph_integer_t) igraph_vector_size(&ch->heap); } /* Number of all (defined) vertices */ igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch) { return (igraph_integer_t) (ch->dnodes); } igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch) { return VECTOR(ch->heap)[0]; } igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch) { long int size=igraph_vector_size(&ch->heap); igraph_integer_t maxindex=(igraph_integer_t) VECTOR(ch->index)[0]; /* put the last element to the top */ igraph_i_cutheap_switch(ch, 0, size-1); /* remove the last element */ VECTOR(ch->hptr)[(long int) igraph_vector_tail(&ch->index)] = INACTIVE; igraph_vector_pop_back(&ch->heap); igraph_vector_pop_back(&ch->index); igraph_i_cutheap_sink(ch, 0); return maxindex; } /* Update the value of an active vertex, if not active it will be ignored */ int igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add) { igraph_real_t hidx=VECTOR(ch->hptr)[(long int)index]; if (hidx != INACTIVE && hidx != UNDEFINED) { long int hidx2=(long int) (hidx-INDEXINC); /* printf("updating vertex %li, heap index %li\n", (long int) index, hidx2); */ VECTOR(ch->heap)[hidx2] += add; igraph_i_cutheap_sink(ch, hidx2); igraph_i_cutheap_shift_up(ch, hidx2); } return 0; } /* Reset the value of all vertices to zero and make them active */ int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex) { long int i, j, n=igraph_vector_size(&ch->hptr); /* undefine */ VECTOR(ch->hptr)[vertex] = UNDEFINED; ch->dnodes -= 1; IGRAPH_CHECK(igraph_vector_resize(&ch->heap, ch->dnodes)); igraph_vector_null(&ch->heap); IGRAPH_CHECK(igraph_vector_resize(&ch->index, ch->dnodes)); j=0; for (i=0; ihptr)[i] != UNDEFINED) { VECTOR(ch->index)[j]=i; VECTOR(ch->hptr)[i]=j+INDEXINC; j++; } } return 0; } /* -------------------------------------------------- */ /* Two-way indexed heap */ /* -------------------------------------------------- */ #undef PARENT #undef LEFTCHILD #undef RIGHTCHILD #define PARENT(x) (((x)+1)/2-1) #define LEFTCHILD(x) (((x)+1)*2-1) #define RIGHTCHILD(x) (((x)+1)*2) /* This is a smart indexed heap. In addition to the "normal" indexed heap it allows to access every element through its index in O(1) time. In other words, for this heap the indexing operation is O(1), the normal heap does this in O(n) time.... */ void igraph_i_2wheap_switch(igraph_2wheap_t *h, long int e1, long int e2) { if (e1 != e2) { long int tmp1, tmp2; igraph_real_t tmp3=VECTOR(h->data)[e1]; VECTOR(h->data)[e1]=VECTOR(h->data)[e2]; VECTOR(h->data)[e2]=tmp3; tmp1=VECTOR(h->index)[e1]; tmp2=VECTOR(h->index)[e2]; VECTOR(h->index2)[tmp1]=e2+2; VECTOR(h->index2)[tmp2]=e1+2; VECTOR(h->index)[e1]=tmp2; VECTOR(h->index)[e2]=tmp1; } } void igraph_i_2wheap_shift_up(igraph_2wheap_t *h, long int elem) { if (elem==0 || VECTOR(h->data)[elem] < VECTOR(h->data)[PARENT(elem)]) { /* at the top */ } else { igraph_i_2wheap_switch(h, elem, PARENT(elem)); igraph_i_2wheap_shift_up(h, PARENT(elem)); } } void igraph_i_2wheap_sink(igraph_2wheap_t *h, long int head) { long int size=igraph_2wheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || VECTOR(h->data)[LEFTCHILD(head)]>=VECTOR(h->data)[RIGHTCHILD(head)]) { /* sink to the left if needed */ if (VECTOR(h->data)[head] < VECTOR(h->data)[LEFTCHILD(head)]) { igraph_i_2wheap_switch(h, head, LEFTCHILD(head)); igraph_i_2wheap_sink(h, LEFTCHILD(head)); } } else { /* sink to the right */ if (VECTOR(h->data)[head] < VECTOR(h->data)[RIGHTCHILD(head)]) { igraph_i_2wheap_switch(h, head, RIGHTCHILD(head)); igraph_i_2wheap_sink(h, RIGHTCHILD(head)); } } } /* ------------------ */ /* These are public */ /* ------------------ */ int igraph_2wheap_init(igraph_2wheap_t *h, long int size) { h->size=size; /* We start with the biggest */ IGRAPH_CHECK(igraph_vector_long_init(&h->index2, size)); IGRAPH_FINALLY(igraph_vector_long_destroy, &h->index2); IGRAPH_VECTOR_INIT_FINALLY(&h->data, 0); IGRAPH_CHECK(igraph_vector_long_init(&h->index, 0)); /* IGRAPH_FINALLY(igraph_vector_long_destroy, &h->index); */ IGRAPH_FINALLY_CLEAN(2); return 0; } void igraph_2wheap_destroy(igraph_2wheap_t *h) { igraph_vector_destroy(&h->data); igraph_vector_long_destroy(&h->index); igraph_vector_long_destroy(&h->index2); } int igraph_2wheap_clear(igraph_2wheap_t *h) { igraph_vector_clear(&h->data); igraph_vector_long_clear(&h->index); igraph_vector_long_null(&h->index2); return 0; } igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h) { return igraph_vector_empty(&h->data); } int igraph_2wheap_push_with_index(igraph_2wheap_t *h, long int idx, igraph_real_t elem) { /* printf("-> %.2g [%li]\n", elem, idx); */ long int size=igraph_vector_size(&h->data); IGRAPH_CHECK(igraph_vector_push_back(&h->data, elem)); IGRAPH_CHECK(igraph_vector_long_push_back(&h->index, idx)); VECTOR(h->index2)[idx] = size+2; /* maintain heap */ igraph_i_2wheap_shift_up(h, size); return 0; } long int igraph_2wheap_size(const igraph_2wheap_t *h) { return igraph_vector_size(&h->data); } long int igraph_2wheap_max_size(const igraph_2wheap_t *h) { return h->size; } igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h) { return VECTOR(h->data)[0]; } long int igraph_2wheap_max_index(const igraph_2wheap_t *h) { return VECTOR(h->index)[0]; } igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx) { return VECTOR(h->index2)[idx] != 0; } igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx) { return VECTOR(h->index2)[idx] > 1; } igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, long int idx) { long int i=VECTOR(h->index2)[idx]-2; return VECTOR(h->data)[i]; } igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h) { igraph_real_t tmp=VECTOR(h->data)[0]; long int tmpidx=VECTOR(h->index)[0]; igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h)-1); igraph_vector_pop_back(&h->data); igraph_vector_long_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 0; igraph_i_2wheap_sink(h, 0); /* printf("<-max %.2g\n", tmp); */ return tmp; } igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h) { igraph_real_t tmp=VECTOR(h->data)[0]; long int tmpidx=VECTOR(h->index)[0]; igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h)-1); igraph_vector_pop_back(&h->data); igraph_vector_long_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 1; igraph_i_2wheap_sink(h, 0); return tmp; } igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx) { igraph_real_t tmp=VECTOR(h->data)[0]; long int tmpidx=VECTOR(h->index)[0]; igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h)-1); igraph_vector_pop_back(&h->data); igraph_vector_long_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 0; igraph_i_2wheap_sink(h, 0); if (idx) { *idx=tmpidx; } return tmp; } int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem) { long int pos=VECTOR(h->index2)[idx]-2; /* printf("-- %.2g -> %.2g\n", VECTOR(h->data)[pos], elem); */ VECTOR(h->data)[pos] = elem; igraph_i_2wheap_sink(h, pos); igraph_i_2wheap_shift_up(h, pos); return 0; } /* Check that the heap is in a consistent state */ int igraph_2wheap_check(igraph_2wheap_t *h) { long int size=igraph_2wheap_size(h); long int i; igraph_bool_t error=0; /* Check the heap property */ for (i=0; i= size) { break; } if (VECTOR(h->data)[LEFTCHILD(i)] > VECTOR(h->data)[i]) { error=1; break; } if (RIGHTCHILD(i) >= size) { break; } if (VECTOR(h->data)[RIGHTCHILD(i)] > VECTOR(h->data)[i]) { error=1; break; } } if (error) { IGRAPH_ERROR("Inconsistent heap", IGRAPH_EINTERNAL); } return 0; } igraph/src/igraph_paths.h0000644000176000001440000001120712325527073015174 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_PATHS_H #define IGRAPH_PATHS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_matrix.h" __BEGIN_DECLS int igraph_diameter(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t *from, igraph_integer_t *to, igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t unconn); int igraph_diameter_dijkstra(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *pres, igraph_integer_t *pfrom, igraph_integer_t *pto, igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t unconn); int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t from, const igraph_vs_t to, igraph_neimode_t mode); int igraph_get_shortest_paths(const igraph_t *graph, igraph_vector_ptr_t *vertices, igraph_vector_ptr_t *edges, igraph_integer_t from, const igraph_vs_t to, igraph_neimode_t mode, igraph_vector_long_t *predecessors, igraph_vector_long_t *inbound_edges); int igraph_get_shortest_path(const igraph_t *graph, igraph_vector_t *vertices, igraph_vector_t *edges, igraph_integer_t from, igraph_integer_t to, igraph_neimode_t mode); int igraph_get_all_shortest_paths(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vector_t *nrgeo, igraph_integer_t from, const igraph_vs_t to, igraph_neimode_t mode); int igraph_shortest_paths_dijkstra(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t from, const igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode); int igraph_shortest_paths_bellman_ford(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t from, const igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode); int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, igraph_vector_ptr_t *vertices, igraph_vector_ptr_t *edges, igraph_integer_t from, igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode, igraph_vector_long_t *predecessors, igraph_vector_long_t *inbound_edges); int igraph_get_shortest_path_dijkstra(const igraph_t *graph, igraph_vector_t *vertices, igraph_vector_t *edges, igraph_integer_t from, igraph_integer_t to, const igraph_vector_t *weights, igraph_neimode_t mode); int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vector_t *nrgeo, igraph_integer_t from, igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode); int igraph_shortest_paths_johnson(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t from, const igraph_vs_t to, const igraph_vector_t *weights); int igraph_average_path_length(const igraph_t *graph, igraph_real_t *res, igraph_bool_t directed, igraph_bool_t unconn); int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, igraph_real_t *unconnected, igraph_bool_t directed); int igraph_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_neimode_t mode); int igraph_radius(const igraph_t *graph, igraph_real_t *radius, igraph_neimode_t mode); __END_DECLS #endif igraph/src/Ray.cpp0000755000176000001440000000105612325527072013614 0ustar ripleyusers#include "Ray.h" namespace igraph { Ray::Ray() {} Ray::~Ray() {} Ray::Ray(const Point& rOrigin, const Vector& rDirection) { Direction(rDirection); Origin(rOrigin); } Ray::Ray(const Point& rOrigin, const Point& rEndPoint) { Direction(Vector(rOrigin,rEndPoint)); Origin(rOrigin); } const Point& Ray::Origin() const { return mOrigin; } void Ray::Origin(Point vOrigin) { mOrigin = vOrigin; } const Vector& Ray::Direction() const { return mDirection; } void Ray::Direction(Vector vDirection) { mDirection = vDirection; } } // namespace igraph igraph/src/glpgmp.c0000644000176000001440000007603112325527073014012 0ustar ripleyusers/* glpgmp.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #define _GLPSTD_STDIO #include "glpdmp.h" #include "glpgmp.h" #define xfault xerror #ifdef HAVE_GMP /* use GNU MP bignum library */ int gmp_pool_count(void) { return 0; } void gmp_free_mem(void) { return; } #else /* use GLPK bignum module */ static DMP *gmp_pool = NULL; static int gmp_size = 0; static unsigned short *gmp_work = NULL; void *gmp_get_atom(int size) { if (gmp_pool == NULL) gmp_pool = dmp_create_pool(); return dmp_get_atom(gmp_pool, size); } void gmp_free_atom(void *ptr, int size) { xassert(gmp_pool != NULL); dmp_free_atom(gmp_pool, ptr, size); return; } int gmp_pool_count(void) { if (gmp_pool == NULL) return 0; else return dmp_in_use(gmp_pool).lo; } unsigned short *gmp_get_work(int size) { xassert(size > 0); if (gmp_size < size) { if (gmp_size == 0) { xassert(gmp_work == NULL); gmp_size = 100; } else { xassert(gmp_work != NULL); xfree(gmp_work); } while (gmp_size < size) gmp_size += gmp_size; gmp_work = xcalloc(gmp_size, sizeof(unsigned short)); } return gmp_work; } void gmp_free_mem(void) { if (gmp_pool != NULL) dmp_delete_pool(gmp_pool); if (gmp_work != NULL) xfree(gmp_work); gmp_pool = NULL; gmp_size = 0; gmp_work = NULL; return; } /*====================================================================*/ mpz_t _mpz_init(void) { /* initialize x, and set its value to 0 */ mpz_t x; x = gmp_get_atom(sizeof(struct mpz)); x->val = 0; x->ptr = NULL; return x; } void mpz_clear(mpz_t x) { /* free the space occupied by x */ mpz_set_si(x, 0); xassert(x->ptr == NULL); /* free the number descriptor */ gmp_free_atom(x, sizeof(struct mpz)); return; } void mpz_set(mpz_t z, mpz_t x) { /* set the value of z from x */ struct mpz_seg *e, *ee, *es; if (z != x) { mpz_set_si(z, 0); z->val = x->val; xassert(z->ptr == NULL); for (e = x->ptr, es = NULL; e != NULL; e = e->next) { ee = gmp_get_atom(sizeof(struct mpz_seg)); memcpy(ee->d, e->d, 12); ee->next = NULL; if (z->ptr == NULL) z->ptr = ee; else es->next = ee; es = ee; } } return; } void mpz_set_si(mpz_t x, int val) { /* set the value of x to val */ struct mpz_seg *e; /* free existing segments, if any */ while (x->ptr != NULL) { e = x->ptr; x->ptr = e->next; gmp_free_atom(e, sizeof(struct mpz_seg)); } /* assign new value */ if (val == 0x80000000) { /* long format is needed */ x->val = -1; x->ptr = e = gmp_get_atom(sizeof(struct mpz_seg)); memset(e->d, 0, 12); e->d[1] = 0x8000; e->next = NULL; } else { /* short format is enough */ x->val = val; } return; } double mpz_get_d(mpz_t x) { /* convert x to a double, truncating if necessary */ struct mpz_seg *e; int j; double val, deg; if (x->ptr == NULL) val = (double)x->val; else { xassert(x->val != 0); val = 0.0; deg = 1.0; for (e = x->ptr; e != NULL; e = e->next) { for (j = 0; j <= 5; j++) { val += deg * (double)((int)e->d[j]); deg *= 65536.0; } } if (x->val < 0) val = - val; } return val; } double mpz_get_d_2exp(int *exp, mpz_t x) { /* convert x to a double, truncating if necessary (i.e. rounding towards zero), and returning the exponent separately; the return value is in the range 0.5 <= |d| < 1 and the exponent is stored to *exp; d*2^exp is the (truncated) x value; if x is zero, the return is 0.0 and 0 is stored to *exp; this is similar to the standard C frexp function */ struct mpz_seg *e; int j, n, n1; double val; if (x->ptr == NULL) val = (double)x->val, n = 0; else { xassert(x->val != 0); val = 0.0, n = 0; for (e = x->ptr; e != NULL; e = e->next) { for (j = 0; j <= 5; j++) { val += (double)((int)e->d[j]); val /= 65536.0, n += 16; } } if (x->val < 0) val = - val; } val = frexp(val, &n1); *exp = n + n1; return val; } void mpz_swap(mpz_t x, mpz_t y) { /* swap the values x and y efficiently */ int val; void *ptr; val = x->val, ptr = x->ptr; x->val = y->val, x->ptr = y->ptr; y->val = val, y->ptr = ptr; return; } static void normalize(mpz_t x) { /* normalize integer x that includes removing non-significant (leading) zeros and converting to short format, if possible */ struct mpz_seg *es, *e; /* if the integer is in short format, it remains unchanged */ if (x->ptr == NULL) { xassert(x->val != 0x80000000); goto done; } xassert(x->val == +1 || x->val == -1); /* find the last (most significant) non-zero segment */ es = NULL; for (e = x->ptr; e != NULL; e = e->next) { if (e->d[0] || e->d[1] || e->d[2] || e->d[3] || e->d[4] || e->d[5]) es = e; } /* if all segments contain zeros, the integer is zero */ if (es == NULL) { mpz_set_si(x, 0); goto done; } /* remove non-significant (leading) zero segments */ while (es->next != NULL) { e = es->next; es->next = e->next; gmp_free_atom(e, sizeof(struct mpz_seg)); } /* convert the integer to short format, if possible */ e = x->ptr; if (e->next == NULL && e->d[1] <= 0x7FFF && !e->d[2] && !e->d[3] && !e->d[4] && !e->d[5]) { int val; val = (int)e->d[0] + ((int)e->d[1] << 16); if (x->val < 0) val = - val; mpz_set_si(x, val); } done: return; } void mpz_add(mpz_t z, mpz_t x, mpz_t y) { /* set z to x + y */ static struct mpz_seg zero = { { 0, 0, 0, 0, 0, 0 }, NULL }; struct mpz_seg dumx, dumy, *ex, *ey, *ez, *es, *ee; int k, sx, sy, sz; unsigned int t; /* if [x] = 0 then [z] = [y] */ if (x->val == 0) { xassert(x->ptr == NULL); mpz_set(z, y); goto done; } /* if [y] = 0 then [z] = [x] */ if (y->val == 0) { xassert(y->ptr == NULL); mpz_set(z, x); goto done; } /* special case when both [x] and [y] are in short format */ if (x->ptr == NULL && y->ptr == NULL) { int xval = x->val, yval = y->val, zval = x->val + y->val; xassert(xval != 0x80000000 && yval != 0x80000000); if (!(xval > 0 && yval > 0 && zval <= 0 || xval < 0 && yval < 0 && zval >= 0)) { mpz_set_si(z, zval); goto done; } } /* convert [x] to long format, if necessary */ if (x->ptr == NULL) { xassert(x->val != 0x80000000); if (x->val >= 0) { sx = +1; t = (unsigned int)(+ x->val); } else { sx = -1; t = (unsigned int)(- x->val); } ex = &dumx; ex->d[0] = (unsigned short)t; ex->d[1] = (unsigned short)(t >> 16); ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; ex->next = NULL; } else { sx = x->val; xassert(sx == +1 || sx == -1); ex = x->ptr; } /* convert [y] to long format, if necessary */ if (y->ptr == NULL) { xassert(y->val != 0x80000000); if (y->val >= 0) { sy = +1; t = (unsigned int)(+ y->val); } else { sy = -1; t = (unsigned int)(- y->val); } ey = &dumy; ey->d[0] = (unsigned short)t; ey->d[1] = (unsigned short)(t >> 16); ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; ey->next = NULL; } else { sy = y->val; xassert(sy == +1 || sy == -1); ey = y->ptr; } /* main fragment */ sz = sx; ez = es = NULL; if (sx > 0 && sy > 0 || sx < 0 && sy < 0) { /* [x] and [y] have identical signs -- addition */ t = 0; for (; ex || ey; ex = ex->next, ey = ey->next) { if (ex == NULL) ex = &zero; if (ey == NULL) ey = &zero; ee = gmp_get_atom(sizeof(struct mpz_seg)); for (k = 0; k <= 5; k++) { t += (unsigned int)ex->d[k]; t += (unsigned int)ey->d[k]; ee->d[k] = (unsigned short)t; t >>= 16; } ee->next = NULL; if (ez == NULL) ez = ee; else es->next = ee; es = ee; } if (t) { /* overflow -- one extra digit is needed */ ee = gmp_get_atom(sizeof(struct mpz_seg)); ee->d[0] = 1; ee->d[1] = ee->d[2] = ee->d[3] = ee->d[4] = ee->d[5] = 0; ee->next = NULL; xassert(es != NULL); es->next = ee; } } else { /* [x] and [y] have different signs -- subtraction */ t = 1; for (; ex || ey; ex = ex->next, ey = ey->next) { if (ex == NULL) ex = &zero; if (ey == NULL) ey = &zero; ee = gmp_get_atom(sizeof(struct mpz_seg)); for (k = 0; k <= 5; k++) { t += (unsigned int)ex->d[k]; t += (0xFFFF - (unsigned int)ey->d[k]); ee->d[k] = (unsigned short)t; t >>= 16; } ee->next = NULL; if (ez == NULL) ez = ee; else es->next = ee; es = ee; } if (!t) { /* |[x]| < |[y]| -- result in complement coding */ sz = - sz; t = 1; for (ee = ez; ee != NULL; ee = ee->next) for (k = 0; k <= 5; k++) { t += (0xFFFF - (unsigned int)ee->d[k]); ee->d[k] = (unsigned short)t; t >>= 16; } } } /* contruct and normalize result */ mpz_set_si(z, 0); z->val = sz; z->ptr = ez; normalize(z); done: return; } void mpz_sub(mpz_t z, mpz_t x, mpz_t y) { /* set z to x - y */ if (x == y) mpz_set_si(z, 0); else { y->val = - y->val; mpz_add(z, x, y); if (y != z) y->val = - y->val; } return; } void mpz_mul(mpz_t z, mpz_t x, mpz_t y) { /* set z to x * y */ struct mpz_seg dumx, dumy, *ex, *ey, *es, *e; int sx, sy, k, nx, ny, n; unsigned int t; unsigned short *work, *wx, *wy; /* if [x] = 0 then [z] = 0 */ if (x->val == 0) { xassert(x->ptr == NULL); mpz_set_si(z, 0); goto done; } /* if [y] = 0 then [z] = 0 */ if (y->val == 0) { xassert(y->ptr == NULL); mpz_set_si(z, 0); goto done; } /* special case when both [x] and [y] are in short format */ if (x->ptr == NULL && y->ptr == NULL) { int xval = x->val, yval = y->val, sz = +1; xassert(xval != 0x80000000 && yval != 0x80000000); if (xval < 0) xval = - xval, sz = - sz; if (yval < 0) yval = - yval, sz = - sz; if (xval <= 0x7FFFFFFF / yval) { mpz_set_si(z, sz * (xval * yval)); goto done; } } /* convert [x] to long format, if necessary */ if (x->ptr == NULL) { xassert(x->val != 0x80000000); if (x->val >= 0) { sx = +1; t = (unsigned int)(+ x->val); } else { sx = -1; t = (unsigned int)(- x->val); } ex = &dumx; ex->d[0] = (unsigned short)t; ex->d[1] = (unsigned short)(t >> 16); ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; ex->next = NULL; } else { sx = x->val; xassert(sx == +1 || sx == -1); ex = x->ptr; } /* convert [y] to long format, if necessary */ if (y->ptr == NULL) { xassert(y->val != 0x80000000); if (y->val >= 0) { sy = +1; t = (unsigned int)(+ y->val); } else { sy = -1; t = (unsigned int)(- y->val); } ey = &dumy; ey->d[0] = (unsigned short)t; ey->d[1] = (unsigned short)(t >> 16); ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; ey->next = NULL; } else { sy = y->val; xassert(sy == +1 || sy == -1); ey = y->ptr; } /* determine the number of digits of [x] */ nx = n = 0; for (e = ex; e != NULL; e = e->next) for (k = 0; k <= 5; k++) { n++; if (e->d[k]) nx = n; } xassert(nx > 0); /* determine the number of digits of [y] */ ny = n = 0; for (e = ey; e != NULL; e = e->next) for (k = 0; k <= 5; k++) { n++; if (e->d[k]) ny = n; } xassert(ny > 0); /* we need working array containing at least nx+ny+ny places */ work = gmp_get_work(nx+ny+ny); /* load digits of [x] */ wx = &work[0]; for (n = 0; n < nx; n++) wx[ny+n] = 0; for (n = 0, e = ex; e != NULL; e = e->next) for (k = 0; k <= 5; k++, n++) if (e->d[k]) wx[ny+n] = e->d[k]; /* load digits of [y] */ wy = &work[nx+ny]; for (n = 0; n < ny; n++) wy[n] = 0; for (n = 0, e = ey; e != NULL; e = e->next) for (k = 0; k <= 5; k++, n++) if (e->d[k]) wy[n] = e->d[k]; /* compute [x] * [y] */ bigmul(nx, ny, wx, wy); /* construct and normalize result */ mpz_set_si(z, 0); z->val = sx * sy; es = NULL; k = 6; for (n = 0; n < nx+ny; n++) { if (k > 5) { e = gmp_get_atom(sizeof(struct mpz_seg)); e->d[0] = e->d[1] = e->d[2] = 0; e->d[3] = e->d[4] = e->d[5] = 0; e->next = NULL; if (z->ptr == NULL) z->ptr = e; else es->next = e; es = e; k = 0; } es->d[k++] = wx[n]; } normalize(z); done: return; } void mpz_neg(mpz_t z, mpz_t x) { /* set z to 0 - x */ mpz_set(z, x); z->val = - z->val; return; } void mpz_abs(mpz_t z, mpz_t x) { /* set z to the absolute value of x */ mpz_set(z, x); if (z->val < 0) z->val = - z->val; return; } void mpz_div(mpz_t q, mpz_t r, mpz_t x, mpz_t y) { /* divide x by y, forming quotient q and/or remainder r if q = NULL then quotient is not stored; if r = NULL then remainder is not stored the sign of quotient is determined as in algebra while the sign of remainder is the same as the sign of dividend: +26 : +7 = +3, remainder is +5 -26 : +7 = -3, remainder is -5 +26 : -7 = -3, remainder is +5 -26 : -7 = +3, remainder is -5 */ struct mpz_seg dumx, dumy, *ex, *ey, *es, *e; int sx, sy, k, nx, ny, n; unsigned int t; unsigned short *work, *wx, *wy; /* divide by zero is not allowed */ if (y->val == 0) { xassert(y->ptr == NULL); xfault("mpz_div: divide by zero not allowed\n"); } /* if [x] = 0 then [q] = [r] = 0 */ if (x->val == 0) { xassert(x->ptr == NULL); if (q != NULL) mpz_set_si(q, 0); if (r != NULL) mpz_set_si(r, 0); goto done; } /* special case when both [x] and [y] are in short format */ if (x->ptr == NULL && y->ptr == NULL) { int xval = x->val, yval = y->val; xassert(xval != 0x80000000 && yval != 0x80000000); if (q != NULL) mpz_set_si(q, xval / yval); if (r != NULL) mpz_set_si(r, xval % yval); goto done; } /* convert [x] to long format, if necessary */ if (x->ptr == NULL) { xassert(x->val != 0x80000000); if (x->val >= 0) { sx = +1; t = (unsigned int)(+ x->val); } else { sx = -1; t = (unsigned int)(- x->val); } ex = &dumx; ex->d[0] = (unsigned short)t; ex->d[1] = (unsigned short)(t >> 16); ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; ex->next = NULL; } else { sx = x->val; xassert(sx == +1 || sx == -1); ex = x->ptr; } /* convert [y] to long format, if necessary */ if (y->ptr == NULL) { xassert(y->val != 0x80000000); if (y->val >= 0) { sy = +1; t = (unsigned int)(+ y->val); } else { sy = -1; t = (unsigned int)(- y->val); } ey = &dumy; ey->d[0] = (unsigned short)t; ey->d[1] = (unsigned short)(t >> 16); ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; ey->next = NULL; } else { sy = y->val; xassert(sy == +1 || sy == -1); ey = y->ptr; } /* determine the number of digits of [x] */ nx = n = 0; for (e = ex; e != NULL; e = e->next) for (k = 0; k <= 5; k++) { n++; if (e->d[k]) nx = n; } xassert(nx > 0); /* determine the number of digits of [y] */ ny = n = 0; for (e = ey; e != NULL; e = e->next) for (k = 0; k <= 5; k++) { n++; if (e->d[k]) ny = n; } xassert(ny > 0); /* if nx < ny then [q] = 0 and [r] = [x] */ if (nx < ny) { if (r != NULL) mpz_set(r, x); if (q != NULL) mpz_set_si(q, 0); goto done; } /* we need working array containing at least nx+ny+1 places */ work = gmp_get_work(nx+ny+1); /* load digits of [x] */ wx = &work[0]; for (n = 0; n < nx; n++) wx[n] = 0; for (n = 0, e = ex; e != NULL; e = e->next) for (k = 0; k <= 5; k++, n++) if (e->d[k]) wx[n] = e->d[k]; /* load digits of [y] */ wy = &work[nx+1]; for (n = 0; n < ny; n++) wy[n] = 0; for (n = 0, e = ey; e != NULL; e = e->next) for (k = 0; k <= 5; k++, n++) if (e->d[k]) wy[n] = e->d[k]; /* compute quotient and remainder */ xassert(wy[ny-1] != 0); bigdiv(nx-ny, ny, wx, wy); /* construct and normalize quotient */ if (q != NULL) { mpz_set_si(q, 0); q->val = sx * sy; es = NULL; k = 6; for (n = ny; n <= nx; n++) { if (k > 5) { e = gmp_get_atom(sizeof(struct mpz_seg)); e->d[0] = e->d[1] = e->d[2] = 0; e->d[3] = e->d[4] = e->d[5] = 0; e->next = NULL; if (q->ptr == NULL) q->ptr = e; else es->next = e; es = e; k = 0; } es->d[k++] = wx[n]; } normalize(q); } /* construct and normalize remainder */ if (r != NULL) { mpz_set_si(r, 0); r->val = sx; es = NULL; k = 6; for (n = 0; n < ny; n++) { if (k > 5) { e = gmp_get_atom(sizeof(struct mpz_seg)); e->d[0] = e->d[1] = e->d[2] = 0; e->d[3] = e->d[4] = e->d[5] = 0; e->next = NULL; if (r->ptr == NULL) r->ptr = e; else es->next = e; es = e; k = 0; } es->d[k++] = wx[n]; } normalize(r); } done: return; } void mpz_gcd(mpz_t z, mpz_t x, mpz_t y) { /* set z to the greatest common divisor of x and y */ /* in case of arbitrary integers GCD(x, y) = GCD(|x|, |y|), and, in particular, GCD(0, 0) = 0 */ mpz_t u, v, r; mpz_init(u); mpz_init(v); mpz_init(r); mpz_abs(u, x); mpz_abs(v, y); while (mpz_sgn(v)) { mpz_div(NULL, r, u, v); mpz_set(u, v); mpz_set(v, r); } mpz_set(z, u); mpz_clear(u); mpz_clear(v); mpz_clear(r); return; } int mpz_cmp(mpz_t x, mpz_t y) { /* compare x and y; return a positive value if x > y, zero if x = y, or a nefative value if x < y */ static struct mpz_seg zero = { { 0, 0, 0, 0, 0, 0 }, NULL }; struct mpz_seg dumx, dumy, *ex, *ey; int cc, sx, sy, k; unsigned int t; if (x == y) { cc = 0; goto done; } /* special case when both [x] and [y] are in short format */ if (x->ptr == NULL && y->ptr == NULL) { int xval = x->val, yval = y->val; xassert(xval != 0x80000000 && yval != 0x80000000); cc = (xval > yval ? +1 : xval < yval ? -1 : 0); goto done; } /* special case when [x] and [y] have different signs */ if (x->val > 0 && y->val <= 0 || x->val == 0 && y->val < 0) { cc = +1; goto done; } if (x->val < 0 && y->val >= 0 || x->val == 0 && y->val > 0) { cc = -1; goto done; } /* convert [x] to long format, if necessary */ if (x->ptr == NULL) { xassert(x->val != 0x80000000); if (x->val >= 0) { sx = +1; t = (unsigned int)(+ x->val); } else { sx = -1; t = (unsigned int)(- x->val); } ex = &dumx; ex->d[0] = (unsigned short)t; ex->d[1] = (unsigned short)(t >> 16); ex->d[2] = ex->d[3] = ex->d[4] = ex->d[5] = 0; ex->next = NULL; } else { sx = x->val; xassert(sx == +1 || sx == -1); ex = x->ptr; } /* convert [y] to long format, if necessary */ if (y->ptr == NULL) { xassert(y->val != 0x80000000); if (y->val >= 0) { sy = +1; t = (unsigned int)(+ y->val); } else { sy = -1; t = (unsigned int)(- y->val); } ey = &dumy; ey->d[0] = (unsigned short)t; ey->d[1] = (unsigned short)(t >> 16); ey->d[2] = ey->d[3] = ey->d[4] = ey->d[5] = 0; ey->next = NULL; } else { sy = y->val; xassert(sy == +1 || sy == -1); ey = y->ptr; } /* main fragment */ xassert(sx > 0 && sy > 0 || sx < 0 && sy < 0); cc = 0; for (; ex || ey; ex = ex->next, ey = ey->next) { if (ex == NULL) ex = &zero; if (ey == NULL) ey = &zero; for (k = 0; k <= 5; k++) { if (ex->d[k] > ey->d[k]) cc = +1; if (ex->d[k] < ey->d[k]) cc = -1; } } if (sx < 0) cc = - cc; done: return cc; } int mpz_sgn(mpz_t x) { /* return +1 if x > 0, 0 if x = 0, and -1 if x < 0 */ int s; s = (x->val > 0 ? +1 : x->val < 0 ? -1 : 0); return s; } int mpz_out_str(void *_fp, int base, mpz_t x) { /* output x on stream fp, as a string in given base; the base may vary from 2 to 36; return the number of bytes written, or if an error occurred, return 0 */ FILE *fp = _fp; mpz_t b, y, r; int n, j, nwr = 0; unsigned char *d; static char *set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (!(2 <= base && base <= 36)) xfault("mpz_out_str: base = %d; invalid base\n", base); mpz_init(b); mpz_set_si(b, base); mpz_init(y); mpz_init(r); /* determine the number of digits */ mpz_abs(y, x); for (n = 0; mpz_sgn(y) != 0; n++) mpz_div(y, NULL, y, b); if (n == 0) n = 1; /* compute the digits */ d = xmalloc(n); mpz_abs(y, x); for (j = 0; j < n; j++) { mpz_div(y, r, y, b); xassert(0 <= r->val && r->val < base && r->ptr == NULL); d[j] = (unsigned char)r->val; } /* output the integer to the stream */ /* if (fp == NULL) fp = stdout; */ if (mpz_sgn(x) < 0) fputc('-', fp), nwr++; for (j = n-1; j >= 0; j--) fputc(set[d[j]], fp), nwr++; if (ferror(fp)) nwr = 0; mpz_clear(b); mpz_clear(y); mpz_clear(r); xfree(d); return nwr; } /*====================================================================*/ mpq_t _mpq_init(void) { /* initialize x, and set its value to 0/1 */ mpq_t x; x = gmp_get_atom(sizeof(struct mpq)); x->p.val = 0; x->p.ptr = NULL; x->q.val = 1; x->q.ptr = NULL; return x; } void mpq_clear(mpq_t x) { /* free the space occupied by x */ mpz_set_si(&x->p, 0); xassert(x->p.ptr == NULL); mpz_set_si(&x->q, 0); xassert(x->q.ptr == NULL); /* free the number descriptor */ gmp_free_atom(x, sizeof(struct mpq)); return; } void mpq_canonicalize(mpq_t x) { /* remove any factors that are common to the numerator and denominator of x, and make the denominator positive */ mpz_t f; xassert(x->q.val != 0); if (x->q.val < 0) { mpz_neg(&x->p, &x->p); mpz_neg(&x->q, &x->q); } mpz_init(f); mpz_gcd(f, &x->p, &x->q); if (!(f->val == 1 && f->ptr == NULL)) { mpz_div(&x->p, NULL, &x->p, f); mpz_div(&x->q, NULL, &x->q, f); } mpz_clear(f); return; } void mpq_set(mpq_t z, mpq_t x) { /* set the value of z from x */ if (z != x) { mpz_set(&z->p, &x->p); mpz_set(&z->q, &x->q); } return; } void mpq_set_si(mpq_t x, int p, unsigned int q) { /* set the value of x to p/q */ if (q == 0) xfault("mpq_set_si: zero denominator not allowed\n"); mpz_set_si(&x->p, p); xassert(q <= 0x7FFFFFFF); mpz_set_si(&x->q, q); return; } double mpq_get_d(mpq_t x) { /* convert x to a double, truncating if necessary */ int np, nq; double p, q; p = mpz_get_d_2exp(&np, &x->p); q = mpz_get_d_2exp(&nq, &x->q); return ldexp(p / q, np - nq); } void mpq_set_d(mpq_t x, double val) { /* set x to val; there is no rounding, the conversion is exact */ int s, n, d, j; double f; mpz_t temp; xassert(-DBL_MAX <= val && val <= +DBL_MAX); mpq_set_si(x, 0, 1); if (val > 0.0) s = +1; else if (val < 0.0) s = -1; else goto done; f = frexp(fabs(val), &n); /* |val| = f * 2^n, where 0.5 <= f < 1.0 */ mpz_init(temp); while (f != 0.0) { f *= 16.0, n -= 4; d = (int)f; xassert(0 <= d && d <= 15); f -= (double)d; /* x := 16 * x + d */ mpz_set_si(temp, 16); mpz_mul(&x->p, &x->p, temp); mpz_set_si(temp, d); mpz_add(&x->p, &x->p, temp); } mpz_clear(temp); /* x := x * 2^n */ if (n > 0) { for (j = 1; j <= n; j++) mpz_add(&x->p, &x->p, &x->p); } else if (n < 0) { for (j = 1; j <= -n; j++) mpz_add(&x->q, &x->q, &x->q); mpq_canonicalize(x); } if (s < 0) mpq_neg(x, x); done: return; } void mpq_add(mpq_t z, mpq_t x, mpq_t y) { /* set z to x + y */ mpz_t p, q; mpz_init(p); mpz_init(q); mpz_mul(p, &x->p, &y->q); mpz_mul(q, &x->q, &y->p); mpz_add(p, p, q); mpz_mul(q, &x->q, &y->q); mpz_set(&z->p, p); mpz_set(&z->q, q); mpz_clear(p); mpz_clear(q); mpq_canonicalize(z); return; } void mpq_sub(mpq_t z, mpq_t x, mpq_t y) { /* set z to x - y */ mpz_t p, q; mpz_init(p); mpz_init(q); mpz_mul(p, &x->p, &y->q); mpz_mul(q, &x->q, &y->p); mpz_sub(p, p, q); mpz_mul(q, &x->q, &y->q); mpz_set(&z->p, p); mpz_set(&z->q, q); mpz_clear(p); mpz_clear(q); mpq_canonicalize(z); return; } void mpq_mul(mpq_t z, mpq_t x, mpq_t y) { /* set z to x * y */ mpz_mul(&z->p, &x->p, &y->p); mpz_mul(&z->q, &x->q, &y->q); mpq_canonicalize(z); return; } void mpq_div(mpq_t z, mpq_t x, mpq_t y) { /* set z to x / y */ mpz_t p, q; if (mpq_sgn(y) == 0) xfault("mpq_div: zero divisor not allowed\n"); mpz_init(p); mpz_init(q); mpz_mul(p, &x->p, &y->q); mpz_mul(q, &x->q, &y->p); mpz_set(&z->p, p); mpz_set(&z->q, q); mpz_clear(p); mpz_clear(q); mpq_canonicalize(z); return; } void mpq_neg(mpq_t z, mpq_t x) { /* set z to 0 - x */ mpq_set(z, x); mpz_neg(&z->p, &z->p); return; } void mpq_abs(mpq_t z, mpq_t x) { /* set z to the absolute value of x */ mpq_set(z, x); mpz_abs(&z->p, &z->p); xassert(mpz_sgn(&x->q) > 0); return; } int mpq_cmp(mpq_t x, mpq_t y) { /* compare x and y; return a positive value if x > y, zero if x = y, or a nefative value if x < y */ mpq_t temp; int s; mpq_init(temp); mpq_sub(temp, x, y); s = mpq_sgn(temp); mpq_clear(temp); return s; } int mpq_sgn(mpq_t x) { /* return +1 if x > 0, 0 if x = 0, and -1 if x < 0 */ int s; s = mpz_sgn(&x->p); xassert(mpz_sgn(&x->q) > 0); return s; } int mpq_out_str(void *_fp, int base, mpq_t x) { /* output x on stream fp, as a string in given base; the base may vary from 2 to 36; output is in the form 'num/den' or if the denominator is 1 then just 'num'; if the parameter fp is a null pointer, stdout is assumed; return the number of bytes written, or if an error occurred, return 0 */ FILE *fp = _fp; int nwr; if (!(2 <= base && base <= 36)) xfault("mpq_out_str: base = %d; invalid base\n", base); /* if (fp == NULL) fp = stdout; */ nwr = mpz_out_str(fp, base, &x->p); if (x->q.val == 1 && x->q.ptr == NULL) ; else { fputc('/', fp), nwr++; nwr += mpz_out_str(fp, base, &x->q); } if (ferror(fp)) nwr = 0; return nwr; } #endif /* eof */ igraph/src/igraph_bipartite.h0000644000176000001440000000660212325527073016043 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_BIPARTITE_H #define IGRAPH_BIPARTITE_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" #include "igraph_datatype.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Bipartite networks */ /* -------------------------------------------------- */ int igraph_full_bipartite(igraph_t *graph, igraph_vector_bool_t *types, igraph_integer_t n1, igraph_integer_t n2, igraph_bool_t directed, igraph_neimode_t mode); int igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, const igraph_vector_t *edges, igraph_bool_t directed); int igraph_bipartite_projection_size(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_integer_t *vcount1, igraph_integer_t *ecount1, igraph_integer_t *vcount2, igraph_integer_t *ecount2); int igraph_bipartite_projection(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_t *proj1, igraph_t *proj2, igraph_vector_t *multiplicity1, igraph_vector_t *multiplicity2, igraph_integer_t probe1); int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, const igraph_matrix_t *incidence, igraph_bool_t directed, igraph_neimode_t mode, igraph_bool_t multiple); int igraph_get_incidence(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, igraph_vector_t *row_ids, igraph_vector_t *col_ids); int igraph_is_bipartite(const igraph_t *graph, igraph_bool_t *res, igraph_vector_bool_t *type); int igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types, igraph_erdos_renyi_t type, igraph_integer_t n1, igraph_integer_t n2, igraph_real_t p, igraph_integer_t m, igraph_bool_t directed, igraph_neimode_t mode); int igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types, igraph_integer_t n1, igraph_integer_t n2, igraph_real_t p, igraph_bool_t directed, igraph_neimode_t mode); int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, igraph_integer_t n1, igraph_integer_t n2, igraph_integer_t m, igraph_bool_t directed, igraph_neimode_t mode); __END_DECLS #endif igraph/src/igraph_spmatrix.h0000644000176000001440000001237312325527073015731 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_SPMATRIX_H #define IGRAPH_SPMATRIX_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_vector.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Sparse matrix */ /* -------------------------------------------------- */ /** * \section about_igraph_spmatrix_t_objects About \type igraph_spmatrix_t objects * * The \type igraph_spmatrix_t type stores a sparse matrix with the * assumption that the number of nonzero elements in the matrix scales * linearly with the row or column count of the matrix (so most of the * elements are zero). Of course it can store an arbitrary real matrix, * but if most of the elements are nonzero, one should use \type igraph_matrix_t * instead. * * The elements are stored in column compressed format, so the elements * in the same column are stored adjacent in the computer's memory. The storage * requirement for a sparse matrix is O(n) where n is the number of nonzero * elements. Actually it can be a bit larger, see the documentation of * the vector type for an explanation. */ typedef struct s_spmatrix { igraph_vector_t ridx, cidx, data; long int nrow, ncol; } igraph_spmatrix_t; #define IGRAPH_SPMATRIX_INIT_FINALLY(m, nr, nc) \ do { IGRAPH_CHECK(igraph_spmatrix_init(m, nr, nc)); \ IGRAPH_FINALLY(igraph_spmatrix_destroy, m); } while (0) int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol); void igraph_spmatrix_destroy(igraph_spmatrix_t *m); int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol); igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col); int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, igraph_real_t value); int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, igraph_real_t value); int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from); long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m); long int igraph_spmatrix_size(const igraph_spmatrix_t *m); long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m); long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m); int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to); int igraph_spmatrix_null(igraph_spmatrix_t *m); int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n); int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n); int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col); int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row); int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from); igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, igraph_real_t *ridx, igraph_real_t *cidx); igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, igraph_real_t *ridx, igraph_real_t *cidx); void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by); int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res); int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res); int igraph_spmatrix_print(const igraph_spmatrix_t *matrix); int igraph_spmatrix_fprint(const igraph_spmatrix_t *matrix, FILE* file); int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m, igraph_vector_t *res, long int col); int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row); int igraph_i_spmatrix_cleanup(igraph_spmatrix_t *m); typedef struct s_spmatrix_iter { const igraph_spmatrix_t *m; /* pointer to the matrix we are iterating over */ long int pos; /* internal index into the data vector */ long int ri; /* row index */ long int ci; /* column index */ igraph_real_t value; /* value at the given cell */ } igraph_spmatrix_iter_t; int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m); int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit); int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit); igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit); void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit); __END_DECLS #endif igraph/src/igraph_arpack_internal.h0000644000176000001440000001574112325527073017221 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ARPACK_INTERNAL_H #define ARPACK_INTERNAL_H /* Note: only files calling the arpack routines directly need to include this header. */ #include "igraph_types.h" #include "config.h" #ifndef INTERNAL_ARPACK #define igraphdsaupd_ dsaupd_ #define igraphdseupd_ dseupd_ #define igraphdsaup2_ dsaup2_ #define igraphdstats_ dstats_ #define igraphdsesrt_ dsesrt_ #define igraphdsortr_ dsortr_ #define igraphdsortc_ dsortc_ #define igraphdgetv0_ dgetv0_ #define igraphdsaitr_ dsaitr_ #define igraphdsapps_ dsapps_ #define igraphdsconv_ dsconv_ #define igraphdseigt_ dseigt_ #define igraphdsgets_ dsgets_ #define igraphdstqrb_ dstqrb_ #define igraphdmout_ dmout_ #define igraphivout_ ivout_ #define igraphsecond_ second_ #define igraphdvout_ dvout_ #define igraphdnaitr_ dnaitr_ #define igraphdnapps_ dnapps_ #define igraphdnaup2_ dnaup2_ #define igraphdnaupd_ dnaupd_ #define igraphdnconv_ dnconv_ #define igraphdlabad_ dlabad_ #define igraphdlanhs_ dlanhs_ #define igraphdsortc_ dsortc_ #define igraphdneigh_ dneigh_ #define igraphdngets_ dngets_ #define igraphdstatn_ dstatn_ #define igraphdlaqrb_ dlaqrb_ #define igraphdsaupd_ dsaupd_ #define igraphdseupd_ dseupd_ #define igraphdnaupd_ dnaupd_ #define igraphdneupd_ dneupd_ #endif #ifndef INTERNAL_LAPACK #define igraphdlarnv_ dlarnv_ #define igraphdlascl_ dlascl_ #define igraphdlartg_ dlartg_ #define igraphdlaset_ dlaset_ #define igraphdlae2_ dlae2_ #define igraphdlaev2_ dlaev2_ #define igraphdlasr_ dlasr_ #define igraphdlasrt_ dlasrt_ #define igraphdgeqr2_ dgeqr2_ #define igraphdlacpy_ dlacpy_ #define igraphdorm2r_ dorm2r_ #define igraphdsteqr_ dsteqr_ #define igraphdlanst_ dlanst_ #define igraphdlapy2_ dlapy2_ #define igraphdlamch_ dlamch_ #define igraphdlaruv_ dlaruv_ #define igraphdlarfg_ dlarfg_ #define igraphdlarf_ dlarf_ #define igraphdlassq_ dlassq_ #define igraphdlamc2_ dlamc2_ #define igraphdlamc1_ dlamc1_ #define igraphdlamc2_ dlamc2_ #define igraphdlamc3_ dlamc3_ #define igraphdlamc4_ dlamc4_ #define igraphdlamc5_ dlamc5_ #define igraphdlabad_ dlabad_ #define igraphdlanhs_ dlanhs_ #define igraphdtrevc_ dtrevc_ #define igraphdlanv2_ dlanv2_ #define igraphdlaln2_ dlaln2_ #define igraphdladiv_ dladiv_ #define igraphdtrsen_ dtrsen_ #define igraphdlahqr_ dlahqr_ #define igraphdtrsen_ dtrsen_ #define igraphdlacon_ dlacon_ #define igraphdtrsyl_ dtrsyl_ #define igraphdtrexc_ dtrexc_ #define igraphdlange_ dlange_ #define igraphdlaexc_ dlaexc_ #define igraphdlasy2_ dlasy2_ #define igraphdlarfx_ dlarfx_ #endif #if 0 /* internal f2c functions always used */ #define igraphd_sign d_sign #define igraphetime_ etime_ #define igraphpow_dd pow_dd #define igraphpow_di pow_di #define igraphs_cmp s_cmp #define igraphs_copy s_copy #define igraphd_lg10_ d_lg10_ #define igraphi_dnnt_ i_dnnt_ #endif #ifdef HAVE_GFORTRAN int igraphdsaupd_(int *ido, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info, int bmat_len, int which_len); int igraphdseupd_(int *rvec, char *howmny, int *select, igraph_real_t *d, igraph_real_t *z, int *ldz, igraph_real_t *sigma, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info, int howmny_len, int bmat_len, int which_len); int igraphdnaupd_(int *ido, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info, int bmat_len, int which_len); int igraphdneupd_(int *rvec, char *howmny, int *select, igraph_real_t *dr, igraph_real_t *di, igraph_real_t *z, int *ldz, igraph_real_t *sigmar, igraph_real_t *sigmai, igraph_real_t *workev, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info, int howmny_len, int bmat_len, int which_len); int igraphdsortr_(char *which, int *apply, int* n, igraph_real_t *x1, igraph_real_t *x2, int which_len); int igraphdsortc_(char *which, int *apply, int* n, igraph_real_t *xreal, igraph_real_t *ximag, igraph_real_t *y, int which_len); #else int igraphdsaupd_(int *ido, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info); int igraphdseupd_(int *rvec, char *howmny, int *select, igraph_real_t *d, igraph_real_t *z, int *ldz, igraph_real_t *sigma, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info); int igraphdnaupd_(int *ido, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info); int igraphdneupd_(int *rvec, char *howmny, int *select, igraph_real_t *dr, igraph_real_t *di, igraph_real_t *z, int *ldz, igraph_real_t *sigmar, igraph_real_t *sigmai, igraph_real_t *workev, char *bmat, int *n, char *which, int *nev, igraph_real_t *tol, igraph_real_t *resid, int *ncv, igraph_real_t *v, int *ldv, int *iparam, int *ipntr, igraph_real_t *workd, igraph_real_t *workl, int *lworkl, int *info); int igraphdsortr_(char *which, int *apply, int* n, igraph_real_t *x1, igraph_real_t *x2); int igraphdsortc_(char *which, int *apply, int* n, igraph_real_t *xreal, igraph_real_t *ximag, igraph_real_t *y); #endif #endif /* ARPACK_INTERNAL_H */ igraph/src/igraph_matrix_pmt.h0000644000176000001440000002251012325527073016240 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ typedef struct TYPE(igraph_matrix) { TYPE(igraph_vector) data; long int nrow, ncol; } TYPE(igraph_matrix); /*---------------*/ /* Allocation */ /*---------------*/ int FUNCTION(igraph_matrix,init)(TYPE(igraph_matrix) *m, long int nrow, long int ncol); int FUNCTION(igraph_matrix,copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); void FUNCTION(igraph_matrix,destroy)(TYPE(igraph_matrix) *m); long int FUNCTION(igraph_matrix,capacity)(const TYPE(igraph_matrix) *m); /*--------------------*/ /* Accessing elements */ /*--------------------*/ /* MATRIX */ BASE FUNCTION(igraph_matrix,e)(const TYPE(igraph_matrix) *m, long int row, long int col); BASE* FUNCTION(igraph_matrix,e_ptr)(const TYPE(igraph_matrix) *m, long int row, long int col); void FUNCTION(igraph_matrix,set)(TYPE(igraph_matrix)* m, long int row, long int col, BASE value); /*------------------------------*/ /* Initializing matrix elements */ /*------------------------------*/ void FUNCTION(igraph_matrix,null)(TYPE(igraph_matrix) *m); void FUNCTION(igraph_matrix,fill)(TYPE(igraph_matrix) *m, BASE e); /*------------------*/ /* Copying matrices */ /*------------------*/ void FUNCTION(igraph_matrix,copy_to)(const TYPE(igraph_matrix) *m, BASE *to); int FUNCTION(igraph_matrix,update)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); int FUNCTION(igraph_matrix,rbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); int FUNCTION(igraph_matrix,cbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); int FUNCTION(igraph_matrix,swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); /*--------------------------*/ /* Copying rows and columns */ /*--------------------------*/ int FUNCTION(igraph_matrix,get_row)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, long int index); int FUNCTION(igraph_matrix,get_col)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, long int index); int FUNCTION(igraph_matrix,set_row)(TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, long int index); int FUNCTION(igraph_matrix,set_col)(TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, long int index); int FUNCTION(igraph_matrix,select_rows)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_t *rows); int FUNCTION(igraph_matrix,select_cols)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_t *cols); int FUNCTION(igraph_matrix,select_rows_cols)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_t *rows, const igraph_vector_t *cols); /*-----------------------------*/ /* Exchanging rows and columns */ /*-----------------------------*/ int FUNCTION(igraph_matrix,swap_rows)(TYPE(igraph_matrix) *m, long int i, long int j); int FUNCTION(igraph_matrix,swap_cols)(TYPE(igraph_matrix) *m, long int i, long int j); int FUNCTION(igraph_matrix,swap_rowcol)(TYPE(igraph_matrix) *m, long int i, long int j); int FUNCTION(igraph_matrix,transpose)(TYPE(igraph_matrix) *m); /*-----------------------------*/ /* Matrix operations */ /*-----------------------------*/ int FUNCTION(igraph_matrix,add)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); int FUNCTION(igraph_matrix,sub)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); int FUNCTION(igraph_matrix,mul_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); int FUNCTION(igraph_matrix,div_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); void FUNCTION(igraph_matrix,scale)(TYPE(igraph_matrix) *m, BASE by); void FUNCTION(igraph_matrix,add_constant)(TYPE(igraph_matrix) *m, BASE plus); /*-----------------------------*/ /* Finding minimum and maximum */ /*-----------------------------*/ igraph_real_t FUNCTION(igraph_matrix,min)(const TYPE(igraph_matrix) *m); igraph_real_t FUNCTION(igraph_matrix,max)(const TYPE(igraph_matrix) *m); int FUNCTION(igraph_matrix,which_min)(const TYPE(igraph_matrix) *m, long int *i, long int *j); int FUNCTION(igraph_matrix,which_max)(const TYPE(igraph_matrix) *m, long int *i, long int *j); int FUNCTION(igraph_matrix,minmax)(const TYPE(igraph_matrix) *m, BASE *min, BASE *max); int FUNCTION(igraph_matrix,which_minmax)(const TYPE(igraph_matrix) *m, long int *imin, long int *jmin, long int *imax, long int *jmax); /*------------------------------*/ /* Comparison */ /*------------------------------*/ igraph_bool_t FUNCTION(igraph_matrix,all_e)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs); igraph_bool_t FUNCTION(igraph_matrix,all_l)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs); igraph_bool_t FUNCTION(igraph_matrix,all_g)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs); igraph_bool_t FUNCTION(igraph_matrix,all_le)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs); igraph_bool_t FUNCTION(igraph_matrix,all_ge)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs); /*-------------------*/ /* Matrix properties */ /*-------------------*/ igraph_bool_t FUNCTION(igraph_matrix,isnull)(const TYPE(igraph_matrix) *m); igraph_bool_t FUNCTION(igraph_matrix,empty)(const TYPE(igraph_matrix) *m); long int FUNCTION(igraph_matrix,size)(const TYPE(igraph_matrix) *m); long int FUNCTION(igraph_matrix,nrow)(const TYPE(igraph_matrix) *m); long int FUNCTION(igraph_matrix,ncol)(const TYPE(igraph_matrix) *m); igraph_bool_t FUNCTION(igraph_matrix,is_symmetric)(const TYPE(igraph_matrix) *m); BASE FUNCTION(igraph_matrix,sum)(const TYPE(igraph_matrix) *m); BASE FUNCTION(igraph_matrix,prod)(const TYPE(igraph_matrix) *m); int FUNCTION(igraph_matrix,rowsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res); int FUNCTION(igraph_matrix,colsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res); igraph_bool_t FUNCTION(igraph_matrix,is_equal)(const TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); BASE FUNCTION(igraph_matrix,maxdifference)(const TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); /*------------------------*/ /* Searching for elements */ /*------------------------*/ igraph_bool_t FUNCTION(igraph_matrix,contains)(const TYPE(igraph_matrix) *m, BASE e); igraph_bool_t FUNCTION(igraph_matrix,search)(const TYPE(igraph_matrix) *m, long int from, BASE what, long int *pos, long int *row, long int *col); /*------------------------*/ /* Resizing operations */ /*------------------------*/ int FUNCTION(igraph_matrix,resize)(TYPE(igraph_matrix) *m, long int nrow, long int ncol); int FUNCTION(igraph_matrix,resize_min)(TYPE(igraph_matrix) *m); int FUNCTION(igraph_matrix,add_cols)(TYPE(igraph_matrix) *m, long int n); int FUNCTION(igraph_matrix,add_rows)(TYPE(igraph_matrix) *m, long int n); int FUNCTION(igraph_matrix,remove_col)(TYPE(igraph_matrix) *m, long int col); int FUNCTION(igraph_matrix,remove_row)(TYPE(igraph_matrix) *m, long int row); /*------------------------*/ /* Print as text */ /*------------------------*/ int FUNCTION(igraph_matrix,print)(const TYPE(igraph_matrix) *m); int FUNCTION(igraph_matrix,printf)(const TYPE(igraph_matrix) *m, const char *format); int FUNCTION(igraph_matrix,fprint)(const TYPE(igraph_matrix) *m, FILE *file); #ifdef BASE_COMPLEX int igraph_matrix_complex_real(const igraph_matrix_complex_t *v, igraph_matrix_t *real); int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, igraph_matrix_t *imag); int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, igraph_matrix_t *real, igraph_matrix_t *imag); int igraph_matrix_complex_create(igraph_matrix_complex_t *v, const igraph_matrix_t *real, const igraph_matrix_t *imag); int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, const igraph_matrix_t *r, const igraph_matrix_t *theta); #endif /* ----------------------------------------------------------------------------*/ /* For internal use only, may be removed, rewritten ... */ /* ----------------------------------------------------------------------------*/ int FUNCTION(igraph_matrix,permdelete_rows)(TYPE(igraph_matrix) *m, long int *index, long int nremove); int FUNCTION(igraph_matrix,delete_rows_neg)(TYPE(igraph_matrix) *m, const igraph_vector_t *neg, long int nremove); igraph/src/components.c0000644000176000001440000006562012325527072014712 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_components.h" #include "igraph_memory.h" #include "igraph_interface.h" #include "igraph_adjlist.h" #include "igraph_interrupt_internal.h" #include "igraph_structural.h" #include "igraph_dqueue.h" #include "igraph_stack.h" #include "igraph_vector.h" #include "config.h" #include #include int igraph_clusters_weak(const igraph_t *graph, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t *no); int igraph_clusters_strong(const igraph_t *graph, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t *no); /** * \ingroup structural * \function igraph_clusters * \brief Calculates the (weakly or strongly) connected components in a graph. * * \param graph The graph object to analyze. * \param membership First half of the result will be stored here. For * every vertex the id of its component is given. The vector * has to be preinitialized and will be resized. Alternatively * this argument can be \c NULL, in which case it is ignored. * \param csize The second half of the result. For every component it * gives its size, the order is defined by the component ids. * The vector has to be preinitialized and will be resized. * Alternatively this argument can be \c NULL, in which * case it is ignored. * \param no Pointer to an integer, if not \c NULL then the number of * clusters will be stored here. * \param mode For directed graph this specifies whether to calculate * weakly or strongly connected components. Possible values: * \c IGRAPH_WEAK, * \c IGRAPH_STRONG. This argument is * ignored for undirected graphs. * \return Error code: * \c IGRAPH_EINVAL: invalid mode argument. * * Time complexity: O(|V|+|E|), * |V| and * |E| are the number of vertices and * edges in the graph. */ int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t *no, igraph_connectedness_t mode) { if (mode==IGRAPH_WEAK || !igraph_is_directed(graph)) { return igraph_clusters_weak(graph, membership, csize, no); } else if (mode==IGRAPH_STRONG) { return igraph_clusters_strong(graph, membership, csize, no); } else { IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_EINVAL); } return 1; } int igraph_clusters_weak(const igraph_t *graph, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t *no) { long int no_of_nodes=igraph_vcount(graph); char *already_added; long int first_node, act_cluster_size=0, no_of_clusters=1; igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; long int i; igraph_vector_t neis=IGRAPH_VECTOR_NULL; already_added=igraph_Calloc(no_of_nodes,char); if (already_added==0) { IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, already_added); IGRAPH_DQUEUE_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes/10); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); /* Memory for result, csize is dynamically allocated */ if (membership) { IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); } if (csize) { igraph_vector_clear(csize); } /* The algorithm */ for (first_node=0; first_node < no_of_nodes; ++first_node) { if (already_added[first_node]==1) continue; IGRAPH_ALLOW_INTERRUPTION(); already_added[first_node]=1; act_cluster_size=1; if (membership) { VECTOR(*membership)[first_node]=no_of_clusters-1; } IGRAPH_CHECK(igraph_dqueue_push(&q, first_node)); while ( !igraph_dqueue_empty(&q) ) { long int act_node=(long int) igraph_dqueue_pop(&q); IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) act_node, IGRAPH_ALL)); for (i=0; i igraph_vector_size(&tmp)) { continue; } IGRAPH_CHECK(igraph_dqueue_push(&q, i)); while (!igraph_dqueue_empty(&q)) { long int act_node=(long int) igraph_dqueue_back(&q); IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) act_node, IGRAPH_OUT)); if (VECTOR(next_nei)[act_node]==0) { /* this is the first time we've met this vertex */ VECTOR(next_nei)[act_node]++; } else if (VECTOR(next_nei)[act_node] <= igraph_vector_size(&tmp)) { /* we've already met this vertex but it has more children */ long int neighbor=(long int) VECTOR(tmp)[(long int) VECTOR(next_nei)[act_node]-1]; if (VECTOR(next_nei)[neighbor] == 0) { IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); } VECTOR(next_nei)[act_node]++; } else { /* we've met this vertex and it has no more children */ IGRAPH_CHECK(igraph_vector_push_back(&out, act_node)); igraph_dqueue_pop_back(&q); } } /* while q */ } /* for */ /* OK, we've the 'out' values for the nodes, let's use them in decreasing order with the help of a heap */ igraph_vector_null(&next_nei); /* mark already added vertices */ while (!igraph_vector_empty(&out)) { long int grandfather=(long int) igraph_vector_pop_back(&out); IGRAPH_ALLOW_INTERRUPTION(); if (VECTOR(next_nei)[grandfather] != 0) { continue; } VECTOR(next_nei)[grandfather]=1; act_cluster_size=1; if (membership) { VECTOR(*membership)[grandfather]=no_of_clusters-1; } IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); while (!igraph_dqueue_empty(&q)) { long int act_node=(long int) igraph_dqueue_pop_back(&q); IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) act_node, IGRAPH_IN)); for (i=0; i * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. * * \example examples/simple/igraph_decompose.c */ int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, igraph_connectedness_t mode, long int maxcompno, long int minelements) { long int actstart; long int no_of_nodes=igraph_vcount(graph); long int resco=0; /* number of graphs created so far */ char *already_added; igraph_dqueue_t q; igraph_vector_t verts; igraph_vector_t neis; long int i; igraph_t *newg; if (!igraph_is_directed(graph)) { mode=IGRAPH_WEAK; } if (mode != IGRAPH_WEAK) { IGRAPH_ERROR("only 'IGRAPH_WEAK' is implemented", IGRAPH_EINVAL); } if (maxcompno<0) { maxcompno=LONG_MAX; } igraph_vector_ptr_clear(components); IGRAPH_FINALLY(igraph_decompose_destroy, components); already_added=igraph_Calloc(no_of_nodes, char); if (already_added==0) { IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, already_added); IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &q); IGRAPH_VECTOR_INIT_FINALLY(&verts, 0); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); for(actstart=0; resco * A biconnected component of a graph is a maximal biconnected * subgraph of it. The biconnected components of a graph can be given * by the partition of its edges: every edge is a member of exactly * one biconnected component. Note that this is not true for * vertices: the same vertex can be part of many biconnected * components. * \param graph The input graph. * \param no The number of biconnected components will be stored here. * \param tree_edges If not a NULL pointer, then the found components * are stored here, in a list of vectors. Every vector in the list * is a biconnected component, represented by its edges. More precisely, * a spanning tree of the biconnected component is returned. * Note you'll have to * destroy each vector first by calling \ref igraph_vector_destroy() * and then free() on it, plus you need to call * \ref igraph_vector_ptr_destroy() on the list to regain all * allocated memory. * \param component_edges If not a NULL pointer, then the edges of the * biconnected components are stored here, in the same form as for * \c tree_edges. * \param components If not a NULL pointer, then the vertices of the * biconnected components are stored here, in the same format as * for the previous two arguments. * \param articulation_points If not a NULL pointer, then the * articulation points of the graph are stored in this vector. * A vertex is an articulation point if its removal increases the * number of (weakly) connected components in the graph. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges, but only if you do not calculate \c components and * \c component_edges. If you calculate \c components, then it is * quadratic in the number of vertices. If you calculate \c * component_edges as well, then it is cubic in the number of * vertices. * * \sa \ref igraph_articulation_points(), \ref igraph_clusters(). * * \example examples/simple/igraph_biconnected_components.c */ int igraph_biconnected_components(const igraph_t *graph, igraph_integer_t *no, igraph_vector_ptr_t *tree_edges, igraph_vector_ptr_t *component_edges, igraph_vector_ptr_t *components, igraph_vector_t *articulation_points) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_long_t nextptr; igraph_vector_long_t num, low; igraph_vector_bool_t found; igraph_vector_t *adjedges; igraph_stack_t path; igraph_vector_t edgestack; igraph_inclist_t inclist; long int i, counter, rootdfs=0; igraph_vector_long_t vertex_added; long int comps=0; igraph_vector_ptr_t *mycomponents=components, vcomponents; IGRAPH_CHECK(igraph_vector_long_init(&nextptr, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &nextptr); IGRAPH_CHECK(igraph_vector_long_init(&num, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &num); IGRAPH_CHECK(igraph_vector_long_init(&low, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &low); IGRAPH_CHECK(igraph_vector_bool_init(&found, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &found); IGRAPH_CHECK(igraph_stack_init(&path, 100)); IGRAPH_FINALLY(igraph_stack_destroy, &path); IGRAPH_VECTOR_INIT_FINALLY(&edgestack, 0); IGRAPH_CHECK(igraph_vector_reserve(&edgestack, 100)); IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); IGRAPH_CHECK(igraph_vector_long_init(&vertex_added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &vertex_added); if (no) { *no=0; } if (tree_edges) { igraph_vector_ptr_clear(tree_edges); } if (components) { igraph_vector_ptr_clear(components); } if (component_edges) { igraph_vector_ptr_clear(component_edges); } if (articulation_points) { igraph_vector_clear(articulation_points); } if (component_edges && !components) { mycomponents=&vcomponents; IGRAPH_CHECK(igraph_vector_ptr_init(mycomponents, 0)); IGRAPH_FINALLY(igraph_i_free_vectorlist, mycomponents); } for (i=0; i= VECTOR(num)[prev]) { if (articulation_points && !VECTOR(found)[prev] && prev != i /* the root */) { IGRAPH_CHECK(igraph_vector_push_back(articulation_points, prev)); VECTOR(found)[prev] = 1; } if (no) { *no += 1; } /*------------------------------------*/ /* Record the biconnected component just found */ if (tree_edges || mycomponents) { igraph_vector_t *v = 0, *v2 = 0; comps++; if (tree_edges) { v=igraph_Calloc(1, igraph_vector_t); if (!v) { IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_vector_init(v, 0)); IGRAPH_FINALLY(igraph_vector_destroy, v); } if (mycomponents) { v2=igraph_Calloc(1, igraph_vector_t); if (!v2) { IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_vector_init(v2, 0)); IGRAPH_FINALLY(igraph_vector_destroy, v2); } while (!igraph_vector_empty(&edgestack)) { long int e=(long int) igraph_vector_pop_back(&edgestack); long int from=IGRAPH_FROM(graph,e); long int to=IGRAPH_TO(graph,e); if (tree_edges) { IGRAPH_CHECK(igraph_vector_push_back(v, e)); } if (mycomponents) { if (VECTOR(vertex_added)[from] != comps) { VECTOR(vertex_added)[from] = comps; IGRAPH_CHECK(igraph_vector_push_back(v2, from)); } if (VECTOR(vertex_added)[to] != comps) { VECTOR(vertex_added)[to] = comps; IGRAPH_CHECK(igraph_vector_push_back(v2, to)); } } if (from==prev || to==prev) { break; } } if (mycomponents) { IGRAPH_CHECK(igraph_vector_ptr_push_back(mycomponents, v2)); IGRAPH_FINALLY_CLEAN(1); } if (tree_edges) { IGRAPH_CHECK(igraph_vector_ptr_push_back(tree_edges, v)); IGRAPH_FINALLY_CLEAN(1); } if (component_edges) { igraph_vector_t *nodes=VECTOR(*mycomponents)[comps-1]; igraph_vector_t *vv=igraph_Calloc(1, igraph_vector_t); long int ii, no_vert=igraph_vector_size(nodes); if (!vv) { IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_vector_init(vv, 0)); IGRAPH_FINALLY(igraph_vector_destroy, vv); for (ii=0; ii= 2) { IGRAPH_CHECK(igraph_vector_push_back(articulation_points, i)); } } /* i < no_of_nodes */ if (mycomponents != components) { igraph_i_free_vectorlist(mycomponents); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_long_destroy(&vertex_added); igraph_inclist_destroy(&inclist); igraph_vector_destroy(&edgestack); igraph_stack_destroy(&path); igraph_vector_bool_destroy(&found); igraph_vector_long_destroy(&low); igraph_vector_long_destroy(&num); igraph_vector_long_destroy(&nextptr); IGRAPH_FINALLY_CLEAN(8); return 0; } igraph/src/glpnet04.c0000644000176000001440000006226412325527073014164 0ustar ripleyusers/* glpnet04.c (grid-like network problem generator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * This code is a modified version of the program GRIDGEN, a grid-like * network problem generator developed by Yusin Lee and Jim Orlin. * The original code is publically available on the DIMACS ftp site at: * . * * All changes concern only the program interface, so this modified * version produces exactly the same instances as the original version. * * Changes were made by Andrew Makhorin . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_gridgen - grid-like network problem generator * * SYNOPSIS * * int glp_gridgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, * const int parm[1+14]); * * DESCRIPTION * * The routine glp_gridgen is a grid-like network problem generator * developed by Yusin Lee and Jim Orlin. * * The parameter G specifies the graph object, to which the generated * problem data have to be stored. Note that on entry the graph object * is erased with the routine glp_erase_graph. * * The parameter v_rhs specifies an offset of the field of type double * in the vertex data block, to which the routine stores the supply or * demand value. If v_rhs < 0, the value is not stored. * * The parameter a_cap specifies an offset of the field of type double * in the arc data block, to which the routine stores the arc capacity. * If a_cap < 0, the capacity is not stored. * * The parameter a_cost specifies an offset of the field of type double * in the arc data block, to which the routine stores the per-unit cost * if the arc flow. If a_cost < 0, the cost is not stored. * * The array parm contains description of the network to be generated: * * parm[0] not used * parm[1] two-ways arcs indicator: * 1 - if links in both direction should be generated * 0 - otherwise * parm[2] random number seed (a positive integer) * parm[3] number of nodes (the number of nodes generated might be * slightly different to make the network a grid) * parm[4] grid width * parm[5] number of sources * parm[6] number of sinks * parm[7] average degree * parm[8] total flow * parm[9] distribution of arc costs: * 1 - uniform * 2 - exponential * parm[10] lower bound for arc cost (uniform) * 100 * lambda (exponential) * parm[11] upper bound for arc cost (uniform) * not used (exponential) * parm[12] distribution of arc capacities: * 1 - uniform * 2 - exponential * parm[13] lower bound for arc capacity (uniform) * 100 * lambda (exponential) * parm[14] upper bound for arc capacity (uniform) * not used (exponential) * * RETURNS * * If the instance was successfully generated, the routine glp_gridgen * returns zero; otherwise, if specified parameters are inconsistent, * the routine returns a non-zero error code. * * COMMENTS * * This network generator generates a grid-like network plus a super * node. In additional to the arcs connecting the nodes in the grid, * there is an arc from each supply node to the super node and from the * super node to each demand node to guarantee feasiblity. These arcs * have very high costs and very big capacities. * * The idea of this network generator is as follows: First, a grid of * n1 * n2 is generated. For example, 5 * 3. The nodes are numbered as * 1 to 15, and the supernode is numbered as n1*n2+1. Then arcs between * adjacent nodes are generated. For these arcs, the user is allowed to * specify either to generate two-way arcs or one-way arcs. If two-way * arcs are to be generated, two arcs, one in each direction, will be * generated between each adjacent node pairs. Otherwise, only one arc * will be generated. If this is the case, the arcs will be generated * in alterntive directions as shown below. * * 1 ---> 2 ---> 3 ---> 4 ---> 5 * | ^ | ^ | * | | | | | * V | V | V * 6 <--- 7 <--- 8 <--- 9 <--- 10 * | ^ | ^ | * | | | | | * V | V | V * 11 --->12 --->13 --->14 ---> 15 * * Then the arcs between the super node and the source/sink nodes are * added as mentioned before. If the number of arcs still doesn't reach * the requirement, additional arcs will be added by uniformly picking * random node pairs. There is no checking to prevent multiple arcs * between any pair of nodes. However, there will be no self-arcs (arcs * that poins back to its tail node) in the network. * * The source and sink nodes are selected uniformly in the network, and * the imbalances of each source/sink node are also assigned by uniform * distribution. */ struct stat_para { /* structure for statistical distributions */ int distribution; /* the distribution: */ #define UNIFORM 1 /* uniform distribution */ #define EXPONENTIAL 2 /* exponential distribution */ double parameter[5]; /* the parameters of the distribution */ }; struct arcs { int from; /* the FROM node of that arc */ int to; /* the TO node of that arc */ int cost; /* original cost of that arc */ int u; /* capacity of the arc */ }; struct imbalance { int node; /* Node ID */ int supply; /* Supply of that node */ }; struct csa { /* common storage area */ glp_graph *G; int v_rhs, a_cap, a_cost; int seed; /* random number seed */ int seed_original; /* the original seed from input */ int two_way; /* 0: generate arcs in both direction for the basic grid, except for the arcs to/from the super node. 1: o/w */ int n_node; /* total number of nodes in the network, numbered 1 to n_node, including the super node, which is the last one */ int n_arc; /* total number of arcs in the network, counting EVERY arc. */ int n_grid_arc; /* number of arcs in the basic grid, including the arcs to/from the super node */ int n_source, n_sink; /* number of source and sink nodes */ int avg_degree; /* average degree, arcs to and from the super node are counted */ int t_supply; /* total supply in the network */ int n1, n2; /* the two edges of the network grid. n1 >= n2 */ struct imbalance *source_list, *sink_list; /* head of the array of source/sink nodes */ struct stat_para arc_costs; /* the distribution of arc costs */ struct stat_para capacities; /* distribution of the capacities of the arcs */ struct arcs *arc_list; /* head of the arc list array. Arcs in this array are in the order of grid_arcs, arcs to/from super node, and other arcs */ }; #define G (csa->G) #define v_rhs (csa->v_rhs) #define a_cap (csa->a_cap) #define a_cost (csa->a_cost) #define seed (csa->seed) #define seed_original (csa->seed_original) #define two_way (csa->two_way) #define n_node (csa->n_node) #define n_arc (csa->n_arc) #define n_grid_arc (csa->n_grid_arc) #define n_source (csa->n_source) #define n_sink (csa->n_sink) #define avg_degree (csa->avg_degree) #define t_supply (csa->t_supply) #define n1 (csa->n1) #define n2 (csa->n2) #define source_list (csa->source_list) #define sink_list (csa->sink_list) #define arc_costs (csa->arc_costs) #define capacities (csa->capacities) #define arc_list (csa->arc_list) static void assign_capacities(struct csa *csa); static void assign_costs(struct csa *csa); static void assign_imbalance(struct csa *csa); static int exponential(struct csa *csa, double lambda[1]); static struct arcs *gen_additional_arcs(struct csa *csa, struct arcs *arc_ptr); static struct arcs *gen_basic_grid(struct csa *csa, struct arcs *arc_ptr); static void gen_more_arcs(struct csa *csa, struct arcs *arc_ptr); static void generate(struct csa *csa); static void output(struct csa *csa); static double randy(struct csa *csa); static void select_source_sinks(struct csa *csa); static int uniform(struct csa *csa, double a[2]); int glp_gridgen(glp_graph *G_, int _v_rhs, int _a_cap, int _a_cost, const int parm[1+14]) { struct csa _csa, *csa = &_csa; int n, ret; G = G_; v_rhs = _v_rhs; a_cap = _a_cap; a_cost = _a_cost; if (G != NULL) { if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) xerror("glp_gridgen: v_rhs = %d; invalid offset\n", v_rhs); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_gridgen: a_cap = %d; invalid offset\n", a_cap); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_gridgen: a_cost = %d; invalid offset\n", a_cost) ; } /* Check the parameters for consistency. */ if (!(parm[1] == 0 || parm[1] == 1)) { ret = 1; goto done; } if (parm[2] < 1) { ret = 2; goto done; } if (!(10 <= parm[3] && parm[3] <= 40000)) { ret = 3; goto done; } if (!(1 <= parm[4] && parm[4] <= 40000)) { ret = 4; goto done; } if (!(parm[5] >= 0 && parm[6] >= 0 && parm[5] + parm[6] <= parm[3])) { ret = 5; goto done; } if (!(1 <= parm[7] && parm[7] <= parm[3])) { ret = 6; goto done; } if (parm[8] < 0) { ret = 7; goto done; } if (!(parm[9] == 1 || parm[9] == 2)) { ret = 8; goto done; } if (parm[9] == 1 && parm[10] > parm[11] || parm[9] == 2 && parm[10] < 1) { ret = 9; goto done; } if (!(parm[12] == 1 || parm[12] == 2)) { ret = 10; goto done; } if (parm[12] == 1 && !(0 <= parm[13] && parm[13] <= parm[14]) || parm[12] == 2 && parm[13] < 1) { ret = 11; goto done; } /* Initialize the graph object. */ if (G != NULL) { glp_erase_graph(G, G->v_size, G->a_size); glp_set_graph_name(G, "GRIDGEN"); } /* Copy the generator parameters. */ two_way = parm[1]; seed_original = seed = parm[2]; n_node = parm[3]; n = parm[4]; n_source = parm[5]; n_sink = parm[6]; avg_degree = parm[7]; t_supply = parm[8]; arc_costs.distribution = parm[9]; if (parm[9] == 1) { arc_costs.parameter[0] = parm[10]; arc_costs.parameter[1] = parm[11]; } else { arc_costs.parameter[0] = (double)parm[10] / 100.0; arc_costs.parameter[1] = 0.0; } capacities.distribution = parm[12]; if (parm[12] == 1) { capacities.parameter[0] = parm[13]; capacities.parameter[1] = parm[14]; } else { capacities.parameter[0] = (double)parm[13] / 100.0; capacities.parameter[1] = 0.0; } /* Calculate the edge lengths of the grid according to the input. */ if (n * n >= n_node) { n1 = n; n2 = (int)((double)n_node / (double)n + 0.5); } else { n2 = n; n1 = (int)((double)n_node / (double)n + 0.5); } /* Recalculate the total number of nodes and plus 1 for the super node. */ n_node = n1 * n2 + 1; n_arc = n_node * avg_degree; n_grid_arc = (two_way + 1) * ((n1 - 1) * n2 + (n2 - 1) * n1) + n_source + n_sink; if (n_grid_arc > n_arc) n_arc = n_grid_arc; arc_list = xcalloc(n_arc, sizeof(struct arcs)); source_list = xcalloc(n_source, sizeof(struct imbalance)); sink_list = xcalloc(n_sink, sizeof(struct imbalance)); /* Generate a random network. */ generate(csa); /* Output the network. */ output(csa); /* Free all allocated memory. */ xfree(arc_list); xfree(source_list); xfree(sink_list); /* The instance has been successfully generated. */ ret = 0; done: return ret; } #undef random static void assign_capacities(struct csa *csa) { /* Assign a capacity to each arc. */ struct arcs *arc_ptr = arc_list; int (*random)(struct csa *csa, double *); int i; /* Determine the random number generator to use. */ switch (arc_costs.distribution) { case UNIFORM: random = uniform; break; case EXPONENTIAL: random = exponential; break; default: xassert(csa != csa); } /* Assign capacities to grid arcs. */ for (i = n_source + n_sink; i < n_grid_arc; i++, arc_ptr++) arc_ptr->u = random(csa, capacities.parameter); i = i - n_source - n_sink; /* Assign capacities to arcs to/from supernode. */ for (; i < n_grid_arc; i++, arc_ptr++) arc_ptr->u = t_supply; /* Assign capacities to all other arcs. */ for (; i < n_arc; i++, arc_ptr++) arc_ptr->u = random(csa, capacities.parameter); return; } static void assign_costs(struct csa *csa) { /* Assign a cost to each arc. */ struct arcs *arc_ptr = arc_list; int (*random)(struct csa *csa, double *); int i; /* A high cost assigned to arcs to/from the supernode. */ int high_cost; /* The maximum cost assigned to arcs in the base grid. */ int max_cost = 0; /* Determine the random number generator to use. */ switch (arc_costs.distribution) { case UNIFORM: random = uniform; break; case EXPONENTIAL: random = exponential; break; default: xassert(csa != csa); } /* Assign costs to arcs in the base grid. */ for (i = n_source + n_sink; i < n_grid_arc; i++, arc_ptr++) { arc_ptr->cost = random(csa, arc_costs.parameter); if (max_cost < arc_ptr->cost) max_cost = arc_ptr->cost; } i = i - n_source - n_sink; /* Assign costs to arcs to/from the super node. */ high_cost = max_cost * 2; for (; i < n_grid_arc; i++, arc_ptr++) arc_ptr->cost = high_cost; /* Assign costs to all other arcs. */ for (; i < n_arc; i++, arc_ptr++) arc_ptr->cost = random(csa, arc_costs.parameter); return; } static void assign_imbalance(struct csa *csa) { /* Assign an imbalance to each node. */ int total, i; double avg; struct imbalance *ptr; /* assign the supply nodes */ avg = 2.0 * t_supply / n_source; do { for (i = 1, total = t_supply, ptr = source_list + 1; i < n_source; i++, ptr++) { ptr->supply = (int)(randy(csa) * avg + 0.5); total -= ptr->supply; } source_list->supply = total; } /* redo all if the assignment "overshooted" */ while (total <= 0); /* assign the demand nodes */ avg = -2.0 * t_supply / n_sink; do { for (i = 1, total = t_supply, ptr = sink_list + 1; i < n_sink; i++, ptr++) { ptr->supply = (int)(randy(csa) * avg - 0.5); total += ptr->supply; } sink_list->supply = - total; } while (total <= 0); return; } static int exponential(struct csa *csa, double lambda[1]) { /* Returns an "exponentially distributed" integer with parameter lambda. */ return ((int)(- lambda[0] * log((double)randy(csa)) + 0.5)); } static struct arcs *gen_additional_arcs(struct csa *csa, struct arcs *arc_ptr) { /* Generate an arc from each source to the supernode and from supernode to each sink. */ int i; for (i = 0; i < n_source; i++, arc_ptr++) { arc_ptr->from = source_list[i].node; arc_ptr->to = n_node; } for (i = 0; i < n_sink; i++, arc_ptr++) { arc_ptr->to = sink_list[i].node; arc_ptr->from = n_node; } return arc_ptr; } static struct arcs *gen_basic_grid(struct csa *csa, struct arcs *arc_ptr) { /* Generate the basic grid. */ int direction = 1, i, j, k; if (two_way) { /* Generate an arc in each direction. */ for (i = 1; i < n_node; i += n1) { for (j = i, k = j + n1 - 1; j < k; j++) { arc_ptr->from = j; arc_ptr->to = j + 1; arc_ptr++; arc_ptr->from = j + 1; arc_ptr->to = j; arc_ptr++; } } for (i = 1; i <= n1; i++) { for (j = i + n1; j < n_node; j += n1) { arc_ptr->from = j; arc_ptr->to = j - n1; arc_ptr++; arc_ptr->from = j - n1; arc_ptr->to = j; arc_ptr++; } } } else { /* Generate one arc in each direction. */ for (i = 1; i < n_node; i += n1) { if (direction == 1) j = i; else j = i + 1; for (k = j + n1 - 1; j < k; j++) { arc_ptr->from = j; arc_ptr->to = j + direction; arc_ptr++; } direction = - direction; } for (i = 1; i <= n1; i++) { j = i + n1; if (direction == 1) { for (; j < n_node; j += n1) { arc_ptr->from = j - n1; arc_ptr->to = j; arc_ptr++; } } else { for (; j < n_node; j += n1) { arc_ptr->from = j - n1; arc_ptr->to = j; arc_ptr++; } } direction = - direction; } } return arc_ptr; } static void gen_more_arcs(struct csa *csa, struct arcs *arc_ptr) { /* Generate random arcs to meet the specified density. */ int i; double ab[2]; ab[0] = 0.9; ab[1] = n_node - 0.99; /* upper limit is n_node-1 because the supernode cannot be selected */ for (i = n_grid_arc; i < n_arc; i++, arc_ptr++) { arc_ptr->from = uniform(csa, ab); arc_ptr->to = uniform(csa, ab); if (arc_ptr->from == arc_ptr->to) { arc_ptr--; i--; } } return; } static void generate(struct csa *csa) { /* Generate a random network. */ struct arcs *arc_ptr = arc_list; arc_ptr = gen_basic_grid(csa, arc_ptr); select_source_sinks(csa); arc_ptr = gen_additional_arcs(csa, arc_ptr); gen_more_arcs(csa, arc_ptr); assign_costs(csa); assign_capacities(csa); assign_imbalance(csa); return; } static void output(struct csa *csa) { /* Output the network in DIMACS format. */ struct arcs *arc_ptr; struct imbalance *imb_ptr; int i; if (G != NULL) goto skip; /* Output "c", "p" records. */ xprintf("c generated by GRIDGEN\n"); xprintf("c seed %d\n", seed_original); xprintf("c nodes %d\n", n_node); xprintf("c grid size %d X %d\n", n1, n2); xprintf("c sources %d sinks %d\n", n_source, n_sink); xprintf("c avg. degree %d\n", avg_degree); xprintf("c supply %d\n", t_supply); switch (arc_costs.distribution) { case UNIFORM: xprintf("c arc costs: UNIFORM distr. min %d max %d\n", (int)arc_costs.parameter[0], (int)arc_costs.parameter[1]); break; case EXPONENTIAL: xprintf("c arc costs: EXPONENTIAL distr. lambda %d\n", (int)arc_costs.parameter[0]); break; default: xassert(csa != csa); } switch (capacities.distribution) { case UNIFORM: xprintf("c arc caps : UNIFORM distr. min %d max %d\n", (int)capacities.parameter[0], (int)capacities.parameter[1]); break; case EXPONENTIAL: xprintf("c arc caps : EXPONENTIAL distr. %d lambda %d\n", (int)capacities.parameter[0]); break; default: xassert(csa != csa); } skip: if (G == NULL) xprintf("p min %d %d\n", n_node, n_arc); else { glp_add_vertices(G, n_node); if (v_rhs >= 0) { double zero = 0.0; for (i = 1; i <= n_node; i++) { glp_vertex *v = G->v[i]; memcpy((char *)v->data + v_rhs, &zero, sizeof(double)); } } } /* Output "n node supply". */ for (i = 0, imb_ptr = source_list; i < n_source; i++, imb_ptr++) { if (G == NULL) xprintf("n %d %d\n", imb_ptr->node, imb_ptr->supply); else { if (v_rhs >= 0) { double temp = (double)imb_ptr->supply; glp_vertex *v = G->v[imb_ptr->node]; memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); } } } for (i = 0, imb_ptr = sink_list; i < n_sink; i++, imb_ptr++) { if (G == NULL) xprintf("n %d %d\n", imb_ptr->node, imb_ptr->supply); else { if (v_rhs >= 0) { double temp = (double)imb_ptr->supply; glp_vertex *v = G->v[imb_ptr->node]; memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); } } } /* Output "a from to lowcap=0 hicap cost". */ for (i = 0, arc_ptr = arc_list; i < n_arc; i++, arc_ptr++) { if (G == NULL) xprintf("a %d %d 0 %d %d\n", arc_ptr->from, arc_ptr->to, arc_ptr->u, arc_ptr->cost); else { glp_arc *a = glp_add_arc(G, arc_ptr->from, arc_ptr->to); if (a_cap >= 0) { double temp = (double)arc_ptr->u; memcpy((char *)a->data + a_cap, &temp, sizeof(double)); } if (a_cost >= 0) { double temp = (double)arc_ptr->cost; memcpy((char *)a->data + a_cost, &temp, sizeof(double)); } } } return; } static double randy(struct csa *csa) { /* Returns a random number between 0.0 and 1.0. See Ward Cheney & David Kincaid, "Numerical Mathematics and Computing," 2Ed, pp. 335. */ seed = 16807 * seed % 2147483647; if (seed < 0) seed = - seed; return seed * 4.6566128752459e-10; } static void select_source_sinks(struct csa *csa) { /* Randomly select the source nodes and sink nodes. */ int i, *int_ptr; int *temp_list; /* a temporary list of nodes */ struct imbalance *ptr; double ab[2]; /* parameter for random number generator */ ab[0] = 0.9; ab[1] = n_node - 0.99; /* upper limit is n_node-1 because the supernode cannot be selected */ temp_list = xcalloc(n_node, sizeof(int)); for (i = 0, int_ptr = temp_list; i < n_node; i++, int_ptr++) *int_ptr = 0; /* Select the source nodes. */ for (i = 0, ptr = source_list; i < n_source; i++, ptr++) { ptr->node = uniform(csa, ab); if (temp_list[ptr->node] == 1) /* check for duplicates */ { ptr--; i--; } else temp_list[ptr->node] = 1; } /* Select the sink nodes. */ for (i = 0, ptr = sink_list; i < n_sink; i++, ptr++) { ptr->node = uniform(csa, ab); if (temp_list[ptr->node] == 1) { ptr--; i--; } else temp_list[ptr->node] = 1; } xfree(temp_list); return; } int uniform(struct csa *csa, double a[2]) { /* Generates an integer uniformly selected from [a[0],a[1]]. */ return (int)((a[1] - a[0]) * randy(csa) + a[0] + 0.5); } /**********************************************************************/ #if 0 int main(void) { int parm[1+14]; double temp; scanf("%d", &parm[1]); scanf("%d", &parm[2]); scanf("%d", &parm[3]); scanf("%d", &parm[4]); scanf("%d", &parm[5]); scanf("%d", &parm[6]); scanf("%d", &parm[7]); scanf("%d", &parm[8]); scanf("%d", &parm[9]); if (parm[9] == 1) { scanf("%d", &parm[10]); scanf("%d", &parm[11]); } else { scanf("%le", &temp); parm[10] = (int)(100.0 * temp + .5); parm[11] = 0; } scanf("%d", &parm[12]); if (parm[12] == 1) { scanf("%d", &parm[13]); scanf("%d", &parm[14]); } else { scanf("%le", &temp); parm[13] = (int)(100.0 * temp + .5); parm[14] = 0; } glp_gridgen(NULL, 0, 0, 0, parm); return 0; } #endif /* eof */ igraph/src/igraph_attributes.h0000644000176000001440000010127112325527073016244 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef REST_ATTRIBUTES_H #define REST_ATTRIBUTES_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_strvector.h" #include "igraph_vector_ptr.h" #include "igraph_iterators.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Attributes */ /* -------------------------------------------------- */ /** * \section about_attributes * * Attributes are numbers or strings (or basically any kind * of data) associated with the vertices or edges of a graph, or * with the graph itself. Eg. you may label vertices with symbolic names * or attach numeric weights to the edges of a graph. * * igraph attributes are designed to be flexible and extensible. * In igraph attributes are implemented via an interface abstraction: * any type implementing the functions in the interface, can be used * for storing vertex, edge and graph attributes. This means that * different attribute implementations can be used together with * igraph. This is reasonable: if igraph is used from Python attributes can be * of any Python type, from GNU R all R types are allowed. There is an * experimental attribute implementation to be used when programming * in C, but by default it is currently turned off. * * First we briefly look over how attribute handlers can be * implemented. This is not something a user does every day. It is * rather typically the job of the high level interface writers. (But * it is possible to write an interface without implementing * attributes.) Then we show the experimental C attribute handler. */ /** * \section about_attribute_table * It is possible to attach an attribute handling * interface to \a igraph. This is simply a table of functions, of * type \ref igraph_attribute_table_t. These functions are invoked to * notify the attribute handling code about the structural changes in * a graph. See the documentation of this type for details. * * By default there is no attribute interface attached to \a igraph, * to attach one, call \ref igraph_i_set_attribute_table with your new * table. * */ /** * \typedef igraph_attribute_type_t * The possible types of the attributes. * * Note that this is only the * type communicated by the attribute interface towards igraph * functions. Eg. in the GNU R attribute handler, it is safe to say * that all complex R object attributes are strings, as long as this * interface is able to serialize them into strings. See also \ref * igraph_attribute_table_t. * \enumval IGRAPH_ATTRIBUTE_DEFAULT Currently not used for anything. * \enumval IGRAPH_ATTRIBUTE_NUMERIC Numeric attribute. * \enumval IGRAPH_ATTRIBUTE_BOOLEAN Logical values, true or false. * \enumval IGRAPH_ATTRIBUTE_STRING Attribute that can be converted to * a string. * \enumval IGRAPH_ATTRIBUTE_R_OBJECT An R object. This is usually * ignored by the igraph functions. * \enumval IGRAPH_ATTRIBUTE_PY_OBJECT A Python object. Usually * ignored by the igraph functions. * */ typedef enum { IGRAPH_ATTRIBUTE_DEFAULT=0, IGRAPH_ATTRIBUTE_NUMERIC=1, IGRAPH_ATTRIBUTE_BOOLEAN=5, IGRAPH_ATTRIBUTE_STRING=2, IGRAPH_ATTRIBUTE_R_OBJECT=3, IGRAPH_ATTRIBUTE_PY_OBJECT=4 } igraph_attribute_type_t; typedef struct igraph_attribute_record_t { const char *name; igraph_attribute_type_t type; const void *value; } igraph_attribute_record_t; typedef enum { IGRAPH_ATTRIBUTE_GRAPH=0, IGRAPH_ATTRIBUTE_VERTEX, IGRAPH_ATTRIBUTE_EDGE } igraph_attribute_elemtype_t; typedef enum { IGRAPH_ATTRIBUTE_COMBINE_IGNORE=0, IGRAPH_ATTRIBUTE_COMBINE_DEFAULT=1, IGRAPH_ATTRIBUTE_COMBINE_FUNCTION=2, IGRAPH_ATTRIBUTE_COMBINE_SUM=3, IGRAPH_ATTRIBUTE_COMBINE_PROD=4, IGRAPH_ATTRIBUTE_COMBINE_MIN=5, IGRAPH_ATTRIBUTE_COMBINE_MAX=6, IGRAPH_ATTRIBUTE_COMBINE_RANDOM=7, IGRAPH_ATTRIBUTE_COMBINE_FIRST=8, IGRAPH_ATTRIBUTE_COMBINE_LAST=9, IGRAPH_ATTRIBUTE_COMBINE_MEAN=10, IGRAPH_ATTRIBUTE_COMBINE_MEDIAN=11, IGRAPH_ATTRIBUTE_COMBINE_CONCAT=12 } igraph_attribute_combination_type_t; typedef struct igraph_attribute_combination_record_t { const char *name; /* can be NULL, meaning: the rest */ igraph_attribute_combination_type_t type; void *func; } igraph_attribute_combination_record_t; typedef struct igraph_attribute_combination_t { igraph_vector_ptr_t list; } igraph_attribute_combination_t; #define IGRAPH_NO_MORE_ATTRIBUTES ((const char*)0) int igraph_attribute_combination_init(igraph_attribute_combination_t *comb); int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb); int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t type, void *func); int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, const char *name); int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t *type, void **func); /** * \struct igraph_attribute_table_t * \brief Table of functions to perform operations on attributes * * This type collects the functions defining an attribute handler. * It has the following members: * \member init This function is called whenever a new graph object is * created, right after it is created but before any vertices or * edges are added. It is supposed to set the \c attr member of the \c * igraph_t object. It is expected to return an error code. * \member destroy This function is called whenever the graph object * is destroyed, right before freeing the allocated memory. * \member copy This function is called when copying a graph with \ref * igraph_copy, after the structure of the graph has been already * copied. It is expected to return an error code. * \member add_vertices Called when vertices are added to a * graph, before adding the vertices themselves. * The number of vertices to add is supplied as an * argument. Expected to return an error code. * \member permute_vertices Typically called when a new graph is * created based on an existing one, e.g. if vertices are removed * from a graph. The supplied index vector defines which old vertex * a new vertex corresponds to. Its length must be the same as the * number of vertices in the new graph. * \member combine_vertices This function is called when the creation * of a new graph involves a merge (contraction, etc.) of vertices * from another graph. The function is after the new graph was created. * An argument specifies how several vertices from the old graph map to a * single vertex in the new graph. * \member add_edges Called when new edges have been added. The number * of new edges are supplied as well. It is expected to return an * error code. * \member permute_edges Typically called when a new graph is created and * some of the new edges should carry the attributes of some of the * old edges. The idx vector shows the mapping between the old edges and * the new ones. Its length is the same as the number of edges in the new * graph, and for each edge it gives the id of the old edge (the edge in * the old graph). * \member combine_edges This function is called when the creation * of a new graph involves a merge (contraction, etc.) of edges * from another graph. The function is after the new graph was created. * An argument specifies how several edges from the old graph map to a * single edge in the new graph. * \member get_info Query the attributes of a graph, the names and * types should be returned. * \member has_attr Check whether a graph has the named * graph/vertex/edge attribute. * \member gettype Query the type of a graph/vertex/edge attribute. * \member get_numeric_graph_attr Query a numeric graph attribute. The * value should be placed as the first element of the \p value * vector. * \member get_string_graph_attr Query a string graph attribute. The * value should be placed as the first element of the \p value * string vector. * \member get_bool_graph_attr Query a boolean graph attribute. The * value should be placed as the first element of the \p value * boolean vector. * \member get_numeric_vertex_attr Query a numeric vertex attribute, * for the vertices included in \p vs. * \member get_string_vertex_attr Query a string vertex attribute, * for the vertices included in \p vs. * \member get_bool_vertex_attr Query a boolean vertex attribute, * for the vertices included in \p vs. * \member get_numeric_edge_attr Query a numeric edge attribute, for * the edges included in \p es. * \member get_string_edge_attr Query a string edge attribute, for the * edges included in \p es. * \member get_bool_edge_attr Query a boolean edge attribute, for the * edges included in \p es. * * Note that the get_*_*_attr are allowed to * convert the attributes to numeric or string. E.g. if a vertex attribute * is a GNU R complex data type, then * get_string_vertex_attribute may serialize it * into a string, but this probably makes sense only if * add_vertices is able to deserialize it. */ typedef struct igraph_attribute_table_t { int (*init)(igraph_t *graph, igraph_vector_ptr_t *attr); void (*destroy)(igraph_t *graph); int (*copy)(igraph_t *to, const igraph_t *from, igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea); int (*add_vertices)(igraph_t *graph, long int nv, igraph_vector_ptr_t *attr); int (*permute_vertices)(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx); int (*combine_vertices)(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb); int (*add_edges)(igraph_t *graph, const igraph_vector_t *edges, igraph_vector_ptr_t *attr); int (*permute_edges)(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx); int (*combine_edges)(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb); int (*get_info)(const igraph_t *graph, igraph_strvector_t *gnames, igraph_vector_t *gtypes, igraph_strvector_t *vnames, igraph_vector_t *vtypes, igraph_strvector_t *enames, igraph_vector_t *etypes); igraph_bool_t (*has_attr)(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); int (*gettype)(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name); int (*get_numeric_graph_attr)(const igraph_t *graph, const char *name, igraph_vector_t *value); int (*get_string_graph_attr)(const igraph_t *graph, const char *name, igraph_strvector_t *value); int (*get_bool_graph_attr)(const igraph_t *igraph, const char *name, igraph_vector_bool_t *value); int (*get_numeric_vertex_attr)(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_t *value); int (*get_string_vertex_attr)(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_strvector_t *value); int (*get_bool_vertex_attr)(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_bool_t *value); int (*get_numeric_edge_attr)(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value); int (*get_string_edge_attr)(const igraph_t *graph, const char *name, igraph_es_t es, igraph_strvector_t *value); int (*get_bool_edge_attr)(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value); } igraph_attribute_table_t; igraph_attribute_table_t * igraph_i_set_attribute_table(const igraph_attribute_table_t * table); igraph_bool_t igraph_has_attribute_table(void); #define IGRAPH_I_ATTRIBUTE_DESTROY(graph) \ do {if ((graph)->attr) igraph_i_attribute_destroy(graph);} while(0) #define IGRAPH_I_ATTRIBUTE_COPY(to,from,ga,va,ea) do { \ int igraph_i_ret2=0; \ if ((from)->attr) { \ IGRAPH_CHECK(igraph_i_ret2=igraph_i_attribute_copy((to),(from),(ga),(va),(ea))); \ } else { \ (to)->attr = 0; \ } \ if (igraph_i_ret2 != 0) { \ IGRAPH_ERROR("", igraph_i_ret2); \ } \ } while(0) int igraph_i_attribute_init(igraph_t *graph, void *attr); void igraph_i_attribute_destroy(igraph_t *graph); int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea); int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr); int igraph_i_attribute_permute_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx); int igraph_i_attribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb); int igraph_i_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, void *attr); int igraph_i_attribute_permute_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx); int igraph_i_attribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb); int igraph_i_attribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, igraph_vector_t *gtypes, igraph_strvector_t *vnames, igraph_vector_t *vtypes, igraph_strvector_t *enames, igraph_vector_t *etypes); igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); int igraph_i_attribute_gettype(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name); int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, const char *name, igraph_vector_t *value); int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_t *value); int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value); int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, const char *name, igraph_strvector_t *value); int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_strvector_t *value); int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_strvector_t *value); int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, const char *name, igraph_vector_bool_t *value); int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_bool_t *value); int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value); /* Experimental attribute handler in C */ extern const igraph_attribute_table_t igraph_cattribute_table; igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name); igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name); const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name); igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name, igraph_integer_t vid); igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name, igraph_integer_t vid); const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name, igraph_integer_t vid); igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name, igraph_integer_t eid); igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name, igraph_integer_t eid); const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, igraph_integer_t eid); int igraph_cattribute_VANV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_t *result); int igraph_cattribute_EANV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_t *result); int igraph_cattribute_VASV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_strvector_t *result); int igraph_cattribute_EASV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_strvector_t *result); int igraph_cattribute_VABV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_bool_t *result); int igraph_cattribute_EABV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_bool_t *result); int igraph_cattribute_list(const igraph_t *graph, igraph_strvector_t *gnames, igraph_vector_t *gtypes, igraph_strvector_t *vnames, igraph_vector_t *vtypes, igraph_strvector_t *enames, igraph_vector_t *etypes); igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, igraph_real_t value); int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, igraph_bool_t value); int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, const char *value); int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_real_t value); int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_bool_t value); int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, igraph_integer_t vid, const char *value); int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_real_t value); int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_bool_t value); int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, igraph_integer_t eid, const char *value); int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v); int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v); int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv); int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v); int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v); int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv); void igraph_cattribute_remove_g(igraph_t *graph, const char *name); void igraph_cattribute_remove_v(igraph_t *graph, const char *name); void igraph_cattribute_remove_e(igraph_t *graph, const char *name); void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, igraph_bool_t v, igraph_bool_t e); /** * \define GAN * Query a numeric graph attribute. * * This is shorthand for \ref igraph_cattribute_GAN(). * \param graph The graph. * \param n The name of the attribute. * \return The value of the attribute. */ #define GAN(graph,n) (igraph_cattribute_GAN((graph), (n))) /** * \define GAB * Query a boolean graph attribute. * * This is shorthand for \ref igraph_cattribute_GAB(). * \param graph The graph. * \param n The name of the attribute. * \return The value of the attribute. */ #define GAB(graph,n) (igraph_cattribute_GAB((graph), (n))) /** * \define GAS * Query a string graph attribute. * * This is shorthand for \ref igraph_cattribute_GAS(). * \param graph The graph. * \param n The name of the attribute. * \return The value of the attribute. */ #define GAS(graph,n) (igraph_cattribute_GAS((graph), (n))) /** * \define VAN * Query a numeric vertex attribute. * * This is shorthand for \ref igraph_cattribute_VAN(). * \param graph The graph. * \param n The name of the attribute. * \param v The id of the vertex. * \return The value of the attribute. */ #define VAN(graph,n,v) (igraph_cattribute_VAN((graph), (n), (v))) /** * \define VAB * Query a boolean vertex attribute. * * This is shorthand for \ref igraph_cattribute_VAB(). * \param graph The graph. * \param n The name of the attribute. * \param v The id of the vertex. * \return The value of the attribute. */ #define VAB(graph,n,v) (igraph_cattribute_VAB((graph), (n), (v))) /** * \define VAS * Query a string vertex attribute. * * This is shorthand for \ref igraph_cattribute_VAS(). * \param graph The graph. * \param n The name of the attribute. * \param v The id of the vertex. * \return The value of the attribute. */ #define VAS(graph,n,v) (igraph_cattribute_VAS((graph), (n), (v))) /** * \define VANV * Query a numeric vertex attribute for all vertices. * * This is a shorthand for \ref igraph_cattribute_VANV(). * \param graph The graph. * \param n The name of the attribute. * \param vec Pointer to an initialized vector, the result is * stored here. It will be resized, if needed. * \return Error code. */ #define VANV(graph,n,vec) (igraph_cattribute_VANV((graph),(n), \ igraph_vss_all(), (vec))) /** * \define VABV * Query a boolean vertex attribute for all vertices. * * This is a shorthand for \ref igraph_cattribute_VABV(). * \param graph The graph. * \param n The name of the attribute. * \param vec Pointer to an initialized boolean vector, the result is * stored here. It will be resized, if needed. * \return Error code. */ #define VABV(graph,n,vec) (igraph_cattribute_VABV((graph),(n), \ igraph_vss_all(), (vec))) /** * \define VASV * Query a string vertex attribute for all vertices. * * This is a shorthand for \ref igraph_cattribute_VASV(). * \param graph The graph. * \param n The name of the attribute. * \param vec Pointer to an initialized string vector, the result is * stored here. It will be resized, if needed. * \return Error code. */ #define VASV(graph,n,vec) (igraph_cattribute_VASV((graph),(n), \ igraph_vss_all(), (vec))) /** * \define EAN * Query a numeric edge attribute. * * This is shorthand for \ref igraph_cattribute_EAN(). * \param graph The graph. * \param n The name of the attribute. * \param e The id of the edge. * \return The value of the attribute. */ #define EAN(graph,n,e) (igraph_cattribute_EAN((graph), (n), (e))) /** * \define EAB * Query a boolean edge attribute. * * This is shorthand for \ref igraph_cattribute_EAB(). * \param graph The graph. * \param n The name of the attribute. * \param e The id of the edge. * \return The value of the attribute. */ #define EAB(graph,n,e) (igraph_cattribute_EAB((graph), (n), (e))) /** * \define EAS * Query a string edge attribute. * * This is shorthand for \ref igraph_cattribute_EAS(). * \param graph The graph. * \param n The name of the attribute. * \param e The id of the edge. * \return The value of the attribute. */ #define EAS(graph,n,e) (igraph_cattribute_EAS((graph), (n), (e))) /** * \define EANV * Query a numeric edge attribute for all edges. * * This is a shorthand for \ref igraph_cattribute_EANV(). * \param graph The graph. * \param n The name of the attribute. * \param vec Pointer to an initialized vector, the result is * stored here. It will be resized, if needed. * \return Error code. */ #define EANV(graph,n,vec) (igraph_cattribute_EANV((graph),(n), \ igraph_ess_all(IGRAPH_EDGEORDER_ID), (vec))) /** * \define EABV * Query a boolean edge attribute for all edges. * * This is a shorthand for \ref igraph_cattribute_EABV(). * \param graph The graph. * \param n The name of the attribute. * \param vec Pointer to an initialized vector, the result is * stored here. It will be resized, if needed. * \return Error code. */ #define EABV(graph,n,vec) (igraph_cattribute_EABV((graph),(n), \ igraph_ess_all(IGRAPH_EDGEORDER_ID), (vec))) /** * \define EASV * Query a string edge attribute for all edges. * * This is a shorthand for \ref igraph_cattribute_EASV(). * \param graph The graph. * \param n The name of the attribute. * \param vec Pointer to an initialized string vector, the result is * stored here. It will be resized, if needed. * \return Error code. */ #define EASV(graph,n,vec) (igraph_cattribute_EASV((graph),(n), \ igraph_ess_all(IGRAPH_EDGEORDER_ID), (vec))) /** * \define SETGAN * Set a numeric graph attribute * * This is a shorthand for \ref igraph_cattribute_GAN_set(). * \param graph The graph. * \param n The name of the attribute. * \param value The new value of the attribute. * \return Error code. */ #define SETGAN(graph,n,value) (igraph_cattribute_GAN_set((graph),(n),(value))) /** * \define SETGAB * Set a boolean graph attribute * * This is a shorthand for \ref igraph_cattribute_GAB_set(). * \param graph The graph. * \param n The name of the attribute. * \param value The new value of the attribute. * \return Error code. */ #define SETGAB(graph,n,value) (igraph_cattribute_GAB_set((graph),(n),(value))) /** * \define SETGAS * Set a string graph attribute * * This is a shorthand for \ref igraph_cattribute_GAS_set(). * \param graph The graph. * \param n The name of the attribute. * \param value The new value of the attribute. * \return Error code. */ #define SETGAS(graph,n,value) (igraph_cattribute_GAS_set((graph),(n),(value))) /** * \define SETVAN * Set a numeric vertex attribute * * This is a shorthand for \ref igraph_cattribute_VAN_set(). * \param graph The graph. * \param n The name of the attribute. * \param vid Ids of the vertices to set. * \param value The new value of the attribute. * \return Error code. */ #define SETVAN(graph,n,vid,value) (igraph_cattribute_VAN_set((graph),(n),(vid),(value))) /** * \define SETVAB * Set a boolean vertex attribute * * This is a shorthand for \ref igraph_cattribute_VAB_set(). * \param graph The graph. * \param n The name of the attribute. * \param vid Ids of the vertices to set. * \param value The new value of the attribute. * \return Error code. */ #define SETVAB(graph,n,vid,value) (igraph_cattribute_VAB_set((graph),(n),(vid),(value))) /** * \define SETVAS * Set a string vertex attribute * * This is a shorthand for \ref igraph_cattribute_VAS_set(). * \param graph The graph. * \param n The name of the attribute. * \param vid Ids of the vertices to set. * \param value The new value of the attribute. * \return Error code. */ #define SETVAS(graph,n,vid,value) (igraph_cattribute_VAS_set((graph),(n),(vid),(value))) /** * \define SETEAN * Set a numeric edge attribute * * This is a shorthand for \ref igraph_cattribute_EAN_set(). * \param graph The graph. * \param n The name of the attribute. * \param eid Ids of the edges to set. * \param value The new value of the attribute. * \return Error code. */ #define SETEAN(graph,n,eid,value) (igraph_cattribute_EAN_set((graph),(n),(eid),(value))) /** * \define SETEAB * Set a boolean edge attribute * * This is a shorthand for \ref igraph_cattribute_EAB_set(). * \param graph The graph. * \param n The name of the attribute. * \param eid Ids of the edges to set. * \param value The new value of the attribute. * \return Error code. */ #define SETEAB(graph,n,eid,value) (igraph_cattribute_EAB_set((graph),(n),(eid),(value))) /** * \define SETEAS * Set a string edge attribute * * This is a shorthand for \ref igraph_cattribute_EAS_set(). * \param graph The graph. * \param n The name of the attribute. * \param eid Ids of the edges to set. * \param value The new value of the attribute. * \return Error code. */ #define SETEAS(graph,n,eid,value) (igraph_cattribute_EAS_set((graph),(n),(eid),(value))) /** * \define SETVANV * Set a numeric vertex attribute for all vertices * * This is a shorthand for \ref igraph_cattribute_VAN_setv(). * \param graph The graph. * \param n The name of the attribute. * \param v Vector containing the new values of the attributes. * \return Error code. */ #define SETVANV(graph,n,v) (igraph_cattribute_VAN_setv((graph),(n),(v))) /** * \define SETVABV * Set a boolean vertex attribute for all vertices * * This is a shorthand for \ref igraph_cattribute_VAB_setv(). * \param graph The graph. * \param n The name of the attribute. * \param v Vector containing the new values of the attributes. * \return Error code. */ #define SETVABV(graph,n,v) (igraph_cattribute_VAB_setv((graph),(n),(v))) /** * \define SETVASV * Set a string vertex attribute for all vertices * * This is a shorthand for \ref igraph_cattribute_VAS_setv(). * \param graph The graph. * \param n The name of the attribute. * \param v Vector containing the new values of the attributes. * \return Error code. */ #define SETVASV(graph,n,v) (igraph_cattribute_VAS_setv((graph),(n),(v))) /** * \define SETEANV * Set a numeric edge attribute for all vertices * * This is a shorthand for \ref igraph_cattribute_EAN_setv(). * \param graph The graph. * \param n The name of the attribute. * \param v Vector containing the new values of the attributes. */ #define SETEANV(graph,n,v) (igraph_cattribute_EAN_setv((graph),(n),(v))) /** * \define SETEABV * Set a boolean edge attribute for all vertices * * This is a shorthand for \ref igraph_cattribute_EAB_setv(). * \param graph The graph. * \param n The name of the attribute. * \param v Vector containing the new values of the attributes. */ #define SETEABV(graph,n,v) (igraph_cattribute_EAB_setv((graph),(n),(v))) /** * \define SETEASV * Set a string edge attribute for all vertices * * This is a shorthand for \ref igraph_cattribute_EAS_setv(). * \param graph The graph. * \param n The name of the attribute. * \param v Vector containing the new values of the attributes. */ #define SETEASV(graph,n,v) (igraph_cattribute_EAS_setv((graph),(n),(v))) /** * \define DELGA * Remove a graph attribute. * * A shorthand for \ref igraph_cattribute_remove_g(). * \param graph The graph. * \param n The name of the attribute to remove. */ #define DELGA(graph,n) (igraph_cattribute_remove_g((graph),(n))) /** * \define DELVA * Remove a vertex attribute. * * A shorthand for \ref igraph_cattribute_remove_v(). * \param graph The graph. * \param n The name of the attribute to remove. */ #define DELVA(graph,n) (igraph_cattribute_remove_v((graph),(n))) /** * \define DELEA * Remove an edge attribute. * * A shorthand for \ref igraph_cattribute_remove_e(). * \param graph The graph. * \param n The name of the attribute to remove. */ #define DELEA(graph,n) (igraph_cattribute_remove_e((graph),(n))) /** * \define DELGAS * Remove all graph attributes. * * Calls \ref igraph_cattribute_remove_all(). * \param graph The graph. */ #define DELGAS(graph) (igraph_cattribute_remove_all((graph),1,0,0)) /** * \define DELVAS * Remove all vertex attributes. * * Calls \ref igraph_cattribute_remove_all(). * \param graph The graph. */ #define DELVAS(graph) (igraph_cattribute_remove_all((graph),0,1,0)) /** * \define DELEAS * Remove all edge attributes. * * Calls \ref igraph_cattribute_remove_all(). * \param graph The graph. */ #define DELEAS(graph) (igraph_cattribute_remove_all((graph),0,0,1)) /** * \define DELALL * Remove all attributes. * * All graph, vertex and edges attributes will be removed. * Calls \ref igraph_cattribute_remove_all(). * \param graph The graph. */ #define DELALL(graph) (igraph_cattribute_remove_all((graph),1,1,1)) __END_DECLS #endif igraph/src/sugiyama.c0000644000176000001440000013663312325527074014351 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "igraph_centrality.h" #include "igraph_components.h" #include "igraph_constants.h" #include "igraph_constructors.h" #include "igraph_datatype.h" #include "igraph_error.h" #include "igraph_glpk_support.h" #include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_structural.h" #include "igraph_types.h" #include /* #define SUGIYAMA_DEBUG */ #ifdef _MSC_VER /* MSVC does not support variadic macros */ #include static void debug(const char* fmt, ...) { va_list args; va_start(args, fmt); #ifdef SUGIYAMA_DEBUG vfprintf(stderr, fmt, args); #endif va_end(args); } #else # ifdef SUGIYAMA_DEBUG # define debug(...) fprintf(stderr, __VA_ARGS__) # else # define debug(...) # endif #endif /* MSVC uses __forceinline instead of inline */ #ifdef _MSC_VER # define INLINE __forceinline #else # define INLINE inline #endif /* * Implementation of the Sugiyama layout algorithm as described in: * * [1] K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual Understanding of * Hierarchical Systems". IEEE Transactions on Systems, Man and Cybernetics * 11(2):109-125, 1981. * * The layering (if not given in advance) is calculated by ... TODO * * [2] TODO * * The X coordinates of nodes within a layer are calculated using the method of * Brandes & Köpf: * * [3] U. Brandes and B. Köpf, "Fast and Simple Horizontal Coordinate * Assignment". In: Lecture Notes in Computer Science 2265:31-44, 2002. * * Layer compaction is done according to: * * [4] N.S. Nikolov and A. Tarassov, "Graph layering by promotion of nodes". * Journal of Discrete Applied Mathematics, special issue: IV ALIO/EURO * workshop on applied combinatorial optimization, 154(5). * * The steps of the algorithm are as follows: * * 1. Cycle removal by finding an approximately minimal feedback arc set * and reversing the direction of edges in the set. Algorithms for * finding minimal feedback arc sets are as follows: * * - Find a cycle and find its minimum weight edge. Decrease the weight * of all the edges by w. Remove those edges whose weight became zero. * Repeat until there are no cycles. Re-introduce removed edges in * decreasing order of weights, ensuring that no cycles are created. * * - Order the vertices somehow and remove edges which point backwards * in the ordering. Eades et al proposed the following procedure: * * 1. Iteratively remove sinks and prepend them to a vertex sequence * s2. * * 2. Iteratively remove sources and append them to a vertex sequence * s1. * * 3. Choose a vertex u s.t. the difference between the number of * rightward arcs and the number of leftward arcs is the largest, * remove u and append it to s1. Goto step 1 if there are still * more vertices. * * 4. Concatenate s1 with s2. * * This algorithm is known to produce feedback arc sets at most the * size of m/2 - n/6, where m is the number of edges. Further * improvements are possible in step 3 which bring down the size of * the set to at most m/4 for cubic directed graphs, see Eades (1995). * * - For undirected graphs, find a maximum weight spanning tree and * remove all the edges not in the spanning tree. For directed graphs, * find minimal cuts iteratively and remove edges pointing from A to * B or from B to A in the cut, depending on which one is smaller. Yes, * this is time-consuming. * * 2. Assigning vertices to layers according to [2]. * * 3. Extracting weakly connected components. The remaining steps are * executed for each component. * * 4. Compacting the layering using the method of [4]. TODO * Steps 2-4 are performed only when no layering is given in advance. * * 5. Adding dummy nodes to ensure that each edge spans at most one layer * only. * * 6. Finding an optimal ordering of vertices within a layer using the * Sugiyama framework [1]. * * 7. Assigning horizontal coordinates to each vertex using [3]. * * 8. ??? * * 9. Profit! */ /** * Data structure to store a layering of the graph. */ typedef struct { igraph_vector_ptr_t layers; } igraph_i_layering_t; /** * Initializes a layering. */ int igraph_i_layering_init(igraph_i_layering_t* layering, const igraph_vector_t* membership) { long int i, n, num_layers; if (igraph_vector_size(membership) == 0) num_layers = 0; else num_layers = (long int) igraph_vector_max(membership) + 1; IGRAPH_CHECK(igraph_vector_ptr_init(&layering->layers, num_layers)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &layering->layers); for (i = 0; i < num_layers; i++) { igraph_vector_t* vec = igraph_Calloc(1, igraph_vector_t); IGRAPH_VECTOR_INIT_FINALLY(vec, 0); VECTOR(layering->layers)[i] = vec; IGRAPH_FINALLY_CLEAN(1); } IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&layering->layers, igraph_vector_destroy); n = igraph_vector_size(membership); for (i = 0; i < n; i++) { long int l = (long int) VECTOR(*membership)[i]; igraph_vector_t* vec = VECTOR(layering->layers)[l]; IGRAPH_CHECK(igraph_vector_push_back(vec, i)); } IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * Destroys a layering. */ void igraph_i_layering_destroy(igraph_i_layering_t* layering) { igraph_vector_ptr_destroy_all(&layering->layers); } /** * Returns the number of layers in a layering. */ int igraph_i_layering_num_layers(const igraph_i_layering_t* layering) { return (int) igraph_vector_ptr_size(&layering->layers); } /** * Returns the list of vertices in a given layer */ igraph_vector_t* igraph_i_layering_get(const igraph_i_layering_t* layering, long int index) { return (igraph_vector_t*)VECTOR(layering->layers)[index]; } /** * Forward declarations */ static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, const igraph_vector_t* weights, igraph_vector_t* membership); static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, igraph_matrix_t* layout, const igraph_i_layering_t* layering, long int maxiter); static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph, igraph_matrix_t* layout, const igraph_i_layering_t* layering, igraph_real_t hgap, igraph_integer_t no_of_real_nodes); /** * Calculated the median of four numbers (not necessarily sorted). */ static INLINE igraph_real_t igraph_i_median_4(igraph_real_t x1, igraph_real_t x2, igraph_real_t x3, igraph_real_t x4) { igraph_real_t arr[4] = { x1, x2, x3, x4 }; igraph_vector_t vec; igraph_vector_view(&vec, arr, 4); igraph_vector_sort(&vec); return (arr[1] + arr[2]) / 2.0; } /** * \ingroup layout * \function igraph_layout_sugiyama * \brief Sugiyama layout algorithm for layered directed acyclic graphs. * * * This layout algorithm is designed for directed acyclic graphs where each * vertex is assigned to a layer. Layers are indexed from zero, and vertices * of the same layer will be placed on the same horizontal line. The X coordinates * of vertices within each layer are decided by the heuristic proposed by * Sugiyama et al to minimize edge crossings. * * * You can also try to lay out undirected graphs, graphs containing cycles, or * graphs without an a priori layered assignment with this algorithm. igraph * will try to eliminate cycles and assign vertices to layers, but there is no * guarantee on the quality of the layout in such cases. * * * The Sugiyama layout may introduce "bends" on the edges in order to obtain a * visually more pleasing layout. This is achieved by adding dummy nodes to * edges spanning more than one layer. The resulting layout assigns coordinates * not only to the nodes of the original graph but also to the dummy nodes. * The layout algorithm will also return the extended graph with the dummy nodes. * An edge in the original graph may either be mapped to a single edge in the * extended graph or a \em path that starts and ends in the original * source and target vertex and passes through multiple dummy vertices. In * such cases, the user may also request the mapping of the edges of the extended * graph back to the edges of the original graph. * * * For more details, see K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual * Understanding of Hierarchical Systems". IEEE Transactions on Systems, Man and * Cybernetics 11(2):109-125, 1981. * * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will contain * the result and will be resized as needed. The first |V| rows * of the layout will contain the coordinates of the original graph, * the remaining rows contain the positions of the dummy nodes. * Therefore, you can use the result both with \p graph or with * \p extended_graph. * \param extended_graph Pointer to an uninitialized graph object or \c NULL. * The extended graph with the added dummy nodes will be * returned here. In this graph, each edge points downwards * to lower layers, spans exactly one layer and the first * |V| vertices coincide with the vertices of the * original graph. * \param extd_to_orig_eids Pointer to a vector or \c NULL. If not \c NULL, the * mapping from the edge IDs of the extended graph back * to the edge IDs of the original graph will be stored * here. * \param layers The layer index for each vertex or \c NULL if the layers should * be determined automatically by igraph. * \param hgap The preferred minimum horizontal gap between vertices in the same * layer. * \param vgap The distance between layers. * \param maxiter Maximum number of iterations in the crossing minimization stage. * 100 is a reasonable default; if you feel that you have too * many edge crossings, increase this. * \param weights Weights of the edges. These are used only if the graph contains * cycles; igraph will tend to reverse edges with smaller * weights when breaking the cycles. */ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, igraph_t *extd_graph, igraph_vector_t *extd_to_orig_eids, const igraph_vector_t* layers, igraph_real_t hgap, igraph_real_t vgap, long int maxiter, const igraph_vector_t *weights) { long int i, j, k, l, m, nei; long int no_of_nodes = (long int)igraph_vcount(graph); long int comp_idx; long int next_extd_vertex_id = no_of_nodes; igraph_bool_t directed = igraph_is_directed(graph); igraph_integer_t no_of_components; /* number of components of the original graph */ igraph_vector_t membership; /* components of the original graph */ igraph_vector_t extd_edgelist; /* edge list of the extended graph */ igraph_vector_t layers_own; /* layer indices after having eliminated empty layers */ igraph_real_t dx=0, dx2=0; /* displacement of the current component on the X axis */ igraph_vector_t layer_to_y; /* mapping from layer indices to final Y coordinates */ if (layers && igraph_vector_size(layers) != no_of_nodes) { IGRAPH_ERROR("layer vector too short or too long", IGRAPH_EINVAL); } if (extd_graph != 0) { IGRAPH_VECTOR_INIT_FINALLY(&extd_edgelist, 0); if (extd_to_orig_eids != 0) igraph_vector_clear(extd_to_orig_eids); } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); IGRAPH_VECTOR_INIT_FINALLY(&layer_to_y, 0); /* 1. Find a feedback arc set if we don't have a layering yet. If we do have * a layering, we can leave all the edges as is as they will be re-oriented * to point downwards only anyway. */ if (layers == 0) { IGRAPH_VECTOR_INIT_FINALLY(&layers_own, no_of_nodes); IGRAPH_CHECK(igraph_i_layout_sugiyama_place_nodes_vertically( graph, weights, &layers_own)); } else { IGRAPH_CHECK(igraph_vector_copy(&layers_own, layers)); IGRAPH_FINALLY(igraph_vector_destroy, &layers_own); } /* Normalize layering, eliminate empty layers */ if (no_of_nodes > 0) { igraph_vector_t inds; IGRAPH_VECTOR_INIT_FINALLY(&inds, 0); IGRAPH_CHECK((int) igraph_vector_qsort_ind(&layers_own, &inds, 0)); j = -1; dx = VECTOR(layers_own)[(long int)VECTOR(inds)[0]] - 1; for (i = 0; i < no_of_nodes; i++) { k = (long int)VECTOR(inds)[i]; if (VECTOR(layers_own)[k] > dx) { /* New layer starts here */ dx = VECTOR(layers_own)[k]; j++; IGRAPH_CHECK(igraph_vector_push_back(&layer_to_y, dx * vgap)); } VECTOR(layers_own)[k] = j; } igraph_vector_destroy(&inds); IGRAPH_FINALLY_CLEAN(1); } /* 2. Find the connected components. */ IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components, IGRAPH_WEAK)); /* 3. For each component... */ dx = 0; for (comp_idx = 0; comp_idx < no_of_components; comp_idx++) { /* Extract the edges of the comp_idx'th component and add dummy nodes for edges * spanning more than one layer. */ long int component_size, next_new_vertex_id; igraph_vector_t old2new_vertex_ids; igraph_vector_t new2old_vertex_ids; igraph_vector_t new_layers; igraph_vector_t edgelist; igraph_vector_t neis; IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&new2old_vertex_ids, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&old2new_vertex_ids, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&new_layers, 0); igraph_vector_fill(&old2new_vertex_ids, -1); /* Construct a mapping from the old vertex ids to the new ones */ for (i = 0, next_new_vertex_id = 0; i < no_of_nodes; i++) { if (VECTOR(membership)[i] == comp_idx) { IGRAPH_CHECK(igraph_vector_push_back(&new_layers, VECTOR(layers_own)[i])); VECTOR(new2old_vertex_ids)[next_new_vertex_id] = i; VECTOR(old2new_vertex_ids)[i] = next_new_vertex_id; next_new_vertex_id++; } } component_size = next_new_vertex_id; /* Construct a proper layering of the component in new_graph where each edge * points downwards and spans exactly one layer. */ for (i = 0; i < no_of_nodes; i++) { if (VECTOR(membership)[i] != comp_idx) continue; /* Okay, this vertex is in the component we are considering. * Add the neighbors of this vertex, excluding loops */ IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT)); j = igraph_vector_size(&neis); for (k = 0; k < j; k++) { long int eid = (long int) VECTOR(neis)[k]; if (directed) { nei = IGRAPH_TO(graph, eid); } else { nei = IGRAPH_OTHER(graph, eid, i); if (nei < i) /* to avoid considering edges twice */ continue; } if (VECTOR(layers_own)[i] == VECTOR(layers_own)[nei]) { /* Edge goes within the same layer, we don't need this in the * layered graph, but we need it in the extended graph */ if (extd_graph != 0) { IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i)); IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } } else if (VECTOR(layers_own)[i] > VECTOR(layers_own)[nei]) { /* Edge goes upwards, we have to flip it */ IGRAPH_CHECK(igraph_vector_push_back(&edgelist, VECTOR(old2new_vertex_ids)[nei])); for (l = (long int) VECTOR(layers_own)[nei]+1; l < VECTOR(layers_own)[i]; l++) { IGRAPH_CHECK(igraph_vector_push_back(&new_layers, l)); IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id)); IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id++)); } IGRAPH_CHECK(igraph_vector_push_back(&edgelist, VECTOR(old2new_vertex_ids)[i])); /* Also add the edge to the extended graph if needed, but this time * with the proper orientation */ if (extd_graph != 0) { IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i)); next_extd_vertex_id += VECTOR(layers_own)[i] - VECTOR(layers_own)[nei] - 1; for (l = (long int) VECTOR(layers_own)[i]-1, m = 1; l > VECTOR(layers_own)[nei]; l--, m++) { IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id-m)); IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id-m)); if (extd_to_orig_eids != 0) IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } } else { /* Edge goes downwards */ IGRAPH_CHECK(igraph_vector_push_back(&edgelist, VECTOR(old2new_vertex_ids)[i])); for (l = (long int) VECTOR(layers_own)[i]+1; l < VECTOR(layers_own)[nei]; l++) { IGRAPH_CHECK(igraph_vector_push_back(&new_layers, l)); IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id)); IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id++)); } IGRAPH_CHECK(igraph_vector_push_back(&edgelist, VECTOR(old2new_vertex_ids)[nei])); /* Also add the edge to the extended graph */ if (extd_graph != 0) { IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i)); for (l = (long int) VECTOR(layers_own)[i]+1; l < VECTOR(layers_own)[nei]; l++) { IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id)); IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id++)); if (extd_to_orig_eids != 0) IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } } } } /* At this point, we have the subgraph with the dummy nodes and * edges, so we can run Sugiyama's algorithm on it. */ { igraph_matrix_t layout; igraph_i_layering_t layering; igraph_t subgraph; IGRAPH_CHECK(igraph_matrix_init(&layout, next_new_vertex_id, 2)); IGRAPH_FINALLY(igraph_matrix_destroy, &layout); IGRAPH_CHECK(igraph_create(&subgraph, &edgelist, (igraph_integer_t) next_new_vertex_id, 1)); IGRAPH_FINALLY(igraph_destroy, &subgraph); /* igraph_vector_print(&edgelist); igraph_vector_print(&new_layers); */ /* Assign the vertical coordinates */ for (i = 0; i < next_new_vertex_id; i++) MATRIX(layout, i, 1) = VECTOR(new_layers)[i]; /* Create a layering */ IGRAPH_CHECK(igraph_i_layering_init(&layering, &new_layers)); IGRAPH_FINALLY(igraph_i_layering_destroy, &layering); /* Find the order in which the nodes within a layer should be placed */ IGRAPH_CHECK(igraph_i_layout_sugiyama_order_nodes_horizontally(&subgraph, &layout, &layering, maxiter)); /* Assign the horizontal coordinates. This is according to the algorithm * of Brandes & Köpf */ IGRAPH_CHECK(igraph_i_layout_sugiyama_place_nodes_horizontally(&subgraph, &layout, &layering, hgap, (igraph_integer_t) component_size)); /* Re-assign rows into the result matrix, and at the same time, */ /* adjust dx so that the next component does not overlap this one */ j = next_new_vertex_id - component_size; k = igraph_matrix_nrow(res); IGRAPH_CHECK(igraph_matrix_add_rows(res, j)); dx2 = dx; for (i = 0; i < component_size; i++) { l = (long int)VECTOR(new2old_vertex_ids)[i]; MATRIX(*res, l, 0) = MATRIX(layout, i, 0) + dx; MATRIX(*res, l, 1) = VECTOR(layer_to_y)[(long)MATRIX(layout, i, 1)]; if (dx2 < MATRIX(*res, l, 0)) dx2 = MATRIX(*res, l, 0); } for (i = component_size; i < next_new_vertex_id; i++) { MATRIX(*res, k, 0) = MATRIX(layout, i, 0) + dx; MATRIX(*res, k, 1) = VECTOR(layer_to_y)[(long)MATRIX(layout, i, 1)]; if (dx2 < MATRIX(*res, k, 0)) dx2 = MATRIX(*res, k, 0); k++; } dx = dx2 + hgap; igraph_destroy(&subgraph); igraph_i_layering_destroy(&layering); igraph_matrix_destroy(&layout); IGRAPH_FINALLY_CLEAN(3); } igraph_vector_destroy(&new_layers); igraph_vector_destroy(&old2new_vertex_ids); igraph_vector_destroy(&new2old_vertex_ids); igraph_vector_destroy(&edgelist); igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(5); } igraph_vector_destroy(&layers_own); igraph_vector_destroy(&layer_to_y); igraph_vector_destroy(&membership); IGRAPH_FINALLY_CLEAN(3); if (extd_graph != 0) { IGRAPH_CHECK(igraph_create(extd_graph, &extd_edgelist, (igraph_integer_t) next_extd_vertex_id, igraph_is_directed(graph))); igraph_vector_destroy(&extd_edgelist); IGRAPH_FINALLY_CLEAN(1); } return IGRAPH_SUCCESS; } static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, const igraph_vector_t* weights, igraph_vector_t* membership) { long int no_of_nodes = igraph_vcount(graph); long int no_of_edges = igraph_ecount(graph); IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); if (no_of_edges == 0) { igraph_vector_fill(membership, 0); return IGRAPH_SUCCESS; } #ifdef HAVE_GLPK if (igraph_is_directed(graph) && no_of_nodes <= 1000) { /* Network simplex algorithm of Gansner et al, using the original linear * programming formulation */ long int i, j; igraph_vector_t outdegs, indegs, feedback_edges; glp_prob *ip; glp_smcp parm; /* Allocate storage and create the problem */ ip = glp_create_prob(); IGRAPH_FINALLY(glp_delete_prob, ip); IGRAPH_VECTOR_INIT_FINALLY(&feedback_edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&outdegs, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&indegs, no_of_nodes); /* Find an approximate feedback edge set */ IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, &feedback_edges, weights, 0)); igraph_vector_sort(&feedback_edges); /* Calculate in- and out-strengths for the remaining edges */ IGRAPH_CHECK(igraph_strength(graph, &indegs, igraph_vss_all(), IGRAPH_IN, 1, weights)); IGRAPH_CHECK(igraph_strength(graph, &outdegs, igraph_vss_all(), IGRAPH_IN, 1, weights)); j = igraph_vector_size(&feedback_edges); for (i = 0; i < j; i++) { long int eid = (long int) VECTOR(feedback_edges)[i]; long int from = IGRAPH_FROM(graph, eid); long int to = IGRAPH_TO(graph, eid); VECTOR(outdegs)[from] -= weights ? VECTOR(*weights)[eid] : 1; VECTOR(indegs)[to] -= weights ? VECTOR(*weights)[eid] : 1; } /* Configure GLPK */ glp_term_out(GLP_OFF); glp_init_smcp(&parm); parm.msg_lev = GLP_MSG_OFF; parm.presolve = GLP_OFF; /* Set up variables and objective function coefficients */ glp_set_obj_dir(ip, GLP_MIN); glp_add_cols(ip, (int) no_of_nodes); IGRAPH_CHECK(igraph_vector_sub(&outdegs, &indegs)); for (i = 1; i <= no_of_nodes; i++) { glp_set_col_kind(ip, (int) i, GLP_IV); glp_set_col_bnds(ip, (int) i, GLP_LO, 0.0, 0.0); glp_set_obj_coef(ip, (int) i, VECTOR(outdegs)[i-1]); } igraph_vector_destroy(&indegs); igraph_vector_destroy(&outdegs); IGRAPH_FINALLY_CLEAN(2); /* Add constraints */ glp_add_rows(ip, (int) no_of_edges); IGRAPH_CHECK(igraph_vector_push_back(&feedback_edges, -1)); j = 0; for (i = 0; i < no_of_edges; i++) { int ind[3]; double val[3] = {0, -1, 1}; ind[1] = IGRAPH_FROM(graph, i)+1; ind[2] = IGRAPH_TO(graph, i)+1; if (ind[1] == ind[2]) { if (VECTOR(feedback_edges)[j] == i) j++; continue; } if (VECTOR(feedback_edges)[j] == i) { /* This is a feedback edge, add it reversed */ glp_set_row_bnds(ip, (int) i+1, GLP_UP, -1, -1); j++; } else { glp_set_row_bnds(ip, (int) i+1, GLP_LO, 1, 1); } glp_set_mat_row(ip, (int) i+1, 2, ind, val); } /* Solve the problem */ IGRAPH_GLPK_CHECK(glp_simplex(ip, &parm), "Vertical arrangement step using IP failed"); /* The problem is totally unimodular, therefore the output of the simplex * solver can be converted to an integer solution easily */ for (i = 0; i < no_of_nodes; i++) VECTOR(*membership)[i] = floor(glp_get_col_prim(ip, (int) i+1)); glp_delete_prob(ip); igraph_vector_destroy(&feedback_edges); IGRAPH_FINALLY_CLEAN(2); } else if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, 0, weights, membership)); } else { IGRAPH_CHECK(igraph_i_feedback_arc_set_undirected(graph, 0, weights, membership)); } #else if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, 0, weights, membership)); } else { IGRAPH_CHECK(igraph_i_feedback_arc_set_undirected(graph, 0, weights, membership)); } #endif return IGRAPH_SUCCESS; } static int igraph_i_layout_sugiyama_calculate_barycenters(const igraph_t* graph, const igraph_i_layering_t* layering, long int layer_index, igraph_neimode_t direction, const igraph_matrix_t* layout, igraph_vector_t* barycenters) { long int i, j, m, n; igraph_vector_t* layer_members = igraph_i_layering_get(layering, layer_index); igraph_vector_t neis; IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); n = igraph_vector_size(layer_members); IGRAPH_CHECK(igraph_vector_resize(barycenters, n)); igraph_vector_null(barycenters); for (i = 0; i < n; i++) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) VECTOR(*layer_members)[i], direction)); m = igraph_vector_size(&neis); if (m == 0) { /* No neighbors in this direction. Just use the current X coordinate */ VECTOR(*barycenters)[i] = MATRIX(*layout, i, 0); } else { for (j = 0; j < m; j++) { VECTOR(*barycenters)[i] += MATRIX(*layout, (long)VECTOR(neis)[j], 0); } VECTOR(*barycenters)[i] /= m; } } igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * Given a properly layered graph where each edge points downwards and spans * exactly one layer, arranges the nodes in each layer horizontally in a way * that strives to minimize edge crossings. */ static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, igraph_matrix_t* layout, const igraph_i_layering_t* layering, long int maxiter) { long int i, n, nei; long int no_of_vertices = igraph_vcount(graph); long int no_of_layers = igraph_i_layering_num_layers(layering); long int iter, layer_index; igraph_vector_t* layer_members; igraph_vector_t neis, barycenters, sort_indices; igraph_bool_t changed; /* The first column of the matrix will serve as the ordering */ /* Start with a first-seen ordering within each layer */ { long int *xs = igraph_Calloc(no_of_layers, long int); if (xs == 0) IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM); for (i = 0; i < no_of_vertices; i++) MATRIX(*layout, i, 0) = xs[(long int)MATRIX(*layout, i, 1)]++; free(xs); } IGRAPH_VECTOR_INIT_FINALLY(&barycenters, 0); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&sort_indices, 0); /* Start the effective part of the Sugiyama algorithm */ iter = 0; changed = 1; while (changed && iter < maxiter) { changed = 0; /* Phase 1 */ /* Moving downwards and sorting by upper barycenters */ for (layer_index = 1; layer_index < no_of_layers; layer_index++) { layer_members = igraph_i_layering_get(layering, layer_index); n = igraph_vector_size(layer_members); igraph_i_layout_sugiyama_calculate_barycenters(graph, layering, layer_index, IGRAPH_IN, layout, &barycenters); #ifdef SUGIYAMA_DEBUG printf("Layer %ld, aligning to upper barycenters\n", layer_index); printf("Vertices: "); igraph_vector_print(layer_members); printf("Barycenters: "); igraph_vector_print(&barycenters); #endif IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, &sort_indices, 0)); for (i = 0; i < n; i++) { nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; VECTOR(barycenters)[i] = nei; MATRIX(*layout, nei, 0) = i; } if (!igraph_vector_all_e(layer_members, &barycenters)) { IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters)); #ifdef SUGIYAMA_DEBUG printf("New vertex order: "); igraph_vector_print(layer_members); #endif changed = 1; } else { #ifdef SUGIYAMA_DEBUG printf("Order did not change.\n"); #endif } } /* Moving upwards and sorting by lower barycenters */ for (layer_index = no_of_layers - 2; layer_index >= 0; layer_index--) { layer_members = igraph_i_layering_get(layering, layer_index); n = igraph_vector_size(layer_members); igraph_i_layout_sugiyama_calculate_barycenters(graph, layering, layer_index, IGRAPH_OUT, layout, &barycenters); #ifdef SUGIYAMA_DEBUG printf("Layer %ld, aligning to lower barycenters\n", layer_index); printf("Vertices: "); igraph_vector_print(layer_members); printf("Barycenters: "); igraph_vector_print(&barycenters); #endif IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, &sort_indices, 0)); for (i = 0; i < n; i++) { nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; VECTOR(barycenters)[i] = nei; MATRIX(*layout, nei, 0) = i; } if (!igraph_vector_all_e(layer_members, &barycenters)) { IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters)); #ifdef SUGIYAMA_DEBUG printf("New vertex order: "); igraph_vector_print(layer_members); #endif changed = 1; } else { #ifdef SUGIYAMA_DEBUG printf("Order did not change.\n"); #endif } } #ifdef SUGIYAMA_DEBUG printf("==== Finished iteration %ld\n", iter); #endif iter++; } igraph_vector_destroy(&barycenters); igraph_vector_destroy(&neis); igraph_vector_destroy(&sort_indices); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } #define IS_DUMMY(v) ((v >= no_of_real_nodes)) #define IS_INNER_SEGMENT(u, v) (IS_DUMMY(u) && IS_DUMMY(v)) #define X_POS(v) (MATRIX(*layout, v, 0)) static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, const igraph_i_layering_t* layering, const igraph_matrix_t* layout, const igraph_vector_bool_t* ignored_edges, igraph_bool_t reverse, igraph_bool_t align_right, igraph_vector_t* roots, igraph_vector_t* align); static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, const igraph_vector_t* vertex_to_the_left, const igraph_vector_t* roots, const igraph_vector_t* align, igraph_real_t hgap, igraph_vector_t* xs); static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v, const igraph_vector_t* vertex_to_the_left, const igraph_vector_t* roots, const igraph_vector_t* align, igraph_vector_t* sinks, igraph_vector_t* shifts, igraph_real_t hgap, igraph_vector_t* xs); static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph, igraph_matrix_t* layout, const igraph_i_layering_t* layering, igraph_real_t hgap, igraph_integer_t no_of_real_nodes) { long int i, j, k, l, n; long int no_of_layers = igraph_i_layering_num_layers(layering); long int no_of_nodes = igraph_vcount(graph); long int no_of_edges = igraph_ecount(graph); igraph_vector_t neis1, neis2; igraph_vector_t xs[4]; igraph_vector_t roots, align; igraph_vector_t vertex_to_the_left; igraph_vector_bool_t ignored_edges; /* { igraph_vector_t edgelist; IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0); IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0)); igraph_vector_print(&edgelist); igraph_vector_destroy(&edgelist); IGRAPH_FINALLY_CLEAN(1); for (i = 0; i < no_of_layers; i++) { igraph_vector_t* layer = igraph_i_layering_get(layering, i); igraph_vector_print(layer); } } */ IGRAPH_CHECK(igraph_vector_bool_init(&ignored_edges, no_of_edges)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &ignored_edges); IGRAPH_VECTOR_INIT_FINALLY(&vertex_to_the_left, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis1, 0); IGRAPH_VECTOR_INIT_FINALLY(&neis2, 0); /* First, find all type 1 conflicts and mark one of the edges participating * in the conflict as being ignored. If one of the edges in the conflict * is a non-inner segment and the other is an inner segment, we ignore the * non-inner segment as we want to keep inner segments vertical. */ for (i = 0; i < no_of_layers-1; i++) { igraph_vector_t* vertices = igraph_i_layering_get(layering, i); n = igraph_vector_size(vertices); /* Find all the edges from this layer to the next */ igraph_vector_clear(&neis1); for (j = 0; j < n; j++) { IGRAPH_CHECK(igraph_neighbors(graph, &neis2, (igraph_integer_t) VECTOR(*vertices)[j], IGRAPH_OUT)); IGRAPH_CHECK(igraph_vector_append(&neis1, &neis2)); } /* Consider all pairs of edges and check whether they are in a type 1 * conflict */ n = igraph_vector_size(&neis1); for (j = 0; j < n; j++) { long int u = IGRAPH_FROM(graph, j); long int v = IGRAPH_TO(graph, j); igraph_bool_t j_inner = IS_INNER_SEGMENT(u, v); igraph_bool_t crossing; for (k = j+1; k < n; k++) { long int w = IGRAPH_FROM(graph, k); long int x = IGRAPH_TO(graph, k); if (IS_INNER_SEGMENT(w, x) == j_inner) continue; /* Do the u --> v and w --> x edges cross? */ crossing = (u == w || v == x); if (!crossing) { if (X_POS(u) <= X_POS(w)) { crossing = X_POS(v) >= X_POS(x); } else { crossing = X_POS(v) <= X_POS(x); } } if (crossing) { if (j_inner) { VECTOR(ignored_edges)[k] = 1; } else { VECTOR(ignored_edges)[j] = 1; } } } } } igraph_vector_destroy(&neis1); igraph_vector_destroy(&neis2); IGRAPH_FINALLY_CLEAN(2); /* * Prepare vertex_to_the_left where the ith element stores * the index of the vertex to the left of vertex i, or i itself if the * vertex is the leftmost vertex in a layer. */ for (i = 0; i < no_of_layers; i++) { igraph_vector_t* vertices = igraph_i_layering_get(layering, i); n = igraph_vector_size(vertices); if (n == 0) continue; k = l = (long int)VECTOR(*vertices)[0]; VECTOR(vertex_to_the_left)[k] = k; for (j = 1; j < n; j++) { k = (long int)VECTOR(*vertices)[j]; VECTOR(vertex_to_the_left)[k] = l; l = k; } } /* Type 1 conflicts found, ignored edges chosen, vertex_to_the_left * prepared. Run vertical alignment for all four combinations */ for (i = 0; i < 4; i++) IGRAPH_VECTOR_INIT_FINALLY(&xs[i], no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&align, no_of_nodes); for (i = 0; i < 4; i++) { IGRAPH_CHECK(igraph_i_layout_sugiyama_vertical_alignment(graph, layering, layout, &ignored_edges, /* reverse = */ (igraph_bool_t) i / 2, /* align_right = */ i % 2, &roots, &align)); IGRAPH_CHECK(igraph_i_layout_sugiyama_horizontal_compaction(graph, &vertex_to_the_left, &roots, &align, hgap, &xs[i])); } { igraph_real_t width, min_width, mins[4], maxs[4], diff; /* Find the alignment with the minimum width */ min_width = IGRAPH_INFINITY; j = 0; for (i = 0; i < 4; i++) { mins[i] = igraph_vector_min(&xs[i]); maxs[i] = igraph_vector_max(&xs[i]); width = maxs[i] - mins[i]; if (width < min_width) { min_width = width; j = i; } } /* Leftmost alignments: align them s.t. the min X coordinate is equal to * the minimum X coordinate of the alignment with the smallest width. * Rightmost alignments: align them s.t. the max X coordinate is equal to * the max X coordinate of the alignment with the smallest width. */ for (i = 0; i < 4; i++) { if (j == i) continue; if (i % 2 == 0) { /* Leftmost alignment */ diff = mins[j] - mins[i]; } else { /* Rightmost alignment */ diff = maxs[j] - maxs[i]; } igraph_vector_add_constant(&xs[i], diff); } } /* For every vertex, find the median of the X coordinates in the four * alignments */ for (i = 0; i < no_of_nodes; i++) { X_POS(i) = igraph_i_median_4(VECTOR(xs[0])[i], VECTOR(xs[1])[i], VECTOR(xs[2])[i], VECTOR(xs[3])[i]); } igraph_vector_destroy(&roots); igraph_vector_destroy(&align); IGRAPH_FINALLY_CLEAN(2); for (i = 0; i < 4; i++) igraph_vector_destroy(&xs[i]); IGRAPH_FINALLY_CLEAN(4); igraph_vector_destroy(&vertex_to_the_left); IGRAPH_FINALLY_CLEAN(1); igraph_vector_bool_destroy(&ignored_edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, const igraph_i_layering_t* layering, const igraph_matrix_t* layout, const igraph_vector_bool_t* ignored_edges, igraph_bool_t reverse, igraph_bool_t align_right, igraph_vector_t* roots, igraph_vector_t* align) { long int i, j, k, n, di, dj, i_limit, j_limit, r; long int no_of_layers = igraph_i_layering_num_layers(layering); long int no_of_nodes = igraph_vcount(graph); igraph_neimode_t neimode = (reverse ? IGRAPH_OUT : IGRAPH_IN); igraph_vector_t neis, xs, inds; IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&xs, 0); IGRAPH_VECTOR_INIT_FINALLY(&inds, 0); IGRAPH_CHECK(igraph_vector_resize(roots, no_of_nodes)); IGRAPH_CHECK(igraph_vector_resize(align, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { VECTOR(*roots)[i] = VECTOR(*align)[i] = i; } /* When reverse = False, we are aligning "upwards" in the tree, hence we * have to loop i from 1 to no_of_layers-1 (inclusive) and use neimode=IGRAPH_IN. * When reverse = True, we are aligning "downwards", hence we have to loop * i from no_of_layers-2 to 0 (inclusive) and use neimode=IGRAPH_OUT. */ i = reverse ? (no_of_layers-2) : 1; di = reverse ? -1 : 1; i_limit = reverse ? -1 : no_of_layers; for (; i != i_limit; i += di) { igraph_vector_t *layer = igraph_i_layering_get(layering, i); /* r = 0 in the paper, but C arrays are indexed from 0 */ r = align_right ? LONG_MAX : -1; /* If align_right is 1, we have to process the layer in reverse order */ j = align_right ? (igraph_vector_size(layer)-1) : 0; dj = align_right ? -1 : 1; j_limit = align_right ? -1 : igraph_vector_size(layer); for (; j != j_limit; j += dj) { long int medians[2]; long int vertex = (long int) VECTOR(*layer)[j]; long int pos; if (VECTOR(*align)[vertex] != vertex) /* This vertex is already aligned with some other vertex, * so there's nothing to do */ continue; /* Find the neighbors of vertex j in layer i */ IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) vertex, neimode)); n = igraph_vector_size(&neis); if (n == 0) /* No neighbors in this direction, continue */ continue; if (n == 1) { /* Just one neighbor; the median is trivial */ medians[0] = (long int) VECTOR(neis)[0]; medians[1] = -1; } else { /* Sort the neighbors by their X coordinates */ IGRAPH_CHECK(igraph_vector_resize(&xs, n)); for (k = 0; k < n; k++) VECTOR(xs)[k] = X_POS((long int)VECTOR(neis)[k]); IGRAPH_CHECK((int) igraph_vector_qsort_ind(&xs, &inds, 0)); if (n % 2 == 1) { /* Odd number of neighbors, so the median is unique */ medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]]; medians[1] = -1; } else { /* Even number of neighbors, so we have two medians. The order * depends on whether we are processing the layer in leftmost * or rightmost fashion. */ if (align_right) { medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]]; medians[1] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2 - 1]]; } else { medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2 - 1]]; medians[1] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]]; } } } /* Try aligning with the medians */ for (k = 0; k < 2; k++) { igraph_integer_t eid; if (medians[k] < 0) continue; if (VECTOR(*align)[vertex] != vertex) { /* Vertex already aligned, continue */ continue; } /* Is the edge between medians[k] and vertex ignored * because of a type 1 conflict? */ IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) vertex, (igraph_integer_t) medians[k], 0, 1)); if (VECTOR(*ignored_edges)[(long int)eid]) continue; /* Okay, align with the median if possible */ pos = (long int) X_POS(medians[k]); if ((align_right && r > pos) || (!align_right && r < pos)) { VECTOR(*align)[medians[k]] = vertex; VECTOR(*roots)[vertex] = VECTOR(*roots)[medians[k]]; VECTOR(*align)[vertex] = VECTOR(*roots)[medians[k]]; r = pos; } } } } igraph_vector_destroy(&inds); igraph_vector_destroy(&neis); igraph_vector_destroy(&xs); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } /* * Runs a horizontal compaction given a vertical alignment (in `align`) * and the roots (in `roots`). These come out directly from * igraph_i_layout_sugiyama_vertical_alignment. * * Returns the X coordinates for each vertex in `xs`. * * `graph` is the input graph, `layering` is the layering on which we operate. * `hgap` is the preferred horizontal gap between vertices. */ static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, const igraph_vector_t* vertex_to_the_left, const igraph_vector_t* roots, const igraph_vector_t* align, igraph_real_t hgap, igraph_vector_t* xs) { long int i; long int no_of_nodes = igraph_vcount(graph); igraph_vector_t sinks, shifts, old_xs; igraph_real_t shift; /* Initialization */ IGRAPH_VECTOR_INIT_FINALLY(&sinks, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&shifts, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&old_xs, no_of_nodes); IGRAPH_CHECK(igraph_vector_resize(xs, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { VECTOR(sinks)[i] = i; } igraph_vector_fill(&shifts, IGRAPH_INFINITY); igraph_vector_fill(xs, -1); /* Calculate the coordinates of the vertices relative to their sinks * in their own class. At the end of this for loop, xs will contain the * relative displacement of a vertex from its sink, while the shifts list * will contain the absolute displacement of the sinks. * (For the sinks only, of course, the rest is undefined and unused) */ for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*roots)[i] == i) { IGRAPH_CHECK( igraph_i_layout_sugiyama_horizontal_compaction_place_block(i, vertex_to_the_left, roots, align, &sinks, &shifts, hgap, xs) ); } } /* In "sinks", only those indices `i` matter for which `i` is in `roots`. * All the other values will never be touched. */ /* Calculate the absolute coordinates */ IGRAPH_CHECK(igraph_vector_update(&old_xs, xs)); for (i = 0; i < no_of_nodes; i++) { long int root = (long int) VECTOR(*roots)[i]; VECTOR(*xs)[i] = VECTOR(old_xs)[root]; shift = VECTOR(shifts)[(long int)VECTOR(sinks)[root]]; if (shift < IGRAPH_INFINITY) VECTOR(*xs)[i] += shift; } igraph_vector_destroy(&sinks); igraph_vector_destroy(&shifts); igraph_vector_destroy(&old_xs); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v, const igraph_vector_t* vertex_to_the_left, const igraph_vector_t* roots, const igraph_vector_t* align, igraph_vector_t* sinks, igraph_vector_t* shifts, igraph_real_t hgap, igraph_vector_t* xs) { long int u, w; long int u_sink, v_sink; if (VECTOR(*xs)[v] >= 0) return IGRAPH_SUCCESS; VECTOR(*xs)[v] = 0; w = v; do { /* Check whether vertex w is the leftmost in its own layer */ u = (long int) VECTOR(*vertex_to_the_left)[w]; if (u != w) { /* Get the root of u (proceeding all the way upwards in the block) */ u = (long int) VECTOR(*roots)[u]; /* Place the block of u recursively */ IGRAPH_CHECK( igraph_i_layout_sugiyama_horizontal_compaction_place_block(u, vertex_to_the_left, roots, align, sinks, shifts, hgap, xs) ); u_sink = (long int) VECTOR(*sinks)[u]; v_sink = (long int) VECTOR(*sinks)[v]; /* If v is its own sink yet, set its sink to the sink of u */ if (v_sink == v) { VECTOR(*sinks)[v] = v_sink = u_sink; } /* If v and u have different sinks (i.e. they are in different classes), * shift the sink of u so that the two blocks are separated by the * preferred gap */ if (v_sink != u_sink) { if (VECTOR(*shifts)[u_sink] > VECTOR(*xs)[v] - VECTOR(*xs)[u] - hgap) { VECTOR(*shifts)[u_sink] = VECTOR(*xs)[v] - VECTOR(*xs)[u] - hgap; } } else { /* v and u have the same sink, i.e. they are in the same class. Make sure * that v is separated from u by at least hgap. */ if (VECTOR(*xs)[v] < VECTOR(*xs)[u] + hgap) VECTOR(*xs)[v] = VECTOR(*xs)[u] + hgap; } } /* Follow the alignment */ w = (long int) VECTOR(*align)[w]; } while (w != v); return IGRAPH_SUCCESS; } #undef IS_INNER_SEGMENT #undef IS_DUMMY #undef X_POS #ifdef SUGIYAMA_DEBUG #undef SUGIYAMA_DEBUG #endif igraph/src/cs_leaf.c0000644000176000001440000000361512325527073014116 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* consider A(i,j), node j in ith row subtree and return lca(jprev,j) */ CS_INT cs_leaf (CS_INT i, CS_INT j, const CS_INT *first, CS_INT *maxfirst, CS_INT *prevleaf, CS_INT *ancestor, CS_INT *jleaf) { CS_INT q, s, sparent, jprev ; if (!first || !maxfirst || !prevleaf || !ancestor || !jleaf) return (-1) ; *jleaf = 0 ; if (i <= j || first [j] <= maxfirst [i]) return (-1) ; /* j not a leaf */ maxfirst [i] = first [j] ; /* update max first[j] seen so far */ jprev = prevleaf [i] ; /* jprev = previous leaf of ith subtree */ prevleaf [i] = j ; *jleaf = (jprev == -1) ? 1: 2 ; /* j is first or subsequent leaf */ if (*jleaf == 1) return (i) ; /* if 1st leaf, q = root of ith subtree */ for (q = jprev ; q != ancestor [q] ; q = ancestor [q]) ; for (s = jprev ; s != q ; s = sparent) { sparent = ancestor [s] ; /* path compression */ ancestor [s] = q ; } return (q) ; /* q = least common ancester (jprev,j) */ } igraph/src/drl_layout.cpp0000644000176000001440000003537712325527073015252 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Layout // // This program implements a parallel force directed graph drawing // algorithm. The algorithm used is based upon a random decomposition // of the graph and simulated shared memory of node position and density. // In this version, the simulated shared memory is spread among all processors // // The structure of the inputs and outputs of this code will be displayed // if the program is called without parameters, or if an erroneous // parameter is passed to the program. // // S. Martin // 5/6/2005 // C++ library routines #include #include #include #include #include #include #include using namespace std; // layout routines and constants #include "drl_layout.h" #include "drl_parse.h" #include "drl_graph.h" // MPI #ifdef MUSE_MPI #include #endif using namespace drl; #include "igraph_layout.h" #include "igraph_random.h" #include "igraph_interface.h" namespace drl { // int main(int argc, char **argv) { // // initialize MPI // int myid, num_procs; // #ifdef MUSE_MPI // MPI_Init ( &argc, &argv ); // MPI_Comm_size ( MPI_COMM_WORLD, &num_procs ); // MPI_Comm_rank ( MPI_COMM_WORLD, &myid ); // #else // myid = 0; // num_procs = 1; // #endif // // parameters that must be broadcast to all processors // int rand_seed; // float edge_cut; // char int_file[MAX_FILE_NAME]; // char coord_file[MAX_FILE_NAME]; // char real_file[MAX_FILE_NAME]; // char parms_file[MAX_FILE_NAME]; // int int_out = 0; // int edges_out = 0; // int parms_in = 0; // float real_in = -1.0; // // user interaction is handled by processor 0 // if ( myid == 0 ) // { // if ( num_procs > MAX_PROCS ) // { // cout << "Error: Maximum number of processors is " << MAX_PROCS << "." << endl; // cout << "Adjust compile time parameter." << endl; // #ifdef MUSE_MPI // MPI_Abort ( MPI_COMM_WORLD, 1 ); // #else // exit (1); // #endif // } // // get user input // parse command_line ( argc, argv ); // rand_seed = command_line.rand_seed; // edge_cut = command_line.edge_cut; // int_out = command_line.int_out; // edges_out = command_line.edges_out; // parms_in = command_line.parms_in; // real_in = command_line.real_in; // strcpy ( coord_file, command_line.coord_file.c_str() ); // strcpy ( int_file, command_line.sim_file.c_str() ); // strcpy ( real_file, command_line.real_file.c_str() ); // strcpy ( parms_file, command_line.parms_file.c_str() ); // } // // now we initialize all processors by reading .int file // #ifdef MUSE_MPI // MPI_Bcast ( &int_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); // #endif // graph neighbors ( myid, num_procs, int_file ); // // check for user supplied parameters // #ifdef MUSE_MPI // MPI_Bcast ( &parms_in, 1, MPI_INT, 0, MPI_COMM_WORLD ); // #endif // if ( parms_in ) // { // #ifdef MUSE_MPI // MPI_Bcast ( &parms_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); // #endif // neighbors.read_parms ( parms_file ); // } // // set random seed, edge cutting, and real iterations parameters // #ifdef MUSE_MPI // MPI_Bcast ( &rand_seed, 1, MPI_INT, 0, MPI_COMM_WORLD ); // MPI_Bcast ( &edge_cut, 1, MPI_FLOAT, 0, MPI_COMM_WORLD ); // MPI_Bcast ( &real_in, 1, MPI_INT, 0, MPI_COMM_WORLD ); // #endif // neighbors.init_parms ( rand_seed, edge_cut, real_in ); // // check for .real file with existing coordinates // if ( real_in >= 0 ) // { // #ifdef MUSE_MPI // MPI_Bcast ( &real_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); // #endif // neighbors.read_real ( real_file ); // } // neighbors.draw_graph ( int_out, coord_file ); // // do we have to write out the edges? // #ifdef MUSE_MPI // MPI_Bcast ( &edges_out, 1, MPI_INT, 0, MPI_COMM_WORLD ); // #endif // if ( edges_out ) // { // #ifdef MUSE_MPI // MPI_Bcast ( &coord_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); // #endif // for ( int i = 0; i < num_procs; i++ ) // { // if ( myid == i ) // neighbors.write_sim ( coord_file ); // #ifdef MUSE_MPI // MPI_Barrier ( MPI_COMM_WORLD ); // #endif // } // } // // finally we output file and quit // float tot_energy; // tot_energy = neighbors.get_tot_energy (); // if ( myid == 0 ) // { // neighbors.write_coord ( coord_file ); // cout << "Total Energy: " << tot_energy << "." << endl // << "Program terminated successfully." << endl; // } // // MPI finalize // #ifdef MUSE_MPI // MPI_Finalize (); // #endif // return 0; // } } // namespace drl /** * \section about_drl * * * DrL is a sophisticated layout generator developed and implemented by * Shawn Martin et al. As of October 2012 the original DrL homepage is * unfortunately not available. You can read more about this algorithm * in the following technical report: Martin, S., Brown, W.M., * Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph) * Layout. SAND Reports, 2008. 2936: p. 1-10. * * * * Only a subset of the complete DrL functionality is * included in igraph, parallel runs and recursive, multi-level * layouting is not supported. * * * * The parameters of the layout are stored in an \ref * igraph_layout_drl_options_t structure, this can be initialized by * calling the function \ref igraph_layout_drl_options_init(). * The fields of this structure can then be adjusted by hand if needed. * The layout is calculated by an \ref igraph_layout_drl() call. * */ /** * \function igraph_layout_drl_options_init * Initialize parameters for the DrL layout generator * * This function can be used to initialize the struct holding the * parameters for the DrL layout generator. There are a number of * predefined templates available, it is a good idea to start from one * of these by modifying some parameters. * \param options The struct to initialize. * \param templ The template to use. Currently the following templates * are supplied: \c IGRAPH_LAYOUT_DRL_DEFAULT, \c * IGRAPH_LAYOUT_DRL_COARSEN, \c IGRAPH_LAYOUT_DRL_COARSEST, * \c IGRAPH_LAYOUT_DRL_REFINE and \c IGRAPH_LAYOUT_DRL_FINAL. * \return Error code. * * Time complexity: O(1). */ int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, igraph_layout_drl_default_t templ) { options->edge_cut=32.0/40.0; switch (templ) { case IGRAPH_LAYOUT_DRL_DEFAULT: options->init_iterations = 0; options->init_temperature = 2000; options->init_attraction = 10; options->init_damping_mult = 1.0; options->liquid_iterations = 200; options->liquid_temperature = 2000; options->liquid_attraction = 10; options->liquid_damping_mult = 1.0; options->expansion_iterations = 200; options->expansion_temperature = 2000; options->expansion_attraction = 2; options->expansion_damping_mult = 1.0; options->cooldown_iterations = 200; options->cooldown_temperature = 2000; options->cooldown_attraction = 1; options->cooldown_damping_mult = .1; options->crunch_iterations = 50; options->crunch_temperature = 250; options->crunch_attraction = 1; options->crunch_damping_mult = 0.25; options->simmer_iterations = 100; options->simmer_temperature = 250; options->simmer_attraction = .5; options->simmer_damping_mult = 0; break; case IGRAPH_LAYOUT_DRL_COARSEN: options->init_iterations = 0; options->init_temperature = 2000; options->init_attraction = 10; options->init_damping_mult = 1.0; options->liquid_iterations = 200; options->liquid_temperature = 2000; options->liquid_attraction = 2; options->liquid_damping_mult = 1.0; options->expansion_iterations = 200; options->expansion_temperature = 2000; options->expansion_attraction = 10; options->expansion_damping_mult = 1.0; options->cooldown_iterations = 200; options->cooldown_temperature = 2000; options->cooldown_attraction = 1; options->cooldown_damping_mult = .1; options->crunch_iterations = 50; options->crunch_temperature = 250; options->crunch_attraction = 1; options->crunch_damping_mult = 0.25; options->simmer_iterations = 100; options->simmer_temperature = 250; options->simmer_attraction = .5; options->simmer_damping_mult = 0; break; case IGRAPH_LAYOUT_DRL_COARSEST: options->init_iterations = 0; options->init_temperature = 2000; options->init_attraction = 10; options->init_damping_mult = 1.0; options->liquid_iterations = 200; options->liquid_temperature = 2000; options->liquid_attraction = 2; options->liquid_damping_mult = 1.0; options->expansion_iterations = 200; options->expansion_temperature = 2000; options->expansion_attraction = 10; options->expansion_damping_mult = 1.0; options->cooldown_iterations = 200; options->cooldown_temperature = 2000; options->cooldown_attraction = 1; options->cooldown_damping_mult = .1; options->crunch_iterations = 200; options->crunch_temperature = 250; options->crunch_attraction = 1; options->crunch_damping_mult = 0.25; options->simmer_iterations = 100; options->simmer_temperature = 250; options->simmer_attraction = .5; options->simmer_damping_mult = 0; break; case IGRAPH_LAYOUT_DRL_REFINE: options->init_iterations = 0; options->init_temperature = 50; options->init_attraction = .5; options->init_damping_mult = 0; options->liquid_iterations = 0; options->liquid_temperature = 2000; options->liquid_attraction = 2; options->liquid_damping_mult = 1.0; options->expansion_iterations = 50; options->expansion_temperature = 500; options->expansion_attraction = .1; options->expansion_damping_mult = .25; options->cooldown_iterations = 50; options->cooldown_temperature = 200; options->cooldown_attraction = 1; options->cooldown_damping_mult = .1; options->crunch_iterations = 50; options->crunch_temperature = 250; options->crunch_attraction = 1; options->crunch_damping_mult = 0.25; options->simmer_iterations = 0; options->simmer_temperature = 250; options->simmer_attraction = .5; options->simmer_damping_mult = 0; break; case IGRAPH_LAYOUT_DRL_FINAL: options->init_iterations = 0; options->init_temperature = 50; options->init_attraction = .5; options->init_damping_mult = 0; options->liquid_iterations = 0; options->liquid_temperature = 2000; options->liquid_attraction = 2; options->liquid_damping_mult = 1.0; options->expansion_iterations = 50; options->expansion_temperature = 50; options->expansion_attraction = .1; options->expansion_damping_mult = .25; options->cooldown_iterations = 50; options->cooldown_temperature = 200; options->cooldown_attraction = 1; options->cooldown_damping_mult = .1; options->crunch_iterations = 50; options->crunch_temperature = 250; options->crunch_attraction = 1; options->crunch_damping_mult = 0.25; options->simmer_iterations = 25; options->simmer_temperature = 250; options->simmer_attraction = .5; options->simmer_damping_mult = 0; default: IGRAPH_ERROR("Unkown DrL template", IGRAPH_EINVAL); break; } return 0; } /** * \function igraph_layout_drl * The DrL layout generator * * This function implements the force-directed DrL layout generator. * Please see more in the following technical report: Martin, S., * Brown, W.M., Klavans, R., Boyack, K.W., DrL: Distributed Recursive * (Graph) Layout. SAND Reports, 2008. 2936: p. 1-10. * \param graph The input graph. * \param use_seed Logical scalar, if true, then the coordinates * supplied in the \p res argument are used as starting points. * \param res Pointer to a matrix, the result layout is stored * here. It will be resized as needed. * \param options The parameters to pass to the layout generator. * \param weights Edge weights, pointer to a vector. If this is a null * pointer then every edge will have the same weight. * \param fixed Pointer to a logical vector, or a null pointer. This * can be used to fix the position of some vertices. Vertices for * which it is true will not be moved, but stay at the coordinates * given in the \p res matrix. This argument is ignored if it is a * null pointer or if use_seed is false. * \return Error code. * * Time complexity: ???. */ int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t use_seed, igraph_layout_drl_options_t *options, const igraph_vector_t *weights, const igraph_vector_bool_t *fixed) { RNG_BEGIN(); drl::graph neighbors(graph, options, weights); neighbors.init_parms(options); if (use_seed) { IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2)); neighbors.read_real(res, fixed); } neighbors.draw_graph(res); RNG_END(); return 0; } igraph/src/igraph_cocitation.h0000644000176000001440000000531312325527073016212 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_COCITATION_H #define IGRAPH_COCITATION_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_datatype.h" #include "igraph_iterators.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Cocitation and other similarity measures */ /* -------------------------------------------------- */ int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids); int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids); int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops); int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); int igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res, const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops); int igraph_similarity_inverse_log_weighted(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode); __END_DECLS #endif igraph/src/foreign.c0000644000176000001440000033650112325527073014156 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_foreign.h" #include "config.h" #include "igraph_math.h" #include "igraph_gml_tree.h" #include "igraph_memory.h" #include "igraph_attributes.h" #include "igraph_interface.h" #include "igraph_interrupt_internal.h" #include "igraph_constructors.h" #include "igraph_types_internal.h" #include /* isspace */ #include #include /** * \section about_loadsave * * These functions can write a graph to a file, or read a graph * from a file. * * Note that as \a igraph uses the traditional C streams, it is * possible to read/write files from/to memory, at least on GNU * operating systems supporting \quote non-standard\endquote streams. */ /** * \ingroup loadsave * \function igraph_read_graph_edgelist * \brief Reads an edge list from a file and creates a graph. * * * This format is simply a series of even number integers separated by * whitespace. The one edge (ie. two integers) per line format is thus * not required (but recommended for readability). Edges of directed * graphs are assumed to be in from, to order. * \param graph Pointer to an uninitialized graph object. * \param instream Pointer to a stream, it should be readable. * \param n The number of vertices in the graph. If smaller than the * largest integer in the file it will be ignored. It is thus * safe to supply zero here. * \param directed Logical, if true the graph is directed, if false it * will be undirected. * \return Error code: * \c IGRAPH_PARSEERROR: if there is a * problem reading the file, or the file is syntactically * incorrect. * * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges. It is assumed that * reading an integer requires O(1) * time. */ int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, igraph_integer_t n, igraph_bool_t directed) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; long int from, to; int c; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, 100)); /* skip all whitespace */ do { c = getc (instream); } while (isspace (c)); ungetc (c, instream); while (!feof(instream)) { int read; IGRAPH_ALLOW_INTERRUPTION(); read=fscanf(instream, "%li", &from); if (read != 1) { IGRAPH_ERROR("parsing edgelist file failed", IGRAPH_PARSEERROR); } read=fscanf(instream, "%li", &to); if (read != 1) { IGRAPH_ERROR("parsing edgelist file failed", IGRAPH_PARSEERROR); } IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); /* skip all whitespace */ do { c = getc (instream); } while (isspace (c)); ungetc (c, instream); } IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } #include "foreign-ncol-header.h" int igraph_ncol_yylex_init_extra (igraph_i_ncol_parsedata_t* user_defined, void* scanner); int igraph_ncol_yylex_destroy (void *scanner ); int igraph_ncol_yyparse (igraph_i_ncol_parsedata_t* context); void igraph_ncol_yyset_in (FILE * in_str, void* yyscanner ); /** * \ingroup loadsave * \function igraph_read_graph_ncol * \brief Reads a .ncol file used by LGL. * * Also useful for creating graphs from \quote named\endquote (and * optionally weighted) edge lists. * * * This format is used by the Large Graph Layout program * (http://lgl.sourceforge.net), and it is simply a * symbolic weighted edge list. It is a simple text file with one edge * per line. An edge is defined by two symbolic vertex names separated * by whitespace. (The symbolic vertex names themselves cannot contain * whitespace. They might follow by an optional number, this will be * the weight of the edge; the number can be negative and can be in * scientific notation. If there is no weight specified to an edge it * is assumed to be zero. * * * The resulting graph is always undirected. * LGL cannot deal with files which contain multiple or loop edges, * this is however not checked here, as \a igraph is happy with * these. * \param graph Pointer to an uninitialized graph object. * \param instream Pointer to a stream, it should be readable. * \param predefnames Pointer to the symbolic names of the vertices in * the file. If \c NULL is given here then vertex ids will be * assigned to vertex names in the order of their appearance in * the \c .ncol file. If it is not \c NULL and some unknown * vertex names are found in the \c .ncol file then new vertex * ids will be assigned to them. * \param names Logical value, if TRUE the symbolic names of the * vertices will be added to the graph as a vertex attribute * called \quote name\endquote. * \param weights Whether to add the weights of the edges to the * graph as an edge attribute called \quote weight\endquote. * \c IGRAPH_ADD_WEIGHTS_YES adds the weights (even if they * are not present in the file, in this case they are assumed * to be zero). \c IGRAPH_ADD_WEIGHTS_NO does not add any * edge attribute. \c IGRAPH_ADD_WEIGHTS_IF_PRESENT adds the * attribute if and only if there is at least one explicit * edge weight in the input file. * \param directed Whether to create a directed graph. As this format * was originally used only for undirected graphs there is no * information in the file about the directedness of the graph. * Set this parameter to \c IGRAPH_DIRECTED or \c * IGRAPH_UNDIRECTED to create a directed or undirected graph. * \return Error code: * \c IGRAPH_PARSEERROR: if there is a * problem reading * the file, or the file is syntactically incorrect. * * Time complexity: * O(|V|+|E|log(|V|)) if we neglect * the time required by the parsing. As usual * |V| is the number of vertices, * while |E| is the number of edges. * * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol() */ int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, igraph_strvector_t *predefnames, igraph_bool_t names, igraph_add_weights_t weights, igraph_bool_t directed) { igraph_vector_t edges, ws; igraph_trie_t trie=IGRAPH_TRIE_NULL; long int no_predefined=0; igraph_vector_ptr_t name, weight; igraph_vector_ptr_t *pname=0, *pweight=0; igraph_attribute_record_t namerec, weightrec; const char *namestr="name", *weightstr="weight"; igraph_i_ncol_parsedata_t context; IGRAPH_CHECK(igraph_empty(graph, 0, directed)); IGRAPH_FINALLY(igraph_destroy, graph); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_TRIE_INIT_FINALLY(&trie, names); IGRAPH_VECTOR_INIT_FINALLY(&ws, 0); /* Add the predefined names, if any */ if (predefnames != 0) { long int i, id, n; char *key; n=no_predefined=igraph_strvector_size(predefnames); for (i=0; i.lgl file * * * The .lgl format is used by the Large Graph * Layout visualization software * (http://lgl.sourceforge.net), it can * describe undirected optionally weighted graphs. From the LGL * manual: * * \blockquote The second format is the LGL file format * (.lgl file * suffix). This is yet another graph file format that tries to be as * stingy as possible with space, yet keeping the edge file in a human * readable (not binary) format. The format itself is like the * following: * \verbatim # vertex1name vertex2name [optionalWeight] vertex3name [optionalWeight] \endverbatim * Here, the first vertex of an edge is preceded with a pound sign * '#'. Then each vertex that shares an edge with that vertex is * listed one per line on subsequent lines. \endblockquote * * * LGL cannot handle loop and multiple edges or directed graphs, but * in \a igraph it is not an error to have multiple and loop edges. * \param graph Pointer to an uninitialized graph object. * \param instream A stream, it should be readable. * \param names Logical value, if TRUE the symbolic names of the * vertices will be added to the graph as a vertex attribute * called \quote name\endquote. * \param weights Whether to add the weights of the edges to the * graph as an edge attribute called \quote weight\endquote. * \c IGRAPH_ADD_WEIGHTS_YES adds the weights (even if they * are not present in the file, in this case they are assumed * to be zero). \c IGRAPH_ADD_WEIGHTS_NO does not add any * edge attribute. \c IGRAPH_ADD_WEIGHTS_IF_PRESENT adds the * attribute if and only if there is at least one explicit * edge weight in the input file. * \param directed Whether to create a directed graph. As this format * was originally used only for undirected graphs there is no * information in the file about the directedness of the graph. * Set this parameter to \c IGRAPH_DIRECTED or \c * IGRAPH_UNDIRECTED to create a directed or undirected graph. * \return Error code: * \c IGRAPH_PARSEERROR: if there is a * problem reading the file, or the file is syntactically * incorrect. * * Time complexity: * O(|V|+|E|log(|V|)) if we neglect * the time required by the parsing. As usual * |V| is the number of vertices, * while |E| is the number of edges. * * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl() * * \example examples/simple/igraph_read_graph_lgl.c */ int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, igraph_bool_t names, igraph_add_weights_t weights, igraph_bool_t directed) { igraph_vector_t edges=IGRAPH_VECTOR_NULL, ws=IGRAPH_VECTOR_NULL; igraph_trie_t trie=IGRAPH_TRIE_NULL; igraph_vector_ptr_t name, weight; igraph_vector_ptr_t *pname=0, *pweight=0; igraph_attribute_record_t namerec, weightrec; const char *namestr="name", *weightstr="weight"; igraph_i_lgl_parsedata_t context; IGRAPH_VECTOR_INIT_FINALLY(&ws, 0); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_TRIE_INIT_FINALLY(&trie, names); context.has_weights=0; context.vector=&edges; context.weights=&ws; context.trie=≜ context.eof=0; igraph_lgl_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_lgl_yylex_destroy, context.scanner); igraph_lgl_yyset_in(instream, context.scanner); if (igraph_lgl_yyparse(&context)) { if (context.errmsg) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); } else { IGRAPH_ERROR("Cannot read LGL file", IGRAPH_PARSEERROR); } } IGRAPH_CHECK(igraph_empty(graph, 0, directed)); IGRAPH_FINALLY(igraph_destroy, graph); if (names) { const igraph_strvector_t *namevec; IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &name); pname=&name; igraph_trie_getkeys(&trie, &namevec); /* dirty */ namerec.name=namestr; namerec.type=IGRAPH_ATTRIBUTE_STRING; namerec.value=namevec; VECTOR(name)[0]=&namerec; } if (weights == IGRAPH_ADD_WEIGHTS_YES || (weights == IGRAPH_ADD_WEIGHTS_IF_PRESENT && context.has_weights)) { IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &weight); pweight=&weight; weightrec.name=weightstr; weightrec.type=IGRAPH_ATTRIBUTE_NUMERIC; weightrec.value=&ws; VECTOR(weight)[0]=&weightrec; } IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) igraph_trie_size(&trie), pname)); IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight)); if (pweight) { igraph_vector_ptr_destroy(pweight); IGRAPH_FINALLY_CLEAN(1); } if (pname) { igraph_vector_ptr_destroy(pname); IGRAPH_FINALLY_CLEAN(1); } igraph_trie_destroy(&trie); igraph_vector_destroy(&edges); igraph_vector_destroy(&ws); igraph_lgl_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(5); return 0; } #include "foreign-pajek-header.h" int igraph_pajek_yylex_init_extra(igraph_i_pajek_parsedata_t* user_defined, void* scanner); int igraph_pajek_yylex_destroy (void *scanner ); int igraph_pajek_yyparse (igraph_i_pajek_parsedata_t* context); void igraph_pajek_yyset_in (FILE * in_str, void* yyscanner ); /** * \function igraph_read_graph_pajek * \brief Reads a file in Pajek format * * \param graph Pointer to an uninitialized graph object. * \param file An already opened file handler. * \return Error code. * * * Only a subset of the Pajek format is implemented. This is partially * because this format is not very well documented, but also because * igraph does not support some Pajek features, like * multigraphs. * * * Starting from version 0.6.1 igraph reads bipartite (two-mode) * graphs from Pajek files and add the \c type vertex attribute for them. * Warnings are given for invalid edges, i.e. edges connecting * vertices of the same type. * * * The list of the current limitations: * \olist * \oli Only .net files are supported, Pajek * project files (.paj) are not. These might be * supported in the future if there is need for it. * \oli Time events networks are not supported. * \oli Hypergraphs (ie. graphs with non-binary edges) are not * supported. * \oli Graphs with both directed and non-directed edges are not * supported, are they cannot be represented in * igraph. * \oli Only Pajek networks are supported, permutations, hierarchies, * clusters and vectors are not. * \oli Graphs with multiple edge sets are not supported. * \endolist * * * If there are attribute handlers installed, * igraph also reads the vertex and edge attributes * from the file. Most attributes are renamed to be more informative: * `\c color' instead of `\c c', `\c xfact' instead of `\c x_fact', * `\c yfact' instead of `y_fact', `\c labeldist' instead of `\c lr', * `\c labeldegree2' instead of `\c lphi', `\c framewidth' instead of `\c bw', * `\c fontsize' * instead of `\c fos', `\c rotation' instead of `\c phi', `\c radius' instead * of `\c r', * `\c diamondratio' instead of `\c q', `\c labeldegree' instead of `\c la', * `\c vertexsize' * instead of `\c size', `\c color' instead of `\c ic', `\c framecolor' instead of * `\c bc', `\c labelcolor' instead of `\c lc', these belong to vertices. * * * Edge attributes are also renamed, `\c s' to `\c arrowsize', `\c w' * to `\c edgewidth', `\c h1' to `\c hook1', `\c h2' to `\c hook2', * `\c a1' to `\c angle1', `\c a2' to `\c angle2', `\c k1' to * `\c velocity1', `\c k2' to `\c velocity2', `\c ap' to `\c * arrowpos', `\c lp' to `\c labelpos', `\c lr' to * `\c labelangle', `\c lphi' to `\c labelangle2', `\c la' to `\c * labeldegree', `\c fos' to * `\c fontsize', `\c a' to `\c arrowtype', `\c p' to `\c * linepattern', `\c l' to `\c label', `\c lc' to * `\c labelcolor', `\c c' to `\c color'. * * * In addition the following vertex attributes might be added: `\c id' * if there are vertex ids in the file, `\c x' and `\c y' or `\c x' * and `\c y' and `\c z' if there are vertex coordinates in the file. * * The `\c weight' edge attribute might be * added if there are edge weights present. * * * See the pajek homepage: * http://vlado.fmf.uni-lj.si/pub/networks/pajek/ for more info on * Pajek and the Pajek manual: * http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/pajekman.pdf for * information on the Pajek file format. * * * Time complexity: O(|V|+|E|+|A|), |V| is the number of vertices, |E| * the number of edges, |A| the number of attributes (vertex + edge) * in the graph if there are attribute handlers installed. * * \sa \ref igraph_write_graph_pajek() for writing Pajek files, \ref * igraph_read_graph_graphml() for reading GraphML files. * * \example examples/simple/foreign.c */ int igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { igraph_vector_t edges; igraph_trie_t vattrnames; igraph_vector_ptr_t vattrs; igraph_trie_t eattrnames; igraph_vector_ptr_t eattrs; long int i, j; igraph_i_pajek_parsedata_t context; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&vattrs, 0); IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&eattrs, 0); context.vector=&edges; context.mode=0; context.vcount=-1; context.vertexid=0; context.vertex_attribute_names=&vattrnames; context.vertex_attributes=&vattrs; context.edge_attribute_names=&eattrnames; context.edge_attributes=&eattrs; context.actedge=0; context.eof=0; igraph_pajek_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_pajek_yylex_destroy, context.scanner); igraph_pajek_yyset_in(instream, context.scanner); if (igraph_pajek_yyparse(&context)) { if (context.errmsg) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); } else { IGRAPH_ERROR("Cannot read Pajek file", IGRAPH_PARSEERROR); } } if (context.vcount < 0) IGRAPH_ERROR("invalid vertex count in Pajek file", IGRAPH_EINVAL); if (context.vcount2 < 0) IGRAPH_ERROR("invalid 2-mode vertex count in Pajek file", IGRAPH_EINVAL); for (i=0; itype==IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec=(igraph_vector_t*)rec->value; long int origsize=igraph_vector_size(vec); igraph_vector_resize(vec, context.actedge); for (j=origsize; jtype==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strvec=(igraph_strvector_t*)rec->value; long int origsize=igraph_strvector_size(strvec); igraph_strvector_resize(strvec, context.actedge); for (j=origsize; jtype == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec=(igraph_vector_t*) rec->value; igraph_vector_destroy(vec); igraph_Free(vec); } else if (rec->type==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strvec=(igraph_strvector_t *)rec->value; igraph_strvector_destroy(strvec); igraph_Free(strvec); } igraph_free( (char*)(rec->name)); igraph_Free(rec); } for (i=0; itype == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec=(igraph_vector_t*) rec->value; igraph_vector_destroy(vec); igraph_Free(vec); } else if (rec->type==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strvec=(igraph_strvector_t *)rec->value; igraph_strvector_destroy(strvec); igraph_Free(strvec); } igraph_free( (char*)(rec->name)); igraph_Free(rec); } igraph_vector_destroy(&edges); igraph_vector_ptr_destroy(&eattrs); igraph_trie_destroy(&eattrnames); igraph_vector_ptr_destroy(&vattrs); igraph_trie_destroy(&vattrnames); igraph_pajek_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(7); return 0; } /** * \function igraph_read_graph_dimacs * \brief Read a graph in DIMACS format. * * This function reads the DIMACS file format, more specifically the * version for network flow problems, see the files at * ftp://dimacs.rutgers.edu/pub/netflow/general-info/ * * * This is a line-oriented text file (ASCII) format. The first * character of each line defines the type of the line. If the first * character is c the line is a comment line and it is * ignored. There is one problem line (p in the file, it * must appear before any node and arc descriptor lines. The problem * line has three fields separated by spaces: the problem type * (min, max or asn), the * number of vertices and number of edges in the graph. * Exactly two node identification lines are expected * (n), one for the source, one for the target vertex. * These have two fields: the id of the vertex and the type of the * vertex, either s (=source) or t * (=target). Arc lines start with a and have three * fields: the source vertex, the target vertex and the edge capacity. * * * Vertex ids are numbered from 1. * \param graph Pointer to an uninitialized graph object. * \param instream The file to read from. * \param source Pointer to an integer, the id of the source node will * be stored here. (The igraph vertex id, which is one less than * the actual number in the file.) It is ignored if * NULL. * \param target Pointer to an integer, the (igraph) id of the target * node will be stored here. It is ignored if NULL. * \param capacity Pointer to an initialized vector, the capacity of * the edges will be stored here if not NULL. * \param directed Boolean, whether to create a directed graph. * \return Error code. * * Time complexity: O(|V|+|E|+c), the number of vertices plus the * number of edges, plus the size of the file in characters. * * \sa \ref igraph_write_graph_dimacs() */ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, igraph_strvector_t *problem, igraph_vector_t *label, igraph_integer_t *source, igraph_integer_t *target, igraph_vector_t *capacity, igraph_bool_t directed) { igraph_vector_t edges; long int no_of_nodes=-1; long int no_of_edges=-1; long int tsource=-1; long int ttarget=-1; char prob[21]; char c; int problem_type=0; #define PROBLEM_EDGE 1 #define PROBLEM_MAX 2 IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (capacity) { igraph_vector_clear(capacity); } while (!feof(instream)) { int read; char str[3]; IGRAPH_ALLOW_INTERRUPTION(); read=fscanf(instream, "%2c", str); if (feof(instream)) { break; } if (read != 1) { IGRAPH_ERROR("parsing dimacs file failed", IGRAPH_PARSEERROR); } switch (str[0]) { long int tmp, tmp2; long int from, to; igraph_real_t cap; case 'c': /* comment */ break; case 'p': if (no_of_nodes != -1) { IGRAPH_ERROR("reading dimacs file failed, double 'p' line", IGRAPH_PARSEERROR); } read=fscanf(instream, "%20s %li %li", prob, &no_of_nodes, &no_of_edges); if (read != 3) { IGRAPH_ERROR("reading dimacs file failed", IGRAPH_PARSEERROR); } if (!strcmp(prob, "edge")) { /* edge list */ problem_type=PROBLEM_EDGE; if (label) { long int i; IGRAPH_CHECK(igraph_vector_resize(label, no_of_nodes)); for (i=0; i * * \blockquote * The graphs are stored in a compact binary format, one graph per * file. The file is composed of 16 bit words, which are represented * using the so-called little-endian convention, i.e. the least * significant byte of the word is stored first. * * * Then, for each node, the file contains the list of edges coming * out of the node itself. The list is represented by a word encoding * its length, followed by a word for each edge, representing the * destination node of the edge. Node numeration is 0-based, so the * first node of the graph has index 0. \endblockquote * * * Only unlabelled graphs are implemented. * \param graph Pointer to an uninitialized graph object. * \param instream The stream to read from. * \param directed Logical scalar, whether to create a directed graph. * \return Error code. * * Time complexity: O(|V|+|E|), the number of vertices plus the * number of edges. * * \example examples/simple/igraph_read_graph_graphdb.c */ int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, igraph_bool_t directed) { igraph_vector_t edges; long int nodes; long int i, j; igraph_bool_t end=0; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); nodes=igraph_i_read_graph_graphdb_getword(instream); if (nodes<0) { IGRAPH_ERROR("Can't read from file", IGRAPH_EFILE); } for (i=0; !end && itype == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *value=(igraph_vector_t*)atrec->value; if (value != 0) { igraph_vector_destroy(value); igraph_Free(value); } } else { igraph_strvector_t *value=(igraph_strvector_t*)atrec->value; if (value != 0) { igraph_strvector_destroy(value); igraph_Free(value); } } igraph_Free(atrec->name); igraph_Free(atrec); } igraph_vector_ptr_destroy(vec); } } igraph_real_t igraph_i_gml_toreal(igraph_gml_tree_t *node, long int pos) { igraph_real_t value=0.0; int type=igraph_gml_tree_type(node, pos); switch (type) { case IGRAPH_I_GML_TREE_INTEGER: value=igraph_gml_tree_get_integer(node, pos); break; case IGRAPH_I_GML_TREE_REAL: value=igraph_gml_tree_get_real(node, pos); break; default: IGRAPH_ERROR("Internal error while parsing GML file", IGRAPH_FAILURE); break; } return value; } const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, long int pos) { int type=igraph_gml_tree_type(node, pos); char tmp[256]; const char *p=tmp; long int i; igraph_real_t d; switch (type) { case IGRAPH_I_GML_TREE_INTEGER: i=igraph_gml_tree_get_integer(node, pos); snprintf(tmp, sizeof(tmp)/sizeof(char), "%li", i); break; case IGRAPH_I_GML_TREE_REAL: d=igraph_gml_tree_get_real(node, pos); igraph_real_snprintf_precise(tmp, sizeof(tmp)/sizeof(char), d); break; case IGRAPH_I_GML_TREE_STRING: p=igraph_gml_tree_get_string(node, pos); break; default: break; } return p; } /** * \function igraph_read_graph_gml * \brief Read a graph in GML format. * * GML is a simple textual format, see * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html for details. * * * Although all syntactically correct GML can be parsed, * we implement only a subset of this format, some attributes might be * ignored. Here is a list of all the differences: * \olist * \oli Only node and edge attributes are * used, and only if they have a simple type: integer, real or * string. So if an attribute is an array or a record, then it is * ignored. This is also true if only some values of the * attribute are complex. * \oli Top level attributes except for Version and the * first graph attribute are completely ignored. * \oli Graph attributes except for node and * edge are completely ignored. * \oli There is no maximum line length. * \oli There is no maximum keyword length. * \oli Character entities in strings are not interpreted. * \oli We allow inf (infinity) and nan * (not a number) as a real number. This is case insensitive, so * nan, NaN and NAN are equal. * \endolist * * Please contact us if you cannot live with these * limitations of the GML parser. * \param graph Pointer to an uninitialized graph object. * \param instream The stream to read the GML file from. * \return Error code. * * Time complexity: should be proportional to the length of the file. * * \sa \ref igraph_read_graph_graphml() for a more modern format, * \ref igraph_write_graph_gml() for writing GML files. * * \example examples/simple/gml.c */ int igraph_read_graph_gml(igraph_t *graph, FILE *instream) { long int i, p; long int no_of_nodes=0, no_of_edges=0; igraph_trie_t trie; igraph_vector_t edges; igraph_bool_t directed=IGRAPH_UNDIRECTED; igraph_gml_tree_t *gtree; long int gidx; igraph_trie_t vattrnames; igraph_trie_t eattrnames; igraph_trie_t gattrnames; igraph_vector_ptr_t gattrs=IGRAPH_VECTOR_PTR_NULL, vattrs=IGRAPH_VECTOR_PTR_NULL, eattrs=IGRAPH_VECTOR_PTR_NULL; igraph_vector_ptr_t *attrs[3]; long int edgeptr=0; igraph_i_gml_parsedata_t context; attrs[0]=&gattrs; attrs[1]=&vattrs; attrs[2]=&eattrs; context.eof=0; context.tree=0; igraph_gml_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_gml_yylex_destroy, context.scanner); igraph_gml_yyset_in(instream, context.scanner); i=igraph_gml_yyparse(&context); if (i != 0) { if (context.errmsg) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); } else { IGRAPH_ERROR("Cannot read GML file", IGRAPH_PARSEERROR); } } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); /* Check version, if present, integer and not '1' then ignored */ i=igraph_gml_tree_find(context.tree, "Version", 0); if (i>=0 && igraph_gml_tree_type(context.tree, i)==IGRAPH_I_GML_TREE_INTEGER && igraph_gml_tree_get_integer(context.tree, i) != 1) { igraph_gml_tree_destroy(context.tree); IGRAPH_ERROR("Unknown GML version", IGRAPH_UNIMPLEMENTED); /* RETURN HERE!!!! */ } /* get the graph */ gidx=igraph_gml_tree_find(context.tree, "graph", 0); if (gidx==-1) { IGRAPH_ERROR("No 'graph' object in GML file", IGRAPH_PARSEERROR); } if (igraph_gml_tree_type(context.tree, gidx) != IGRAPH_I_GML_TREE_TREE) { IGRAPH_ERROR("Invalid type for 'graph' object in GML file", IGRAPH_PARSEERROR); } gtree=igraph_gml_tree_get_tree(context.tree, gidx); IGRAPH_FINALLY(igraph_i_gml_destroy_attrs, &attrs); igraph_vector_ptr_init(&gattrs, 0); igraph_vector_ptr_init(&vattrs, 0); igraph_vector_ptr_init(&eattrs, 0); IGRAPH_TRIE_INIT_FINALLY(&trie, 0); IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 0); IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 0); IGRAPH_TRIE_INIT_FINALLY(&gattrnames, 0); /* Is is directed? */ i=igraph_gml_tree_find(gtree, "directed", 0); if (i>=0 && igraph_gml_tree_type(gtree, i)==IGRAPH_I_GML_TREE_INTEGER) { if (igraph_gml_tree_get_integer(gtree, i) == 1) { directed=IGRAPH_DIRECTED; } } /* Now we go over all objects in the graph and collect the attribute names and types. Plus we collect node ids. We also do some checks. */ for (i=0; iname=strdup(name); if (type==IGRAPH_I_GML_TREE_INTEGER || type==IGRAPH_I_GML_TREE_REAL) { atrec->type=IGRAPH_ATTRIBUTE_NUMERIC; } else { atrec->type=IGRAPH_ATTRIBUTE_STRING; } } else { /* already seen, should we update type? */ igraph_attribute_record_t *atrec=VECTOR(vattrs)[trieid]; int type1=atrec->type; int type2=igraph_gml_tree_type(node, j); if (type1==IGRAPH_ATTRIBUTE_NUMERIC && type2==IGRAPH_I_GML_TREE_STRING) { atrec->type=IGRAPH_ATTRIBUTE_STRING; } } /* check id */ if (!hasid && !strcmp(name, "id")) { long int id; if (igraph_gml_tree_type(node, j) != IGRAPH_I_GML_TREE_INTEGER) { IGRAPH_ERROR("Non-integer node id in GML file", IGRAPH_PARSEERROR); } id=igraph_gml_tree_get_integer(node, j); snprintf(cname, sizeof(cname)/sizeof(char)-1, "%li", id); IGRAPH_CHECK(igraph_trie_get(&trie, cname, &id)); hasid=1; } } if (!hasid) { IGRAPH_ERROR("Node without 'id' while parsing GML file", IGRAPH_PARSEERROR); } } else if (!strcmp(name, "edge")) { igraph_gml_tree_t *edge; igraph_bool_t has_source=0, has_target=0; no_of_edges++; if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) { IGRAPH_ERROR("'edge' is not a list", IGRAPH_PARSEERROR); } edge=igraph_gml_tree_get_tree(gtree, i); has_source=has_target=0; for (j=0; jname=strdup(name); if (type==IGRAPH_I_GML_TREE_INTEGER || type==IGRAPH_I_GML_TREE_REAL) { atrec->type=IGRAPH_ATTRIBUTE_NUMERIC; } else { atrec->type=IGRAPH_ATTRIBUTE_STRING; } } else { /* already seen, should we update type? */ igraph_attribute_record_t *atrec=VECTOR(eattrs)[trieid]; int type1=atrec->type; int type2=igraph_gml_tree_type(edge, j); if (type1==IGRAPH_ATTRIBUTE_NUMERIC && type2==IGRAPH_I_GML_TREE_STRING) { atrec->type=IGRAPH_ATTRIBUTE_STRING; } } } } /* for */ if (!has_source) { IGRAPH_ERROR("No 'source' for edge in GML file", IGRAPH_PARSEERROR); } if (!has_target) { IGRAPH_ERROR("No 'target' for edge in GML file", IGRAPH_PARSEERROR); } } else { /* anything to do? Maybe add as graph attribute.... */ } } /* check vertex id uniqueness */ if (igraph_trie_size(&trie) != no_of_nodes) { IGRAPH_ERROR("Node 'id' not unique", IGRAPH_PARSEERROR); } /* now we allocate the vectors and strvectors for the attributes */ for (i=0; itype; if (type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *p=igraph_Calloc(1, igraph_vector_t); atrec->value=p; IGRAPH_CHECK(igraph_vector_init(p, no_of_nodes)); } else if (type == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *p=igraph_Calloc(1, igraph_strvector_t); atrec->value=p; IGRAPH_CHECK(igraph_strvector_init(p, no_of_nodes)); } else { IGRAPH_WARNING("A composite attribute ignored"); } } for (i=0; itype; if (type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *p=igraph_Calloc(1, igraph_vector_t); atrec->value=p; IGRAPH_CHECK(igraph_vector_init(p, no_of_edges)); } else if (type == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *p=igraph_Calloc(1, igraph_strvector_t); atrec->value=p; IGRAPH_CHECK(igraph_strvector_init(p, no_of_edges)); } else { IGRAPH_WARNING("A composite attribute ignored"); } } /* Ok, now the edges, attributes too */ IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges*2)); p=-1; while ( (p=igraph_gml_tree_find(gtree, "edge", p+1)) != -1) { igraph_gml_tree_t *edge; long int from, to, fromidx=0, toidx=0; char name[100]; long int j; edge=igraph_gml_tree_get_tree(gtree, p); for (j=0; jtype; if (type==IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *v=(igraph_vector_t *)atrec->value; VECTOR(*v)[edgeid]=igraph_i_gml_toreal(edge, j); } else if (type==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *v=(igraph_strvector_t *)atrec->value; const char *value=igraph_i_gml_tostring(edge, j); IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value)); } } } from=igraph_gml_tree_get_integer(edge, fromidx); to=igraph_gml_tree_get_integer(edge, toidx); snprintf(name, sizeof(name)/sizeof(char)-1, "%li", from); IGRAPH_CHECK(igraph_trie_get(&trie, name, &from)); snprintf(name, sizeof(name)/sizeof(char)-1, "%li", to); IGRAPH_CHECK(igraph_trie_get(&trie, name, &to)); if (igraph_trie_size(&trie) != no_of_nodes) { IGRAPH_ERROR("Unknown node id found at an edge", IGRAPH_PARSEERROR); } VECTOR(edges)[edgeptr++]=from; VECTOR(edges)[edgeptr++]=to; } /* and add vertex attributes */ for (i=0; itype; if (type==IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *v=(igraph_vector_t *)atrec->value; VECTOR(*v)[id]=igraph_i_gml_toreal(node, j); } else if (type==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *v=(igraph_strvector_t *)atrec->value; const char *value=igraph_i_gml_tostring(node, j); IGRAPH_CHECK(igraph_strvector_set(v, id, value)); } } } } igraph_gml_tree_destroy(context.tree); igraph_trie_destroy(&trie); igraph_trie_destroy(&gattrnames); igraph_trie_destroy(&vattrnames); igraph_trie_destroy(&eattrnames); IGRAPH_FINALLY_CLEAN(4); IGRAPH_CHECK(igraph_empty_attrs(graph, 0, directed, 0)); /* TODO */ IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) no_of_nodes, &vattrs)); IGRAPH_CHECK(igraph_add_edges(graph, &edges, &eattrs)); igraph_i_gml_destroy_attrs(attrs); igraph_vector_destroy(&edges); igraph_gml_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \ingroup loadsave * \function igraph_write_graph_edgelist * \brief Writes the edge list of a graph to a file. * * * One edge is written per line, separated by a single space. * For directed graphs edges are written in from, to order. * \param graph The graph object to write. * \param outstream Pointer to a stream, it should be writable. * \return Error code: * \c IGRAPH_EFILE if there is an error writing the * file. * * Time complexity: O(|E|), the * number of edges in the graph. It is assumed that writing an * integer to the file requires O(1) * time. */ int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { igraph_eit_t it; IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); while (!IGRAPH_EIT_END(it)) { igraph_integer_t from, to; int ret; igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); ret=fprintf(outstream, "%li %li\n", (long int) from, (long int) to); if (ret < 0) { IGRAPH_ERROR("Write error", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup loadsave * \function igraph_write_graph_ncol * \brief Writes the graph to a file in .ncol format * * * .ncol is a format used by LGL, see \ref * igraph_read_graph_ncol() for details. * * * Note that having multiple or loop edges in an * .ncol file breaks the LGL software but * \a igraph does not check for this condition. * \param graph The graph to write. * \param outstream The stream object to write to, it should be * writable. * \param names The name of the vertex attribute, if symbolic names * are written to the file. If not, supply 0 here. * \param weights The name of the edge attribute, if they are also * written to the file. If you don't want weights, supply 0 * here. * \return Error code: * \c IGRAPH_EFILE if there is an error writing the * file. * * Time complexity: O(|E|), the * number of edges. All file operations are expected to have time * complexity O(1). * * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl() */ int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, const char *names, const char *weights) { igraph_eit_t it; igraph_attribute_type_t nametype, weighttype; IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); /* Check if we have the names attribute */ if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, names)) { names=0; IGRAPH_WARNING("names attribute does not exists"); } if (names) { IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype, IGRAPH_ATTRIBUTE_VERTEX, names)); } if (names && nametype != IGRAPH_ATTRIBUTE_NUMERIC && nametype != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_WARNING("ignoring names attribute, unknown attribute type"); names=0; } /* Check the weights as well */ if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, weights)) { weights=0; IGRAPH_WARNING("weights attribute does not exists"); } if (weights) { IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype, IGRAPH_ATTRIBUTE_EDGE, weights)); } if (weights && weighttype != IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_WARNING("ignoring weights attribute, unknown attribute type"); weights=0; } if (names==0 && weights ==0) { /* No names, no weights */ while (!IGRAPH_EIT_END(it)) { igraph_integer_t from, to; int ret; igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); ret=fprintf(outstream, "%li %li\n", (long int) from, (long int) to); if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } } else if (weights==0) { /* No weights, but use names */ igraph_strvector_t nvec; IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, igraph_vss_all(), &nvec)); while (!IGRAPH_EIT_END(it)) { igraph_integer_t edge=IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret=0; char *str1, *str2; igraph_edge(graph, edge, &from, &to); igraph_strvector_get(&nvec, from, &str1); igraph_strvector_get(&nvec, to, &str2); ret=fprintf(outstream, "%s %s\n", str1, str2); if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_strvector_destroy(&nvec); IGRAPH_FINALLY_CLEAN(1); } else if (names==0) { /* No names but weights */ igraph_vector_t wvec; IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights, igraph_ess_all(IGRAPH_EDGEORDER_ID), &wvec)); while (!IGRAPH_EIT_END(it)) { igraph_integer_t edge=IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret1, ret2, ret3; igraph_edge(graph, edge, &from, &to); ret1=fprintf(outstream, "%li %li ", (long int)from, (long int)to); ret2=igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); ret3=fputc('\n', outstream); if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_vector_destroy(&wvec); IGRAPH_FINALLY_CLEAN(1); } else { /* Both names and weights */ igraph_strvector_t nvec; igraph_vector_t wvec; IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights, igraph_ess_all(IGRAPH_EDGEORDER_ID), &wvec)); IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, igraph_vss_all(), &nvec)); while (!IGRAPH_EIT_END(it)) { igraph_integer_t edge=IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret=0, ret2=0; char *str1, *str2; igraph_edge(graph, edge, &from, &to); igraph_strvector_get(&nvec, from, &str1); igraph_strvector_get(&nvec, to, &str2); ret=fprintf(outstream, "%s %s ", str1, str2); if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret=igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); ret2=fputc('\n', outstream); if (ret < 0 || ret2 == EOF) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_strvector_destroy(&nvec); igraph_vector_destroy(&wvec); IGRAPH_FINALLY_CLEAN(2); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup loadsave * \function igraph_write_graph_lgl * \brief Writes the graph to a file in .lgl format * * * .lgl is a format used by LGL, see \ref * igraph_read_graph_lgl() for details. * * * Note that having multiple or loop edges in an * .lgl file breaks the LGL software but \a igraph * does not check for this condition. * \param graph The graph to write. * \param outstream The stream object to write to, it should be * writable. * \param names The name of the vertex attribute, if symbolic names * are written to the file. If not supply 0 here. * \param weights The name of the edge attribute, if they are also * written to the file. If you don't want weights supply 0 * here. * \param isolates Logical, if TRUE isolated vertices are also written * to the file. If FALSE they will be omitted. * \return Error code: * \c IGRAPH_EFILE if there is an error * writing the file. * * Time complexity: O(|E|), the * number of edges if \p isolates is * FALSE, O(|V|+|E|) otherwise. All * file operations are expected to have time complexity * O(1). * * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol() * * \example examples/simple/igraph_write_graph_lgl.c */ int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, const char *names, const char *weights, igraph_bool_t isolates) { igraph_eit_t it; long int actvertex=-1; igraph_attribute_type_t nametype, weighttype; IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); /* Check if we have the names attribute */ if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, names)) { names=0; IGRAPH_WARNING("names attribute does not exists"); } if (names) { IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype, IGRAPH_ATTRIBUTE_VERTEX, names)); } if (names && nametype != IGRAPH_ATTRIBUTE_NUMERIC && nametype != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_WARNING("ignoring names attribute, unknown attribute type"); names=0; } /* Check the weights as well */ if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, weights)) { weights=0; IGRAPH_WARNING("weights attribute does not exists"); } if (weights) { IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype, IGRAPH_ATTRIBUTE_EDGE, weights)); } if (weights && weighttype != IGRAPH_ATTRIBUTE_NUMERIC && weighttype != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_WARNING("ignoring weights attribute, unknown attribute type"); weights=0; } if (names==0 && weights==0) { /* No names, no weights */ while (!IGRAPH_EIT_END(it)) { igraph_integer_t from, to; int ret; igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); if (from==actvertex) { ret=fprintf(outstream, "%li\n", (long int)to); } else { actvertex=from; ret=fprintf(outstream, "# %li\n%li\n", (long int)from, (long int)to); } if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } } else if (weights==0) { /* No weights but use names */ igraph_strvector_t nvec; IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, igraph_vss_all(), &nvec)); while (!IGRAPH_EIT_END(it)) { igraph_integer_t edge=IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret=0; char *str1, *str2; igraph_edge(graph, edge, &from, &to); igraph_strvector_get(&nvec, to, &str2); if (from==actvertex) { ret=fprintf(outstream, "%s\n", str2); } else { actvertex=from; igraph_strvector_get(&nvec, from, &str1); ret=fprintf(outstream, "# %s\n%s\n", str1, str2); } if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } IGRAPH_FINALLY_CLEAN(1); } else if (names==0) { igraph_strvector_t wvec; IGRAPH_CHECK(igraph_strvector_init(&wvec, igraph_ecount(graph))); IGRAPH_FINALLY(igraph_strvector_destroy, &wvec); IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, weights, igraph_ess_all(IGRAPH_EDGEORDER_ID), &wvec)); /* No names but weights */ while (!IGRAPH_EIT_END(it)) { igraph_integer_t edge=IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret=0; char *str1; igraph_edge(graph, edge, &from, &to); igraph_strvector_get(&wvec, edge, &str1); if (from==actvertex) { ret=fprintf(outstream, "%li %s\n", (long)to, str1); } else { actvertex=from; ret=fprintf(outstream, "# %li\n%li %s\n", (long)from, (long)to, str1); } if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_strvector_destroy(&wvec); IGRAPH_FINALLY_CLEAN(1); } else { /* Both names and weights */ igraph_strvector_t nvec, wvec; IGRAPH_CHECK(igraph_strvector_init(&wvec, igraph_ecount(graph))); IGRAPH_FINALLY(igraph_strvector_destroy, &wvec); IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, weights, igraph_ess_all(IGRAPH_EDGEORDER_ID), &wvec)); IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, igraph_vss_all(), &nvec)); while (!IGRAPH_EIT_END(it)) { igraph_integer_t edge=IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret=0; char *str1, *str2, *str3; igraph_edge(graph, edge, &from, &to); igraph_strvector_get(&nvec, to, &str2); igraph_strvector_get(&wvec, edge, &str3); if (from==actvertex) { ret=fprintf(outstream, "%s ", str2); } else { actvertex=from; igraph_strvector_get(&nvec, from, &str1); ret=fprintf(outstream, "# %s\n%s ", str1, str2); } if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret=fprintf(outstream, "%s\n", str3); if (ret<0) { IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_strvector_destroy(&nvec); igraph_strvector_destroy(&wvec); IGRAPH_FINALLY_CLEAN(2); } if (isolates) { long int nov=igraph_vcount(graph); long int i; int ret=0; igraph_vector_t deg; igraph_strvector_t nvec; char *str; IGRAPH_VECTOR_INIT_FINALLY(°, 1); IGRAPH_CHECK(igraph_strvector_init(&nvec, 1)); IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); for (i=0; i * The Pajek vertex and edge parameters (like color) are determined by * the attributes of the vertices and edges, of course this requires * an attribute handler to be installed. The names of the * corresponding vertex and edge attributes are listed at \ref * igraph_read_graph_pajek(), eg. the `\c color' vertex attributes * determines the color (`\c c' in Pajek) parameter. * * * As of version 0.6.1 igraph writes bipartite graphs into Pajek files * correctly, i.e. they will be also bipartite when read into Pajek. * As Pajek is less flexible for bipartite graphs (the numeric ids of * the vertices must be sorted according to vertex type), igraph might * need to reorder the vertices when writing a bipartite Pajek file. * This effectively means that numeric vertex ids usually change when * a bipartite graph is written to a Pajek file, and then read back * into igraph. * \param graph The graph object to write. * \param outstream The file to write to. It should be opened and * writable. Make sure that you open the file in binary format if you use MS Windows, * otherwise end of line characters will be messed up. (igraph will be able * to read back these messed up files, but Pajek won't.) * \return Error code. * * Time complexity: O(|V|+|E|+|A|), |V| is the number of vertices, |E| * is the number of edges, |A| the number of attributes (vertex + * edge) in the graph if there are attribute handlers installed. * * \sa \ref igraph_read_graph_pajek() for reading Pajek graphs, \ref * igraph_write_graph_graphml() for writing a graph in GraphML format, * this suites igraph graphs better. * * \example examples/simple/igraph_write_graph_pajek.c */ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { long int no_of_nodes=igraph_vcount(graph); long int i, j; igraph_attribute_type_t vtypes[V_LAST], etypes[E_LAST]; igraph_bool_t write_vertex_attrs=0; /* Same order as the #define's */ const char *vnames[] = { "id", "x", "y", "z", "shape", "xfact", "yfact", "", "", "", "", "", "", "", "", "", "labeldist", "labeldegree2", "framewidth", "fontsize", "rotation", "radius", "diamondratio", "labeldegree", "vertexsize", "font", "url", "color", "framecolor", "labelcolor" }; const char *vnumnames[] = { "xfact", "yfact", "labeldist", "labeldegree2", "framewidth", "fontsize", "rotation", "radius", "diamondratio", "labeldegree", "vertexsize" }; const char *vnumnames2[]= { "x_fact", "y_fact", "lr", "lphi", "bw", "fos", "phi", "r", "q", "la", "size" }; const char *vstrnames[] = { "font", "url", "color", "framecolor", "labelcolor" }; const char *vstrnames2[]= { "font", "url", "ic", "bc", "lc" }; const char *enames[] = { "weight", "", "", "", "arrowsize", "edgewidth", "hook1", "hook2", "angle1", "angle2", "velocity1", "velocity2", "arrowpos", "labelpos", "labelangle", "labelangle2", "labeldegree", "fontsize", "arrowtype", "linepattern", "label", "labelcolor", "color" }; const char *enumnames[] = { "arrowsize", "edgewidth", "hook1", "hook2", "angle1", "angle2", "velocity1", "velocity2", "arrowpos", "labelpos", "labelangle", "labelangle2", "labeldegree", "fontsize" }; const char *enumnames2[]= { "s", "w", "h1", "h2", "a1", "a2", "k1", "k2", "ap", "lp", "lr", "lphi", "la", "fos" }; const char *estrnames[] = { "arrowtype", "linepattern", "label", "labelcolor", "color" }; const char *estrnames2[]= { "a", "p", "l", "lc", "c" }; const char *newline="\x0d\x0a"; igraph_es_t es; igraph_eit_t eit; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_t ex_numa; igraph_vector_t ex_stra; igraph_vector_t vx_numa; igraph_vector_t vx_stra; char *s, *escaped; igraph_bool_t bipartite=0; igraph_vector_int_t bip_index, bip_index2; igraph_vector_bool_t bvec; long int notop=0, nobottom=0; IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); IGRAPH_VECTOR_INIT_FINALLY(&ex_numa, 0); IGRAPH_VECTOR_INIT_FINALLY(&ex_stra, 0); IGRAPH_VECTOR_INIT_FINALLY(&vx_numa, 0); IGRAPH_VECTOR_INIT_FINALLY(&vx_stra, 0); /* Check if graph is bipartite */ if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, "type")) { igraph_attribute_type_t type_type; igraph_i_attribute_gettype(graph, &type_type, IGRAPH_ATTRIBUTE_VERTEX, "type"); if (type_type == IGRAPH_ATTRIBUTE_BOOLEAN) { int bptr=0, tptr=0; bipartite = 1; write_vertex_attrs = 1; /* Count top and bottom vertices, we go over them twice, because we want to keep their original order */ IGRAPH_CHECK(igraph_vector_int_init(&bip_index, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_int_destroy, &bip_index); IGRAPH_CHECK(igraph_vector_int_init(&bip_index2, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_int_destroy, &bip_index2); IGRAPH_CHECK(igraph_vector_bool_init(&bvec, 1)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &bvec); for (i=0; i * This file format is discussed in the documentation of \ref * igraph_read_graph_dimacs(), see that for more information. * * \param graph The graph to write to the stream. * \param outstream The stream. * \param source Integer, the id of the source vertex for the maximum * flow. * \param target Integer, the id of the target vertex. * \param capacity Pointer to an initialized vector containing the * edge capacity values. * \return Error code. * * Time complexity: O(|E|), the number of edges in the graph. * * \sa igraph_read_graph_dimacs() */ int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, long int source, long int target, const igraph_vector_t *capacity) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_eit_t it; long int i=0; int ret, ret1, ret2, ret3; if (igraph_vector_size(capacity) != no_of_edges) { IGRAPH_ERROR("invalid capacity vector length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); ret=fprintf(outstream, "c created by igraph\np max %li %li\nn %li s\nn %li t\n", no_of_nodes, no_of_edges, source+1, target+1); if (ret < 0) { IGRAPH_ERROR("Write error", IGRAPH_EFILE); } while (!IGRAPH_EIT_END(it)) { igraph_integer_t from, to; igraph_real_t cap; igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); cap=VECTOR(*capacity)[i++]; ret1=fprintf(outstream, "a %li %li ", (long int) from+1, (long int) to+1); ret2=igraph_real_fprintf_precise(outstream, cap); ret3=fputc('\n', outstream); if (ret1 < 0 || ret2 < 0 || ret3==EOF) { IGRAPH_ERROR("Write error", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_gml_convert_to_key(const char *orig, char **key) { int no=1; char strno[50]; size_t i, len = strlen(orig), newlen = 0, plen = 0; /* do we need a prefix? */ if (len==0 || !isalpha(orig[0])) { no++; snprintf(strno, sizeof(strno)-1, "igraph"); plen=newlen=strlen(strno); } for (i=0; i The graph, vertex and edges attributes are written to the * file as well, if they are numeric of string. * * As igraph is more forgiving about attribute names, it might * be necessary to simplify the them before writing to the GML file. * This way we'll have a syntactically correct GML file. The following * simple procedure is performed on each attribute name: first the alphanumeric * characters are extracted, the others are ignored. Then if the first character * is not a letter then the attribute name is prefixed with igraph. * Note that this might result identical names for two attributes, igraph * does not check this. * * The id vertex attribute is treated specially. * If the id argument is not 0 then it should be a numeric * vector with the vertex ids and the id vertex attribute is * ignored (if there is one). If id is 0 and there is a * numeric id vertex attribute that is used instead. If ids * are not specified in either way then the regular igraph vertex ids are used. * * Note that whichever way vertex ids are specified, their * uniqueness is not checked. * * If the graph has edge attributes named source * or target they're silently ignored. GML uses these attributes * to specify the edges, so we cannot write them to the file. Rename them * before calling this function if you want to preserve them. * \param graph The graph to write to the stream. * \param outstream The stream to write the file to. * \param id Either NULL or a numeric vector with the vertex ids. * See details above. * \param creator An optional string to write to the stream in the creator line. * If this is 0 then the current date and time is added. * \return Error code. * * Time complexity: should be proportional to the number of characters written * to the file. * * \sa \ref igraph_read_graph_gml() for reading GML files, * \ref igraph_read_graph_graphml() for a more modern format. * * \example examples/simple/gml.c */ int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, const igraph_vector_t *id, const char *creator) { int ret; igraph_strvector_t gnames, vnames, enames; igraph_vector_t gtypes, vtypes, etypes; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; long int i; long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vector_t v_myid; const igraph_vector_t *myid=id; time_t curtime=time(0); char *timestr=ctime(&curtime); timestr[strlen(timestr)-1]='\0'; /* nicely remove \n */ CHECK(fprintf(outstream, "Creator \"igraph version %s %s\"\nVersion 1\ngraph\n[\n", PACKAGE_VERSION, creator ? creator : timestr)); IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); IGRAPH_VECTOR_INIT_FINALLY(>ypes, 0); IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); IGRAPH_CHECK(igraph_i_attribute_get_info(graph, &gnames, >ypes, &vnames, &vtypes, &enames, &etypes)); IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1); /* Check whether there is an 'id' node attribute if the supplied is 0 */ if (!id) { igraph_bool_t found=0; for (i=0; iThis is only a preliminary implementation, only the vertices * and the edges are written but not the attributes or any visualization * information. * * \param graph The graph to write to the stream. * \param outstream The stream to write the file to. * * Time complexity: should be proportional to the number of characters written * to the file. * * \sa \ref igraph_write_graph_graphml() for a more modern format. * * \example examples/simple/dot.c */ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { int ret; long int i, j; long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); char edgeop[3]; igraph_strvector_t gnames, vnames, enames; igraph_vector_t gtypes, vtypes, etypes; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); IGRAPH_VECTOR_INIT_FINALLY(>ypes, 0); IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); IGRAPH_CHECK(igraph_i_attribute_get_info(graph, &gnames, >ypes, &vnames, &vtypes, &enames, &etypes)); IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1); CHECK(fprintf(outstream, "/* Created by igraph %s */\n", PACKAGE_VERSION)); if (igraph_is_directed(graph)) { CHECK(fprintf(outstream, "digraph {\n")); strcpy(edgeop, "->"); } else { CHECK(fprintf(outstream, "graph {\n")); strcpy(edgeop, "--"); } /* Write the graph attributes */ if (igraph_vector_size(>ypes)>0) { CHECK(fprintf(outstream, " graph [\n")); for (i=0; i 0) { for (i=0; i 0) { for (i=0; i Note the specification does not mention whether the * format is case sensitive or not. For igraph DL files are case * sensitive, i.e. \c Larry and \c larry are not the same. * \param graph Pointer to an uninitialized graph object. * \param instream The stream to read the DL file from. * \param directed Logical scalar, whether to create a directed file. * \return Error code. * * Time complexity: linear in terms of the number of edges and * vertices, except for the matrix format, which is quadratic in the * number of vertices. * * \example examples/simple/igraph_read_graph_dl.c */ int igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_bool_t directed) { int i; long int n, n2; const igraph_strvector_t *namevec=0; igraph_vector_ptr_t name, weight; igraph_vector_ptr_t *pname=0, *pweight=0; igraph_attribute_record_t namerec, weightrec; const char *namestr="name", *weightstr="weight"; igraph_i_dl_parsedata_t context; context.eof=0; context.mode=0; context.n=-1; context.from=0; context.to=0; IGRAPH_VECTOR_INIT_FINALLY(&context.edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&context.weights, 0); IGRAPH_CHECK(igraph_strvector_init(&context.labels, 0)); IGRAPH_FINALLY(igraph_strvector_destroy, &context.labels); IGRAPH_TRIE_INIT_FINALLY(&context.trie, /*names=*/ 1); igraph_dl_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_dl_yylex_destroy, context.scanner); igraph_dl_yyset_in(instream, context.scanner); i=igraph_dl_yyparse(&context); if (i != 0) { if (context.errmsg) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); } else { IGRAPH_ERROR("Cannot read DL file", IGRAPH_PARSEERROR); } } /* Extend the weight vector, if needed */ n=igraph_vector_size(&context.weights); n2=igraph_vector_size(&context.edges) / 2; if (n != 0) { igraph_vector_resize(&context.weights, n2); for (; n= context.n) { IGRAPH_WARNING("More vertices than specified in `DL' file"); context.n=n; } /* OK, everything is ready, create the graph */ IGRAPH_CHECK(igraph_empty(graph, 0, directed)); IGRAPH_FINALLY(igraph_destroy, graph); /* Labels */ if (igraph_strvector_size(&context.labels) != 0) { namevec=(const igraph_strvector_t*) &context.labels; } else if (igraph_trie_size(&context.trie) != 0) { igraph_trie_getkeys(&context.trie, &namevec); } if (namevec) { IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &name); pname=&name; namerec.name=namestr; namerec.type=IGRAPH_ATTRIBUTE_STRING; namerec.value=namevec; VECTOR(name)[0]=&namerec; } /* Weights */ if (igraph_vector_size(&context.weights) != 0) { IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &weight); pweight=&weight; weightrec.name=weightstr; weightrec.type=IGRAPH_ATTRIBUTE_NUMERIC; weightrec.value=&context.weights; VECTOR(weight)[0]=&weightrec; } IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) context.n, pname)); IGRAPH_CHECK(igraph_add_edges(graph, &context.edges, pweight)); if (pweight) { igraph_vector_ptr_destroy(pweight); IGRAPH_FINALLY_CLEAN(1); } if (pname) { igraph_vector_ptr_destroy(pname); IGRAPH_FINALLY_CLEAN(1); } /* don't destroy the graph itself but pop it from the finally stack */ IGRAPH_FINALLY_CLEAN(1); igraph_trie_destroy(&context.trie); igraph_strvector_destroy(&context.labels); igraph_vector_destroy(&context.edges); igraph_vector_destroy(&context.weights); igraph_dl_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(5); return 0; } /** * \function igraph_write_graph_leda * \brief Write a graph in LEDA native graph format. * * This function writes a graph to an output stream in LEDA format. * See http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html * * * The support for the LEDA format is very basic at the moment; igraph * writes only the LEDA graph section which supports one selected vertex * and edge attribute and no layout information or visual attributes. * * \param graph The graph to write to the stream. * \param outstream The stream. * \param vertex_attr_name The name of the vertex attribute whose values * are to be stored in the output or \c NULL if no * vertex attribute has to be stored. * \param edge_attr_name The name of the edge attribute whose values * are to be stored in the output or \c NULL if no * edge attribute has to be stored. * \return Error code. * * Time complexity: O(|V|+|E|), the number of vertices and edges in the * graph. * * \example examples/simple/igraph_write_graph_leda.c */ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, const char* vertex_attr_name, const char* edge_attr_name) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_eit_t it; long int i=0; int ret; igraph_attribute_type_t vertex_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; igraph_attribute_type_t edge_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; igraph_integer_t from, to, rev; IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); /* Check if we have the vertex attribute */ if (vertex_attr_name && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name)) { vertex_attr_name=0; IGRAPH_WARNING("specified vertex attribute does not exist"); } if (vertex_attr_name) { IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &vertex_attr_type, IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name)); if (vertex_attr_type != IGRAPH_ATTRIBUTE_NUMERIC && vertex_attr_type != IGRAPH_ATTRIBUTE_STRING) { vertex_attr_name=0; vertex_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; IGRAPH_WARNING("specified vertex attribute must be numeric or string"); } } /* Check if we have the edge attribute */ if (edge_attr_name && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, edge_attr_name)) { edge_attr_name=0; IGRAPH_WARNING("specified edge attribute does not exist"); } if (edge_attr_name) { IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &edge_attr_type, IGRAPH_ATTRIBUTE_EDGE, edge_attr_name)); if (edge_attr_type != IGRAPH_ATTRIBUTE_NUMERIC && edge_attr_type != IGRAPH_ATTRIBUTE_STRING) { edge_attr_name=0; edge_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; IGRAPH_WARNING("specified edge attribute must be numeric or string"); } } /* Start writing header */ CHECK(fprintf(outstream, "LEDA.GRAPH\n")); switch (vertex_attr_type) { case IGRAPH_ATTRIBUTE_NUMERIC: CHECK(fprintf(outstream, "float\n")); break; case IGRAPH_ATTRIBUTE_STRING: CHECK(fprintf(outstream, "string\n")); break; default: CHECK(fprintf(outstream, "void\n")); } switch (edge_attr_type) { case IGRAPH_ATTRIBUTE_NUMERIC: CHECK(fprintf(outstream, "float\n")); break; case IGRAPH_ATTRIBUTE_STRING: CHECK(fprintf(outstream, "string\n")); break; default: CHECK(fprintf(outstream, "void\n")); } CHECK(fprintf(outstream, "%d\n", (igraph_is_directed(graph) ? -1 : -2))); /* Start writing vertices */ CHECK(fprintf(outstream, "# Vertices\n")); CHECK(fprintf(outstream, "%ld\n", no_of_nodes)); if (vertex_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { /* Vertices with numeric attributes */ igraph_vector_t values; IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes); IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( graph, vertex_attr_name, igraph_vss_all(), &values)); for (i = 0; i < no_of_nodes; i++) { CHECK(fprintf(outstream, "|{")); CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[i])); CHECK(fprintf(outstream, "}|\n")); } igraph_vector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_STRING) { /* Vertices with string attributes */ igraph_strvector_t values; IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes)); IGRAPH_FINALLY(igraph_strvector_destroy, &values); IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( graph, vertex_attr_name, igraph_vss_all(), &values)); for (i = 0; i < no_of_nodes; i++) { const char* str = STR(values, i); if (strchr(str, '\n') != 0) { IGRAPH_ERROR("edge attribute values cannot contain newline characters", IGRAPH_EINVAL); } CHECK(fprintf(outstream, "|{%s}|\n", str)); } igraph_strvector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); } else { /* Vertices with no attributes */ for (i = 0; i < no_of_nodes; i++) CHECK(fprintf(outstream, "|{}|\n")); } CHECK(fprintf(outstream, "# Edges\n")); CHECK(fprintf(outstream, "%ld\n", no_of_edges)); if (edge_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { /* Edges with numeric attributes */ igraph_vector_t values; IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes); IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values)); while (!IGRAPH_EIT_END(it)) { long int eid = IGRAPH_EIT_GET(it); igraph_edge(graph, (igraph_integer_t) eid, &from, &to); igraph_get_eid(graph, &rev, to, from, 1, 0); if (rev == IGRAPH_EIT_GET(it)) rev = -1; CHECK(fprintf(outstream, "%ld %ld %ld |{", (long int) from+1, (long int) to+1, (long int) rev+1)); CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[eid])); CHECK(fprintf(outstream, "}|\n")); IGRAPH_EIT_NEXT(it); } igraph_vector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); } else if (edge_attr_type == IGRAPH_ATTRIBUTE_STRING) { /* Edges with string attributes */ igraph_strvector_t values; IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes)); IGRAPH_FINALLY(igraph_strvector_destroy, &values); IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr( graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values)); while (!IGRAPH_EIT_END(it)) { long int eid = IGRAPH_EIT_GET(it); const char* str = STR(values, eid); igraph_edge(graph, (igraph_integer_t) eid, &from, &to); igraph_get_eid(graph, &rev, to, from, 1, 0); if (rev == IGRAPH_EIT_GET(it)) rev = -1; if (strchr(str, '\n') != 0) { IGRAPH_ERROR("edge attribute values cannot contain newline characters", IGRAPH_EINVAL); } CHECK(fprintf(outstream, "%ld %ld %ld |{%s}|\n", (long int) from+1, (long int) to+1, (long int) rev+1, str)); IGRAPH_EIT_NEXT(it); } igraph_strvector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); } else { /* Edges with no attributes */ while (!IGRAPH_EIT_END(it)) { igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); igraph_get_eid(graph, &rev, to, from, 1, 0); if (rev == IGRAPH_EIT_GET(it)) rev = -1; CHECK(fprintf(outstream, "%ld %ld %ld |{}|\n", (long int) from+1, (long int) to+1, (long int) rev+1)); IGRAPH_EIT_NEXT(it); } } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); return 0; } #undef CHECK igraph/src/types.c0000644000176000001440000000764512325527074013676 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include #ifdef _MSC_VER # define snprintf _snprintf #endif #ifdef DBL_DIG /* Use DBL_DIG to determine the maximum precision used for %g */ # define STRINGIFY_HELPER(x) #x # define STRINGIFY(x) STRINGIFY_HELPER(x) # define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%." STRINGIFY(DBL_DIG) "g" #else /* Assume a precision of 10 digits for %g */ # define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%.10g" #endif #ifndef USING_R int igraph_real_printf(igraph_real_t val) { if (igraph_finite(val)) { return printf("%g", val); } else if (igraph_is_nan(val)) { return printf("NaN"); } else if (igraph_is_inf(val)) { if (val < 0) { return printf("-Inf"); } else { return printf("Inf"); } } else { /* fallback */ return printf("%g", val); } } #endif int igraph_real_fprintf(FILE *file, igraph_real_t val) { if (igraph_finite(val)) { return fprintf(file, "%g", val); } else if (igraph_is_nan(val)) { return fprintf(file, "NaN"); } else if (igraph_is_inf(val)) { if (val < 0) { return fprintf(file, "-Inf"); } else { return fprintf(file, "Inf"); } } else { /* fallback */ return fprintf(file, "%g", val); } } int igraph_real_snprintf(char* str, size_t size, igraph_real_t val) { if (igraph_finite(val)) { return snprintf(str, size, "%g", val); } else if (igraph_is_nan(val)) { return snprintf(str, size, "NaN"); } else if (igraph_is_inf(val)) { if (val < 0) { return snprintf(str, size, "-Inf"); } else { return snprintf(str, size, "Inf"); } } else { /* fallback */ return snprintf(str, size, "%g", val); } } #ifndef USING_R int igraph_real_printf_precise(igraph_real_t val) { if (igraph_finite(val)) { return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } else if (igraph_is_nan(val)) { return printf("NaN"); } else if (igraph_is_inf(val)) { if (val < 0) { return printf("-Inf"); } else { return printf("Inf"); } } else { /* fallback */ return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } } #endif int igraph_real_fprintf_precise(FILE *file, igraph_real_t val) { if (igraph_finite(val)) { return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } else if (igraph_is_nan(val)) { return fprintf(file, "NaN"); } else if (igraph_is_inf(val)) { if (val < 0) { return fprintf(file, "-Inf"); } else { return fprintf(file, "Inf"); } } else { /* fallback */ return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } } int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val) { if (igraph_finite(val)) { return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } else if (igraph_is_nan(val)) { return snprintf(str, size, "NaN"); } else if (igraph_is_inf(val)) { if (val < 0) { return snprintf(str, size, "-Inf"); } else { return snprintf(str, size, "Inf"); } } else { /* fallback */ return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } } igraph/src/rinterface_extra.c0000644000176000001440000001361512325527074016051 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library R interface. Copyright (C) 2013 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph.h" #define USE_RINTERNALS #include #include #include #include /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++C */ /* C */ /* Given a HIERARCHIC CLUSTERING, described as a sequence of C */ /* agglomerations, prepare the seq. of aggloms. and "horiz." C */ /* order of objects for plotting the dendrogram using S routine C */ /* 'plclust'. C */ /* C */ /* Parameters: C */ /* C */ /* IA, IB: vectors of dimension N defining the agglomer- C */ /* ations. C */ /* IIA, IIB: used to store IA and IB values differently C */ /* (in form needed for S command 'plclust' C */ /* IORDER: "horiz." order of objects for dendrogram C */ /* C */ /* F. Murtagh, ESA/ESO/STECF, Garching, June 1991 C */ /* C */ /* HISTORY C */ /* C */ /* Adapted from routine HCASS, which additionally determines C */ /* cluster assignments at all levels, at extra comput. expense C */ /* C */ /* ---------------------------------------------------------------C */ int igraphhcass2(int *n, int *ia, int *ib, int *iorder, int *iia, int *iib) { /* System generated locals */ int i__1, i__2, i__3; /* Local variables */ static int i__, j, k, k1, k2, loc; /* Args */ /* Var */ /* Following bit is to get seq. of merges into format acceptable to plclust I coded clusters as lowest seq. no. of constituents; S's 'hclust' codes singletons as -ve numbers, and non-singletons with their seq. nos. */ /* Parameter adjustments */ --iib; --iia; --iorder; --ib; --ia; /* Function Body */ i__1 = *n; for (i__ = 1; i__ <= i__1; ++i__) { iia[i__] = ia[i__]; iib[i__] = ib[i__]; } i__1 = *n - 2; for (i__ = 1; i__ <= i__1; ++i__) { /* In the following, smallest (+ve or -ve) seq. no. wanted */ /* Computing MIN */ i__2 = ia[i__], i__3 = ib[i__]; k = i__2 < i__3 ? i__2 : i__3; i__2 = *n - 1; for (j = i__ + 1; j <= i__2; ++j) { if (ia[j] == k) { iia[j] = -i__; } if (ib[j] == k) { iib[j] = -i__; } } } i__1 = *n - 1; for (i__ = 1; i__ <= i__1; ++i__) { iia[i__] = -iia[i__]; iib[i__] = -iib[i__]; } i__1 = *n - 1; for (i__ = 1; i__ <= i__1; ++i__) { if (iia[i__] > 0 && iib[i__] < 0) { k = iia[i__]; iia[i__] = iib[i__]; iib[i__] = k; } if (iia[i__] > 0 && iib[i__] > 0) { /* Computing MIN */ i__2 = iia[i__], i__3 = iib[i__]; k1 = i__2 < i__3 ? i__2 : i__3; /* Computing MAX */ i__2 = iia[i__], i__3 = iib[i__]; k2 = i__2 > i__3 ? i__2 : i__3; iia[i__] = k1; iib[i__] = k2; } } /* NEW PART FOR 'ORDER' */ iorder[1] = iia[*n - 1]; iorder[2] = iib[*n - 1]; loc = 2; for (i__ = *n - 2; i__ >= 1; --i__) { i__1 = loc; for (j = 1; j <= i__1; ++j) { if (iorder[j] == i__) { /* REPLACE IORDER(J) WITH IIA(I) AND IIB(I) */ iorder[j] = iia[i__]; if (j == loc) { ++loc; iorder[loc] = iib[i__]; } else { ++loc; i__2 = j + 2; for (k = loc; k >= i__2; --k) { iorder[k] = iorder[k - 1]; } iorder[j + 1] = iib[i__]; } goto L171; } } /* SHOULD NEVER REACH HERE */ L171: ; } i__1 = *n; for (i__ = 1; i__ <= i__1; ++i__) { iorder[i__] = -iorder[i__]; } return 0; } /* hcass2_ */ SEXP R_igraph_psumtree_draw(SEXP plength, SEXP howmany, SEXP prob) { SEXP result; int length=INTEGER(plength)[0]; int i, n=INTEGER(howmany)[0]; igraph_psumtree_t tree; igraph_real_t sum; PROTECT(result=NEW_INTEGER(n)); igraph_psumtree_init(&tree, length); if (isNull(prob)) { for (i=0; in ; Lp = L->p ; Li = L->i ; Lx = L->x ; for (j = n-1 ; j >= 0 ; j--) { for (p = Lp [j]+1 ; p < Lp [j+1] ; p++) { x [j] -= CS_CONJ (Lx [p]) * x [Li [p]] ; } x [j] /= CS_CONJ (Lx [Lp [j]]) ; } return (1) ; } igraph/src/igraph_arpack.h0000644000176000001440000003301712325527073015321 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" #ifndef ARPACK_H #define ARPACK_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /** * \section about_arpack ARPACK interface in igraph * * * ARPACK is a library for solving large scale eigenvalue problems. * The package is designed to compute a few eigenvalues and corresponding * eigenvectors of a general \c n by \c n matrix \c A. It is * most appropriate for large sparse or structured matrices \c A where * structured means that a matrix-vector product w <- Av requires * order \c n rather than the usual order n^2 floating point * operations. Please see * http://www.caam.rice.edu/software/ARPACK/ for details. * * * * The eigenvalue calculation in ARPACK (in the simplest * case) involves the calculation of the \c Av product where \c A * is the matrix we work with and \c v is an arbitrary vector. A * user-defined function of type \ref igraph_arpack_function_t * is expected to perform this product. If the product can be done * efficiently, e.g. if the matrix is sparse, then ARPACK is usually * able to calculate the eigenvalues very quickly. * * * In igraph, eigenvalue/eigenvector calculations usually * involve the following steps: * \olist * \oli Initialization of an \ref igraph_arpack_options_t data * structure using \ref igraph_arpack_options_init. * \oli Setting some options in the initialized \ref * igraph_arpack_options_t object. * \oli Defining a function of type \ref igraph_arpack_function_t. * The input of this function is a vector, and the output * should be the output matrix multiplied by the input vector. * \oli Calling \ref igraph_arpack_rssolve() (is the matrix is * symmetric), or \ref igraph_arpack_rnsolve(). * \endolist * The \ref igraph_arpack_options_t object can be used multiple * times. * * * * If we have many eigenvalue problems to solve, then it might worth * to create an \ref igraph_arpack_storage_t object, and initialize it * via \ref igraph_arpack_storage_init(). This structure contains all * memory needed for ARPACK (with the given upper limit regerding to * the size of the eigenvalue problem). Then many problems can be * solved using the same \ref igraph_arpack_storage_t object, without * always reallocating the required memory. * The \ref igraph_arpack_storage_t object needs to be destroyed by * calling \ref igraph_arpack_storage_destroy() on it, when it is not * needed any more. * * * * igraph does not contain all * ARPACK routines, only the ones dealing with symmetric and * non-symmetric eigenvalue problems using double precision real * numbers. * * */ /** * \struct igraph_arpack_options_t * \brief Options for ARPACK * * This data structure contains the options of thee ARPACK eigenvalue * solver routines. It must be initialized by calling \ref * igraph_arpack_options_init() on it. Then it can be used for * multiple ARPACK calls, as the ARPACK solvers do not modify it. * * Input options: * \member bmat Character. Whether to solve a standard ('I') ot a * generalized problem ('B'). * \member n Dimension of the eigenproblem. * \member which Specifies which eigenvalues/vectors to * compute. Possible values for symmetric matrices: * \clist \cli LA * Compute \c nev largest (algebraic) eigenvalues. * \cli SA * Compute \c nev smallest (algebraic) eigenvalues. * \cli LM * Compute \c nev largest (in magnitude) eigenvalues. * \cli SM * Compute \c nev smallest (in magnitude) eigenvalues. * \cli BE * Compute \c nev eigenvalues, half from each end of * the spectrum. When \c nev is odd, compute one * more from the high en than from the low * end. \endclist * Possible values for non-symmetric matrices: * \clist \cli LM * Compute \c nev largest (in magnitude) eigenvalues. * \cli SM * Compute \c nev smallest (in magnitude) eigenvalues. * \cli LR * Compute \c nev eigenvalues of largest real part. * \cli SR * Compute \c nev eigenvalues of smallest real part. * \cli LI * Compute \c nev eigenvalues of largest imaginary part. * \cli SI * Compute \c nev eigenvalues of smallest imaginary * part. \endclist * \member nev The number of eigenvalues to be computed. * \member tol Stopping criterion: the relative accuracy * of the Ritz value is considered acceptable if its error is less * than \c tol times its estimated value. If this is set to zero * then machine precision is used. * \member ncv Number of Lanczos vectors to be generated. Setting this * to zero means that \ref igraph_arpack_rssolve and \ref igraph_arpack_rnsolve * will determine a suitable value for \c ncv automatically. * \member ldv Numberic scalar. It should be set to * zero in the current igraph implementation. * \member ishift Either zero or one. If zero then the shifts are * provided by the user via reverse communication. If one then exact * shifts with respect to the reduced tridiagonal matrix \c T. * Please always set this to one. * \member mxiter Maximum number of Arnoldi update iterations allowed. * \member nb Blocksize to be used in the recurrence. Please always * leave this on the default value, one. * \member mode The type of the eigenproblem to be solved. * Possible values if the input matrix is symmetric: * \olist * \oli A*x=lambda*x, A is symmetric. * \oli A*x=lambda*M*x, A is * symmetric, M is symmetric positive definite. * \oli K*x=lambda*M*x, K is * symmetric, M is symmetric positive semi-definite. * \oli K*x=lambda*KG*x, K is * symmetric positive semi-definite, KG is symmetric * indefinite. * \oli A*x=lambda*M*x, A is * symmetric, M is symmetric positive * semi-definite. (Cayley transformed mode.) \endolist * Please note that only \c mode ==1 was tested and other values * might not work properly. * Possible values if the input matrix is not symmetric: * \olist * \oli A*x=lambda*x. * \oli A*x=lambda*M*x, M is * symmetric positive definite. * \oli A*x=lambda*M*x, M is * symmetric semi-definite. * \oli A*x=lambda*M*x, M is * symmetric semi-definite. \endolist * Please note that only \c mode == 1 was tested and other values * might not work properly. * \member start Whether to use the supplied starting vector (1), or * use a random starting vector (0). The starting vector must be * supplied in the first column of the \c vectors argument of the * \ref igraph_arpack_rssolve() of \ref igraph_arpack_rnsolve() call. * * Output options: * \member info Error flag of ARPACK. Possible values: * \clist \cli 0 * Normal exit. * \cli 1 * Maximum number of iterations taken. * \cli 3 * No shifts could be applied during a cycle of the * Implicitly restarted Arnoldi iteration. One possibility * is to increase the size of \c ncv relative to \c * nev. \endclist * ARPACK can return other error flags as well, but these are * converted to igraph errors, see \ref igraph_error_type_t. * \member ierr Error flag of the second ARPACK call (one eigenvalue * computation usually involves two calls to ARPACK). This is * always zero, as other error codes are converted to igraph errors. * \member noiter Number of Arnoldi iterations taken. * \member nconv Number of converged Ritz values. This * represents the number of Ritz values that satisfy the * convergence critetion. * \member numop Total number of matrix-vector multiplications. * \member numopb Not used currently. * \member numreo Total number of steps of re-orthogonalization. * * Internal options: * \member lworkl Do not modify this option. * \member sigma The shift for the shift-invert mode. * \member sigmai The imaginary part of the shift, for the * non-symmetric or complex shift-invert mode. * \member iparam Do not modify this option. * \member ipntr Do not modify this option. * */ typedef struct igraph_arpack_options_t { /* INPUT */ char bmat[1]; /* I-standard problem, G-generalized */ int n; /* Dimension of the eigenproblem */ char which[2]; /* LA, SA, LM, SM, BE */ int nev; /* Number of eigenvalues to be computed */ igraph_real_t tol; /* Stopping criterion */ int ncv; /* Number of columns in V */ int ldv; /* Leading dimension of V */ int ishift; /* 0-reverse comm., 1-exact with tridiagonal */ int mxiter; /* Maximum number of update iterations to take */ int nb; /* Block size on the recurrence, only 1 works */ int mode; /* The kind of problem to be solved (1-5) 1: A*x=l*x, A symmetric 2: A*x=l*M*x, A symm. M pos. def. 3: K*x = l*M*x, K symm., M pos. semidef. 4: K*x = l*KG*x, K s. pos. semidef. KG s. indef. 5: A*x = l*M*x, A symm., M symm. pos. semidef. */ int start; /* 0: random, 1: use the supplied vector */ int lworkl; /* Size of temporary storage, default is fine */ igraph_real_t sigma; /* The shift for modes 3,4,5 */ igraph_real_t sigmai; /* The imaginary part of shift for rnsolve */ /* OUTPUT */ int info; /* What happened, see docs */ int ierr; /* What happened in the dseupd call */ int noiter; /* The number of iterations taken */ int nconv; int numop; /* Number of OP*x operations */ int numopb; /* Number of B*x operations if BMAT='G' */ int numreo; /* Number of steps of re-orthogonalizations */ /* INTERNAL */ int iparam[11]; int ipntr[14]; } igraph_arpack_options_t; /** * \struct igraph_arpack_storage_t * \brief Storage for ARPACK * * Public members, do not modify them directly, these are considered * to be read-only. * \member maxn Maximum rank of matrix. * \member maxncv Maximum NCV. * \member maxldv Maximum LDV. * * These members are considered to be private: * \member workl Working memory. * \member workd Working memory. * \member d Memory for eigenvalues. * \member resid Memory for residuals. * \member ax Working memory. * \member select Working memory. * \member di Memory for eigenvalues, non-symmetric case only. * \member workev Working memory, non-symmetric case only. */ typedef struct igraph_arpack_storage_t { int maxn, maxncv, maxldv; igraph_real_t *v; igraph_real_t *workl; igraph_real_t *workd; igraph_real_t *d; igraph_real_t *resid; igraph_real_t *ax; int *select; igraph_real_t *di; /* These two only for non-symmetric problems */ igraph_real_t *workev; } igraph_arpack_storage_t; void igraph_arpack_options_init(igraph_arpack_options_t *o); int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, long int maxncv, long int maxldv, igraph_bool_t symm); void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); /** * \typedef igraph_arpack_function_t * Type of the ARPACK callback function * * \param to Pointer to an \c igraph_real_t, the result of the * matrix-vector product is expected to be stored here. * \param from Pointer to an \c igraph_real_t, the input matrix should * be multiplied by the vector stored here. * \param n The length of the vector (which is the same as the order * of the input matrix). * \param extra Extra argument to the matrix-vector calculation * function. This is coming from the \ref igraph_arpack_rssolve() * or \ref igraph_arpack_rnsolve() function. * \return Error code, if not zero, then the ARPACK solver considers * this as an error, stops and calls the igraph error handler. */ typedef int igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from, int n, void *extra); int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors); int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_matrix_t *values, igraph_matrix_t *vectors); int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, long int nev); __END_DECLS #endif igraph/src/igraph_scg.h0000644000176000001440000001030712325527073014631 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_SCG_H #define IGRAPH_SCG_H #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" #include "igraph_sparsemat.h" typedef enum { IGRAPH_SCG_SYMMETRIC=1, IGRAPH_SCG_LAPLACIAN=2, IGRAPH_SCG_STOCHASTIC=3 } igraph_scg_matrix_t; typedef enum { IGRAPH_SCG_OPTIMUM=1, IGRAPH_SCG_INTERV_KM=2, IGRAPH_SCG_INTERV=3, IGRAPH_SCG_EXACT=4 } igraph_scg_algorithm_t; typedef enum { IGRAPH_SCG_NORM_ROW=1, IGRAPH_SCG_NORM_COL=2 } igraph_scg_norm_t; typedef enum { IGRAPH_SCG_DIRECTION_DEFAULT=1, IGRAPH_SCG_DIRECTION_LEFT=2, IGRAPH_SCG_DIRECTION_RIGHT=3 } igraph_scg_direction_t; int igraph_scg_grouping(const igraph_matrix_t *V, igraph_vector_t *groups, igraph_integer_t nt, const igraph_vector_t *nt_vec, igraph_scg_matrix_t mtype, igraph_scg_algorithm_t algo, const igraph_vector_t *p, igraph_integer_t maxiter); int igraph_scg_semiprojectors(const igraph_vector_t *groups, igraph_scg_matrix_t mtype, igraph_matrix_t *L, igraph_matrix_t *R, igraph_sparsemat_t *Lsparse, igraph_sparsemat_t *Rsparse, const igraph_vector_t *p, igraph_scg_norm_t norm); int igraph_scg_norm_eps(const igraph_matrix_t *V, const igraph_vector_t *groups, igraph_vector_t *eps, igraph_scg_matrix_t mtype, const igraph_vector_t *p, igraph_scg_norm_t norm); int igraph_scg_adjacency(const igraph_t *graph, const igraph_matrix_t *matrix, const igraph_sparsemat_t *sparsemat, const igraph_vector_t *ev, igraph_integer_t nt, const igraph_vector_t *nt_vec, igraph_scg_algorithm_t algo, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_t *groups, igraph_bool_t use_arpack, igraph_integer_t maxiter, igraph_t *scg_graph, igraph_matrix_t *scg_matrix, igraph_sparsemat_t *scg_sparsemat, igraph_matrix_t *L, igraph_matrix_t *R, igraph_sparsemat_t *Lsparse, igraph_sparsemat_t *Rsparse); int igraph_scg_stochastic(const igraph_t *graph, const igraph_matrix_t *matrix, const igraph_sparsemat_t *sparsemat, const igraph_vector_t *ev, igraph_integer_t nt, const igraph_vector_t *nt_vec, igraph_scg_algorithm_t algo, igraph_scg_norm_t norm, igraph_vector_complex_t *values, igraph_matrix_complex_t *vectors, igraph_vector_t *groups, igraph_vector_t *p, igraph_bool_t use_arpack, igraph_integer_t maxiter, igraph_t *scg_graph, igraph_matrix_t *scg_matrix, igraph_sparsemat_t *scg_sparsemat, igraph_matrix_t *L, igraph_matrix_t *R, igraph_sparsemat_t *Lsparse, igraph_sparsemat_t *Rsparse); int igraph_scg_laplacian(const igraph_t *graph, const igraph_matrix_t *matrix, const igraph_sparsemat_t *sparsemat, const igraph_vector_t *ev, igraph_integer_t nt, const igraph_vector_t *nt_vec, igraph_scg_algorithm_t algo, igraph_scg_norm_t norm, igraph_scg_direction_t direction, igraph_vector_complex_t *values, igraph_matrix_complex_t *vectors, igraph_vector_t *groups, igraph_bool_t use_arpack, igraph_integer_t maxiter, igraph_t *scg_graph, igraph_matrix_t *scg_matrix, igraph_sparsemat_t *scg_sparsemat, igraph_matrix_t *L, igraph_matrix_t *R, igraph_sparsemat_t *Lsparse, igraph_sparsemat_t *Rsparse); #endif igraph/src/cs_updown.c0000644000176000001440000000567012325527073014526 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* sparse Cholesky update/downdate, L*L' + sigma*w*w' (sigma = +1 or -1) */ CS_INT cs_updown (cs *L, CS_INT sigma, const cs *C, const CS_INT *parent) { CS_INT n, p, f, j, *Lp, *Li, *Cp, *Ci ; CS_ENTRY *Lx, *Cx, alpha, gamma, w1, w2, *w ; double beta = 1, beta2 = 1, delta ; #ifdef CS_COMPLEX cs_complex_t phase ; #endif if (!CS_CSC (L) || !CS_CSC (C) || !parent) return (0) ; /* check inputs */ Lp = L->p ; Li = L->i ; Lx = L->x ; n = L->n ; Cp = C->p ; Ci = C->i ; Cx = C->x ; if ((p = Cp [0]) >= Cp [1]) return (1) ; /* return if C empty */ w = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get workspace */ if (!w) return (0) ; /* out of memory */ f = Ci [p] ; for ( ; p < Cp [1] ; p++) f = CS_MIN (f, Ci [p]) ; /* f = min (find (C)) */ for (j = f ; j != -1 ; j = parent [j]) w [j] = 0 ; /* clear workspace w */ for (p = Cp [0] ; p < Cp [1] ; p++) w [Ci [p]] = Cx [p] ; /* w = C */ for (j = f ; j != -1 ; j = parent [j]) /* walk path f up to root */ { p = Lp [j] ; alpha = w [j] / Lx [p] ; /* alpha = w(j) / L(j,j) */ beta2 = beta*beta + sigma*alpha*CS_CONJ(alpha) ; if (beta2 <= 0) break ; /* not positive definite */ beta2 = sqrt (beta2) ; delta = (sigma > 0) ? (beta / beta2) : (beta2 / beta) ; gamma = sigma * CS_CONJ(alpha) / (beta2 * beta) ; Lx [p] = delta * Lx [p] + ((sigma > 0) ? (gamma * w [j]) : 0) ; beta = beta2 ; #ifdef CS_COMPLEX phase = CS_ABS (Lx [p]) / Lx [p] ; /* phase = abs(L(j,j))/L(j,j)*/ Lx [p] *= phase ; /* L(j,j) = L(j,j) * phase */ #endif for (p++ ; p < Lp [j+1] ; p++) { w1 = w [Li [p]] ; w [Li [p]] = w2 = w1 - alpha * Lx [p] ; Lx [p] = delta * Lx [p] + gamma * ((sigma > 0) ? w1 : w2) ; #ifdef CS_COMPLEX Lx [p] *= phase ; /* L(i,j) = L(i,j) * phase */ #endif } } cs_free (w) ; return (beta2 > 0) ; } igraph/src/vector.pmt0000644000176000001440000021667012325372072014406 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_memory.h" #include "igraph_error.h" #include "igraph_random.h" #include "igraph_qsort.h" #include #include /* memcpy & co. */ #include #include /* va_start & co */ #include /** * \ingroup vector * \section about_igraph_vector_t_objects About \type igraph_vector_t objects * * The \type igraph_vector_t data type is a simple and efficient * interface to arrays containing numbers. It is something * similar as (but much simpler than) the \type vector template * in the C++ standard library. * * Vectors are used extensively in \a igraph, all * functions which expect or return a list of numbers use * igraph_vector_t to achieve this. * * The \type igraph_vector_t type usually uses * O(n) space * to store n elements. Sometimes it * uses more, this is because vectors can shrink, but even if they * shrink, the current implementation does not free a single bit of * memory. * * The elements in an \type igraph_vector_t * object are indexed from zero, we follow the usual C convention * here. * * The elements of a vector always occupy a single block of * memory, the starting address of this memory block can be queried * with the \ref VECTOR macro. This way, vector objects can be used * with standard mathematical libraries, like the GNU Scientific * Library. */ /** * \ingroup vector * \section igraph_vector_constructors_and_destructors Constructors and * Destructors * * \type igraph_vector_t objects have to be initialized before using * them, this is analogous to calling a constructor on them. There are a * number of \type igraph_vector_t constructors, for your * convenience. \ref igraph_vector_init() is the basic constructor, it * creates a vector of the given length, filled with zeros. * \ref igraph_vector_copy() creates a new identical copy * of an already existing and initialized vector. \ref * igraph_vector_init_copy() creates a vector by copying a regular C array. * \ref igraph_vector_init_seq() creates a vector containing a regular * sequence with increment one. * * \ref igraph_vector_view() is a special constructor, it allows you to * handle a regular C array as a \type vector without copying * its elements. * * * If a \type igraph_vector_t object is not needed any more, it * should be destroyed to free its allocated memory by calling the * \type igraph_vector_t destructor, \ref igraph_vector_destroy(). * * Note that vectors created by \ref igraph_vector_view() are special, * you mustn't call \ref igraph_vector_destroy() on these. */ /** * \ingroup vector * \function igraph_vector_init * \brief Initializes a vector object (constructor). * * * Every vector needs to be initialized before it can be used, and * there are a number of initialization functions or otherwise called * constructors. This function constructs a vector of the given size and * initializes each entry to 0. Note that \ref igraph_vector_null() can be * used to set each element of a vector to zero. However, if you want a * vector of zeros, it is much faster to use this function than to create a * vector and then invoke \ref igraph_vector_null(). * * * Every vector object initialized by this function should be * destroyed (ie. the memory allocated for it should be freed) when it * is not needed anymore, the \ref igraph_vector_destroy() function is * responsible for this. * \param v Pointer to a not yet initialized vector object. * \param size The size of the vector. * \return error code: * \c IGRAPH_ENOMEM if there is not enough memory. * * Time complexity: operating system dependent, the amount of * \quote time \endquote required to allocate * O(n) elements, * n is the number of elements. */ int FUNCTION(igraph_vector,init) (TYPE(igraph_vector)* v, int long size) { long int alloc_size= size > 0 ? size : 1; if (size < 0) { size=0; } v->stor_begin=igraph_Calloc(alloc_size, BASE); if (v->stor_begin==0) { IGRAPH_ERROR("cannot init vector", IGRAPH_ENOMEM); } v->stor_end=v->stor_begin + alloc_size; v->end=v->stor_begin+size; return 0; } /** * \ingroup vector * \function igraph_vector_view * \brief Handle a regular C array as a \type igraph_vector_t. * * * This is a special \type igraph_vector_t constructor. It allows to * handle a regular C array as a \type igraph_vector_t temporarily. * Be sure that you \em don't ever call the destructor (\ref * igraph_vector_destroy()) on objects created by this constructor. * \param v Pointer to an uninitialized \type igraph_vector_t object. * \param data Pointer, the C array. * \param length The length of the C array. * \return Pointer to the vector object, the same as the * \p v parameter, for convenience. * * Time complexity: O(1) */ const TYPE(igraph_vector)*FUNCTION(igraph_vector,view) (const TYPE(igraph_vector) *v, const BASE *data, long int length) { TYPE(igraph_vector) *v2=(TYPE(igraph_vector)*)v; v2->stor_begin=(BASE*)data; v2->stor_end=(BASE*)data+length; v2->end=v2->stor_end; return v; } #ifndef BASE_COMPLEX /** * \ingroup vector * \function igraph_vector_init_real * \brief Create an \type igraph_vector_t from the parameters. * * * Because of how C and the C library handles variable length argument * lists, it is required that you supply real constants to this * function. This means that * \verbatim igraph_vector_t v; * igraph_vector_init_real(&v, 5, 1,2,3,4,5); \endverbatim * is an error at runtime and the results are undefined. This is * the proper way: * \verbatim igraph_vector_t v; * igraph_vector_init_real(&v, 5, 1.0,2.0,3.0,4.0,5.0); \endverbatim * \param v Pointer to an uninitialized \type igraph_vector_t object. * \param no Positive integer, the number of \type igraph_real_t * parameters to follow. * \param ... The elements of the vector. * \return Error code, this can be \c IGRAPH_ENOMEM * if there isn't enough memory to allocate the vector. * * \sa \ref igraph_vector_init_real_end(), \ref igraph_vector_init_int() for similar * functions. * * Time complexity: depends on the time required to allocate memory, * but at least O(n), the number of * elements in the vector. */ int FUNCTION(igraph_vector,init_real)(TYPE(igraph_vector) *v, int no, ...) { int i=0; va_list ap; IGRAPH_CHECK(FUNCTION(igraph_vector,init)(v, no)); va_start(ap, no); for (i=0; i * This constructor is similar to \ref igraph_vector_init_real(), the only * difference is that instead of giving the number of elements in the * vector, a special marker element follows the last real vector * element. * \param v Pointer to an uninitialized \type igraph_vector_t object. * \param endmark This element will signal the end of the vector. It * will \em not be part of the vector. * \param ... The elements of the vector. * \return Error code, \c IGRAPH_ENOMEM if there * isn't enough memory. * * \sa \ref igraph_vector_init_real() and \ref igraph_vector_init_int_end() for * similar functions. * * Time complexity: at least O(n) for * n elements plus the time * complexity of the memory allocation. */ int FUNCTION(igraph_vector,init_real_end)(TYPE(igraph_vector) *v, BASE endmark, ...) { int i=0, n=0; va_list ap; va_start(ap, endmark); while (1) { BASE num = (BASE) va_arg(ap, double); if (num == endmark) { break; } n++; } va_end(ap); IGRAPH_CHECK(FUNCTION(igraph_vector,init)(v,n)); IGRAPH_FINALLY(FUNCTION(igraph_vector,destroy), v); va_start(ap, endmark); for (i=0; i * This function is similar to \ref igraph_vector_init_real(), but it expects * \type int parameters. It is important that all parameters * should be of this type, otherwise the result of the function call * is undefined. * \param v Pointer to an uninitialized \type igraph_vector_t object. * \param no The number of \type int parameters to follow. * \param ... The elements of the vector. * \return Error code, \c IGRAPH_ENOMEM if there is * not enough memory. * \sa \ref igraph_vector_init_real() and igraph_vector_init_int_end(), these are * similar functions. * * Time complexity: at least O(n) for * n elements plus the time * complexity of the memory allocation. */ int FUNCTION(igraph_vector,init_int)(TYPE(igraph_vector) *v, int no, ...) { int i=0; va_list ap; IGRAPH_CHECK(FUNCTION(igraph_vector,init)(v, no)); va_start(ap, no); for (i=0; i * This constructor is similar to \ref igraph_vector_init_int(), the only * difference is that instead of giving the number of elements in the * vector, a special marker element follows the last real vector * element. * \param v Pointer to an uninitialized \type igraph_vector_t object. * \param endmark This element will signal the end of the vector. It * will \em not be part of the vector. * \param ... The elements of the vector. * \return Error code, \c IGRAPH_ENOMEM if there * isn't enough memory. * * \sa \ref igraph_vector_init_int() and \ref igraph_vector_init_real_end() for * similar functions. * * Time complexity: at least O(n) for * n elements plus the time * complexity of the memory allocation. */ int FUNCTION(igraph_vector_init,int_end)(TYPE(igraph_vector) *v, int endmark, ...) { int i=0, n=0; va_list ap; va_start(ap, endmark); while (1) { int num = va_arg(ap, int); if (num == endmark) { break; } n++; } va_end(ap); IGRAPH_CHECK(FUNCTION(igraph_vector,init)(v, n)); IGRAPH_FINALLY(FUNCTION(igraph_vector,destroy), v); va_start(ap, endmark); for (i=0; i * All vectors initialized by \ref igraph_vector_init() should be properly * destroyed by this function. A destroyed vector needs to be * reinitialized by \ref igraph_vector_init(), \ref igraph_vector_init_copy() or * another constructor. * \param v Pointer to the (previously initialized) vector object to * destroy. * * Time complexity: operating system dependent. */ void FUNCTION(igraph_vector,destroy) (TYPE(igraph_vector)* v) { assert(v != 0); if (v->stor_begin != 0) { igraph_Free(v->stor_begin); v->stor_begin = NULL; } } /** * \ingroup vector * \function igraph_vector_capacity * \brief Returns the allocated capacity of the vector * * Note that this might be different from the size of the vector (as * queried by \ref igraph_vector_size(), and specifies how many elements * the vector can hold, without reallocation. * \param v Pointer to the (previously initialized) vector object * to query. * \return The allocated capacity. * * \sa \ref igraph_vector_size(). * * Time complexity: O(1). */ long int FUNCTION(igraph_vector,capacity)(const TYPE(igraph_vector)*v) { return v->stor_end - v->stor_begin; } /** * \ingroup vector * \function igraph_vector_reserve * \brief Reserves memory for a vector. * * * \a igraph vectors are flexible, they can grow and * shrink. Growing * however occasionally needs the data in the vector to be copied. * In order to avoid this, you can call this function to reserve space for * future growth of the vector. * * * Note that this function does \em not change the size of the * vector. Let us see a small example to clarify things: if you * reserve space for 100 elements and the size of your * vector was (and still is) 60, then you can surely add additional 40 * elements to your vector before it will be copied. * \param v The vector object. * \param size The new \em allocated size of the vector. * \return Error code: * \c IGRAPH_ENOMEM if there is not enough memory. * * Time complexity: operating system dependent, should be around * O(n), n * is the new allocated size of the vector. */ int FUNCTION(igraph_vector,reserve) (TYPE(igraph_vector)* v, long int size) { long int actual_size=FUNCTION(igraph_vector,size)(v); BASE *tmp; assert(v != NULL); assert(v->stor_begin != NULL); if (size <= FUNCTION(igraph_vector,size)(v)) { return 0; } tmp=igraph_Realloc(v->stor_begin, (size_t) size, BASE); if (tmp==0) { IGRAPH_ERROR("cannot reserve space for vector", IGRAPH_ENOMEM); } v->stor_begin=tmp; v->stor_end=v->stor_begin + size; v->end=v->stor_begin+actual_size; return 0; } /** * \ingroup vector * \function igraph_vector_empty * \brief Decides whether the size of the vector is zero. * * \param v The vector object. * \return Non-zero number (true) if the size of the vector is zero and * zero (false) otherwise. * * Time complexity: O(1). */ igraph_bool_t FUNCTION(igraph_vector,empty) (const TYPE(igraph_vector)* v) { assert(v != NULL); assert(v->stor_begin != NULL); return v->stor_begin == v->end; } /** * \ingroup vector * \function igraph_vector_size * \brief Gives the size (=length) of the vector. * * \param v The vector object * \return The size of the vector. * * Time complexity: O(1). */ long int FUNCTION(igraph_vector,size) (const TYPE(igraph_vector)* v) { assert(v != NULL); assert(v->stor_begin != NULL); return v->end - v->stor_begin; } /** * \ingroup vector * \function igraph_vector_clear * \brief Removes all elements from a vector. * * * This function simply sets the size of the vector to zero, it does * not free any allocated memory. For that you have to call * \ref igraph_vector_destroy(). * \param v The vector object. * * Time complexity: O(1). */ void FUNCTION(igraph_vector,clear) (TYPE(igraph_vector)* v) { assert(v != NULL); assert(v->stor_begin != NULL); v->end = v->stor_begin; } /** * \ingroup vector * \function igraph_vector_push_back * \brief Appends one element to a vector. * * * This function resizes the vector to be one element longer and * sets the very last element in the vector to \p e. * \param v The vector object. * \param e The element to append to the vector. * \return Error code: * \c IGRAPH_ENOMEM: not enough memory. * * Time complexity: operating system dependent. What is important is that * a sequence of n * subsequent calls to this function has time complexity * O(n), even if there * hadn't been any space reserved for the new elements by * \ref igraph_vector_reserve(). This is implemented by a trick similar to the C++ * \type vector class: each time more memory is allocated for a * vector, the size of the additionally allocated memory is the same * as the vector's current length. (We assume here that the time * complexity of memory allocation is at most linear.) */ int FUNCTION(igraph_vector,push_back) (TYPE(igraph_vector)* v, BASE e) { assert(v != NULL); assert(v->stor_begin != NULL); /* full, allocate more storage */ if (v->stor_end == v->end) { long int new_size = FUNCTION(igraph_vector,size)(v) * 2; if (new_size == 0) { new_size = 1; } IGRAPH_CHECK(FUNCTION(igraph_vector,reserve)(v, new_size)); } *(v->end) = e; v->end += 1; return 0; } /** * \ingroup vector * \function igraph_vector_insert * \brief Inserts a single element into a vector. * * Note that this function does not do range checking. Insertion will shift the * elements from the position given to the end of the vector one position to the * right, and the new element will be inserted in the empty space created at * the given position. The size of the vector will increase by one. * * \param v The vector object. * \param pos The position where the new element is to be inserted. * \param value The new element to be inserted. */ int FUNCTION(igraph_vector,insert)(TYPE(igraph_vector) *v, long int pos, BASE value) { size_t size = (size_t) FUNCTION(igraph_vector,size)(v); IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(v, (long) size+1)); if (posstor_begin+pos+1, v->stor_begin+pos, sizeof(BASE)*(size - (size_t) pos)); } v->stor_begin[pos] = value; return 0; } /** * \ingroup vector * \section igraph_vector_accessing_elements Accessing elements * * The simplest way to access an element of a vector is to use the * \ref VECTOR macro. This macro can be used both for querying and setting * \type igraph_vector_t elements. If you need a function, \ref * igraph_vector_e() queries and \ref igraph_vector_set() sets an element of a * vector. \ref igraph_vector_e_ptr() returns the address of an element. * * \ref igraph_vector_tail() returns the last element of a non-empty * vector. There is no igraph_vector_head() function * however, as it is easy to write VECTOR(v)[0] * instead. */ /** * \ingroup vector * \function igraph_vector_e * \brief Access an element of a vector. * \param v The \type igraph_vector_t object. * \param pos The position of the element, the index of the first * element is zero. * \return The desired element. * \sa \ref igraph_vector_e_ptr() and the \ref VECTOR macro. * * Time complexity: O(1). */ BASE FUNCTION(igraph_vector,e) (const TYPE(igraph_vector)* v, long int pos) { assert(v != NULL); assert(v->stor_begin != NULL); return * (v->stor_begin + pos); } /** * \ingroup vector * \function igraph_vector_e_ptr * \brief Get the address of an element of a vector * \param v The \type igraph_vector_t object. * \param pos The position of the element, the position of the first * element is zero. * \return Pointer to the desired element. * \sa \ref igraph_vector_e() and the \ref VECTOR macro. * * Time complexity: O(1). */ BASE* FUNCTION(igraph_vector,e_ptr) (const TYPE(igraph_vector)* v, long int pos) { assert(v!=NULL); assert(v->stor_begin != NULL); return v->stor_begin+pos; } /** * \ingroup vector * \function igraph_vector_set * \brief Assignment to an element of a vector. * \param v The \type igraph_vector_t element. * \param pos Position of the element to set. * \param value New value of the element. * \sa \ref igraph_vector_e(). */ void FUNCTION(igraph_vector,set) (TYPE(igraph_vector)* v, long int pos, BASE value) { assert(v != NULL); assert(v->stor_begin != NULL); *(v->stor_begin + pos) = value; } /** * \ingroup vector * \function igraph_vector_null * \brief Sets each element in the vector to zero. * * * Note that \ref igraph_vector_init() sets the elements to zero as well, so * it makes no sense to call this function on a just initialized * vector. Thus if you want to construct a vector of zeros, then you should * use \ref igraph_vector_init(). * \param v The vector object. * * Time complexity: O(n), the size of * the vector. */ void FUNCTION(igraph_vector,null) (TYPE(igraph_vector)* v) { assert(v != NULL); assert(v->stor_begin != NULL); if (FUNCTION(igraph_vector,size)(v)>0) { memset(v->stor_begin, 0, sizeof(BASE)*(size_t) FUNCTION(igraph_vector,size)(v)); } } /** * \function igraph_vector_fill * \brief Fill a vector with a constant element * * Sets each element of the vector to the supplied constant. * \param vector The vector to work on. * \param e The element to fill with. * * Time complexity: O(n), the size of the vector. */ void FUNCTION(igraph_vector,fill) (TYPE(igraph_vector)* v, BASE e) { BASE *ptr; assert(v != NULL); assert(v->stor_begin != NULL); for (ptr = v->stor_begin; ptr < v->end; ptr++) { *ptr = e; } } /** * \ingroup vector * \function igraph_vector_tail * \brief Returns the last element in a vector. * * * It is an error to call this function on an empty vector, the result * is undefined. * \param v The vector object. * \return The last element. * * Time complexity: O(1). */ BASE FUNCTION(igraph_vector,tail)(const TYPE(igraph_vector) *v) { assert(v!=NULL); assert(v->stor_begin != NULL); return *((v->end)-1); } /** * \ingroup vector * \function igraph_vector_pop_back * \brief Removes and returns the last element of a vector. * * * It is an error to call this function with an empty vector. * \param v The vector object. * \return The removed last element. * * Time complexity: O(1). */ BASE FUNCTION(igraph_vector,pop_back)(TYPE(igraph_vector)* v) { BASE tmp; assert(v!=NULL); assert(v->stor_begin != NULL); assert(v->end != v->stor_begin); tmp=FUNCTION(igraph_vector,e)(v, FUNCTION(igraph_vector,size)(v)-1); v->end -= 1; return tmp; } #ifndef NOTORDERED /** * \ingroup vector * \function igraph_vector_sort_cmp * \brief Internal comparison function of vector elements, used by * \ref igraph_vector_sort(). */ int FUNCTION(igraph_vector,sort_cmp)(const void *a, const void *b) { const BASE *da = (const BASE *) a; const BASE *db = (const BASE *) b; return (*da > *db) - (*da < *db); } /** * \ingroup vector * \function igraph_vector_sort * \brief Sorts the elements of the vector into ascending order. * * * This function uses the built-in sort function of the C library. * \param v Pointer to an initialized vector object. * * Time complexity: should be * O(nlogn) for * n * elements. */ void FUNCTION(igraph_vector,sort)(TYPE(igraph_vector) *v) { assert(v != NULL); assert(v->stor_begin != NULL); igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector,size)(v), sizeof(BASE), FUNCTION(igraph_vector,sort_cmp)); } /** * Ascending comparison function passed to qsort from igraph_vector_qsort_ind */ int FUNCTION(igraph_vector,i_qsort_ind_cmp_asc)(const void *p1 , const void *p2) { BASE **pa = (BASE **) p1; BASE **pb = (BASE **) p2; if( **pa < **pb ) return -1; if( **pa > **pb) return 1; return 0; } /** * Descending comparison function passed to qsort from igraph_vector_qsort_ind */ int FUNCTION(igraph_vector,i_qsort_ind_cmp_desc)(const void *p1 , const void *p2) { BASE **pa = (BASE **) p1; BASE **pb = (BASE **) p2; if( **pa < **pb ) return 1; if( **pa > **pb) return -1; return 0; } /** * \function igraph_vector_qsort_ind * \brief Return a permutation of indices that sorts a vector * * Takes an unsorted array \c v as input and computes an array of * indices inds such that v[ inds[i] ], with i increasing from 0, is * an ordered array (either ascending or descending, depending on * \v order). The order of indices for identical elements is not * defined. * * \param v the array to be sorted * \param inds the output array of indices. this must be initialized, * but will be resized * \param descending whether the output array should be sorted in descending * order. * \return Error code. * * This routine uses the C library qsort routine. * Algorithm: 1) create an array of pointers to the elements of v. 2) * Pass this array to qsort. 3) after sorting the difference between * the pointer value and the first pointer value gives its original * position in the array. Use this to set the values of inds. * * Some tests show that this routine is faster than * igraph_vector_heapsort_ind by about 10 percent * for small vectors to a factor of two for large vectors. */ long int FUNCTION(igraph_vector,qsort_ind)(TYPE(igraph_vector) *v, igraph_vector_t *inds, igraph_bool_t descending) { long int i; BASE **vind, *first; size_t n = (size_t) FUNCTION(igraph_vector,size)(v); IGRAPH_CHECK(igraph_vector_resize(inds, (long) n)); if (n==0) { return 0; } vind = igraph_Calloc(n, BASE*); if (vind == 0) { IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM); } for(i=0; i * Note that this function does not free any memory, just sets the * size of the vector to the given one. It can on the other hand * allocate more memory if the new size is larger than the previous * one. In this case the newly appeared elements in the vector are * \em not set to zero, they are uninitialized. * \param v The vector object * \param newsize The new size of the vector. * \return Error code, * \c IGRAPH_ENOMEM if there is not enough * memory. Note that this function \em never returns an error * if the vector is made smaller. * \sa \ref igraph_vector_reserve() for allocating memory for future * extensions of a vector. \ref igraph_vector_resize_min() for * deallocating the unnneded memory for a vector. * * Time complexity: O(1) if the new * size is smaller, operating system dependent if it is larger. In the * latter case it is usually around * O(n), * n is the new size of the vector. */ int FUNCTION(igraph_vector,resize)(TYPE(igraph_vector)* v, long int newsize) { assert(v != NULL); assert(v->stor_begin != NULL); IGRAPH_CHECK(FUNCTION(igraph_vector,reserve)(v, newsize)); v->end = v->stor_begin+newsize; return 0; } /** * \ingroup vector * \function igraph_vector_resize_min * \brief Deallocate the unused memory of a vector. * * * Note that this function involves additional memory allocation and * may result an out-of-memory error. * \param v Pointer to an initialized vector. * \return Error code. * * \sa \ref igraph_vector_resize(), \ref igraph_vector_reserve(). * * Time complexity: operating system dependent. */ int FUNCTION(igraph_vector,resize_min)(TYPE(igraph_vector)*v) { size_t size; BASE *tmp; if (v->stor_end == v->end) { return 0; } size = (size_t) (v->end - v->stor_begin); tmp=igraph_Realloc(v->stor_begin, size, BASE); if (tmp==0) { IGRAPH_ERROR("cannot resize vector", IGRAPH_ENOMEM); } else { v->stor_begin = tmp; v->stor_end = v->end = v->stor_begin + size; } return 0; } #ifndef NOTORDERED /** * \ingroup vector * \function igraph_vector_max * \brief Gives the maximum element of the vector. * * * If the size of the vector is zero, an arbitrary number is * returned. * \param v The vector object. * \return The maximum element. * * Time complexity: O(n), * n is the size of the vector. */ BASE FUNCTION(igraph_vector,max)(const TYPE(igraph_vector)* v) { BASE max; BASE *ptr; assert(v != NULL); assert(v->stor_begin != NULL); max=*(v->stor_begin); ptr=v->stor_begin+1; while (ptr < v->end) { if ((*ptr) > max) { max=*ptr; } ptr++; } return max; } /** * \ingroup vector * \function igraph_vector_which_max * \brief Gives the position of the maximum element of the vector. * * * If the size of the vector is zero, -1 is * returned. * \param v The vector object. * \return The position of the first maximum element. * * Time complexity: O(n), * n is the size of the vector. */ long int FUNCTION(igraph_vector,which_max)(const TYPE(igraph_vector)* v) { long int which=-1; if (!FUNCTION(igraph_vector,empty)(v)) { BASE max; BASE *ptr; long int pos; assert(v != NULL); assert(v->stor_begin != NULL); max=*(v->stor_begin); which=0; ptr=v->stor_begin+1; pos=1; while (ptr < v->end) { if ((*ptr) > max) { max=*ptr; which=pos; } ptr++; pos++; } } return which; } /** * \function igraph_vector_min * \brief Smallest element of a vector. * * The vector must be non-empty. * \param v The input vector. * \return The smallest element of \p v. * * Time complexity: O(n), the number of elements. */ BASE FUNCTION(igraph_vector,min)(const TYPE(igraph_vector)* v) { BASE min; BASE *ptr; assert(v != NULL); assert(v->stor_begin != NULL); min=*(v->stor_begin); ptr=v->stor_begin+1; while (ptr < v->end) { if ((*ptr) < min) { min=*ptr; } ptr++; } return min; } /** * \function igraph_vector_which_min * \brief Index of the smallest element. * * The vector must be non-empty. * If the smallest element is not unique, then the index of the first * is returned. * \param v The input vector. * \return Index of the smallest element. * * Time complexity: O(n), the number of elements. */ long int FUNCTION(igraph_vector,which_min)(const TYPE(igraph_vector)* v) { long int which=-1; if (!FUNCTION(igraph_vector,empty)(v)) { BASE min; BASE *ptr; long int pos; assert(v != NULL); assert(v->stor_begin != NULL); min=*(v->stor_begin); which=0; ptr=v->stor_begin+1; pos=1; while (ptr < v->end) { if ((*ptr) < min) { min=*ptr; which=pos; } ptr++; pos++; } } return which; } #endif /** * \ingroup vector * \function igraph_vector_init_copy * \brief Initializes a vector from an ordinary C array (constructor). * * \param v Pointer to an uninitialized vector object. * \param data A regular C array. * \param length The length of the C array. * \return Error code: * \c IGRAPH_ENOMEM if there is not enough memory. * * Time complexity: operating system specific, usually * O(\p length). */ int FUNCTION(igraph_vector,init_copy)(TYPE(igraph_vector) *v, BASE *data, long int length) { v->stor_begin=igraph_Calloc(length, BASE); if (v->stor_begin==0) { IGRAPH_ERROR("cannot init vector from array", IGRAPH_ENOMEM); } v->stor_end=v->stor_begin+length; v->end=v->stor_end; memcpy(v->stor_begin, data, (size_t) length * sizeof(BASE)); return 0; } /** * \ingroup vector * \function igraph_vector_copy_to * \brief Copies the contents of a vector to a C array. * * * The C array should have sufficient length. * \param v The vector object. * \param to The C array. * * Time complexity: O(n), * n is the size of the vector. */ void FUNCTION(igraph_vector,copy_to)(const TYPE(igraph_vector) *v, BASE *to) { assert(v != NULL); assert(v->stor_begin != NULL); if (v->end != v->stor_begin) { memcpy(to, v->stor_begin, sizeof(BASE) * (size_t) (v->end - v->stor_begin)); } } /** * \ingroup vector * \function igraph_vector_copy * \brief Initializes a vector from another vector object (constructor). * * * The contents of the existing vector object will be copied to * the new one. * \param to Pointer to a not yet initialized vector object. * \param from The original vector object to copy. * \return Error code: * \c IGRAPH_ENOMEM if there is not enough memory. * * Time complexity: operating system dependent, usually * O(n), * n is the size of the vector. */ int FUNCTION(igraph_vector,copy)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { assert(from != NULL); assert(from->stor_begin != NULL); to->stor_begin=igraph_Calloc(FUNCTION(igraph_vector,size)(from), BASE); if (to->stor_begin==0) { IGRAPH_ERROR("cannot copy vector", IGRAPH_ENOMEM); } to->stor_end=to->stor_begin+FUNCTION(igraph_vector,size)(from); to->end=to->stor_end; memcpy(to->stor_begin, from->stor_begin, (size_t) FUNCTION(igraph_vector,size)(from) * sizeof(BASE)); return 0; } /** * \ingroup vector * \function igraph_vector_sum * \brief Calculates the sum of the elements in the vector. * * * For the empty vector 0.0 is returned. * \param v The vector object. * \return The sum of the elements. * * Time complexity: O(n), the size of * the vector. */ BASE FUNCTION(igraph_vector,sum)(const TYPE(igraph_vector) *v) { BASE res=ZERO; BASE *p; assert(v != NULL); assert(v->stor_begin != NULL); for (p=v->stor_begin; pend; p++) { #ifdef SUM SUM(res,res,*p); #else res += *p; #endif } return res; } igraph_real_t FUNCTION(igraph_vector,sumsq)(const TYPE(igraph_vector) *v) { igraph_real_t res=0.0; BASE *p; assert(v != NULL); assert(v->stor_begin != NULL); for (p=v->stor_begin; pend; p++) { #ifdef SQ res += SQ(*p); #else res += (*p) * (*p); #endif } return res; } /** * \ingroup vector * \function igraph_vector_prod * \brief Calculates the product of the elements in the vector. * * * For the empty vector one (1) is returned. * \param v The vector object. * \return The product of the elements. * * Time complexity: O(n), the size of * the vector. */ BASE FUNCTION(igraph_vector,prod)(const TYPE(igraph_vector) *v) { BASE res=ONE; BASE *p; assert(v != NULL); assert(v->stor_begin != NULL); for (p=v->stor_begin; pend; p++) { #ifdef PROD PROD(res,res,*p); #else res *= *p; #endif } return res; } /** * \ingroup vector * \function igraph_vector_cumsum * \brief Calculates the cumulative sum of the elements in the vector. * * * \param to An initialized vector object that will store the cumulative * sums. Element i of this vector will store the sum of the elements * of the 'from' vector, up to and including element i. * \param from The input vector. * \return Error code. * * Time complexity: O(n), the size of the vector. */ int FUNCTION(igraph_vector,cumsum)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { BASE res=ZERO; BASE *p, *p2; assert(from != NULL); assert(from->stor_begin != NULL); assert(to != NULL); assert(to->stor_begin != NULL); IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(to, FUNCTION(igraph_vector,size)(from))); for (p = from->stor_begin, p2 = to->stor_begin; p < from->end; p++, p2++) { #ifdef SUM SUM(res,res,*p); #else res += *p; #endif *p2 = res; } return 0; } #ifndef NOTORDERED /** * \ingroup vector * \function igraph_vector_init_seq * \brief Initializes a vector with a sequence. * * * The vector will contain the numbers \p from, * \p from+1, ..., \p to. * \param v Pointer to an uninitialized vector object. * \param from The lower limit in the sequence (inclusive). * \param to The upper limit in the sequence (inclusive). * \return Error code: * \c IGRAPH_ENOMEM: out of memory. * * Time complexity: O(n), the number * of elements in the vector. */ int FUNCTION(igraph_vector,init_seq)(TYPE(igraph_vector) *v, BASE from, BASE to) { BASE *p; IGRAPH_CHECK(FUNCTION(igraph_vector,init)(v, (long int) (to-from+1))); for (p=v->stor_begin; pend; p++) { *p = from++; } return 0; } #endif /** * \ingroup vector * \function igraph_vector_remove_section * \brief Deletes a section from a vector. * * * Note that this function does not do range checking. The result is * undefined if you supply invalid limits. * \param v The vector object. * \param from The position of the first element to remove. * \param to The position of the first element \em not to remove. * * Time complexity: O(n-from), * n is the number of elements in the * vector. */ void FUNCTION(igraph_vector,remove_section)(TYPE(igraph_vector) *v, long int from, long int to) { assert(v != NULL); assert(v->stor_begin != NULL); /* Not removing from the end? */ if (to < FUNCTION(igraph_vector,size)(v)) { memmove(v->stor_begin+from, v->stor_begin+to, sizeof(BASE) * (size_t) (v->end-v->stor_begin-to)); } v->end -= (to-from); } /** * \ingroup vector * \function igraph_vector_remove * \brief Removes a single element from a vector. * * Note that this function does not do range checking. * \param v The vector object. * \param elem The position of the element to remove. * * Time complexity: O(n-elem), * n is the number of elements in the * vector. */ void FUNCTION(igraph_vector,remove)(TYPE(igraph_vector) *v, long int elem) { assert(v != NULL); assert(v->stor_begin != NULL); FUNCTION(igraph_vector,remove_section)(v, elem, elem+1); } /** * \ingroup vector * \function igraph_vector_move_interval * \brief Copies a section of a vector. * * * The result of this function is undefined if the source and target * intervals overlap. * \param v The vector object. * \param begin The position of the first element to move. * \param end The position of the first element \em not to move. * \param to The target position. * \return Error code, the current implementation always returns with * success. * * Time complexity: O(end-begin). */ int FUNCTION(igraph_vector,move_interval)(TYPE(igraph_vector) *v, long int begin, long int end, long int to) { assert(v != NULL); assert(v->stor_begin != NULL); memcpy(v->stor_begin+to, v->stor_begin+begin, sizeof(BASE) * (size_t) (end-begin)); return 0; } int FUNCTION(igraph_vector,move_interval2)(TYPE(igraph_vector) *v, long int begin, long int end, long int to) { assert(v != NULL); assert(v->stor_begin != NULL); memmove(v->stor_begin+to, v->stor_begin+begin, sizeof(BASE) * (size_t) (end-begin)); return 0; } /** * \ingroup vector * \function igraph_vector_permdelete * \brief Remove elements of a vector (for internal use). */ void FUNCTION(igraph_vector,permdelete)(TYPE(igraph_vector) *v, const igraph_vector_t *index, long int nremove) { long int i, n; assert(v != NULL); assert(v->stor_begin != NULL); n = FUNCTION(igraph_vector,size)(v); for (i=0; iend -= nremove; } #ifndef NOTORDERED /** * \ingroup vector * \function igraph_vector_isininterval * \brief Checks if all elements of a vector are in the given * interval. * * \param v The vector object. * \param low The lower limit of the interval (inclusive). * \param high The higher limit of the interval (inclusive). * \return True (positive integer) if all vector elements are in the * interval, false (zero) otherwise. * * Time complexity: O(n), the number * of elements in the vector. */ igraph_bool_t FUNCTION(igraph_vector,isininterval)(const TYPE(igraph_vector) *v, BASE low, BASE high) { BASE *ptr; assert(v != NULL); assert(v->stor_begin != NULL); for (ptr=v->stor_begin; ptrend; ptr++) { if (*ptr < low || *ptr >high) { return 0; } } return 1; } /** * \ingroup vector * \function igraph_vector_any_smaller * \brief Checks if any element of a vector is smaller than a limit. * * \param v The \type igraph_vector_t object. * \param limit The limit. * \return True (positive integer) if the vector contains at least one * smaller element than \p limit, false (zero) * otherwise. * * Time complexity: O(n), the number * of elements in the vector. */ igraph_bool_t FUNCTION(igraph_vector,any_smaller)(const TYPE(igraph_vector) *v, BASE limit) { BASE *ptr; assert(v != NULL); assert(v->stor_begin != NULL); for (ptr=v->stor_begin; ptrend; ptr++) { if (*ptr < limit) { return 1; } } return 0; } #endif /** * \ingroup vector * \function igraph_vector_all_e * \brief Are all elements equal? * * \param lhs The first vector. * \param rhs The second vector. * \return Positive integer (=true) if the elements in the \p lhs are all * equal to the corresponding elements in \p rhs. Returns \c 0 * (=false) if the lengths of the vectors don't match. * * Time complexity: O(n), the length of the vectors. */ igraph_bool_t FUNCTION(igraph_vector,all_e)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { long int i, s; assert(lhs != 0); assert(rhs != 0); assert(lhs->stor_begin != 0); assert(rhs->stor_begin != 0); s=FUNCTION(igraph_vector,size)(lhs); if (s != FUNCTION(igraph_vector,size)(rhs)) { return 0; } else { for (i=0; istor_begin != 0); assert(rhs->stor_begin != 0); s=FUNCTION(igraph_vector,size)(lhs); if (s != FUNCTION(igraph_vector,size)(rhs)) { return 0; } else { for (i=0; i=r) { return 0; } } return 1; } } /** * \ingroup vector * \function igraph_vector_all_g * \brief Are all elements greater? * * \param lhs The first vector. * \param rhs The second vector. * \return Positive integer (=true) if the elements in the \p lhs are all * greater than the corresponding elements in \p rhs. Returns \c 0 * (=false) if the lengths of the vectors don't match. * * Time complexity: O(n), the length of the vectors. */ igraph_bool_t FUNCTION(igraph_vector,all_g)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { long int i, s; assert(lhs != 0); assert(rhs != 0); assert(lhs->stor_begin != 0); assert(rhs->stor_begin != 0); s=FUNCTION(igraph_vector,size)(lhs); if (s != FUNCTION(igraph_vector,size)(rhs)) { return 0; } else { for (i=0; istor_begin != 0); assert(rhs->stor_begin != 0); s=FUNCTION(igraph_vector,size)(lhs); if (s != FUNCTION(igraph_vector,size)(rhs)) { return 0; } else { for (i=0; ir) { return 0; } } return 1; } } /** * \ingroup vector * \function igraph_vector_all_ge * \brief Are all elements greater or equal? * * \param lhs The first vector. * \param rhs The second vector. * \return Positive integer (=true) if the elements in the \p lhs are all * greater than or equal to the corresponding elements in \p * rhs. Returns \c 0 (=false) if the lengths of the vectors don't * match. * * Time complexity: O(n), the length of the vectors. */ igraph_bool_t FUNCTION(igraph_vector,all_ge)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { long int i, s; assert(lhs != 0); assert(rhs != 0); assert(lhs->stor_begin != 0); assert(rhs->stor_begin != 0); s=FUNCTION(igraph_vector,size)(lhs); if (s != FUNCTION(igraph_vector,size)(rhs)) { return 0; } else { for (i=0; i * It is assumed that the vector is sorted. If the specified element * (\p what) is not in the vector, then the * position of where it should be inserted (to keep the vector sorted) * is returned. * \param v The \type igraph_vector_t object. * \param what The element to search for. * \param pos Pointer to a \type long int. This is set to the * position of an instance of \p what in the * vector if it is present. If \p v does not * contain \p what then * \p pos is set to the position to which it * should be inserted (to keep the the vector sorted of course). * \return Positive integer (true) if \p what is * found in the vector, zero (false) otherwise. * * Time complexity: O(log(n)), * n is the number of elements in * \p v. */ igraph_bool_t FUNCTION(igraph_vector,binsearch)(const TYPE(igraph_vector) *v, BASE what, long int *pos) { return FUNCTION(igraph_i_vector,binsearch_slice)(v, what, pos, 0, FUNCTION(igraph_vector,size)(v)); } igraph_bool_t FUNCTION(igraph_i_vector,binsearch_slice)(const TYPE(igraph_vector) *v, BASE what, long int *pos, long int start, long int end) { long int left = start; long int right = end-1; while (left <= right) { /* (right + left) / 2 could theoretically overflow for long vectors */ long int middle = left + ((right - left) >> 1); if (VECTOR(*v)[middle] > what) { right = middle - 1; } else if (VECTOR(*v)[middle] < what) { left = middle + 1; } else { if (pos != 0) { *pos = middle; } return 1; } } /* if we are here, the element was not found */ if (pos != 0) { *pos = left; } return 0; } /** * \ingroup vector * \function igraph_vector_binsearch2 * \brief Binary search, without returning the index. * * * It is assumed that the vector is sorted. * \param v The \type igraph_vector_t object. * \param what The element to search for. * \return Positive integer (true) if \p what is * found in the vector, zero (false) otherwise. * * Time complexity: O(log(n)), * n is the number of elements in * \p v. */ igraph_bool_t FUNCTION(igraph_vector,binsearch2)(const TYPE(igraph_vector) *v, BASE what) { long int left=0; long int right=FUNCTION(igraph_vector,size)(v)-1; while (left <= right) { /* (right + left) / 2 could theoretically overflow for long vectors */ long int middle = left + ((right - left) >> 1); if (what < VECTOR(*v)[middle]) { right = middle - 1; } else if (what > VECTOR(*v)[middle]) { left = middle + 1; } else { return 1; } } return 0; } #endif /** * \function igraph_vector_scale * \brief Multiply all elements of a vector by a constant * * \param v The vector. * \param by The constant. * \return Error code. The current implementation always returns with success. * * Added in version 0.2. * * Time complexity: O(n), the number of elements in a vector. */ void FUNCTION(igraph_vector,scale)(TYPE(igraph_vector) *v, BASE by) { long int i; for (i=0; istor_begin; while (pend) { #ifdef EQ if (EQ(*p,e)) { #else if (*p==e) { #endif return 1; } p++; } return 0; } /** * \function igraph_vector_search * \brief Search from a given position * * The supplied element \p what is searched in vector \p v, starting * from element index \p from. If found then the index of the first * instance (after \p from) is stored in \p pos. * \param v The input vector. * \param from The index to start searching from. No range checking is * performed. * \param what The element to find. * \param pos If not \c NULL then the index of the found element is * stored here. * \return Boolean, \c TRUE if the element was found, \c FALSE * otherwise. * * Time complexity: O(m), the number of elements to search, the length * of the vector minus the \p from argument. */ igraph_bool_t FUNCTION(igraph_vector,search)(const TYPE(igraph_vector) *v, long int from, BASE what, long int *pos) { long int i, n=FUNCTION(igraph_vector,size)(v); for (i=from; istor_begin+tosize, from->stor_begin, sizeof(BASE) * (size_t) fromsize); to->end=to->stor_begin+tosize+fromsize; return 0; } /** * \function igraph_vector_get_interval */ int FUNCTION(igraph_vector,get_interval)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *res, long int from, long int to) { IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(res, to-from)); memcpy(res->stor_begin, v->stor_begin+from, (size_t) (to-from) * sizeof(BASE)); return 0; } #ifndef NOTORDERED /** * \function igraph_vector_maxdifference * \brief The maximum absolute difference of \p m1 and \p m2 * * The element with the largest absolute value in \p m1 - \p m2 is * returned. Both vectors must be non-empty, but they not need to have * the same length, the extra elements in the longer vector are ignored. * \param m1 The first vector. * \param m2 The second vector. * \return The maximum absolute difference of \p m1 and \p m2. * * Time complexity: O(n), the number of elements in the shorter * vector. */ BASE FUNCTION(igraph_vector,maxdifference)(const TYPE(igraph_vector) *m1, const TYPE(igraph_vector) *m2) { long int n1=FUNCTION(igraph_vector,size)(m1); long int n2=FUNCTION(igraph_vector,size)(m2); long int n= n1 < n2 ? n1 : n2; long int i; BASE diff=ZERO; for (i=0; i diff) { diff=d; } } return diff; } #endif /** * \function igraph_vector_update * \brief Update a vector from another one. * * After this operation the contents of \p to will be exactly the same * \p from. \p to will be resized if it was originally shorter or * longer than \p from. * \param to The vector to update. * \param from The vector to update from. * \return Error code. * * Time complexity: O(n), the number of elements in \p from. */ int FUNCTION(igraph_vector,update)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { size_t n=(size_t) FUNCTION(igraph_vector,size)(from); FUNCTION(igraph_vector,resize)(to, (long) n); memcpy(to->stor_begin, from->stor_begin, sizeof(BASE)*n); return 0; } /** * \function igraph_vector_swap * \brief Swap elements of two vectors. * * The two vectors must have the same length, otherwise an error * happens. * \param v1 The first vector. * \param v2 The second vector. * \return Error code. * * Time complexity: O(n), the length of the vectors. */ int FUNCTION(igraph_vector,swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) { long int i, n1=FUNCTION(igraph_vector,size)(v1); long int n2=FUNCTION(igraph_vector,size)(v2); if (n1 != n2) { IGRAPH_ERROR("Vectors must have the same number of elements for swapping", IGRAPH_EINVAL); } for (i=0; i * The Fisher-Yates shuffle ensures that every implementation is * equally probable when using a proper randomness source. Of course * this does not apply to pseudo-random generators as the cycle of * these generators is less than the number of possible permutations * of the vector if the vector is long enough. * \param v The vector object. * \return Error code, currently always \c IGRAPH_SUCCESS. * * Time complexity: O(n), * n is the number of elements in the * vector. * * * References: * \clist * \cli (Fisher & Yates 1963) * R. A. Fisher and F. Yates. \emb Statistical Tables for Biological, * Agricultural and Medical Research. \eme Oliver and Boyd, 6th edition, * 1963, page 37. * \cli (Knuth 1998) * D. E. Knuth. \emb Seminumerical Algorithms, \eme volume 2 of \emb The Art * of Computer Programming. \eme Addison-Wesley, 3rd edition, 1998, page 145. * \endclist * * \example examples/simple/igraph_fisher_yates_shuffle.c */ int FUNCTION(igraph_vector,shuffle)(TYPE(igraph_vector) *v) { long int n = FUNCTION(igraph_vector,size)(v); long int k; BASE dummy; RNG_BEGIN(); while (n > 1) { k = RNG_INTEGER(0, n-1); n--; dummy = VECTOR(*v)[n]; VECTOR(*v)[n] = VECTOR(*v)[k]; VECTOR(*v)[k] = dummy; } RNG_END(); return IGRAPH_SUCCESS; } /** * \function igraph_vector_add * \brief Add two vectors. * * Add the elements of \p v2 to \p v1, the result is stored in \p * v1. The two vectors must have the same length. * \param v1 The first vector, the result will be stored here. * \param v2 The second vector, its contents will be unchanged. * \return Error code. * * Time complexity: O(n), the number of elements. */ int FUNCTION(igraph_vector,add)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2) { long int n1=FUNCTION(igraph_vector,size)(v1); long int n2=FUNCTION(igraph_vector,size)(v2); long int i; if (n1 != n2) { IGRAPH_ERROR("Vectors must have the same number of elements for swapping", IGRAPH_EINVAL); } for (i=0; i= 0 ? VECTOR(*v)[i] : -VECTOR(*v)[i]; } #endif return 0; } #endif #ifndef NOTORDERED /** * \function igraph_vector_minmax * \brief Minimum and maximum elements of a vector. * * Handy if you want to have both the smallest and largest element of * a vector. The vector is only traversed once. The vector must by non-empty. * \param v The input vector. It must contain at least one element. * \param min Pointer to a base type variable, the minimum is stored * here. * \param max Pointer to a base type variable, the maximum is stored * here. * \return Error code. * * Time complexity: O(n), the number of elements. */ int FUNCTION(igraph_vector,minmax)(const TYPE(igraph_vector) *v, BASE *min, BASE *max) { long int n=FUNCTION(igraph_vector,size)(v); long int i; *min=*max=VECTOR(*v)[0]; for (i=1; i *max) { *max=tmp; } else if (tmp < *min) { *min=tmp; } } return 0; } /** * \function igraph_vector_which_minmax * \brief Index of the minimum and maximum elements * * Handy if you need the indices of the smallest and largest * elements. The vector is traversed only once. The vector must to * non-empty. * \param v The input vector. It must contain at least one element. * \param which_min The index of the minimum element will be stored * here. * \param which_max The index of the maximum element will be stored * here. * \return Error code. * * Time complexity: O(n), the number of elements. */ int FUNCTION(igraph_vector,which_minmax)(const TYPE(igraph_vector) *v, long int *which_min, long int *which_max) { long int n=FUNCTION(igraph_vector,size)(v); long int i; BASE min, max; *which_min=*which_max=0; min=max=VECTOR(*v)[0]; for (i=1; i max) { max=tmp; *which_max=i; } else if (tmp < min) { min=tmp; *which_min=i; } } return 0; } #endif /** * \function igraph_vector_isnull * \brief Are all elements zero? * * Checks whether all elements of a vector are zero. * \param v The input vector * \return Boolean, \c TRUE if the vector contains only zeros, \c * FALSE otherwise. * * Time complexity: O(n), the number of elements. */ igraph_bool_t FUNCTION(igraph_vector,isnull)(const TYPE(igraph_vector) *v) { long int n=FUNCTION(igraph_vector,size)(v); long int i=0; #ifdef EQ while (i * Instead of the naive intersection which takes O(n), this function uses * the set intersection method of Ricardo Baeza-Yates, which is more efficient * when one of the vectors is significantly smaller than the other, and * gives similar performance on average when the two vectors are equal. * * * The algorithm keeps the multiplicities of the elements: if an element appears * k1 times in the first vector and k2 times in the second, the result * will include that element min(k1, k2) times. * * * Reference: Baeza-Yates R: A fast set intersection algorithm for sorted * sequences. In: Lecture Notes in Computer Science, vol. 3109/2004, pp. * 400--408, 2004. Springer Berlin/Heidelberg. ISBN: 978-3-540-22341-2. * * \param v1 the first vector * \param v2 the second vector * \param result the result vector, which will also be sorted. * * Time complexity: O(m log(n)) where m is the size of the smaller vector * and n is the size of the larger one. */ int FUNCTION(igraph_vector,intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { long int size1, size2; size1 = FUNCTION(igraph_vector,size)(v1); size2 = FUNCTION(igraph_vector,size)(v2); FUNCTION(igraph_vector,clear)(result); if (size1 == 0 || size2 == 0) return 0; IGRAPH_CHECK(FUNCTION(igraph_i_vector,intersect_sorted)( v1, 0, size1, v2, 0, size2, result)); return 0; } int FUNCTION(igraph_i_vector,intersect_sorted)( const TYPE(igraph_vector) *v1, long int begin1, long int end1, const TYPE(igraph_vector) *v2, long int begin2, long int end2, TYPE(igraph_vector) *result) { long int size1, size2, probe1, probe2; if (begin1 == end1 || begin2 == end2) return 0; size1 = end1 - begin1; size2 = end2 - begin2; if (size1 < size2) { probe1 = begin1 + (size1 >> 1); /* pick the median element */ FUNCTION(igraph_i_vector,binsearch_slice)(v2, VECTOR(*v1)[probe1], &probe2, begin2, end2); IGRAPH_CHECK(FUNCTION(igraph_i_vector,intersect_sorted)( v1, begin1, probe1, v2, begin2, probe2, result )); if (!(probe2 == end2 || VECTOR(*v1)[probe1] < VECTOR(*v2)[probe2])) { IGRAPH_CHECK(FUNCTION(igraph_vector,push_back)(result, VECTOR(*v2)[probe2])); probe2++; } IGRAPH_CHECK(FUNCTION(igraph_i_vector,intersect_sorted)( v1, probe1+1, end1, v2, probe2, end2, result )); } else { probe2 = begin2 + (size2 >> 1); /* pick the median element */ FUNCTION(igraph_i_vector,binsearch_slice)(v1, VECTOR(*v2)[probe2], &probe1, begin1, end1); IGRAPH_CHECK(FUNCTION(igraph_i_vector,intersect_sorted)( v1, begin1, probe1, v2, begin2, probe2, result )); if (!(probe1 == end1 || VECTOR(*v2)[probe2] < VECTOR(*v1)[probe1])) { IGRAPH_CHECK(FUNCTION(igraph_vector,push_back)(result, VECTOR(*v2)[probe2])); probe1++; } IGRAPH_CHECK(FUNCTION(igraph_i_vector,intersect_sorted)( v1, probe1, end1, v2, probe2+1, end2, result )); } return 0; } /** * \function igraph_vector_difference_sorted * \brief Calculates the difference between two sorted vectors (considered as sets) * * The elements that are contained in only the first vector but not the second are * stored in the result vector. All three vectors must be initialized. * * \param v1 the first vector * \param v2 the second vector * \param result the result vector */ int FUNCTION(igraph_vector,difference_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { long int i, j, i0, j0; i0 = FUNCTION(igraph_vector,size)(v1); j0 = FUNCTION(igraph_vector,size)(v2); i = j = 0; if (i0 == 0) { /* v1 is empty, this is easy */ FUNCTION(igraph_vector,clear)(result); return IGRAPH_SUCCESS; } if (j0 == 0) { /* v2 is empty, this is easy */ IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(result, i0)); memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i0); return IGRAPH_SUCCESS; } FUNCTION(igraph_vector,clear)(result); /* Copy the part of v1 that is less than the first element of v2 */ while (i < i0 && VECTOR(*v1)[i] < VECTOR(*v2)[j]) { i++; } if (i > 0) { IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(result, i)); memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i); } while (i < i0 && j < j0) { BASE element = VECTOR(*v1)[i]; if (element == VECTOR(*v2)[j]) { i++; j++; while (i < i0 && VECTOR(*v1)[i] == element) { i++; } while (j < j0 && VECTOR(*v2)[j] == element) { j++; } } else if (element < VECTOR(*v2)[j]) { IGRAPH_CHECK(FUNCTION(igraph_vector,push_back)(result, element)); i++; } else j++; } if (i < i0) { long int oldsize = FUNCTION(igraph_vector,size)(result); IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(result, oldsize+i0-i)); memcpy(result->stor_begin+oldsize, v1->stor_begin+i, sizeof(BASE)* (size_t) (i0-i)); } return 0; } #endif #if defined(OUT_FORMAT) #ifndef USING_R int FUNCTION(igraph_vector,print)(const TYPE(igraph_vector) *v) { long int i, n=FUNCTION(igraph_vector,size)(v); if (n!=0) { #ifdef PRINTFUNC PRINTFUNC(VECTOR(*v)[0]); #else printf(OUT_FORMAT, VECTOR(*v)[0]); #endif } for (i=1; istor_begin); v->stor_begin = tmp; v->stor_end = v->end = tmp + n; return 0; } igraph/src/glpscf.c0000644000176000001440000004770512325527073014010 0ustar ripleyusers/* glpscf.c (Schur complement factorization) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wself-assign" #endif #include "glpenv.h" #include "glpscf.h" #define xfault xerror #define _GLPSCF_DEBUG 0 #define eps 1e-10 /*********************************************************************** * NAME * * scf_create_it - create Schur complement factorization * * SYNOPSIS * * #include "glpscf.h" * SCF *scf_create_it(int n_max); * * DESCRIPTION * * The routine scf_create_it creates the factorization of matrix C, * which initially has no rows and columns. * * The parameter n_max specifies the maximal order of matrix C to be * factorized, 1 <= n_max <= 32767. * * RETURNS * * The routine scf_create_it returns a pointer to the structure SCF, * which defines the factorization. */ SCF *scf_create_it(int n_max) { SCF *scf; #if _GLPSCF_DEBUG xprintf("scf_create_it: warning: debug mode enabled\n"); #endif if (!(1 <= n_max && n_max <= 32767)) xfault("scf_create_it: n_max = %d; invalid parameter\n", n_max); scf = xmalloc(sizeof(SCF)); scf->n_max = n_max; scf->n = 0; scf->f = xcalloc(1 + n_max * n_max, sizeof(double)); scf->u = xcalloc(1 + n_max * (n_max + 1) / 2, sizeof(double)); scf->p = xcalloc(1 + n_max, sizeof(int)); scf->t_opt = SCF_TBG; scf->rank = 0; #if _GLPSCF_DEBUG scf->c = xcalloc(1 + n_max * n_max, sizeof(double)); #else scf->c = NULL; #endif scf->w = xcalloc(1 + n_max, sizeof(double)); return scf; } /*********************************************************************** * The routine f_loc determines location of matrix element F[i,j] in * the one-dimensional array f. */ static int f_loc(SCF *scf, int i, int j) { int n_max = scf->n_max; int n = scf->n; xassert(1 <= i && i <= n); xassert(1 <= j && j <= n); return (i - 1) * n_max + j; } /*********************************************************************** * The routine u_loc determines location of matrix element U[i,j] in * the one-dimensional array u. */ static int u_loc(SCF *scf, int i, int j) { int n_max = scf->n_max; int n = scf->n; xassert(1 <= i && i <= n); xassert(i <= j && j <= n); return (i - 1) * n_max + j - i * (i - 1) / 2; } /*********************************************************************** * The routine bg_transform applies Bartels-Golub version of gaussian * elimination to restore triangular structure of matrix U. * * On entry matrix U has the following structure: * * 1 k n * 1 * * * * * * * * * * * . * * * * * * * * * * . . * * * * * * * * * . . . * * * * * * * * k . . . . * * * * * * * . . . . . * * * * * * . . . . . . * * * * * . . . . . . . * * * * . . . . . . . . * * * n . . . . # # # # # # * * where '#' is a row spike to be eliminated. * * Elements of n-th row are passed separately in locations un[k], ..., * un[n]. On exit the content of the array un is destroyed. * * REFERENCES * * R.H.Bartels, G.H.Golub, "The Simplex Method of Linear Programming * Using LU-decomposition", Comm. ACM, 12, pp. 266-68, 1969. */ static void bg_transform(SCF *scf, int k, double un[]) { int n = scf->n; double *f = scf->f; double *u = scf->u; int j, k1, kj, kk, n1, nj; double t; xassert(1 <= k && k <= n); /* main elimination loop */ for (k = k; k < n; k++) { /* determine location of U[k,k] */ kk = u_loc(scf, k, k); /* determine location of F[k,1] */ k1 = f_loc(scf, k, 1); /* determine location of F[n,1] */ n1 = f_loc(scf, n, 1); /* if |U[k,k]| < |U[n,k]|, interchange k-th and n-th rows to provide |U[k,k]| >= |U[n,k]| */ if (fabs(u[kk]) < fabs(un[k])) { /* interchange k-th and n-th rows of matrix U */ for (j = k, kj = kk; j <= n; j++, kj++) t = u[kj], u[kj] = un[j], un[j] = t; /* interchange k-th and n-th rows of matrix F to keep the main equality F * C = U * P */ for (j = 1, kj = k1, nj = n1; j <= n; j++, kj++, nj++) t = f[kj], f[kj] = f[nj], f[nj] = t; } /* now |U[k,k]| >= |U[n,k]| */ /* if U[k,k] is too small in the magnitude, replace U[k,k] and U[n,k] by exact zero */ if (fabs(u[kk]) < eps) u[kk] = un[k] = 0.0; /* if U[n,k] is already zero, elimination is not needed */ if (un[k] == 0.0) continue; /* compute gaussian multiplier t = U[n,k] / U[k,k] */ t = un[k] / u[kk]; /* apply gaussian elimination to nullify U[n,k] */ /* (n-th row of U) := (n-th row of U) - t * (k-th row of U) */ for (j = k+1, kj = kk+1; j <= n; j++, kj++) un[j] -= t * u[kj]; /* (n-th row of F) := (n-th row of F) - t * (k-th row of F) to keep the main equality F * C = U * P */ for (j = 1, kj = k1, nj = n1; j <= n; j++, kj++, nj++) f[nj] -= t * f[kj]; } /* if U[n,n] is too small in the magnitude, replace it by exact zero */ if (fabs(un[n]) < eps) un[n] = 0.0; /* store U[n,n] in a proper location */ u[u_loc(scf, n, n)] = un[n]; return; } /*********************************************************************** * The routine givens computes the parameters of Givens plane rotation * c = cos(teta) and s = sin(teta) such that: * * ( c -s ) ( a ) ( r ) * ( ) ( ) = ( ) , * ( s c ) ( b ) ( 0 ) * * where a and b are given scalars. * * REFERENCES * * G.H.Golub, C.F.Van Loan, "Matrix Computations", 2nd ed. */ static void givens(double a, double b, double *c, double *s) { double t; if (b == 0.0) (*c) = 1.0, (*s) = 0.0; else if (fabs(a) <= fabs(b)) t = - a / b, (*s) = 1.0 / sqrt(1.0 + t * t), (*c) = (*s) * t; else t = - b / a, (*c) = 1.0 / sqrt(1.0 + t * t), (*s) = (*c) * t; return; } /*---------------------------------------------------------------------- * The routine gr_transform applies Givens plane rotations to restore * triangular structure of matrix U. * * On entry matrix U has the following structure: * * 1 k n * 1 * * * * * * * * * * * . * * * * * * * * * * . . * * * * * * * * * . . . * * * * * * * * k . . . . * * * * * * * . . . . . * * * * * * . . . . . . * * * * * . . . . . . . * * * * . . . . . . . . * * * n . . . . # # # # # # * * where '#' is a row spike to be eliminated. * * Elements of n-th row are passed separately in locations un[k], ..., * un[n]. On exit the content of the array un is destroyed. * * REFERENCES * * R.H.Bartels, G.H.Golub, "The Simplex Method of Linear Programming * Using LU-decomposition", Comm. ACM, 12, pp. 266-68, 1969. */ static void gr_transform(SCF *scf, int k, double un[]) { int n = scf->n; double *f = scf->f; double *u = scf->u; int j, k1, kj, kk, n1, nj; double c, s; xassert(1 <= k && k <= n); /* main elimination loop */ for (k = k; k < n; k++) { /* determine location of U[k,k] */ kk = u_loc(scf, k, k); /* determine location of F[k,1] */ k1 = f_loc(scf, k, 1); /* determine location of F[n,1] */ n1 = f_loc(scf, n, 1); /* if both U[k,k] and U[n,k] are too small in the magnitude, replace them by exact zero */ if (fabs(u[kk]) < eps && fabs(un[k]) < eps) u[kk] = un[k] = 0.0; /* if U[n,k] is already zero, elimination is not needed */ if (un[k] == 0.0) continue; /* compute the parameters of Givens plane rotation */ givens(u[kk], un[k], &c, &s); /* apply Givens rotation to k-th and n-th rows of matrix U */ for (j = k, kj = kk; j <= n; j++, kj++) { double ukj = u[kj], unj = un[j]; u[kj] = c * ukj - s * unj; un[j] = s * ukj + c * unj; } /* apply Givens rotation to k-th and n-th rows of matrix F to keep the main equality F * C = U * P */ for (j = 1, kj = k1, nj = n1; j <= n; j++, kj++, nj++) { double fkj = f[kj], fnj = f[nj]; f[kj] = c * fkj - s * fnj; f[nj] = s * fkj + c * fnj; } } /* if U[n,n] is too small in the magnitude, replace it by exact zero */ if (fabs(un[n]) < eps) un[n] = 0.0; /* store U[n,n] in a proper location */ u[u_loc(scf, n, n)] = un[n]; return; } /*********************************************************************** * The routine transform restores triangular structure of matrix U. * It is a driver to the routines bg_transform and gr_transform (see * comments to these routines above). */ static void transform(SCF *scf, int k, double un[]) { switch (scf->t_opt) { case SCF_TBG: bg_transform(scf, k, un); break; case SCF_TGR: gr_transform(scf, k, un); break; default: xassert(scf != scf); } return; } /*********************************************************************** * The routine estimate_rank estimates the rank of matrix C. * * Since all transformations applied to matrix F are non-singular, * and F is assumed to be well conditioned, from the main equaility * F * C = U * P it follows that rank(C) = rank(U), where rank(U) is * estimated as the number of non-zero diagonal elements of U. */ static int estimate_rank(SCF *scf) { int n_max = scf->n_max; int n = scf->n; double *u = scf->u; int i, ii, inc, rank = 0; for (i = 1, ii = u_loc(scf, i, i), inc = n_max; i <= n; i++, ii += inc, inc--) if (u[ii] != 0.0) rank++; return rank; } #if _GLPSCF_DEBUG /*********************************************************************** * The routine check_error computes the maximal relative error between * left- and right-hand sides of the main equality F * C = U * P. (This * routine is intended only for debugging.) */ static void check_error(SCF *scf, const char *func) { int n = scf->n; double *f = scf->f; double *u = scf->u; int *p = scf->p; double *c = scf->c; int i, j, k; double d, dmax = 0.0, s, t; xassert(c != NULL); for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { /* compute element (i,j) of product F * C */ s = 0.0; for (k = 1; k <= n; k++) s += f[f_loc(scf, i, k)] * c[f_loc(scf, k, j)]; /* compute element (i,j) of product U * P */ k = p[j]; t = (i <= k ? u[u_loc(scf, i, k)] : 0.0); /* compute the maximal relative error */ d = fabs(s - t) / (1.0 + fabs(t)); if (dmax < d) dmax = d; } } if (dmax > 1e-8) xprintf("%s: dmax = %g; relative error too large\n", func, dmax); return; } #endif /*********************************************************************** * NAME * * scf_update_exp - update factorization on expanding C * * SYNOPSIS * * #include "glpscf.h" * int scf_update_exp(SCF *scf, const double x[], const double y[], * double z); * * DESCRIPTION * * The routine scf_update_exp updates the factorization of matrix C on * expanding it by adding a new row and column as follows: * * ( C x ) * new C = ( ) * ( y' z ) * * where x[1,...,n] is a new column, y[1,...,n] is a new row, and z is * a new diagonal element. * * If on entry the factorization is empty, the parameters x and y can * be specified as NULL. * * RETURNS * * 0 The factorization has been successfully updated. * * SCF_ESING * The factorization has been successfully updated, however, new * matrix C is singular within working precision. Note that the new * factorization remains valid. * * SCF_ELIMIT * There is not enough room to expand the factorization, because * n = n_max. The factorization remains unchanged. * * ALGORITHM * * We can see that: * * ( F 0 ) ( C x ) ( FC Fx ) ( UP Fx ) * ( ) ( ) = ( ) = ( ) = * ( 0 1 ) ( y' z ) ( y' z ) ( y' z ) * * ( U Fx ) ( P 0 ) * = ( ) ( ), * ( y'P' z ) ( 0 1 ) * * therefore to keep the main equality F * C = U * P we can take: * * ( F 0 ) ( U Fx ) ( P 0 ) * new F = ( ), new U = ( ), new P = ( ), * ( 0 1 ) ( y'P' z ) ( 0 1 ) * * and eliminate the row spike y'P' in the last row of new U to restore * its upper triangular structure. */ int scf_update_exp(SCF *scf, const double x[], const double y[], double z) { int n_max = scf->n_max; int n = scf->n; double *f = scf->f; double *u = scf->u; int *p = scf->p; #if _GLPSCF_DEBUG double *c = scf->c; #endif double *un = scf->w; int i, ij, in, j, k, nj, ret = 0; double t; /* check if the factorization can be expanded */ if (n == n_max) { /* there is not enough room */ ret = SCF_ELIMIT; goto done; } /* increase the order of the factorization */ scf->n = ++n; /* fill new zero column of matrix F */ for (i = 1, in = f_loc(scf, i, n); i < n; i++, in += n_max) f[in] = 0.0; /* fill new zero row of matrix F */ for (j = 1, nj = f_loc(scf, n, j); j < n; j++, nj++) f[nj] = 0.0; /* fill new unity diagonal element of matrix F */ f[f_loc(scf, n, n)] = 1.0; /* compute new column of matrix U, which is (old F) * x */ for (i = 1; i < n; i++) { /* u[i,n] := (i-th row of old F) * x */ t = 0.0; for (j = 1, ij = f_loc(scf, i, 1); j < n; j++, ij++) t += f[ij] * x[j]; u[u_loc(scf, i, n)] = t; } /* compute new (spiked) row of matrix U, which is (old P) * y */ for (j = 1; j < n; j++) un[j] = y[p[j]]; /* store new diagonal element of matrix U, which is z */ un[n] = z; /* expand matrix P */ p[n] = n; #if _GLPSCF_DEBUG /* expand matrix C */ /* fill its new column, which is x */ for (i = 1, in = f_loc(scf, i, n); i < n; i++, in += n_max) c[in] = x[i]; /* fill its new row, which is y */ for (j = 1, nj = f_loc(scf, n, j); j < n; j++, nj++) c[nj] = y[j]; /* fill its new diagonal element, which is z */ c[f_loc(scf, n, n)] = z; #endif /* restore upper triangular structure of matrix U */ for (k = 1; k < n; k++) if (un[k] != 0.0) break; transform(scf, k, un); /* estimate the rank of matrices C and U */ scf->rank = estimate_rank(scf); if (scf->rank != n) ret = SCF_ESING; #if _GLPSCF_DEBUG /* check that the factorization is accurate enough */ check_error(scf, "scf_update_exp"); #endif done: return ret; } /*********************************************************************** * The routine solve solves the system C * x = b. * * From the main equation F * C = U * P it follows that: * * C * x = b => F * C * x = F * b => U * P * x = F * b => * * P * x = inv(U) * F * b => x = P' * inv(U) * F * b. * * On entry the array x contains right-hand side vector b. On exit this * array contains solution vector x. */ static void solve(SCF *scf, double x[]) { int n = scf->n; double *f = scf->f; double *u = scf->u; int *p = scf->p; double *y = scf->w; int i, j, ij; double t; /* y := F * b */ for (i = 1; i <= n; i++) { /* y[i] = (i-th row of F) * b */ t = 0.0; for (j = 1, ij = f_loc(scf, i, 1); j <= n; j++, ij++) t += f[ij] * x[j]; y[i] = t; } /* y := inv(U) * y */ for (i = n; i >= 1; i--) { t = y[i]; for (j = n, ij = u_loc(scf, i, n); j > i; j--, ij--) t -= u[ij] * y[j]; y[i] = t / u[ij]; } /* x := P' * y */ for (i = 1; i <= n; i++) x[p[i]] = y[i]; return; } /*********************************************************************** * The routine tsolve solves the transposed system C' * x = b. * * From the main equation F * C = U * P it follows that: * * C' * F' = P' * U', * * therefore: * * C' * x = b => C' * F' * inv(F') * x = b => * * P' * U' * inv(F') * x = b => U' * inv(F') * x = P * b => * * inv(F') * x = inv(U') * P * b => x = F' * inv(U') * P * b. * * On entry the array x contains right-hand side vector b. On exit this * array contains solution vector x. */ static void tsolve(SCF *scf, double x[]) { int n = scf->n; double *f = scf->f; double *u = scf->u; int *p = scf->p; double *y = scf->w; int i, j, ij; double t; /* y := P * b */ for (i = 1; i <= n; i++) y[i] = x[p[i]]; /* y := inv(U') * y */ for (i = 1; i <= n; i++) { /* compute y[i] */ ij = u_loc(scf, i, i); t = (y[i] /= u[ij]); /* substitute y[i] in other equations */ for (j = i+1, ij++; j <= n; j++, ij++) y[j] -= u[ij] * t; } /* x := F' * y (computed as linear combination of rows of F) */ for (j = 1; j <= n; j++) x[j] = 0.0; for (i = 1; i <= n; i++) { t = y[i]; /* coefficient of linear combination */ for (j = 1, ij = f_loc(scf, i, 1); j <= n; j++, ij++) x[j] += f[ij] * t; } return; } /*********************************************************************** * NAME * * scf_solve_it - solve either system C * x = b or C' * x = b * * SYNOPSIS * * #include "glpscf.h" * void scf_solve_it(SCF *scf, int tr, double x[]); * * DESCRIPTION * * The routine scf_solve_it solves either the system C * x = b (if tr * is zero) or the system C' * x = b, where C' is a matrix transposed * to C (if tr is non-zero). C is assumed to be non-singular. * * On entry the array x should contain the right-hand side vector b in * locations x[1], ..., x[n], where n is the order of matrix C. On exit * the array x contains the solution vector x in the same locations. */ void scf_solve_it(SCF *scf, int tr, double x[]) { if (scf->rank < scf->n) xfault("scf_solve_it: singular matrix\n"); if (!tr) solve(scf, x); else tsolve(scf, x); return; } void scf_reset_it(SCF *scf) { /* reset factorization for empty matrix C */ scf->n = scf->rank = 0; return; } /*********************************************************************** * NAME * * scf_delete_it - delete Schur complement factorization * * SYNOPSIS * * #include "glpscf.h" * void scf_delete_it(SCF *scf); * * DESCRIPTION * * The routine scf_delete_it deletes the specified factorization and * frees all the memory allocated to this object. */ void scf_delete_it(SCF *scf) { xfree(scf->f); xfree(scf->u); xfree(scf->p); #if _GLPSCF_DEBUG xfree(scf->c); #endif xfree(scf->w); xfree(scf); return; } /* eof */ igraph/src/foreign-pajek-parser.h0000644000176000001440000001042312325527073016535 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton interface for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { NEWLINE = 258, NUM = 259, ALNUM = 260, QSTR = 261, PSTR = 262, NETWORKLINE = 263, VERTICESLINE = 264, ARCSLINE = 265, EDGESLINE = 266, ARCSLISTLINE = 267, EDGESLISTLINE = 268, MATRIXLINE = 269, VP_X_FACT = 270, VP_Y_FACT = 271, VP_IC = 272, VP_BC = 273, VP_LC = 274, VP_LR = 275, VP_LPHI = 276, VP_BW = 277, VP_FOS = 278, VP_PHI = 279, VP_R = 280, VP_Q = 281, VP_LA = 282, VP_FONT = 283, VP_URL = 284, VP_SIZE = 285, EP_C = 286, EP_S = 287, EP_A = 288, EP_W = 289, EP_H1 = 290, EP_H2 = 291, EP_A1 = 292, EP_A2 = 293, EP_K1 = 294, EP_K2 = 295, EP_AP = 296, EP_P = 297, EP_L = 298, EP_LP = 299, EP_LR = 300, EP_LPHI = 301, EP_LC = 302, EP_LA = 303, EP_SIZE = 304, EP_FOS = 305 }; #endif /* Tokens. */ #define NEWLINE 258 #define NUM 259 #define ALNUM 260 #define QSTR 261 #define PSTR 262 #define NETWORKLINE 263 #define VERTICESLINE 264 #define ARCSLINE 265 #define EDGESLINE 266 #define ARCSLISTLINE 267 #define EDGESLISTLINE 268 #define MATRIXLINE 269 #define VP_X_FACT 270 #define VP_Y_FACT 271 #define VP_IC 272 #define VP_BC 273 #define VP_LC 274 #define VP_LR 275 #define VP_LPHI 276 #define VP_BW 277 #define VP_FOS 278 #define VP_PHI 279 #define VP_R 280 #define VP_Q 281 #define VP_LA 282 #define VP_FONT 283 #define VP_URL 284 #define VP_SIZE 285 #define EP_C 286 #define EP_S 287 #define EP_A 288 #define EP_W 289 #define EP_H1 290 #define EP_H2 291 #define EP_A1 292 #define EP_A2 293 #define EP_K1 294 #define EP_K2 295 #define EP_AP 296 #define EP_P 297 #define EP_L 298 #define EP_LP 299 #define EP_LR 300 #define EP_LPHI 301 #define EP_LC 302 #define EP_LA 303 #define EP_SIZE 304 #define EP_FOS 305 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 123 "igraph/src/foreign-pajek-parser.y" { long int intnum; double realnum; struct { char *str; int len; } string; } /* Line 1529 of yacc.c. */ #line 158 "y.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif igraph/src/mixing.c0000644000176000001440000002361012325527073014012 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_mixing.h" #include "igraph_interface.h" /** * \function igraph_assortativity_nominal * Assortativity of a graph based on vertex categories * * Assuming the vertices of the input graph belong to different * categories, this function calculates the assortativity coefficient of * the graph. The assortativity coefficient is between minus one and one * and it is one if all connections stay within categories, it is * minus one, if the network is perfectly disassortative. For a * randomly connected network it is (asymptotically) zero. * * See equation (2) in M. E. J. Newman: Mixing patterns * in networks, Phys. Rev. E 67, 026126 (2003) * (http://arxiv.org/abs/cond-mat/0209450) for the proper * definition. * * \param graph The input graph, it can be directed or undirected. * \param types Vector giving the vertex types. They are assumed to be * integer numbers, starting with zero. * \param res Pointer to a real variable, the result is stored here. * \param directed Boolean, it gives whether to consider edge * directions in a directed graph. It is ignored for undirected * graphs. * \return Error code. * * Time complexity: O(|E|+t), |E| is the number of edges, t is the * number of vertex types. * * \sa \ref igraph_assortativity if the vertex types are defines by * numeric values (e.g. vertex degree), instead of categories. * * \example examples/simple/assortativity.c */ int igraph_assortativity_nominal(const igraph_t *graph, const igraph_vector_t *types, igraph_real_t *res, igraph_bool_t directed) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); long int no_of_types; igraph_vector_t ai, bi, eii; long int e, i; igraph_real_t sumaibi=0.0, sumeii=0.0; if (igraph_vector_size(types) != no_of_nodes) { IGRAPH_ERROR("Invalid `types' vector length", IGRAPH_EINVAL); } if (igraph_vector_min(types) < 0) { IGRAPH_ERROR("Invalid `types' vector", IGRAPH_EINVAL); } directed = directed && igraph_is_directed(graph); no_of_types=(long int) igraph_vector_max(types)+1; IGRAPH_VECTOR_INIT_FINALLY(&ai, no_of_types); IGRAPH_VECTOR_INIT_FINALLY(&bi, no_of_types); IGRAPH_VECTOR_INIT_FINALLY(&eii, no_of_types); for (e=0; eSee equation (21) in M. E. J. Newman: Mixing patterns * in networks, Phys. Rev. E 67, 026126 (2003) * (http://arxiv.org/abs/cond-mat/0209450) for the proper * definition. The actual calculation is performed using equation (26) * in the same paper for directed graphs, and equation (4) in * M. E. J. Newman: Assortative mixing in networks, * Phys. Rev. Lett. 89, 208701 (2002) * (http://arxiv.org/abs/cond-mat/0205405/) for undirected graphs. * * \param graph The input graph, it can be directed or undirected. * \param types1 The vertex values, these can be arbitrary numeric * values. * \param types2 A second value vector to be using for the incoming * edges when calculating assortativity for a directed graph. * Supply a null pointer here if you want to use the same values * for outgoing and incoming edges. This argument is ignored * (with a warning) if it is not a null pointer and undirected * assortativity coefficient is being calculated. * \param res Pointer to a real variable, the result is stored here. * \param directed Boolean, whether to consider edge directions for * directed graphs. It is ignored for undirected graphs. * \return Error code. * * Time complexity: O(|E|), linear in the number of edges of the * graph. * * \sa \ref igraph_assortativity_nominal() if you have discrete vertex * categories instead of numeric labels, and \ref * igraph_assortativity_degree() for the special case of assortativity * based on vertex degree. * * \example examples/simple/assortativity.c */ int igraph_assortativity(const igraph_t *graph, const igraph_vector_t *types1, const igraph_vector_t *types2, igraph_real_t *res, igraph_bool_t directed) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); long int e; directed = directed && igraph_is_directed(graph); if (!directed && types2) { IGRAPH_WARNING("Only `types1' is used for undirected case"); } if (igraph_vector_size(types1) != no_of_nodes) { IGRAPH_ERROR("Invalid `types1' vector length", IGRAPH_EINVAL); } if (types2 && igraph_vector_size(types2) != no_of_nodes) { IGRAPH_ERROR("Invalid `types2' vector length", IGRAPH_EINVAL); } if (!directed) { igraph_real_t num1=0.0, num2=0.0, den1=0.0; for (e=0; e 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_nongraph.h" #include "igraph_interrupt_internal.h" #include "igraph_statusbar.h" #include "memory.h" #include "config.h" #include /* This is from GNU R's optim.c, slightly adapted to igraph */ #define stepredn 0.2 #define acctol 0.0001 #define reltest 10.0 #define FALSE 0 #define TRUE 1 /* BFGS variable-metric method, based on Pascal code in J.C. Nash, `Compact Numerical Methods for Computers', 2nd edition, converted by p2c then re-crafted by B.D. Ripley */ int igraph_bfgs(igraph_vector_t *b, igraph_real_t *Fmin, igraph_scalar_function_t fminfn, igraph_vector_function_t fmingr, int maxit, int trace, igraph_real_t abstol, igraph_real_t reltol, int nREPORT, void *ex, igraph_integer_t *fncount, igraph_integer_t *grcount) { int n=(int) igraph_vector_size(b); igraph_bool_t accpoint, enough; igraph_vector_t g, t, X, c; igraph_matrix_t B; /* Lmatrix really */ int count, funcount, gradcount; igraph_real_t f, gradproj; int i, j, ilast, iter = 0; igraph_real_t s, steplength; igraph_real_t D1, D2; if (maxit <= 0) { *Fmin = fminfn(b, 0, ex); *fncount = 1; *grcount = 0; return 0; } if (nREPORT <= 0) IGRAPH_ERROR("REPORT must be > 0 (method = \"BFGS\")", IGRAPH_EINVAL); IGRAPH_VECTOR_INIT_FINALLY(&g, n); IGRAPH_VECTOR_INIT_FINALLY(&t, n); IGRAPH_VECTOR_INIT_FINALLY(&X, n); IGRAPH_VECTOR_INIT_FINALLY(&c, n); IGRAPH_MATRIX_INIT_FINALLY(&B, n, n); f = fminfn(b, 0, ex); if (!IGRAPH_FINITE(f)) IGRAPH_ERROR("initial value in 'BFGS' is not finite", IGRAPH_DIVERGED); if (trace) igraph_statusf("initial value %f ", 0, f); *Fmin = f; funcount = gradcount = 1; fmingr(b, 0, &g, ex); iter++; ilast = gradcount; do { IGRAPH_ALLOW_INTERRUPTION(); if (ilast == gradcount) { for (i = 0; i < n; i++) { for (j = 0; j < i; j++) MATRIX(B,i,j) = 0.0; MATRIX(B, i, i) = 1.0; } } for (i = 0; i < n; i++) { VECTOR(X)[i] = VECTOR(*b)[i]; VECTOR(c)[i] = VECTOR(g)[i]; } gradproj = 0.0; for (i = 0; i < n; i++) { s = 0.0; for (j = 0; j <= i; j++) s -= MATRIX(B,i,j) * VECTOR(g)[j]; for (j = i + 1; j < n; j++) s -= MATRIX(B,j,i) * VECTOR(g)[j]; VECTOR(t)[i] = s; gradproj += s * VECTOR(g)[i]; } if (gradproj < 0.0) { /* search direction is downhill */ steplength = 1.0; accpoint = FALSE; do { count = 0; for (i = 0; i < n; i++) { VECTOR(*b)[i] = VECTOR(X)[i] + steplength * VECTOR(t)[i]; if (reltest + VECTOR(X)[i] == reltest + VECTOR(*b)[i]) /* no change */ count++; } if (count < n) { f = fminfn(b, 0, ex); funcount++; accpoint = IGRAPH_FINITE(f) && (f <= *Fmin + gradproj * steplength * acctol); if (!accpoint) { steplength *= stepredn; } } } while (!(count == n || accpoint)); enough = (f > abstol) && fabs(f - *Fmin) > reltol * (fabs(*Fmin) + reltol); /* stop if value if small or if relative change is low */ if (!enough) { count = n; *Fmin = f; } if (count < n) {/* making progress */ *Fmin = f; fmingr(b, 0, &g, ex); gradcount++; iter++; D1 = 0.0; for (i = 0; i < n; i++) { VECTOR(t)[i] = steplength * VECTOR(t)[i]; VECTOR(c)[i] = VECTOR(g)[i] - VECTOR(c)[i]; D1 += VECTOR(t)[i] * VECTOR(c)[i]; } if (D1 > 0) { D2 = 0.0; for (i = 0; i < n; i++) { s = 0.0; for (j = 0; j <= i; j++) s += MATRIX(B,i,j) * VECTOR(c)[j]; for (j = i + 1; j < n; j++) s += MATRIX(B,j,i) * VECTOR(c)[j]; VECTOR(X)[i] = s; D2 += s * VECTOR(c)[i]; } D2 = 1.0 + D2 / D1; for (i = 0; i < n; i++) { for (j = 0; j <= i; j++) MATRIX(B,i,j) += (D2 * VECTOR(t)[i] * VECTOR(t)[j] - VECTOR(X)[i] * VECTOR(t)[j] - VECTOR(t)[i] * VECTOR(X)[j]) / D1; } } else { /* D1 < 0 */ ilast = gradcount; } } else { /* no progress */ if (ilast < gradcount) { count = 0; ilast = gradcount; } } } else { /* uphill search */ count = 0; if (ilast == gradcount) count = n; else ilast = gradcount; /* Resets unless has just been reset */ } if (trace && (iter % nREPORT == 0)) igraph_statusf("iter%4d value %f", 0, iter, f); if (iter >= maxit) break; if (gradcount - ilast > 2 * n) ilast = gradcount; /* periodic restart */ } while (count != n || ilast != gradcount); if (trace) { igraph_statusf("final value %f ", 0, *Fmin); if (iter < maxit) igraph_status("converged", 0); else igraph_statusf("stopped after %i iterations", 0, iter); } *fncount = funcount; *grcount = gradcount; igraph_matrix_destroy(&B); igraph_vector_destroy(&c); igraph_vector_destroy(&X); igraph_vector_destroy(&t); igraph_vector_destroy(&g); IGRAPH_FINALLY_CLEAN(5); return (iter 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_CONVERSION_H #define IGRAPH_CONVERSION_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_spmatrix.h" #include "igraph_matrix.h" #include "igraph_sparsemat.h" #include "igraph_attributes.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Conversion */ /* -------------------------------------------------- */ int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, igraph_bool_t eids); int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, igraph_get_adjacency_t type); int igraph_get_stochastic(const igraph_t *graph, igraph_matrix_t *matrix, igraph_bool_t column_wise); int igraph_get_stochastic_sparsemat(const igraph_t *graph, igraph_sparsemat_t *sparsemat, igraph_bool_t column_wise); int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol); int igraph_to_directed(igraph_t *graph, igraph_to_directed_t flags); int igraph_to_undirected(igraph_t *graph, igraph_to_undirected_t flags, const igraph_attribute_combination_t *edge_comb); __END_DECLS #endif igraph/src/cs_util.c0000644000176000001440000001160412325527073014161 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* allocate a sparse matrix (triplet form or compressed-column form) */ cs *cs_spalloc (CS_INT m, CS_INT n, CS_INT nzmax, CS_INT values, CS_INT triplet) { cs *A = cs_calloc (1, sizeof (cs)) ; /* allocate the cs struct */ if (!A) return (NULL) ; /* out of memory */ A->m = m ; /* define dimensions and nzmax */ A->n = n ; A->nzmax = nzmax = CS_MAX (nzmax, 1) ; A->nz = triplet ? 0 : -1 ; /* allocate triplet or comp.col */ A->p = cs_malloc (triplet ? nzmax : n+1, sizeof (CS_INT)) ; A->i = cs_malloc (nzmax, sizeof (CS_INT)) ; A->x = values ? cs_malloc (nzmax, sizeof (CS_ENTRY)) : NULL ; return ((!A->p || !A->i || (values && !A->x)) ? cs_spfree (A) : A) ; } /* change the max # of entries sparse matrix */ CS_INT cs_sprealloc (cs *A, CS_INT nzmax) { CS_INT ok, oki, okj = 1, okx = 1 ; if (!A) return (0) ; if (nzmax <= 0) nzmax = (CS_CSC (A)) ? (A->p [A->n]) : A->nz ; A->i = cs_realloc (A->i, nzmax, sizeof (CS_INT), &oki) ; if (CS_TRIPLET (A)) A->p = cs_realloc (A->p, nzmax, sizeof (CS_INT), &okj) ; if (A->x) A->x = cs_realloc (A->x, nzmax, sizeof (CS_ENTRY), &okx) ; ok = (oki && okj && okx) ; if (ok) A->nzmax = nzmax ; return (ok) ; } /* free a sparse matrix */ cs *cs_spfree (cs *A) { if (!A) return (NULL) ; /* do nothing if A already NULL */ cs_free (A->p) ; cs_free (A->i) ; cs_free (A->x) ; return (cs_free (A)) ; /* free the cs struct and return NULL */ } /* free a numeric factorization */ csn *cs_nfree (csn *N) { if (!N) return (NULL) ; /* do nothing if N already NULL */ cs_spfree (N->L) ; cs_spfree (N->U) ; cs_free (N->pinv) ; cs_free (N->B) ; return (cs_free (N)) ; /* free the csn struct and return NULL */ } /* free a symbolic factorization */ css *cs_sfree (css *S) { if (!S) return (NULL) ; /* do nothing if S already NULL */ cs_free (S->pinv) ; cs_free (S->q) ; cs_free (S->parent) ; cs_free (S->cp) ; cs_free (S->leftmost) ; return (cs_free (S)) ; /* free the css struct and return NULL */ } /* allocate a cs_dmperm or cs_scc result */ csd *cs_dalloc (CS_INT m, CS_INT n) { csd *D ; D = cs_calloc (1, sizeof (csd)) ; if (!D) return (NULL) ; D->p = cs_malloc (m, sizeof (CS_INT)) ; D->r = cs_malloc (m+6, sizeof (CS_INT)) ; D->q = cs_malloc (n, sizeof (CS_INT)) ; D->s = cs_malloc (n+6, sizeof (CS_INT)) ; return ((!D->p || !D->r || !D->q || !D->s) ? cs_dfree (D) : D) ; } /* free a cs_dmperm or cs_scc result */ csd *cs_dfree (csd *D) { if (!D) return (NULL) ; /* do nothing if D already NULL */ cs_free (D->p) ; cs_free (D->q) ; cs_free (D->r) ; cs_free (D->s) ; return (cs_free (D)) ; } /* free workspace and return a sparse matrix result */ cs *cs_done (cs *C, void *w, void *x, CS_INT ok) { cs_free (w) ; /* free workspace */ cs_free (x) ; return (ok ? C : cs_spfree (C)) ; /* return result if OK, else free it */ } /* free workspace and return CS_INT array result */ CS_INT *cs_idone (CS_INT *p, cs *C, void *w, CS_INT ok) { cs_spfree (C) ; /* free temporary matrix */ cs_free (w) ; /* free workspace */ return (ok ? p : cs_free (p)) ; /* return result if OK, else free it */ } /* free workspace and return a numeric factorization (Cholesky, LU, or QR) */ csn *cs_ndone (csn *N, cs *C, void *w, void *x, CS_INT ok) { cs_spfree (C) ; /* free temporary matrix */ cs_free (w) ; /* free workspace */ cs_free (x) ; return (ok ? N : cs_nfree (N)) ; /* return result if OK, else free it */ } /* free workspace and return a csd result */ csd *cs_ddone (csd *D, cs *C, void *w, CS_INT ok) { cs_spfree (C) ; /* free temporary matrix */ cs_free (w) ; /* free workspace */ return (ok ? D : cs_dfree (D)) ; /* return result if OK, else free it */ } igraph/src/bliss_kqueue.hh0000644000176000001440000000514312325372072015365 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_KQUEUE_HH #define BLISS_KQUEUE_HH #include "bliss_defs.hh" #include // malloc namespace igraph { /* * A queue with fixed capacity */ template class KQueue { public: KQueue(); ~KQueue(); void init(const unsigned int k); bool is_empty() const; unsigned int size() const; unsigned int capacity() const; void clear(); Type front() const; Type pop_front(); void push_front(Type e); Type pop_back(); void push_back(Type e); private: Type *entries, *end; Type *head, *tail; }; template KQueue::KQueue() { entries = 0; end = 0; head = 0; tail = 0; } template KQueue::~KQueue() { if(entries) free(entries); } template void KQueue::init(const unsigned int k) { assert(k > 0); if(entries) free(entries); entries = (Type*)malloc((k + 1) * sizeof(Type)); end = entries + k + 1; head = entries; tail = head; } template void KQueue::clear() { head = entries; tail = head; } template bool KQueue::is_empty() const { return(head == tail); } template unsigned int KQueue::size() const { if(tail >= head) return(tail - head); return((end - head) + (tail - entries)); } template Type KQueue::front() const { DEBUG_ASSERT(head != tail); return *head; } template Type KQueue::pop_front() { DEBUG_ASSERT(head != tail); Type *old_head = head; head++; if(head == end) head = entries; return *old_head; } template void KQueue::push_front(Type e) { if(head == entries) head = end - 1; else head--; DEBUG_ASSERT(head != tail); *head = e; } template void KQueue::push_back(Type e) { *tail = e; tail++; if(tail == end) tail = entries; DEBUG_ASSERT(head != tail); } } #endif igraph/src/glpbfx.c0000644000176000001440000000513312325527073014001 0ustar ripleyusers/* glpbfx.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ typedef struct BFX BFX; #define GLPBFX_DEFINED #include "glpbfx.h" #include "glpenv.h" #include "glplux.h" struct BFX { int valid; LUX *lux; }; BFX *bfx_create_binv(void) { /* create factorization of the basis matrix */ BFX *bfx; bfx = xmalloc(sizeof(BFX)); bfx->valid = 0; bfx->lux = NULL; return bfx; } int bfx_factorize(BFX *binv, int m, int (*col)(void *info, int j, int ind[], mpq_t val[]), void *info) { /* compute factorization of the basis matrix */ int ret; xassert(m > 0); if (binv->lux != NULL && binv->lux->n != m) { lux_delete(binv->lux); binv->lux = NULL; } if (binv->lux == NULL) binv->lux = lux_create(m); ret = lux_decomp(binv->lux, col, info); binv->valid = (ret == 0); return ret; } void bfx_ftran(BFX *binv, mpq_t x[], int save) { /* perform forward transformation (FTRAN) */ xassert(binv->valid); lux_solve(binv->lux, 0, x); xassert(save == save); return; } void bfx_btran(BFX *binv, mpq_t x[]) { /* perform backward transformation (BTRAN) */ xassert(binv->valid); lux_solve(binv->lux, 1, x); return; } int bfx_update(BFX *binv, int j) { /* update factorization of the basis matrix */ xassert(binv->valid); xassert(1 <= j && j <= binv->lux->n); return 1; } void bfx_delete_binv(BFX *binv) { /* delete factorization of the basis matrix */ if (binv->lux != NULL) lux_delete(binv->lux); xfree(binv); return; } /* eof */ igraph/src/cs_cumsum.c0000644000176000001440000000270112325527073014513 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* p [0..n] = cumulative sum of c [0..n-1], and then copy p [0..n-1] into c */ double cs_cumsum (CS_INT *p, CS_INT *c, CS_INT n) { CS_INT i, nz = 0 ; double nz2 = 0 ; if (!p || !c) return (-1) ; /* check inputs */ for (i = 0 ; i < n ; i++) { p [i] = nz ; nz += c [i] ; nz2 += c [i] ; /* also in double to avoid CS_INT overflow */ c [i] = p [i] ; /* also copy p[0..n-1] back into c[0..n-1]*/ } p [n] = nz ; return (nz2) ; /* return sum (c [0..n-1]) */ } igraph/src/vector.c0000644000176000001440000002565312325527074014033 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_complex.h" #include "config.h" #include #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "vector.pmt" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "vector.pmt" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "vector.pmt" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "vector.pmt" #include "igraph_pmt_off.h" #undef BASE_BOOL #define BASE_INT #include "igraph_pmt.h" #include "vector.pmt" #include "igraph_pmt_off.h" #undef BASE_INT #define BASE_COMPLEX #include "igraph_pmt.h" #include "vector.pmt" #include "igraph_pmt_off.h" #undef BASE_COMPLEX #include "igraph_math.h" int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to) { long int i, n=igraph_vector_size(from); IGRAPH_CHECK(igraph_vector_long_resize(to, n)); for (i=0; i * The smallest element will have order zero, the second smallest * order one, etc. * \param v The original \type igraph_vector_t object. * \param v2 A secondary key, another \type igraph_vector_t object. * \param res An initialized \type igraph_vector_t object, it will be * resized to match the size of \p v. The * result of the computation will be stored here. * \param nodes Hint, the largest element in \p v. * \return Error code: * \c IGRAPH_ENOMEM: out of memory * * Time complexity: O() */ int igraph_vector_order(const igraph_vector_t* v, const igraph_vector_t *v2, igraph_vector_t* res, igraph_real_t nodes) { long int edges=igraph_vector_size(v); igraph_vector_t ptr; igraph_vector_t rad; long int i, j; assert(v!=NULL); assert(v->stor_begin != NULL); IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes+1); IGRAPH_VECTOR_INIT_FINALLY(&rad, edges); IGRAPH_CHECK(igraph_vector_resize(res, edges)); for (i=0; istor_begin[i]; if (VECTOR(ptr)[radix]!=0) { VECTOR(rad)[i]=VECTOR(ptr)[radix]; } VECTOR(ptr)[radix]=i+1; } j=0; for (i=0; istor_begin[j++]=next; while (VECTOR(rad)[next] != 0) { next=(long int) VECTOR(rad)[next]-1; res->stor_begin[j++]=next; } } } igraph_vector_null(&ptr); igraph_vector_null(&rad); for (i=0; istor_begin[j++]=next; while (VECTOR(rad)[next] != 0) { next=(long int) VECTOR(rad)[next]-1; res->stor_begin[j++]=next; } } } igraph_vector_destroy(&ptr); igraph_vector_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_vector_order1(const igraph_vector_t* v, igraph_vector_t* res, igraph_real_t nodes) { long int edges=igraph_vector_size(v); igraph_vector_t ptr; igraph_vector_t rad; long int i, j; assert(v!=NULL); assert(v->stor_begin != NULL); IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes+1); IGRAPH_VECTOR_INIT_FINALLY(&rad, edges); IGRAPH_CHECK(igraph_vector_resize(res, edges)); for (i=0; istor_begin[i]; if (VECTOR(ptr)[radix]!=0) { VECTOR(rad)[i]=VECTOR(ptr)[radix]; } VECTOR(ptr)[radix]=i+1; } j=0; for (i=0; istor_begin[j++]=next; while (VECTOR(rad)[next] != 0) { next=(long int) VECTOR(rad)[next]-1; res->stor_begin[j++]=next; } } } igraph_vector_destroy(&ptr); igraph_vector_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_vector_order1_int(const igraph_vector_t* v, igraph_vector_int_t* res, igraph_real_t nodes) { long int edges=igraph_vector_size(v); igraph_vector_t ptr; igraph_vector_t rad; long int i, j; assert(v!=NULL); assert(v->stor_begin != NULL); IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes+1); IGRAPH_VECTOR_INIT_FINALLY(&rad, edges); IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); for (i=0; istor_begin[i]; if (VECTOR(ptr)[radix]!=0) { VECTOR(rad)[i]=VECTOR(ptr)[radix]; } VECTOR(ptr)[radix]=i+1; } j=0; for (i=0; istor_begin[j++]=next; while (VECTOR(rad)[next] != 0) { next=(long int) VECTOR(rad)[next]-1; res->stor_begin[j++]=next; } } } igraph_vector_destroy(&ptr); igraph_vector_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, long int nodes) { igraph_vector_t rad; igraph_vector_t ptr; long int edges = igraph_vector_size(v); long int i, c=0; IGRAPH_VECTOR_INIT_FINALLY(&rad, nodes); IGRAPH_VECTOR_INIT_FINALLY(&ptr, edges); IGRAPH_CHECK(igraph_vector_resize(res, edges)); for (i=0; istor_begin != 0); assert(rhs->stor_begin != 0); s=igraph_vector_size(lhs); if (s != igraph_vector_size(rhs)) { return 0; } else { if (tol==0) { tol=DBL_EPSILON; } for (i=0; i r+tol) { return 0; } } return 1; } } igraph/src/rinterface.h0000644000176000001440000000157212325527074014652 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010 Gabor Csardi Rue de l'Industrie 5, Lausanne 1005, Switzerland This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ igraph/src/pstdint.h0000644000176000001440000006335112325527074014220 0ustar ripleyusers/* A portable stdint.h **************************************************************************** * BSD License: **************************************************************************** * * Copyright (c) 2005-2007 Paul Hsieh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************** * * Version 0.1.11 * * The ANSI C standard committee, for the C99 standard, specified the * inclusion of a new standard include file called stdint.h. This is * a very useful and long desired include file which contains several * very precise definitions for integer scalar types that is * critically important for making portable several classes of * applications including cryptography, hashing, variable length * integer libraries and so on. But for most developers its likely * useful just for programming sanity. * * The problem is that most compiler vendors have decided not to * implement the C99 standard, and the next C++ language standard * (which has a lot more mindshare these days) will be a long time in * coming and its unknown whether or not it will include stdint.h or * how much adoption it will have. Either way, it will be a long time * before all compilers come with a stdint.h and it also does nothing * for the extremely large number of compilers available today which * do not include this file, or anything comparable to it. * * So that's what this file is all about. Its an attempt to build a * single universal include file that works on as many platforms as * possible to deliver what stdint.h is supposed to. A few things * that should be noted about this file: * * 1) It is not guaranteed to be portable and/or present an identical * interface on all platforms. The extreme variability of the * ANSI C standard makes this an impossibility right from the * very get go. Its really only meant to be useful for the vast * majority of platforms that possess the capability of * implementing usefully and precisely defined, standard sized * integer scalars. Systems which are not intrinsically 2s * complement may produce invalid constants. * * 2) There is an unavoidable use of non-reserved symbols. * * 3) Other standard include files are invoked. * * 4) This file may come in conflict with future platforms that do * include stdint.h. The hope is that one or the other can be * used with no real difference. * * 5) In the current verison, if your platform can't represent * int32_t, int16_t and int8_t, it just dumps out with a compiler * error. * * 6) 64 bit integers may or may not be defined. Test for their * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. * Note that this is different from the C99 specification which * requires the existence of 64 bit support in the compiler. If * this is not defined for your platform, yet it is capable of * dealing with 64 bits then it is because this file has not yet * been extended to cover all of your system's capabilities. * * 7) (u)intptr_t may or may not be defined. Test for its presence * with the test: #ifdef PTRDIFF_MAX. If this is not defined * for your platform, then it is because this file has not yet * been extended to cover all of your system's capabilities, not * because its optional. * * 8) The following might not been defined even if your platform is * capable of defining it: * * WCHAR_MIN * WCHAR_MAX * (u)int64_t * PTRDIFF_MIN * PTRDIFF_MAX * (u)intptr_t * * 9) The following have not been defined: * * WINT_MIN * WINT_MAX * * 10) The criteria for defining (u)int_least(*)_t isn't clear, * except for systems which don't have a type that precisely * defined 8, 16, or 32 bit types (which this include file does * not support anyways). Default definitions have been given. * * 11) The criteria for defining (u)int_fast(*)_t isn't something I * would trust to any particular compiler vendor or the ANSI C * committee. It is well known that "compatible systems" are * commonly created that have very different performance * characteristics from the systems they are compatible with, * especially those whose vendors make both the compiler and the * system. Default definitions have been given, but its strongly * recommended that users never use these definitions for any * reason (they do *NOT* deliver any serious guarantee of * improved performance -- not in this file, nor any vendor's * stdint.h). * * 12) The following macros: * * PRINTF_INTMAX_MODIFIER * PRINTF_INT64_MODIFIER * PRINTF_INT32_MODIFIER * PRINTF_INT16_MODIFIER * PRINTF_LEAST64_MODIFIER * PRINTF_LEAST32_MODIFIER * PRINTF_LEAST16_MODIFIER * PRINTF_INTPTR_MODIFIER * * are strings which have been defined as the modifiers required * for the "d", "u" and "x" printf formats to correctly output * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. * PRINTF_INTPTR_MODIFIER is not defined for some systems which * provide their own stdint.h. PRINTF_INT64_MODIFIER is not * defined if INT64_MAX is not defined. These are an extension * beyond what C99 specifies must be in stdint.h. * * In addition, the following macros are defined: * * PRINTF_INTMAX_HEX_WIDTH * PRINTF_INT64_HEX_WIDTH * PRINTF_INT32_HEX_WIDTH * PRINTF_INT16_HEX_WIDTH * PRINTF_INT8_HEX_WIDTH * PRINTF_INTMAX_DEC_WIDTH * PRINTF_INT64_DEC_WIDTH * PRINTF_INT32_DEC_WIDTH * PRINTF_INT16_DEC_WIDTH * PRINTF_INT8_DEC_WIDTH * * Which specifies the maximum number of characters required to * print the number of that type in either hexadecimal or decimal. * These are an extension beyond what C99 specifies must be in * stdint.h. * * Compilers tested (all with 0 warnings at their highest respective * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 * * This file should be considered a work in progress. Suggestions for * improvements, especially those which increase coverage are strongly * encouraged. * * Acknowledgements * * The following people have made significant contributions to the * development and testing of this file: * * Chris Howie * John Steele Scott * Dave Thorup * */ #include #include #include /* * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. */ #if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED) #include #define _PSTDINT_H_INCLUDED # ifndef PRINTF_INT64_MODIFIER # define PRINTF_INT64_MODIFIER "ll" # endif # ifndef PRINTF_INT32_MODIFIER # define PRINTF_INT32_MODIFIER "l" # endif # ifndef PRINTF_INT16_MODIFIER # define PRINTF_INT16_MODIFIER "h" # endif # ifndef PRINTF_INTMAX_MODIFIER # define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER # endif # ifndef PRINTF_INT64_HEX_WIDTH # define PRINTF_INT64_HEX_WIDTH "16" # endif # ifndef PRINTF_INT32_HEX_WIDTH # define PRINTF_INT32_HEX_WIDTH "8" # endif # ifndef PRINTF_INT16_HEX_WIDTH # define PRINTF_INT16_HEX_WIDTH "4" # endif # ifndef PRINTF_INT8_HEX_WIDTH # define PRINTF_INT8_HEX_WIDTH "2" # endif # ifndef PRINTF_INT64_DEC_WIDTH # define PRINTF_INT64_DEC_WIDTH "20" # endif # ifndef PRINTF_INT32_DEC_WIDTH # define PRINTF_INT32_DEC_WIDTH "10" # endif # ifndef PRINTF_INT16_DEC_WIDTH # define PRINTF_INT16_DEC_WIDTH "5" # endif # ifndef PRINTF_INT8_DEC_WIDTH # define PRINTF_INT8_DEC_WIDTH "3" # endif # ifndef PRINTF_INTMAX_HEX_WIDTH # define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH # endif # ifndef PRINTF_INTMAX_DEC_WIDTH # define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH # endif /* * Something really weird is going on with Open Watcom. Just pull some of * these duplicated definitions from Open Watcom's stdint.h file for now. */ # if defined (__WATCOMC__) && __WATCOMC__ >= 1250 # if !defined (INT64_C) # define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) # endif # if !defined (UINT64_C) # define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) # endif # if !defined (INT32_C) # define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) # endif # if !defined (UINT32_C) # define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) # endif # if !defined (INT16_C) # define INT16_C(x) (x) # endif # if !defined (UINT16_C) # define UINT16_C(x) (x) # endif # if !defined (INT8_C) # define INT8_C(x) (x) # endif # if !defined (UINT8_C) # define UINT8_C(x) (x) # endif # if !defined (UINT64_MAX) # define UINT64_MAX 18446744073709551615ULL # endif # if !defined (INT64_MAX) # define INT64_MAX 9223372036854775807LL # endif # if !defined (UINT32_MAX) # define UINT32_MAX 4294967295UL # endif # if !defined (INT32_MAX) # define INT32_MAX 2147483647L # endif # if !defined (INTMAX_MAX) # define INTMAX_MAX INT64_MAX # endif # if !defined (INTMAX_MIN) # define INTMAX_MIN INT64_MIN # endif # endif #endif #ifndef _PSTDINT_H_INCLUDED #define _PSTDINT_H_INCLUDED #ifndef SIZE_MAX # define SIZE_MAX (~(size_t)0) #endif /* * Deduce the type assignments from limits.h under the assumption that * integer sizes in bits are powers of 2, and follow the ANSI * definitions. */ #ifndef UINT8_MAX # define UINT8_MAX 0xff #endif #ifndef uint8_t # if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) typedef unsigned char uint8_t; # define UINT8_C(v) ((uint8_t) v) # else # error "Platform not supported" # endif #endif #ifndef INT8_MAX # define INT8_MAX 0x7f #endif #ifndef INT8_MIN # define INT8_MIN INT8_C(0x80) #endif #ifndef int8_t # if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) typedef signed char int8_t; # define INT8_C(v) ((int8_t) v) # else # error "Platform not supported" # endif #endif #ifndef UINT16_MAX # define UINT16_MAX 0xffff #endif #ifndef uint16_t #if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) typedef unsigned int uint16_t; # ifndef PRINTF_INT16_MODIFIER # define PRINTF_INT16_MODIFIER "" # endif # define UINT16_C(v) ((uint16_t) (v)) #elif (USHRT_MAX == UINT16_MAX) typedef unsigned short uint16_t; # define UINT16_C(v) ((uint16_t) (v)) # ifndef PRINTF_INT16_MODIFIER # define PRINTF_INT16_MODIFIER "h" # endif #else #error "Platform not supported" #endif #endif #ifndef INT16_MAX # define INT16_MAX 0x7fff #endif #ifndef INT16_MIN # define INT16_MIN INT16_C(0x8000) #endif #ifndef int16_t #if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) typedef signed int int16_t; # define INT16_C(v) ((int16_t) (v)) # ifndef PRINTF_INT16_MODIFIER # define PRINTF_INT16_MODIFIER "" # endif #elif (SHRT_MAX == INT16_MAX) typedef signed short int16_t; # define INT16_C(v) ((int16_t) (v)) # ifndef PRINTF_INT16_MODIFIER # define PRINTF_INT16_MODIFIER "h" # endif #else #error "Platform not supported" #endif #endif #ifndef UINT32_MAX # define UINT32_MAX (0xffffffffUL) #endif #ifndef uint32_t #if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) typedef unsigned long uint32_t; # define UINT32_C(v) v ## UL # ifndef PRINTF_INT32_MODIFIER # define PRINTF_INT32_MODIFIER "l" # endif #elif (UINT_MAX == UINT32_MAX) typedef unsigned int uint32_t; # ifndef PRINTF_INT32_MODIFIER # define PRINTF_INT32_MODIFIER "" # endif # define UINT32_C(v) v ## U #elif (USHRT_MAX == UINT32_MAX) typedef unsigned short uint32_t; # define UINT32_C(v) ((unsigned short) (v)) # ifndef PRINTF_INT32_MODIFIER # define PRINTF_INT32_MODIFIER "" # endif #else #error "Platform not supported" #endif #endif #ifndef INT32_MAX # define INT32_MAX (0x7fffffffL) #endif #ifndef INT32_MIN # define INT32_MIN INT32_C(0x80000000) #endif #ifndef int32_t #if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) typedef signed long int32_t; # define INT32_C(v) v ## L # ifndef PRINTF_INT32_MODIFIER # define PRINTF_INT32_MODIFIER "l" # endif #elif (INT_MAX == INT32_MAX) typedef signed int int32_t; # define INT32_C(v) v # ifndef PRINTF_INT32_MODIFIER # define PRINTF_INT32_MODIFIER "" # endif #elif (SHRT_MAX == INT32_MAX) typedef signed short int32_t; # define INT32_C(v) ((short) (v)) # ifndef PRINTF_INT32_MODIFIER # define PRINTF_INT32_MODIFIER "" # endif #else #error "Platform not supported" #endif #endif /* * The macro stdint_int64_defined is temporarily used to record * whether or not 64 integer support is available. It must be * defined for any 64 integer extensions for new platforms that are * added. */ #undef stdint_int64_defined #if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) # if (__STDC__ && __STDC_VERSION >= 199901L) || defined (S_SPLINT_S) # define stdint_int64_defined typedef long long int64_t; typedef unsigned long long uint64_t; # define UINT64_C(v) v ## ULL # define INT64_C(v) v ## LL # ifndef PRINTF_INT64_MODIFIER # define PRINTF_INT64_MODIFIER "ll" # endif # endif #endif #if !defined (stdint_int64_defined) # if defined(__GNUC__) # define stdint_int64_defined __extension__ typedef long long int64_t; __extension__ typedef unsigned long long uint64_t; # define UINT64_C(v) v ## ULL # define INT64_C(v) v ## LL # ifndef PRINTF_INT64_MODIFIER # define PRINTF_INT64_MODIFIER "ll" # endif # elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) # define stdint_int64_defined typedef long long int64_t; typedef unsigned long long uint64_t; # define UINT64_C(v) v ## ULL # define INT64_C(v) v ## LL # ifndef PRINTF_INT64_MODIFIER # define PRINTF_INT64_MODIFIER "ll" # endif # elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) # define stdint_int64_defined typedef __int64 int64_t; typedef unsigned __int64 uint64_t; # define UINT64_C(v) v ## UI64 # define INT64_C(v) v ## I64 # ifndef PRINTF_INT64_MODIFIER # define PRINTF_INT64_MODIFIER "I64" # endif # endif #endif #if !defined (LONG_LONG_MAX) && defined (INT64_C) # define LONG_LONG_MAX INT64_C (9223372036854775807) #endif #ifndef ULONG_LONG_MAX # define ULONG_LONG_MAX UINT64_C (18446744073709551615) #endif #if !defined (INT64_MAX) && defined (INT64_C) # define INT64_MAX INT64_C (9223372036854775807) #endif #if !defined (INT64_MIN) && defined (INT64_C) # define INT64_MIN INT64_C (-9223372036854775808) #endif #if !defined (UINT64_MAX) && defined (INT64_C) # define UINT64_MAX UINT64_C (18446744073709551615) #endif /* * Width of hexadecimal for number field. */ #ifndef PRINTF_INT64_HEX_WIDTH # define PRINTF_INT64_HEX_WIDTH "16" #endif #ifndef PRINTF_INT32_HEX_WIDTH # define PRINTF_INT32_HEX_WIDTH "8" #endif #ifndef PRINTF_INT16_HEX_WIDTH # define PRINTF_INT16_HEX_WIDTH "4" #endif #ifndef PRINTF_INT8_HEX_WIDTH # define PRINTF_INT8_HEX_WIDTH "2" #endif #ifndef PRINTF_INT64_DEC_WIDTH # define PRINTF_INT64_DEC_WIDTH "20" #endif #ifndef PRINTF_INT32_DEC_WIDTH # define PRINTF_INT32_DEC_WIDTH "10" #endif #ifndef PRINTF_INT16_DEC_WIDTH # define PRINTF_INT16_DEC_WIDTH "5" #endif #ifndef PRINTF_INT8_DEC_WIDTH # define PRINTF_INT8_DEC_WIDTH "3" #endif /* * Ok, lets not worry about 128 bit integers for now. Moore's law says * we don't need to worry about that until about 2040 at which point * we'll have bigger things to worry about. */ #ifdef stdint_int64_defined typedef int64_t intmax_t; typedef uint64_t uintmax_t; # define INTMAX_MAX INT64_MAX # define INTMAX_MIN INT64_MIN # define UINTMAX_MAX UINT64_MAX # define UINTMAX_C(v) UINT64_C(v) # define INTMAX_C(v) INT64_C(v) # ifndef PRINTF_INTMAX_MODIFIER # define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER # endif # ifndef PRINTF_INTMAX_HEX_WIDTH # define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH # endif # ifndef PRINTF_INTMAX_DEC_WIDTH # define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH # endif #else typedef int32_t intmax_t; typedef uint32_t uintmax_t; # define INTMAX_MAX INT32_MAX # define UINTMAX_MAX UINT32_MAX # define UINTMAX_C(v) UINT32_C(v) # define INTMAX_C(v) INT32_C(v) # ifndef PRINTF_INTMAX_MODIFIER # define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER # endif # ifndef PRINTF_INTMAX_HEX_WIDTH # define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH # endif # ifndef PRINTF_INTMAX_DEC_WIDTH # define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH # endif #endif /* * Because this file currently only supports platforms which have * precise powers of 2 as bit sizes for the default integers, the * least definitions are all trivial. Its possible that a future * version of this file could have different definitions. */ #ifndef stdint_least_defined typedef int8_t int_least8_t; typedef uint8_t uint_least8_t; typedef int16_t int_least16_t; typedef uint16_t uint_least16_t; typedef int32_t int_least32_t; typedef uint32_t uint_least32_t; # define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER # define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER # define UINT_LEAST8_MAX UINT8_MAX # define INT_LEAST8_MAX INT8_MAX # define UINT_LEAST16_MAX UINT16_MAX # define INT_LEAST16_MAX INT16_MAX # define UINT_LEAST32_MAX UINT32_MAX # define INT_LEAST32_MAX INT32_MAX # define INT_LEAST8_MIN INT8_MIN # define INT_LEAST16_MIN INT16_MIN # define INT_LEAST32_MIN INT32_MIN # ifdef stdint_int64_defined typedef int64_t int_least64_t; typedef uint64_t uint_least64_t; # define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER # define UINT_LEAST64_MAX UINT64_MAX # define INT_LEAST64_MAX INT64_MAX # define INT_LEAST64_MIN INT64_MIN # endif #endif #undef stdint_least_defined /* * The ANSI C committee pretending to know or specify anything about * performance is the epitome of misguided arrogance. The mandate of * this file is to *ONLY* ever support that absolute minimum * definition of the fast integer types, for compatibility purposes. * No extensions, and no attempt to suggest what may or may not be a * faster integer type will ever be made in this file. Developers are * warned to stay away from these types when using this or any other * stdint.h. */ typedef int_least8_t int_fast8_t; typedef uint_least8_t uint_fast8_t; typedef int_least16_t int_fast16_t; typedef uint_least16_t uint_fast16_t; typedef int_least32_t int_fast32_t; typedef uint_least32_t uint_fast32_t; #define UINT_FAST8_MAX UINT_LEAST8_MAX #define INT_FAST8_MAX INT_LEAST8_MAX #define UINT_FAST16_MAX UINT_LEAST16_MAX #define INT_FAST16_MAX INT_LEAST16_MAX #define UINT_FAST32_MAX UINT_LEAST32_MAX #define INT_FAST32_MAX INT_LEAST32_MAX #define INT_FAST8_MIN INT_LEAST8_MIN #define INT_FAST16_MIN INT_LEAST16_MIN #define INT_FAST32_MIN INT_LEAST32_MIN #ifdef stdint_int64_defined typedef int_least64_t int_fast64_t; typedef uint_least64_t uint_fast64_t; # define UINT_FAST64_MAX UINT_LEAST64_MAX # define INT_FAST64_MAX INT_LEAST64_MAX # define INT_FAST64_MIN INT_LEAST64_MIN #endif #undef stdint_int64_defined /* * Whatever piecemeal, per compiler thing we can do about the wchar_t * type limits. */ #if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) # include # ifndef WCHAR_MIN # define WCHAR_MIN 0 # endif # ifndef WCHAR_MAX # define WCHAR_MAX ((wchar_t)-1) # endif #endif /* * Whatever piecemeal, per compiler/platform thing we can do about the * (u)intptr_t types and limits. */ #if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) # define STDINT_H_UINTPTR_T_DEFINED #endif #ifndef STDINT_H_UINTPTR_T_DEFINED # if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) # define stdint_intptr_bits 64 # elif defined (__WATCOMC__) || defined (__TURBOC__) # if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) # define stdint_intptr_bits 16 # else # define stdint_intptr_bits 32 # endif # elif defined (__i386__) || defined (_WIN32) || defined (WIN32) # define stdint_intptr_bits 32 # elif defined (__INTEL_COMPILER) /* TODO -- what will Intel do about x86-64? */ # endif # ifdef stdint_intptr_bits # define stdint_intptr_glue3_i(a,b,c) a##b##c # define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) # ifndef PRINTF_INTPTR_MODIFIER # define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) # endif # ifndef PTRDIFF_MAX # define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) # endif # ifndef PTRDIFF_MIN # define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) # endif # ifndef UINTPTR_MAX # define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) # endif # ifndef INTPTR_MAX # define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) # endif # ifndef INTPTR_MIN # define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) # endif # ifndef INTPTR_C # define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) # endif # ifndef UINTPTR_C # define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) # endif typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; # else /* TODO -- This following is likely wrong for some platforms, and does nothing for the definition of uintptr_t. */ typedef ptrdiff_t intptr_t; # endif # define STDINT_H_UINTPTR_T_DEFINED #endif /* * Assumes sig_atomic_t is signed and we have a 2s complement machine. */ #ifndef SIG_ATOMIC_MAX # define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) #endif #endif #if defined (__TEST_PSTDINT_FOR_CORRECTNESS) /* * Please compile with the maximum warning settings to make sure macros are not * defined more than once. */ #include #include #include #define glue3_aux(x,y,z) x ## y ## z #define glue3(x,y,z) glue3_aux(x,y,z) #define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0); #define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0); #define DECL(us,bits) glue3(DECL,us,) (bits) #define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits) int main () { DECL(I,8) DECL(U,8) DECL(I,16) DECL(U,16) DECL(I,32) DECL(U,32) #ifdef INT64_MAX DECL(I,64) DECL(U,64) #endif intmax_t imax = INTMAX_C(0); uintmax_t umax = UINTMAX_C(0); char str0[256], str1[256]; sprintf (str0, "%d %x\n", 0, ~0); sprintf (str1, "%d %x\n", i8, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with i8 : %s\n", str1); sprintf (str1, "%u %x\n", u8, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with u8 : %s\n", str1); sprintf (str1, "%d %x\n", i16, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with i16 : %s\n", str1); sprintf (str1, "%u %x\n", u16, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with u16 : %s\n", str1); sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with i32 : %s\n", str1); sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with u32 : %s\n", str1); #ifdef INT64_MAX sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with i64 : %s\n", str1); #endif sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with imax : %s\n", str1); sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); if (0 != strcmp (str0, str1)) printf ("Something wrong with umax : %s\n", str1); TESTUMAX(8); TESTUMAX(16); TESTUMAX(32); #ifdef INT64_MAX TESTUMAX(64); #endif return EXIT_SUCCESS; } #endif igraph/src/igraph_graphlets.h0000644000176000001440000000416012325527073016046 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2013 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_GRAPHLETS_H #define IGRAPH_GRAPHLETS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_datatype.h" #include "igraph_vector_ptr.h" #include "igraph_interface.h" __BEGIN_DECLS int igraph_subclique_next(const igraph_t *graph, const igraph_vector_t *weights, const igraph_vector_int_t *ids, const igraph_vector_ptr_t *cliques, igraph_vector_ptr_t *result, igraph_vector_ptr_t *resultweights, igraph_vector_ptr_t *resultids, igraph_vector_t *clique_thr, igraph_vector_t *next_thr); int igraph_graphlets_candidate_basis(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_ptr_t *cliques, igraph_vector_t *thresholds); int igraph_graphlets_project(const igraph_t *graph, const igraph_vector_t *weights, const igraph_vector_ptr_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, int niter); int igraph_graphlets(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_ptr_t *cliques, igraph_vector_t *Mu, int niter); __END_DECLS #endif igraph/src/igraph_eigen.h0000644000176000001440000000672312325527073015153 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_arpack.h" #include "igraph_lapack.h" #include "igraph_sparsemat.h" #ifndef IGRAPH_EIGEN_H #define IGRAPH_EIGEN_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS typedef enum { IGRAPH_EIGEN_AUTO=0, IGRAPH_EIGEN_LAPACK, IGRAPH_EIGEN_ARPACK, IGRAPH_EIGEN_COMP_AUTO, IGRAPH_EIGEN_COMP_LAPACK, IGRAPH_EIGEN_COMP_ARPACK } igraph_eigen_algorithm_t; typedef enum { IGRAPH_EIGEN_LM=0, IGRAPH_EIGEN_SM, IGRAPH_EIGEN_LA, IGRAPH_EIGEN_SA, IGRAPH_EIGEN_BE, IGRAPH_EIGEN_LR, IGRAPH_EIGEN_SR, IGRAPH_EIGEN_LI, IGRAPH_EIGEN_SI, IGRAPH_EIGEN_ALL, IGRAPH_EIGEN_INTERVAL, IGRAPH_EIGEN_SELECT } igraph_eigen_which_position_t; typedef struct igraph_eigen_which_t { igraph_eigen_which_position_t pos; int howmany; int il, iu; igraph_real_t vl, vu; int vestimate; igraph_lapack_dgeevx_balance_t balance; } igraph_eigen_which_t; int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors); int igraph_eigen_matrix(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_complex_t *values, igraph_matrix_complex_t *vectors); int igraph_eigen_adjacency(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_complex_t *cmplxvalues, igraph_matrix_complex_t *cmplxvectors); int igraph_eigen_laplacian(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_complex_t *cmplxvalues, igraph_matrix_complex_t *cmplxvectors); __END_DECLS #endif igraph/src/drl_graph_3d.cpp0000644000176000001440000006252312325527073015415 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file contains the member definitions of the master class #include #include #include #include #include #include #include using namespace std; #include "drl_graph_3d.h" #include "igraph_random.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #ifdef MUSE_MPI #include #endif namespace drl3d { graph::graph(const igraph_t *igraph, const igraph_layout_drl_options_t *options, const igraph_vector_t *weights) { myid = 0; num_procs = 1; STAGE = 0; iterations = options->init_iterations; temperature = options->init_temperature; attraction = options->init_attraction; damping_mult = options->init_damping_mult; min_edges = 20; first_add = fine_first_add = true; fineDensity = false; // Brian's original Vx schedule liquid.iterations = options->liquid_iterations; liquid.temperature = options->liquid_temperature; liquid.attraction = options->liquid_attraction; liquid.damping_mult = options->liquid_damping_mult; liquid.time_elapsed = 0; expansion.iterations = options->expansion_iterations; expansion.temperature = options->expansion_temperature; expansion.attraction = options->expansion_attraction; expansion.damping_mult = options->expansion_damping_mult; expansion.time_elapsed = 0; cooldown.iterations = options->cooldown_iterations; cooldown.temperature = options->cooldown_temperature; cooldown.attraction = options->cooldown_attraction; cooldown.damping_mult = options->cooldown_damping_mult; cooldown.time_elapsed = 0; crunch.iterations = options->crunch_iterations; crunch.temperature = options->crunch_temperature; crunch.attraction = options->crunch_attraction; crunch.damping_mult = options->crunch_damping_mult; crunch.time_elapsed = 0; simmer.iterations = options->simmer_iterations; simmer.temperature = options->simmer_temperature; simmer.attraction = options->simmer_attraction; simmer.damping_mult = options->simmer_damping_mult; simmer.time_elapsed = 0; // scan .int file for node info highest_sim = 1.0; num_nodes=igraph_vcount(igraph); long int no_of_edges=igraph_ecount(igraph); for (long int i=0; i::iterator cat_iter; for ( cat_iter = id_catalog.begin(); cat_iter != id_catalog.end(); cat_iter++) { cat_iter->second = cat_iter->first; } // populate node positions and ids positions.reserve ( num_nodes ); for ( cat_iter = id_catalog.begin(); cat_iter != id_catalog.end(); cat_iter++ ) { positions.push_back ( Node( cat_iter->first ) ); } // read .int file for graph info long int node_1, node_2; double weight; for (long int i=0; i 0 ) real_fixed = true; else real_fixed = false; // calculate total expected iterations (for progress bar display) tot_expected_iterations = liquid.iterations + expansion.iterations + cooldown.iterations + crunch.iterations + simmer.iterations; /* // output edge_cutting parms (for debugging) cout << "Processor " << myid << ": " << "cut_length_end = CUT_END = " << cut_length_end << ", cut_length_start = " << cut_length_start << ", cut_rate = " << cut_rate << endl; */ // set random seed // srand ( rand_seed ); // Don't need this in igraph } void graph::init_parms(const igraph_layout_drl_options_t *options) { double rand_seed = 0.0; double real_in = -1.0; init_parms(rand_seed, options->edge_cut, real_in); } int graph::read_real ( const igraph_matrix_t *real_mat, const igraph_vector_bool_t *fixed) { long int n=igraph_matrix_nrow(real_mat); for (long int i=0; i 0 ) { density_server.Add ( positions[id_catalog[i]], fineDensity ); } } return 0; } /********************************************* * Function: ReCompute * * Description: Compute the graph locations * * Modified from original code by B. Wylie * ********************************************/ int graph::ReCompute( ) { // carryover from original VxOrd int MIN = 1; /* // output parameters (for debugging) cout << "ReCompute is using the following parameters: "<< endl; cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature << ", attract = " << attraction << ", damping_mult = " << damping_mult << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length << ", fineDensity = " << fineDensity << endl; */ /* igraph progress report */ float progress = (tot_iterations * 100.0 / tot_expected_iterations); switch (STAGE) { case 0: if (iterations == 0) IGRAPH_PROGRESS("DrL layout (initialization stage)", progress, 0); else IGRAPH_PROGRESS("DrL layout (liquid stage)", progress, 0); break; case 1: IGRAPH_PROGRESS("DrL layout (expansion stage)", progress, 0); break; case 2: IGRAPH_PROGRESS("DrL layout (cooldown and cluster phase)", progress, 0); break; case 3: IGRAPH_PROGRESS("DrL layout (crunch phase)", progress, 0); break; case 5: IGRAPH_PROGRESS("DrL layout (simmer phase)", progress, 0); break; case 6: IGRAPH_PROGRESS("DrL layout (final phase)", 100.0, 0); break; default: IGRAPH_PROGRESS("DrL layout (unknown phase)", 0.0, 0); break; } /* Compute Energies for individual nodes */ update_nodes (); // check to see if we need to free fixed nodes tot_iterations++; if ( tot_iterations >= real_iterations ) real_fixed = false; // **************************************** // AUTOMATIC CONTROL SECTION // **************************************** // STAGE 0: LIQUID if (STAGE == 0) { if ( iterations == 0 ) { start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering liquid stage ..."; } if (iterations < liquid.iterations) { temperature = liquid.temperature; attraction = liquid.attraction; damping_mult = liquid.damping_mult; iterations++; // if ( myid == 0 ) // cout << "." << flush; } else { stop_time = time( NULL ); liquid.time_elapsed = liquid.time_elapsed + (stop_time - start_time); temperature = expansion.temperature; attraction = expansion.attraction; damping_mult = expansion.damping_mult; iterations = 0; // go to next stage STAGE = 1; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering expansion stage ..."; } } // STAGE 1: EXPANSION if (STAGE == 1) { if (iterations < expansion.iterations) { // Play with vars if (attraction > 1) attraction -= .05; if (min_edges > 12) min_edges -= .05; cut_off_length -= cut_rate; if (damping_mult > .1) damping_mult -= .005; iterations++; // if ( myid == 0 ) cout << "." << flush; } else { stop_time = time( NULL ); expansion.time_elapsed = expansion.time_elapsed + (stop_time - start_time); min_edges = 12; damping_mult = cooldown.damping_mult; STAGE = 2; attraction = cooldown.attraction; temperature = cooldown.temperature; iterations = 0; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering cool-down stage ..."; } } // STAGE 2: Cool down and cluster else if(STAGE==2) { if (iterations < cooldown.iterations) { // Reduce temperature if (temperature > 50) temperature -= 10; // Reduce cut length if (cut_off_length > cut_length_end) cut_off_length -= cut_rate*2; if (min_edges > MIN) min_edges -= .2; //min_edges = 99; iterations++; // if ( myid == 0 ) // cout << "." << flush; } else { stop_time = time( NULL ); cooldown.time_elapsed = cooldown.time_elapsed + (stop_time - start_time); cut_off_length = cut_length_end; temperature = crunch.temperature; damping_mult = crunch.damping_mult; min_edges = MIN; //min_edges = 99; // In other words: no more cutting STAGE = 3; iterations = 0; attraction = crunch.attraction; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering crunch stage ..."; } } // STAGE 3: Crunch else if(STAGE==3) { if (iterations < crunch.iterations) { iterations++; // if ( myid == 0 ) cout << "." << flush; } else { stop_time = time( NULL ); crunch.time_elapsed = crunch.time_elapsed + (stop_time - start_time); iterations = 0; temperature = simmer.temperature; attraction = simmer.attraction; damping_mult = simmer.damping_mult; min_edges = 99; fineDensity = true; STAGE = 5; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering simmer stage ..."; } } // STAGE 5: Simmer else if( STAGE==5 ) { if (iterations < simmer.iterations) { if (temperature > 50) temperature -= 2; iterations++; // if ( myid == 0 ) cout << "." << flush; } else { stop_time = time( NULL ); simmer.time_elapsed = simmer.time_elapsed + (stop_time - start_time); STAGE = 6; // if ( myid == 0 ) // cout << "Layout calculation completed in " << // ( liquid.time_elapsed + expansion.time_elapsed + // cooldown.time_elapsed + crunch.time_elapsed + // simmer.time_elapsed ) // << " seconds (not including I/O)." // << endl; } } // STAGE 6: All Done! else if ( STAGE == 6) { /* // output parameters (for debugging) cout << "ReCompute is using the following parameters: "<< endl; cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature << ", attract = " << attraction << ", damping_mult = " << damping_mult << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length << ", fineDensity = " << fineDensity << endl; */ return 0; } // **************************************** // END AUTOMATIC CONTROL SECTION // **************************************** // Still need more recomputation return 1; } // update_nodes -- this function will complete the primary node update // loop in layout's recompute routine. It follows exactly the same // sequence to ensure similarity of parallel layout to the standard layout void graph::update_nodes ( ) { vector node_indices; // node list of nodes currently being updated float old_positions[2*MAX_PROCS]; // positions before update float new_positions[2*MAX_PROCS]; // positions after update bool all_fixed; // check if all nodes are fixed // initial node list consists of 0,1,...,num_procs for ( int i = 0; i < num_procs; i++ ) node_indices.push_back( i ); // next we calculate the number of nodes there would be if the // num_nodes by num_procs schedule grid were perfectly square int square_num_nodes = (int)(num_procs + num_procs*floor ((float)(num_nodes-1)/(float)num_procs )); for ( int i = myid; i < square_num_nodes; i += num_procs ) { // get old positions get_positions ( node_indices, old_positions ); // default new position is old position get_positions ( node_indices, new_positions ); if ( i < num_nodes ) { // advance random sequence according to myid for ( int j = 0; j < 2*myid; j++ ) RNG_UNIF01(); // rand(); // calculate node energy possibilities if ( !(positions[i].fixed && real_fixed) ) update_node_pos ( i, old_positions, new_positions ); // advance random sequence for next iteration for ( unsigned int j = 2*myid; j < 2*(node_indices.size()-1); j++ ) RNG_UNIF01(); // rand(); } else { // advance random sequence according to use by // the other processors for ( unsigned int j = 0; j < 2*(node_indices.size()); j++ ) RNG_UNIF01(); //rand(); } // check if anything was actually updated (e.g. everything was fixed) all_fixed = true; for ( unsigned int j = 0; j < node_indices.size (); j++ ) if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) all_fixed = false; // update positions across processors (if not all fixed) if ( !all_fixed ) { #ifdef MUSE_MPI MPI_Allgather ( &new_positions[2*myid], 2, MPI_FLOAT, new_positions, 2, MPI_FLOAT, MPI_COMM_WORLD ); #endif // update positions (old to new) update_density ( node_indices, old_positions, new_positions ); } /* if ( myid == 0 ) { // output node list (for debugging) for ( unsigned int j = 0; j < node_indices.size(); j++ ) cout << node_indices[j] << " "; cout << endl; } */ // compute node list for next update for ( unsigned int j = 0; j < node_indices.size(); j++ ) node_indices [j] += num_procs; while ( !node_indices.empty() && node_indices.back() >= num_nodes ) node_indices.pop_back ( ); } // update first_add and fine_first_add first_add = false; if ( fineDensity ) fine_first_add = false; } // The get_positions function takes the node_indices list // and returns the corresponding positions in an array. void graph::get_positions ( vector &node_indices, float return_positions[3*MAX_PROCS] ) { // fill positions for(unsigned int i=0; i < node_indices.size(); i++) { return_positions[3*i] = positions[ node_indices[i] ].x; return_positions[3*i+1] = positions[ node_indices[i] ].y; return_positions[3*i+2] = positions[ node_indices[i] ].z; } } // update_node_pos -- this subroutine does the actual work of computing // the new position of a given node. num_act_proc gives the number // of active processes at this level for use by the random number // generators. void graph::update_node_pos ( int node_ind, float old_positions[3*MAX_PROCS], float new_positions[3*MAX_PROCS] ) { float energies[2]; // node energies for possible positions float updated_pos[2][3]; // possible positions float pos_x, pos_y, pos_z; // old VxOrd parameter float jump_length = .010 * temperature; // subtract old node density_server.Subtract ( positions[node_ind], first_add, fine_first_add, fineDensity ); // compute node energy for old solution energies[0] = Compute_Node_Energy ( node_ind ); // move node to centroid position Solve_Analytic ( node_ind, pos_x, pos_y, pos_z ); positions[node_ind].x = updated_pos[0][0] = pos_x; positions[node_ind].y = updated_pos[0][1] = pos_y; positions[node_ind].z = updated_pos[0][2] = pos_z; /* // ouput random numbers (for debugging) int rand_0, rand_1; rand_0 = rand(); rand_1 = rand(); cout << myid << ": " << rand_0 << ", " << rand_1 << endl; */ // Do random method (RAND_MAX is C++ maximum random number) updated_pos[1][0] = updated_pos[0][0] + (.5 - RNG_UNIF01()) * jump_length; updated_pos[1][1] = updated_pos[0][1] + (.5 - RNG_UNIF01()) * jump_length; updated_pos[1][2] = updated_pos[0][2] + (.5 - RNG_UNIF01()) * jump_length; // compute node energy for random position positions[node_ind].x = updated_pos[1][0]; positions[node_ind].y = updated_pos[1][1]; positions[node_ind].z = updated_pos[1][2]; energies[1] = Compute_Node_Energy ( node_ind ); /* // output update possiblities (debugging): cout << node_ind << ": (" << updated_pos[0][0] << "," << updated_pos[0][1] << "), " << energies[0] << "; (" << updated_pos[1][0] << "," << updated_pos[1][1] << "), " << energies[1] << endl; */ // add back old position positions[node_ind].x = old_positions[3*myid]; positions[node_ind].y = old_positions[3*myid+1]; positions[node_ind].z = old_positions[3*myid+2]; if ( !fineDensity && !first_add ) density_server.Add ( positions[node_ind], fineDensity ); else if ( !fine_first_add ) density_server.Add ( positions[node_ind], fineDensity ); // choose updated node position with lowest energy if ( energies[0] < energies[1] ) { new_positions[3*myid] = updated_pos[0][0]; new_positions[3*myid+1] = updated_pos[0][1]; new_positions[3*myid+2] = updated_pos[0][2]; positions[node_ind].energy = energies[0]; } else { new_positions[3*myid] = updated_pos[1][0]; new_positions[3*myid+1] = updated_pos[1][1]; new_positions[3*myid+2] = updated_pos[1][2]; positions[node_ind].energy = energies[1]; } } // update_density takes a sequence of node_indices and their positions and // updates the positions by subtracting the old positions and adding the // new positions to the density grid. void graph::update_density ( vector &node_indices, float old_positions[3*MAX_PROCS], float new_positions[3*MAX_PROCS] ) { // go through each node and subtract old position from // density grid before adding new position for ( unsigned int i = 0; i < node_indices.size(); i++ ) { positions[node_indices[i]].x = old_positions[3*i]; positions[node_indices[i]].y = old_positions[3*i+1]; positions[node_indices[i]].z = old_positions[3*i+2]; density_server.Subtract ( positions[node_indices[i]], first_add, fine_first_add, fineDensity ); positions[node_indices[i]].x = new_positions[3*i]; positions[node_indices[i]].y = new_positions[3*i+1]; positions[node_indices[i]].z = new_positions[3*i+2]; density_server.Add ( positions[node_indices[i]], fineDensity ); } } /******************************************** * Function: Compute_Node_Energy * * Description: Compute the node energy * * This code has been modified from the * * original code by B. Wylie. * *********************************************/ float graph::Compute_Node_Energy( int node_ind ) { /* Want to expand 4th power range of attraction */ float attraction_factor = attraction*attraction* attraction*attraction*2e-2; map ::iterator EI; float x_dis,y_dis,z_dis; float energy_distance, weight; float node_energy=0; // Add up all connection energies for(EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { // Get edge weight weight = EI->second; // Compute x,y distance x_dis = positions[ node_ind ].x - positions[ EI->first ].x; y_dis = positions[ node_ind ].y - positions[ EI->first ].y; z_dis = positions[ node_ind ].z - positions[ EI->first ].z; // Energy Distance energy_distance = x_dis*x_dis + y_dis*y_dis + z_dis*z_dis; if (STAGE<2) energy_distance *= energy_distance; // In the liquid phase we want to discourage long link distances if (STAGE==0) energy_distance *= energy_distance; node_energy += weight * attraction_factor * energy_distance; } // output effect of density (debugging) //cout << "[before: " << node_energy; // add density node_energy += density_server.GetDensity ( positions[ node_ind ].x, positions[ node_ind ].y, positions[ node_ind ].z, fineDensity ); // after calling density server (debugging) //cout << ", after: " << node_energy << "]" << endl; // return computated energy return node_energy; } /********************************************* * Function: Solve_Analytic * * Description: Compute the node position * * This is a modified version of the function * * originally written by B. Wylie * *********************************************/ void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y, float &pos_z) { map ::iterator EI; float total_weight = 0; float x_dis, y_dis, z_dis, x_cen=0, y_cen=0, z_cen=0; float x=0,y=0,z=0,dis; float damping,weight; // Sum up all connections for(EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { weight = EI->second; total_weight += weight; x += weight * positions[ EI->first ].x; y += weight * positions[ EI->first ].y; z += weight * positions[ EI->first ].z; } // Now set node position if (total_weight > 0) { // Compute centriod x_cen = x/total_weight; y_cen = y/total_weight; z_cen = z/total_weight; damping = 1.0 - damping_mult; pos_x = damping*positions[ node_ind ].x + (1.0-damping) * x_cen; pos_y = damping*positions[ node_ind ].y + (1.0-damping) * y_cen; pos_z = damping*positions[ node_ind ].z + (1.0-damping) * z_cen; } // No cut edge flag (?) if (min_edges == 99) return; // Don't cut at end of scale if ( CUT_END >= 39500 ) return; float num_connections = (float)sqrt((float)neighbors[node_ind].size()); float maxLength = 0; map::iterator maxIndex; // Go through nodes edges... cutting if necessary for(EI = maxIndex = neighbors[node_ind].begin(); EI !=neighbors[node_ind].end(); ++EI) { // Check for at least min edges if (neighbors[node_ind].size() < min_edges) continue; x_dis = x_cen - positions[ EI->first ].x; y_dis = y_cen - positions[ EI->first ].y; z_dis = z_cen - positions[ EI->first ].z; dis = x_dis*x_dis+y_dis*y_dis+z_dis*z_dis; dis *= num_connections; // Store maximum edge if (dis > maxLength) {maxLength = dis; maxIndex=EI;} } // If max length greater than cut_length then cut if (maxLength > cut_off_length) neighbors[ node_ind ].erase( maxIndex ); } // get_tot_energy adds up the energy for each node to give an estimate of the // quality of the minimization. float graph::get_tot_energy ( ) { float my_tot_energy, tot_energy; my_tot_energy = 0; for ( int i = myid; i < num_nodes; i += num_procs ) my_tot_energy += positions[i].energy; //vector::iterator i; //for ( i = positions.begin(); i != positions.end(); i++ ) // tot_energy += i->energy; #ifdef MUSE_MPI MPI_Reduce ( &my_tot_energy, &tot_energy, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD ); #else tot_energy = my_tot_energy; #endif return tot_energy; } int graph::draw_graph(igraph_matrix_t *res) { int count_iter=0; while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); count_iter++; } long int n=positions.size(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 3)); for (long int i=0; i 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include "igraph_hacks_internal.h" #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_memory.h" #include "igraph_error.h" #include "igraph_attributes.h" #include "config.h" #include "igraph_math.h" #include #include "foreign-pajek-header.h" #include "foreign-pajek-parser.h" #define yyscan_t void* int igraph_pajek_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_pajek_yyerror(YYLTYPE* locp, igraph_i_pajek_parsedata_t *context, char *s); char *igraph_pajek_yyget_text (yyscan_t yyscanner ); int igraph_pajek_yyget_leng (yyscan_t yyscanner ); int igraph_i_pajek_add_string_vertex_attribute(const char *name, const char *value, int len, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, int len, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_numeric_edge_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, long int count, const char *attrname, igraph_integer_t vid, igraph_real_t number); int igraph_i_pajek_add_string_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, long int count, const char *attrname, igraph_integer_t vid, const char *str); int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context); extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); extern long int igraph_i_pajek_actvertex; extern long int igraph_i_pajek_actedge; #define scanner context->scanner %} %pure-parser %output="y.tab.c" %name-prefix="igraph_pajek_yy" %defines %locations %error-verbose %parse-param { igraph_i_pajek_parsedata_t* context } %lex-param { void *scanner } %union { long int intnum; double realnum; struct { char *str; int len; } string; } %type longint; %type arcfrom; %type arcto; %type edgefrom; %type edgeto; %type number; %type word; %type vpwordpar; %type epwordpar; %type vertex; %token NEWLINE %token NUM %token ALNUM %token QSTR %token PSTR %token NETWORKLINE %token VERTICESLINE %token ARCSLINE %token EDGESLINE %token ARCSLISTLINE %token EDGESLISTLINE %token MATRIXLINE %token VP_X_FACT %token VP_Y_FACT %token VP_IC %token VP_BC %token VP_LC %token VP_LR %token VP_LPHI %token VP_BW %token VP_FOS %token VP_PHI %token VP_R %token VP_Q %token VP_LA %token VP_FONT %token VP_URL %token VP_SIZE %token EP_C %token EP_S %token EP_A %token EP_W %token EP_H1 %token EP_H2 %token EP_A1 %token EP_A2 %token EP_K1 %token EP_K2 %token EP_AP %token EP_P %token EP_L %token EP_LP %token EP_LR %token EP_LPHI %token EP_LC %token EP_LA %token EP_SIZE %token EP_FOS %% input: nethead vertices edgeblock { if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } }; nethead: /* empty */ | NETWORKLINE words NEWLINE; vertices: verticeshead NEWLINE vertdefs; verticeshead: VERTICESLINE longint { context->vcount=$2; context->vcount2=0; } | VERTICESLINE longint longint { context->vcount=$2; context->vcount2=$3; igraph_i_pajek_add_bipartite_type(context); }; vertdefs: /* empty */ | vertdefs vertexline; vertexline: NEWLINE | vertex NEWLINE | vertex { context->actvertex=$1; } vertexid vertexcoords shape params NEWLINE { } ; vertex: longint { $$=$1; context->mode=1; }; vertexid: word { igraph_i_pajek_add_string_vertex_attribute("id", $1.str, $1.len, context); }; vertexcoords: /* empty */ | number number { igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context); igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context); } | number number number { igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context); igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context); igraph_i_pajek_add_numeric_vertex_attribute("z", $3, context); }; shape: /* empty */ | word { igraph_i_pajek_add_string_vertex_attribute("shape", $1.str, $1.len, context); }; params: /* empty */ | params param; param: vpword | VP_X_FACT number { igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context); } | VP_Y_FACT number { igraph_i_pajek_add_numeric_vertex_attribute("yfact", $2, context); } | VP_IC number number number { /* RGB color */ igraph_i_pajek_add_numeric_vertex_attribute("color-red", $2, context); igraph_i_pajek_add_numeric_vertex_attribute("color-green", $3, context); igraph_i_pajek_add_numeric_vertex_attribute("color-blue", $4, context); } | VP_BC number number number { igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", $2, context); igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", $3, context); igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", $4, context); } | VP_LC number number number { igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", $2, context); igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", $3, context); igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", $4, context); } | VP_LR number { igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context); } | VP_LPHI number { igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context); } | VP_BW number { igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context); } | VP_FOS number { igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context); } | VP_PHI number { igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context); } | VP_R number { igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context); } | VP_Q number { igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context); } | VP_LA number { igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context); } | VP_SIZE number { igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", $2, context); } ; vpword: VP_FONT { context->mode=3; } vpwordpar { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("font", $3.str, $3.len, context); } | VP_URL { context->mode=3; } vpwordpar { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("url", $3.str, $3.len, context); } | VP_IC { context->mode=3; } vpwordpar { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("color", $3.str, $3.len, context); } | VP_BC { context->mode=3; } vpwordpar { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("framecolor", $3.str, $3.len, context); } | VP_LC { context->mode=3; } vpwordpar { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("labelcolor", $3.str, $3.len, context); } ; vpwordpar: word { $$=$1; }; edgeblock: /* empty */ | edgeblock arcs | edgeblock edges | edgeblock arcslist | edgeblock edgeslist | edgeblock adjmatrix; arcs: ARCSLINE NEWLINE arcsdefs { context->directed=1; } | ARCSLINE number NEWLINE arcsdefs { context->directed=1; }; arcsdefs: /* empty */ | arcsdefs arcsline; arcsline: NEWLINE | arcfrom arcto { context->actedge++; context->mode=2; } weight edgeparams NEWLINE { igraph_vector_push_back(context->vector, $1-1); igraph_vector_push_back(context->vector, $2-1); } ; arcfrom: longint; arcto: longint; edges: EDGESLINE NEWLINE edgesdefs { context->directed=0; } | EDGESLINE number NEWLINE edgesdefs { context->directed=0; } edgesdefs: /* empty */ | edgesdefs edgesline; edgesline: NEWLINE | edgefrom edgeto { context->actedge++; context->mode=2; } weight edgeparams NEWLINE { igraph_vector_push_back(context->vector, $1-1); igraph_vector_push_back(context->vector, $2-1); } ; edgefrom: longint; edgeto: longint; weight: /* empty */ | number { igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); }; edgeparams: /* empty */ | edgeparams edgeparam; edgeparam: epword | EP_C number number number { igraph_i_pajek_add_numeric_edge_attribute("color-red", $2, context); igraph_i_pajek_add_numeric_edge_attribute("color-green", $3, context); igraph_i_pajek_add_numeric_edge_attribute("color-blue", $4, context); } | EP_S number { igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); } | EP_W number { igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context); } | EP_H1 number { igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context); } | EP_H2 number { igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context); } | EP_A1 number { igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context); } | EP_A2 number { igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context); } | EP_K1 number { igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context); } | EP_K2 number { igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context); } | EP_AP number { igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context); } | EP_LP number { igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context); } | EP_LR number { igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context); } | EP_LPHI number { igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context); } | EP_LA number { igraph_i_pajek_add_numeric_edge_attribute("labeldegree", $2, context); } | EP_SIZE number { /* what is this??? */ igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); } | EP_FOS number { igraph_i_pajek_add_numeric_edge_attribute("fontsize", $2, context); } ; epword: EP_A { context->mode=4; } epwordpar { context->mode=2; igraph_i_pajek_add_string_edge_attribute("arrowtype", $3.str, $3.len, context); } | EP_P { context->mode=4; } epwordpar { context->mode=2; igraph_i_pajek_add_string_edge_attribute("linepattern", $3.str, $3.len, context); } | EP_L { context->mode=4; } epwordpar { context->mode=2; igraph_i_pajek_add_string_edge_attribute("label", $3.str, $3.len, context); } | EP_LC { context->mode=4; } epwordpar { context->mode=2; igraph_i_pajek_add_string_edge_attribute("labelcolor", $3.str, $3.len, context); } | EP_C { context->mode=4; } epwordpar { context->mode=2; igraph_i_pajek_add_string_edge_attribute("color", $3.str, $3.len, context); } ; epwordpar: word { context->mode=2; $$=$1; }; arcslist: ARCSLISTLINE NEWLINE arcslistlines { context->directed=1; }; arcslistlines: /* empty */ | arcslistlines arclistline; arclistline: NEWLINE | arclistfrom arctolist NEWLINE; arctolist: /* empty */ | arctolist arclistto; arclistfrom: longint { context->mode=0; context->actfrom=fabs($1)-1; }; arclistto: longint { igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, fabs($1)-1); }; edgeslist: EDGESLISTLINE NEWLINE edgelistlines { context->directed=0; }; edgelistlines: /* empty */ | edgelistlines edgelistline; edgelistline: NEWLINE | edgelistfrom edgetolist NEWLINE; edgetolist: /* empty */ | edgetolist edgelistto; edgelistfrom: longint { context->mode=0; context->actfrom=fabs($1)-1; }; edgelistto: longint { igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, fabs($1)-1); }; /* -----------------------------------------------------*/ adjmatrix: matrixline NEWLINE adjmatrixlines; matrixline: MATRIXLINE { context->actfrom=0; context->actto=0; context->directed=(context->vcount2==0); }; adjmatrixlines: /* empty */ | adjmatrixlines adjmatrixline; adjmatrixline: adjmatrixnumbers NEWLINE { context->actfrom++; context->actto=0; }; adjmatrixnumbers: /* empty */ | adjmatrixentry adjmatrixnumbers; adjmatrixentry: number { if ($1 != 0) { if (context->vcount2==0) { context->actedge++; igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, context->actto); } else if (context->vcount2 + context->actto < context->vcount) { context->actedge++; igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, context->vcount2+context->actto); } } context->actto++; }; /* -----------------------------------------------------*/ longint: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), igraph_pajek_yyget_leng(scanner)); }; number: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), igraph_pajek_yyget_leng(scanner)); }; words: /* empty */ | words word; word: ALNUM { $$.str=igraph_pajek_yyget_text(scanner); $$.len=igraph_pajek_yyget_leng(scanner); } | NUM { $$.str=igraph_pajek_yyget_text(scanner); $$.len=igraph_pajek_yyget_leng(scanner); } | QSTR { $$.str=igraph_pajek_yyget_text(scanner)+1; $$.len=igraph_pajek_yyget_leng(scanner)-2; }; %% int igraph_pajek_yyerror(YYLTYPE* locp, igraph_i_pajek_parsedata_t *context, char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "Parse error in Pajek file, line %i (%s)", locp->first_line, s); return 0; } igraph_real_t igraph_pajek_get_number(const char *str, long int length) { igraph_real_t num; char *tmp=igraph_Calloc(length+1, char); strncpy(tmp, str, length); tmp[length]='\0'; sscanf(tmp, "%lf", &num); igraph_Free(tmp); return num; } /* TODO: NA's */ int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, long int count, const char *attrname, igraph_integer_t vid, igraph_real_t number) { long int attrsize=igraph_trie_size(names); long int id; igraph_vector_t *na; igraph_attribute_record_t *rec; igraph_trie_get(names, attrname, &id); if (id == attrsize) { /* add a new attribute */ rec=igraph_Calloc(1, igraph_attribute_record_t); na=igraph_Calloc(1, igraph_vector_t); igraph_vector_init(na, count); rec->name=strdup(attrname); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->value=na; igraph_vector_ptr_push_back(attrs, rec); } rec=VECTOR(*attrs)[id]; na=(igraph_vector_t*)rec->value; if (igraph_vector_size(na) == vid) { IGRAPH_CHECK(igraph_vector_push_back(na, number)); } else if (igraph_vector_size(na) < vid) { long int origsize=igraph_vector_size(na); IGRAPH_CHECK(igraph_vector_resize(na, (long int)vid+1)); for (;origsizename=strdup(attrname); rec->type=IGRAPH_ATTRIBUTE_STRING; rec->value=na; igraph_vector_ptr_push_back(attrs, rec); } rec=VECTOR(*attrs)[id]; na=(igraph_strvector_t*)rec->value; if (igraph_strvector_size(na) <= vid) { long int origsize=igraph_strvector_size(na); IGRAPH_CHECK(igraph_strvector_resize(na, vid+1)); for (;origsizevertex_attribute_names, context->vertex_attributes, context->vcount, name, context->actvertex-1, tmp); igraph_Free(tmp); IGRAPH_FINALLY_CLEAN(1); return ret; } int igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, int len, igraph_i_pajek_parsedata_t *context) { char *tmp; int ret; tmp=igraph_Calloc(len+1, char); if (tmp==0) { IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp); strncpy(tmp, value, len); tmp[len]='\0'; ret=igraph_i_pajek_add_string_attribute(context->edge_attribute_names, context->edge_attributes, context->actedge, name, context->actedge-1, tmp); igraph_Free(tmp); IGRAPH_FINALLY_CLEAN(1); return ret; } int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context) { return igraph_i_pajek_add_numeric_attribute(context->vertex_attribute_names, context->vertex_attributes, context->vcount, name, context->actvertex-1, value); } int igraph_i_pajek_add_numeric_edge_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context) { return igraph_i_pajek_add_numeric_attribute(context->edge_attribute_names, context->edge_attributes, context->actedge, name, context->actedge-1, value); } int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context) { const char *attrname="type"; igraph_trie_t *names=context->vertex_attribute_names; igraph_vector_ptr_t *attrs=context->vertex_attributes; int i, n=context->vcount, n1=context->vcount2; long int attrid, attrsize=igraph_trie_size(names); igraph_attribute_record_t *rec; igraph_vector_t *na; if (n1 > n) { IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file", IGRAPH_PARSEERROR); } igraph_trie_get(names, attrname, &attrid); if (attrid != attrsize) { IGRAPH_ERROR("Duplicate 'type' attribute in Pajek file, " "this should not happen", IGRAPH_EINTERNAL); } /* add a new attribute */ rec=igraph_Calloc(1, igraph_attribute_record_t); na=igraph_Calloc(1, igraph_vector_t); igraph_vector_init(na, n); rec->name=strdup(attrname); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->value=na; igraph_vector_ptr_push_back(attrs, rec); for (i=0; ivector; int i, n1=context->vcount2; int ne=igraph_vector_size(edges); for (i=0; i n1 && v2 > n1) ) { IGRAPH_WARNING("Invalid edge in bipartite graph"); } } return 0; } igraph/src/igraph_threading.h0000644000176000001440000000253412325527073016025 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_THREADING_H #define IGRAPH_THREADING_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /** * \define IGRAPH_THREAD_SAFE * * Macro that is defined to be 1 if the current build of the * igraph library is thread-safe, and 0 if it is not. */ #define IGRAPH_THREAD_SAFE 0 __END_DECLS #endif igraph/src/glpini02.c0000644000176000001440000002155712325527073014153 0ustar ripleyusers/* glpini02.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpapi.h" struct var { /* structural variable */ int j; /* ordinal number */ double q; /* penalty value */ }; static int fcmp(const void *ptr1, const void *ptr2) { /* this routine is passed to the qsort() function */ struct var *col1 = (void *)ptr1, *col2 = (void *)ptr2; if (col1->q < col2->q) return -1; if (col1->q > col2->q) return +1; return 0; } static int get_column(glp_prob *lp, int j, int ind[], double val[]) { /* Bixby's algorithm assumes that the constraint matrix is scaled such that the maximum absolute value in every non-zero row and column is 1 */ int k, len; double big; len = glp_get_mat_col(lp, j, ind, val); big = 0.0; for (k = 1; k <= len; k++) if (big < fabs(val[k])) big = fabs(val[k]); if (big == 0.0) big = 1.0; for (k = 1; k <= len; k++) val[k] /= big; return len; } static void cpx_basis(glp_prob *lp) { /* main routine */ struct var *C, *C2, *C3, *C4; int m, n, i, j, jk, k, l, ll, t, n2, n3, n4, type, len, *I, *r, *ind; double alpha, gamma, cmax, temp, *v, *val; xprintf("Constructing initial basis...\n"); /* determine the number of rows and columns */ m = glp_get_num_rows(lp); n = glp_get_num_cols(lp); /* allocate working arrays */ C = xcalloc(1+n, sizeof(struct var)); I = xcalloc(1+m, sizeof(int)); r = xcalloc(1+m, sizeof(int)); v = xcalloc(1+m, sizeof(double)); ind = xcalloc(1+m, sizeof(int)); val = xcalloc(1+m, sizeof(double)); /* make all auxiliary variables non-basic */ for (i = 1; i <= m; i++) { if (glp_get_row_type(lp, i) != GLP_DB) glp_set_row_stat(lp, i, GLP_NS); else if (fabs(glp_get_row_lb(lp, i)) <= fabs(glp_get_row_ub(lp, i))) glp_set_row_stat(lp, i, GLP_NL); else glp_set_row_stat(lp, i, GLP_NU); } /* make all structural variables non-basic */ for (j = 1; j <= n; j++) { if (glp_get_col_type(lp, j) != GLP_DB) glp_set_col_stat(lp, j, GLP_NS); else if (fabs(glp_get_col_lb(lp, j)) <= fabs(glp_get_col_ub(lp, j))) glp_set_col_stat(lp, j, GLP_NL); else glp_set_col_stat(lp, j, GLP_NU); } /* C2 is a set of free structural variables */ n2 = 0, C2 = C + 0; for (j = 1; j <= n; j++) { type = glp_get_col_type(lp, j); if (type == GLP_FR) { n2++; C2[n2].j = j; C2[n2].q = 0.0; } } /* C3 is a set of structural variables having excatly one (lower or upper) bound */ n3 = 0, C3 = C2 + n2; for (j = 1; j <= n; j++) { type = glp_get_col_type(lp, j); if (type == GLP_LO) { n3++; C3[n3].j = j; C3[n3].q = + glp_get_col_lb(lp, j); } else if (type == GLP_UP) { n3++; C3[n3].j = j; C3[n3].q = - glp_get_col_ub(lp, j); } } /* C4 is a set of structural variables having both (lower and upper) bounds */ n4 = 0, C4 = C3 + n3; for (j = 1; j <= n; j++) { type = glp_get_col_type(lp, j); if (type == GLP_DB) { n4++; C4[n4].j = j; C4[n4].q = glp_get_col_lb(lp, j) - glp_get_col_ub(lp, j); } } /* compute gamma = max{|c[j]|: 1 <= j <= n} */ gamma = 0.0; for (j = 1; j <= n; j++) { temp = fabs(glp_get_obj_coef(lp, j)); if (gamma < temp) gamma = temp; } /* compute cmax */ cmax = (gamma == 0.0 ? 1.0 : 1000.0 * gamma); /* compute final penalty for all structural variables within sets C2, C3, and C4 */ switch (glp_get_obj_dir(lp)) { case GLP_MIN: temp = +1.0; break; case GLP_MAX: temp = -1.0; break; default: xassert(lp != lp); } for (k = 1; k <= n2+n3+n4; k++) { j = C[k].j; C[k].q += (temp * glp_get_obj_coef(lp, j)) / cmax; } /* sort structural variables within C2, C3, and C4 in ascending order of penalty value */ qsort(C2+1, n2, sizeof(struct var), fcmp); for (k = 1; k < n2; k++) xassert(C2[k].q <= C2[k+1].q); qsort(C3+1, n3, sizeof(struct var), fcmp); for (k = 1; k < n3; k++) xassert(C3[k].q <= C3[k+1].q); qsort(C4+1, n4, sizeof(struct var), fcmp); for (k = 1; k < n4; k++) xassert(C4[k].q <= C4[k+1].q); /*** STEP 1 ***/ for (i = 1; i <= m; i++) { type = glp_get_row_type(lp, i); if (type != GLP_FX) { /* row i is either free or inequality constraint */ glp_set_row_stat(lp, i, GLP_BS); I[i] = 1; r[i] = 1; } else { /* row i is equality constraint */ I[i] = 0; r[i] = 0; } v[i] = +DBL_MAX; } /*** STEP 2 ***/ for (k = 1; k <= n2+n3+n4; k++) { jk = C[k].j; len = get_column(lp, jk, ind, val); /* let alpha = max{|A[l,jk]|: r[l] = 0} and let l' be such that alpha = |A[l',jk]| */ alpha = 0.0, ll = 0; for (t = 1; t <= len; t++) { l = ind[t]; if (r[l] == 0 && alpha < fabs(val[t])) alpha = fabs(val[t]), ll = l; } if (alpha >= 0.99) { /* B := B union {jk} */ glp_set_col_stat(lp, jk, GLP_BS); I[ll] = 1; v[ll] = alpha; /* r[l] := r[l] + 1 for all l such that |A[l,jk]| != 0 */ for (t = 1; t <= len; t++) { l = ind[t]; if (val[t] != 0.0) r[l]++; } /* continue to the next k */ continue; } /* if |A[l,jk]| > 0.01 * v[l] for some l, continue to the next k */ for (t = 1; t <= len; t++) { l = ind[t]; if (fabs(val[t]) > 0.01 * v[l]) break; } if (t <= len) continue; /* otherwise, let alpha = max{|A[l,jk]|: I[l] = 0} and let l' be such that alpha = |A[l',jk]| */ alpha = 0.0, ll = 0; for (t = 1; t <= len; t++) { l = ind[t]; if (I[l] == 0 && alpha < fabs(val[t])) alpha = fabs(val[t]), ll = l; } /* if alpha = 0, continue to the next k */ if (alpha == 0.0) continue; /* B := B union {jk} */ glp_set_col_stat(lp, jk, GLP_BS); I[ll] = 1; v[ll] = alpha; /* r[l] := r[l] + 1 for all l such that |A[l,jk]| != 0 */ for (t = 1; t <= len; t++) { l = ind[t]; if (val[t] != 0.0) r[l]++; } } /*** STEP 3 ***/ /* add an artificial variable (auxiliary variable for equality constraint) to cover each remaining uncovered row */ for (i = 1; i <= m; i++) if (I[i] == 0) glp_set_row_stat(lp, i, GLP_BS); /* free working arrays */ xfree(C); xfree(I); xfree(r); xfree(v); xfree(ind); xfree(val); return; } /*********************************************************************** * NAME * * glp_cpx_basis - construct Bixby's initial LP basis * * SYNOPSIS * * void glp_cpx_basis(glp_prob *lp); * * DESCRIPTION * * The routine glp_cpx_basis constructs an advanced initial basis for * the specified problem object. * * The routine is based on Bixby's algorithm described in the paper: * * Robert E. Bixby. Implementing the Simplex Method: The Initial Basis. * ORSA Journal on Computing, Vol. 4, No. 3, 1992, pp. 267-84. */ void glp_cpx_basis(glp_prob *lp) { if (lp->m == 0 || lp->n == 0) glp_std_basis(lp); else cpx_basis(lp); return; } /* eof */ igraph/src/dsortc.f0000644000176000001440000002207612325527073014025 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsortc c c\Description: c Sorts the complex array in XREAL and XIMAG into the order c specified by WHICH and optionally applies the permutation to the c real array Y. It is assumed that if an element of XIMAG is c nonzero, then its negative is also an element. In other words, c both members of a complex conjugate pair are to be sorted and the c pairs are kept adjacent to each other. c c\Usage: c call igraphdsortc c ( WHICH, APPLY, N, XREAL, XIMAG, Y ) c c\Arguments c WHICH Character*2. (Input) c 'LM' -> sort XREAL,XIMAG into increasing order of magnitude. c 'SM' -> sort XREAL,XIMAG into decreasing order of magnitude. c 'LR' -> sort XREAL into increasing order of algebraic. c 'SR' -> sort XREAL into decreasing order of algebraic. c 'LI' -> sort XIMAG into increasing order of magnitude. c 'SI' -> sort XIMAG into decreasing order of magnitude. c NOTE: If an element of XIMAG is non-zero, then its negative c is also an element. c c APPLY Logical. (Input) c APPLY = .TRUE. -> apply the sorted order to array Y. c APPLY = .FALSE. -> do not apply the sorted order to array Y. c c N Integer. (INPUT) c Size of the arrays. c c XREAL, Double precision array of length N. (INPUT/OUTPUT) c XIMAG Real and imaginary part of the array to be sorted. c c Y Double precision array of length N. (INPUT/OUTPUT) c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.1' c Adapted from the sort routine in LANSO. c c\SCCS Information: @(#) c FILE: sortc.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsortc (which, apply, n, xreal, ximag, y) c c %------------------% c | Scalar Arguments | c %------------------% c character*2 which logical apply integer n c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & xreal(0:n-1), ximag(0:n-1), y(0:n-1) c c %---------------% c | Local Scalars | c %---------------% c integer i, igap, j Double precision & temp, temp1, temp2 c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlapy2 external dlapy2 c c %-----------------------% c | Executable Statements | c %-----------------------% c igap = n / 2 c if (which .eq. 'LM') then c c %------------------------------------------------------% c | Sort XREAL,XIMAG into increasing order of magnitude. | c %------------------------------------------------------% c 10 continue if (igap .eq. 0) go to 9000 c do 30 i = igap, n-1 j = i-igap 20 continue c if (j.lt.0) go to 30 c temp1 = dlapy2(xreal(j),ximag(j)) temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) c if (temp1.gt.temp2) then temp = xreal(j) xreal(j) = xreal(j+igap) xreal(j+igap) = temp c temp = ximag(j) ximag(j) = ximag(j+igap) ximag(j+igap) = temp c if (apply) then temp = y(j) y(j) = y(j+igap) y(j+igap) = temp end if else go to 30 end if j = j-igap go to 20 30 continue igap = igap / 2 go to 10 c else if (which .eq. 'SM') then c c %------------------------------------------------------% c | Sort XREAL,XIMAG into decreasing order of magnitude. | c %------------------------------------------------------% c 40 continue if (igap .eq. 0) go to 9000 c do 60 i = igap, n-1 j = i-igap 50 continue c if (j .lt. 0) go to 60 c temp1 = dlapy2(xreal(j),ximag(j)) temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) c if (temp1.lt.temp2) then temp = xreal(j) xreal(j) = xreal(j+igap) xreal(j+igap) = temp c temp = ximag(j) ximag(j) = ximag(j+igap) ximag(j+igap) = temp c if (apply) then temp = y(j) y(j) = y(j+igap) y(j+igap) = temp end if else go to 60 endif j = j-igap go to 50 60 continue igap = igap / 2 go to 40 c else if (which .eq. 'LR') then c c %------------------------------------------------% c | Sort XREAL into increasing order of algebraic. | c %------------------------------------------------% c 70 continue if (igap .eq. 0) go to 9000 c do 90 i = igap, n-1 j = i-igap 80 continue c if (j.lt.0) go to 90 c if (xreal(j).gt.xreal(j+igap)) then temp = xreal(j) xreal(j) = xreal(j+igap) xreal(j+igap) = temp c temp = ximag(j) ximag(j) = ximag(j+igap) ximag(j+igap) = temp c if (apply) then temp = y(j) y(j) = y(j+igap) y(j+igap) = temp end if else go to 90 endif j = j-igap go to 80 90 continue igap = igap / 2 go to 70 c else if (which .eq. 'SR') then c c %------------------------------------------------% c | Sort XREAL into decreasing order of algebraic. | c %------------------------------------------------% c 100 continue if (igap .eq. 0) go to 9000 do 120 i = igap, n-1 j = i-igap 110 continue c if (j.lt.0) go to 120 c if (xreal(j).lt.xreal(j+igap)) then temp = xreal(j) xreal(j) = xreal(j+igap) xreal(j+igap) = temp c temp = ximag(j) ximag(j) = ximag(j+igap) ximag(j+igap) = temp c if (apply) then temp = y(j) y(j) = y(j+igap) y(j+igap) = temp end if else go to 120 endif j = j-igap go to 110 120 continue igap = igap / 2 go to 100 c else if (which .eq. 'LI') then c c %------------------------------------------------% c | Sort XIMAG into increasing order of magnitude. | c %------------------------------------------------% c 130 continue if (igap .eq. 0) go to 9000 do 150 i = igap, n-1 j = i-igap 140 continue c if (j.lt.0) go to 150 c if (abs(ximag(j)).gt.abs(ximag(j+igap))) then temp = xreal(j) xreal(j) = xreal(j+igap) xreal(j+igap) = temp c temp = ximag(j) ximag(j) = ximag(j+igap) ximag(j+igap) = temp c if (apply) then temp = y(j) y(j) = y(j+igap) y(j+igap) = temp end if else go to 150 endif j = j-igap go to 140 150 continue igap = igap / 2 go to 130 c else if (which .eq. 'SI') then c c %------------------------------------------------% c | Sort XIMAG into decreasing order of magnitude. | c %------------------------------------------------% c 160 continue if (igap .eq. 0) go to 9000 do 180 i = igap, n-1 j = i-igap 170 continue c if (j.lt.0) go to 180 c if (abs(ximag(j)).lt.abs(ximag(j+igap))) then temp = xreal(j) xreal(j) = xreal(j+igap) xreal(j+igap) = temp c temp = ximag(j) ximag(j) = ximag(j+igap) ximag(j+igap) = temp c if (apply) then temp = y(j) y(j) = y(j+igap) y(j+igap) = temp end if else go to 180 endif j = j-igap go to 170 180 continue igap = igap / 2 go to 160 end if c 9000 continue return c c %---------------% c | End of igraphdsortc | c %---------------% c end igraph/src/atlas.c0000644000176000001440000000526212325527072013625 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_constructors.h" #include "atlas-edges.h" #include "config.h" /** * \function igraph_atlas * \brief Create a small graph from the \quote Graph Atlas \endquote. * * * The number of the graph is given as a parameter. * The graphs are listed: \olist * \oli in increasing order of number of nodes; * \oli for a fixed number of nodes, in increasing order of the * number of edges; * \oli for fixed numbers of nodes and edges, in increasing * order of the degree sequence, for example 111223 < 112222; * \oli for fixed degree sequence, in increasing number of * automorphisms. * \endolist * * * The data was converted from the NetworkX software package, * see http://networkx.github.io . * * * See \emb An Atlas of Graphs \eme by Ronald C. Read and Robin J. Wilson, * Oxford University Press, 1998. * * \param graph Pointer to an uninitialized graph object. * \param number The number of the graph to generate. * * Added in version 0.2. * * Time complexity: O(|V|+|E|), the number of vertices plus the number of * edges. * * \example examples/simple/igraph_atlas.c */ int igraph_atlas(igraph_t *graph, int number) { igraph_integer_t pos, n, e; igraph_vector_t v=IGRAPH_VECTOR_NULL; if (number < 0 || number >= (int) (sizeof(igraph_i_atlas_edges_pos)/sizeof(long int))) { IGRAPH_ERROR("No such graph in atlas", IGRAPH_EINVAL); } pos=(igraph_integer_t) igraph_i_atlas_edges_pos[number]; n=(igraph_integer_t) igraph_i_atlas_edges[pos]; e=(igraph_integer_t) igraph_i_atlas_edges[pos+1]; IGRAPH_CHECK(igraph_create(graph, igraph_vector_view(&v,igraph_i_atlas_edges+pos+2, e*2), n, IGRAPH_UNDIRECTED)); return 0; } igraph/src/forestfire.c0000644000176000001440000002132012325527073014663 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_games.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_interface.h" #include "igraph_constructors.h" #include "igraph_dqueue.h" #include "config.h" typedef struct igraph_i_forest_fire_data_t { igraph_vector_t *inneis; igraph_vector_t *outneis; long int no_of_nodes; } igraph_i_forest_fire_data_t; void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { long int i; for (i=0; ino_of_nodes; i++) { igraph_vector_destroy(data->inneis+i); igraph_vector_destroy(data->outneis+i); } } /** * \function igraph_forest_fire_game * \brief Generates a network according to the \quote forest fire game \endquote * * The forest fire model intends to reproduce the following network * characteristics, observed in real networks: * \ilist * \ili Heavy-tailed in-degree distribution. * \ili Heavy-tailed out-degree distribution. * \ili Communities. * \ili Densification power-law. The network is densifying in time, * according to a power-law rule. * \ili Shrinking diameter. The diameter of the network decreases in * time. * \endilist * * * The network is generated in the following way. One vertex is added at * a time. This vertex connects to (cites) ambs vertices already * present in the network, chosen uniformly random. Now, for each cited * vertex v we do the following procedure: * \olist * \oli We generate two random number, x and y, that are * geometrically distributed with means p/(1-p) and * rp(1-rp). (p is fw_prob, r is * bw_factor.) The new vertex cites x outgoing neighbors * and y incoming neighbors of v, from those which are * not yet cited by the new vertex. If there are less than x or * y such vertices available then we cite all of them. * \oli The same procedure is applied to all the newly cited * vertices. * \endolist * * See also: * Jure Leskovec, Jon Kleinberg and Christos Faloutsos. Graphs over time: * densification laws, shrinking diameters and possible explanations. * \emb KDD '05: Proceeding of the eleventh ACM SIGKDD international * conference on Knowledge discovery in data mining \eme, 177--187, 2005. * * Note however, that the version of the model in the published paper is incorrect * in the sense that it cannot generate the kind of graphs the authors * claim. A corrected version is available from * http://cs.stanford.edu/people/jure/pubs/powergrowth-tkdd.pdf , our * implementation is based on this. * * \param graph Pointer to an uninitialized graph object. * \param nodes The number of vertices in the graph. * \param fw_prob The forward burning probability. * \param bw_factor The backward burning ratio. The backward burning probability is calculated as bw.factor*fw.prob. * \param pambs The number of ambassador vertices. * \param directed Whether to create a directed graph. * \return Error code. * * Time complexity: TODO. */ int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, igraph_real_t fw_prob, igraph_real_t bw_factor, igraph_integer_t pambs, igraph_bool_t directed) { igraph_vector_long_t visited; long int no_of_nodes=nodes, actnode, i; igraph_vector_t edges; igraph_vector_t *inneis, *outneis; igraph_i_forest_fire_data_t data; igraph_dqueue_t neiq; long int ambs=pambs; igraph_real_t param_geom_out=1-fw_prob; igraph_real_t param_geom_in=1-fw_prob*bw_factor; if (fw_prob < 0) { IGRAPH_ERROR("Forest fire model: 'fw_prob' should be between non-negative", IGRAPH_EINVAL); } if (bw_factor < 0) { IGRAPH_ERROR("Forest fire model: 'bw_factor' should be non-negative", IGRAPH_EINVAL); } if (ambs < 0) { IGRAPH_ERROR("Number of ambassadors ('ambs') should be non-negative", IGRAPH_EINVAL); } if (fw_prob == 0 || ambs == 0) { IGRAPH_WARNING("'fw_prob or ambs is zero, creating empty graph"); IGRAPH_CHECK(igraph_empty(graph, nodes, directed)); return 0; } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); inneis=igraph_Calloc(no_of_nodes, igraph_vector_t); if (!inneis) { IGRAPH_ERROR("Cannot run forest fire model", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, inneis); outneis=igraph_Calloc(no_of_nodes, igraph_vector_t); if (!outneis) { IGRAPH_ERROR("Cannot run forest fire model", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, outneis); data.inneis=inneis; data.outneis=outneis; data.no_of_nodes=no_of_nodes; IGRAPH_FINALLY(igraph_i_forest_fire_free, &data); for (i=0; i= no_out) { for (i=0; i 0; ) { long int which=RNG_INTEGER(0, oleft-1); long int nei=(long int) VECTOR(*outv)[which]; VECTOR(*outv)[which] = VECTOR(*outv)[oleft-1]; VECTOR(*outv)[oleft-1] = nei; if (VECTOR(visited)[nei] != actnode+1) { ADD_EDGE_TO(nei); i++; } oleft--; } } /* incoming neighbors */ if (neis_in >= no_in) { for (i=0; i 0; ) { long int which=RNG_INTEGER(0, ileft-1); long int nei=(long int) VECTOR(*inv)[which]; VECTOR(*inv)[which] = VECTOR(*inv)[ileft-1]; VECTOR(*inv)[ileft-1] = nei; if (VECTOR(visited)[nei] != actnode+1) { ADD_EDGE_TO(nei); i++; } ileft--; } } } /* while neiq not empty */ } /* actnode < no_of_nodes */ #undef ADD_EDGE_TO RNG_END(); IGRAPH_PROGRESS("Forest fire: ", 100.0, NULL); igraph_dqueue_destroy(&neiq); igraph_vector_long_destroy(&visited); igraph_i_forest_fire_free(&data); igraph_free(outneis); igraph_free(inneis); IGRAPH_FINALLY_CLEAN(5); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } igraph/src/igraph_vector_pmt.h0000644000176000001440000002516012325527073016242 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*--------------------*/ /* Allocation */ /*--------------------*/ int FUNCTION(igraph_vector,init)(TYPE(igraph_vector)* v, long int size); int FUNCTION(igraph_vector,init_copy)(TYPE(igraph_vector)* v, BASE* data, long int length); int FUNCTION(igraph_vector,init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); int FUNCTION(igraph_vector,copy)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); void FUNCTION(igraph_vector,destroy)(TYPE(igraph_vector)* v); long int FUNCTION(igraph_vector,capacity)(const TYPE(igraph_vector)*v); /*--------------------*/ /* Accessing elements */ /*--------------------*/ #ifndef VECTOR /** * \ingroup vector * \define VECTOR * \brief Accessing an element of a vector. * * Usage: * \verbatim VECTOR(v)[0] \endverbatim * to access the first element of the vector, you can also use this in * assignments, like: * \verbatim VECTOR(v)[10]=5; \endverbatim * * Note that there are no range checks right now. * This functionality might be redefined later as a real function * instead of a #define. * \param v The vector object. * * Time complexity: O(1). */ #define VECTOR(v) ((v).stor_begin) #endif BASE FUNCTION(igraph_vector,e)(const TYPE(igraph_vector)* v, long int pos); BASE* FUNCTION(igraph_vector,e_ptr)(const TYPE(igraph_vector)* v, long int pos); void FUNCTION(igraph_vector,set)(TYPE(igraph_vector)* v, long int pos, BASE value); BASE FUNCTION(igraph_vector,tail)(const TYPE(igraph_vector) *v); /*-----------------------*/ /* Initializing elements */ /*-----------------------*/ void FUNCTION(igraph_vector,null)(TYPE(igraph_vector)* v); void FUNCTION(igraph_vector,fill)(TYPE(igraph_vector)* v, BASE e); /*-----------------------*/ /* Vector views */ /*-----------------------*/ const TYPE(igraph_vector) *FUNCTION(igraph_vector,view)(const TYPE(igraph_vector) *v, const BASE *data, long int length); /*-----------------------*/ /* Copying vectors */ /*-----------------------*/ void FUNCTION(igraph_vector,copy_to)(const TYPE(igraph_vector) *v, BASE* to); int FUNCTION(igraph_vector,update)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); int FUNCTION(igraph_vector,append)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); int FUNCTION(igraph_vector,swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); /*-----------------------*/ /* Exchanging elements */ /*-----------------------*/ int FUNCTION(igraph_vector,swap_elements)(TYPE(igraph_vector) *v, long int i, long int j); int FUNCTION(igraph_vector,reverse)(TYPE(igraph_vector) *v); int FUNCTION(igraph_vector,shuffle)(TYPE(igraph_vector) *v); /*-----------------------*/ /* Vector operations */ /*-----------------------*/ void FUNCTION(igraph_vector,add_constant)(TYPE(igraph_vector) *v, BASE plus); void FUNCTION(igraph_vector,scale)(TYPE(igraph_vector) *v, BASE by); int FUNCTION(igraph_vector,add)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); int FUNCTION(igraph_vector,sub)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); int FUNCTION(igraph_vector,mul)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); int FUNCTION(igraph_vector,div)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); int FUNCTION(igraph_vector,cumsum)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); #ifndef NOABS int FUNCTION(igraph_vector,abs)(TYPE(igraph_vector) *v); #endif /*------------------------------*/ /* Comparison */ /*------------------------------*/ igraph_bool_t FUNCTION(igraph_vector,all_e)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); igraph_bool_t FUNCTION(igraph_vector,all_l)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); igraph_bool_t FUNCTION(igraph_vector,all_g)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); igraph_bool_t FUNCTION(igraph_vector,all_le)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); igraph_bool_t FUNCTION(igraph_vector,all_ge)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); /*------------------------------*/ /* Finding minimum and maximum */ /*------------------------------*/ BASE FUNCTION(igraph_vector,min)(const TYPE(igraph_vector)* v); BASE FUNCTION(igraph_vector,max)(const TYPE(igraph_vector)* v); long int FUNCTION(igraph_vector,which_min)(const TYPE(igraph_vector)* v); long int FUNCTION(igraph_vector,which_max)(const TYPE(igraph_vector)* v); int FUNCTION(igraph_vector,minmax)(const TYPE(igraph_vector) *v, BASE *min, BASE *max); int FUNCTION(igraph_vector,which_minmax)(const TYPE(igraph_vector) *v, long int *which_min, long int *which_max); /*-------------------*/ /* Vector properties */ /*-------------------*/ igraph_bool_t FUNCTION(igraph_vector,empty) (const TYPE(igraph_vector)* v); long int FUNCTION(igraph_vector,size) (const TYPE(igraph_vector)* v); igraph_bool_t FUNCTION(igraph_vector,isnull)(const TYPE(igraph_vector) *v); BASE FUNCTION(igraph_vector,sum)(const TYPE(igraph_vector) *v); igraph_real_t FUNCTION(igraph_vector,sumsq)(const TYPE(igraph_vector) *v); BASE FUNCTION(igraph_vector,prod)(const TYPE(igraph_vector) *v); igraph_bool_t FUNCTION(igraph_vector,isininterval)(const TYPE(igraph_vector) *v, BASE low, BASE high); igraph_bool_t FUNCTION(igraph_vector,any_smaller)(const TYPE(igraph_vector) *v, BASE limit); igraph_bool_t FUNCTION(igraph_vector,is_equal)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); BASE FUNCTION(igraph_vector,maxdifference)(const TYPE(igraph_vector) *m1, const TYPE(igraph_vector) *m2); /*------------------------*/ /* Searching for elements */ /*------------------------*/ igraph_bool_t FUNCTION(igraph_vector,contains)(const TYPE(igraph_vector) *v, BASE e); igraph_bool_t FUNCTION(igraph_vector,search)(const TYPE(igraph_vector) *v, long int from, BASE what, long int *pos); igraph_bool_t FUNCTION(igraph_vector,binsearch)(const TYPE(igraph_vector) *v, BASE what, long int *pos); igraph_bool_t FUNCTION(igraph_vector,binsearch2)(const TYPE(igraph_vector) *v, BASE what); /*------------------------*/ /* Resizing operations */ /*------------------------*/ void FUNCTION(igraph_vector,clear)(TYPE(igraph_vector)* v); int FUNCTION(igraph_vector,resize)(TYPE(igraph_vector)* v, long int newsize); int FUNCTION(igraph_vector,resize_min)(TYPE(igraph_vector)*v); int FUNCTION(igraph_vector,reserve)(TYPE(igraph_vector)* v, long int size); int FUNCTION(igraph_vector,push_back)(TYPE(igraph_vector)* v, BASE e); BASE FUNCTION(igraph_vector,pop_back)(TYPE(igraph_vector)* v); int FUNCTION(igraph_vector,insert)(TYPE(igraph_vector) *v, long int pos, BASE value); void FUNCTION(igraph_vector,remove)(TYPE(igraph_vector) *v, long int elem); void FUNCTION(igraph_vector,remove_section)(TYPE(igraph_vector) *v, long int from, long int to); /*-----------*/ /* Sorting */ /*-----------*/ void FUNCTION(igraph_vector,sort)(TYPE(igraph_vector) *v); long int FUNCTION(igraph_vector,qsort_ind)(TYPE(igraph_vector) *v, igraph_vector_t *inds, igraph_bool_t descending); /*-----------*/ /* Printing */ /*-----------*/ int FUNCTION(igraph_vector,print)(const TYPE(igraph_vector) *v); int FUNCTION(igraph_vector,printf)(const TYPE(igraph_vector) *v, const char *format); int FUNCTION(igraph_vector,fprint)(const TYPE(igraph_vector) *v, FILE *file); #ifdef BASE_COMPLEX int igraph_vector_complex_real(const igraph_vector_complex_t *v, igraph_vector_t *real); int igraph_vector_complex_imag(const igraph_vector_complex_t *v, igraph_vector_t *imag); int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, igraph_vector_t *real, igraph_vector_t *imag); int igraph_vector_complex_create(igraph_vector_complex_t *v, const igraph_vector_t *real, const igraph_vector_t *imag); int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, const igraph_vector_t *r, const igraph_vector_t *theta); #endif /* ----------------------------------------------------------------------------*/ /* For internal use only, may be removed, rewritten ... */ /* ----------------------------------------------------------------------------*/ int FUNCTION(igraph_vector,init_real)(TYPE(igraph_vector)*v, int no, ...); int FUNCTION(igraph_vector,init_int)(TYPE(igraph_vector)*v, int no, ...); int FUNCTION(igraph_vector,init_real_end)(TYPE(igraph_vector)*v, BASE endmark, ...); int FUNCTION(igraph_vector,init_int_end)(TYPE(igraph_vector)*v, int endmark, ...); int FUNCTION(igraph_vector,move_interval)(TYPE(igraph_vector) *v, long int begin, long int end, long int to); int FUNCTION(igraph_vector,move_interval2)(TYPE(igraph_vector) *v, long int begin, long int end, long int to); void FUNCTION(igraph_vector,permdelete)(TYPE(igraph_vector) *v, const igraph_vector_t *index, long int nremove); int FUNCTION(igraph_vector,filter_smaller)(TYPE(igraph_vector) *v, BASE elem); int FUNCTION(igraph_vector,get_interval)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *res, long int from, long int to); int FUNCTION(igraph_vector,difference_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); int FUNCTION(igraph_vector,intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); int FUNCTION(igraph_vector,index)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *newv, const igraph_vector_t *idx); int FUNCTION(igraph_vector,index_int)(TYPE(igraph_vector) *v, const igraph_vector_int_t *idx); igraph/src/amd_preprocess.c0000644000176000001440000001017712325527072015530 0ustar ripleyusers/* ========================================================================= */ /* === AMD_preprocess ====================================================== */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* Sorts, removes duplicate entries, and transposes from the nonzero pattern of * a column-form matrix A, to obtain the matrix R. The input matrix can have * duplicate entries and/or unsorted columns (AMD_valid (n,Ap,Ai) must not be * AMD_INVALID). * * This input condition is NOT checked. This routine is not user-callable. */ #include "amd_internal.h" /* ========================================================================= */ /* === AMD_preprocess ====================================================== */ /* ========================================================================= */ /* AMD_preprocess does not check its input for errors or allocate workspace. * On input, the condition (AMD_valid (n,n,Ap,Ai) != AMD_INVALID) must hold. */ GLOBAL void AMD_preprocess ( Int n, /* input matrix: A is n-by-n */ const Int Ap [ ], /* size n+1 */ const Int Ai [ ], /* size nz = Ap [n] */ /* output matrix R: */ Int Rp [ ], /* size n+1 */ Int Ri [ ], /* size nz (or less, if duplicates present) */ Int W [ ], /* workspace of size n */ Int Flag [ ] /* workspace of size n */ ) { /* --------------------------------------------------------------------- */ /* local variables */ /* --------------------------------------------------------------------- */ Int i, j, p, p2 ; ASSERT (AMD_valid (n, n, Ap, Ai) != AMD_INVALID) ; /* --------------------------------------------------------------------- */ /* count the entries in each row of A (excluding duplicates) */ /* --------------------------------------------------------------------- */ for (i = 0 ; i < n ; i++) { W [i] = 0 ; /* # of nonzeros in row i (excl duplicates) */ Flag [i] = EMPTY ; /* Flag [i] = j if i appears in column j */ } for (j = 0 ; j < n ; j++) { p2 = Ap [j+1] ; for (p = Ap [j] ; p < p2 ; p++) { i = Ai [p] ; if (Flag [i] != j) { /* row index i has not yet appeared in column j */ W [i]++ ; /* one more entry in row i */ Flag [i] = j ; /* flag row index i as appearing in col j*/ } } } /* --------------------------------------------------------------------- */ /* compute the row pointers for R */ /* --------------------------------------------------------------------- */ Rp [0] = 0 ; for (i = 0 ; i < n ; i++) { Rp [i+1] = Rp [i] + W [i] ; } for (i = 0 ; i < n ; i++) { W [i] = Rp [i] ; Flag [i] = EMPTY ; } /* --------------------------------------------------------------------- */ /* construct the row form matrix R */ /* --------------------------------------------------------------------- */ /* R = row form of pattern of A */ for (j = 0 ; j < n ; j++) { p2 = Ap [j+1] ; for (p = Ap [j] ; p < p2 ; p++) { i = Ai [p] ; if (Flag [i] != j) { /* row index i has not yet appeared in column j */ Ri [W [i]++] = j ; /* put col j in row i */ Flag [i] = j ; /* flag row index i as appearing in col j*/ } } } #ifndef NDEBUG ASSERT (AMD_valid (n, n, Rp, Ri) == AMD_OK) ; for (j = 0 ; j < n ; j++) { ASSERT (W [j] == Rp [j+1]) ; } #endif } igraph/src/igraph_epidemics.h0000644000176000001440000000440512325527073016021 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2014 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_EPIDEMICS_H #define IGRAPH_EPIDEMICS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_datatype.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /** * \struct igraph_sir_t * * Data structure to store the results of one simulation * of the SIR (susceptible-infected-recovered) model on a graph. * * It has the following members. They are all (real or integer) * vectors, and they are of the same length. * * \member times A vector, the times of the events are stored here. * \member no_s An integer vector, the number of susceptibles in * each time step is stored here. * \member no_i An integer vector, the number of infected individuals * at each time step, is stored here. * \member no_r An integer vector, the number of recovered individuals * is stored here at each time step. */ typedef struct igraph_sir_t { igraph_vector_t times; igraph_vector_int_t no_s, no_i, no_r; } igraph_sir_t; int igraph_sir_init(igraph_sir_t *sir); void igraph_sir_destroy(igraph_sir_t *sir); int igraph_sir(const igraph_t *graph, igraph_real_t beta, igraph_real_t gamma, igraph_integer_t no_sim, igraph_vector_ptr_t *result); __END_DECLS #endif igraph/src/igraph_strvector.c0000644000176000001440000003611712325527073016112 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_strvector.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include /** * \section igraph_strvector_t * The igraph_strvector_t type is a vector of strings. * The current implementation is very simple and not too efficient. It * works fine for not too many strings, e.g. the list of attribute * names is returned in a string vector by \ref * igraph_cattribute_list(). Do not expect great performance from this * type. * * * \example examples/simple/igraph_strvector.c * */ /** * \ingroup strvector * \function igraph_strvector_init * \brief Initialize * * Reserves memory for the string vector, a string vector must be * first initialized before calling other functions on it. * All elements of the string vector are set to the empty string. * \param sv Pointer to an initialized string vector. * \param len The (initial) length of the string vector. * \return Error code. * * Time complexity: O(\p len). */ int igraph_strvector_init(igraph_strvector_t *sv, long int len) { long int i; sv->data=igraph_Calloc(len, char*); if (sv->data==0) { IGRAPH_ERROR("strvector init failed", IGRAPH_ENOMEM); } for (i=0; idata[i]=igraph_Calloc(1, char); if (sv->data[i]==0) { igraph_strvector_destroy(sv); IGRAPH_ERROR("strvector init failed", IGRAPH_ENOMEM); } sv->data[i][0]='\0'; } sv->len=len; return 0; } /** * \ingroup strvector * \function igraph_strvector_destroy * \brief Free allocated memory * * Destroy a string vector. It may be reinitialized with \ref * igraph_strvector_init() later. * \param sv The string vector. * * Time complexity: O(l), the total length of the strings, maybe less * depending on the memory manager. */ void igraph_strvector_destroy(igraph_strvector_t *sv) { long int i; assert(sv != 0); if (sv->data != 0) { for (i=0; ilen; i++) { if (sv->data[i] != 0) { igraph_Free(sv->data[i]); } } igraph_Free(sv->data); } } /** * \ingroup strvector * \function igraph_strvector_get * \brief Indexing * * Query an element of a string vector. See also the \ref STR macro * for an easier way. * \param sv The input string vector. * \param idx The index of the element to query. * \param Pointer to a char*, the address of the string * is stored here. * * Time complexity: O(1). */ void igraph_strvector_get(const igraph_strvector_t *sv, long int idx, char **value) { assert(sv != 0); assert(sv->data != 0); assert(sv->data[idx] != 0); *value = sv->data[idx]; } /** * \ingroup strvector * \function igraph_strvector_set * \brief Set an element * * The provided \p value is copied into the \p idx position in the * string vector. * \param sv The string vector. * \param idx The position to set. * \param value The new value. * \return Error code. * * Time complexity: O(l), the length of the new string. Maybe more, * depending on the memory management, if reallocation is needed. */ int igraph_strvector_set(igraph_strvector_t *sv, long int idx, const char *value) { assert(sv != 0); assert(sv->data != 0); if (sv->data[idx] == 0) { sv->data[idx] = igraph_Calloc(strlen(value)+1, char); if (sv->data[idx]==0) { IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); } } else { char *tmp=igraph_Realloc(sv->data[idx], strlen(value)+1, char); if (tmp==0) { IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); } sv->data[idx]=tmp; } strcpy(sv->data[idx], value); return 0; } /** * \ingroup strvector * \function igraph_strvector_set2 * \brief Sets an element * * This is almost the same as \ref igraph_strvector_set, but the new * value is not a zero terminated string, but its length is given. * \param sv The string vector. * \param idx The position to set. * \param value The new value. * \param len The length of the new value. * \return Error code. * * Time complexity: O(l), the length of the new string. Maybe more, * depending on the memory management, if reallocation is needed. */ int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, const char *value, int len) { assert(sv != 0); assert(sv->data != 0); if (sv->data[idx] == 0) { sv->data[idx] = igraph_Calloc(len+1, char); if (sv->data[idx]==0) { IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); } } else { char *tmp=igraph_Realloc(sv->data[idx], (size_t) len+1, char); if (tmp==0) { IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); } sv->data[idx]=tmp; } memcpy(sv->data[idx], value, (size_t) len*sizeof(char)); sv->data[idx][len]='\0'; return 0; } /** * \ingroup strvector * \function igraph_strvector_remove_section * \brief Removes a section from a string vector. * \todo repair realloc */ void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, long int to) { long int i; /* char **tmp; */ assert(v != 0); assert(v->data != 0); for (i=from; idata[i] != 0) { igraph_Free(v->data[i]); } } for (i=0; ilen-to; i++) { v->data[from+i]=v->data[to+i]; } v->len -= (to-from); /* try to make it smaller */ /* tmp=igraph_Realloc(v->data, v->len, char*); */ /* if (tmp!=0) { */ /* v->data=tmp; */ /* } */ } /** * \ingroup strvector * \function igraph_strvector_remove * \brief Removes a single element from a string vector. * * The string will be one shorter. * \param The string vector. * \param elem The index of the element to remove. * * Time complexity: O(n), the length of the string. */ void igraph_strvector_remove(igraph_strvector_t *v, long int elem) { assert(v != 0); assert(v->data != 0); igraph_strvector_remove_section(v, elem, elem+1); } /** * \ingroup strvector * \function igraph_strvector_move_interval * \brief Copies an interval of a string vector. */ void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, long int end, long int to) { long int i; assert(v != 0); assert(v->data != 0); for (i=to; idata[i] != 0) { igraph_Free(v->data[i]); } } for (i=0; idata[begin+i] != 0) { size_t len=strlen(v->data[begin+i])+1; v->data[to+i]=igraph_Calloc(len, char); memcpy(v->data[to+i], v->data[begin+i], sizeof(char)*len); } } } /** * \ingroup strvector * \function igraph_strvector_copy * \brief Initialization by copying. * * Initializes a string vector by copying another string vector. * \param to Pointer to an uninitialized string vector. * \param from The other string vector, to be copied. * \return Error code. * * Time complexity: O(l), the total length of the strings in \p from. */ int igraph_strvector_copy(igraph_strvector_t *to, const igraph_strvector_t *from) { long int i; char *str; assert(from != 0); /* assert(from->data != 0); */ to->data=igraph_Calloc(from->len, char*); if (to->data==0) { IGRAPH_ERROR("Cannot copy string vector", IGRAPH_ENOMEM); } to->len=from->len; for (i=0; ilen; i++) { int ret; igraph_strvector_get(from, i, &str); ret=igraph_strvector_set(to, i, str); if (ret != 0) { igraph_strvector_destroy(to); IGRAPH_ERROR("cannot copy string vector", ret); } } return 0; } /** * \function igraph_strvector_append * Concatenate two string vectors. * * \param to The first string vector, the result is stored here. * \param from The second string vector, it is kept unchanged. * \return Error code. * * Time complexity: O(n+l2), n is the number of strings in the new * string vector, l2 is the total length of strings in the \p from * string vector. */ int igraph_strvector_append(igraph_strvector_t *to, const igraph_strvector_t *from) { long int len1=igraph_strvector_size(to), len2=igraph_strvector_size(from); long int i; igraph_bool_t error=0; IGRAPH_CHECK(igraph_strvector_resize(to, len1+len2)); for (i=0; idata[i][0] != '\0') { igraph_Free(to->data[len1+i]); to->data[len1+i] = strdup(from->data[i]); if (!to->data[len1+i]) { error=1; break; } } } if (error) { igraph_strvector_resize(to, len1); IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM); } return 0; } /** * \function igraph_strvector_clear * Remove all elements * * After this operation the string vector will be empty. * \param sv The string vector. * * Time complexity: O(l), the total length of strings, maybe less, * depending on the memory manager. */ void igraph_strvector_clear(igraph_strvector_t *sv) { long int i, n=igraph_strvector_size(sv); char **tmp; for (i=0; idata[i]); } sv->len=0; /* try to give back some memory */ tmp=igraph_Realloc(sv->data, 1, char*); if (tmp != 0) { sv->data=tmp; } } /** * \ingroup strvector * \function igraph_strvector_resize * \brief Resize * * If the new size is bigger then empty strings are added, if it is * smaller then the unneeded elements are removed. * \param v The string vector. * \param newsize The new size. * \return Error code. * * Time complexity: O(n), the number of strings if the vector is made * bigger, O(l), the total length of the deleted strings if it is made * smaller, maybe less, depending on memory management. */ int igraph_strvector_resize(igraph_strvector_t* v, long int newsize) { long int toadd=newsize-v->len, i, j; char **tmp; long int reallocsize=newsize; if (reallocsize==0) { reallocsize=1; } assert(v != 0); assert(v->data != 0); /* printf("resize %li to %li\n", v->len, newsize); */ if (newsize < v->len) { for (i=newsize; ilen; i++) { igraph_Free(v->data[i]); } /* try to give back some space */ tmp=igraph_Realloc(v->data, (size_t) reallocsize, char*); /* printf("resize %li to %li, %p\n", v->len, newsize, tmp); */ if (tmp != 0) { v->data=tmp; } } else if (newsize > v->len) { igraph_bool_t error=0; tmp=igraph_Realloc(v->data, (size_t) reallocsize, char*); if (tmp==0) { IGRAPH_ERROR("cannot resize string vector", IGRAPH_ENOMEM); } v->data = tmp; for (i=0; idata[v->len+i] = igraph_Calloc(1, char); if (v->data[v->len+i] == 0) { error=1; break; } v->data[v->len+i][0]='\0'; } if (error) { /* There was an error, free everything we've allocated so far */ for (j=0; jdata[v->len+i] != 0) { igraph_Free(v->data[v->len+i]); } } /* Try to give back space */ tmp=igraph_Realloc(v->data, (size_t) (v->len), char*); if (tmp != 0) { v->data=tmp; } IGRAPH_ERROR("Cannot resize string vector", IGRAPH_ENOMEM); } } v->len = newsize; return 0; } /** * \ingroup strvector * \function igraph_strvector_size * \brief Gives the size of a string vector. * * \param sv The string vector. * \return The length of the string vector. * * Time complexity: O(1). */ long int igraph_strvector_size(const igraph_strvector_t *sv) { assert(sv != 0); assert(sv->data != 0); return sv->len; } /** * \ingroup strvector * \function igraph_strvector_add * \brief Adds an element to the back of a string vector. * * \param v The string vector. * \param value The string to add, it will be copied. * \return Error code. * * Time complexity: O(n+l), n is the total number of strings, l is the * length of the new string. */ int igraph_strvector_add(igraph_strvector_t *v, const char *value) { long int s=igraph_strvector_size(v); char **tmp; assert(v != 0); assert(v->data != 0); tmp=igraph_Realloc(v->data, (size_t) s+1, char*); if (tmp == 0) { IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM); } v->data=tmp; v->data[s]=igraph_Calloc(strlen(value)+1, char); if (v->data[s]==0) { IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM); } strcpy(v->data[s], value); v->len += 1; return 0; } /** * \ingroup strvector * \function igraph_strvector_permdelete * \brief Removes elements from a string vector (for internal use) */ void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, long int nremove) { long int i; char **tmp; assert(v != 0); assert(v->data != 0); for (i=0; idata[ (long int) VECTOR(*index)[i]-1 ] = v->data[i]; } else { igraph_Free(v->data[i]); } } /* Try to make it shorter */ tmp=igraph_Realloc(v->data, v->len-nremove ? (size_t) (v->len-nremove) : 1, char*); if (tmp != 0) { v->data=tmp; } v->len -= nremove; } /** * \ingroup strvector * \function igraph_strvector_remove_negidx * \brief Removes elements from a string vector (for internal use) */ void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg, long int nremove) { long int i, idx=0; char **tmp; assert(v != 0); assert(v->data != 0); for (i=0; i= 0) { v->data[idx++] = v->data[i]; } else { igraph_Free(v->data[i]); } } /* Try to give back some memory */ tmp=igraph_Realloc(v->data, v->len-nremove ? (size_t) (v->len-nremove) : 1, char*); if (tmp != 0) { v->data=tmp; } v->len -= nremove; } int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, const char *sep) { long int i, n=igraph_strvector_size(v); if (n!=0) { fprintf(file, "%s", STR(*v, 0)); } for (i=1; in ; Ap = A->p ; Ai = A->i ; Ax = A->x ; for (j = 0 ; j < n ; j++) { p = Ap [j] ; /* get current location of col j */ Ap [j] = nz ; /* record new location of col j */ for ( ; p < Ap [j+1] ; p++) { if (fkeep (Ai [p], j, Ax ? Ax [p] : 1, other)) { if (Ax) Ax [nz] = Ax [p] ; /* keep A(i,j) */ Ai [nz++] = Ai [p] ; } } } Ap [n] = nz ; /* finalize A */ cs_sprealloc (A, 0) ; /* remove extra space from A */ return (nz) ; } igraph/src/amd_postorder.c0000644000176000001440000001543112325527072015362 0ustar ripleyusers/* ========================================================================= */ /* === AMD_postorder ======================================================= */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* Perform a postordering (via depth-first search) of an assembly tree. */ #include "amd_internal.h" GLOBAL void AMD_postorder ( /* inputs, not modified on output: */ Int nn, /* nodes are in the range 0..nn-1 */ Int Parent [ ], /* Parent [j] is the parent of j, or EMPTY if root */ Int Nv [ ], /* Nv [j] > 0 number of pivots represented by node j, * or zero if j is not a node. */ Int Fsize [ ], /* Fsize [j]: size of node j */ /* output, not defined on input: */ Int Order [ ], /* output post-order */ /* workspaces of size nn: */ Int Child [ ], Int Sibling [ ], Int Stack [ ] ) { Int i, j, k, parent, frsize, f, fprev, maxfrsize, bigfprev, bigf, fnext ; for (j = 0 ; j < nn ; j++) { Child [j] = EMPTY ; Sibling [j] = EMPTY ; } /* --------------------------------------------------------------------- */ /* place the children in link lists - bigger elements tend to be last */ /* --------------------------------------------------------------------- */ for (j = nn-1 ; j >= 0 ; j--) { if (Nv [j] > 0) { /* this is an element */ parent = Parent [j] ; if (parent != EMPTY) { /* place the element in link list of the children its parent */ /* bigger elements will tend to be at the end of the list */ Sibling [j] = Child [parent] ; Child [parent] = j ; } } } #ifndef NDEBUG { Int nels, ff, nchild ; AMD_DEBUG1 (("\n\n================================ AMD_postorder:\n")); nels = 0 ; for (j = 0 ; j < nn ; j++) { if (Nv [j] > 0) { AMD_DEBUG1 (( ""ID" : nels "ID" npiv "ID" size "ID " parent "ID" maxfr "ID"\n", j, nels, Nv [j], Fsize [j], Parent [j], Fsize [j])) ; /* this is an element */ /* dump the link list of children */ nchild = 0 ; AMD_DEBUG1 ((" Children: ")) ; for (ff = Child [j] ; ff != EMPTY ; ff = Sibling [ff]) { AMD_DEBUG1 ((ID" ", ff)) ; ASSERT (Parent [ff] == j) ; nchild++ ; ASSERT (nchild < nn) ; } AMD_DEBUG1 (("\n")) ; parent = Parent [j] ; if (parent != EMPTY) { ASSERT (Nv [parent] > 0) ; } nels++ ; } } } AMD_DEBUG1 (("\n\nGo through the children of each node, and put\n" "the biggest child last in each list:\n")) ; #endif /* --------------------------------------------------------------------- */ /* place the largest child last in the list of children for each node */ /* --------------------------------------------------------------------- */ for (i = 0 ; i < nn ; i++) { if (Nv [i] > 0 && Child [i] != EMPTY) { #ifndef NDEBUG Int nchild ; AMD_DEBUG1 (("Before partial sort, element "ID"\n", i)) ; nchild = 0 ; for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) { ASSERT (f >= 0 && f < nn) ; AMD_DEBUG1 ((" f: "ID" size: "ID"\n", f, Fsize [f])) ; nchild++ ; ASSERT (nchild <= nn) ; } #endif /* find the biggest element in the child list */ fprev = EMPTY ; maxfrsize = EMPTY ; bigfprev = EMPTY ; bigf = EMPTY ; for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) { ASSERT (f >= 0 && f < nn) ; frsize = Fsize [f] ; if (frsize >= maxfrsize) { /* this is the biggest seen so far */ maxfrsize = frsize ; bigfprev = fprev ; bigf = f ; } fprev = f ; } ASSERT (bigf != EMPTY) ; fnext = Sibling [bigf] ; AMD_DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID " fprev " ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ; if (fnext != EMPTY) { /* if fnext is EMPTY then bigf is already at the end of list */ if (bigfprev == EMPTY) { /* delete bigf from the element of the list */ Child [i] = fnext ; } else { /* delete bigf from the middle of the list */ Sibling [bigfprev] = fnext ; } /* put bigf at the end of the list */ Sibling [bigf] = EMPTY ; ASSERT (Child [i] != EMPTY) ; ASSERT (fprev != bigf) ; ASSERT (fprev != EMPTY) ; Sibling [fprev] = bigf ; } #ifndef NDEBUG AMD_DEBUG1 (("After partial sort, element "ID"\n", i)) ; for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) { ASSERT (f >= 0 && f < nn) ; AMD_DEBUG1 ((" "ID" "ID"\n", f, Fsize [f])) ; ASSERT (Nv [f] > 0) ; nchild-- ; } ASSERT (nchild == 0) ; #endif } } /* --------------------------------------------------------------------- */ /* postorder the assembly tree */ /* --------------------------------------------------------------------- */ for (i = 0 ; i < nn ; i++) { Order [i] = EMPTY ; } k = 0 ; for (i = 0 ; i < nn ; i++) { if (Parent [i] == EMPTY && Nv [i] > 0) { AMD_DEBUG1 (("Root of assembly tree "ID"\n", i)) ; k = AMD_post_tree (i, k, Child, Sibling, Order, Stack #ifndef NDEBUG , nn #endif ) ; } } } igraph/src/complex.c0000644000176000001440000002555512325527072014177 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_complex.h" #include "igraph_math.h" #include /** * \example igraph_complex.c */ igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y) { igraph_complex_t res; IGRAPH_REAL(res) = x; IGRAPH_IMAG(res) = y; return res; } igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta) { igraph_complex_t res; IGRAPH_REAL(res) = r * cos(theta); IGRAPH_IMAG(res) = r * sin(theta); return res; } igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, igraph_complex_t z2, igraph_real_t tol) { if (fabs(IGRAPH_REAL(z1) - IGRAPH_REAL(z2)) > tol || fabs(IGRAPH_IMAG(z1) - IGRAPH_IMAG(z2)) > tol) { return 0; } return 1; } igraph_real_t igraph_complex_mod(igraph_complex_t z) { igraph_real_t x=IGRAPH_REAL(z); igraph_real_t y=IGRAPH_IMAG(z); return hypot(x,y); } igraph_real_t igraph_complex_arg(igraph_complex_t z) { igraph_real_t x=IGRAPH_REAL(z); igraph_real_t y=IGRAPH_IMAG(z); if (x==0.0 && y==0.0) { return 0.0; } return atan2(y,x); } igraph_complex_t igraph_complex_add(igraph_complex_t z1, igraph_complex_t z2) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z1) + IGRAPH_REAL(z2); IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) + IGRAPH_IMAG(z2); return res; } igraph_complex_t igraph_complex_sub(igraph_complex_t z1, igraph_complex_t z2) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z1) - IGRAPH_REAL(z2); IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) - IGRAPH_IMAG(z2); return res; } igraph_complex_t igraph_complex_mul(igraph_complex_t z1, igraph_complex_t z2) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z1) * IGRAPH_REAL(z2) - IGRAPH_IMAG(z1) * IGRAPH_IMAG(z2); IGRAPH_IMAG(res) = IGRAPH_REAL(z1) * IGRAPH_IMAG(z2) + IGRAPH_IMAG(z1) * IGRAPH_REAL(z2); return res; } igraph_complex_t igraph_complex_div(igraph_complex_t z1, igraph_complex_t z2) { igraph_complex_t res; igraph_real_t z1r = IGRAPH_REAL(z1), z1i = IGRAPH_IMAG(z1); igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2); igraph_real_t s = 1.0 / igraph_complex_abs(z2); igraph_real_t sz2r = s * z2r; igraph_real_t sz2i = s * z2i; IGRAPH_REAL(res) = (z1r * sz2r + z1i * sz2i) * s; IGRAPH_IMAG(res) = (z1i * sz2r - z1r * sz2i) * s; return res; } igraph_complex_t igraph_complex_add_real(igraph_complex_t z, igraph_real_t x) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z) + x; IGRAPH_IMAG(res) = IGRAPH_IMAG(z); return res; } igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, igraph_real_t y) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z); IGRAPH_IMAG(res) = IGRAPH_IMAG(z) + y; return res; } igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, igraph_real_t x) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z) - x; IGRAPH_IMAG(res) = IGRAPH_IMAG(z); return res; } igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, igraph_real_t y) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z); IGRAPH_IMAG(res) = IGRAPH_IMAG(z) - y; return res; } igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, igraph_real_t x) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z) * x; IGRAPH_IMAG(res) = IGRAPH_IMAG(z) * x; return res; } igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, igraph_real_t y) { igraph_complex_t res; IGRAPH_REAL(res) = - IGRAPH_IMAG(z) * y; IGRAPH_IMAG(res) = IGRAPH_REAL(z) * y; return res; } igraph_complex_t igraph_complex_div_real(igraph_complex_t z, igraph_real_t x) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z) / x; IGRAPH_IMAG(res) = IGRAPH_IMAG(z) / x; return res; } igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, igraph_real_t y) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_IMAG(z) / y; IGRAPH_IMAG(res) = - IGRAPH_REAL(z) / y; return res; } igraph_complex_t igraph_complex_conj(igraph_complex_t z) { igraph_complex_t res; IGRAPH_REAL(res) = IGRAPH_REAL(z); IGRAPH_IMAG(res) = - IGRAPH_IMAG(z); return res; } igraph_complex_t igraph_complex_neg(igraph_complex_t z) { igraph_complex_t res; IGRAPH_REAL(res) = - IGRAPH_REAL(z); IGRAPH_IMAG(res) = - IGRAPH_IMAG(z); return res; } igraph_complex_t igraph_complex_inv(igraph_complex_t z) { igraph_complex_t res; igraph_real_t s = 1.0 / igraph_complex_abs(z); IGRAPH_REAL(res) = (IGRAPH_REAL(z) * s) * s; IGRAPH_IMAG(res) = - (IGRAPH_IMAG(z) * s) * s; return res; } igraph_real_t igraph_complex_abs(igraph_complex_t z) { return hypot(IGRAPH_REAL(z), IGRAPH_IMAG(z)); } igraph_real_t igraph_complex_logabs(igraph_complex_t z) { igraph_real_t xabs = fabs(IGRAPH_REAL(z)); igraph_real_t yabs = fabs(IGRAPH_IMAG(z)); igraph_real_t max, u; if (xabs >= yabs) { max = xabs; u = yabs / xabs; } else { max = yabs; u = xabs / yabs; } return log (max) + 0.5 * log1p (u * u); } igraph_complex_t igraph_complex_sqrt(igraph_complex_t z) { igraph_complex_t res; if (IGRAPH_REAL(z)==0.0 && IGRAPH_IMAG(z) == 0.0) { IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0; } else { igraph_real_t x = fabs (IGRAPH_REAL(z)); igraph_real_t y = fabs (IGRAPH_IMAG(z)); igraph_real_t w; if (x >= y) { igraph_real_t t = y / x; w = sqrt (x) * sqrt (0.5 * (1.0 + sqrt (1.0 + t * t))); } else { igraph_real_t t = x / y; w = sqrt (y) * sqrt (0.5 * (t + sqrt (1.0 + t * t))); } if (IGRAPH_REAL(z) >= 0.0) { igraph_real_t ai = IGRAPH_IMAG(z); IGRAPH_REAL(res) = w; IGRAPH_IMAG(res) = ai / (2.0 * w); } else { igraph_real_t ai = IGRAPH_IMAG(z); igraph_real_t vi = (ai >= 0) ? w : -w; IGRAPH_REAL(res) = ai / (2.0 * vi); IGRAPH_IMAG(res) = vi; } } return res; } igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x) { igraph_complex_t res; if (x >= 0) { IGRAPH_REAL(res) = sqrt(x); IGRAPH_IMAG(res) = 0.0; } else { IGRAPH_REAL(res) = 0.0; IGRAPH_IMAG(res) = sqrt(-x); } return res; } igraph_complex_t igraph_complex_exp(igraph_complex_t z) { igraph_real_t rho = exp(IGRAPH_REAL(z)); igraph_real_t theta = IGRAPH_IMAG(z); igraph_complex_t res; IGRAPH_REAL(res) = rho * cos(theta); IGRAPH_IMAG(res) = rho * sin(theta); return res; } igraph_complex_t igraph_complex_pow(igraph_complex_t z1, igraph_complex_t z2) { igraph_complex_t res; if (IGRAPH_REAL(z1) == 0 && IGRAPH_IMAG(z1) == 0.0) { if (IGRAPH_REAL(z2) == 0 && IGRAPH_IMAG(z2) == 0.0) { IGRAPH_REAL(res) = 1.0; IGRAPH_IMAG(res) = 0.0; } else { IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0; } } else if (IGRAPH_REAL(z2) == 1.0 && IGRAPH_IMAG(z2) == 0.0) { IGRAPH_REAL(res) = IGRAPH_REAL(z1); IGRAPH_IMAG(res) = IGRAPH_IMAG(z1); } else if (IGRAPH_REAL(z2) == -1.0 && IGRAPH_IMAG(z2) == 0.0) { res = igraph_complex_inv(z1); } else { igraph_real_t logr = igraph_complex_logabs (z1); igraph_real_t theta = igraph_complex_arg (z1); igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2); igraph_real_t rho = exp (logr * z2r - z2i * theta); igraph_real_t beta = theta * z2r + z2i * logr; IGRAPH_REAL(res) = rho * cos(beta); IGRAPH_IMAG(res) = rho * sin(beta); } return res; } igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, igraph_real_t x) { igraph_complex_t res; if (IGRAPH_REAL(z) == 0.0 && IGRAPH_IMAG(z) == 0.0) { if (x==0) { IGRAPH_REAL(res) = 1.0; IGRAPH_IMAG(res) = 0.0; } else { IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0; } } else { igraph_real_t logr = igraph_complex_logabs(z); igraph_real_t theta = igraph_complex_arg(z); igraph_real_t rho = exp (logr * x); igraph_real_t beta = theta * x; IGRAPH_REAL(res) = rho * cos(beta); IGRAPH_IMAG(res) = rho * sin(beta); } return res; } igraph_complex_t igraph_complex_log(igraph_complex_t z) { igraph_complex_t res; IGRAPH_REAL(res) = igraph_complex_logabs(z); IGRAPH_IMAG(res) = igraph_complex_arg(z); return res; } igraph_complex_t igraph_complex_log10(igraph_complex_t z) { return igraph_complex_mul_real(igraph_complex_log(z), 1/log(10.0)); } igraph_complex_t igraph_complex_log_b(igraph_complex_t z, igraph_complex_t b) { return igraph_complex_div (igraph_complex_log(z), igraph_complex_log(b)); } igraph_complex_t igraph_complex_sin(igraph_complex_t z) { igraph_real_t zr = IGRAPH_REAL(z); igraph_real_t zi = IGRAPH_IMAG(z); igraph_complex_t res; if (zi == 0.0) { IGRAPH_REAL(res) = sin(zr); IGRAPH_IMAG(res) = 0.0; } else { IGRAPH_REAL(res) = sin(zr) * cosh(zi); IGRAPH_IMAG(res) = cos(zr) * sinh(zi); } return res; } igraph_complex_t igraph_complex_cos(igraph_complex_t z) { igraph_real_t zr=IGRAPH_REAL(z); igraph_real_t zi=IGRAPH_IMAG(z); igraph_complex_t res; if (zi == 0.0) { IGRAPH_REAL(res) = cos(zr); IGRAPH_IMAG(res) = 0.0; } else { IGRAPH_REAL(res) = cos(zr) * cosh(zi); IGRAPH_IMAG(res) = sin(zr) * sinh(-zi); } return res; } igraph_complex_t igraph_complex_tan(igraph_complex_t z) { igraph_real_t zr=IGRAPH_REAL(z); igraph_real_t zi=IGRAPH_IMAG(z); igraph_complex_t res; if (fabs (zi) < 1) { igraph_real_t D = pow (cos (zr), 2.0) + pow (sinh (zi), 2.0); IGRAPH_REAL(res) = 0.5 * sin (2 * zr) / D; IGRAPH_IMAG(res) = 0.5 * sinh (2 * zi) / D; } else { igraph_real_t u = exp (-zi); igraph_real_t C = 2 * u / (1 - pow (u, 2.0)); igraph_real_t D = 1 + pow (cos (zr), 2.0) * pow (C, 2.0); igraph_real_t S = pow (C, 2.0); igraph_real_t T = 1.0 / tanh (zi); IGRAPH_REAL(res) = 0.5 * sin (2 * zr) * S / D; IGRAPH_IMAG(res) = T / D; } return res; } igraph_complex_t igraph_complex_sec(igraph_complex_t z) { return igraph_complex_inv(igraph_complex_cos(z)); } igraph_complex_t igraph_complex_csc(igraph_complex_t z) { return igraph_complex_inv(igraph_complex_sin(z)); } igraph_complex_t igraph_complex_cot(igraph_complex_t z) { return igraph_complex_inv(igraph_complex_tan(z)); } igraph/src/dqueue.c0000644000176000001440000000256612325527073014016 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_dqueue.h" #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "dqueue.pmt" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "dqueue.pmt" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "dqueue.pmt" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "dqueue.pmt" #include "igraph_pmt_off.h" #undef BASE_BOOL igraph/src/igraph_heap.c0000644000176000001440000000333212325527073014765 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_heap.h" #define BASE_IGRAPH_REAL #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MAX #define HEAP_TYPE_MIN #include "igraph_pmt.h" #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN #undef BASE_IGRAPH_REAL #define BASE_LONG #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MAX #define HEAP_TYPE_MIN #include "igraph_pmt.h" #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN #undef BASE_LONG #define BASE_CHAR #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MAX #define HEAP_TYPE_MIN #include "igraph_pmt.h" #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN #undef BASE_CHAR igraph/src/glpenv01.c0000644000176000001440000001635512325527073014163 0ustar ripleyusers/* glpenv01.c (environment initialization/termination) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wint-conversion" #endif #include "glpapi.h" #include "igraph_error.h" /*********************************************************************** * NAME * * glp_init_env - initialize GLPK environment * * SYNOPSIS * * int glp_init_env(void); * * DESCRIPTION * * The routine glp_init_env initializes the GLPK environment. Normally * the application program does not need to call this routine, because * it is called automatically on the first call to any API routine. * * RETURNS * * The routine glp_init_env returns one of the following codes: * * 0 - initialization successful; * 1 - environment has been already initialized; * 2 - initialization failed (insufficient memory); * 3 - initialization failed (unsupported programming model). */ int glp_init_env(void) { ENV *env; int ok; /* check if the programming model is supported */ ok = (CHAR_BIT == 8 && sizeof(char) == 1 && sizeof(short) == 2 && sizeof(int) == 4 && (sizeof(void *) == 4 || sizeof(void *) == 8)); if (!ok) return 3; /* check if the environment is already initialized */ if (tls_get_ptr() != NULL) return 1; /* allocate and initialize the environment block */ env = malloc(sizeof(ENV)); if (env == NULL) return 2; env->magic = ENV_MAGIC; sprintf(env->version, "%d.%d", GLP_MAJOR_VERSION, GLP_MINOR_VERSION); env->term_buf = malloc(TERM_BUF_SIZE); if (env->term_buf == NULL) { free(env); return 2; } env->term_out = GLP_ON; env->term_hook = NULL; env->term_info = NULL; env->tee_file = NULL; env->err_file = ""; env->err_line = 0; env->err_hook = NULL; env->err_info = NULL; env->mem_limit.hi = 0x7FFFFFFF, env->mem_limit.lo = 0xFFFFFFFF; env->mem_ptr = NULL; env->mem_count = env->mem_cpeak = 0; env->mem_total = env->mem_tpeak = xlset(0); env->file_ptr = NULL; env->ioerr_msg = malloc(IOERR_MSG_SIZE); if (env->ioerr_msg == NULL) { free(env->term_buf); free(env); return 2; } strcpy(env->ioerr_msg, "No error"); env->h_odbc = env->h_mysql = NULL; /* save pointer to the environment block */ tls_set_ptr(env); /* initialization successful */ return 0; } /*********************************************************************** * NAME * * get_env_ptr - retrieve pointer to environment block * * SYNOPSIS * * #include "glpenv.h" * ENV *get_env_ptr(void); * * DESCRIPTION * * The routine get_env_ptr retrieves and returns a pointer to the GLPK * environment block. * * If the GLPK environment has not been initialized yet, the routine * performs initialization. If initialization fails, the routine prints * an error message to stderr and terminates the program. * * RETURNS * * The routine returns a pointer to the environment block. */ ENV *get_env_ptr(void) { ENV *env = tls_get_ptr(); /* check if the environment has been initialized */ if (env == NULL) { /* not initialized yet; perform initialization */ if (glp_init_env() != 0) { /* initialization failed; display an error message */ IGRAPH_ERROR("GLPK initialization failed", IGRAPH_EGLP); } /* initialization successful; retrieve the pointer */ env = tls_get_ptr(); } /* check if the environment block is valid */ if (env->magic != ENV_MAGIC) { IGRAPH_ERROR("Invalid GLPK environment", IGRAPH_EGLP); } return env; } /*********************************************************************** * NAME * * glp_version - determine library version * * SYNOPSIS * * const char *glp_version(void); * * RETURNS * * The routine glp_version returns a pointer to a null-terminated * character string, which specifies the version of the GLPK library in * the form "X.Y", where X is the major version number, and Y is the * minor version number, for example, "4.16". */ const char *glp_version(void) { ENV *env = get_env_ptr(); return env->version; } /*********************************************************************** * NAME * * glp_free_env - free GLPK environment * * SYNOPSIS * * int glp_free_env(void); * * DESCRIPTION * * The routine glp_free_env frees all resources used by GLPK routines * (memory blocks, etc.) which are currently still in use. * * Normally the application program does not need to call this routine, * because GLPK routines always free all unused resources. However, if * the application program even has deleted all problem objects, there * will be several memory blocks still allocated for the library needs. * For some reasons the application program may want GLPK to free this * memory, in which case it should call glp_free_env. * * Note that a call to glp_free_env invalidates all problem objects as * if no GLPK routine were called. * * RETURNS * * 0 - termination successful; * 1 - environment is inactive (was not initialized). */ int glp_free_env(void) { ENV *env = tls_get_ptr(); MEM *desc; /* check if the environment is active */ if (env == NULL) return 1; /* check if the environment block is valid */ if (env->magic != ENV_MAGIC) { IGRAPH_ERROR("Invalid GLPK environment", IGRAPH_EGLP); } /* close handles to shared libraries */ if (env->h_odbc != NULL) xdlclose(env->h_odbc); if (env->h_mysql != NULL) xdlclose(env->h_mysql); /* close streams which are still open */ while (env->file_ptr != NULL) xfclose(env->file_ptr); /* free memory blocks which are still allocated */ while (env->mem_ptr != NULL) { desc = env->mem_ptr; env->mem_ptr = desc->next; free(desc); } /* invalidate the environment block */ env->magic = -1; /* free memory allocated to the environment block */ free(env->term_buf); free(env->ioerr_msg); free(env); /* reset a pointer to the environment block */ tls_set_ptr(NULL); /* termination successful */ return 0; } /* eof */ igraph/src/igraph_set.c0000644000176000001440000002003112325527073014636 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_error.h" #include "igraph_types_internal.h" #include "config.h" #include #include /* memmove */ #define SET(s) ((s).stor_begin) /** * \ingroup set * \function igraph_set_init * \brief Initializes a set. * * \param set pointer to the set to be initialized * \param size the expected number of elements in the set * * \return error code: * \c IGRAPH_ENOMEM if there is not enough memory. * * Time complexity: operating system dependent, should be around * O(n), n is the expected size of the set. */ int igraph_set_init(igraph_set_t *set, int long size) { long int alloc_size = size > 0 ? size : 1; if (size < 0) { size = 0; } set->stor_begin=igraph_Calloc(alloc_size, igraph_integer_t); set->stor_end=set->stor_begin + alloc_size; set->end=set->stor_begin; return 0; } /** * \ingroup set * \function igraph_set_destroy * \brief Destroys a set object. * * \param set pointer to the set to be destroyed * * Time complexity: operating system dependent. */ void igraph_set_destroy(igraph_set_t* set) { assert(set != 0); if (set->stor_begin != 0) { igraph_Free(set->stor_begin); set->stor_begin = NULL; } } /** * \ingroup set * \function igraph_set_inited * \brief Determines whether a set is initialized or not. * * This function checks whether the internal storage for the members of the * set has been allocated or not, and it assumes that the pointer for the * internal storage area contains \c NULL if the area is not initialized yet. * This only applies if you have allocated an array of sets with \c igraph_Calloc or * if you used the \c IGRAPH_SET_NULL constant to initialize the set. * * \param set The set object. * * Time complexity: O(1) */ igraph_bool_t igraph_set_inited(igraph_set_t* set) { return (set->stor_begin != 0); } /** * \ingroup set * \function igraph_set_reserve * \brief Reserve memory for a set. * * \param set The set object. * \param size the new \em allocated size of the set. * * Time complexity: operating system dependent, should be around * O(n), n is the new allocated size of the set. */ int igraph_set_reserve(igraph_set_t* set, long int size) { long int actual_size = igraph_set_size(set); igraph_integer_t *tmp; assert(set != NULL); assert(set->stor_begin != NULL); if (size <= actual_size) return 0; tmp=igraph_Realloc(set->stor_begin, (size_t) size, igraph_integer_t); if (tmp==0) { IGRAPH_ERROR("cannot reserve space for set", IGRAPH_ENOMEM); } set->stor_begin=tmp; set->stor_end=set->stor_begin+size; set->end=set->stor_begin+actual_size; return 0; } /** * \ingroup set * \function igraph_set_empty * \brief Decides whether the size of the set is zero. * * \param set The set object. * \return Non-zero number if the size of the set is not zero and * zero otherwise. * * Time complexity: O(1). */ igraph_bool_t igraph_set_empty(const igraph_set_t* set) { assert(set != NULL); assert(set->stor_begin != NULL); return set->stor_begin == set->end; } /** * \ingroup set * \function igraph_set_clear * \brief Removes all elements from a set. * * * This function simply sets the size of the set to zero, it does * not free any allocated memory. For that you have to call * \ref igraph_set_destroy(). * \param v The set object. * * Time complexity: O(1). */ void igraph_set_clear(igraph_set_t* set) { assert(set != NULL); assert(set->stor_begin != NULL); set->end = set->stor_begin; } /** * \ingroup set * \function igraph_vector_set * \brief Gives the size (=length) of the set. * * \param v The set object * \return The size of the set. * * Time complexity: O(1). */ long int igraph_set_size(const igraph_set_t* set) { assert(set != NULL); assert(set->stor_begin != NULL); return set->end-set->stor_begin; } /** * \ingroup set * \function igraph_set_add * \brief Adds an element to the set. * * \param set The set object. * \param e The element to be added. * \return Error code: * \c IGRAPH_ENOMEM: not enough memory. * * Time complexity: O(log(n)), n is the number of elements in \p set. */ int igraph_set_add(igraph_set_t* set, igraph_integer_t e) { long int left, right, middle; long int size; assert(set != NULL); assert(set->stor_begin != NULL); size = igraph_set_size(set); /* search where to insert the new element */ left = 0; right = size-1; while (left < right-1) { middle = (left+right)/2; if (SET(*set)[middle] > e) { right = middle; } else if (SET(*set)[middle] < e) { left = middle; } else { left = middle; break; } } if (right >= 0 && SET(*set)[left] != e && SET(*set)[right] == e) { left = right; } while (left < size && set->stor_begin[left] < e) left++; if (left >= size || set->stor_begin[left] != e) { /* full, allocate more storage */ if (set->stor_end == set->end) { long int new_size = size * 2; if (new_size == 0) new_size = 1; IGRAPH_CHECK(igraph_set_reserve(set, new_size)); } /* Element should be inserted at position 'left' */ if (left < size) memmove(set->stor_begin+left+1, set->stor_begin+left, (size_t) (size-left)*sizeof(set->stor_begin[0])); set->stor_begin[left] = e; set->end += 1; } return 0; } /** * \ingroup set * \function igraph_set_contains * \brief Checks whether a given element is in the set or not. * * \param set The set object. * \param e The element being sought. * \return Positive integer (true) if \p e is found, zero (false) otherwise. * * Time complexity: O(log(n)), n is the number of elements in \p set. */ int igraph_set_contains(igraph_set_t* set, igraph_integer_t e) { long int left, right, middle; assert(set != NULL); assert(set->stor_begin != NULL); left = 0; right = igraph_set_size(set)-1; /* search for the new element */ while (left < right-1) { middle = (left+right)/2; if (SET(*set)[middle] > e) { right = middle; } else if (SET(*set)[middle] < e) { left = middle; } else { left = middle; return 1; } } if (SET(*set)[left] != e && SET(*set)[right] == e) return 1; return (SET(*set)[left] == e); } /** * \ingroup set * \function igraph_set_iterate * \brief Iterates through the element to the set. * * Elements are returned in an arbitrary order. * * \param set The set object. * \param state Internal state of the iteration. * This should be a pointer to a \c long variable * which must be zero for the first invocation. * The object should not be adjusted and its value should * not be used for anything during the iteration. * \param element The next element or \c NULL (if the iteration * has ended) is returned here. * * \return Nonzero if there are more elements, zero otherwise. */ igraph_bool_t igraph_set_iterate(igraph_set_t* set, long int* state, igraph_integer_t* element) { assert(set != 0); assert(set->stor_begin != 0); assert(state != 0); assert(element != 0); if (*state < igraph_set_size(set)) { *element = set->stor_begin[*state]; *state = *state+1; return 1; } else { *element = 0; return 0; } } igraph/src/glpapi11.c0000644000176000001440000012710312325527073014137 0ustar ripleyusers/* glpapi11.c (utility routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpapi.h" int glp_print_sol(glp_prob *P, const char *fname) { /* write basic solution in printable format */ XFILE *fp; GLPROW *row; GLPCOL *col; int i, j, t, ae_ind, re_ind, ret; double ae_max, re_max; xprintf("Writing basic solution to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "%-12s%s\n", "Problem:", P->name == NULL ? "" : P->name); xfprintf(fp, "%-12s%d\n", "Rows:", P->m); xfprintf(fp, "%-12s%d\n", "Columns:", P->n); xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz); t = glp_get_status(P); xfprintf(fp, "%-12s%s\n", "Status:", t == GLP_OPT ? "OPTIMAL" : t == GLP_FEAS ? "FEASIBLE" : t == GLP_INFEAS ? "INFEASIBLE (INTERMEDIATE)" : t == GLP_NOFEAS ? "INFEASIBLE (FINAL)" : t == GLP_UNBND ? "UNBOUNDED" : t == GLP_UNDEF ? "UNDEFINED" : "???"); xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", P->obj == NULL ? "" : P->obj, P->obj == NULL ? "" : " = ", P->obj_val, P->dir == GLP_MIN ? "MINimum" : P->dir == GLP_MAX ? "MAXimum" : "???"); xfprintf(fp, "\n"); xfprintf(fp, " No. Row name St Activity Lower bound " " Upper bound Marginal\n"); xfprintf(fp, "------ ------------ -- ------------- ------------- " "------------- -------------\n"); for (i = 1; i <= P->m; i++) { row = P->row[i]; xfprintf(fp, "%6d ", i); if (row->name == NULL || strlen(row->name) <= 12) xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name); else xfprintf(fp, "%s\n%20s", row->name, ""); xfprintf(fp, "%s ", row->stat == GLP_BS ? "B " : row->stat == GLP_NL ? "NL" : row->stat == GLP_NU ? "NU" : row->stat == GLP_NF ? "NF" : row->stat == GLP_NS ? "NS" : "??"); xfprintf(fp, "%13.6g ", fabs(row->prim) <= 1e-9 ? 0.0 : row->prim); if (row->type == GLP_LO || row->type == GLP_DB || row->type == GLP_FX) xfprintf(fp, "%13.6g ", row->lb); else xfprintf(fp, "%13s ", ""); if (row->type == GLP_UP || row->type == GLP_DB) xfprintf(fp, "%13.6g ", row->ub); else xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : ""); if (row->stat != GLP_BS) { if (fabs(row->dual) <= 1e-9) xfprintf(fp, "%13s", "< eps"); else xfprintf(fp, "%13.6g ", row->dual); } xfprintf(fp, "\n"); } xfprintf(fp, "\n"); xfprintf(fp, " No. Column name St Activity Lower bound " " Upper bound Marginal\n"); xfprintf(fp, "------ ------------ -- ------------- ------------- " "------------- -------------\n"); for (j = 1; j <= P->n; j++) { col = P->col[j]; xfprintf(fp, "%6d ", j); if (col->name == NULL || strlen(col->name) <= 12) xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name); else xfprintf(fp, "%s\n%20s", col->name, ""); xfprintf(fp, "%s ", col->stat == GLP_BS ? "B " : col->stat == GLP_NL ? "NL" : col->stat == GLP_NU ? "NU" : col->stat == GLP_NF ? "NF" : col->stat == GLP_NS ? "NS" : "??"); xfprintf(fp, "%13.6g ", fabs(col->prim) <= 1e-9 ? 0.0 : col->prim); if (col->type == GLP_LO || col->type == GLP_DB || col->type == GLP_FX) xfprintf(fp, "%13.6g ", col->lb); else xfprintf(fp, "%13s ", ""); if (col->type == GLP_UP || col->type == GLP_DB) xfprintf(fp, "%13.6g ", col->ub); else xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : ""); if (col->stat != GLP_BS) { if (fabs(col->dual) <= 1e-9) xfprintf(fp, "%13s", "< eps"); else xfprintf(fp, "%13.6g ", col->dual); } xfprintf(fp, "\n"); } xfprintf(fp, "\n"); xfprintf(fp, "Karush-Kuhn-Tucker optimality conditions:\n"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_SOL, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n", ae_max, ae_ind); xfprintf(fp, " max.rel.err = %.2e on row %d\n", re_max, re_ind); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS WRONG"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_SOL, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n", ae_max, ae_ind <= P->m ? "row" : "column", ae_ind <= P->m ? ae_ind : ae_ind - P->m); xfprintf(fp, " max.rel.err = %.2e on %s %d\n", re_max, re_ind <= P->m ? "row" : "column", re_ind <= P->m ? re_ind : re_ind - P->m); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS INFEASIBL" "E"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_SOL, GLP_KKT_DE, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.DE: max.abs.err = %.2e on column %d\n", ae_max, ae_ind == 0 ? 0 : ae_ind - P->m); xfprintf(fp, " max.rel.err = %.2e on column %d\n", re_max, re_ind == 0 ? 0 : re_ind - P->m); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS WRONG"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_SOL, GLP_KKT_DB, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.DB: max.abs.err = %.2e on %s %d\n", ae_max, ae_ind <= P->m ? "row" : "column", ae_ind <= P->m ? ae_ind : ae_ind - P->m); xfprintf(fp, " max.rel.err = %.2e on %s %d\n", re_max, re_ind <= P->m ? "row" : "column", re_ind <= P->m ? re_ind : re_ind - P->m); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS INFEASIBLE") ; xfprintf(fp, "\n"); xfprintf(fp, "End of output\n"); xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /*********************************************************************** * NAME * * glp_read_sol - read basic solution from text file * * SYNOPSIS * * int glp_read_sol(glp_prob *lp, const char *fname); * * DESCRIPTION * * The routine glp_read_sol reads basic solution from a text file whose * name is specified by the parameter fname into the problem object. * * For the file format see description of the routine glp_write_sol. * * RETURNS * * On success the routine returns zero, otherwise non-zero. */ int glp_read_sol(glp_prob *lp, const char *fname) { glp_data *data; jmp_buf jump; int i, j, k, ret = 0; xprintf("Reading basic solution from `%s'...\n", fname); data = glp_sdf_open_file(fname); if (data == NULL) { ret = 1; goto done; } if (setjmp(jump)) { ret = 1; goto done; } glp_sdf_set_jump(data, jump); /* number of rows, number of columns */ k = glp_sdf_read_int(data); if (k != lp->m) glp_sdf_error(data, "wrong number of rows\n"); k = glp_sdf_read_int(data); if (k != lp->n) glp_sdf_error(data, "wrong number of columns\n"); /* primal status, dual status, objective value */ k = glp_sdf_read_int(data); if (!(k == GLP_UNDEF || k == GLP_FEAS || k == GLP_INFEAS || k == GLP_NOFEAS)) glp_sdf_error(data, "invalid primal status\n"); lp->pbs_stat = k; k = glp_sdf_read_int(data); if (!(k == GLP_UNDEF || k == GLP_FEAS || k == GLP_INFEAS || k == GLP_NOFEAS)) glp_sdf_error(data, "invalid dual status\n"); lp->dbs_stat = k; lp->obj_val = glp_sdf_read_num(data); /* rows (auxiliary variables) */ for (i = 1; i <= lp->m; i++) { GLPROW *row = lp->row[i]; /* status, primal value, dual value */ k = glp_sdf_read_int(data); if (!(k == GLP_BS || k == GLP_NL || k == GLP_NU || k == GLP_NF || k == GLP_NS)) glp_sdf_error(data, "invalid row status\n"); glp_set_row_stat(lp, i, k); row->prim = glp_sdf_read_num(data); row->dual = glp_sdf_read_num(data); } /* columns (structural variables) */ for (j = 1; j <= lp->n; j++) { GLPCOL *col = lp->col[j]; /* status, primal value, dual value */ k = glp_sdf_read_int(data); if (!(k == GLP_BS || k == GLP_NL || k == GLP_NU || k == GLP_NF || k == GLP_NS)) glp_sdf_error(data, "invalid column status\n"); glp_set_col_stat(lp, j, k); col->prim = glp_sdf_read_num(data); col->dual = glp_sdf_read_num(data); } xprintf("%d lines were read\n", glp_sdf_line(data)); done: if (ret) lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; if (data != NULL) glp_sdf_close_file(data); return ret; } /*********************************************************************** * NAME * * glp_write_sol - write basic solution to text file * * SYNOPSIS * * int glp_write_sol(glp_prob *lp, const char *fname); * * DESCRIPTION * * The routine glp_write_sol writes the current basic solution to a * text file whose name is specified by the parameter fname. This file * can be read back with the routine glp_read_sol. * * RETURNS * * On success the routine returns zero, otherwise non-zero. * * FILE FORMAT * * The file created by the routine glp_write_sol is a plain text file, * which contains the following information: * * m n * p_stat d_stat obj_val * r_stat[1] r_prim[1] r_dual[1] * . . . * r_stat[m] r_prim[m] r_dual[m] * c_stat[1] c_prim[1] c_dual[1] * . . . * c_stat[n] c_prim[n] c_dual[n] * * where: * m is the number of rows (auxiliary variables); * n is the number of columns (structural variables); * p_stat is the primal status of the basic solution (GLP_UNDEF = 1, * GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4); * d_stat is the dual status of the basic solution (GLP_UNDEF = 1, * GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4); * obj_val is the objective value; * r_stat[i], i = 1,...,m, is the status of i-th row (GLP_BS = 1, * GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5); * r_prim[i], i = 1,...,m, is the primal value of i-th row; * r_dual[i], i = 1,...,m, is the dual value of i-th row; * c_stat[j], j = 1,...,n, is the status of j-th column (GLP_BS = 1, * GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5); * c_prim[j], j = 1,...,n, is the primal value of j-th column; * c_dual[j], j = 1,...,n, is the dual value of j-th column. */ int glp_write_sol(glp_prob *lp, const char *fname) { XFILE *fp; int i, j, ret = 0; xprintf("Writing basic solution to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* number of rows, number of columns */ xfprintf(fp, "%d %d\n", lp->m, lp->n); /* primal status, dual status, objective value */ xfprintf(fp, "%d %d %.*g\n", lp->pbs_stat, lp->dbs_stat, DBL_DIG, lp->obj_val); /* rows (auxiliary variables) */ for (i = 1; i <= lp->m; i++) { GLPROW *row = lp->row[i]; /* status, primal value, dual value */ xfprintf(fp, "%d %.*g %.*g\n", row->stat, DBL_DIG, row->prim, DBL_DIG, row->dual); } /* columns (structural variables) */ for (j = 1; j <= lp->n; j++) { GLPCOL *col = lp->col[j]; /* status, primal value, dual value */ xfprintf(fp, "%d %.*g %.*g\n", col->stat, DBL_DIG, col->prim, DBL_DIG, col->dual); } xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", 2 + lp->m + lp->n); done: if (fp != NULL) xfclose(fp); return ret; } /**********************************************************************/ static char *format(char buf[13+1], double x) { /* format floating-point number in MPS/360-like style */ if (x == -DBL_MAX) strcpy(buf, " -Inf"); else if (x == +DBL_MAX) strcpy(buf, " +Inf"); else if (fabs(x) <= 999999.99998) { sprintf(buf, "%13.5f", x); #if 1 if (strcmp(buf, " 0.00000") == 0 || strcmp(buf, " -0.00000") == 0) strcpy(buf, " . "); else if (memcmp(buf, " 0.", 8) == 0) memcpy(buf, " .", 8); else if (memcmp(buf, " -0.", 8) == 0) memcpy(buf, " -.", 8); #endif } else sprintf(buf, "%13.6g", x); return buf; } int glp_print_ranges(glp_prob *P, int len, const int list[], int flags, const char *fname) { /* print sensitivity analysis report */ XFILE *fp = NULL; GLPROW *row; GLPCOL *col; int m, n, pass, k, t, numb, type, stat, var1, var2, count, page, ret; double lb, ub, slack, coef, prim, dual, value1, value2, coef1, coef2, obj1, obj2; const char *name, *limit; char buf[13+1]; /* sanity checks */ if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_print_ranges: P = %p; invalid problem object\n", P); m = P->m, n = P->n; if (len < 0) xerror("glp_print_ranges: len = %d; invalid list length\n", len); if (len > 0) { if (list == NULL) xerror("glp_print_ranges: list = %p: invalid parameter\n", list); for (t = 1; t <= len; t++) { k = list[t]; if (!(1 <= k && k <= m+n)) xerror("glp_print_ranges: list[%d] = %d; row/column numb" "er out of range\n", t, k); } } if (flags != 0) xerror("glp_print_ranges: flags = %d; invalid parameter\n", flags); if (fname == NULL) xerror("glp_print_ranges: fname = %p; invalid parameter\n", fname); if (glp_get_status(P) != GLP_OPT) { xprintf("glp_print_ranges: optimal basic solution required\n"); ret = 1; goto done; } if (!glp_bf_exists(P)) { xprintf("glp_print_ranges: basis factorization required\n"); ret = 2; goto done; } /* start reporting */ xprintf("Write sensitivity analysis report to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 3; goto done; } page = count = 0; for (pass = 1; pass <= 2; pass++) for (t = 1; t <= (len == 0 ? m+n : len); t++) { if (t == 1) count = 0; k = (len == 0 ? t : list[t]); if (pass == 1 && k > m || pass == 2 && k <= m) continue; if (count == 0) { xfprintf(fp, "GLPK %-4s - SENSITIVITY ANALYSIS REPORT%73sPa" "ge%4d\n", glp_version(), "", ++page); xfprintf(fp, "\n"); xfprintf(fp, "%-12s%s\n", "Problem:", P->name == NULL ? "" : P->name); xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", P->obj == NULL ? "" : P->obj, P->obj == NULL ? "" : " = ", P->obj_val, P->dir == GLP_MIN ? "MINimum" : P->dir == GLP_MAX ? "MAXimum" : "???"); xfprintf(fp, "\n"); xfprintf(fp, "%6s %-12s %2s %13s %13s %13s %13s %13s %13s " "%s\n", "No.", pass == 1 ? "Row name" : "Column name", "St", "Activity", pass == 1 ? "Slack" : "Obj coef", "Lower bound", "Activity", "Obj coef", "Obj value at", "Limiting"); xfprintf(fp, "%6s %-12s %2s %13s %13s %13s %13s %13s %13s " "%s\n", "", "", "", "", "Marginal", "Upper bound", "range", "range", "break point", "variable"); xfprintf(fp, "------ ------------ -- ------------- --------" "----- ------------- ------------- ------------- ------" "------- ------------\n"); } if (pass == 1) { numb = k; xassert(1 <= numb && numb <= m); row = P->row[numb]; name = row->name; type = row->type; lb = glp_get_row_lb(P, numb); ub = glp_get_row_ub(P, numb); coef = 0.0; stat = row->stat; prim = row->prim; if (type == GLP_FR) slack = - prim; else if (type == GLP_LO) slack = lb - prim; else if (type == GLP_UP || type == GLP_DB || type == GLP_FX) slack = ub - prim; dual = row->dual; } else { numb = k - m; xassert(1 <= numb && numb <= n); col = P->col[numb]; name = col->name; lb = glp_get_col_lb(P, numb); ub = glp_get_col_ub(P, numb); coef = col->coef; stat = col->stat; prim = col->prim; slack = 0.0; dual = col->dual; } if (stat != GLP_BS) { glp_analyze_bound(P, k, &value1, &var1, &value2, &var2); if (stat == GLP_NF) coef1 = coef2 = coef; else if (stat == GLP_NS) coef1 = -DBL_MAX, coef2 = +DBL_MAX; else if (stat == GLP_NL && P->dir == GLP_MIN || stat == GLP_NU && P->dir == GLP_MAX) coef1 = coef - dual, coef2 = +DBL_MAX; else coef1 = -DBL_MAX, coef2 = coef - dual; if (value1 == -DBL_MAX) { if (dual < -1e-9) obj1 = +DBL_MAX; else if (dual > +1e-9) obj1 = -DBL_MAX; else obj1 = P->obj_val; } else obj1 = P->obj_val + dual * (value1 - prim); if (value2 == +DBL_MAX) { if (dual < -1e-9) obj2 = -DBL_MAX; else if (dual > +1e-9) obj2 = +DBL_MAX; else obj2 = P->obj_val; } else obj2 = P->obj_val + dual * (value2 - prim); } else { glp_analyze_coef(P, k, &coef1, &var1, &value1, &coef2, &var2, &value2); if (coef1 == -DBL_MAX) { if (prim < -1e-9) obj1 = +DBL_MAX; else if (prim > +1e-9) obj1 = -DBL_MAX; else obj1 = P->obj_val; } else obj1 = P->obj_val + (coef1 - coef) * prim; if (coef2 == +DBL_MAX) { if (prim < -1e-9) obj2 = -DBL_MAX; else if (prim > +1e-9) obj2 = +DBL_MAX; else obj2 = P->obj_val; } else obj2 = P->obj_val + (coef2 - coef) * prim; } /*** first line ***/ /* row/column number */ xfprintf(fp, "%6d", numb); /* row/column name */ xfprintf(fp, " %-12.12s", name == NULL ? "" : name); if (name != NULL && strlen(name) > 12) xfprintf(fp, "%s\n%6s %12s", name+12, "", ""); /* row/column status */ xfprintf(fp, " %2s", stat == GLP_BS ? "BS" : stat == GLP_NL ? "NL" : stat == GLP_NU ? "NU" : stat == GLP_NF ? "NF" : stat == GLP_NS ? "NS" : "??"); /* row/column activity */ xfprintf(fp, " %s", format(buf, prim)); /* row slack, column objective coefficient */ xfprintf(fp, " %s", format(buf, k <= m ? slack : coef)); /* row/column lower bound */ xfprintf(fp, " %s", format(buf, lb)); /* row/column activity range */ xfprintf(fp, " %s", format(buf, value1)); /* row/column objective coefficient range */ xfprintf(fp, " %s", format(buf, coef1)); /* objective value at break point */ xfprintf(fp, " %s", format(buf, obj1)); /* limiting variable name */ if (var1 != 0) { if (var1 <= m) limit = glp_get_row_name(P, var1); else limit = glp_get_col_name(P, var1 - m); if (limit != NULL) xfprintf(fp, " %s", limit); } xfprintf(fp, "\n"); /*** second line ***/ xfprintf(fp, "%6s %-12s %2s %13s", "", "", "", ""); /* row/column reduced cost */ xfprintf(fp, " %s", format(buf, dual)); /* row/column upper bound */ xfprintf(fp, " %s", format(buf, ub)); /* row/column activity range */ xfprintf(fp, " %s", format(buf, value2)); /* row/column objective coefficient range */ xfprintf(fp, " %s", format(buf, coef2)); /* objective value at break point */ xfprintf(fp, " %s", format(buf, obj2)); /* limiting variable name */ if (var2 != 0) { if (var2 <= m) limit = glp_get_row_name(P, var2); else limit = glp_get_col_name(P, var2 - m); if (limit != NULL) xfprintf(fp, " %s", limit); } xfprintf(fp, "\n"); xfprintf(fp, "\n"); /* print 10 items per page */ count = (count + 1) % 10; } xfprintf(fp, "End of report\n"); xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 4; goto done; } ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /**********************************************************************/ int glp_print_ipt(glp_prob *P, const char *fname) { /* write interior-point solution in printable format */ XFILE *fp; GLPROW *row; GLPCOL *col; int i, j, t, ae_ind, re_ind, ret; double ae_max, re_max; xprintf("Writing interior-point solution to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "%-12s%s\n", "Problem:", P->name == NULL ? "" : P->name); xfprintf(fp, "%-12s%d\n", "Rows:", P->m); xfprintf(fp, "%-12s%d\n", "Columns:", P->n); xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz); t = glp_ipt_status(P); xfprintf(fp, "%-12s%s\n", "Status:", t == GLP_OPT ? "OPTIMAL" : t == GLP_UNDEF ? "UNDEFINED" : t == GLP_INFEAS ? "INFEASIBLE (INTERMEDIATE)" : t == GLP_NOFEAS ? "INFEASIBLE (FINAL)" : "???"); xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", P->obj == NULL ? "" : P->obj, P->obj == NULL ? "" : " = ", P->ipt_obj, P->dir == GLP_MIN ? "MINimum" : P->dir == GLP_MAX ? "MAXimum" : "???"); xfprintf(fp, "\n"); xfprintf(fp, " No. Row name Activity Lower bound " " Upper bound Marginal\n"); xfprintf(fp, "------ ------------ ------------- ------------- " "------------- -------------\n"); for (i = 1; i <= P->m; i++) { row = P->row[i]; xfprintf(fp, "%6d ", i); if (row->name == NULL || strlen(row->name) <= 12) xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name); else xfprintf(fp, "%s\n%20s", row->name, ""); xfprintf(fp, "%3s", ""); xfprintf(fp, "%13.6g ", fabs(row->pval) <= 1e-9 ? 0.0 : row->pval); if (row->type == GLP_LO || row->type == GLP_DB || row->type == GLP_FX) xfprintf(fp, "%13.6g ", row->lb); else xfprintf(fp, "%13s ", ""); if (row->type == GLP_UP || row->type == GLP_DB) xfprintf(fp, "%13.6g ", row->ub); else xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : ""); if (fabs(row->dval) <= 1e-9) xfprintf(fp, "%13s", "< eps"); else xfprintf(fp, "%13.6g ", row->dval); xfprintf(fp, "\n"); } xfprintf(fp, "\n"); xfprintf(fp, " No. Column name Activity Lower bound " " Upper bound Marginal\n"); xfprintf(fp, "------ ------------ ------------- ------------- " "------------- -------------\n"); for (j = 1; j <= P->n; j++) { col = P->col[j]; xfprintf(fp, "%6d ", j); if (col->name == NULL || strlen(col->name) <= 12) xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name); else xfprintf(fp, "%s\n%20s", col->name, ""); xfprintf(fp, "%3s", ""); xfprintf(fp, "%13.6g ", fabs(col->pval) <= 1e-9 ? 0.0 : col->pval); if (col->type == GLP_LO || col->type == GLP_DB || col->type == GLP_FX) xfprintf(fp, "%13.6g ", col->lb); else xfprintf(fp, "%13s ", ""); if (col->type == GLP_UP || col->type == GLP_DB) xfprintf(fp, "%13.6g ", col->ub); else xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : ""); if (fabs(col->dval) <= 1e-9) xfprintf(fp, "%13s", "< eps"); else xfprintf(fp, "%13.6g ", col->dval); xfprintf(fp, "\n"); } xfprintf(fp, "\n"); xfprintf(fp, "Karush-Kuhn-Tucker optimality conditions:\n"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_IPT, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n", ae_max, ae_ind); xfprintf(fp, " max.rel.err = %.2e on row %d\n", re_max, re_ind); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS WRONG"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_IPT, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n", ae_max, ae_ind <= P->m ? "row" : "column", ae_ind <= P->m ? ae_ind : ae_ind - P->m); xfprintf(fp, " max.rel.err = %.2e on %s %d\n", re_max, re_ind <= P->m ? "row" : "column", re_ind <= P->m ? re_ind : re_ind - P->m); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS INFEASIBL" "E"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_IPT, GLP_KKT_DE, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.DE: max.abs.err = %.2e on column %d\n", ae_max, ae_ind == 0 ? 0 : ae_ind - P->m); xfprintf(fp, " max.rel.err = %.2e on column %d\n", re_max, re_ind == 0 ? 0 : re_ind - P->m); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS WRONG"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_IPT, GLP_KKT_DB, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.DB: max.abs.err = %.2e on %s %d\n", ae_max, ae_ind <= P->m ? "row" : "column", ae_ind <= P->m ? ae_ind : ae_ind - P->m); xfprintf(fp, " max.rel.err = %.2e on %s %d\n", re_max, re_ind <= P->m ? "row" : "column", re_ind <= P->m ? re_ind : re_ind - P->m); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS INFEASIBLE") ; xfprintf(fp, "\n"); xfprintf(fp, "End of output\n"); xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /*********************************************************************** * NAME * * glp_read_ipt - read interior-point solution from text file * * SYNOPSIS * * int glp_read_ipt(glp_prob *lp, const char *fname); * * DESCRIPTION * * The routine glp_read_ipt reads interior-point solution from a text * file whose name is specified by the parameter fname into the problem * object. * * For the file format see description of the routine glp_write_ipt. * * RETURNS * * On success the routine returns zero, otherwise non-zero. */ int glp_read_ipt(glp_prob *lp, const char *fname) { glp_data *data; jmp_buf jump; int i, j, k, ret = 0; xprintf("Reading interior-point solution from `%s'...\n", fname); data = glp_sdf_open_file(fname); if (data == NULL) { ret = 1; goto done; } if (setjmp(jump)) { ret = 1; goto done; } glp_sdf_set_jump(data, jump); /* number of rows, number of columns */ k = glp_sdf_read_int(data); if (k != lp->m) glp_sdf_error(data, "wrong number of rows\n"); k = glp_sdf_read_int(data); if (k != lp->n) glp_sdf_error(data, "wrong number of columns\n"); /* solution status, objective value */ k = glp_sdf_read_int(data); if (!(k == GLP_UNDEF || k == GLP_OPT)) glp_sdf_error(data, "invalid solution status\n"); lp->ipt_stat = k; lp->ipt_obj = glp_sdf_read_num(data); /* rows (auxiliary variables) */ for (i = 1; i <= lp->m; i++) { GLPROW *row = lp->row[i]; /* primal value, dual value */ row->pval = glp_sdf_read_num(data); row->dval = glp_sdf_read_num(data); } /* columns (structural variables) */ for (j = 1; j <= lp->n; j++) { GLPCOL *col = lp->col[j]; /* primal value, dual value */ col->pval = glp_sdf_read_num(data); col->dval = glp_sdf_read_num(data); } xprintf("%d lines were read\n", glp_sdf_line(data)); done: if (ret) lp->ipt_stat = GLP_UNDEF; if (data != NULL) glp_sdf_close_file(data); return ret; } /*********************************************************************** * NAME * * glp_write_ipt - write interior-point solution to text file * * SYNOPSIS * * int glp_write_ipt(glp_prob *lp, const char *fname); * * DESCRIPTION * * The routine glp_write_ipt writes the current interior-point solution * to a text file whose name is specified by the parameter fname. This * file can be read back with the routine glp_read_ipt. * * RETURNS * * On success the routine returns zero, otherwise non-zero. * * FILE FORMAT * * The file created by the routine glp_write_ipt is a plain text file, * which contains the following information: * * m n * stat obj_val * r_prim[1] r_dual[1] * . . . * r_prim[m] r_dual[m] * c_prim[1] c_dual[1] * . . . * c_prim[n] c_dual[n] * * where: * m is the number of rows (auxiliary variables); * n is the number of columns (structural variables); * stat is the solution status (GLP_UNDEF = 1 or GLP_OPT = 5); * obj_val is the objective value; * r_prim[i], i = 1,...,m, is the primal value of i-th row; * r_dual[i], i = 1,...,m, is the dual value of i-th row; * c_prim[j], j = 1,...,n, is the primal value of j-th column; * c_dual[j], j = 1,...,n, is the dual value of j-th column. */ int glp_write_ipt(glp_prob *lp, const char *fname) { XFILE *fp; int i, j, ret = 0; xprintf("Writing interior-point solution to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* number of rows, number of columns */ xfprintf(fp, "%d %d\n", lp->m, lp->n); /* solution status, objective value */ xfprintf(fp, "%d %.*g\n", lp->ipt_stat, DBL_DIG, lp->ipt_obj); /* rows (auxiliary variables) */ for (i = 1; i <= lp->m; i++) { GLPROW *row = lp->row[i]; /* primal value, dual value */ xfprintf(fp, "%.*g %.*g\n", DBL_DIG, row->pval, DBL_DIG, row->dval); } /* columns (structural variables) */ for (j = 1; j <= lp->n; j++) { GLPCOL *col = lp->col[j]; /* primal value, dual value */ xfprintf(fp, "%.*g %.*g\n", DBL_DIG, col->pval, DBL_DIG, col->dval); } xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", 2 + lp->m + lp->n); done: if (fp != NULL) xfclose(fp); return ret; } /**********************************************************************/ int glp_print_mip(glp_prob *P, const char *fname) { /* write MIP solution in printable format */ XFILE *fp; GLPROW *row; GLPCOL *col; int i, j, t, ae_ind, re_ind, ret; double ae_max, re_max; xprintf("Writing MIP solution to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "%-12s%s\n", "Problem:", P->name == NULL ? "" : P->name); xfprintf(fp, "%-12s%d\n", "Rows:", P->m); xfprintf(fp, "%-12s%d (%d integer, %d binary)\n", "Columns:", P->n, glp_get_num_int(P), glp_get_num_bin(P)); xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz); t = glp_mip_status(P); xfprintf(fp, "%-12s%s\n", "Status:", t == GLP_OPT ? "INTEGER OPTIMAL" : t == GLP_FEAS ? "INTEGER NON-OPTIMAL" : t == GLP_NOFEAS ? "INTEGER EMPTY" : t == GLP_UNDEF ? "INTEGER UNDEFINED" : "???"); xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:", P->obj == NULL ? "" : P->obj, P->obj == NULL ? "" : " = ", P->mip_obj, P->dir == GLP_MIN ? "MINimum" : P->dir == GLP_MAX ? "MAXimum" : "???"); xfprintf(fp, "\n"); xfprintf(fp, " No. Row name Activity Lower bound " " Upper bound\n"); xfprintf(fp, "------ ------------ ------------- ------------- " "-------------\n"); for (i = 1; i <= P->m; i++) { row = P->row[i]; xfprintf(fp, "%6d ", i); if (row->name == NULL || strlen(row->name) <= 12) xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name); else xfprintf(fp, "%s\n%20s", row->name, ""); xfprintf(fp, "%3s", ""); xfprintf(fp, "%13.6g ", fabs(row->mipx) <= 1e-9 ? 0.0 : row->mipx); if (row->type == GLP_LO || row->type == GLP_DB || row->type == GLP_FX) xfprintf(fp, "%13.6g ", row->lb); else xfprintf(fp, "%13s ", ""); if (row->type == GLP_UP || row->type == GLP_DB) xfprintf(fp, "%13.6g ", row->ub); else xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : ""); xfprintf(fp, "\n"); } xfprintf(fp, "\n"); xfprintf(fp, " No. Column name Activity Lower bound " " Upper bound\n"); xfprintf(fp, "------ ------------ ------------- ------------- " "-------------\n"); for (j = 1; j <= P->n; j++) { col = P->col[j]; xfprintf(fp, "%6d ", j); if (col->name == NULL || strlen(col->name) <= 12) xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name); else xfprintf(fp, "%s\n%20s", col->name, ""); xfprintf(fp, "%s ", col->kind == GLP_CV ? " " : col->kind == GLP_IV ? "*" : "?"); xfprintf(fp, "%13.6g ", fabs(col->mipx) <= 1e-9 ? 0.0 : col->mipx); if (col->type == GLP_LO || col->type == GLP_DB || col->type == GLP_FX) xfprintf(fp, "%13.6g ", col->lb); else xfprintf(fp, "%13s ", ""); if (col->type == GLP_UP || col->type == GLP_DB) xfprintf(fp, "%13.6g ", col->ub); else xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : ""); xfprintf(fp, "\n"); } xfprintf(fp, "\n"); xfprintf(fp, "Integer feasibility conditions:\n"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_MIP, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n", ae_max, ae_ind); xfprintf(fp, " max.rel.err = %.2e on row %d\n", re_max, re_ind); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "SOLUTION IS WRONG"); xfprintf(fp, "\n"); _glp_check_kkt(P, GLP_MIP, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, &re_ind); xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n", ae_max, ae_ind <= P->m ? "row" : "column", ae_ind <= P->m ? ae_ind : ae_ind - P->m); xfprintf(fp, " max.rel.err = %.2e on %s %d\n", re_max, re_ind <= P->m ? "row" : "column", re_ind <= P->m ? re_ind : re_ind - P->m); xfprintf(fp, "%8s%s\n", "", re_max <= 1e-9 ? "High quality" : re_max <= 1e-6 ? "Medium quality" : re_max <= 1e-3 ? "Low quality" : "SOLUTION IS INFEASIBLE"); xfprintf(fp, "\n"); xfprintf(fp, "End of output\n"); xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /*********************************************************************** * NAME * * glp_read_mip - read MIP solution from text file * * SYNOPSIS * * int glp_read_mip(glp_prob *mip, const char *fname); * * DESCRIPTION * * The routine glp_read_mip reads MIP solution from a text file whose * name is specified by the parameter fname into the problem object. * * For the file format see description of the routine glp_write_mip. * * RETURNS * * On success the routine returns zero, otherwise non-zero. */ int glp_read_mip(glp_prob *mip, const char *fname) { glp_data *data; jmp_buf jump; int i, j, k, ret = 0; xprintf("Reading MIP solution from `%s'...\n", fname); data = glp_sdf_open_file(fname); if (data == NULL) { ret = 1; goto done; } if (setjmp(jump)) { ret = 1; goto done; } glp_sdf_set_jump(data, jump); /* number of rows, number of columns */ k = glp_sdf_read_int(data); if (k != mip->m) glp_sdf_error(data, "wrong number of rows\n"); k = glp_sdf_read_int(data); if (k != mip->n) glp_sdf_error(data, "wrong number of columns\n"); /* solution status, objective value */ k = glp_sdf_read_int(data); if (!(k == GLP_UNDEF || k == GLP_OPT || k == GLP_FEAS || k == GLP_NOFEAS)) glp_sdf_error(data, "invalid solution status\n"); mip->mip_stat = k; mip->mip_obj = glp_sdf_read_num(data); /* rows (auxiliary variables) */ for (i = 1; i <= mip->m; i++) { GLPROW *row = mip->row[i]; row->mipx = glp_sdf_read_num(data); } /* columns (structural variables) */ for (j = 1; j <= mip->n; j++) { GLPCOL *col = mip->col[j]; col->mipx = glp_sdf_read_num(data); if (col->kind == GLP_IV && col->mipx != floor(col->mipx)) glp_sdf_error(data, "non-integer column value"); } xprintf("%d lines were read\n", glp_sdf_line(data)); done: if (ret) mip->mip_stat = GLP_UNDEF; if (data != NULL) glp_sdf_close_file(data); return ret; } /*********************************************************************** * NAME * * glp_write_mip - write MIP solution to text file * * SYNOPSIS * * int glp_write_mip(glp_prob *mip, const char *fname); * * DESCRIPTION * * The routine glp_write_mip writes the current MIP solution to a text * file whose name is specified by the parameter fname. This file can * be read back with the routine glp_read_mip. * * RETURNS * * On success the routine returns zero, otherwise non-zero. * * FILE FORMAT * * The file created by the routine glp_write_sol is a plain text file, * which contains the following information: * * m n * stat obj_val * r_val[1] * . . . * r_val[m] * c_val[1] * . . . * c_val[n] * * where: * m is the number of rows (auxiliary variables); * n is the number of columns (structural variables); * stat is the solution status (GLP_UNDEF = 1, GLP_FEAS = 2, * GLP_NOFEAS = 4, or GLP_OPT = 5); * obj_val is the objective value; * r_val[i], i = 1,...,m, is the value of i-th row; * c_val[j], j = 1,...,n, is the value of j-th column. */ int glp_write_mip(glp_prob *mip, const char *fname) { XFILE *fp; int i, j, ret = 0; xprintf("Writing MIP solution to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* number of rows, number of columns */ xfprintf(fp, "%d %d\n", mip->m, mip->n); /* solution status, objective value */ xfprintf(fp, "%d %.*g\n", mip->mip_stat, DBL_DIG, mip->mip_obj); /* rows (auxiliary variables) */ for (i = 1; i <= mip->m; i++) xfprintf(fp, "%.*g\n", DBL_DIG, mip->row[i]->mipx); /* columns (structural variables) */ for (j = 1; j <= mip->n; j++) xfprintf(fp, "%.*g\n", DBL_DIG, mip->col[j]->mipx); xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", 2 + mip->m + mip->n); done: if (fp != NULL) xfclose(fp); return ret; } /* eof */ igraph/src/DensityGrid.cpp0000644000176000001440000002003112325527072015275 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file contains the member definitions of the DensityGrid.h class // This code is modified from the original code by B.N. Wylie #include #include #include #include #include using namespace std; #include "drl_Node.h" #include "DensityGrid.h" #include "igraph_error.h" #define GET_BIN(y, x) (Bins[y*GRID_SIZE+x]) namespace drl { //******************************************************* // Density Grid Destructor -- deallocates memory used // for Density matrix, fall_off matrix, and node deque. DensityGrid::~DensityGrid () { delete[] Density; delete[] fall_off; delete[] Bins; } /********************************************* * Function: Density_Grid::Reset * * Description: Reset the density grid * *********************************************/ // changed from reset to init since we will only // call this once in the parallel version of layout void DensityGrid::Init() { try { Density = new float[GRID_SIZE][GRID_SIZE]; fall_off = new float[RADIUS*2+1][RADIUS*2+1]; Bins = new deque[GRID_SIZE*GRID_SIZE]; } catch (bad_alloc errora) { // cout << "Error: Out of memory! Program stopped." << endl; #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else igraph_error("DrL is out of memory", __FILE__, __LINE__, IGRAPH_ENOMEM); #endif } // Clear Grid int i; for (i=0; i< GRID_SIZE; i++) for (int j=0; j< GRID_SIZE; j++) { Density[i][j] = 0; GET_BIN(i, j).erase(GET_BIN(i, j).begin(), GET_BIN(i, j).end()); } // Compute fall off for(i=-RADIUS; i<=RADIUS; i++) for(int j=-RADIUS; j<=RADIUS; j++) { fall_off[i+RADIUS][j+RADIUS] = (float)((RADIUS-fabs((float)i))/RADIUS) * (float)((RADIUS-fabs((float)j))/RADIUS); } } /*************************************************** * Function: DensityGrid::GetDensity * * Description: Get_Density from density grid * **************************************************/ float DensityGrid::GetDensity(float Nx, float Ny, bool fineDensity) { deque::iterator BI; int x_grid, y_grid; float x_dist, y_dist, distance, density=0; int boundary=10; // boundary around plane /* Where to look */ x_grid = (int)((Nx+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((Ny+HALF_VIEW+.5)*VIEW_TO_GRID); // Check for edges of density grid (10000 is arbitrary high density) if (x_grid > GRID_SIZE-boundary || x_grid < boundary) return 10000; if (y_grid > GRID_SIZE-boundary || y_grid < boundary) return 10000; // Fine density? if (fineDensity) { // Go through nearest bins for(int i=y_grid-1; i<=y_grid+1; i++) for(int j=x_grid-1; j<=x_grid+1; j++) { // Look through bin and add fine repulsions for(BI = GET_BIN(i, j).begin(); BI != GET_BIN(i, j).end(); ++BI) { x_dist = Nx-(BI->x); y_dist = Ny-(BI->y); distance = x_dist*x_dist+y_dist*y_dist; density += 1e-4/(distance + 1e-50); } } // Course density } else { // Add rough estimate density = Density[y_grid][x_grid]; density *= density; } return density; } /// Wrapper functions for the Add and subtract methods /// Nodes should all be passed by constant ref void DensityGrid::Add(Node &n, bool fineDensity) { if(fineDensity) fineAdd(n); else Add(n); } void DensityGrid::Subtract( Node &n, bool first_add, bool fine_first_add, bool fineDensity) { if ( fineDensity && !fine_first_add ) fineSubtract (n); else if ( !first_add ) Subtract(n); } /*************************************************** * Function: DensityGrid::Subtract * * Description: Subtract a node from density grid * **************************************************/ void DensityGrid::Subtract(Node &N) { int x_grid, y_grid, diam; float *den_ptr, *fall_ptr; /* Where to subtract */ x_grid = (int)((N.sub_x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.sub_y+HALF_VIEW+.5)*VIEW_TO_GRID); x_grid -= RADIUS; y_grid -= RADIUS; diam = 2*RADIUS; /* Subtract density values */ den_ptr = &Density[y_grid][x_grid]; fall_ptr = &fall_off[0][0]; for(int i = 0; i <= diam; i++) { for(int j = 0; j <= diam; j++) *den_ptr++ -= *fall_ptr++; den_ptr += GRID_SIZE - (diam+1); } } /*************************************************** * Function: DensityGrid::Add * * Description: Add a node to the density grid * **************************************************/ void DensityGrid::Add(Node &N) { int x_grid, y_grid, diam; float *den_ptr, *fall_ptr; /* Where to add */ x_grid = (int)((N.x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.y+HALF_VIEW+.5)*VIEW_TO_GRID); N.sub_x = N.x; N.sub_y = N.y; x_grid -= RADIUS; y_grid -= RADIUS; diam = 2*RADIUS; // check to see that we are inside grid if ( (x_grid >= GRID_SIZE) || (x_grid < 0) || (y_grid >= GRID_SIZE) || (y_grid < 0) ) { // cout << endl << "Error: Exceeded density grid with x_grid = " << x_grid // << " and y_grid = " << y_grid << ". Program stopped." << endl; #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else igraph_error("Exceeded density grid in DrL", __FILE__, __LINE__, IGRAPH_EDRL); #endif } /* Add density values */ den_ptr = &Density[y_grid][x_grid]; fall_ptr = &fall_off[0][0]; for(int i = 0; i <= diam; i++) { for(int j = 0; j <= diam; j++) *den_ptr++ += *fall_ptr++; den_ptr += GRID_SIZE - (diam+1); } } /*************************************************** * Function: DensityGrid::fineSubtract * * Description: Subtract a node from bins * **************************************************/ void DensityGrid::fineSubtract(Node &N) { int x_grid, y_grid; /* Where to subtract */ x_grid = (int)((N.sub_x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.sub_y+HALF_VIEW+.5)*VIEW_TO_GRID); GET_BIN(y_grid, x_grid).pop_front(); } /*************************************************** * Function: DensityGrid::fineAdd * * Description: Add a node to the bins * **************************************************/ void DensityGrid::fineAdd(Node &N) { int x_grid, y_grid; /* Where to add */ x_grid = (int)((N.x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.y+HALF_VIEW+.5)*VIEW_TO_GRID); N.sub_x = N.x; N.sub_y = N.y; GET_BIN(y_grid, x_grid).push_back(N); } } // namespace drl igraph/src/topology.c0000644000176000001440000036147412325527074014411 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_topology.h" #include "igraph_memory.h" #include "igraph_adjlist.h" #include "igraph_interface.h" #include "igraph_interrupt_internal.h" #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_stack.h" #include "igraph_attributes.h" #include "config.h" const unsigned int igraph_i_isoclass_3[] = { 0, 1, 1, 3, 1, 5, 6, 7, 1, 6,10,11, 3, 7,11,15, 1, 6, 5, 7,10,21,21,23, 6,25,21,27,11,27,30,31, 1,10, 6,11, 6,21,25,27, 5,21,21,30, 7,23,27,31, 3,11, 7,15,11,30,27,31, 7,27,23,31,15,31,31,63 }; const unsigned int igraph_i_isoclass_3_idx[] = { 0, 4, 16, 1, 0, 32, 2, 8, 0 }; const unsigned int igraph_i_isoclass_4[] = { 0, 1, 1, 3, 1, 3, 3, 7, 1, 9, 10, 11, 10, 11, 14, 15, 1, 10, 18, 19, 20, 21, 22, 23, 3, 11, 19, 27, 21, 29, 30, 31, 1, 10, 20, 21, 18, 19, 22, 23, 3, 11, 21, 29, 19, 27, 30, 31, 3, 14, 22, 30, 22, 30, 54, 55, 7, 15, 23, 31, 23, 31, 55, 63, 1, 10, 9, 11, 10, 14, 11, 15, 18, 73, 73, 75, 76, 77, 77, 79, 10, 81, 73, 83, 84, 85, 86, 87, 19, 83, 90, 91, 92, 93, 94, 95, 20, 84, 98, 99, 100, 101, 102, 103, 22, 86, 106, 107, 108, 109, 110, 111, 21, 85, 106, 115, 116, 117, 118, 119, 23, 87, 122, 123, 124, 125, 126, 127, 1, 18, 10, 19, 20, 22, 21, 23, 10, 73, 81, 83, 84, 86, 85, 87, 9, 73, 73, 90, 98, 106, 106, 122, 11, 75, 83, 91, 99, 107, 115, 123, 10, 76, 84, 92, 100, 108, 116, 124, 14, 77, 85, 93, 101, 109, 117, 125, 11, 77, 86, 94, 102, 110, 118, 126, 15, 79, 87, 95, 103, 111, 119, 127, 3, 19, 11, 27, 21, 30, 29, 31, 19, 90, 83, 91, 92, 94, 93, 95, 11, 83, 75, 91, 99, 115, 107, 123, 27, 91, 91, 219, 220, 221, 221, 223, 21, 92, 99, 220, 228, 229, 230, 231, 30, 94, 115, 221, 229, 237, 238, 239, 29, 93, 107, 221, 230, 238, 246, 247, 31, 95, 123, 223, 231, 239, 247, 255, 1, 20, 10, 21, 18, 22, 19, 23, 20, 98, 84, 99, 100, 102, 101, 103, 10, 84, 76, 92, 100, 116, 108, 124, 21, 99, 92, 220, 228, 230, 229, 231, 18, 100, 100, 228, 292, 293, 293, 295, 22, 102, 116, 230, 293, 301, 302, 303, 19, 101, 108, 229, 293, 302, 310, 311, 23, 103, 124, 231, 295, 303, 311, 319, 3, 21, 11, 29, 19, 30, 27, 31, 22, 106, 86, 107, 108, 110, 109, 111, 14, 85, 77, 93, 101, 117, 109, 125, 30, 115, 94, 221, 229, 238, 237, 239, 22, 116, 102, 230, 293, 302, 301, 303, 54, 118, 118, 246, 310, 365, 365, 367, 30, 117, 110, 238, 302, 373, 365, 375, 55, 119, 126, 247, 311, 375, 382, 383, 3, 22, 14, 30, 22, 54, 30, 55, 21, 106, 85, 115, 116, 118, 117, 119, 11, 86, 77, 94, 102, 118, 110, 126, 29, 107, 93, 221, 230, 246, 238, 247, 19, 108, 101, 229, 293, 310, 302, 311, 30, 110, 117, 238, 302, 365, 373, 375, 27, 109, 109, 237, 301, 365, 365, 382, 31, 111, 125, 239, 303, 367, 375, 383, 7, 23, 15, 31, 23, 55, 31, 63, 23, 122, 87, 123, 124, 126, 125, 127, 15, 87, 79, 95, 103, 119, 111, 127, 31, 123, 95, 223, 231, 247, 239, 255, 23, 124, 103, 231, 295, 311, 303, 319, 55, 126, 119, 247, 311, 382, 375, 383, 31, 125, 111, 239, 303, 375, 367, 383, 63, 127, 127, 255, 319, 383, 383, 511, 1, 10, 10, 14, 9, 11, 11, 15, 18, 73, 76, 77, 73, 75, 77, 79, 20, 84, 100, 101, 98, 99, 102, 103, 22, 86, 108, 109, 106, 107, 110, 111, 10, 81, 84, 85, 73, 83, 86, 87, 19, 83, 92, 93, 90, 91, 94, 95, 21, 85, 116, 117, 106, 115, 118, 119, 23, 87, 124, 125, 122, 123, 126, 127, 18, 76, 73, 77, 73, 77, 75, 79, 292, 585, 585, 587, 585, 587, 587, 591, 100, 593, 594, 595, 596, 597, 598, 599, 293, 601, 602, 603, 604, 605, 606, 607, 100, 593, 596, 597, 594, 595, 598, 599, 293, 601, 604, 605, 602, 603, 606, 607, 228, 625, 626, 627, 626, 627, 630, 631, 295, 633, 634, 635, 634, 635, 638, 639, 20, 100, 84, 101, 98, 102, 99, 103, 100, 594, 593, 595, 596, 598, 597, 599, 98, 596, 596, 659, 660, 661, 661, 663, 102, 598, 666, 667, 661, 669, 670, 671, 84, 593, 674, 675, 596, 666, 678, 679, 101, 595, 675, 683, 659, 667, 686, 687, 99, 597, 678, 686, 661, 670, 694, 695, 103, 599, 679, 687, 663, 671, 695, 703, 22, 108, 86, 109, 106, 110, 107, 111, 293, 602, 601, 603, 604, 606, 605, 607, 102, 666, 598, 667, 661, 670, 669, 671, 301, 729, 729, 731, 732, 733, 733, 735, 116, 737, 678, 739, 626, 741, 742, 743, 302, 745, 746, 747, 748, 749, 750, 751, 230, 753, 742, 755, 756, 757, 758, 759, 303, 761, 762, 763, 764, 765, 766, 767, 10, 84, 81, 85, 73, 86, 83, 87, 100, 596, 593, 597, 594, 598, 595, 599, 84, 674, 593, 675, 596, 678, 666, 679, 116, 678, 737, 739, 626, 742, 741, 743, 76, 593, 593, 625, 585, 601, 601, 633, 108, 666, 737, 753, 602, 729, 745, 761, 92, 675, 737, 819, 604, 746, 822, 823, 124, 679, 826, 827, 634, 762, 830, 831, 19, 92, 83, 93, 90, 94, 91, 95, 293, 604, 601, 605, 602, 606, 603, 607, 101, 675, 595, 683, 659, 686, 667, 687, 302, 746, 745, 747, 748, 750, 749, 751, 108, 737, 666, 753, 602, 745, 729, 761, 310, 822, 822, 875, 876, 877, 877, 879, 229, 819, 741, 883, 748, 885, 886, 887, 311, 823, 830, 891, 892, 893, 894, 895, 21, 116, 85, 117, 106, 118, 115, 119, 228, 626, 625, 627, 626, 630, 627, 631, 99, 678, 597, 686, 661, 694, 670, 695, 230, 742, 753, 755, 756, 758, 757, 759, 92, 737, 675, 819, 604, 822, 746, 823, 229, 741, 819, 883, 748, 886, 885, 887, 220, 739, 739, 947, 732, 949, 949, 951, 231, 743, 827, 955, 764, 957, 958, 959, 23, 124, 87, 125, 122, 126, 123, 127, 295, 634, 633, 635, 634, 638, 635, 639, 103, 679, 599, 687, 663, 695, 671, 703, 303, 762, 761, 763, 764, 766, 765, 767, 124, 826, 679, 827, 634, 830, 762, 831, 311, 830, 823, 891, 892, 894, 893, 895, 231, 827, 743, 955, 764, 958, 957, 959, 319, 831, 831,1019,1020,1021,1021,1023, 1, 18, 20, 22, 10, 19, 21, 23, 10, 73, 84, 86, 81, 83, 85, 87, 10, 76, 100, 108, 84, 92, 116, 124, 14, 77, 101, 109, 85, 93, 117, 125, 9, 73, 98, 106, 73, 90, 106, 122, 11, 75, 99, 107, 83, 91, 115, 123, 11, 77, 102, 110, 86, 94, 118, 126, 15, 79, 103, 111, 87, 95, 119, 127, 20, 100, 98, 102, 84, 101, 99, 103, 100, 594, 596, 598, 593, 595, 597, 599, 84, 593, 596, 666, 674, 675, 678, 679, 101, 595, 659, 667, 675, 683, 686, 687, 98, 596, 660, 661, 596, 659, 661, 663, 102, 598, 661, 669, 666, 667, 670, 671, 99, 597, 661, 670, 678, 686, 694, 695, 103, 599, 663, 671, 679, 687, 695, 703, 18, 292, 100, 293, 100, 293, 228, 295, 76, 585, 593, 601, 593, 601, 625, 633, 73, 585, 594, 602, 596, 604, 626, 634, 77, 587, 595, 603, 597, 605, 627, 635, 73, 585, 596, 604, 594, 602, 626, 634, 77, 587, 597, 605, 595, 603, 627, 635, 75, 587, 598, 606, 598, 606, 630, 638, 79, 591, 599, 607, 599, 607, 631, 639, 22, 293, 102, 301, 116, 302, 230, 303, 108, 602, 666, 729, 737, 745, 753, 761, 86, 601, 598, 729, 678, 746, 742, 762, 109, 603, 667, 731, 739, 747, 755, 763, 106, 604, 661, 732, 626, 748, 756, 764, 110, 606, 670, 733, 741, 749, 757, 765, 107, 605, 669, 733, 742, 750, 758, 766, 111, 607, 671, 735, 743, 751, 759, 767, 10, 100, 84, 116, 76, 108, 92, 124, 84, 596, 674, 678, 593, 666, 675, 679, 81, 593, 593, 737, 593, 737, 737, 826, 85, 597, 675, 739, 625, 753, 819, 827, 73, 594, 596, 626, 585, 602, 604, 634, 86, 598, 678, 742, 601, 729, 746, 762, 83, 595, 666, 741, 601, 745, 822, 830, 87, 599, 679, 743, 633, 761, 823, 831, 21, 228, 99, 230, 92, 229, 220, 231, 116, 626, 678, 742, 737, 741, 739, 743, 85, 625, 597, 753, 675, 819, 739, 827, 117, 627, 686, 755, 819, 883, 947, 955, 106, 626, 661, 756, 604, 748, 732, 764, 118, 630, 694, 758, 822, 886, 949, 957, 115, 627, 670, 757, 746, 885, 949, 958, 119, 631, 695, 759, 823, 887, 951, 959, 19, 293, 101, 302, 108, 310, 229, 311, 92, 604, 675, 746, 737, 822, 819, 823, 83, 601, 595, 745, 666, 822, 741, 830, 93, 605, 683, 747, 753, 875, 883, 891, 90, 602, 659, 748, 602, 876, 748, 892, 94, 606, 686, 750, 745, 877, 885, 893, 91, 603, 667, 749, 729, 877, 886, 894, 95, 607, 687, 751, 761, 879, 887, 895, 23, 295, 103, 303, 124, 311, 231, 319, 124, 634, 679, 762, 826, 830, 827, 831, 87, 633, 599, 761, 679, 823, 743, 831, 125, 635, 687, 763, 827, 891, 955,1019, 122, 634, 663, 764, 634, 892, 764,1020, 126, 638, 695, 766, 830, 894, 958,1021, 123, 635, 671, 765, 762, 893, 957,1021, 127, 639, 703, 767, 831, 895, 959,1023, 3, 19, 21, 30, 11, 27, 29, 31, 19, 90, 92, 94, 83, 91, 93, 95, 21, 92, 228, 229, 99, 220, 230, 231, 30, 94, 229, 237, 115, 221, 238, 239, 11, 83, 99, 115, 75, 91, 107, 123, 27, 91, 220, 221, 91, 219, 221, 223, 29, 93, 230, 238, 107, 221, 246, 247, 31, 95, 231, 239, 123, 223, 247, 255, 22, 108, 106, 110, 86, 109, 107, 111, 293, 602, 604, 606, 601, 603, 605, 607, 116, 737, 626, 741, 678, 739, 742, 743, 302, 745, 748, 749, 746, 747, 750, 751, 102, 666, 661, 670, 598, 667, 669, 671, 301, 729, 732, 733, 729, 731, 733, 735, 230, 753, 756, 757, 742, 755, 758, 759, 303, 761, 764, 765, 762, 763, 766, 767, 22, 293, 116, 302, 102, 301, 230, 303, 108, 602, 737, 745, 666, 729, 753, 761, 106, 604, 626, 748, 661, 732, 756, 764, 110, 606, 741, 749, 670, 733, 757, 765, 86, 601, 678, 746, 598, 729, 742, 762, 109, 603, 739, 747, 667, 731, 755, 763, 107, 605, 742, 750, 669, 733, 758, 766, 111, 607, 743, 751, 671, 735, 759, 767, 54, 310, 118, 365, 118, 365, 246, 367, 310, 876, 822, 877, 822, 877, 875, 879, 118, 822, 630, 886, 694, 949, 758, 957, 365, 877, 886, 1755, 949,1757,1758,1759, 118, 822, 694, 949, 630, 886, 758, 957, 365, 877, 949,1757, 886,1755,1758,1759, 246, 875, 758,1758, 758, 1758,1782,1783, 367, 879, 957,1759, 957,1759,1783,1791, 14, 101, 85, 117, 77, 109, 93, 125, 101, 659, 675, 686, 595, 667, 683, 687, 85, 675, 625, 819, 597, 739, 753, 827, 117, 686, 819, 947, 627, 755, 883, 955, 77, 595, 597, 627, 587, 603, 605, 635, 109, 667, 739, 755, 603, 731, 747, 763, 93, 683, 753, 883, 605, 747, 875, 891, 125, 687, 827, 955, 635, 763, 891,1019, 30, 229, 115, 238, 94, 237, 221, 239, 302, 748, 746, 750, 745, 749, 747, 751, 117, 819, 627, 883, 686, 947, 755, 955, 373, 885, 885,1883, 885, 1883,1883,1887, 110, 741, 670, 757, 606, 749, 733, 765, 365, 886, 949,1758, 877,1755,1757,1759, 238, 883, 757,1907, 750,1883,1758, 1911, 375, 887, 958,1911, 893,1917,1918,1919, 30, 302, 117, 373, 110, 365, 238, 375, 229, 748, 819, 885, 741, 886, 883, 887, 115, 746, 627, 885, 670, 949, 757, 958, 238, 750, 883,1883, 757,1758, 1907,1911, 94, 745, 686, 885, 606, 877, 750, 893, 237, 749, 947, 1883, 749,1755,1883,1917, 221, 747, 755,1883, 733,1757,1758,1918, 239, 751, 955,1887, 765,1759,1911,1919, 55, 311, 119, 375, 126, 382, 247, 383, 311, 892, 823, 893, 830, 894, 891, 895, 119, 823, 631, 887, 695, 951, 759, 959, 375, 893, 887,1917, 958,1918,1911, 1919, 126, 830, 695, 958, 638, 894, 766,1021, 382, 894, 951,1918, 894,2029,1918,2031, 247, 891, 759,1911, 766,1918,1783,2039, 383, 895, 959,1919,1021,2031,2039,2047, 1, 20, 18, 22, 10, 21, 19, 23, 20, 98, 100, 102, 84, 99, 101, 103, 18, 100, 292, 293, 100, 228, 293, 295, 22, 102, 293, 301, 116, 230, 302, 303, 10, 84, 100, 116, 76, 92, 108, 124, 21, 99, 228, 230, 92, 220, 229, 231, 19, 101, 293, 302, 108, 229, 310, 311, 23, 103, 295, 303, 124, 231, 311, 319, 10, 84, 73, 86, 81, 85, 83, 87, 100, 596, 594, 598, 593, 597, 595, 599, 76, 593, 585, 601, 593, 625, 601, 633, 108, 666, 602, 729, 737, 753, 745, 761, 84, 674, 596, 678, 593, 675, 666, 679, 116, 678, 626, 742, 737, 739, 741, 743, 92, 675, 604, 746, 737, 819, 822, 823, 124, 679, 634, 762, 826, 827, 830, 831, 10, 100, 76, 108, 84, 116, 92, 124, 84, 596, 593, 666, 674, 678, 675, 679, 73, 594, 585, 602, 596, 626, 604, 634, 86, 598, 601, 729, 678, 742, 746, 762, 81, 593, 593, 737, 593, 737, 737, 826, 85, 597, 625, 753, 675, 739, 819, 827, 83, 595, 601, 745, 666, 741, 822, 830, 87, 599, 633, 761, 679, 743, 823, 831, 14, 101, 77, 109, 85, 117, 93, 125, 101, 659, 595, 667, 675, 686, 683, 687, 77, 595, 587, 603, 597, 627, 605, 635, 109, 667, 603, 731, 739, 755, 747, 763, 85, 675, 597, 739, 625, 819, 753, 827, 117, 686, 627, 755, 819, 947, 883, 955, 93, 683, 605, 747, 753, 883, 875, 891, 125, 687, 635, 763, 827, 955, 891,1019, 9, 98, 73, 106, 73, 106, 90, 122, 98, 660, 596, 661, 596, 661, 659, 663, 73, 596, 585, 604, 594, 626, 602, 634, 106, 661, 604, 732, 626, 756, 748, 764, 73, 596, 594, 626, 585, 604, 602, 634, 106, 661, 626, 756, 604, 732, 748, 764, 90, 659, 602, 748, 602, 748, 876, 892, 122, 663, 634, 764, 634, 764, 892,1020, 11, 99, 75, 107, 83, 115, 91, 123, 102, 661, 598, 669, 666, 670, 667, 671, 77, 597, 587, 605, 595, 627, 603, 635, 110, 670, 606, 733, 741, 757, 749, 765, 86, 678, 598, 742, 601, 746, 729, 762, 118, 694, 630, 758, 822, 949, 886, 957, 94, 686, 606, 750, 745, 885, 877, 893, 126, 695, 638, 766, 830, 958, 894, 1021, 11, 102, 77, 110, 86, 118, 94, 126, 99, 661, 597, 670, 678, 694, 686, 695, 75, 598, 587, 606, 598, 630, 606, 638, 107, 669, 605, 733, 742, 758, 750, 766, 83, 666, 595, 741, 601, 822, 745, 830, 115, 670, 627, 757, 746, 949, 885, 958, 91, 667, 603, 749, 729, 886, 877, 894, 123, 671, 635, 765, 762, 957, 893,1021, 15, 103, 79, 111, 87, 119, 95, 127, 103, 663, 599, 671, 679, 695, 687, 703, 79, 599, 591, 607, 599, 631, 607, 639, 111, 671, 607, 735, 743, 759, 751, 767, 87, 679, 599, 743, 633, 823, 761, 831, 119, 695, 631, 759, 823, 951, 887, 959, 95, 687, 607, 751, 761, 887, 879, 895, 127, 703, 639, 767, 831, 959, 895,1023, 3, 21, 19, 30, 11, 29, 27, 31, 22, 106, 108, 110, 86, 107, 109, 111, 22, 116, 293, 302, 102, 230, 301, 303, 54, 118, 310, 365, 118, 246, 365, 367, 14, 85, 101, 117, 77, 93, 109, 125, 30, 115, 229, 238, 94, 221, 237, 239, 30, 117, 302, 373, 110, 238, 365, 375, 55, 119, 311, 375, 126, 247, 382, 383, 19, 92, 90, 94, 83, 93, 91, 95, 293, 604, 602, 606, 601, 605, 603, 607, 108, 737, 602, 745, 666, 753, 729, 761, 310, 822, 876, 877, 822, 875, 877, 879, 101, 675, 659, 686, 595, 683, 667, 687, 302, 746, 748, 750, 745, 747, 749, 751, 229, 819, 748, 885, 741, 883, 886, 887, 311, 823, 892, 893, 830, 891, 894, 895, 21, 228, 92, 229, 99, 230, 220, 231, 116, 626, 737, 741, 678, 742, 739, 743, 106, 626, 604, 748, 661, 756, 732, 764, 118, 630, 822, 886, 694, 758, 949, 957, 85, 625, 675, 819, 597, 753, 739, 827, 117, 627, 819, 883, 686, 755, 947, 955, 115, 627, 746, 885, 670, 757, 949, 958, 119, 631, 823, 887, 695, 759, 951, 959, 30, 229, 94, 237, 115, 238, 221, 239, 302, 748, 745, 749, 746, 750, 747, 751, 110, 741, 606, 749, 670, 757, 733, 765, 365, 886, 877,1755, 949,1758, 1757,1759, 117, 819, 686, 947, 627, 883, 755, 955, 373, 885, 885, 1883, 885,1883,1883,1887, 238, 883, 750,1883, 757,1907,1758,1911, 375, 887, 893,1917, 958,1911,1918,1919, 11, 99, 83, 115, 75, 107, 91, 123, 102, 661, 666, 670, 598, 669, 667, 671, 86, 678, 601, 746, 598, 742, 729, 762, 118, 694, 822, 949, 630, 758, 886, 957, 77, 597, 595, 627, 587, 605, 603, 635, 110, 670, 741, 757, 606, 733, 749, 765, 94, 686, 745, 885, 606, 750, 877, 893, 126, 695, 830, 958, 638, 766, 894,1021, 27, 220, 91, 221, 91, 221, 219, 223, 301, 732, 729, 733, 729, 733, 731, 735, 109, 739, 603, 747, 667, 755, 731, 763, 365, 949, 877,1757, 886,1758,1755,1759, 109, 739, 667, 755, 603, 747, 731, 763, 365, 949, 886,1758, 877, 1757,1755,1759, 237, 947, 749,1883, 749,1883,1755,1917, 382, 951, 894,1918, 894,1918,2029,2031, 29, 230, 93, 238, 107, 246, 221, 247, 230, 756, 753, 757, 742, 758, 755, 759, 107, 742, 605, 750, 669, 758, 733, 766, 246, 758, 875,1758, 758,1782,1758,1783, 93, 753, 683, 883, 605, 875, 747, 891, 238, 757, 883,1907, 750,1758, 1883,1911, 221, 755, 747,1883, 733,1758,1757,1918, 247, 759, 891, 1911, 766,1783,1918,2039, 31, 231, 95, 239, 123, 247, 223, 255, 303, 764, 761, 765, 762, 766, 763, 767, 111, 743, 607, 751, 671, 759, 735, 767, 367, 957, 879,1759, 957,1783,1759,1791, 125, 827, 687, 955, 635, 891, 763,1019, 375, 958, 887,1911, 893,1918,1917, 1919, 239, 955, 751,1887, 765,1911,1759,1919, 383, 959, 895,1919, 1021,2039,2031,2047, 3, 22, 22, 54, 14, 30, 30, 55, 21, 106, 116, 118, 85, 115, 117, 119, 19, 108, 293, 310, 101, 229, 302, 311, 30, 110, 302, 365, 117, 238, 373, 375, 11, 86, 102, 118, 77, 94, 110, 126, 29, 107, 230, 246, 93, 221, 238, 247, 27, 109, 301, 365, 109, 237, 365, 382, 31, 111, 303, 367, 125, 239, 375, 383, 21, 116, 106, 118, 85, 117, 115, 119, 228, 626, 626, 630, 625, 627, 627, 631, 92, 737, 604, 822, 675, 819, 746, 823, 229, 741, 748, 886, 819, 883, 885, 887, 99, 678, 661, 694, 597, 686, 670, 695, 230, 742, 756, 758, 753, 755, 757, 759, 220, 739, 732, 949, 739, 947, 949, 951, 231, 743, 764, 957, 827, 955, 958, 959, 19, 293, 108, 310, 101, 302, 229, 311, 92, 604, 737, 822, 675, 746, 819, 823, 90, 602, 602, 876, 659, 748, 748, 892, 94, 606, 745, 877, 686, 750, 885, 893, 83, 601, 666, 822, 595, 745, 741, 830, 93, 605, 753, 875, 683, 747, 883, 891, 91, 603, 729, 877, 667, 749, 886, 894, 95, 607, 761, 879, 687, 751, 887, 895, 30, 302, 110, 365, 117, 373, 238, 375, 229, 748, 741, 886, 819, 885, 883, 887, 94, 745, 606, 877, 686, 885, 750, 893, 237, 749, 749,1755, 947,1883,1883,1917, 115, 746, 670, 949, 627, 885, 757, 958, 238, 750, 757,1758, 883,1883,1907,1911, 221, 747, 733, 1757, 755,1883,1758,1918, 239, 751, 765,1759, 955,1887,1911,1919, 11, 102, 86, 118, 77, 110, 94, 126, 99, 661, 678, 694, 597, 670, 686, 695, 83, 666, 601, 822, 595, 741, 745, 830, 115, 670, 746, 949, 627, 757, 885, 958, 75, 598, 598, 630, 587, 606, 606, 638, 107, 669, 742, 758, 605, 733, 750, 766, 91, 667, 729, 886, 603, 749, 877, 894, 123, 671, 762, 957, 635, 765, 893,1021, 29, 230, 107, 246, 93, 238, 221, 247, 230, 756, 742, 758, 753, 757, 755, 759, 93, 753, 605, 875, 683, 883, 747, 891, 238, 757, 750, 1758, 883,1907,1883,1911, 107, 742, 669, 758, 605, 750, 733, 766, 246, 758, 758,1782, 875,1758,1758,1783, 221, 755, 733,1758, 747, 1883,1757,1918, 247, 759, 766,1783, 891,1911,1918,2039, 27, 301, 109, 365, 109, 365, 237, 382, 220, 732, 739, 949, 739, 949, 947, 951, 91, 729, 603, 877, 667, 886, 749, 894, 221, 733, 747,1757, 755,1758,1883,1918, 91, 729, 667, 886, 603, 877, 749, 894, 221, 733, 755,1758, 747,1757,1883,1918, 219, 731, 731,1755, 731,1755, 1755,2029, 223, 735, 763,1759, 763,1759,1917,2031, 31, 303, 111, 367, 125, 375, 239, 383, 231, 764, 743, 957, 827, 958, 955, 959, 95, 761, 607, 879, 687, 887, 751, 895, 239, 765, 751,1759, 955, 1911,1887,1919, 123, 762, 671, 957, 635, 893, 765,1021, 247, 766, 759,1783, 891,1918,1911,2039, 223, 763, 735,1759, 763,1917,1759, 2031, 255, 767, 767,1791,1019,1919,1919,2047, 7, 23, 23, 55, 15, 31, 31, 63, 23, 122, 124, 126, 87, 123, 125, 127, 23, 124, 295, 311, 103, 231, 303, 319, 55, 126, 311, 382, 119, 247, 375, 383, 15, 87, 103, 119, 79, 95, 111, 127, 31, 123, 231, 247, 95, 223, 239, 255, 31, 125, 303, 375, 111, 239, 367, 383, 63, 127, 319, 383, 127, 255, 383, 511, 23, 124, 122, 126, 87, 125, 123, 127, 295, 634, 634, 638, 633, 635, 635, 639, 124, 826, 634, 830, 679, 827, 762, 831, 311, 830, 892, 894, 823, 891, 893, 895, 103, 679, 663, 695, 599, 687, 671, 703, 303, 762, 764, 766, 761, 763, 765, 767, 231, 827, 764, 958, 743, 955, 957, 959, 319, 831,1020,1021, 831,1019,1021,1023, 23, 295, 124, 311, 103, 303, 231, 319, 124, 634, 826, 830, 679, 762, 827, 831, 122, 634, 634, 892, 663, 764, 764,1020, 126, 638, 830, 894, 695, 766, 958,1021, 87, 633, 679, 823, 599, 761, 743, 831, 125, 635, 827, 891, 687, 763, 955,1019, 123, 635, 762, 893, 671, 765, 957,1021, 127, 639, 831, 895, 703, 767, 959,1023, 55, 311, 126, 382, 119, 375, 247, 383, 311, 892, 830, 894, 823, 893, 891, 895, 126, 830, 638, 894, 695, 958, 766,1021, 382, 894, 894,2029, 951,1918,1918,2031, 119, 823, 695, 951, 631, 887, 759, 959, 375, 893, 958,1918, 887,1917, 1911,1919, 247, 891, 766,1918, 759,1911,1783,2039, 383, 895,1021, 2031, 959,1919,2039,2047, 15, 103, 87, 119, 79, 111, 95, 127, 103, 663, 679, 695, 599, 671, 687, 703, 87, 679, 633, 823, 599, 743, 761, 831, 119, 695, 823, 951, 631, 759, 887, 959, 79, 599, 599, 631, 591, 607, 607, 639, 111, 671, 743, 759, 607, 735, 751, 767, 95, 687, 761, 887, 607, 751, 879, 895, 127, 703, 831, 959, 639, 767, 895,1023, 31, 231, 123, 247, 95, 239, 223, 255, 303, 764, 762, 766, 761, 765, 763, 767, 125, 827, 635, 891, 687, 955, 763,1019, 375, 958, 893,1918, 887,1911,1917,1919, 111, 743, 671, 759, 607, 751, 735, 767, 367, 957, 957,1783, 879,1759,1759,1791, 239, 955, 765,1911, 751,1887,1759,1919, 383, 959,1021,2039, 895, 1919,2031,2047, 31, 303, 125, 375, 111, 367, 239, 383, 231, 764, 827, 958, 743, 957, 955, 959, 123, 762, 635, 893, 671, 957, 765, 1021, 247, 766, 891,1918, 759,1783,1911,2039, 95, 761, 687, 887, 607, 879, 751, 895, 239, 765, 955,1911, 751,1759,1887,1919, 223, 763, 763,1917, 735,1759,1759,2031, 255, 767,1019,1919, 767,1791, 1919,2047, 63, 319, 127, 383, 127, 383, 255, 511, 319,1020, 831, 1021, 831,1021,1019,1023, 127, 831, 639, 895, 703, 959, 767,1023, 383,1021, 895,2031, 959,2039,1919,2047, 127, 831, 703, 959, 639, 895, 767,1023, 383,1021, 959,2039, 895,2031,1919,2047, 255,1019, 767,1919, 767,1919,1791,2047, 511,1023,1023,2047,1023,2047,2047, 4095 }; const unsigned int igraph_i_isoclass_4_idx[] = { 0, 8, 64, 512, 1, 0, 128, 1024, 2, 16, 0, 2048, 4, 32, 256, 0 }; const unsigned int igraph_i_isoclass_3u[] = { 0,1,1,3,1,3,3,7 }; const unsigned int igraph_i_isoclass_3u_idx[] = { 0,1,2,1,0,4,2,4,0 }; const unsigned int igraph_i_isoclass_4u[] = { 0, 1, 1, 3, 1, 3, 3, 7, 1, 3, 3,11,12,13,13,15, 1, 3,12,13, 3,11,13,15, 3, 7, 13,15,13,15,30,31, 1,12, 3,13, 3,13,11,15, 3,13, 7,15,13,30,15,31, 3,13,13,30, 7,15,15,31,11,15,15,31,15,31,31,63 }; const unsigned int igraph_i_isoclass_4u_idx[] = { 0, 1, 2, 8, 1, 0, 4, 16, 2, 4, 0, 32, 8, 16, 32, 0 }; const unsigned int igraph_i_isoclass2_3[] = { 0, 1, 1, 2, 1, 3, 4, 5, 1, 4, 6, 7, 2, 5, 7, 8, 1, 4, 3, 5, 6, 9, 9,10, 4,11, 9,12, 7,12,13,14, 1, 6, 4, 7, 4, 9,11,12, 3, 9, 9,13, 5,10,12,14, 2, 7, 5, 8, 7,13,12,14, 5,12,10,14, 8,14,14,15 }; const unsigned int igraph_i_isoclass2_3u[] = { 0,1,1,2,1,2,2,3 }; const unsigned int igraph_i_isoclass2_4u[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 4, 5, 6, 6, 7, 1, 2, 5, 6, 2, 4, 6, 7, 2, 3, 6, 7, 6, 7, 8, 9, 1, 5, 2, 6, 2, 6, 4, 7, 2, 6, 3, 7, 6, 8, 7, 9, 2, 6, 6, 8, 3, 7, 7, 9, 4, 7, 7, 9, 7, 9, 9,10 }; const unsigned int igraph_i_isoclass2_4[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 4, 5, 6, 5, 6, 7, 8, 1, 5, 9, 10, 11, 12, 13, 14, 2, 6, 10, 15, 12, 16, 17, 18, 1, 5, 11, 12, 9, 10, 13, 14, 2, 6, 12, 16, 10, 15, 17, 18, 2, 7, 13, 17, 13, 17, 19, 20, 3, 8, 14, 18, 14, 18, 20, 21, 1, 5, 4, 6, 5, 7, 6, 8, 9, 22, 22, 23, 24, 25, 25, 26, 5, 27, 22, 28, 29, 30, 31, 32, 10, 28, 33, 34, 35, 36, 37, 38, 11, 29, 39, 40, 41, 42, 43, 44, 13, 31, 45, 46, 47, 48, 49, 50, 12, 30, 45, 51, 52, 53, 54, 55, 14, 32, 56, 57, 58, 59, 60, 61, 1, 9, 5, 10, 11, 13, 12, 14, 5, 22, 27, 28, 29, 31, 30, 32, 4, 22, 22, 33, 39, 45, 45, 56, 6, 23, 28, 34, 40, 46, 51, 57, 5, 24, 29, 35, 41, 47, 52, 58, 7, 25, 30, 36, 42, 48, 53, 59, 6, 25, 31, 37, 43, 49, 54, 60, 8, 26, 32, 38, 44, 50, 55, 61, 2, 10, 6, 15, 12, 17, 16, 18, 10, 33, 28, 34, 35, 37, 36, 38, 6, 28, 23, 34, 40, 51, 46, 57, 15, 34, 34, 62, 63, 64, 64, 65, 12, 35, 40, 63, 66, 67, 68, 69, 17, 37, 51, 64, 67, 70, 71, 72, 16, 36, 46, 64, 68, 71, 73, 74, 18, 38, 57, 65, 69, 72, 74, 75, 1, 11, 5, 12, 9, 13, 10, 14, 11, 39, 29, 40, 41, 43, 42, 44, 5, 29, 24, 35, 41, 52, 47, 58, 12, 40, 35, 63, 66, 68, 67, 69, 9, 41, 41, 66, 76, 77, 77, 78, 13, 43, 52, 68, 77, 79, 80, 81, 10, 42, 47, 67, 77, 80, 82, 83, 14, 44, 58, 69, 78, 81, 83, 84, 2, 12, 6, 16, 10, 17, 15, 18, 13, 45, 31, 46, 47, 49, 48, 50, 7, 30, 25, 36, 42, 53, 48, 59, 17, 51, 37, 64, 67, 71, 70, 72, 13, 52, 43, 68, 77, 80, 79, 81, 19, 54, 54, 73, 82, 85, 85, 86, 17, 53, 49, 71, 80, 87, 85, 88, 20, 55, 60, 74, 83, 88, 89, 90, 2, 13, 7, 17, 13, 19, 17, 20, 12, 45, 30, 51, 52, 54, 53, 55, 6, 31, 25, 37, 43, 54, 49, 60, 16, 46, 36, 64, 68, 73, 71, 74, 10, 47, 42, 67, 77, 82, 80, 83, 17, 49, 53, 71, 80, 85, 87, 88, 15, 48, 48, 70, 79, 85, 85, 89, 18, 50, 59, 72, 81, 86, 88, 90, 3, 14, 8, 18, 14, 20, 18, 21, 14, 56, 32, 57, 58, 60, 59, 61, 8, 32, 26, 38, 44, 55, 50, 61, 18, 57, 38, 65, 69, 74, 72, 75, 14, 58, 44, 69, 78, 83, 81, 84, 20, 60, 55, 74, 83, 89, 88, 90, 18, 59, 50, 72, 81, 88, 86, 90, 21, 61, 61, 75, 84, 90, 90, 91, 1, 5, 5, 7, 4, 6, 6, 8, 9, 22, 24, 25, 22, 23, 25, 26, 11, 29, 41, 42, 39, 40, 43, 44, 13, 31, 47, 48, 45, 46, 49, 50, 5, 27, 29, 30, 22, 28, 31, 32, 10, 28, 35, 36, 33, 34, 37, 38, 12, 30, 52, 53, 45, 51, 54, 55, 14, 32, 58, 59, 56, 57, 60, 61, 9, 24, 22, 25, 22, 25, 23, 26, 76, 92, 92, 93, 92, 93, 93, 94, 41, 95, 96, 97, 98, 99,100,101, 77,102,103,104,105,106,107,108, 41, 95, 98, 99, 96, 97,100,101, 77,102,105,106, 103,104,107,108, 66,109,110,111,110,111,112,113, 78,114,115,116,115,116,117,118, 11, 41, 29, 42, 39, 43, 40, 44, 41, 96, 95, 97, 98,100, 99,101, 39, 98, 98,119, 120,121,121,122, 43,100,123,124,121,125,126,127, 29, 95,128,129, 98,123,130,131, 42, 97,129,132,119,124,133,134, 40, 99,130,133,121,126,135,136, 44,101,131,134, 122,127,136,137, 13, 47, 31, 48, 45, 49, 46, 50, 77,103,102,104,105,107,106,108, 43,123,100,124,121,126,125,127, 79,138,138,139,140,141,141,142, 52,143,130,144, 110,145,146,147, 80,148,149,150,151,152,153,154, 68,155,146,156,157,158,159,160, 81,161,162,163,164,165,166,167, 5, 29, 27, 30, 22, 31, 28, 32, 41, 98, 95, 99, 96,100, 97,101, 29,128, 95,129, 98,130,123,131, 52,130,143,144,110,146,145,147, 24, 95, 95,109, 92,102,102,114, 47,123,143,155,103,138,148,161, 35,129,143,168, 105,149,169,170, 58,131,171,172,115,162,173,174, 10, 35, 28, 36, 33, 37, 34, 38, 77,105,102,106,103,107,104,108, 42,129, 97,132,119,133,124,134, 80,149,148,150, 151,153,152,154, 47,143,123,155,103,148,138,161, 82,169,169,175,176,177,177,178, 67,168,145,179,151,180,181,182, 83,170,173,183,184,185,186,187, 12, 52, 30, 53, 45, 54, 51, 55, 66,110,109,111,110,112,111,113, 40,130, 99,133,121,135,126,136, 68,146,155,156,157,159,158,160, 35,143,129,168,105,169,149,170, 67,145,168,179, 151,181,180,182, 63,144,144,188,140,189,189,190, 69,147,172,191,164,192,193,194, 14, 58, 32, 59, 56, 60, 57, 61, 78,115,114,116,115,117,116,118, 44,131,101,134, 122,136,127,137, 81,162,161,163,164,166,165,167, 58,171,131,172,115,173,162,174, 83,173,170,183,184,186,185,187, 69,172,147,191,164,193,192,194, 84,174,174,195, 196,197,197,198, 1, 9, 11, 13, 5, 10, 12, 14, 5, 22, 29, 31, 27, 28, 30, 32, 5, 24, 41, 47, 29, 35, 52, 58, 7, 25, 42, 48, 30, 36, 53, 59, 4, 22, 39, 45, 22, 33, 45, 56, 6, 23, 40, 46, 28, 34, 51, 57, 6, 25, 43, 49, 31, 37, 54, 60, 8, 26, 44, 50, 32, 38, 55, 61, 11, 41, 39, 43, 29, 42, 40, 44, 41, 96, 98,100, 95, 97, 99,101, 29, 95, 98,123,128,129,130,131, 42, 97,119,124,129,132,133,134, 39, 98,120,121, 98,119,121,122, 43,100,121,125,123,124,126,127, 40, 99,121,126, 130,133,135,136, 44,101,122,127,131,134,136,137, 9, 76, 41, 77, 41, 77, 66, 78, 24, 92, 95,102, 95,102,109,114, 22, 92, 96,103, 98,105,110,115, 25, 93, 97,104, 99,106,111,116, 22, 92, 98,105, 96,103,110,115, 25, 93, 99,106, 97,104,111,116, 23, 93,100,107,100,107,112,117, 26, 94,101,108,101,108,113,118, 13, 77, 43, 79, 52, 80, 68, 81, 47,103,123,138,143,148,155,161, 31,102,100,138,130,149,146,162, 48,104,124,139,144,150,156,163, 45,105,121,140,110,151,157,164, 49,107,126,141, 145,152,158,165, 46,106,125,141,146,153,159,166, 50,108,127,142,147,154,160,167, 5, 41, 29, 52, 24, 47, 35, 58, 29, 98,128,130, 95,123,129,131, 27, 95, 95,143, 95,143,143,171, 30, 99,129,144,109,155,168,172, 22, 96, 98,110, 92,103,105,115, 31,100,130,146,102,138,149,162, 28, 97,123,145,102,148,169,173, 32,101,131,147, 114,161,170,174, 12, 66, 40, 68, 35, 67, 63, 69, 52,110,130,146,143,145,144,147, 30,109, 99,155,129,168,144,172, 53,111,133,156,168,179,188,191, 45,110,121,157, 105,151,140,164, 54,112,135,159,169,181,189,192, 51,111,126,158,149,180,189,193, 55,113,136,160,170,182,190,194, 10, 77, 42, 80, 47, 82, 67, 83, 35,105,129,149, 143,169,168,170, 28,102, 97,148,123,169,145,173, 36,106,132,150,155,175,179,183, 33,103,119,151,103,176,151,184, 37,107,133,153,148,177,180,185, 34,104,124,152, 138,177,181,186, 38,108,134,154,161,178,182,187, 14, 78, 44, 81, 58, 83, 69, 84, 58,115,131,162,171,173,172,174, 32,114,101,161,131,170,147,174, 59,116,134,163, 172,183,191,195, 56,115,122,164,115,184,164,196, 60,117,136,166,173,186,193,197, 57,116,127,165,162,185,192,197, 61,118,137,167,174,187,194,198, 2, 10, 12, 17, 6, 15, 16, 18, 10, 33, 35, 37, 28, 34, 36, 38, 12, 35, 66, 67, 40, 63, 68, 69, 17, 37, 67, 70, 51, 64, 71, 72, 6, 28, 40, 51, 23, 34, 46, 57, 15, 34, 63, 64, 34, 62, 64, 65, 16, 36, 68, 71, 46, 64, 73, 74, 18, 38, 69, 72, 57, 65, 74, 75, 13, 47, 45, 49, 31, 48, 46, 50, 77,103,105,107,102,104,106,108, 52,143,110,145, 130,144,146,147, 80,148,151,152,149,150,153,154, 43,123,121,126,100,124,125,127, 79,138,140,141,138,139,141,142, 68,155,157,158,146,156,159,160, 81,161,164,165, 162,163,166,167, 13, 77, 52, 80, 43, 79, 68, 81, 47,103,143,148,123,138,155,161, 45,105,110,151,121,140,157,164, 49,107,145,152,126,141,158,165, 31,102,130,149, 100,138,146,162, 48,104,144,150,124,139,156,163, 46,106,146,153,125,141,159,166, 50,108,147,154,127,142,160,167, 19, 82, 54, 85, 54, 85, 73, 86, 82,176,169,177, 169,177,175,178, 54,169,112,181,135,189,159,192, 85,177,181,199,189,200,201,202, 54,169,135,189,112,181,159,192, 85,177,189,200,181,199,201,202, 73,175,159,201, 159,201,203,204, 86,178,192,202,192,202,204,205, 7, 42, 30, 53, 25, 48, 36, 59, 42,119,129,133, 97,124,132,134, 30,129,109,168, 99,144,155,172, 53,133,168,188, 111,156,179,191, 25, 97, 99,111, 93,104,106,116, 48,124,144,156,104,139,150,163, 36,132,155,179,106,150,175,183, 59,134,172,191,116,163,183,195, 17, 67, 51, 71, 37, 70, 64, 72, 80,151,149,153,148,152,150,154, 53,168,111,179,133,188,156,191, 87,180,180,206,180,206,206,207, 49,145,126,158,107,152,141,165, 85,181,189,201, 177,199,200,202, 71,179,158,208,153,206,201,209, 88,182,193,209,185,210,211,212, 17, 80, 53, 87, 49, 85, 71, 88, 67,151,168,180,145,181,179,182, 51,149,111,180, 126,189,158,193, 71,153,179,206,158,201,208,209, 37,148,133,180,107,177,153,185, 70,152,188,206,152,199,206,210, 64,150,156,206,141,200,201,211, 72,154,191,207, 165,202,209,212, 20, 83, 55, 88, 60, 89, 74, 90, 83,184,170,185,173,186,183,187, 55,170,113,182,136,190,160,194, 88,185,182,210,193,211,209,212, 60,173,136,193, 117,186,166,197, 89,186,190,211,186,213,211,214, 74,183,160,209,166,211,204,215, 90,187,194,212,197,214,215,216, 1, 11, 9, 13, 5, 12, 10, 14, 11, 39, 41, 43, 29, 40, 42, 44, 9, 41, 76, 77, 41, 66, 77, 78, 13, 43, 77, 79, 52, 68, 80, 81, 5, 29, 41, 52, 24, 35, 47, 58, 12, 40, 66, 68, 35, 63, 67, 69, 10, 42, 77, 80, 47, 67, 82, 83, 14, 44, 78, 81, 58, 69, 83, 84, 5, 29, 22, 31, 27, 30, 28, 32, 41, 98, 96,100, 95, 99, 97,101, 24, 95, 92,102, 95,109,102,114, 47,123,103,138, 143,155,148,161, 29,128, 98,130, 95,129,123,131, 52,130,110,146,143,144,145,147, 35,129,105,149,143,168,169,170, 58,131,115,162,171,172,173,174, 5, 41, 24, 47, 29, 52, 35, 58, 29, 98, 95,123,128,130,129,131, 22, 96, 92,103, 98,110,105,115, 31,100,102,138,130,146,149,162, 27, 95, 95,143, 95,143,143,171, 30, 99,109,155, 129,144,168,172, 28, 97,102,148,123,145,169,173, 32,101,114,161,131,147,170,174, 7, 42, 25, 48, 30, 53, 36, 59, 42,119, 97,124,129,133,132,134, 25, 97, 93,104, 99,111,106,116, 48,124,104,139,144,156,150,163, 30,129, 99,144,109,168,155,172, 53,133,111,156,168,188,179,191, 36,132,106,150,155,179,175,183, 59,134,116,163, 172,191,183,195, 4, 39, 22, 45, 22, 45, 33, 56, 39,120, 98,121, 98,121,119,122, 22, 98, 92,105, 96,110,103,115, 45,121,105,140,110,157,151,164, 22, 98, 96,110, 92,105,103,115, 45,121,110,157,105,140,151,164, 33,119,103,151,103,151,176,184, 56,122,115,164,115,164,184,196, 6, 40, 23, 46, 28, 51, 34, 57, 43,121,100,125, 123,126,124,127, 25, 99, 93,106, 97,111,104,116, 49,126,107,141,145,158,152,165, 31,130,100,146,102,149,138,162, 54,135,112,159,169,189,181,192, 37,133,107,153, 148,180,177,185, 60,136,117,166,173,193,186,197, 6, 43, 25, 49, 31, 54, 37, 60, 40,121, 99,126,130,135,133,136, 23,100, 93,107,100,112,107,117, 46,125,106,141, 146,159,153,166, 28,123, 97,145,102,169,148,173, 51,126,111,158,149,189,180,193, 34,124,104,152,138,181,177,186, 57,127,116,165,162,192,185,197, 8, 44, 26, 50, 32, 55, 38, 61, 44,122,101,127,131,136,134,137, 26,101, 94,108,101,113,108,118, 50,127,108,142,147,160,154,167, 32,131,101,147,114,170,161,174, 55,136,113,160, 170,190,182,194, 38,134,108,154,161,182,178,187, 61,137,118,167,174,194,187,198, 2, 12, 10, 17, 6, 16, 15, 18, 13, 45, 47, 49, 31, 46, 48, 50, 13, 52, 77, 80, 43, 68, 79, 81, 19, 54, 82, 85, 54, 73, 85, 86, 7, 30, 42, 53, 25, 36, 48, 59, 17, 51, 67, 71, 37, 64, 70, 72, 17, 53, 80, 87, 49, 71, 85, 88, 20, 55, 83, 88, 60, 74, 89, 90, 10, 35, 33, 37, 28, 36, 34, 38, 77,105,103,107,102,106,104,108, 47,143,103,148,123,155,138,161, 82,169,176,177,169,175,177,178, 42,129,119,133, 97,132,124,134, 80,149,151,153,148,150,152,154, 67,168,151,180,145,179,181,182, 83,170,184,185,173,183,186,187, 12, 66, 35, 67, 40, 68, 63, 69, 52,110,143,145, 130,146,144,147, 45,110,105,151,121,157,140,164, 54,112,169,181,135,159,189,192, 30,109,129,168, 99,155,144,172, 53,111,168,179,133,156,188,191, 51,111,149,180, 126,158,189,193, 55,113,170,182,136,160,190,194, 17, 67, 37, 70, 51, 71, 64, 72, 80,151,148,152,149,153,150,154, 49,145,107,152,126,158,141,165, 85,181,177,199, 189,201,200,202, 53,168,133,188,111,179,156,191, 87,180,180,206,180,206,206,207, 71,179,153,206,158,208,201,209, 88,182,185,210,193,209,211,212, 6, 40, 28, 51, 23, 46, 34, 57, 43,121,123,126,100,125,124,127, 31,130,102,149,100,146,138,162, 54,135,169,189,112,159,181,192, 25, 99, 97,111, 93,106,104,116, 49,126,145,158, 107,141,152,165, 37,133,148,180,107,153,177,185, 60,136,173,193,117,166,186,197, 15, 63, 34, 64, 34, 64, 62, 65, 79,140,138,141,138,141,139,142, 48,144,104,150, 124,156,139,163, 85,189,177,200,181,201,199,202, 48,144,124,156,104,150,139,163, 85,189,181,201,177,200,199,202, 70,188,152,206,152,206,199,210, 89,190,186,211, 186,211,213,214, 16, 68, 36, 71, 46, 73, 64, 74, 68,157,155,158,146,159,156,160, 46,146,106,153,125,159,141,166, 73,159,175,201,159,203,201,204, 36,155,132,179, 106,175,150,183, 71,158,179,208,153,201,206,209, 64,156,150,206,141,201,200,211, 74,160,183,209,166,204,211,215, 18, 69, 38, 72, 57, 74, 65, 75, 81,164,161,165, 162,166,163,167, 50,147,108,154,127,160,142,167, 86,192,178,202,192,204,202,205, 59,172,134,191,116,183,163,195, 88,193,182,209,185,211,210,212, 72,191,154,207, 165,209,202,212, 90,194,187,212,197,215,214,216, 2, 13, 13, 19, 7, 17, 17, 20, 12, 45, 52, 54, 30, 51, 53, 55, 10, 47, 77, 82, 42, 67, 80, 83, 17, 49, 80, 85, 53, 71, 87, 88, 6, 31, 43, 54, 25, 37, 49, 60, 16, 46, 68, 73, 36, 64, 71, 74, 15, 48, 79, 85, 48, 70, 85, 89, 18, 50, 81, 86, 59, 72, 88, 90, 12, 52, 45, 54, 30, 53, 51, 55, 66,110,110,112,109,111,111,113, 35,143,105,169,129,168,149,170, 67,145,151,181,168,179,180,182, 40,130,121,135, 99,133,126,136, 68,146,157,159, 155,156,158,160, 63,144,140,189,144,188,189,190, 69,147,164,192,172,191,193,194, 10, 77, 47, 82, 42, 80, 67, 83, 35,105,143,169,129,149,168,170, 33,103,103,176, 119,151,151,184, 37,107,148,177,133,153,180,185, 28,102,123,169, 97,148,145,173, 36,106,155,175,132,150,179,183, 34,104,138,177,124,152,181,186, 38,108,161,178, 134,154,182,187, 17, 80, 49, 85, 53, 87, 71, 88, 67,151,145,181,168,180,179,182, 37,148,107,177,133,180,153,185, 70,152,152,199,188,206,206,210, 51,149,126,189, 111,180,158,193, 71,153,158,201,179,206,208,209, 64,150,141,200,156,206,201,211, 72,154,165,202,191,207,209,212, 6, 43, 31, 54, 25, 49, 37, 60, 40,121,130,135, 99,126,133,136, 28,123,102,169, 97,145,148,173, 51,126,149,189,111,158,180,193, 23,100,100,112, 93,107,107,117, 46,125,146,159,106,141,153,166, 34,124,138,181, 104,152,177,186, 57,127,162,192,116,165,185,197, 16, 68, 46, 73, 36, 71, 64, 74, 68,157,146,159,155,158,156,160, 36,155,106,175,132,179,150,183, 71,158,153,201, 179,208,206,209, 46,146,125,159,106,153,141,166, 73,159,159,203,175,201,201,204, 64,156,141,201,150,206,200,211, 74,160,166,204,183,209,211,215, 15, 79, 48, 85, 48, 85, 70, 89, 63,140,144,189,144,189,188,190, 34,138,104,177,124,181,152,186, 64,141,150,200,156,201,206,211, 34,138,124,181,104,177,152,186, 64,141,156,201, 150,200,206,211, 62,139,139,199,139,199,199,213, 65,142,163,202,163,202,210,214, 18, 81, 50, 86, 59, 88, 72, 90, 69,164,147,192,172,193,191,194, 38,161,108,178, 134,182,154,187, 72,165,154,202,191,209,207,212, 57,162,127,192,116,185,165,197, 74,166,160,204,183,211,209,215, 65,163,142,202,163,210,202,214, 75,167,167,205, 195,212,212,216, 3, 14, 14, 20, 8, 18, 18, 21, 14, 56, 58, 60, 32, 57, 59, 61, 14, 58, 78, 83, 44, 69, 81, 84, 20, 60, 83, 89, 55, 74, 88, 90, 8, 32, 44, 55, 26, 38, 50, 61, 18, 57, 69, 74, 38, 65, 72, 75, 18, 59, 81, 88, 50, 72, 86, 90, 21, 61, 84, 90, 61, 75, 90, 91, 14, 58, 56, 60, 32, 59, 57, 61, 78,115,115,117, 114,116,116,118, 58,171,115,173,131,172,162,174, 83,173,184,186,170,183,185,187, 44,131,122,136,101,134,127,137, 81,162,164,166,161,163,165,167, 69,172,164,193, 147,191,192,194, 84,174,196,197,174,195,197,198, 14, 78, 58, 83, 44, 81, 69, 84, 58,115,171,173,131,162,172,174, 56,115,115,184,122,164,164,196, 60,117,173,186, 136,166,193,197, 32,114,131,170,101,161,147,174, 59,116,172,183,134,163,191,195, 57,116,162,185,127,165,192,197, 61,118,174,187,137,167,194,198, 20, 83, 60, 89, 55, 88, 74, 90, 83,184,173,186,170,185,183,187, 60,173,117,186,136,193,166,197, 89,186,186,213,190,211,211,214, 55,170,136,190,113,182,160,194, 88,185,193,211, 182,210,209,212, 74,183,166,211,160,209,204,215, 90,187,197,214,194,212,215,216, 8, 44, 32, 55, 26, 50, 38, 61, 44,122,131,136,101,127,134,137, 32,131,114,170, 101,147,161,174, 55,136,170,190,113,160,182,194, 26,101,101,113, 94,108,108,118, 50,127,147,160,108,142,154,167, 38,134,161,182,108,154,178,187, 61,137,174,194, 118,167,187,198, 18, 69, 57, 74, 38, 72, 65, 75, 81,164,162,166,161,165,163,167, 59,172,116,183,134,191,163,195, 88,193,185,211,182,209,210,212, 50,147,127,160, 108,154,142,167, 86,192,192,204,178,202,202,205, 72,191,165,209,154,207,202,212, 90,194,197,215,187,212,214,216, 18, 81, 59, 88, 50, 86, 72, 90, 69,164,172,193, 147,192,191,194, 57,162,116,185,127,192,165,197, 74,166,183,211,160,204,209,215, 38,161,134,182,108,178,154,187, 72,165,191,209,154,202,207,212, 65,163,163,210, 142,202,202,214, 75,167,195,212,167,205,212,216, 21, 84, 61, 90, 61, 90, 75, 91, 84,196,174,197,174,197,195,198, 61,174,118,187,137,194,167,198, 90,197,187,214, 194,215,212,216, 61,174,137,194,118,187,167,198, 90,197,194,215,187,214,212,216, 75,195,167,212,167,212,205,216, 91,198,198,216,198,216,216,217 }; const unsigned int igraph_i_isographs_3[] = { 0, 1, 3, 5, 6, 7, 10, 11, 15, 21, 23, 25, 27, 30, 31, 63 }; const unsigned int igraph_i_isographs_3u[] = { 0, 1, 3, 7 }; const unsigned int igraph_i_isographs_4[] = { 0, 1, 3, 7, 9, 10, 11, 14, 15, 18, 19, 20, 21, 22, 23, 27, 29, 30, 31, 54, 55, 63, 73, 75, 76, 77, 79, 81, 83, 84, 85, 86, 87, 90, 91, 92, 93, 94, 95, 98, 99, 100, 101, 102, 103, 106, 107, 108, 109, 110, 111, 115, 116, 117, 118, 119, 122, 123, 124, 125, 126, 127, 219, 220, 221, 223, 228, 229, 230, 231, 237, 238, 239, 246, 247, 255, 292, 293, 295, 301, 302, 303, 310, 311, 319, 365, 367, 373, 375, 382, 383, 511, 585, 587, 591, 593, 594, 595, 596, 597, 598, 599, 601, 602, 603, 604, 605, 606, 607, 625, 626, 627, 630, 631, 633, 634, 635, 638, 639, 659, 660, 661, 663, 666, 667, 669, 670, 671, 674, 675, 678, 679, 683, 686, 687, 694, 695, 703, 729, 731, 732, 733, 735, 737, 739, 741, 742, 743, 745, 746, 747, 748, 749, 750, 751, 753, 755, 756, 757, 758, 759, 761, 762, 763, 764, 765, 766, 767, 819, 822, 823, 826, 827, 830, 831, 875, 876, 877, 879, 883, 885, 886, 887, 891, 892, 893, 894, 895, 947, 949, 951, 955, 957, 958, 959, 1019, 1020, 1021, 1023, 1755, 1757, 1758, 1759, 1782, 1783, 1791, 1883, 1887, 1907, 1911, 1917, 1918, 1919, 2029, 2031, 2039, 2047, 4095}; const unsigned int igraph_i_isographs_4u[] = { 0, 1, 3, 7, 11, 12, 13, 15, 30, 31, 63}; const unsigned int igraph_i_classedges_3[] = { 1,2, 0,2, 2,1, 0,1, 2,0, 1,0 }; const unsigned int igraph_i_classedges_3u[] = { 1,2, 0,2, 0,1 }; const unsigned int igraph_i_classedges_4[] = { 2,3, 1,3, 0,3, 3,2, 1,2, 0,2, 3,1, 2,1, 0,1, 3,0, 2,0, 1,0 }; const unsigned int igraph_i_classedges_4u[] = { 2,3, 1,3, 0,3, 1,2, 0,2, 0,1 }; /** * \section about_graph_isomorphism * * igraph provides four set of functions to deal with graph * isomorphism problems. * * The \ref igraph_isomorphic() and \ref igraph_subisomorphic() * functions make up the first set (in addition with the \ref * igraph_permute_vertices() function). These functions choose the * algorithm which is best for the supplied input graph. (The choice is * not very sophisticated though, see their documentation for * details.) * * The VF2 graph (and subgraph) isomorphism algorithm is implemented in * igraph, these functions are the second set. See \ref * igraph_isomorphic_vf2() and \ref igraph_subisomorphic_vf2() for * starters. * * Functions for the BLISS algorithm constitute the third set, * see \ref igraph_isomorphic_bliss(). This implementation only works * for undirected graphs. * * Finally, the isomorphism classes of all graphs with three and * four vertices are precomputed and stored in igraph, so for these * small graphs there is a very simple fast way to decide isomorphism. * See \ref igraph_isomorphic_34(). * */ /** * \function igraph_isoclass * \brief Determine the isomorphism class of a graph with 3 or 4 vertices * * * All graphs with a given number of vertices belong to a number of * isomorphism classes, with every graph in a given class being * isomorphic to each other. * * * This function gives the isomorphism class (a number) of a * graph. Two graphs have the same isomorphism class if and only if * they are isomorphic. * * * The first isomorphism class is numbered zero and it is the empty * graph, the last isomorphism class is the full graph. The number of * isomorphism class for directed graphs with three vertices is 16 * (between 0 and 15), for undirected graph it is only 4. For graphs * with four vertices it is 218 (directed) and 11 (undirected). * * \param graph The graph object. * \param isoclass Pointer to an integer, the isomorphism class will * be stored here. * \return Error code. * \sa \ref igraph_isomorphic(), \ref igraph_isoclass_subgraph(), * \ref igraph_isoclass_create(), \ref igraph_motifs_randesu(). * * Because of some limitations this function works only for graphs * with three of four vertices. * * * Time complexity: O(|E|), the number of edges in the graph. */ int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { long int e; long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_integer_t from, to; unsigned char idx, mul; const unsigned int *arr_idx, *arr_code; int code=0; if (no_of_nodes < 3 || no_of_nodes > 4) { IGRAPH_ERROR("Only implemented for graphs with 3 or 4 vertices", IGRAPH_UNIMPLEMENTED); } if (igraph_is_directed(graph)) { if (no_of_nodes==3) { arr_idx=igraph_i_isoclass_3_idx; arr_code=igraph_i_isoclass2_3; mul=3; } else { arr_idx=igraph_i_isoclass_4_idx; arr_code=igraph_i_isoclass2_4; mul=4; } } else { if (no_of_nodes==3) { arr_idx=igraph_i_isoclass_3u_idx; arr_code=igraph_i_isoclass2_3u; mul=3; } else { arr_idx=igraph_i_isoclass_4u_idx; arr_code=igraph_i_isoclass2_4u; mul=4; } } for (e=0; e * From Wikipedia: The graph isomorphism problem or GI problem is the * graph theory problem of determining whether, given two graphs G1 * and G2, it is possible to permute (or relabel) the vertices of one * graph so that it is equal to the other. Such a permutation is * called a graph isomorphism. * * This function decides which graph isomorphism algorithm to be * used based on the input graphs. Right now it does the following: * \olist * \oli If one graph is directed and the other undirected then an * error is triggered. * \oli If the two graphs does not have the same number of vertices * and edges it returns with \c FALSE. * \oli Otherwise, if the graphs have three or four vertices then an O(1) * algorithm is used with precomputed data. * \oli Otherwise, if the graphs are directed then VF2 is used, see * \ref igraph_isomorphic_vf2(). * \oli Otherwise BLISS is used, see \ref igraph_isomorphic_bliss(). * \endolist * * * Please call the VF2 and BLISS functions directly if you need * something more sophisticated, e.g. you need the isomorphic mapping. * * \param graph1 The first graph. * \param graph2 The second graph. * \param iso Pointer to a logical variable, will be set to TRUE (1) * if the two graphs are isomorphic, and FALSE (0) otherwise. * \return Error code. * \sa \ref igraph_isoclass(), \ref igraph_isoclass_subgraph(), * \ref igraph_isoclass_create(). * * Time complexity: exponential. */ int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso) { long int nodes1=igraph_vcount(graph1), nodes2=igraph_vcount(graph2); long int edges1=igraph_ecount(graph1), edges2=igraph_ecount(graph2); igraph_bool_t dir1=igraph_is_directed(graph1), dir2=igraph_is_directed(graph2); if (dir1 != dir2) { IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL); } else if (nodes1 != nodes2 || edges1 != edges2) { *iso=0; } else if (nodes1==3 || nodes1==4) { igraph_isomorphic_34(graph1, graph2, iso); } else if (dir1) { igraph_isomorphic_vf2(graph1, graph2, 0, 0, 0, 0, iso, 0, 0, 0, 0, 0); } else { igraph_isomorphic_bliss(graph1, graph2, iso, 0, 0, /*sh1=*/0, /*sh2=*/0, 0, 0); } return 0; } /** * \function igraph_isomorphic_34 * Graph isomorphism for 3-4 vertices * * This function uses precomputed indices to decide isomorphism * problems for graphs with only 3 or 4 vertices. * \param graph1 The first input graph. * \param graph2 The second input graph. Must have the same * directedness as \p graph1. * \param iso Pointer to a boolean, the result is stored here. * \return Error code. * * Time complexity: O(1). */ int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso) { igraph_integer_t class1, class2; IGRAPH_CHECK(igraph_isoclass(graph1, &class1)); IGRAPH_CHECK(igraph_isoclass(graph2, &class2)); *iso= (class1 == class2); return 0; } /** * \function igraph_isoclass_subgraph * \brief The isomorphism class of a subgraph of a graph. * * * This function is only implemented for subgraphs with three or four * vertices. * \param graph The graph object. * \param vids A vector containing the vertex ids to be considered as * a subgraph. Each vertex id should be included at most once. * \param isoclass Pointer to an integer, this will be set to the * isomorphism class. * \return Error code. * \sa \ref igraph_isoclass(), \ref igraph_isomorphic(), * \ref igraph_isoclass_create(). * * Time complexity: O((d+n)*n), d is the average degree in the network, * and n is the number of vertices in \c vids. */ int igraph_isoclass_subgraph(const igraph_t *graph, igraph_vector_t *vids, igraph_integer_t *isoclass) { int nodes=(int) igraph_vector_size(vids); igraph_bool_t directed=igraph_is_directed(graph); igraph_vector_t neis; unsigned char mul, idx; const unsigned int *arr_idx, *arr_code; int code=0; long int i, j, s; if (nodes < 3 || nodes > 4) { IGRAPH_ERROR("Only for three- or four-vertex subgraphs", IGRAPH_UNIMPLEMENTED); } IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (directed) { if (nodes==3) { arr_idx=igraph_i_isoclass_3_idx; arr_code=igraph_i_isoclass2_3; mul=3; } else { arr_idx=igraph_i_isoclass_4_idx; arr_code=igraph_i_isoclass2_4; mul=4; } } else { if (nodes==3) { arr_idx=igraph_i_isoclass_3u_idx; arr_code=igraph_i_isoclass2_3u; mul=3; } else { arr_idx=igraph_i_isoclass_4u_idx; arr_code=igraph_i_isoclass2_4u; mul=4; } } for (i=0; i * This function is implemented only for graphs with three or four * vertices. * \param graph Pointer to an uninitialized graph object. * \param size The number of vertices to add to the graph. * \param number The isomorphism class. * \param directed Logical constant, whether to create a directed * graph. * \return Error code. * \sa \ref igraph_isoclass(), * \ref igraph_isoclass_subgraph(), * \ref igraph_isomorphic(). * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges in the graph to create. */ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, igraph_integer_t number, igraph_bool_t directed) { igraph_vector_t edges; const unsigned int *classedges; long int power; long int code; long int pos; if (size < 3 || size > 4) { IGRAPH_ERROR("Only for graphs with three of four vertices", IGRAPH_UNIMPLEMENTED); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (directed) { if (size==3) { classedges=igraph_i_classedges_3; if (number < 0 || number >= (int)(sizeof(igraph_i_isographs_3)/sizeof(unsigned int))){ IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL); } code=igraph_i_isographs_3[ (long int) number]; power=32; } else { classedges=igraph_i_classedges_4; if (number < 0 || number >= (int)(sizeof(igraph_i_isographs_4)/sizeof(unsigned int))){ IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL); } code=igraph_i_isographs_4[ (long int) number]; power=2048; } } else { if (size==3) { classedges=igraph_i_classedges_3u; if (number < 0 || number >= (int)(sizeof(igraph_i_isographs_3u)/ sizeof(unsigned int))){ IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL); } code=igraph_i_isographs_3u[ (long int) number]; power=4; } else { classedges=igraph_i_classedges_4u; if (number < 0 || number >= (int)(sizeof(igraph_i_isographs_4u)/ sizeof(unsigned int))) { IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL); } code=igraph_i_isographs_4u[ (long int) number]; power=32; } } pos=0; while (code > 0) { if (code >= power) { IGRAPH_CHECK(igraph_vector_push_back(&edges, classedges[2*pos])); IGRAPH_CHECK(igraph_vector_push_back(&edges, classedges[2*pos+1])); code -= power; } power /= 2; pos++; } IGRAPH_CHECK(igraph_create(graph, &edges, size, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_isomorphic_function_vf2 * The generic VF2 interface * * * This function is an implementation of the VF2 isomorphism algorithm, * see P. Foggia, C. Sansone, M. Vento, An Improved algorithm for * matching large graphs, Proc. of the 3rd IAPR-TC-15 International * Workshop on Graph-based Representations, Italy, 2001. * * For using it you need to define a callback function of type * \ref igraph_isohandler_t. This function will be called whenever VF2 * finds an isomorphism between the two graphs. The mapping between * the two graphs will be also provided to this function. If the * callback returns a nonzero value then the search is continued, * otherwise it stops. * \param graph1 The first input graph. * \param graph2 The second input graph. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param map12 Pointer to an initialized vector or \c NULL. If not \c * NULL and the supplied graphs are isomorphic then the permutation * taking \p graph1 to \p graph is stored here. If not \c NULL and the * graphs are not isomorphic then a zero-length vector is returned. * \param map21 This is the same as \p map12, but for the permutation * taking \p graph2 to \p graph1. * \param isohandler_fn The callback function to be called if an * isomorphism is found. See also \ref igraph_isohandler_t. * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p isohandler_fn, \p * node_compat_fn and \p edge_compat_fn. * \return Error code. * * Time complexity: exponential. */ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { long int no_of_nodes=igraph_vcount(graph1); long int no_of_edges=igraph_ecount(graph1); igraph_vector_t mycore_1, mycore_2, *core_1=&mycore_1, *core_2=&mycore_2; igraph_vector_t in_1, in_2, out_1, out_2; long int in_1_size=0, in_2_size=0, out_1_size=0, out_2_size=0; igraph_vector_t *inneis_1, *inneis_2, *outneis_1, *outneis_2; long int matched_nodes=0; long int depth; long int cand1, cand2; long int last1, last2; igraph_stack_t path; igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL); } if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) { IGRAPH_WARNING("Only one graph is vertex-colored, vertex colors will be ignored"); vertex_color1=vertex_color2=0; } if ( (edge_color1 && !edge_color2) || (!edge_color1 && edge_color2)) { IGRAPH_WARNING("Only one graph is edge-colored, edge colors will be ignored"); edge_color1 = edge_color2 = 0; } if (vertex_color1) { if (igraph_vector_int_size(vertex_color1) != no_of_nodes || igraph_vector_int_size(vertex_color2) != no_of_nodes) { IGRAPH_ERROR("Invalid vertex color vector length", IGRAPH_EINVAL); } } if (edge_color1) { if (igraph_vector_int_size(edge_color1) != no_of_edges || igraph_vector_int_size(edge_color2) != no_of_edges) { IGRAPH_ERROR("Invalid edge color vector length", IGRAPH_EINVAL); } } if (no_of_nodes != igraph_vcount(graph2) || no_of_edges != igraph_ecount(graph2)) { return 0; } /* Check color distribution */ if (vertex_color1) { int ret=0; igraph_vector_int_t tmp1, tmp2; IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, vertex_color1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, vertex_color2)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2); igraph_vector_int_sort(&tmp1); igraph_vector_int_sort(&tmp2); ret= !igraph_vector_int_all_e(&tmp1, &tmp2); igraph_vector_int_destroy(&tmp1); igraph_vector_int_destroy(&tmp2); IGRAPH_FINALLY_CLEAN(2); if (ret) { return 0; } } /* Check edge color distribution */ if (edge_color1) { int ret=0; igraph_vector_int_t tmp1, tmp2; IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, edge_color1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, edge_color2)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2); igraph_vector_int_sort(&tmp1); igraph_vector_int_sort(&tmp2); ret= !igraph_vector_int_all_e(&tmp1, &tmp2); igraph_vector_int_destroy(&tmp1); igraph_vector_int_destroy(&tmp2); IGRAPH_FINALLY_CLEAN(2); if (ret) { return 0; } } if (map12) { core_1=map12; IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes)); } else { IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes); } igraph_vector_fill(core_1, -1); if (map21) { core_2=map21; IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes)); igraph_vector_null(core_2); } else { IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes); } igraph_vector_fill(core_2, -1); IGRAPH_VECTOR_INIT_FINALLY(&in_1, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&in_2, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&out_1, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&out_2, no_of_nodes); IGRAPH_CHECK(igraph_stack_init(&path, 0)); IGRAPH_FINALLY(igraph_stack_destroy, &path); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &inadj1, IGRAPH_IN, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj2); IGRAPH_VECTOR_INIT_FINALLY(&indeg1, 0); IGRAPH_VECTOR_INIT_FINALLY(&indeg2, 0); IGRAPH_VECTOR_INIT_FINALLY(&outdeg1, 0); IGRAPH_VECTOR_INIT_FINALLY(&outdeg2, 0); IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes*2)); IGRAPH_CHECK(igraph_degree(graph1, &indeg1, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_degree(graph2, &indeg2, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_degree(graph1, &outdeg1, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_degree(graph2, &outdeg2, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); depth=0; last1=-1; last2=-1; while (depth >= 0) { long int i; IGRAPH_ALLOW_INTERRUPTION(); cand1=-1; cand2=-1; /* Search for the next pair to try */ if ((in_1_size != in_2_size) || (out_1_size != out_2_size)) { /* step back, nothing to do */ } else if (out_1_size > 0 && out_2_size > 0) { /**************************************************************/ /* cand2, search not always needed */ if (last2 >= 0) { cand2=last2; } else { i=0; while (cand2<0 && i0 && VECTOR(*core_2)[i] < 0) { cand2=i; } i++; } } /* search for cand1 now, it should be bigger than last1 */ i=last1+1; while (cand1<0 && i0 && VECTOR(*core_1)[i] < 0) { cand1=i; } i++; } } else if (in_1_size > 0 && in_2_size > 0) { /**************************************************************/ /* cand2, search not always needed */ if (last2 >= 0) { cand2=last2; } else { i=0; while (cand2<0 && i0 && VECTOR(*core_2)[i] < 0) { cand2=i; } i++; } } /* search for cand1 now, should be bigger than last1 */ i=last1+1; while (cand1<0 && i0 && VECTOR(*core_1)[i] < 0) { cand1=i; } i++; } } else { /**************************************************************/ /* cand2, search not always needed */ if (last2 >= 0) { cand2=last2; } else { i=0; while (cand2<0 && i= 1) { last2=(long int) igraph_stack_pop(&path); last1=(long int) igraph_stack_pop(&path); matched_nodes -= 1; VECTOR(*core_1)[last1]=-1; VECTOR(*core_2)[last2]=-1; if (VECTOR(in_1)[last1] != 0) { in_1_size += 1; } if (VECTOR(out_1)[last1] != 0) { out_1_size += 1; } if (VECTOR(in_2)[last2] != 0) { in_2_size += 1; } if (VECTOR(out_2)[last2] != 0) { out_2_size += 1; } inneis_1=igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); for (i=0; i=0) { long int node2=(long int) VECTOR(*core_1)[node]; /* check if there is a node2->cand2 edge */ if (!igraph_vector_binsearch2(inneis_2, node2)) { end=1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; igraph_get_eid(graph1, &eid1, (igraph_integer_t) node, (igraph_integer_t) cand1, /*directed=*/ 1, /*error=*/ 1); igraph_get_eid(graph2, &eid2, (igraph_integer_t) node2, (igraph_integer_t) cand2, /*directed=*/ 1, /*error=*/ 1); if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != VECTOR(*edge_color2)[(long int)eid2]) { end=1; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { end=1; } } } else { if (VECTOR(in_1)[node] != 0) { xin1++; } if (VECTOR(out_1)[node] != 0) { xout1++; } } } for (i=0; !end && i=0) { long int node2=(long int) VECTOR(*core_1)[node]; /* check if there is a cand2->node2 edge */ if (!igraph_vector_binsearch2(outneis_2, node2)) { end=1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1, (igraph_integer_t) node, /*directed=*/ 1, /*error=*/ 1); igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2, (igraph_integer_t) node2, /*directed=*/ 1, /*error=*/ 1); if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != VECTOR(*edge_color2)[(long int)eid2]) { end=1; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { end=1; } } } else { if (VECTOR(in_1)[node] != 0) { xin1++; } if (VECTOR(out_1)[node] != 0) { xout1++; } } } for (i=0; !end && i=0) { long int node2=(long int) VECTOR(*core_2)[node]; /* check if there is a node2->cand1 edge */ if (!igraph_vector_binsearch2(inneis_1, node2)) { end=1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; igraph_get_eid(graph1, &eid1, (igraph_integer_t) node2, (igraph_integer_t) cand1, /*directed=*/ 1, /*error=*/ 1); igraph_get_eid(graph2, &eid2, (igraph_integer_t) node, (igraph_integer_t) cand2, /*directed=*/ 1, /*error=*/ 1); if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != VECTOR(*edge_color2)[(long int)eid2]) { end=1; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { end=1; } } } else { if (VECTOR(in_2)[node] != 0) { xin2++; } if (VECTOR(out_2)[node] != 0) { xout2++; } } } for (i=0; !end && i= 0) { long int node2=(long int) VECTOR(*core_2)[node]; /* check if there is a cand1->node2 edge */ if (!igraph_vector_binsearch2(outneis_1, node2)) { end=1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1, (igraph_integer_t) node2, /*directed=*/ 1, /*error=*/ 1); igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2, (igraph_integer_t) node, /*directed=*/ 1, /*error=*/ 1); if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != VECTOR(*edge_color2)[(long int)eid2]) { end=1; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { end=1; } } } else { if (VECTOR(in_2)[node] != 0) { xin2++; } if (VECTOR(out_2)[node] != 0) { xout2++; } } } if (!end && (xin1==xin2 && xout1==xout2)) { /* Ok, we add the (cand1, cand2) pair to the mapping */ depth += 1; IGRAPH_CHECK(igraph_stack_push(&path, cand1)); IGRAPH_CHECK(igraph_stack_push(&path, cand2)); matched_nodes += 1; VECTOR(*core_1)[cand1]=cand2; VECTOR(*core_2)[cand2]=cand1; /* update in_*, out_* */ if (VECTOR(in_1)[cand1] != 0) { in_1_size -= 1; } if (VECTOR(out_1)[cand1] != 0) { out_1_size -= 1; } if (VECTOR(in_2)[cand2] != 0) { in_2_size -= 1; } if (VECTOR(out_2)[cand2] != 0) { out_2_size -= 1; } inneis_1=igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); for (i=0; inode_compat_fn(graph1, graph2, g1_num, g2_num, data->carg); } igraph_bool_t igraph_i_isocompat_edge_cb(const igraph_t *graph1, const igraph_t *graph2, const igraph_integer_t g1_num, const igraph_integer_t g2_num, void *arg) { igraph_i_iso_cb_data_t *data=arg; return data->edge_compat_fn(graph1, graph2, g1_num, g2_num, data->carg); } igraph_bool_t igraph_i_isomorphic_vf2(igraph_vector_t *map12, igraph_vector_t *map21, void *arg) { igraph_i_iso_cb_data_t *data = arg; igraph_bool_t *iso = data->arg; IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); *iso = 1; return 0; /* don't need to continue */ } /** * \function igraph_isomorphic_vf2 * \brief Isomorphism via VF2 * * * This function performs the VF2 algorithm via calling \ref * igraph_isomorphic_function_vf2(). * * Note that this function cannot be used for * deciding subgraph isomorphism, use \ref igraph_subisomorphic_vf2() * for that. * \param graph1 The first graph, may be directed or undirected. * \param graph2 The second graph. It must have the same directedness * as \p graph1, otherwise an error is reported. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param iso Pointer to a logical constant, the result of the * algorithm will be placed here. * \param map12 Pointer to an initialized vector or a NULL pointer. If not * a NULL pointer then the mapping from \p graph1 to \p graph2 is * stored here. If the graphs are not isomorphic then the vector is * cleared (ie. has zero elements). * \param map21 Pointer to an initialized vector or a NULL pointer. If not * a NULL pointer then the mapping from \p graph2 to \p graph1 is * stored here. If the graphs are not isomorphic then the vector is * cleared (ie. has zero elements). * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p node_compat_fn * and \p edge_compat_fn. * \return Error code. * * \sa \ref igraph_subisomorphic_vf2(), * \ref igraph_count_isomorphisms_vf2(), * \ref igraph_get_isomorphisms_vf2(), * * Time complexity: exponential, what did you expect? * * \example examples/simple/igraph_isomorphic_vf2.c */ int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_bool_t *iso, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, iso, arg }; igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; *iso=0; IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, map12, map21, (igraph_isohandler_t*) igraph_i_isomorphic_vf2, ncb, ecb, &data)); if (! *iso) { if (map12) { igraph_vector_clear(map12); } if (map21) { igraph_vector_clear(map21); } } return 0; } igraph_bool_t igraph_i_count_isomorphisms_vf2(const igraph_vector_t *map12, const igraph_vector_t *map21, void *arg) { igraph_i_iso_cb_data_t *data = arg; igraph_integer_t *count = data->arg; IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); *count += 1; return 1; /* always continue */ } /** * \function igraph_count_isomorphisms_vf2 * Number of isomorphisms via VF2 * * This function counts the number of isomorphic mappings between two * graphs. It uses the generic \ref igraph_isomorphic_function_vf2() * function. * \param graph1 The first input graph, may be directed or undirected. * \param graph2 The second input graph, it must have the same * directedness as \p graph1, or an error will be reported. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param count Point to an integer, the result will be stored here. * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p node_compat_fn and * \p edge_compat_fn. * \return Error code. * * Time complexity: exponential. */ int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_integer_t *count, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, count, arg }; igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; *count=0; IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, 0, 0, (igraph_isohandler_t*) igraph_i_count_isomorphisms_vf2, ncb, ecb, &data)); return 0; } void igraph_i_get_isomorphisms_free(igraph_vector_ptr_t *data) { long int i, n=igraph_vector_ptr_size(data); for (i=0; iarg; igraph_vector_t *newvector=igraph_Calloc(1, igraph_vector_t); IGRAPH_UNUSED(map12); if (!newvector) { igraph_error("Out of memory", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; /* stop right here */ } IGRAPH_FINALLY(igraph_free, newvector); IGRAPH_CHECK(igraph_vector_copy(newvector, map21)); IGRAPH_FINALLY(igraph_vector_destroy, newvector); IGRAPH_CHECK(igraph_vector_ptr_push_back(ptrvector, newvector)); IGRAPH_FINALLY_CLEAN(2); return 1; /* continue finding subisomorphisms */ } /** * \function igraph_get_isomorphisms_vf2 * Collect the isomorphic mappings * * This function finds all the isomorphic mappings between two * graphs. It uses the \ref igraph_isomorphic_function_vf2() * function. Call the function with the same graph as \p graph1 and \p * graph2 to get automorphisms. * \param graph1 The first input graph, may be directed or undirected. * \param graph2 The second input graph, it must have the same * directedness as \p graph1, or an error will be reported. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param maps Pointer vector. On return it is empty if the input graphs * are no isomorphic. Otherwise it contains pointers to * igraph_vector_t objects, each vector is an * isomorphic mapping of \p graph2 to \p graph1. Please note that * you need to 1) Destroy the vectors via \ref * igraph_vector_destroy(), 2) free them via * free() and then 3) call \ref * igraph_vector_ptr_destroy() on the pointer vector to deallocate all * memory when \p maps is no longer needed. * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p node_compat_fn * and \p edge_compat_fn. * \return Error code. * * Time complexity: exponential. */ int igraph_get_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, maps, arg }; igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; igraph_vector_ptr_clear(maps); IGRAPH_FINALLY(igraph_i_get_isomorphisms_free, maps); IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, 0, 0, (igraph_isohandler_t*) igraph_i_get_isomorphisms_vf2, ncb, ecb, &data)); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_subisomorphic * Decide subgraph isomorphism * * Check whether \p graph2 is isomorphic to a subgraph of \p graph1. * Currently this function just calls \ref igraph_subisomorphic_vf2() * for all graphs. * \param graph1 The first input graph, may be directed or * undirected. This is supposed to be the bigger graph. * \param graph2 The second input graph, it must have the same * directedness as \p graph2, or an error is triggered. This is * supposed to be the smaller graph. * \param iso Pointer to a boolean, the result is stored here. * \return Error code. * * Time complexity: exponential. */ int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso) { return igraph_subisomorphic_vf2(graph1, graph2, 0, 0, 0, 0, iso, 0, 0, 0, 0, 0); } /** * \function igraph_subisomorphic_function_vf2 * Generic VF2 function for subgraph isomorphism problems * * This function is the pair of \ref igraph_isomorphic_function_vf2(), * for subgraph isomorphism problems. It searches for subgraphs of \p * graph1 which are isomorphic to \p graph2. When it founds an * isomorphic mapping it calls the supplied callback \p isohandler_fn. * The mapping (and its inverse) and the additional \p arg argument * are supplied to the callback. * \param graph1 The first input graph, may be directed or * undirected. This is supposed to be the larger graph. * \param graph2 The second input graph, it must have the same * directedness as \p graph1. This is supposed to be the smaller * graph. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the subgraph isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param map12 Pointer to a vector or \c NULL. If not \c NULL, then an * isomorphic mapping from \p graph1 to \p graph2 is stored here. * \param map21 Pointer to a vector ot \c NULL. If not \c NULL, then * an isomorphic mapping from \p graph2 to \p graph1 is stored * here. * \param isohandler_fn A pointer to a function of type \ref * igraph_isohandler_t. This will be called whenever a subgraph * isomorphism is found. If the function returns with a non-zero value * then the search is continued, otherwise it stops and the function * returns. * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p isohandler_fn, \p * node_compat_fn and \p edge_compat_fn. * \return Error code. * * Time complexity: exponential. */ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { long int no_of_nodes1=igraph_vcount(graph1), no_of_nodes2=igraph_vcount(graph2); long int no_of_edges1=igraph_ecount(graph1), no_of_edges2=igraph_ecount(graph2); igraph_vector_t mycore_1, mycore_2, *core_1=&mycore_1, *core_2=&mycore_2; igraph_vector_t in_1, in_2, out_1, out_2; long int in_1_size=0, in_2_size=0, out_1_size=0, out_2_size=0; igraph_vector_t *inneis_1, *inneis_2, *outneis_1, *outneis_2; long int matched_nodes=0; long int depth; long int cand1, cand2; long int last1, last2; igraph_stack_t path; igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL); } if (no_of_nodes1 < no_of_nodes2 || no_of_edges1 < no_of_edges2) { return 0; } if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) { IGRAPH_WARNING("Only one graph is vertex colored, colors will be ignored"); vertex_color1=vertex_color2=0; } if ( (edge_color1 && !edge_color2) || (!edge_color1 && edge_color2) ) { IGRAPH_WARNING("Only one graph is edge colored, colors will be ignored"); edge_color1=edge_color2=0; } if (vertex_color1) { if (igraph_vector_int_size(vertex_color1) != no_of_nodes1 || igraph_vector_int_size(vertex_color2) != no_of_nodes2) { IGRAPH_ERROR("Invalid vertex color vector length", IGRAPH_EINVAL); } } if (edge_color1) { if (igraph_vector_int_size(edge_color1) != no_of_edges1 || igraph_vector_int_size(edge_color2) != no_of_edges2) { IGRAPH_ERROR("Invalid edge color vector length", IGRAPH_EINVAL); } } /* Check color distribution */ if (vertex_color1) { /* TODO */ } /* Check edge color distribution */ if (edge_color1) { /* TODO */ } if (map12) { core_1=map12; IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes1)); } else { IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes1); } igraph_vector_fill(core_1, -1); if (map21) { core_2=map21; IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes2)); } else { IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes2); } igraph_vector_fill(core_2, -1); IGRAPH_VECTOR_INIT_FINALLY(&in_1, no_of_nodes1); IGRAPH_VECTOR_INIT_FINALLY(&in_2, no_of_nodes2); IGRAPH_VECTOR_INIT_FINALLY(&out_1, no_of_nodes1); IGRAPH_VECTOR_INIT_FINALLY(&out_2, no_of_nodes2); IGRAPH_CHECK(igraph_stack_init(&path, 0)); IGRAPH_FINALLY(igraph_stack_destroy, &path); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &inadj1, IGRAPH_IN, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj2); IGRAPH_VECTOR_INIT_FINALLY(&indeg1, 0); IGRAPH_VECTOR_INIT_FINALLY(&indeg2, 0); IGRAPH_VECTOR_INIT_FINALLY(&outdeg1, 0); IGRAPH_VECTOR_INIT_FINALLY(&outdeg2, 0); IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes2*2)); IGRAPH_CHECK(igraph_degree(graph1, &indeg1, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_degree(graph2, &indeg2, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_degree(graph1, &outdeg1, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_degree(graph2, &outdeg2, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); depth=0; last1=-1; last2=-1; while (depth >= 0) { long int i; IGRAPH_ALLOW_INTERRUPTION(); cand1=-1; cand2=-1; /* Search for the next pair to try */ if ((in_1_size < in_2_size) || (out_1_size < out_2_size)) { /* step back, nothing to do */ } else if (out_1_size > 0 && out_2_size > 0) { /**************************************************************/ /* cand2, search not always needed */ if (last2 >= 0) { cand2=last2; } else { i=0; while (cand2<0 && i0 && VECTOR(*core_2)[i] < 0) { cand2=i; } i++; } } /* search for cand1 now, it should be bigger than last1 */ i=last1+1; while (cand1<0 && i0 && VECTOR(*core_1)[i] < 0) { cand1=i; } i++; } } else if (in_1_size > 0 && in_2_size > 0) { /**************************************************************/ /* cand2, search not always needed */ if (last2 >= 0) { cand2=last2; } else { i=0; while (cand2<0 && i0 && VECTOR(*core_2)[i] < 0) { cand2=i; } i++; } } /* search for cand1 now, should be bigger than last1 */ i=last1+1; while (cand1<0 && i0 && VECTOR(*core_1)[i] < 0) { cand1=i; } i++; } } else { /**************************************************************/ /* cand2, search not always needed */ if (last2 >= 0) { cand2=last2; } else { i=0; while (cand2<0 && i= 1) { last2=(long int) igraph_stack_pop(&path); last1=(long int) igraph_stack_pop(&path); matched_nodes -= 1; VECTOR(*core_1)[last1]=-1; VECTOR(*core_2)[last2]=-1; if (VECTOR(in_1)[last1] != 0) { in_1_size += 1; } if (VECTOR(out_1)[last1] != 0) { out_1_size += 1; } if (VECTOR(in_2)[last2] != 0) { in_2_size += 1; } if (VECTOR(out_2)[last2] != 0) { out_2_size += 1; } inneis_1=igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); for (i=0; i= 0) { long int node2=(long int) VECTOR(*core_2)[node]; /* check if there is a node2->cand1 edge */ if (!igraph_vector_binsearch2(inneis_1, node2)) { end=1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; igraph_get_eid(graph1, &eid1, (igraph_integer_t) node2, (igraph_integer_t) cand1, /*directed=*/ 1, /*error=*/ 1); igraph_get_eid(graph2, &eid2, (igraph_integer_t) node, (igraph_integer_t) cand2, /*directed=*/ 1, /*error=*/ 1); if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != VECTOR(*edge_color2)[(long int)eid2]) { end=1; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { end=1; } } } else { if (VECTOR(in_2)[node] != 0) { xin2++; } if (VECTOR(out_2)[node] != 0) { xout2++; } } } for (i=0; !end && i= 0) { long int node2=(long int) VECTOR(*core_2)[node]; /* check if there is a cand1->node2 edge */ if (!igraph_vector_binsearch2(outneis_1, node2)) { end=1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1, (igraph_integer_t) node2, /*directed=*/ 1, /*error=*/ 1); igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2, (igraph_integer_t) node, /*directed=*/ 1, /*error=*/ 1); if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != VECTOR(*edge_color2)[(long int)eid2]) { end=1; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { end=1; } } } else { if (VECTOR(in_2)[node] != 0) { xin2++; } if (VECTOR(out_2)[node] != 0) { xout2++; } } } if (!end && (xin1>=xin2 && xout1>=xout2)) { /* Ok, we add the (cand1, cand2) pair to the mapping */ depth += 1; IGRAPH_CHECK(igraph_stack_push(&path, cand1)); IGRAPH_CHECK(igraph_stack_push(&path, cand2)); matched_nodes += 1; VECTOR(*core_1)[cand1]=cand2; VECTOR(*core_2)[cand2]=cand1; /* update in_*, out_* */ if (VECTOR(in_1)[cand1] != 0) { in_1_size -= 1; } if (VECTOR(out_1)[cand1] != 0) { out_1_size -= 1; } if (VECTOR(in_2)[cand2] != 0) { in_2_size -= 1; } if (VECTOR(out_2)[cand2] != 0) { out_2_size -= 1; } inneis_1=igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); for (i=0; iarg; IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); *iso=1; return 0; /* stop */ } /** * \function igraph_subisomorphic_vf2 * Decide subgraph isomorphism using VF2 * * Decides whether a subgraph of \p graph1 is isomorphic to \p * graph2. It uses \ref igraph_subisomorphic_function_vf2(). * \param graph1 The first input graph, may be directed or * undirected. This is supposed to be the larger graph. * \param graph2 The second input graph, it must have the same * directedness as \p graph1. This is supposed to be the smaller * graph. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the subgraph isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param iso Pointer to a boolean. The result of the decision problem * is stored here. * \param map12 Pointer to a vector or \c NULL. If not \c NULL, then an * isomorphic mapping from \p graph1 to \p graph2 is stored here. * \param map21 Pointer to a vector ot \c NULL. If not \c NULL, then * an isomorphic mapping from \p graph2 to \p graph1 is stored * here. * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p node_compat_fn * and \p edge_compat_fn. * \return Error code. * * Time complexity: exponential. */ int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_bool_t *iso, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, iso, arg }; igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; *iso=0; IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, map12, map21, (igraph_isohandler_t *) igraph_i_subisomorphic_vf2, ncb, ecb, &data)); if (! *iso) { if (map12) { igraph_vector_clear(map12); } if (map21) { igraph_vector_clear(map21); } } return 0; } igraph_bool_t igraph_i_count_subisomorphisms_vf2(const igraph_vector_t *map12, const igraph_vector_t *map21, void *arg) { igraph_i_iso_cb_data_t *data = arg; igraph_integer_t *count = data->arg; IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); *count += 1; return 1; /* always continue */ } /** * \function igraph_count_subisomorphisms_vf2 * Number of subgraph isomorphisms using VF2 * * Count the number of isomorphisms between subgraphs of \p graph1 and * \p graph2. This function uses \ref * igraph_subisomorphic_function_vf2(). * \param graph1 The first input graph, may be directed or * undirected. This is supposed to be the larger graph. * \param graph2 The second input graph, it must have the same * directedness as \p graph1. This is supposed to be the smaller * graph. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the subgraph isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param count Pointer to an integer. The number of subgraph * isomorphisms is stored here. * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p node_compat_fn and * \p edge_compat_fn. * \return Error code. * * Time complexity: exponential. */ int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_integer_t *count, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, count, arg }; igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; *count=0; IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, 0, 0, (igraph_isohandler_t*) igraph_i_count_subisomorphisms_vf2, ncb, ecb, &data)); return 0; } void igraph_i_get_subisomorphisms_free(igraph_vector_ptr_t *data) { long int i, n=igraph_vector_ptr_size(data); for (i=0; iarg; igraph_vector_t *newvector=igraph_Calloc(1, igraph_vector_t); IGRAPH_UNUSED(map12); if (!newvector) { igraph_error("Out of memory", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; /* stop right here */ } IGRAPH_FINALLY(igraph_free, newvector); IGRAPH_CHECK(igraph_vector_copy(newvector, map21)); IGRAPH_FINALLY(igraph_vector_destroy, newvector); IGRAPH_CHECK(igraph_vector_ptr_push_back(vector, newvector)); IGRAPH_FINALLY_CLEAN(2); return 1; /* continue finding subisomorphisms */ } /** * \function igraph_get_subisomorphisms_vf2 * Return all subgraph isomorphic mappings * * This function collects all isomorphic mappings of \p graph2 to a * subgraph of \p graph1. It uses the \ref * igraph_subisomorphic_function_vf2() function. * \param graph1 The first input graph, may be directed or * undirected. This is supposed to be the larger graph. * \param graph2 The second input graph, it must have the same * directedness as \p graph1. This is supposed to be the smaller * graph. * \param vertex_color1 An optional color vector for the first graph. If * color vectors are given for both graphs, then the subgraph isomorphism is * calculated on the colored graphs; i.e. two vertices can match * only if their color also matches. Supply a null pointer here if * your graphs are not colored. * \param vertex_color2 An optional color vector for the second graph. See * the previous argument for explanation. * \param edge_color1 An optional edge color vector for the first * graph. The matching edges in the two graphs must have matching * colors as well. Supply a null pointer here if your graphs are not * edge-colored. * \param edge_color2 The edge color vector for the second graph. * \param maps Pointer vector. On return it contains pointers to * igraph_vector_t objects, each vector is an * isomorphic mapping of \p graph2 to a subgraph of \p graph1. Please note that * you need to 1) Destroy the vectors via \ref * igraph_vector_destroy(), 2) free them via * free() and then 3) call \ref * igraph_vector_ptr_destroy() on the pointer vector to deallocate all * memory when \p maps is no longer needed. * \param node_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two nodes are compatible. * \param edge_compat_fn A pointer to a function of type \ref * igraph_isocompat_t. This function will be called by the algorithm to * determine whether two edges are compatible. * \param arg Extra argument to supply to functions \p node_compat_fn * and \p edge_compat_fn. * \return Error code. * * Time complexity: exponential. */ int igraph_get_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, maps, arg }; igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; igraph_vector_ptr_clear(maps); IGRAPH_FINALLY(igraph_i_get_subisomorphisms_free, maps); IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, 0, 0, (igraph_isohandler_t*) igraph_i_get_subisomorphisms_vf2, ncb, ecb, &data)); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_permute_vertices * Permute the vertices * * This function creates a new graph from the input graph by permuting * its vertices according to the specified mapping. Call this function * with the output of \ref igraph_canonical_permutation() to create * the canonical form of a graph. * \param graph The input graph. * \param res Pointer to an uninitialized graph object. The new graph * is created here. * \param permutation The permutation to apply. Vertex 0 is mapped to * the first element of the vector, vertex 1 to the second, * etc. Note that it is not checked that the vector contains every * element only once, and no range checking is performed either. * \return Error code. * * Time complexity: O(|V|+|E|), linear in terms of the number of * vertices and edges. */ int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, const igraph_vector_t *permutation) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vector_t edges; long int i, p=0; if (igraph_vector_size(permutation) != no_of_nodes) { IGRAPH_ERROR("Permute vertices: invalid permutation vector size", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2); for (i=0; iattr) { igraph_vector_t index; igraph_vector_t vtypes; IGRAPH_I_ATTRIBUTE_DESTROY(res); IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/0, /*edge=*/1); IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); IGRAPH_CHECK(igraph_i_attribute_get_info(graph, 0, 0, 0, &vtypes, 0, 0)); if (igraph_vector_size(&vtypes) != 0) { IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_nodes); for (i=0; i * BLISS is a successor of the famous NAUTY algorithm and * implementation. While using the same ideas in general, with better * heuristics and data structure BLISS outperforms NAUTY on most * graphs. * * * * BLISS was developed and implemented by Tommi Junttila and Petteri Kaski at * Helsinki University of Technology, Finland. See Tommi Juntilla's * homepage at http://www.tcs.hut.fi/~tjunttil/ and the publication at * http://www.siam.org/proceedings/alenex/2007/alx07_013junttilat.pdf * for more information. * * * * BLISS version 0.35 is included in igraph. * */ /** * \function igraph_isomorphic_bliss * Graph isomorphism via BLISS * * This function uses the BLISS graph isomorphism algorithm, a * successor of the famous NAUTY algorithm and implementation. BLISS * is open source and licensed according to the GNU GPL. See * http://www.tcs.hut.fi/Software/bliss/index.html for * details. Currently the 0.35 version of BLISS is included in igraph. * \param graph1 The first input graph, it is assumed to be * undirected, directed graphs are treated as undirected too. * The algorithm eliminates multiple edges from the graph first. * \param graph2 The second input graph, it is assumed to be * undirected, directed graphs are treated as undirected too. * The algorithm eliminates multiple edges from the graph first. * \param iso Pointer to a boolean, the result is stored here. * \param map12 A vector or \c NULL pointer. If not \c NULL then an * isomorphic mapping from \p graph1 to \p graph2 is stored here. * If the input graphs are not isomorphic then this vector is * cleared, i.e. it will have length zero. * \param map21 Similar to \p map12, but for the mapping from \p * graph2 to \p graph1. * \param sh1 Splitting heuristics to be used for the first graph. See * \ref igraph_bliss_sh_t. * \param sh2 Splitting heuristics to be used for the second * graph. See \ref igraph_bliss_sh_t. * \param info1 If not \c NULL, information about the canonization of * the first input graph is stored here. See \ref igraph_bliss_info_t * for details. Note that if the two graphs have different number * of vertices or edges, then this is not filled. * \param info2 Same as \p info1, but for the second graph. * \return Error code. * * Time complexity: exponential, but in practice it is quite fast. */ int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso, igraph_vector_t *map12, igraph_vector_t *map21, igraph_bliss_sh_t sh1, igraph_bliss_sh_t sh2, igraph_bliss_info_t *info1, igraph_bliss_info_t *info2) { long int no_of_nodes=igraph_vcount(graph1); long int no_of_edges=igraph_ecount(graph1); igraph_vector_t perm1, perm2; igraph_vector_t vmap12, *mymap12=&vmap12; igraph_vector_t from, to, index; igraph_vector_t from2, to2, index2; long int i, j; *iso=0; if (info1) { info1->nof_nodes = info1->nof_leaf_nodes = info1->nof_bad_nodes = info1->nof_canupdates = info1->max_level = -1; info1->group_size = 0; } if (info2) { info2->nof_nodes = info2->nof_leaf_nodes = info2->nof_bad_nodes = info2->nof_canupdates = info2->max_level = -1; info2->group_size = 0; } if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL); } if (no_of_nodes != igraph_vcount(graph2) || no_of_edges != igraph_ecount(graph2)) { if (map12) { igraph_vector_clear(map12); } if (map21) { igraph_vector_clear(map21); } return 0; } if (map12) { mymap12=map12; } else { IGRAPH_VECTOR_INIT_FINALLY(mymap12, 0); } IGRAPH_VECTOR_INIT_FINALLY(&perm1, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&perm2, no_of_nodes); IGRAPH_CHECK(igraph_canonical_permutation(graph1, &perm1, sh1, info1)); IGRAPH_CHECK(igraph_canonical_permutation(graph2, &perm2, sh2, info2)); IGRAPH_CHECK(igraph_vector_resize(mymap12, no_of_nodes)); /* The inverse of perm2 is produced in mymap12 */ for (i=0; i 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE igraph_gml_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via igraph_gml_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void igraph_gml_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void igraph_gml_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_gml_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void igraph_gml_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_gml_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_gml_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void igraph_gml_yypop_buffer_state (yyscan_t yyscanner ); static void igraph_gml_yyensure_buffer_stack (yyscan_t yyscanner ); static void igraph_gml_yy_load_buffer_state (yyscan_t yyscanner ); static void igraph_gml_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER igraph_gml_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE igraph_gml_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_gml_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_gml_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *igraph_gml_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *igraph_gml_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void igraph_gml_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer igraph_gml_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ igraph_gml_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ igraph_gml_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define igraph_gml_yywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (yy_size_t) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 9 #define YY_END_OF_BUFFER 10 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[29] = { 0, 0, 0, 10, 9, 8, 7, 7, 9, 9, 3, 4, 5, 6, 1, 9, 7, 0, 2, 3, 0, 0, 4, 0, 1, 3, 0, 3, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 5, 6, 1, 1, 1, 1, 1, 1, 1, 7, 1, 8, 9, 1, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 1, 14, 1, 11, 1, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[15] = { 0, 1, 1, 1, 2, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1 } ; static yyconst flex_int16_t yy_base[32] = { 0, 0, 11, 42, 43, 43, 37, 37, 34, 28, 9, 0, 43, 43, 34, 33, 43, 30, 43, 0, 24, 15, 0, 30, 43, 14, 21, 10, 43, 26, 13, 29 } ; static yyconst flex_int16_t yy_def[32] = { 0, 28, 1, 28, 28, 28, 28, 28, 29, 28, 28, 30, 28, 28, 28, 31, 28, 29, 28, 10, 28, 28, 30, 31, 28, 28, 28, 28, 0, 28, 28, 28 } ; static yyconst flex_int16_t yy_nxt[58] = { 0, 4, 5, 6, 7, 8, 4, 4, 9, 4, 10, 11, 11, 12, 13, 14, 22, 15, 20, 19, 27, 21, 26, 26, 25, 27, 21, 17, 17, 17, 23, 27, 23, 24, 25, 18, 24, 16, 19, 18, 16, 16, 28, 3, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 } ; static yyconst flex_int16_t yy_chk[58] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 30, 2, 10, 10, 27, 10, 21, 21, 25, 21, 25, 29, 29, 29, 31, 26, 31, 23, 20, 17, 15, 14, 9, 8, 7, 6, 3, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "igraph/src/foreign-gml-lexer.l" /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #line 24 "igraph/src/foreign-gml-lexer.l" /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-gml-header.h" #include "foreign-gml-parser.h" #define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); #define YY_NO_INPUT 1 #line 518 "lex.yy.c" #define INITIAL 0 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; yy_size_t yy_n_chars; yy_size_t yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r int igraph_gml_yylex_init (yyscan_t* scanner); int igraph_gml_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int igraph_gml_yylex_destroy (yyscan_t yyscanner ); int igraph_gml_yyget_debug (yyscan_t yyscanner ); void igraph_gml_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE igraph_gml_yyget_extra (yyscan_t yyscanner ); void igraph_gml_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *igraph_gml_yyget_in (yyscan_t yyscanner ); void igraph_gml_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *igraph_gml_yyget_out (yyscan_t yyscanner ); void igraph_gml_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t igraph_gml_yyget_leng (yyscan_t yyscanner ); char *igraph_gml_yyget_text (yyscan_t yyscanner ); int igraph_gml_yyget_lineno (yyscan_t yyscanner ); void igraph_gml_yyset_lineno (int line_number ,yyscan_t yyscanner ); YYSTYPE * igraph_gml_yyget_lval (yyscan_t yyscanner ); void igraph_gml_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *igraph_gml_yyget_lloc (yyscan_t yyscanner ); void igraph_gml_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int igraph_gml_yywrap (yyscan_t yyscanner ); #else extern int igraph_gml_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ yy_size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int igraph_gml_yylex \ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int igraph_gml_yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ if ( yyleng > 0 ) \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ (yytext[yyleng - 1] == '\n'); \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; #line 81 "igraph/src/foreign-gml-lexer.l" #line 763 "lex.yy.c" yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { igraph_gml_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_gml_yy_load_buffer_state(yyscanner ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 29 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 43 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: /* rule 1 can match eol */ YY_RULE_SETUP #line 83 "igraph/src/foreign-gml-lexer.l" { /* comments ignored */ } YY_BREAK case 2: /* rule 2 can match eol */ YY_RULE_SETUP #line 85 "igraph/src/foreign-gml-lexer.l" { return STRING; } YY_BREAK case 3: YY_RULE_SETUP #line 86 "igraph/src/foreign-gml-lexer.l" { return NUM; } YY_BREAK case 4: YY_RULE_SETUP #line 87 "igraph/src/foreign-gml-lexer.l" { return KEYWORD; } YY_BREAK case 5: YY_RULE_SETUP #line 88 "igraph/src/foreign-gml-lexer.l" { return LISTOPEN; } YY_BREAK case 6: YY_RULE_SETUP #line 89 "igraph/src/foreign-gml-lexer.l" { return LISTCLOSE; } YY_BREAK case 7: /* rule 7 can match eol */ YY_RULE_SETUP #line 90 "igraph/src/foreign-gml-lexer.l" { } YY_BREAK case 8: /* rule 8 can match eol */ YY_RULE_SETUP #line 91 "igraph/src/foreign-gml-lexer.l" { /* other whitespace ignored */ } YY_BREAK case YY_STATE_EOF(INITIAL): #line 93 "igraph/src/foreign-gml-lexer.l" { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return EOFF; } } YY_BREAK case 9: YY_RULE_SETUP #line 102 "igraph/src/foreign-gml-lexer.l" ECHO; YY_BREAK #line 911 "lex.yy.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * igraph_gml_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( igraph_gml_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of igraph_gml_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { yy_size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { yy_size_t new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ igraph_gml_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; igraph_gml_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_gml_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 29 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register char *yy_cp = yyg->yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 29 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 28); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ igraph_gml_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( igraph_gml_yywrap(yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void igraph_gml_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ igraph_gml_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_gml_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); igraph_gml_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void igraph_gml_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * igraph_gml_yypop_buffer_state(); * igraph_gml_yypush_buffer_state(new_buffer); */ igraph_gml_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; igraph_gml_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (igraph_gml_yywrap()) processing, but the only time this flag * is looked at is after igraph_gml_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void igraph_gml_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE igraph_gml_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) igraph_gml_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) igraph_gml_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_create_buffer()" ); b->yy_is_our_buffer = 1; igraph_gml_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with igraph_gml_yy_create_buffer() * @param yyscanner The scanner object. */ void igraph_gml_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) igraph_gml_yyfree((void *) b->yy_ch_buf ,yyscanner ); igraph_gml_yyfree((void *) b ,yyscanner ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a igraph_gml_yyrestart() or at EOF. */ static void igraph_gml_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; igraph_gml_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then igraph_gml_yy_init_buffer was _probably_ * called from igraph_gml_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void igraph_gml_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) igraph_gml_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void igraph_gml_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; igraph_gml_yyensure_buffer_stack(yyscanner); /* This block is copied from igraph_gml_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from igraph_gml_yy_switch_to_buffer. */ igraph_gml_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void igraph_gml_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; igraph_gml_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { igraph_gml_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void igraph_gml_yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_gml_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_gml_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_gml_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) igraph_gml_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; igraph_gml_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to igraph_gml_yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * igraph_gml_yy_scan_bytes() instead. */ YY_BUFFER_STATE igraph_gml_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return igraph_gml_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to igraph_gml_yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_gml_yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n, i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) igraph_gml_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = igraph_gml_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in igraph_gml_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE igraph_gml_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int igraph_gml_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int igraph_gml_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *igraph_gml_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *igraph_gml_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ yy_size_t igraph_gml_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *igraph_gml_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void igraph_gml_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void igraph_gml_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_gml_yyset_lineno called with no buffer" , yyscanner); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void igraph_gml_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_gml_yyset_column called with no buffer" , yyscanner); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see igraph_gml_yy_switch_to_buffer */ void igraph_gml_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void igraph_gml_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int igraph_gml_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void igraph_gml_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * igraph_gml_yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void igraph_gml_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *igraph_gml_yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void igraph_gml_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* igraph_gml_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int igraph_gml_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_gml_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* igraph_gml_yylex_init_extra has the same functionality as igraph_gml_yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to igraph_gml_yyalloc in * the yyextra field. */ int igraph_gml_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; igraph_gml_yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_gml_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); igraph_gml_yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from igraph_gml_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * igraph_gml_yylex_init() */ return 0; } /* igraph_gml_yylex_destroy is for both reentrant and non-reentrant scanners. */ int igraph_gml_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ igraph_gml_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; igraph_gml_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ igraph_gml_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ igraph_gml_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * igraph_gml_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ igraph_gml_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *igraph_gml_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *igraph_gml_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void igraph_gml_yyfree (void * ptr , yyscan_t yyscanner) { free( (char *) ptr ); /* see igraph_gml_yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 102 "igraph/src/foreign-gml-lexer.l" igraph/src/foreign-lgl-lexer.c0000644000176000001440000016076012325527073016051 0ustar ripleyusers#line 2 "lex.yy.c" #line 4 "lex.yy.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE igraph_lgl_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via igraph_lgl_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void igraph_lgl_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void igraph_lgl_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_lgl_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void igraph_lgl_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_lgl_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_lgl_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void igraph_lgl_yypop_buffer_state (yyscan_t yyscanner ); static void igraph_lgl_yyensure_buffer_stack (yyscan_t yyscanner ); static void igraph_lgl_yy_load_buffer_state (yyscan_t yyscanner ); static void igraph_lgl_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER igraph_lgl_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE igraph_lgl_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_lgl_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_lgl_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *igraph_lgl_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *igraph_lgl_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void igraph_lgl_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer igraph_lgl_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ igraph_lgl_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ igraph_lgl_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define igraph_lgl_yywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (yy_size_t) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 5 #define YY_END_OF_BUFFER 6 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[13] = { 0, 2, 2, 6, 4, 2, 3, 3, 1, 4, 2, 3, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[6] = { 0, 1, 2, 3, 4, 5 } ; static yyconst flex_int16_t yy_base[17] = { 0, 0, 0, 10, 0, 0, 0, 0, 11, 0, 0, 11, 11, 8, 6, 3, 3 } ; static yyconst flex_int16_t yy_def[17] = { 0, 12, 1, 12, 13, 14, 15, 16, 12, 13, 14, 12, 0, 12, 12, 12, 12 } ; static yyconst flex_int16_t yy_nxt[17] = { 0, 4, 5, 6, 7, 8, 11, 11, 10, 9, 12, 3, 12, 12, 12, 12, 12 } ; static yyconst flex_int16_t yy_chk[17] = { 0, 1, 1, 1, 1, 1, 16, 15, 14, 13, 3, 12, 12, 12, 12, 12, 12 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "igraph/src/foreign-lgl-lexer.l" /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #line 24 "igraph/src/foreign-lgl-lexer.l" /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-lgl-header.h" #include "foreign-lgl-parser.h" #define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); #define YY_NO_INPUT 1 #line 504 "lex.yy.c" #define INITIAL 0 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; yy_size_t yy_n_chars; yy_size_t yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r int igraph_lgl_yylex_init (yyscan_t* scanner); int igraph_lgl_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int igraph_lgl_yylex_destroy (yyscan_t yyscanner ); int igraph_lgl_yyget_debug (yyscan_t yyscanner ); void igraph_lgl_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE igraph_lgl_yyget_extra (yyscan_t yyscanner ); void igraph_lgl_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *igraph_lgl_yyget_in (yyscan_t yyscanner ); void igraph_lgl_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *igraph_lgl_yyget_out (yyscan_t yyscanner ); void igraph_lgl_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t igraph_lgl_yyget_leng (yyscan_t yyscanner ); char *igraph_lgl_yyget_text (yyscan_t yyscanner ); int igraph_lgl_yyget_lineno (yyscan_t yyscanner ); void igraph_lgl_yyset_lineno (int line_number ,yyscan_t yyscanner ); YYSTYPE * igraph_lgl_yyget_lval (yyscan_t yyscanner ); void igraph_lgl_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *igraph_lgl_yyget_lloc (yyscan_t yyscanner ); void igraph_lgl_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int igraph_lgl_yywrap (yyscan_t yyscanner ); #else extern int igraph_lgl_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ yy_size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int igraph_lgl_yylex \ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int igraph_lgl_yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; #line 80 "igraph/src/foreign-lgl-lexer.l" /* --------------------------------------------------hashmark------*/ #line 747 "lex.yy.c" yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { igraph_lgl_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_lgl_yy_load_buffer_state(yyscanner ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 13 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 11 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 83 "igraph/src/foreign-lgl-lexer.l" { return HASH; } YY_BREAK /* ------------------------------------------------whitespace------*/ case 2: YY_RULE_SETUP #line 86 "igraph/src/foreign-lgl-lexer.l" { } YY_BREAK /* ---------------------------------------------------newline------*/ case 3: /* rule 3 can match eol */ YY_RULE_SETUP #line 89 "igraph/src/foreign-lgl-lexer.l" { return NEWLINE; } YY_BREAK /* ----------------------------------------------alphanumeric------*/ case 4: YY_RULE_SETUP #line 92 "igraph/src/foreign-lgl-lexer.l" { return ALNUM; } YY_BREAK case YY_STATE_EOF(INITIAL): #line 94 "igraph/src/foreign-lgl-lexer.l" { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return NEWLINE; } } YY_BREAK case 5: YY_RULE_SETUP #line 102 "igraph/src/foreign-lgl-lexer.l" ECHO; YY_BREAK #line 873 "lex.yy.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * igraph_lgl_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( igraph_lgl_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of igraph_lgl_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { yy_size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { yy_size_t new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ igraph_lgl_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; igraph_lgl_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_lgl_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 13 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register char *yy_cp = yyg->yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 13 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 12); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ igraph_lgl_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( igraph_lgl_yywrap(yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void igraph_lgl_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ igraph_lgl_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_lgl_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); igraph_lgl_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void igraph_lgl_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * igraph_lgl_yypop_buffer_state(); * igraph_lgl_yypush_buffer_state(new_buffer); */ igraph_lgl_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; igraph_lgl_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (igraph_lgl_yywrap()) processing, but the only time this flag * is looked at is after igraph_lgl_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void igraph_lgl_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE igraph_lgl_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) igraph_lgl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) igraph_lgl_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_create_buffer()" ); b->yy_is_our_buffer = 1; igraph_lgl_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with igraph_lgl_yy_create_buffer() * @param yyscanner The scanner object. */ void igraph_lgl_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) igraph_lgl_yyfree((void *) b->yy_ch_buf ,yyscanner ); igraph_lgl_yyfree((void *) b ,yyscanner ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a igraph_lgl_yyrestart() or at EOF. */ static void igraph_lgl_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; igraph_lgl_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then igraph_lgl_yy_init_buffer was _probably_ * called from igraph_lgl_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void igraph_lgl_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) igraph_lgl_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void igraph_lgl_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; igraph_lgl_yyensure_buffer_stack(yyscanner); /* This block is copied from igraph_lgl_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from igraph_lgl_yy_switch_to_buffer. */ igraph_lgl_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void igraph_lgl_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; igraph_lgl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { igraph_lgl_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void igraph_lgl_yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_lgl_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_lgl_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_lgl_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) igraph_lgl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; igraph_lgl_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to igraph_lgl_yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * igraph_lgl_yy_scan_bytes() instead. */ YY_BUFFER_STATE igraph_lgl_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return igraph_lgl_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to igraph_lgl_yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_lgl_yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n, i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) igraph_lgl_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = igraph_lgl_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in igraph_lgl_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE igraph_lgl_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int igraph_lgl_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int igraph_lgl_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *igraph_lgl_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *igraph_lgl_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ yy_size_t igraph_lgl_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *igraph_lgl_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void igraph_lgl_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void igraph_lgl_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_lgl_yyset_lineno called with no buffer" , yyscanner); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void igraph_lgl_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_lgl_yyset_column called with no buffer" , yyscanner); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see igraph_lgl_yy_switch_to_buffer */ void igraph_lgl_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void igraph_lgl_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int igraph_lgl_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void igraph_lgl_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * igraph_lgl_yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void igraph_lgl_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *igraph_lgl_yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void igraph_lgl_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* igraph_lgl_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int igraph_lgl_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_lgl_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* igraph_lgl_yylex_init_extra has the same functionality as igraph_lgl_yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to igraph_lgl_yyalloc in * the yyextra field. */ int igraph_lgl_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; igraph_lgl_yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_lgl_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); igraph_lgl_yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from igraph_lgl_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * igraph_lgl_yylex_init() */ return 0; } /* igraph_lgl_yylex_destroy is for both reentrant and non-reentrant scanners. */ int igraph_lgl_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ igraph_lgl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; igraph_lgl_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ igraph_lgl_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ igraph_lgl_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * igraph_lgl_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ igraph_lgl_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *igraph_lgl_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *igraph_lgl_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void igraph_lgl_yyfree (void * ptr , yyscan_t yyscanner) { free( (char *) ptr ); /* see igraph_lgl_yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 102 "igraph/src/foreign-lgl-lexer.l" igraph/src/igraph_centrality.h0000644000176000001440000002015312325527073016233 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_CENTRALITY_H #define IGRAPH_CENTRALITY_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_iterators.h" #include "igraph_arpack.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Centrality */ /* -------------------------------------------------- */ int igraph_closeness(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, const igraph_vector_t *weights, igraph_bool_t normalized); int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_real_t cutoff, const igraph_vector_t *weights, igraph_bool_t normalized); int igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, const igraph_vector_t *weights, igraph_bool_t nobigint); int igraph_betweenness_estimate(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t cutoff, const igraph_vector_t *weights, igraph_bool_t nobigint); int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, igraph_bool_t directed, const igraph_vector_t *weigths); int igraph_edge_betweenness_estimate(const igraph_t *graph, igraph_vector_t *result, igraph_bool_t directed, igraph_real_t cutoff, const igraph_vector_t *weights); int igraph_pagerank_old(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, igraph_integer_t niter, igraph_real_t eps, igraph_real_t damping, igraph_bool_t old); /** * \typedef igraph_pagerank_algo_t * \brief PageRank algorithm implementation * * Algorithms to calculate PageRank. * \enumval IGRAPH_PAGERANK_ALGO_POWER Use a simple power iteration, * as it was implemented before igraph version 0.5. * \enumval IGRAPH_PAGERANK_ALGO_ARPACK Use the ARPACK library, this * was the PageRank implementation in igraph from version 0.5, until * version 0.7. * \enumval IGRAPH_PAGERANK_ALGO_PRPACK Use the PRPACK * library. Currently this implementation is recommended. */ typedef enum { IGRAPH_PAGERANK_ALGO_POWER=0, IGRAPH_PAGERANK_ALGO_ARPACK=1, IGRAPH_PAGERANK_ALGO_PRPACK=2 } igraph_pagerank_algo_t; /** * \struct igraph_pagerank_power_options_t * \brief Options for the power method * * \member niter The number of iterations to perform, integer. * \member eps The algorithm will consider the calculation as complete * if the difference of values between iterations change * less than this value for every vertex. */ typedef struct igraph_pagerank_power_options_t { igraph_integer_t niter; igraph_real_t eps; } igraph_pagerank_power_options_t; int igraph_pagerank(const igraph_t *graph, igraph_pagerank_algo_t algo, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, const igraph_vector_t *weights, void *options); int igraph_personalized_pagerank(const igraph_t *graph, igraph_pagerank_algo_t algo, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vector_t *reset, const igraph_vector_t *weights, void *options); int igraph_personalized_pagerank_vs(const igraph_t *graph, igraph_pagerank_algo_t algo, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vs_t reset_vids, const igraph_vector_t *weights, void *options); int igraph_eigenvector_centrality(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t directed, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options); int igraph_hub_score(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options); int igraph_authority_score(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options); int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, const igraph_vector_t *weights); int igraph_strength(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops, const igraph_vector_t *weights); int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, igraph_vector_t *ins, igraph_vector_t *outs); int igraph_sort_vertex_ids_by_degree(const igraph_t *graph, igraph_vector_t *outvids, igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops, igraph_order_t order, igraph_bool_t only_indices); igraph_real_t igraph_centralization(const igraph_vector_t *scores, igraph_real_t theoretical_max, igraph_bool_t normalized); int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, igraph_neimode_t mode, igraph_bool_t loops, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized); int igraph_centralization_degree_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_bool_t loops, igraph_real_t *res); int igraph_centralization_betweenness(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t directed, igraph_bool_t nobigint, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized); int igraph_centralization_betweenness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_real_t *res); int igraph_centralization_closeness(const igraph_t *graph, igraph_vector_t *res, igraph_neimode_t mode, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized); int igraph_centralization_closeness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_real_t *res); int igraph_centralization_eigenvector_centrality( const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t directed, igraph_bool_t scale, igraph_arpack_options_t *options, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized); int igraph_centralization_eigenvector_centrality_tmax( const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_bool_t scale, igraph_real_t *res); __END_DECLS #endif igraph/src/igraph_gml_tree.h0000644000176000001440000000577012325527073015663 0ustar ripleyusers/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef REST_GML_TREE_H #define REST_GML_TREE_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" typedef enum { IGRAPH_I_GML_TREE_TREE=0, IGRAPH_I_GML_TREE_INTEGER, IGRAPH_I_GML_TREE_REAL, IGRAPH_I_GML_TREE_STRING, IGRAPH_I_GML_TREE_DELETED } igraph_i_gml_tree_type_t; typedef struct igraph_gml_tree_t { igraph_vector_ptr_t names; igraph_vector_char_t types; igraph_vector_ptr_t children; } igraph_gml_tree_t; int igraph_gml_tree_init_integer(igraph_gml_tree_t *t, const char *name, int namelen, igraph_integer_t value); int igraph_gml_tree_init_real(igraph_gml_tree_t *t, const char *name, int namelen, igraph_real_t value); int igraph_gml_tree_init_string(igraph_gml_tree_t *t, const char *name, int namelen, const char *value, int valuelen); int igraph_gml_tree_init_tree(igraph_gml_tree_t *t, const char *name, int namelen, igraph_gml_tree_t *value); void igraph_gml_tree_destroy(igraph_gml_tree_t *t); void igraph_gml_tree_delete(igraph_gml_tree_t *t, long int pos); int igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2); long int igraph_gml_tree_length(const igraph_gml_tree_t *t); long int igraph_gml_tree_find(const igraph_gml_tree_t *t, const char *name, long int from); long int igraph_gml_tree_findback(const igraph_gml_tree_t *t, const char *name, long int from); int igraph_gml_tree_type(const igraph_gml_tree_t *t, long int pos); const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, long int pos); igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, long int pos); igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, long int pos); const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, long int pos); igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, long int pos); #endif igraph/src/scg_approximate_methods.c0000644000176000001440000001311212325527074017424 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2011-12 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * SCGlib : A C library for the spectral coarse graining of matrices * as described in the paper: Shrinking Matrices while preserving their * eigenpairs with Application to the Spectral Coarse Graining of Graphs. * Preprint available at * * Copyright (C) 2008 David Morton de Lachapelle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * DESCRIPTION * ----------- * The intervals_method and intervals_plus_kmeans implements the * methods of sec. 5.3.2 and sec. 5.3.3 of the above reference. * They take an eigenvector 'v' as parameter and a vector 'breaks' * of length 'nb', which provide the intervals used to cut 'v'. * Then all components of 'v' that fall into the same interval are * assigned the same group label in 'gr'. The group labels are * positive consecutive integers starting from 0. * The intervals_method function is adapted from bincode of the R * base package. * The intervals_plus_kmeans is initialized with regularly-spaced * breaks, which rougly corresponds to the intervals_method. Then * kmeans minimizes iteratively the objective function until it gets * stuck in a (usually) local minimum, or until 'itermax' is reached. * So far, the breaks_computation function allows computation of * constant bins, as used in intervals_method, and of equidistant * centers as used in intervals_plus_kmeans. */ #include "igraph_error.h" #include "igraph_types.h" #include "scg_headers.h" #include "igraph_memory.h" #include "igraph_vector.h" int igraph_i_intervals_plus_kmeans(const igraph_vector_t *v, int *gr, int n, int n_interv, int maxiter) { int i; igraph_vector_t centers; IGRAPH_VECTOR_INIT_FINALLY(¢ers, n_interv); igraph_i_breaks_computation(v, ¢ers, n_interv, 2); IGRAPH_CHECK(igraph_i_kmeans_Lloyd(v, n, 1, ¢ers, n_interv, gr, maxiter)); /*renumber the groups*/ for (i=0; i= 2) { new = (hi + lo)/2; if (VECTOR(*v)[i] > VECTOR(breaks)[new] || (lft && VECTOR(*v)[i] == VECTOR(breaks)[new])) { lo = new; } else { hi = new; } } gr[i] = lo; } } igraph_vector_destroy(&breaks); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_breaks_computation(const igraph_vector_t *v, igraph_vector_t *breaks, int nb, int method) { int i; igraph_real_t eps, vmin, vmax; igraph_vector_minmax(v, &vmin, &vmax); if (vmax == vmin) { IGRAPH_ERROR("There is only one (repeated) value in argument 'v' " "of bin_size_computation()", IGRAPH_EINVAL); } if (nb < 2) { IGRAPH_ERROR("'nb' in bin_size_computation() must be >= 2", IGRAPH_EINVAL); } switch (method) { case 1: /* constant bins for fixed-size intervals method */ eps = (vmax - vmin) / (igraph_real_t)(nb-1); VECTOR(*breaks)[0] = vmin; for (i=1; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_structural.h" #include "igraph_error.h" #include "igraph_adjlist.h" #include "igraph_interface.h" /** * \function igraph_maximum_cardinality_search * Maximum cardinality search * * This function implements the maximum cardinality search algorithm * discussed in * Robert E Tarjan and Mihalis Yannakakis: Simple linear-time * algorithms to test chordality of graphs, test acyclicity of * hypergraphs, and selectively reduce acyclic hypergraphs. * SIAM Journal of Computation 13, 566--579, 1984. * * \param graph The input graph. Can be directed, but the direction * of the edges is ignored. * \param alpha Pointer to an initialized vector, the result is stored here. * It will be resized, as needed. Upon return it contains * the rank of the each vertex. * \param alpham1 Pointer to an initialized vector or a \c NULL * pointer. If not \c NULL, then the inverse of \p alpha is stored * here. * \return Error code. * * Time complexity: O(|V|+|E|), linear in terms of the number of * vertices and edges. * * \sa \ref igraph_is_chordal(). */ int igraph_maximum_cardinality_search(const igraph_t *graph, igraph_vector_t *alpha, igraph_vector_t *alpham1) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_long_t size; igraph_vector_long_t head, next, prev; /* doubly linked list with head */ long int i; igraph_adjlist_t adjlist; /***************/ /* local j, v; */ /***************/ long int j, v; IGRAPH_CHECK(igraph_vector_long_init(&size, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &size); IGRAPH_CHECK(igraph_vector_long_init(&head, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &head); IGRAPH_CHECK(igraph_vector_long_init(&next, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &next); IGRAPH_CHECK(igraph_vector_long_init(&prev, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &prev); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_vector_resize(alpha, no_of_nodes)); if (alpham1) { IGRAPH_CHECK(igraph_vector_resize(alpham1, no_of_nodes)); } /***********************************************/ /* for i in [0,n-1] -> set(i) := emptyset rof; */ /***********************************************/ /* nothing to do, 'head' contains all zeros */ /*********************************************************/ /* for v in vertices -> size(v):=0; add v to set(0) rof; */ /*********************************************************/ VECTOR(head)[0]=1; for (v=0; v=1 -> */ /**************/ while (i>=1) { long int x, k, len; igraph_vector_int_t *neis; /********************************/ /* v := delete any from set(j) */ /********************************/ v=VECTOR(head)[j]-1; x=VECTOR(next)[v]; VECTOR(head)[j]=x; if (x != 0) { VECTOR(prev)[x-1]=0; } /*************************************************/ /* alpha(v) := i; alpham1(i) := v; size(v) := -1 */ /*************************************************/ VECTOR(*alpha)[v]=i-1; if (alpham1) { VECTOR(*alpham1)[i-1]=v; } VECTOR(size)[v]=-1; /********************************************/ /* for {v,w} in E such that size(w) >= 0 -> */ /********************************************/ neis=igraph_adjlist_get(&adjlist, v); len=igraph_vector_int_size(neis); for (k=0; k= 0) { /******************************/ /* delete w from set(size(w)) */ /******************************/ long int nw=VECTOR(next)[w]; long int pw=VECTOR(prev)[w]; if (nw != 0) { VECTOR(prev)[nw-1] = pw; } if (pw != 0) { VECTOR(next)[pw-1] = nw; } else { VECTOR(head)[ws]=nw; } /******************************/ /* size(w) := size(w)+1 */ /******************************/ VECTOR(size)[w] += 1; /******************************/ /* add w to set(size(w)) */ /******************************/ ws=VECTOR(size)[w]; nw=VECTOR(head)[ws]; VECTOR(next)[w]=nw; VECTOR(prev)[w]=0; if (nw != 0) { VECTOR(prev)[nw-1]=w+1; } VECTOR(head)[ws]=w+1; } } /***********************/ /* i := i-1; j := j+1; */ /***********************/ i -= 1; j += 1; /*********************************************/ /* do j>=0 and set(j)=emptyset -> j:=j-1; od */ /*********************************************/ while (j>=0 && VECTOR(head)[j]==0) j--; } igraph_adjlist_destroy(&adjlist); igraph_vector_long_destroy(&prev); igraph_vector_long_destroy(&next); igraph_vector_long_destroy(&head); igraph_vector_long_destroy(&size); IGRAPH_FINALLY_CLEAN(5); return 0; } /** * \function igraph_is_chordal * Decides whether a graph is chordal * * A graph is chordal if each of its cycles of four or more nodes * has a chord, which is an edge joining two nodes that are not * adjacent in the cycle. An equivalent definition is that any * chordless cycles have at most three nodes. * * If either \p alpha or \p alpha1 is given, then the other is * calculated by taking simply the inverse. If neither are given, * then \ref igraph_maximum_cardinality_search() is called to calculate * them. * \param graph The input graph, it might be directed, but edge * direction is ignored. * \param alpha Either an alpha vector coming from * \ref igraph_maximum_cardinality_search() (on the same graph), or a * null pointer. * \param alpham1 Either an inverse alpha vector coming from \ref * igraph_maximum_cardinality_search() (on the same graph) or a null * pointer. * \param chordal Pointer to a boolean, the result is stored here. * \param fill_in Pointer to an initialized vector, or a null * pointer. If not a null pointer, then the fill-in of the graph is * stored here. The fill-in is the set of edges that are needed to * make the graph chordal. The vector is resized as needed. * \param newgraph Pointer to an uninitialized graph, or a null * pointer. If not a null pointer, then a new triangulated graph is * created here. This essentially means adding the fill-in edges to * the original graph. * \return Error code. * * Time complexity: O(n). * * \sa \ref igraph_maximum_cardinality_search(). */ int igraph_is_chordal(const igraph_t *graph, const igraph_vector_t *alpha, const igraph_vector_t *alpham1, igraph_bool_t *chordal, igraph_vector_t *fill_in, igraph_t *newgraph) { long int no_of_nodes=igraph_vcount(graph); const igraph_vector_t *my_alpha=alpha, *my_alpham1=alpham1; igraph_vector_t v_alpha, v_alpham1; igraph_vector_long_t f, index; long int i; igraph_adjlist_t adjlist; igraph_vector_long_t mark; igraph_bool_t calc_edges= fill_in || newgraph; igraph_vector_t *my_fill_in=fill_in, v_fill_in; /*****************/ /* local v, w, x */ /*****************/ long int v, w, x; if (!chordal && !calc_edges) { /* Nothing to calculate */ return 0; } if (!alpha && !alpham1) { IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes); my_alpha=&v_alpha; IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); my_alpham1=&v_alpham1; IGRAPH_CHECK(igraph_maximum_cardinality_search(graph, (igraph_vector_t*) my_alpha, (igraph_vector_t*) my_alpham1)); } else if (alpha && !alpham1) { long int v; IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); my_alpham1=&v_alpham1; for (v=0; v */ /*********************/ for (i=0; i */ /******************************************/ neis=igraph_adjlist_get(&adjlist, w); len=igraph_vector_int_size(neis); for (j=0; j= i) { continue; } /**********/ /* x := v */ /**********/ x=v; /********************/ /* do index(x) */ /********************/ while (VECTOR(index)[x] < i) { /******************/ /* index(x) := i; */ /******************/ VECTOR(index)[x] = i; /**********************************/ /* add {x,w} to E union F(alpha); */ /**********************************/ if (VECTOR(mark)[x] != w+1) { if (chordal) { *chordal=0; } if (my_fill_in) { IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, x)); IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, w)); } if (!calc_edges) { /* make sure that we exit from all loops */ i=no_of_nodes; j=len; break; } } /*************/ /* x := f(x) */ /*************/ x=VECTOR(f)[x]; } /* while (VECTOR(index)[x] < i) */ /*****************************/ /* if (f(x)=x -> f(x):=w; fi */ /*****************************/ if (VECTOR(f)[x] == x) { VECTOR(f)[x] = w; } } } igraph_vector_long_destroy(&mark); igraph_adjlist_destroy(&adjlist); igraph_vector_long_destroy(&index); igraph_vector_long_destroy(&f); IGRAPH_FINALLY_CLEAN(4); if (newgraph) { IGRAPH_CHECK(igraph_copy(newgraph, graph)); IGRAPH_FINALLY(igraph_destroy, newgraph); IGRAPH_CHECK(igraph_add_edges(newgraph, my_fill_in, 0)); IGRAPH_FINALLY_CLEAN(1); } if (!fill_in && newgraph) { igraph_vector_destroy(&v_fill_in); IGRAPH_FINALLY_CLEAN(1); } if (!alpha && !alpham1) { igraph_vector_destroy(&v_alpham1); igraph_vector_destroy(&v_alpha); IGRAPH_FINALLY_CLEAN(2); } else if (alpha && !alpham1) { igraph_vector_destroy(&v_alpham1); IGRAPH_FINALLY_CLEAN(1); } else if (!alpha && alpham1) { igraph_vector_destroy(&v_alpha); IGRAPH_FINALLY_CLEAN(1); } return 0; } igraph/src/RayTracer.cpp0000755000176000001440000002016512325527072014757 0ustar ripleyusers#include "RayTracer.h" #include "unit_limiter.h" #include #include namespace igraph { RayTracer::RayTracer() : mBackgroundColor(0,0,0,0), mAmbientColor(0,0,0), mEyePoint(0,0,0), mSpecularColor(1,1,1) { // begin settings mAmbientIntensity = .7; mRecursionLimit = 700; mAntiAliasDetail = 1; // end settings mRecursions = 0; mpShapes = new ShapeList; mpLights = new LightList; } RayTracer::~RayTracer() { ShapeListIterator iter1 = mpShapes->begin(); while ( iter1 != mpShapes->end() ) { delete *iter1; iter1++; } delete mpShapes; LightListIterator iter2 = mpLights->begin(); while ( iter2 != mpLights->end() ) { delete *iter2; iter2++; } delete mpLights; } void RayTracer::RayTrace(Image &result) { int mWidth=result.width; int mHeight=result.height; Ray eye_ray(mEyePoint,Vector(0,0,1)); Color draw_color; double i_inc, j_inc, anti_alias_i_inc, anti_alias_j_inc; // amount to increment the ray in each direction double i, j, anti_alias_i, anti_alias_j; // the i and j values of the ray int pixel_x, pixel_y, anti_alias_pixel_x, anti_alias_pixel_y; // the pixels being drawn double average_red_byte, average_green_byte, average_blue_byte, average_trans_byte; int anti_alias_count; // the number of anti aliases (used in averaging) int idx=0; i_inc = 2.0/(double)mWidth; j_inc = 2.0/(double)mHeight; anti_alias_i_inc = 1.0/(double)mAntiAliasDetail; anti_alias_j_inc = 1.0/(double)mAntiAliasDetail; pixel_y = 0; j = 1.0; for (; pixel_y < mHeight; j -= j_inc, pixel_y++) { pixel_x = 0; i = -1.0; for (; pixel_x < mWidth; i += i_inc, pixel_x++) { anti_alias_pixel_y = 0; anti_alias_j = 0.0; average_red_byte = 0; average_green_byte = 0; average_blue_byte = 0; average_trans_byte = 0; anti_alias_count = 0; for (; anti_alias_pixel_y < mAntiAliasDetail; anti_alias_j += anti_alias_j_inc, anti_alias_pixel_y++) { anti_alias_pixel_x = 0; anti_alias_i = 0.0; for (; anti_alias_pixel_x < mAntiAliasDetail; anti_alias_i += anti_alias_i_inc, anti_alias_pixel_x++) { anti_alias_count++; eye_ray.Direction( Vector(i+(anti_alias_i*i_inc),j+(anti_alias_j*j_inc),1.0) ); draw_color = Render(eye_ray); average_red_byte = average_red_byte + ((double)draw_color.RedByte() - average_red_byte)/(double)anti_alias_count; average_green_byte = average_green_byte + ((double)draw_color.GreenByte() - average_green_byte)/(double)anti_alias_count; average_blue_byte = average_blue_byte + ((double)draw_color.BlueByte() - average_blue_byte)/(double)anti_alias_count; average_trans_byte = average_trans_byte + ((double)draw_color.TransparentByte() - average_trans_byte)/(double)anti_alias_count; } } result.red [idx] = average_red_byte/255; result.green[idx] = average_green_byte/255; result.blue [idx] = average_blue_byte/255; result.trans[idx] = average_trans_byte/255; idx++; } } } Color RayTracer::Render(const Ray& rRay, bool vIsReflecting, const Shape* pReflectingFrom ) { mRecursions++; Shape* closest_shape; Point intersect_point; Color result; if (vIsReflecting) closest_shape = QueryScene(rRay, intersect_point, vIsReflecting, pReflectingFrom); else closest_shape = QueryScene(rRay, intersect_point); if (closest_shape == NULL && !vIsReflecting) { mRecursions = 0; return mBackgroundColor; } if (closest_shape == NULL && vIsReflecting) { mRecursions = 0; return mAmbientColor*mAmbientIntensity; } if ( mRecursions > mRecursionLimit ) { mRecursions = 0; return Color(0,0,0); // mAmbientColor*mAmbientIntensity; } result = closest_shape->ShapeColor()*Shade(closest_shape, intersect_point); Ray backwards_ray(intersect_point,rRay.Direction()*-1); if ( closest_shape->DiffuseReflectivity() > 0.0 ) result = result + (Render( closest_shape->Reflect(intersect_point,backwards_ray), true, closest_shape )*closest_shape->DiffuseReflectivity()); return (result + mSpecularColor); } double RayTracer::Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade) { double intensity = mAmbientIntensity * pShapeToShade->AmbientReflectivity(); // the ambient intensity of the scene Ray light_ray; // the ray that goes from the intersection point to the light sources double dot_product; Shape* closest_shape; // the shape closest from the intersection point to the light source Point light_intersect; // the intersection point of the ray that goes from the intersection point to the light source light_ray.Origin(rPointOnShapeToShade); // lightRay. org= object. intersect; Ray light_ray_from_light; LightListIterator iter = mpLights->begin(); mSpecularColor.Red(0); mSpecularColor.Green(0); mSpecularColor.Blue(0); while ( iter != mpLights->end() ) // foreach light in LightList do { light_ray.Direction(Vector(rPointOnShapeToShade,(*iter)->LightPoint())); // lightRay. dir= light. dir light_ray_from_light.Origin((*iter)->LightPoint()); light_ray_from_light.Direction(Vector((*iter)->LightPoint(),rPointOnShapeToShade)); closest_shape = QueryScene(light_ray_from_light, light_intersect); if ( closest_shape == NULL || (closest_shape == pShapeToShade && light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) ) //if (QueryScene( lightRay)= NIL) { Vector normal_vector = pShapeToShade->Normal(rPointOnShapeToShade, Point() ); dot_product = normal_vector.Dot(light_ray.Direction().Normalize()); dot_product *= (*iter)->Intensity(); if (dot_product < 0.0) { if (pShapeToShade->Type() == "Triangle") dot_product = dot_product*-1.0; else dot_product = 0.0; } intensity = unit_limiter( intensity + dot_product ); if ( light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) { double specular = Specular(pShapeToShade, rPointOnShapeToShade, *iter); mSpecularColor = mSpecularColor + Color(specular,specular,specular); } } iter++; } return intensity; } double RayTracer::Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight) { Ray reflected = pShapeToShade->Reflect(rPointOnShapeToShade,Ray(rPointOnShapeToShade, pLight->LightPoint())); Vector eye_vector(rPointOnShapeToShade, mEyePoint); Vector reflected_vector = reflected.Direction().Normalize(); eye_vector.NormalizeThis(); double dot_product = eye_vector.Dot(reflected_vector); int n = pShapeToShade->SpecularSize(); double specular_intensity = dot_product/(n - n*dot_product+ dot_product); return unit_limiter(specular_intensity*pLight->Intensity()); } Shape* RayTracer::QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting, const Shape* pReflectingFrom) { Shape* closest_shape = NULL; Point intersect_point; double closest_distance; double intersect_distance; bool found_intersection = false; ShapeListIterator iter = mpShapes->begin(); while ( iter != mpShapes->end() ) { if ( (*iter)->Intersect( rRay, intersect_point ) ) { intersect_distance = intersect_point.Distance(rRay.Origin()); if ( !found_intersection && (*iter) != pReflectingFrom) { found_intersection = true; rIntersectionPoint = intersect_point; closest_shape = *iter; closest_distance = intersect_distance; } else if ( intersect_distance < closest_distance && (*iter) != pReflectingFrom ) { rIntersectionPoint = intersect_point; closest_shape = *iter; closest_distance = intersect_distance; } } iter++; } return closest_shape; } void RayTracer::AddShape(Shape* pShape) { // should check if a shape with the same name already exists mpShapes->push_back(pShape); } void RayTracer::AddLight(Light* pLight) { // should check if a shape with the same name already exists mpLights->push_back(pLight); } void RayTracer::BackgroundColor(const Color& rBackgroundColor) { mBackgroundColor = rBackgroundColor; } void RayTracer::EyePoint(const Point& rEyePoint) { mEyePoint = rEyePoint; } void RayTracer::AmbientColor(const Color& rAmbientColor) { mAmbientColor = rAmbientColor; } void RayTracer::AmbientIntensity(double vAmbientIntensity) { mAmbientIntensity = unit_limiter(vAmbientIntensity); } } // namespace igraph igraph/src/igraph_hacks_internal.h0000644000176000001440000000274412325527073017050 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_HACKS_INTERNAL_H #define IGRAPH_HACKS_INTERNAL_H #include "config.h" #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS #ifndef HAVE_STRDUP # define strdup igraph_i_strdup char* igraph_i_strdup(const char *s); #endif #ifndef HAVE_STPCPY # define stpcpy igraph_i_stpcpy char* igraph_i_stpcpy(char* s1, const char* s2); #else # ifndef HAVE_STPCPY_SIGNATURE char* stpcpy(char* s1, const char* s2); # endif #endif __END_DECLS #endif igraph/src/igraph_interrupt_internal.h0000644000176000001440000000402612325527073020006 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_INTERRUPT_INTERNAL_H #define IGRAPH_INTERRUPT_INTERNAL_H #include "config.h" #include "igraph_interrupt.h" #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t *igraph_i_interruption_handler; /** * \define IGRAPH_ALLOW_INTERRUPTION * \brief * * This macro should be called when interruption is allowed. It calls * \ref igraph_allow_interruption() with the proper parameters and if that returns * anything but \c IGRAPH_SUCCESS then * the macro returns the "calling" function as well, with the proper * error code (\c IGRAPH_INTERRUPTED). */ #define IGRAPH_ALLOW_INTERRUPTION() \ do { \ if (igraph_i_interruption_handler) { if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) return IGRAPH_INTERRUPTED; \ } } while (0) #define IGRAPH_ALLOW_INTERRUPTION_NORETURN() \ do { \ if (igraph_i_interruption_handler) { igraph_allow_interruption(NULL); } \ } while (0) __END_DECLS #endif igraph/src/glphbm.c0000644000176000001440000004556712325527073014007 0ustar ripleyusers/* glphbm.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #define _GLPSTD_ERRNO #define _GLPSTD_STDIO #include "glphbm.h" #include "glpenv.h" /*********************************************************************** * NAME * * hbm_read_mat - read sparse matrix in Harwell-Boeing format * * SYNOPSIS * * #include "glphbm.h" * HBM *hbm_read_mat(const char *fname); * * DESCRIPTION * * The routine hbm_read_mat reads a sparse matrix in the Harwell-Boeing * format from a text file whose name is the character string fname. * * Detailed description of the Harwell-Boeing format recognised by this * routine is given in the following report: * * I.S.Duff, R.G.Grimes, J.G.Lewis. User's Guide for the Harwell-Boeing * Sparse Matrix Collection (Release I), TR/PA/92/86, October 1992. * * RETURNS * * If no error occured, the routine hbm_read_mat returns a pointer to * a data structure containing the matrix. In case of error the routine * prints an appropriate error message and returns NULL. */ struct dsa { /* working area used by routine hbm_read_mat */ const char *fname; /* name of input text file */ FILE *fp; /* stream assigned to input text file */ int seqn; /* card sequential number */ char card[80+1]; /* card image buffer */ int fmt_p; /* scale factor */ int fmt_k; /* iterator */ int fmt_f; /* format code */ int fmt_w; /* field width */ int fmt_d; /* number of decimal places after point */ }; /*********************************************************************** * read_card - read next data card * * This routine reads the next 80-column card from the input text file * and stores its image into the character string card. If the card was * read successfully, the routine returns zero, otherwise non-zero. */ static int read_card(struct dsa *dsa) { int k, c; dsa->seqn++; memset(dsa->card, ' ', 80), dsa->card[80] = '\0'; k = 0; for (;;) { c = fgetc(dsa->fp); if (ferror(dsa->fp)) { xprintf("%s:%d: read error - %s\n", dsa->fname, dsa->seqn, strerror(errno)); return 1; } if (feof(dsa->fp)) { if (k == 0) xprintf("%s:%d: unexpected EOF\n", dsa->fname, dsa->seqn); else xprintf("%s:%d: missing final LF\n", dsa->fname, dsa->seqn); return 1; } if (c == '\r') continue; if (c == '\n') break; if (iscntrl(c)) { xprintf("%s:%d: invalid control character 0x%02X\n", dsa->fname, dsa->seqn, c); return 1; } if (k == 80) { xprintf("%s:%d: card image too long\n", dsa->fname, dsa->seqn); return 1; } dsa->card[k++] = (char)c; } return 0; } /*********************************************************************** * scan_int - scan integer value from the current card * * This routine scans an integer value from the current card, where fld * is the name of the field, pos is the position of the field, width is * the width of the field, val points to a location to which the scanned * value should be stored. If the value was scanned successfully, the * routine returns zero, otherwise non-zero. */ static int scan_int(struct dsa *dsa, char *fld, int pos, int width, int *val) { char str[80+1]; xassert(1 <= width && width <= 80); memcpy(str, dsa->card + pos, width), str[width] = '\0'; if (str2int(strspx(str), val)) { xprintf("%s:%d: field `%s' contains invalid value `%s'\n", dsa->fname, dsa->seqn, fld, str); return 1; } return 0; } /*********************************************************************** * parse_fmt - parse Fortran format specification * * This routine parses the Fortran format specification represented as * character string which fmt points to and stores format elements into * appropriate static locations. Should note that not all valid Fortran * format specifications may be recognised. If the format specification * was recognised, the routine returns zero, otherwise non-zero. */ static int parse_fmt(struct dsa *dsa, char *fmt) { int k, s, val; char str[80+1]; /* first character should be left parenthesis */ if (fmt[0] != '(') fail: { xprintf("hbm_read_mat: format `%s' not recognised\n", fmt); return 1; } k = 1; /* optional scale factor */ dsa->fmt_p = 0; if (isdigit((unsigned char)fmt[k])) { s = 0; while (isdigit((unsigned char)fmt[k])) { if (s == 80) goto fail; str[s++] = fmt[k++]; } str[s] = '\0'; if (str2int(str, &val)) goto fail; if (toupper((unsigned char)fmt[k]) != 'P') goto iter; dsa->fmt_p = val, k++; if (!(0 <= dsa->fmt_p && dsa->fmt_p <= 255)) goto fail; /* optional comma may follow scale factor */ if (fmt[k] == ',') k++; } /* optional iterator */ dsa->fmt_k = 1; if (isdigit((unsigned char)fmt[k])) { s = 0; while (isdigit((unsigned char)fmt[k])) { if (s == 80) goto fail; str[s++] = fmt[k++]; } str[s] = '\0'; if (str2int(str, &val)) goto fail; iter: dsa->fmt_k = val; if (!(1 <= dsa->fmt_k && dsa->fmt_k <= 255)) goto fail; } /* format code */ dsa->fmt_f = toupper((unsigned char)fmt[k++]); if (!(dsa->fmt_f == 'D' || dsa->fmt_f == 'E' || dsa->fmt_f == 'F' || dsa->fmt_f == 'G' || dsa->fmt_f == 'I')) goto fail; /* field width */ if (!isdigit((unsigned char)fmt[k])) goto fail; s = 0; while (isdigit((unsigned char)fmt[k])) { if (s == 80) goto fail; str[s++] = fmt[k++]; } str[s] = '\0'; if (str2int(str, &dsa->fmt_w)) goto fail; if (!(1 <= dsa->fmt_w && dsa->fmt_w <= 255)) goto fail; /* optional number of decimal places after point */ dsa->fmt_d = 0; if (fmt[k] == '.') { k++; if (!isdigit((unsigned char)fmt[k])) goto fail; s = 0; while (isdigit((unsigned char)fmt[k])) { if (s == 80) goto fail; str[s++] = fmt[k++]; } str[s] = '\0'; if (str2int(str, &dsa->fmt_d)) goto fail; if (!(0 <= dsa->fmt_d && dsa->fmt_d <= 255)) goto fail; } /* last character should be right parenthesis */ if (!(fmt[k] == ')' && fmt[k+1] == '\0')) goto fail; return 0; } /*********************************************************************** * read_int_array - read array of integer type * * This routine reads an integer array from the input text file, where * name is array name, fmt is Fortran format specification that controls * reading, n is number of array elements, val is array of integer type. * If the array was read successful, the routine returns zero, otherwise * non-zero. */ static int read_int_array(struct dsa *dsa, char *name, char *fmt, int n, int val[]) { int k, pos; char str[80+1]; if (parse_fmt(dsa, fmt)) return 1; if (!(dsa->fmt_f == 'I' && dsa->fmt_w <= 80 && dsa->fmt_k * dsa->fmt_w <= 80)) { xprintf( "%s:%d: can't read array `%s' - invalid format `%s'\n", dsa->fname, dsa->seqn, name, fmt); return 1; } for (k = 1, pos = INT_MAX; k <= n; k++, pos++) { if (pos >= dsa->fmt_k) { if (read_card(dsa)) return 1; pos = 0; } memcpy(str, dsa->card + dsa->fmt_w * pos, dsa->fmt_w); str[dsa->fmt_w] = '\0'; strspx(str); if (str2int(str, &val[k])) { xprintf( "%s:%d: can't read array `%s' - invalid value `%s'\n", dsa->fname, dsa->seqn, name, str); return 1; } } return 0; } /*********************************************************************** * read_real_array - read array of real type * * This routine reads a real array from the input text file, where name * is array name, fmt is Fortran format specification that controls * reading, n is number of array elements, val is array of real type. * If the array was read successful, the routine returns zero, otherwise * non-zero. */ static int read_real_array(struct dsa *dsa, char *name, char *fmt, int n, double val[]) { int k, pos; char str[80+1], *ptr; if (parse_fmt(dsa, fmt)) return 1; if (!(dsa->fmt_f != 'I' && dsa->fmt_w <= 80 && dsa->fmt_k * dsa->fmt_w <= 80)) { xprintf( "%s:%d: can't read array `%s' - invalid format `%s'\n", dsa->fname, dsa->seqn, name, fmt); return 1; } for (k = 1, pos = INT_MAX; k <= n; k++, pos++) { if (pos >= dsa->fmt_k) { if (read_card(dsa)) return 1; pos = 0; } memcpy(str, dsa->card + dsa->fmt_w * pos, dsa->fmt_w); str[dsa->fmt_w] = '\0'; strspx(str); if (strchr(str, '.') == NULL && strcmp(str, "0")) { xprintf("%s(%d): can't read array `%s' - value `%s' has no " "decimal point\n", dsa->fname, dsa->seqn, name, str); return 1; } /* sometimes lower case letters appear */ for (ptr = str; *ptr; ptr++) *ptr = (char)toupper((unsigned char)*ptr); ptr = strchr(str, 'D'); if (ptr != NULL) *ptr = 'E'; /* value may appear with decimal exponent but without letters E or D (for example, -123.456-012), so missing letter should be inserted */ ptr = strchr(str+1, '+'); if (ptr == NULL) ptr = strchr(str+1, '-'); if (ptr != NULL && *(ptr-1) != 'E') { xassert(strlen(str) < 80); memmove(ptr+1, ptr, strlen(ptr)+1); *ptr = 'E'; } if (str2num(str, &val[k])) { xprintf( "%s:%d: can't read array `%s' - invalid value `%s'\n", dsa->fname, dsa->seqn, name, str); return 1; } } return 0; } HBM *hbm_read_mat(const char *fname) { struct dsa _dsa, *dsa = &_dsa; HBM *hbm = NULL; dsa->fname = fname; xprintf("hbm_read_mat: reading matrix from `%s'...\n", dsa->fname); dsa->fp = fopen(dsa->fname, "r"); if (dsa->fp == NULL) { xprintf("hbm_read_mat: unable to open `%s' - %s\n", dsa->fname, strerror(errno)); goto fail; } dsa->seqn = 0; hbm = xmalloc(sizeof(HBM)); memset(hbm, 0, sizeof(HBM)); /* read the first heading card */ if (read_card(dsa)) goto fail; memcpy(hbm->title, dsa->card, 72), hbm->title[72] = '\0'; strtrim(hbm->title); xprintf("%s\n", hbm->title); memcpy(hbm->key, dsa->card+72, 8), hbm->key[8] = '\0'; strspx(hbm->key); xprintf("key = %s\n", hbm->key); /* read the second heading card */ if (read_card(dsa)) goto fail; if (scan_int(dsa, "totcrd", 0, 14, &hbm->totcrd)) goto fail; if (scan_int(dsa, "ptrcrd", 14, 14, &hbm->ptrcrd)) goto fail; if (scan_int(dsa, "indcrd", 28, 14, &hbm->indcrd)) goto fail; if (scan_int(dsa, "valcrd", 42, 14, &hbm->valcrd)) goto fail; if (scan_int(dsa, "rhscrd", 56, 14, &hbm->rhscrd)) goto fail; xprintf("totcrd = %d; ptrcrd = %d; indcrd = %d; valcrd = %d; rhsc" "rd = %d\n", hbm->totcrd, hbm->ptrcrd, hbm->indcrd, hbm->valcrd, hbm->rhscrd); /* read the third heading card */ if (read_card(dsa)) goto fail; memcpy(hbm->mxtype, dsa->card, 3), hbm->mxtype[3] = '\0'; if (strchr("RCP", hbm->mxtype[0]) == NULL || strchr("SUHZR", hbm->mxtype[1]) == NULL || strchr("AE", hbm->mxtype[2]) == NULL) { xprintf("%s:%d: matrix type `%s' not recognised\n", dsa->fname, dsa->seqn, hbm->mxtype); goto fail; } if (scan_int(dsa, "nrow", 14, 14, &hbm->nrow)) goto fail; if (scan_int(dsa, "ncol", 28, 14, &hbm->ncol)) goto fail; if (scan_int(dsa, "nnzero", 42, 14, &hbm->nnzero)) goto fail; if (scan_int(dsa, "neltvl", 56, 14, &hbm->neltvl)) goto fail; xprintf("mxtype = %s; nrow = %d; ncol = %d; nnzero = %d; neltvl =" " %d\n", hbm->mxtype, hbm->nrow, hbm->ncol, hbm->nnzero, hbm->neltvl); /* read the fourth heading card */ if (read_card(dsa)) goto fail; memcpy(hbm->ptrfmt, dsa->card, 16), hbm->ptrfmt[16] = '\0'; strspx(hbm->ptrfmt); memcpy(hbm->indfmt, dsa->card+16, 16), hbm->indfmt[16] = '\0'; strspx(hbm->indfmt); memcpy(hbm->valfmt, dsa->card+32, 20), hbm->valfmt[20] = '\0'; strspx(hbm->valfmt); memcpy(hbm->rhsfmt, dsa->card+52, 20), hbm->rhsfmt[20] = '\0'; strspx(hbm->rhsfmt); xprintf("ptrfmt = %s; indfmt = %s; valfmt = %s; rhsfmt = %s\n", hbm->ptrfmt, hbm->indfmt, hbm->valfmt, hbm->rhsfmt); /* read the fifth heading card (optional) */ if (hbm->rhscrd <= 0) { strcpy(hbm->rhstyp, "???"); hbm->nrhs = 0; hbm->nrhsix = 0; } else { if (read_card(dsa)) goto fail; memcpy(hbm->rhstyp, dsa->card, 3), hbm->rhstyp[3] = '\0'; if (scan_int(dsa, "nrhs", 14, 14, &hbm->nrhs)) goto fail; if (scan_int(dsa, "nrhsix", 28, 14, &hbm->nrhsix)) goto fail; xprintf("rhstyp = `%s'; nrhs = %d; nrhsix = %d\n", hbm->rhstyp, hbm->nrhs, hbm->nrhsix); } /* read matrix structure */ hbm->colptr = xcalloc(1+hbm->ncol+1, sizeof(int)); if (read_int_array(dsa, "colptr", hbm->ptrfmt, hbm->ncol+1, hbm->colptr)) goto fail; hbm->rowind = xcalloc(1+hbm->nnzero, sizeof(int)); if (read_int_array(dsa, "rowind", hbm->indfmt, hbm->nnzero, hbm->rowind)) goto fail; /* read matrix values */ if (hbm->valcrd <= 0) goto done; if (hbm->mxtype[2] == 'A') { /* assembled matrix */ hbm->values = xcalloc(1+hbm->nnzero, sizeof(double)); if (read_real_array(dsa, "values", hbm->valfmt, hbm->nnzero, hbm->values)) goto fail; } else { /* elemental (unassembled) matrix */ hbm->values = xcalloc(1+hbm->neltvl, sizeof(double)); if (read_real_array(dsa, "values", hbm->valfmt, hbm->neltvl, hbm->values)) goto fail; } /* read right-hand sides */ if (hbm->nrhs <= 0) goto done; if (hbm->rhstyp[0] == 'F') { /* dense format */ hbm->nrhsvl = hbm->nrow * hbm->nrhs; hbm->rhsval = xcalloc(1+hbm->nrhsvl, sizeof(double)); if (read_real_array(dsa, "rhsval", hbm->rhsfmt, hbm->nrhsvl, hbm->rhsval)) goto fail; } else if (hbm->rhstyp[0] == 'M' && hbm->mxtype[2] == 'A') { /* sparse format */ /* read pointers */ hbm->rhsptr = xcalloc(1+hbm->nrhs+1, sizeof(int)); if (read_int_array(dsa, "rhsptr", hbm->ptrfmt, hbm->nrhs+1, hbm->rhsptr)) goto fail; /* read sparsity pattern */ hbm->rhsind = xcalloc(1+hbm->nrhsix, sizeof(int)); if (read_int_array(dsa, "rhsind", hbm->indfmt, hbm->nrhsix, hbm->rhsind)) goto fail; /* read values */ hbm->rhsval = xcalloc(1+hbm->nrhsix, sizeof(double)); if (read_real_array(dsa, "rhsval", hbm->rhsfmt, hbm->nrhsix, hbm->rhsval)) goto fail; } else if (hbm->rhstyp[0] == 'M' && hbm->mxtype[2] == 'E') { /* elemental format */ hbm->rhsval = xcalloc(1+hbm->nrhsvl, sizeof(double)); if (read_real_array(dsa, "rhsval", hbm->rhsfmt, hbm->nrhsvl, hbm->rhsval)) goto fail; } else { xprintf("%s:%d: right-hand side type `%c' not recognised\n", dsa->fname, dsa->seqn, hbm->rhstyp[0]); goto fail; } /* read starting guesses */ if (hbm->rhstyp[1] == 'G') { hbm->nguess = hbm->nrow * hbm->nrhs; hbm->sguess = xcalloc(1+hbm->nguess, sizeof(double)); if (read_real_array(dsa, "sguess", hbm->rhsfmt, hbm->nguess, hbm->sguess)) goto fail; } /* read solution vectors */ if (hbm->rhstyp[2] == 'X') { hbm->nexact = hbm->nrow * hbm->nrhs; hbm->xexact = xcalloc(1+hbm->nexact, sizeof(double)); if (read_real_array(dsa, "xexact", hbm->rhsfmt, hbm->nexact, hbm->xexact)) goto fail; } done: /* reading has been completed */ xprintf("hbm_read_mat: %d cards were read\n", dsa->seqn); fclose(dsa->fp); return hbm; fail: /* something wrong in Danish kingdom */ if (hbm != NULL) { if (hbm->colptr != NULL) xfree(hbm->colptr); if (hbm->rowind != NULL) xfree(hbm->rowind); if (hbm->rhsptr != NULL) xfree(hbm->rhsptr); if (hbm->rhsind != NULL) xfree(hbm->rhsind); if (hbm->values != NULL) xfree(hbm->values); if (hbm->rhsval != NULL) xfree(hbm->rhsval); if (hbm->sguess != NULL) xfree(hbm->sguess); if (hbm->xexact != NULL) xfree(hbm->xexact); xfree(hbm); } if (dsa->fp != NULL) fclose(dsa->fp); return NULL; } /*********************************************************************** * NAME * * hbm_free_mat - free sparse matrix in Harwell-Boeing format * * SYNOPSIS * * #include "glphbm.h" * void hbm_free_mat(HBM *hbm); * * DESCRIPTION * * The hbm_free_mat routine frees all the memory allocated to the data * structure containing a sparse matrix in the Harwell-Boeing format. */ void hbm_free_mat(HBM *hbm) { if (hbm->colptr != NULL) xfree(hbm->colptr); if (hbm->rowind != NULL) xfree(hbm->rowind); if (hbm->rhsptr != NULL) xfree(hbm->rhsptr); if (hbm->rhsind != NULL) xfree(hbm->rhsind); if (hbm->values != NULL) xfree(hbm->values); if (hbm->rhsval != NULL) xfree(hbm->rhsval); if (hbm->sguess != NULL) xfree(hbm->sguess); if (hbm->xexact != NULL) xfree(hbm->xexact); xfree(hbm); return; } /* eof */ igraph/src/glpluf.c0000644000176000001440000021376212325527073014021 0ustar ripleyusers/* glpluf.c (LU-factorization) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wself-assign" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpenv.h" #include "glpluf.h" #define xfault xerror /* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ #define N_MAX 100000000 /* = 100*10^6 */ /* maximal order of the original matrix */ /*********************************************************************** * NAME * * luf_create_it - create LU-factorization * * SYNOPSIS * * #include "glpluf.h" * LUF *luf_create_it(void); * * DESCRIPTION * * The routine luf_create_it creates a program object, which represents * LU-factorization of a square matrix. * * RETURNS * * The routine luf_create_it returns a pointer to the object created. */ LUF *luf_create_it(void) { LUF *luf; luf = xmalloc(sizeof(LUF)); luf->n_max = luf->n = 0; luf->valid = 0; luf->fr_ptr = luf->fr_len = NULL; luf->fc_ptr = luf->fc_len = NULL; luf->vr_ptr = luf->vr_len = luf->vr_cap = NULL; luf->vr_piv = NULL; luf->vc_ptr = luf->vc_len = luf->vc_cap = NULL; luf->pp_row = luf->pp_col = NULL; luf->qq_row = luf->qq_col = NULL; luf->sv_size = 0; luf->sv_beg = luf->sv_end = 0; luf->sv_ind = NULL; luf->sv_val = NULL; luf->sv_head = luf->sv_tail = 0; luf->sv_prev = luf->sv_next = NULL; luf->vr_max = NULL; luf->rs_head = luf->rs_prev = luf->rs_next = NULL; luf->cs_head = luf->cs_prev = luf->cs_next = NULL; luf->flag = NULL; luf->work = NULL; luf->new_sva = 0; luf->piv_tol = 0.10; luf->piv_lim = 4; luf->suhl = 1; luf->eps_tol = 1e-15; luf->max_gro = 1e+10; luf->nnz_a = luf->nnz_f = luf->nnz_v = 0; luf->max_a = luf->big_v = 0.0; luf->rank = 0; return luf; } /*********************************************************************** * NAME * * luf_defrag_sva - defragment the sparse vector area * * SYNOPSIS * * #include "glpluf.h" * void luf_defrag_sva(LUF *luf); * * DESCRIPTION * * The routine luf_defrag_sva defragments the sparse vector area (SVA) * gathering all unused locations in one continuous extent. In order to * do that the routine moves all unused locations from the left part of * SVA (which contains rows and columns of the matrix V) to the middle * part (which contains free locations). This is attained by relocating * elements of rows and columns of the matrix V toward the beginning of * the left part. * * NOTE that this "garbage collection" involves changing row and column * pointers of the matrix V. */ void luf_defrag_sva(LUF *luf) { int n = luf->n; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; int *vr_cap = luf->vr_cap; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *vc_cap = luf->vc_cap; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int *sv_next = luf->sv_next; int sv_beg = 1; int i, j, k; /* skip rows and columns, which do not need to be relocated */ for (k = luf->sv_head; k != 0; k = sv_next[k]) { if (k <= n) { /* i-th row of the matrix V */ i = k; if (vr_ptr[i] != sv_beg) break; vr_cap[i] = vr_len[i]; sv_beg += vr_cap[i]; } else { /* j-th column of the matrix V */ j = k - n; if (vc_ptr[j] != sv_beg) break; vc_cap[j] = vc_len[j]; sv_beg += vc_cap[j]; } } /* relocate other rows and columns in order to gather all unused locations in one continuous extent */ for (k = k; k != 0; k = sv_next[k]) { if (k <= n) { /* i-th row of the matrix V */ i = k; memmove(&sv_ind[sv_beg], &sv_ind[vr_ptr[i]], vr_len[i] * sizeof(int)); memmove(&sv_val[sv_beg], &sv_val[vr_ptr[i]], vr_len[i] * sizeof(double)); vr_ptr[i] = sv_beg; vr_cap[i] = vr_len[i]; sv_beg += vr_cap[i]; } else { /* j-th column of the matrix V */ j = k - n; memmove(&sv_ind[sv_beg], &sv_ind[vc_ptr[j]], vc_len[j] * sizeof(int)); memmove(&sv_val[sv_beg], &sv_val[vc_ptr[j]], vc_len[j] * sizeof(double)); vc_ptr[j] = sv_beg; vc_cap[j] = vc_len[j]; sv_beg += vc_cap[j]; } } /* set new pointer to the beginning of the free part */ luf->sv_beg = sv_beg; return; } /*********************************************************************** * NAME * * luf_enlarge_row - enlarge row capacity * * SYNOPSIS * * #include "glpluf.h" * int luf_enlarge_row(LUF *luf, int i, int cap); * * DESCRIPTION * * The routine luf_enlarge_row enlarges capacity of the i-th row of the * matrix V to cap locations (assuming that its current capacity is less * than cap). In order to do that the routine relocates elements of the * i-th row to the end of the left part of SVA (which contains rows and * columns of the matrix V) and then expands the left part by allocating * cap free locations from the free part. If there are less than cap * free locations, the routine defragments the sparse vector area. * * Due to "garbage collection" this operation may change row and column * pointers of the matrix V. * * RETURNS * * If no error occured, the routine returns zero. Otherwise, in case of * overflow of the sparse vector area, the routine returns non-zero. */ int luf_enlarge_row(LUF *luf, int i, int cap) { int n = luf->n; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; int *vr_cap = luf->vr_cap; int *vc_cap = luf->vc_cap; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int *sv_prev = luf->sv_prev; int *sv_next = luf->sv_next; int ret = 0; int cur, k, kk; xassert(1 <= i && i <= n); xassert(vr_cap[i] < cap); /* if there are less than cap free locations, defragment SVA */ if (luf->sv_end - luf->sv_beg < cap) { luf_defrag_sva(luf); if (luf->sv_end - luf->sv_beg < cap) { ret = 1; goto done; } } /* save current capacity of the i-th row */ cur = vr_cap[i]; /* copy existing elements to the beginning of the free part */ memmove(&sv_ind[luf->sv_beg], &sv_ind[vr_ptr[i]], vr_len[i] * sizeof(int)); memmove(&sv_val[luf->sv_beg], &sv_val[vr_ptr[i]], vr_len[i] * sizeof(double)); /* set new pointer and new capacity of the i-th row */ vr_ptr[i] = luf->sv_beg; vr_cap[i] = cap; /* set new pointer to the beginning of the free part */ luf->sv_beg += cap; /* now the i-th row starts in the rightmost location among other rows and columns of the matrix V, so its node should be moved to the end of the row/column linked list */ k = i; /* remove the i-th row node from the linked list */ if (sv_prev[k] == 0) luf->sv_head = sv_next[k]; else { /* capacity of the previous row/column can be increased at the expense of old locations of the i-th row */ kk = sv_prev[k]; if (kk <= n) vr_cap[kk] += cur; else vc_cap[kk-n] += cur; sv_next[sv_prev[k]] = sv_next[k]; } if (sv_next[k] == 0) luf->sv_tail = sv_prev[k]; else sv_prev[sv_next[k]] = sv_prev[k]; /* insert the i-th row node to the end of the linked list */ sv_prev[k] = luf->sv_tail; sv_next[k] = 0; if (sv_prev[k] == 0) luf->sv_head = k; else sv_next[sv_prev[k]] = k; luf->sv_tail = k; done: return ret; } /*********************************************************************** * NAME * * luf_enlarge_col - enlarge column capacity * * SYNOPSIS * * #include "glpluf.h" * int luf_enlarge_col(LUF *luf, int j, int cap); * * DESCRIPTION * * The routine luf_enlarge_col enlarges capacity of the j-th column of * the matrix V to cap locations (assuming that its current capacity is * less than cap). In order to do that the routine relocates elements * of the j-th column to the end of the left part of SVA (which contains * rows and columns of the matrix V) and then expands the left part by * allocating cap free locations from the free part. If there are less * than cap free locations, the routine defragments the sparse vector * area. * * Due to "garbage collection" this operation may change row and column * pointers of the matrix V. * * RETURNS * * If no error occured, the routine returns zero. Otherwise, in case of * overflow of the sparse vector area, the routine returns non-zero. */ int luf_enlarge_col(LUF *luf, int j, int cap) { int n = luf->n; int *vr_cap = luf->vr_cap; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *vc_cap = luf->vc_cap; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int *sv_prev = luf->sv_prev; int *sv_next = luf->sv_next; int ret = 0; int cur, k, kk; xassert(1 <= j && j <= n); xassert(vc_cap[j] < cap); /* if there are less than cap free locations, defragment SVA */ if (luf->sv_end - luf->sv_beg < cap) { luf_defrag_sva(luf); if (luf->sv_end - luf->sv_beg < cap) { ret = 1; goto done; } } /* save current capacity of the j-th column */ cur = vc_cap[j]; /* copy existing elements to the beginning of the free part */ memmove(&sv_ind[luf->sv_beg], &sv_ind[vc_ptr[j]], vc_len[j] * sizeof(int)); memmove(&sv_val[luf->sv_beg], &sv_val[vc_ptr[j]], vc_len[j] * sizeof(double)); /* set new pointer and new capacity of the j-th column */ vc_ptr[j] = luf->sv_beg; vc_cap[j] = cap; /* set new pointer to the beginning of the free part */ luf->sv_beg += cap; /* now the j-th column starts in the rightmost location among other rows and columns of the matrix V, so its node should be moved to the end of the row/column linked list */ k = n + j; /* remove the j-th column node from the linked list */ if (sv_prev[k] == 0) luf->sv_head = sv_next[k]; else { /* capacity of the previous row/column can be increased at the expense of old locations of the j-th column */ kk = sv_prev[k]; if (kk <= n) vr_cap[kk] += cur; else vc_cap[kk-n] += cur; sv_next[sv_prev[k]] = sv_next[k]; } if (sv_next[k] == 0) luf->sv_tail = sv_prev[k]; else sv_prev[sv_next[k]] = sv_prev[k]; /* insert the j-th column node to the end of the linked list */ sv_prev[k] = luf->sv_tail; sv_next[k] = 0; if (sv_prev[k] == 0) luf->sv_head = k; else sv_next[sv_prev[k]] = k; luf->sv_tail = k; done: return ret; } /*********************************************************************** * reallocate - reallocate LU-factorization arrays * * This routine reallocates arrays, whose size depends of n, the order * of the matrix A to be factorized. */ static void reallocate(LUF *luf, int n) { int n_max = luf->n_max; luf->n = n; if (n <= n_max) goto done; if (luf->fr_ptr != NULL) xfree(luf->fr_ptr); if (luf->fr_len != NULL) xfree(luf->fr_len); if (luf->fc_ptr != NULL) xfree(luf->fc_ptr); if (luf->fc_len != NULL) xfree(luf->fc_len); if (luf->vr_ptr != NULL) xfree(luf->vr_ptr); if (luf->vr_len != NULL) xfree(luf->vr_len); if (luf->vr_cap != NULL) xfree(luf->vr_cap); if (luf->vr_piv != NULL) xfree(luf->vr_piv); if (luf->vc_ptr != NULL) xfree(luf->vc_ptr); if (luf->vc_len != NULL) xfree(luf->vc_len); if (luf->vc_cap != NULL) xfree(luf->vc_cap); if (luf->pp_row != NULL) xfree(luf->pp_row); if (luf->pp_col != NULL) xfree(luf->pp_col); if (luf->qq_row != NULL) xfree(luf->qq_row); if (luf->qq_col != NULL) xfree(luf->qq_col); if (luf->sv_prev != NULL) xfree(luf->sv_prev); if (luf->sv_next != NULL) xfree(luf->sv_next); if (luf->vr_max != NULL) xfree(luf->vr_max); if (luf->rs_head != NULL) xfree(luf->rs_head); if (luf->rs_prev != NULL) xfree(luf->rs_prev); if (luf->rs_next != NULL) xfree(luf->rs_next); if (luf->cs_head != NULL) xfree(luf->cs_head); if (luf->cs_prev != NULL) xfree(luf->cs_prev); if (luf->cs_next != NULL) xfree(luf->cs_next); if (luf->flag != NULL) xfree(luf->flag); if (luf->work != NULL) xfree(luf->work); luf->n_max = n_max = n + 100; luf->fr_ptr = xcalloc(1+n_max, sizeof(int)); luf->fr_len = xcalloc(1+n_max, sizeof(int)); luf->fc_ptr = xcalloc(1+n_max, sizeof(int)); luf->fc_len = xcalloc(1+n_max, sizeof(int)); luf->vr_ptr = xcalloc(1+n_max, sizeof(int)); luf->vr_len = xcalloc(1+n_max, sizeof(int)); luf->vr_cap = xcalloc(1+n_max, sizeof(int)); luf->vr_piv = xcalloc(1+n_max, sizeof(double)); luf->vc_ptr = xcalloc(1+n_max, sizeof(int)); luf->vc_len = xcalloc(1+n_max, sizeof(int)); luf->vc_cap = xcalloc(1+n_max, sizeof(int)); luf->pp_row = xcalloc(1+n_max, sizeof(int)); luf->pp_col = xcalloc(1+n_max, sizeof(int)); luf->qq_row = xcalloc(1+n_max, sizeof(int)); luf->qq_col = xcalloc(1+n_max, sizeof(int)); luf->sv_prev = xcalloc(1+n_max+n_max, sizeof(int)); luf->sv_next = xcalloc(1+n_max+n_max, sizeof(int)); luf->vr_max = xcalloc(1+n_max, sizeof(double)); luf->rs_head = xcalloc(1+n_max, sizeof(int)); luf->rs_prev = xcalloc(1+n_max, sizeof(int)); luf->rs_next = xcalloc(1+n_max, sizeof(int)); luf->cs_head = xcalloc(1+n_max, sizeof(int)); luf->cs_prev = xcalloc(1+n_max, sizeof(int)); luf->cs_next = xcalloc(1+n_max, sizeof(int)); luf->flag = xcalloc(1+n_max, sizeof(int)); luf->work = xcalloc(1+n_max, sizeof(double)); done: return; } /*********************************************************************** * initialize - initialize LU-factorization data structures * * This routine initializes data structures for subsequent computing * the LU-factorization of a given matrix A, which is specified by the * formal routine col. On exit V = A and F = P = Q = I, where I is the * unity matrix. (Row-wise representation of the matrix F is not used * at the factorization stage and therefore is not initialized.) * * If no error occured, the routine returns zero. Otherwise, in case of * overflow of the sparse vector area, the routine returns non-zero. */ static int initialize(LUF *luf, int (*col)(void *info, int j, int rn[], double aj[]), void *info) { int n = luf->n; int *fc_ptr = luf->fc_ptr; int *fc_len = luf->fc_len; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; int *vr_cap = luf->vr_cap; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *vc_cap = luf->vc_cap; int *pp_row = luf->pp_row; int *pp_col = luf->pp_col; int *qq_row = luf->qq_row; int *qq_col = luf->qq_col; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int *sv_prev = luf->sv_prev; int *sv_next = luf->sv_next; double *vr_max = luf->vr_max; int *rs_head = luf->rs_head; int *rs_prev = luf->rs_prev; int *rs_next = luf->rs_next; int *cs_head = luf->cs_head; int *cs_prev = luf->cs_prev; int *cs_next = luf->cs_next; int *flag = luf->flag; double *work = luf->work; int ret = 0; int i, i_ptr, j, j_beg, j_end, k, len, nnz, sv_beg, sv_end, ptr; double big, val; /* free all locations of the sparse vector area */ sv_beg = 1; sv_end = luf->sv_size + 1; /* (row-wise representation of the matrix F is not initialized, because it is not used at the factorization stage) */ /* build the matrix F in column-wise format (initially F = I) */ for (j = 1; j <= n; j++) { fc_ptr[j] = sv_end; fc_len[j] = 0; } /* clear rows of the matrix V; clear the flag array */ for (i = 1; i <= n; i++) vr_len[i] = vr_cap[i] = 0, flag[i] = 0; /* build the matrix V in column-wise format (initially V = A); count non-zeros in rows of this matrix; count total number of non-zeros; compute largest of absolute values of elements */ nnz = 0; big = 0.0; for (j = 1; j <= n; j++) { int *rn = pp_row; double *aj = work; /* obtain j-th column of the matrix A */ len = col(info, j, rn, aj); if (!(0 <= len && len <= n)) xfault("luf_factorize: j = %d; len = %d; invalid column len" "gth\n", j, len); /* check for free locations */ if (sv_end - sv_beg < len) { /* overflow of the sparse vector area */ ret = 1; goto done; } /* set pointer to the j-th column */ vc_ptr[j] = sv_beg; /* set length of the j-th column */ vc_len[j] = vc_cap[j] = len; /* count total number of non-zeros */ nnz += len; /* walk through elements of the j-th column */ for (ptr = 1; ptr <= len; ptr++) { /* get row index and numerical value of a[i,j] */ i = rn[ptr]; val = aj[ptr]; if (!(1 <= i && i <= n)) xfault("luf_factorize: i = %d; j = %d; invalid row index" "\n", i, j); if (flag[i]) xfault("luf_factorize: i = %d; j = %d; duplicate element" " not allowed\n", i, j); if (val == 0.0) xfault("luf_factorize: i = %d; j = %d; zero element not " "allowed\n", i, j); /* add new element v[i,j] = a[i,j] to j-th column */ sv_ind[sv_beg] = i; sv_val[sv_beg] = val; sv_beg++; /* big := max(big, |a[i,j]|) */ if (val < 0.0) val = - val; if (big < val) big = val; /* mark non-zero in the i-th position of the j-th column */ flag[i] = 1; /* increase length of the i-th row */ vr_cap[i]++; } /* reset all non-zero marks */ for (ptr = 1; ptr <= len; ptr++) flag[rn[ptr]] = 0; } /* allocate rows of the matrix V */ for (i = 1; i <= n; i++) { /* get length of the i-th row */ len = vr_cap[i]; /* check for free locations */ if (sv_end - sv_beg < len) { /* overflow of the sparse vector area */ ret = 1; goto done; } /* set pointer to the i-th row */ vr_ptr[i] = sv_beg; /* reserve locations for the i-th row */ sv_beg += len; } /* build the matrix V in row-wise format using representation of this matrix in column-wise format */ for (j = 1; j <= n; j++) { /* walk through elements of the j-th column */ j_beg = vc_ptr[j]; j_end = j_beg + vc_len[j] - 1; for (k = j_beg; k <= j_end; k++) { /* get row index and numerical value of v[i,j] */ i = sv_ind[k]; val = sv_val[k]; /* store element in the i-th row */ i_ptr = vr_ptr[i] + vr_len[i]; sv_ind[i_ptr] = j; sv_val[i_ptr] = val; /* increase count of the i-th row */ vr_len[i]++; } } /* initialize the matrices P and Q (initially P = Q = I) */ for (k = 1; k <= n; k++) pp_row[k] = pp_col[k] = qq_row[k] = qq_col[k] = k; /* set sva partitioning pointers */ luf->sv_beg = sv_beg; luf->sv_end = sv_end; /* the initial physical order of rows and columns of the matrix V is n+1, ..., n+n, 1, ..., n (firstly columns, then rows) */ luf->sv_head = n+1; luf->sv_tail = n; for (i = 1; i <= n; i++) { sv_prev[i] = i-1; sv_next[i] = i+1; } sv_prev[1] = n+n; sv_next[n] = 0; for (j = 1; j <= n; j++) { sv_prev[n+j] = n+j-1; sv_next[n+j] = n+j+1; } sv_prev[n+1] = 0; sv_next[n+n] = 1; /* clear working arrays */ for (k = 1; k <= n; k++) { flag[k] = 0; work[k] = 0.0; } /* initialize some statistics */ luf->nnz_a = nnz; luf->nnz_f = 0; luf->nnz_v = nnz; luf->max_a = big; luf->big_v = big; luf->rank = -1; /* initially the active submatrix is the entire matrix V */ /* largest of absolute values of elements in each active row is unknown yet */ for (i = 1; i <= n; i++) vr_max[i] = -1.0; /* build linked lists of active rows */ for (len = 0; len <= n; len++) rs_head[len] = 0; for (i = 1; i <= n; i++) { len = vr_len[i]; rs_prev[i] = 0; rs_next[i] = rs_head[len]; if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; rs_head[len] = i; } /* build linked lists of active columns */ for (len = 0; len <= n; len++) cs_head[len] = 0; for (j = 1; j <= n; j++) { len = vc_len[j]; cs_prev[j] = 0; cs_next[j] = cs_head[len]; if (cs_next[j] != 0) cs_prev[cs_next[j]] = j; cs_head[len] = j; } done: /* return to the factorizing routine */ return ret; } /*********************************************************************** * find_pivot - choose a pivot element * * This routine chooses a pivot element in the active submatrix of the * matrix U = P*V*Q. * * It is assumed that on entry the matrix U has the following partially * triangularized form: * * 1 k n * 1 x x x x x x x x x x * . x x x x x x x x x * . . x x x x x x x x * . . . x x x x x x x * k . . . . * * * * * * * . . . . * * * * * * * . . . . * * * * * * * . . . . * * * * * * * . . . . * * * * * * * n . . . . * * * * * * * * where rows and columns k, k+1, ..., n belong to the active submatrix * (elements of the active submatrix are marked by '*'). * * Since the matrix U = P*V*Q is not stored, the routine works with the * matrix V. It is assumed that the row-wise representation corresponds * to the matrix V, but the column-wise representation corresponds to * the active submatrix of the matrix V, i.e. elements of the matrix V, * which doesn't belong to the active submatrix, are missing from the * column linked lists. It is also assumed that each active row of the * matrix V is in the set R[len], where len is number of non-zeros in * the row, and each active column of the matrix V is in the set C[len], * where len is number of non-zeros in the column (in the latter case * only elements of the active submatrix are counted; such elements are * marked by '*' on the figure above). * * For the reason of numerical stability the routine applies so called * threshold pivoting proposed by J.Reid. It is assumed that an element * v[i,j] can be selected as a pivot candidate if it is not very small * (in absolute value) among other elements in the same row, i.e. if it * satisfies to the stability condition |v[i,j]| >= tol * max|v[i,*]|, * where 0 < tol < 1 is a given tolerance. * * In order to keep sparsity of the matrix V the routine uses Markowitz * strategy, trying to choose such element v[p,q], which satisfies to * the stability condition (see above) and has smallest Markowitz cost * (nr[p]-1) * (nc[q]-1), where nr[p] and nc[q] are numbers of non-zero * elements, respectively, in the p-th row and in the q-th column of the * active submatrix. * * In order to reduce the search, i.e. not to walk through all elements * of the active submatrix, the routine exploits a technique proposed by * I.Duff. This technique is based on using the sets R[len] and C[len] * of active rows and columns. * * If the pivot element v[p,q] has been chosen, the routine stores its * indices to the locations *p and *q and returns zero. Otherwise, if * the active submatrix is empty and therefore the pivot element can't * be chosen, the routine returns non-zero. */ static int find_pivot(LUF *luf, int *_p, int *_q) { int n = luf->n; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; double *vr_max = luf->vr_max; int *rs_head = luf->rs_head; int *rs_next = luf->rs_next; int *cs_head = luf->cs_head; int *cs_prev = luf->cs_prev; int *cs_next = luf->cs_next; double piv_tol = luf->piv_tol; int piv_lim = luf->piv_lim; int suhl = luf->suhl; int p, q, len, i, i_beg, i_end, i_ptr, j, j_beg, j_end, j_ptr, ncand, next_j, min_p, min_q, min_len; double best, cost, big, temp; /* initially no pivot candidates have been found so far */ p = q = 0, best = DBL_MAX, ncand = 0; /* if in the active submatrix there is a column that has the only non-zero (column singleton), choose it as pivot */ j = cs_head[1]; if (j != 0) { xassert(vc_len[j] == 1); p = sv_ind[vc_ptr[j]], q = j; goto done; } /* if in the active submatrix there is a row that has the only non-zero (row singleton), choose it as pivot */ i = rs_head[1]; if (i != 0) { xassert(vr_len[i] == 1); p = i, q = sv_ind[vr_ptr[i]]; goto done; } /* there are no singletons in the active submatrix; walk through other non-empty rows and columns */ for (len = 2; len <= n; len++) { /* consider active columns that have len non-zeros */ for (j = cs_head[len]; j != 0; j = next_j) { /* the j-th column has len non-zeros */ j_beg = vc_ptr[j]; j_end = j_beg + vc_len[j] - 1; /* save pointer to the next column with the same length */ next_j = cs_next[j]; /* find an element in the j-th column, which is placed in a row with minimal number of non-zeros and satisfies to the stability condition (such element may not exist) */ min_p = min_q = 0, min_len = INT_MAX; for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) { /* get row index of v[i,j] */ i = sv_ind[j_ptr]; i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; /* if the i-th row is not shorter than that one, where minimal element is currently placed, skip v[i,j] */ if (vr_len[i] >= min_len) continue; /* determine the largest of absolute values of elements in the i-th row */ big = vr_max[i]; if (big < 0.0) { /* the largest value is unknown yet; compute it */ for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) { temp = sv_val[i_ptr]; if (temp < 0.0) temp = - temp; if (big < temp) big = temp; } vr_max[i] = big; } /* find v[i,j] in the i-th row */ for (i_ptr = vr_ptr[i]; sv_ind[i_ptr] != j; i_ptr++); xassert(i_ptr <= i_end); /* if v[i,j] doesn't satisfy to the stability condition, skip it */ temp = sv_val[i_ptr]; if (temp < 0.0) temp = - temp; if (temp < piv_tol * big) continue; /* v[i,j] is better than the current minimal element */ min_p = i, min_q = j, min_len = vr_len[i]; /* if Markowitz cost of the current minimal element is not greater than (len-1)**2, it can be chosen right now; this heuristic reduces the search and works well in many cases */ if (min_len <= len) { p = min_p, q = min_q; goto done; } } /* the j-th column has been scanned */ if (min_p != 0) { /* the minimal element is a next pivot candidate */ ncand++; /* compute its Markowitz cost */ cost = (double)(min_len - 1) * (double)(len - 1); /* choose between the minimal element and the current candidate */ if (cost < best) p = min_p, q = min_q, best = cost; /* if piv_lim candidates have been considered, there are doubts that a much better candidate exists; therefore it's time to terminate the search */ if (ncand == piv_lim) goto done; } else { /* the j-th column has no elements, which satisfy to the stability condition; Uwe Suhl suggests to exclude such column from the further consideration until it becomes a column singleton; in hard cases this significantly reduces a time needed for pivot searching */ if (suhl) { /* remove the j-th column from the active set */ if (cs_prev[j] == 0) cs_head[len] = cs_next[j]; else cs_next[cs_prev[j]] = cs_next[j]; if (cs_next[j] == 0) /* nop */; else cs_prev[cs_next[j]] = cs_prev[j]; /* the following assignment is used to avoid an error when the routine eliminate (see below) will try to remove the j-th column from the active set */ cs_prev[j] = cs_next[j] = j; } } } /* consider active rows that have len non-zeros */ for (i = rs_head[len]; i != 0; i = rs_next[i]) { /* the i-th row has len non-zeros */ i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; /* determine the largest of absolute values of elements in the i-th row */ big = vr_max[i]; if (big < 0.0) { /* the largest value is unknown yet; compute it */ for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) { temp = sv_val[i_ptr]; if (temp < 0.0) temp = - temp; if (big < temp) big = temp; } vr_max[i] = big; } /* find an element in the i-th row, which is placed in a column with minimal number of non-zeros and satisfies to the stability condition (such element always exists) */ min_p = min_q = 0, min_len = INT_MAX; for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) { /* get column index of v[i,j] */ j = sv_ind[i_ptr]; /* if the j-th column is not shorter than that one, where minimal element is currently placed, skip v[i,j] */ if (vc_len[j] >= min_len) continue; /* if v[i,j] doesn't satisfy to the stability condition, skip it */ temp = sv_val[i_ptr]; if (temp < 0.0) temp = - temp; if (temp < piv_tol * big) continue; /* v[i,j] is better than the current minimal element */ min_p = i, min_q = j, min_len = vc_len[j]; /* if Markowitz cost of the current minimal element is not greater than (len-1)**2, it can be chosen right now; this heuristic reduces the search and works well in many cases */ if (min_len <= len) { p = min_p, q = min_q; goto done; } } /* the i-th row has been scanned */ if (min_p != 0) { /* the minimal element is a next pivot candidate */ ncand++; /* compute its Markowitz cost */ cost = (double)(len - 1) * (double)(min_len - 1); /* choose between the minimal element and the current candidate */ if (cost < best) p = min_p, q = min_q, best = cost; /* if piv_lim candidates have been considered, there are doubts that a much better candidate exists; therefore it's time to terminate the search */ if (ncand == piv_lim) goto done; } else { /* this can't be because this can never be */ xassert(min_p != min_p); } } } done: /* bring the pivot to the factorizing routine */ *_p = p, *_q = q; return (p == 0); } /*********************************************************************** * eliminate - perform gaussian elimination. * * This routine performs elementary gaussian transformations in order * to eliminate subdiagonal elements in the k-th column of the matrix * U = P*V*Q using the pivot element u[k,k], where k is the number of * the current elimination step. * * The parameters p and q are, respectively, row and column indices of * the element v[p,q], which corresponds to the element u[k,k]. * * Each time when the routine applies the elementary transformation to * a non-pivot row of the matrix V, it stores the corresponding element * to the matrix F in order to keep the main equality A = F*V. * * The routine assumes that on entry the matrices L = P*F*inv(P) and * U = P*V*Q are the following: * * 1 k 1 k n * 1 1 . . . . . . . . . 1 x x x x x x x x x x * x 1 . . . . . . . . . x x x x x x x x x * x x 1 . . . . . . . . . x x x x x x x x * x x x 1 . . . . . . . . . x x x x x x x * k x x x x 1 . . . . . k . . . . * * * * * * * x x x x _ 1 . . . . . . . . # * * * * * * x x x x _ . 1 . . . . . . . # * * * * * * x x x x _ . . 1 . . . . . . # * * * * * * x x x x _ . . . 1 . . . . . # * * * * * * n x x x x _ . . . . 1 n . . . . # * * * * * * * matrix L matrix U * * where rows and columns of the matrix U with numbers k, k+1, ..., n * form the active submatrix (eliminated elements are marked by '#' and * other elements of the active submatrix are marked by '*'). Note that * each eliminated non-zero element u[i,k] of the matrix U gives the * corresponding element l[i,k] of the matrix L (marked by '_'). * * Actually all operations are performed on the matrix V. Should note * that the row-wise representation corresponds to the matrix V, but the * column-wise representation corresponds to the active submatrix of the * matrix V, i.e. elements of the matrix V, which doesn't belong to the * active submatrix, are missing from the column linked lists. * * Let u[k,k] = v[p,q] be the pivot. In order to eliminate subdiagonal * elements u[i',k] = v[i,q], i' = k+1, k+2, ..., n, the routine applies * the following elementary gaussian transformations: * * (i-th row of V) := (i-th row of V) - f[i,p] * (p-th row of V), * * where f[i,p] = v[i,q] / v[p,q] is a gaussian multiplier. * * Additionally, in order to keep the main equality A = F*V, each time * when the routine applies the transformation to i-th row of the matrix * V, it also adds f[i,p] as a new element to the matrix F. * * IMPORTANT: On entry the working arrays flag and work should contain * zeros. This status is provided by the routine on exit. * * If no error occured, the routine returns zero. Otherwise, in case of * overflow of the sparse vector area, the routine returns non-zero. */ static int eliminate(LUF *luf, int p, int q) { int n = luf->n; int *fc_ptr = luf->fc_ptr; int *fc_len = luf->fc_len; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; int *vr_cap = luf->vr_cap; double *vr_piv = luf->vr_piv; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *vc_cap = luf->vc_cap; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int *sv_prev = luf->sv_prev; int *sv_next = luf->sv_next; double *vr_max = luf->vr_max; int *rs_head = luf->rs_head; int *rs_prev = luf->rs_prev; int *rs_next = luf->rs_next; int *cs_head = luf->cs_head; int *cs_prev = luf->cs_prev; int *cs_next = luf->cs_next; int *flag = luf->flag; double *work = luf->work; double eps_tol = luf->eps_tol; /* at this stage the row-wise representation of the matrix F is not used, so fr_len can be used as a working array */ int *ndx = luf->fr_len; int ret = 0; int len, fill, i, i_beg, i_end, i_ptr, j, j_beg, j_end, j_ptr, k, p_beg, p_end, p_ptr, q_beg, q_end, q_ptr; double fip, val, vpq, temp; xassert(1 <= p && p <= n); xassert(1 <= q && q <= n); /* remove the p-th (pivot) row from the active set; this row will never return there */ if (rs_prev[p] == 0) rs_head[vr_len[p]] = rs_next[p]; else rs_next[rs_prev[p]] = rs_next[p]; if (rs_next[p] == 0) ; else rs_prev[rs_next[p]] = rs_prev[p]; /* remove the q-th (pivot) column from the active set; this column will never return there */ if (cs_prev[q] == 0) cs_head[vc_len[q]] = cs_next[q]; else cs_next[cs_prev[q]] = cs_next[q]; if (cs_next[q] == 0) ; else cs_prev[cs_next[q]] = cs_prev[q]; /* find the pivot v[p,q] = u[k,k] in the p-th row */ p_beg = vr_ptr[p]; p_end = p_beg + vr_len[p] - 1; for (p_ptr = p_beg; sv_ind[p_ptr] != q; p_ptr++) /* nop */; xassert(p_ptr <= p_end); /* store value of the pivot */ vpq = (vr_piv[p] = sv_val[p_ptr]); /* remove the pivot from the p-th row */ sv_ind[p_ptr] = sv_ind[p_end]; sv_val[p_ptr] = sv_val[p_end]; vr_len[p]--; p_end--; /* find the pivot v[p,q] = u[k,k] in the q-th column */ q_beg = vc_ptr[q]; q_end = q_beg + vc_len[q] - 1; for (q_ptr = q_beg; sv_ind[q_ptr] != p; q_ptr++) /* nop */; xassert(q_ptr <= q_end); /* remove the pivot from the q-th column */ sv_ind[q_ptr] = sv_ind[q_end]; vc_len[q]--; q_end--; /* walk through the p-th (pivot) row, which doesn't contain the pivot v[p,q] already, and do the following... */ for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) { /* get column index of v[p,j] */ j = sv_ind[p_ptr]; /* store v[p,j] to the working array */ flag[j] = 1; work[j] = sv_val[p_ptr]; /* remove the j-th column from the active set; this column will return there later with new length */ if (cs_prev[j] == 0) cs_head[vc_len[j]] = cs_next[j]; else cs_next[cs_prev[j]] = cs_next[j]; if (cs_next[j] == 0) ; else cs_prev[cs_next[j]] = cs_prev[j]; /* find v[p,j] in the j-th column */ j_beg = vc_ptr[j]; j_end = j_beg + vc_len[j] - 1; for (j_ptr = j_beg; sv_ind[j_ptr] != p; j_ptr++) /* nop */; xassert(j_ptr <= j_end); /* since v[p,j] leaves the active submatrix, remove it from the j-th column; however, v[p,j] is kept in the p-th row */ sv_ind[j_ptr] = sv_ind[j_end]; vc_len[j]--; } /* walk through the q-th (pivot) column, which doesn't contain the pivot v[p,q] already, and perform gaussian elimination */ while (q_beg <= q_end) { /* element v[i,q] should be eliminated */ /* get row index of v[i,q] */ i = sv_ind[q_beg]; /* remove the i-th row from the active set; later this row will return there with new length */ if (rs_prev[i] == 0) rs_head[vr_len[i]] = rs_next[i]; else rs_next[rs_prev[i]] = rs_next[i]; if (rs_next[i] == 0) ; else rs_prev[rs_next[i]] = rs_prev[i]; /* find v[i,q] in the i-th row */ i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; for (i_ptr = i_beg; sv_ind[i_ptr] != q; i_ptr++) /* nop */; xassert(i_ptr <= i_end); /* compute gaussian multiplier f[i,p] = v[i,q] / v[p,q] */ fip = sv_val[i_ptr] / vpq; /* since v[i,q] should be eliminated, remove it from the i-th row */ sv_ind[i_ptr] = sv_ind[i_end]; sv_val[i_ptr] = sv_val[i_end]; vr_len[i]--; i_end--; /* and from the q-th column */ sv_ind[q_beg] = sv_ind[q_end]; vc_len[q]--; q_end--; /* perform gaussian transformation: (i-th row) := (i-th row) - f[i,p] * (p-th row) note that now the p-th row, which is in the working array, doesn't contain the pivot v[p,q], and the i-th row doesn't contain the eliminated element v[i,q] */ /* walk through the i-th row and transform existing non-zero elements */ fill = vr_len[p]; for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) { /* get column index of v[i,j] */ j = sv_ind[i_ptr]; /* v[i,j] := v[i,j] - f[i,p] * v[p,j] */ if (flag[j]) { /* v[p,j] != 0 */ temp = (sv_val[i_ptr] -= fip * work[j]); if (temp < 0.0) temp = - temp; flag[j] = 0; fill--; /* since both v[i,j] and v[p,j] exist */ if (temp == 0.0 || temp < eps_tol) { /* new v[i,j] is closer to zero; replace it by exact zero, i.e. remove it from the active submatrix */ /* remove v[i,j] from the i-th row */ sv_ind[i_ptr] = sv_ind[i_end]; sv_val[i_ptr] = sv_val[i_end]; vr_len[i]--; i_ptr--; i_end--; /* find v[i,j] in the j-th column */ j_beg = vc_ptr[j]; j_end = j_beg + vc_len[j] - 1; for (j_ptr = j_beg; sv_ind[j_ptr] != i; j_ptr++); xassert(j_ptr <= j_end); /* remove v[i,j] from the j-th column */ sv_ind[j_ptr] = sv_ind[j_end]; vc_len[j]--; } else { /* v_big := max(v_big, |v[i,j]|) */ if (luf->big_v < temp) luf->big_v = temp; } } } /* now flag is the pattern of the set v[p,*] \ v[i,*], and fill is number of non-zeros in this set; therefore up to fill new non-zeros may appear in the i-th row */ if (vr_len[i] + fill > vr_cap[i]) { /* enlarge the i-th row */ if (luf_enlarge_row(luf, i, vr_len[i] + fill)) { /* overflow of the sparse vector area */ ret = 1; goto done; } /* defragmentation may change row and column pointers of the matrix V */ p_beg = vr_ptr[p]; p_end = p_beg + vr_len[p] - 1; q_beg = vc_ptr[q]; q_end = q_beg + vc_len[q] - 1; } /* walk through the p-th (pivot) row and create new elements of the i-th row that appear due to fill-in; column indices of these new elements are accumulated in the array ndx */ len = 0; for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) { /* get column index of v[p,j], which may cause fill-in */ j = sv_ind[p_ptr]; if (flag[j]) { /* compute new non-zero v[i,j] = 0 - f[i,p] * v[p,j] */ temp = (val = - fip * work[j]); if (temp < 0.0) temp = - temp; if (temp == 0.0 || temp < eps_tol) /* if v[i,j] is closer to zero; just ignore it */; else { /* add v[i,j] to the i-th row */ i_ptr = vr_ptr[i] + vr_len[i]; sv_ind[i_ptr] = j; sv_val[i_ptr] = val; vr_len[i]++; /* remember column index of v[i,j] */ ndx[++len] = j; /* big_v := max(big_v, |v[i,j]|) */ if (luf->big_v < temp) luf->big_v = temp; } } else { /* there is no fill-in, because v[i,j] already exists in the i-th row; restore the flag of the element v[p,j], which was reset before */ flag[j] = 1; } } /* add new non-zeros v[i,j] to the corresponding columns */ for (k = 1; k <= len; k++) { /* get column index of new non-zero v[i,j] */ j = ndx[k]; /* one free location is needed in the j-th column */ if (vc_len[j] + 1 > vc_cap[j]) { /* enlarge the j-th column */ if (luf_enlarge_col(luf, j, vc_len[j] + 10)) { /* overflow of the sparse vector area */ ret = 1; goto done; } /* defragmentation may change row and column pointers of the matrix V */ p_beg = vr_ptr[p]; p_end = p_beg + vr_len[p] - 1; q_beg = vc_ptr[q]; q_end = q_beg + vc_len[q] - 1; } /* add new non-zero v[i,j] to the j-th column */ j_ptr = vc_ptr[j] + vc_len[j]; sv_ind[j_ptr] = i; vc_len[j]++; } /* now the i-th row has been completely transformed, therefore it can return to the active set with new length */ rs_prev[i] = 0; rs_next[i] = rs_head[vr_len[i]]; if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; rs_head[vr_len[i]] = i; /* the largest of absolute values of elements in the i-th row is currently unknown */ vr_max[i] = -1.0; /* at least one free location is needed to store the gaussian multiplier */ if (luf->sv_end - luf->sv_beg < 1) { /* there are no free locations at all; defragment SVA */ luf_defrag_sva(luf); if (luf->sv_end - luf->sv_beg < 1) { /* overflow of the sparse vector area */ ret = 1; goto done; } /* defragmentation may change row and column pointers of the matrix V */ p_beg = vr_ptr[p]; p_end = p_beg + vr_len[p] - 1; q_beg = vc_ptr[q]; q_end = q_beg + vc_len[q] - 1; } /* add the element f[i,p], which is the gaussian multiplier, to the matrix F */ luf->sv_end--; sv_ind[luf->sv_end] = i; sv_val[luf->sv_end] = fip; fc_len[p]++; /* end of elimination loop */ } /* at this point the q-th (pivot) column should be empty */ xassert(vc_len[q] == 0); /* reset capacity of the q-th column */ vc_cap[q] = 0; /* remove node of the q-th column from the addressing list */ k = n + q; if (sv_prev[k] == 0) luf->sv_head = sv_next[k]; else sv_next[sv_prev[k]] = sv_next[k]; if (sv_next[k] == 0) luf->sv_tail = sv_prev[k]; else sv_prev[sv_next[k]] = sv_prev[k]; /* the p-th column of the matrix F has been completely built; set its pointer */ fc_ptr[p] = luf->sv_end; /* walk through the p-th (pivot) row and do the following... */ for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++) { /* get column index of v[p,j] */ j = sv_ind[p_ptr]; /* erase v[p,j] from the working array */ flag[j] = 0; work[j] = 0.0; /* the j-th column has been completely transformed, therefore it can return to the active set with new length; however the special case c_prev[j] = c_next[j] = j means that the routine find_pivot excluded the j-th column from the active set due to Uwe Suhl's rule, and therefore in this case the column can return to the active set only if it is a column singleton */ if (!(vc_len[j] != 1 && cs_prev[j] == j && cs_next[j] == j)) { cs_prev[j] = 0; cs_next[j] = cs_head[vc_len[j]]; if (cs_next[j] != 0) cs_prev[cs_next[j]] = j; cs_head[vc_len[j]] = j; } } done: /* return to the factorizing routine */ return ret; } /*********************************************************************** * build_v_cols - build the matrix V in column-wise format * * This routine builds the column-wise representation of the matrix V * using its row-wise representation. * * If no error occured, the routine returns zero. Otherwise, in case of * overflow of the sparse vector area, the routine returns non-zero. */ static int build_v_cols(LUF *luf) { int n = luf->n; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *vc_cap = luf->vc_cap; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int *sv_prev = luf->sv_prev; int *sv_next = luf->sv_next; int ret = 0; int i, i_beg, i_end, i_ptr, j, j_ptr, k, nnz; /* it is assumed that on entry all columns of the matrix V are empty, i.e. vc_len[j] = vc_cap[j] = 0 for all j = 1, ..., n, and have been removed from the addressing list */ /* count non-zeros in columns of the matrix V; count total number of non-zeros in this matrix */ nnz = 0; for (i = 1; i <= n; i++) { /* walk through elements of the i-th row and count non-zeros in the corresponding columns */ i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) vc_cap[sv_ind[i_ptr]]++; /* count total number of non-zeros */ nnz += vr_len[i]; } /* store total number of non-zeros */ luf->nnz_v = nnz; /* check for free locations */ if (luf->sv_end - luf->sv_beg < nnz) { /* overflow of the sparse vector area */ ret = 1; goto done; } /* allocate columns of the matrix V */ for (j = 1; j <= n; j++) { /* set pointer to the j-th column */ vc_ptr[j] = luf->sv_beg; /* reserve locations for the j-th column */ luf->sv_beg += vc_cap[j]; } /* build the matrix V in column-wise format using this matrix in row-wise format */ for (i = 1; i <= n; i++) { /* walk through elements of the i-th row */ i_beg = vr_ptr[i]; i_end = i_beg + vr_len[i] - 1; for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) { /* get column index */ j = sv_ind[i_ptr]; /* store element in the j-th column */ j_ptr = vc_ptr[j] + vc_len[j]; sv_ind[j_ptr] = i; sv_val[j_ptr] = sv_val[i_ptr]; /* increase length of the j-th column */ vc_len[j]++; } } /* now columns are placed in the sparse vector area behind rows in the order n+1, n+2, ..., n+n; so insert column nodes in the addressing list using this order */ for (k = n+1; k <= n+n; k++) { sv_prev[k] = k-1; sv_next[k] = k+1; } sv_prev[n+1] = luf->sv_tail; sv_next[luf->sv_tail] = n+1; sv_next[n+n] = 0; luf->sv_tail = n+n; done: /* return to the factorizing routine */ return ret; } /*********************************************************************** * build_f_rows - build the matrix F in row-wise format * * This routine builds the row-wise representation of the matrix F using * its column-wise representation. * * If no error occured, the routine returns zero. Otherwise, in case of * overflow of the sparse vector area, the routine returns non-zero. */ static int build_f_rows(LUF *luf) { int n = luf->n; int *fr_ptr = luf->fr_ptr; int *fr_len = luf->fr_len; int *fc_ptr = luf->fc_ptr; int *fc_len = luf->fc_len; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int ret = 0; int i, j, j_beg, j_end, j_ptr, ptr, nnz; /* clear rows of the matrix F */ for (i = 1; i <= n; i++) fr_len[i] = 0; /* count non-zeros in rows of the matrix F; count total number of non-zeros in this matrix */ nnz = 0; for (j = 1; j <= n; j++) { /* walk through elements of the j-th column and count non-zeros in the corresponding rows */ j_beg = fc_ptr[j]; j_end = j_beg + fc_len[j] - 1; for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) fr_len[sv_ind[j_ptr]]++; /* increase total number of non-zeros */ nnz += fc_len[j]; } /* store total number of non-zeros */ luf->nnz_f = nnz; /* check for free locations */ if (luf->sv_end - luf->sv_beg < nnz) { /* overflow of the sparse vector area */ ret = 1; goto done; } /* allocate rows of the matrix F */ for (i = 1; i <= n; i++) { /* set pointer to the end of the i-th row; later this pointer will be set to the beginning of the i-th row */ fr_ptr[i] = luf->sv_end; /* reserve locations for the i-th row */ luf->sv_end -= fr_len[i]; } /* build the matrix F in row-wise format using this matrix in column-wise format */ for (j = 1; j <= n; j++) { /* walk through elements of the j-th column */ j_beg = fc_ptr[j]; j_end = j_beg + fc_len[j] - 1; for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) { /* get row index */ i = sv_ind[j_ptr]; /* store element in the i-th row */ ptr = --fr_ptr[i]; sv_ind[ptr] = j; sv_val[ptr] = sv_val[j_ptr]; } } done: /* return to the factorizing routine */ return ret; } /*********************************************************************** * NAME * * luf_factorize - compute LU-factorization * * SYNOPSIS * * #include "glpluf.h" * int luf_factorize(LUF *luf, int n, int (*col)(void *info, int j, * int ind[], double val[]), void *info); * * DESCRIPTION * * The routine luf_factorize computes LU-factorization of a specified * square matrix A. * * The parameter luf specifies LU-factorization program object created * by the routine luf_create_it. * * The parameter n specifies the order of A, n > 0. * * The formal routine col specifies the matrix A to be factorized. To * obtain j-th column of A the routine luf_factorize calls the routine * col with the parameter j (1 <= j <= n). In response the routine col * should store row indices and numerical values of non-zero elements * of j-th column of A to locations ind[1,...,len] and val[1,...,len], * respectively, where len is the number of non-zeros in j-th column * returned on exit. Neither zero nor duplicate elements are allowed. * * The parameter info is a transit pointer passed to the routine col. * * RETURNS * * 0 LU-factorization has been successfully computed. * * LUF_ESING * The specified matrix is singular within the working precision. * (On some elimination step the active submatrix is exactly zero, * so no pivot can be chosen.) * * LUF_ECOND * The specified matrix is ill-conditioned. * (On some elimination step too intensive growth of elements of the * active submatix has been detected.) * * If matrix A is well scaled, the return code LUF_ECOND may also mean * that the threshold pivoting tolerance piv_tol should be increased. * * In case of non-zero return code the factorization becomes invalid. * It should not be used in other operations until the cause of failure * has been eliminated and the factorization has been recomputed again * with the routine luf_factorize. * * REPAIRING SINGULAR MATRIX * * If the routine luf_factorize returns non-zero code, it provides all * necessary information that can be used for "repairing" the matrix A, * where "repairing" means replacing linearly dependent columns of the * matrix A by appropriate columns of the unity matrix. This feature is * needed when this routine is used for factorizing the basis matrix * within the simplex method procedure. * * On exit linearly dependent columns of the (partially transformed) * matrix U have numbers rank+1, rank+2, ..., n, where rank is estimated * rank of the matrix A stored by the routine to the member luf->rank. * The correspondence between columns of A and U is the same as between * columns of V and U. Thus, linearly dependent columns of the matrix A * have numbers qq_col[rank+1], qq_col[rank+2], ..., qq_col[n], where * qq_col is the column-like representation of the permutation matrix Q. * It is understood that each j-th linearly dependent column of the * matrix U should be replaced by the unity vector, where all elements * are zero except the unity diagonal element u[j,j]. On the other hand * j-th row of the matrix U corresponds to the row of the matrix V (and * therefore of the matrix A) with the number pp_row[j], where pp_row is * the row-like representation of the permutation matrix P. Thus, each * j-th linearly dependent column of the matrix U should be replaced by * column of the unity matrix with the number pp_row[j]. * * The code that repairs the matrix A may look like follows: * * for (j = rank+1; j <= n; j++) * { replace the column qq_col[j] of the matrix A by the column * pp_row[j] of the unity matrix; * } * * where rank, pp_row, and qq_col are members of the structure LUF. */ int luf_factorize(LUF *luf, int n, int (*col)(void *info, int j, int ind[], double val[]), void *info) { int *pp_row, *pp_col, *qq_row, *qq_col; double max_gro = luf->max_gro; int i, j, k, p, q, t, ret; if (n < 1) xfault("luf_factorize: n = %d; invalid parameter\n", n); if (n > N_MAX) xfault("luf_factorize: n = %d; matrix too big\n", n); /* invalidate the factorization */ luf->valid = 0; /* reallocate arrays, if necessary */ reallocate(luf, n); pp_row = luf->pp_row; pp_col = luf->pp_col; qq_row = luf->qq_row; qq_col = luf->qq_col; /* estimate initial size of the SVA, if not specified */ if (luf->sv_size == 0 && luf->new_sva == 0) luf->new_sva = 5 * (n + 10); more: /* reallocate the sparse vector area, if required */ if (luf->new_sva > 0) { if (luf->sv_ind != NULL) xfree(luf->sv_ind); if (luf->sv_val != NULL) xfree(luf->sv_val); luf->sv_size = luf->new_sva; luf->sv_ind = xcalloc(1+luf->sv_size, sizeof(int)); luf->sv_val = xcalloc(1+luf->sv_size, sizeof(double)); luf->new_sva = 0; } /* initialize LU-factorization data structures */ if (initialize(luf, col, info)) { /* overflow of the sparse vector area */ luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); goto more; } /* main elimination loop */ for (k = 1; k <= n; k++) { /* choose a pivot element v[p,q] */ if (find_pivot(luf, &p, &q)) { /* no pivot can be chosen, because the active submatrix is exactly zero */ luf->rank = k - 1; ret = LUF_ESING; goto done; } /* let v[p,q] correspond to u[i',j']; permute k-th and i'-th rows and k-th and j'-th columns of the matrix U = P*V*Q to move the element u[i',j'] to the position u[k,k] */ i = pp_col[p], j = qq_row[q]; xassert(k <= i && i <= n && k <= j && j <= n); /* permute k-th and i-th rows of the matrix U */ t = pp_row[k]; pp_row[i] = t, pp_col[t] = i; pp_row[k] = p, pp_col[p] = k; /* permute k-th and j-th columns of the matrix U */ t = qq_col[k]; qq_col[j] = t, qq_row[t] = j; qq_col[k] = q, qq_row[q] = k; /* eliminate subdiagonal elements of k-th column of the matrix U = P*V*Q using the pivot element u[k,k] = v[p,q] */ if (eliminate(luf, p, q)) { /* overflow of the sparse vector area */ luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); goto more; } /* check relative growth of elements of the matrix V */ if (luf->big_v > max_gro * luf->max_a) { /* the growth is too intensive, therefore most probably the matrix A is ill-conditioned */ luf->rank = k - 1; ret = LUF_ECOND; goto done; } } /* now the matrix U = P*V*Q is upper triangular, the matrix V has been built in row-wise format, and the matrix F has been built in column-wise format */ /* defragment the sparse vector area in order to merge all free locations in one continuous extent */ luf_defrag_sva(luf); /* build the matrix V in column-wise format */ if (build_v_cols(luf)) { /* overflow of the sparse vector area */ luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); goto more; } /* build the matrix F in row-wise format */ if (build_f_rows(luf)) { /* overflow of the sparse vector area */ luf->new_sva = luf->sv_size + luf->sv_size; xassert(luf->new_sva > luf->sv_size); goto more; } /* the LU-factorization has been successfully computed */ luf->valid = 1; luf->rank = n; ret = 0; /* if there are few free locations in the sparse vector area, try increasing its size in the future */ t = 3 * (n + luf->nnz_v) + 2 * luf->nnz_f; if (luf->sv_size < t) { luf->new_sva = luf->sv_size; while (luf->new_sva < t) { k = luf->new_sva; luf->new_sva = k + k; xassert(luf->new_sva > k); } } done: /* return to the calling program */ return ret; } /*********************************************************************** * NAME * * luf_f_solve - solve system F*x = b or F'*x = b * * SYNOPSIS * * #include "glpluf.h" * void luf_f_solve(LUF *luf, int tr, double x[]); * * DESCRIPTION * * The routine luf_f_solve solves either the system F*x = b (if the * flag tr is zero) or the system F'*x = b (if the flag tr is non-zero), * where the matrix F is a component of LU-factorization specified by * the parameter luf, F' is a matrix transposed to F. * * On entry the array x should contain elements of the right-hand side * vector b in locations x[1], ..., x[n], where n is the order of the * matrix F. On exit this array will contain elements of the solution * vector x in the same locations. */ void luf_f_solve(LUF *luf, int tr, double x[]) { int n = luf->n; int *fr_ptr = luf->fr_ptr; int *fr_len = luf->fr_len; int *fc_ptr = luf->fc_ptr; int *fc_len = luf->fc_len; int *pp_row = luf->pp_row; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; int i, j, k, beg, end, ptr; double xk; if (!luf->valid) xfault("luf_f_solve: LU-factorization is not valid\n"); if (!tr) { /* solve the system F*x = b */ for (j = 1; j <= n; j++) { k = pp_row[j]; xk = x[k]; if (xk != 0.0) { beg = fc_ptr[k]; end = beg + fc_len[k] - 1; for (ptr = beg; ptr <= end; ptr++) x[sv_ind[ptr]] -= sv_val[ptr] * xk; } } } else { /* solve the system F'*x = b */ for (i = n; i >= 1; i--) { k = pp_row[i]; xk = x[k]; if (xk != 0.0) { beg = fr_ptr[k]; end = beg + fr_len[k] - 1; for (ptr = beg; ptr <= end; ptr++) x[sv_ind[ptr]] -= sv_val[ptr] * xk; } } } return; } /*********************************************************************** * NAME * * luf_v_solve - solve system V*x = b or V'*x = b * * SYNOPSIS * * #include "glpluf.h" * void luf_v_solve(LUF *luf, int tr, double x[]); * * DESCRIPTION * * The routine luf_v_solve solves either the system V*x = b (if the * flag tr is zero) or the system V'*x = b (if the flag tr is non-zero), * where the matrix V is a component of LU-factorization specified by * the parameter luf, V' is a matrix transposed to V. * * On entry the array x should contain elements of the right-hand side * vector b in locations x[1], ..., x[n], where n is the order of the * matrix V. On exit this array will contain elements of the solution * vector x in the same locations. */ void luf_v_solve(LUF *luf, int tr, double x[]) { int n = luf->n; int *vr_ptr = luf->vr_ptr; int *vr_len = luf->vr_len; double *vr_piv = luf->vr_piv; int *vc_ptr = luf->vc_ptr; int *vc_len = luf->vc_len; int *pp_row = luf->pp_row; int *qq_col = luf->qq_col; int *sv_ind = luf->sv_ind; double *sv_val = luf->sv_val; double *b = luf->work; int i, j, k, beg, end, ptr; double temp; if (!luf->valid) xfault("luf_v_solve: LU-factorization is not valid\n"); for (k = 1; k <= n; k++) b[k] = x[k], x[k] = 0.0; if (!tr) { /* solve the system V*x = b */ for (k = n; k >= 1; k--) { i = pp_row[k], j = qq_col[k]; temp = b[i]; if (temp != 0.0) { x[j] = (temp /= vr_piv[i]); beg = vc_ptr[j]; end = beg + vc_len[j] - 1; for (ptr = beg; ptr <= end; ptr++) b[sv_ind[ptr]] -= sv_val[ptr] * temp; } } } else { /* solve the system V'*x = b */ for (k = 1; k <= n; k++) { i = pp_row[k], j = qq_col[k]; temp = b[j]; if (temp != 0.0) { x[i] = (temp /= vr_piv[i]); beg = vr_ptr[i]; end = beg + vr_len[i] - 1; for (ptr = beg; ptr <= end; ptr++) b[sv_ind[ptr]] -= sv_val[ptr] * temp; } } } return; } /*********************************************************************** * NAME * * luf_a_solve - solve system A*x = b or A'*x = b * * SYNOPSIS * * #include "glpluf.h" * void luf_a_solve(LUF *luf, int tr, double x[]); * * DESCRIPTION * * The routine luf_a_solve solves either the system A*x = b (if the * flag tr is zero) or the system A'*x = b (if the flag tr is non-zero), * where the parameter luf specifies LU-factorization of the matrix A, * A' is a matrix transposed to A. * * On entry the array x should contain elements of the right-hand side * vector b in locations x[1], ..., x[n], where n is the order of the * matrix A. On exit this array will contain elements of the solution * vector x in the same locations. */ void luf_a_solve(LUF *luf, int tr, double x[]) { if (!luf->valid) xfault("luf_a_solve: LU-factorization is not valid\n"); if (!tr) { /* A = F*V, therefore inv(A) = inv(V)*inv(F) */ luf_f_solve(luf, 0, x); luf_v_solve(luf, 0, x); } else { /* A' = V'*F', therefore inv(A') = inv(F')*inv(V') */ luf_v_solve(luf, 1, x); luf_f_solve(luf, 1, x); } return; } /*********************************************************************** * NAME * * luf_delete_it - delete LU-factorization * * SYNOPSIS * * #include "glpluf.h" * void luf_delete_it(LUF *luf); * * DESCRIPTION * * The routine luf_delete deletes LU-factorization specified by the * parameter luf and frees all the memory allocated to this program * object. */ void luf_delete_it(LUF *luf) { if (luf->fr_ptr != NULL) xfree(luf->fr_ptr); if (luf->fr_len != NULL) xfree(luf->fr_len); if (luf->fc_ptr != NULL) xfree(luf->fc_ptr); if (luf->fc_len != NULL) xfree(luf->fc_len); if (luf->vr_ptr != NULL) xfree(luf->vr_ptr); if (luf->vr_len != NULL) xfree(luf->vr_len); if (luf->vr_cap != NULL) xfree(luf->vr_cap); if (luf->vr_piv != NULL) xfree(luf->vr_piv); if (luf->vc_ptr != NULL) xfree(luf->vc_ptr); if (luf->vc_len != NULL) xfree(luf->vc_len); if (luf->vc_cap != NULL) xfree(luf->vc_cap); if (luf->pp_row != NULL) xfree(luf->pp_row); if (luf->pp_col != NULL) xfree(luf->pp_col); if (luf->qq_row != NULL) xfree(luf->qq_row); if (luf->qq_col != NULL) xfree(luf->qq_col); if (luf->sv_ind != NULL) xfree(luf->sv_ind); if (luf->sv_val != NULL) xfree(luf->sv_val); if (luf->sv_prev != NULL) xfree(luf->sv_prev); if (luf->sv_next != NULL) xfree(luf->sv_next); if (luf->vr_max != NULL) xfree(luf->vr_max); if (luf->rs_head != NULL) xfree(luf->rs_head); if (luf->rs_prev != NULL) xfree(luf->rs_prev); if (luf->rs_next != NULL) xfree(luf->rs_next); if (luf->cs_head != NULL) xfree(luf->cs_head); if (luf->cs_prev != NULL) xfree(luf->cs_prev); if (luf->cs_next != NULL) xfree(luf->cs_next); if (luf->flag != NULL) xfree(luf->flag); if (luf->work != NULL) xfree(luf->work); xfree(luf); return; } /* eof */ igraph/src/glpios08.c0000644000176000001440000006741612325527073014200 0ustar ripleyusers/* glpios08.c (clique cut generator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif #include "glpios.h" static double get_row_lb(LPX *lp, int i) { /* this routine returns lower bound of row i or -DBL_MAX if the row has no lower bound */ double lb; switch (lpx_get_row_type(lp, i)) { case LPX_FR: case LPX_UP: lb = -DBL_MAX; break; case LPX_LO: case LPX_DB: case LPX_FX: lb = lpx_get_row_lb(lp, i); break; default: xassert(lp != lp); } return lb; } static double get_row_ub(LPX *lp, int i) { /* this routine returns upper bound of row i or +DBL_MAX if the row has no upper bound */ double ub; switch (lpx_get_row_type(lp, i)) { case LPX_FR: case LPX_LO: ub = +DBL_MAX; break; case LPX_UP: case LPX_DB: case LPX_FX: ub = lpx_get_row_ub(lp, i); break; default: xassert(lp != lp); } return ub; } static double get_col_lb(LPX *lp, int j) { /* this routine returns lower bound of column j or -DBL_MAX if the column has no lower bound */ double lb; switch (lpx_get_col_type(lp, j)) { case LPX_FR: case LPX_UP: lb = -DBL_MAX; break; case LPX_LO: case LPX_DB: case LPX_FX: lb = lpx_get_col_lb(lp, j); break; default: xassert(lp != lp); } return lb; } static double get_col_ub(LPX *lp, int j) { /* this routine returns upper bound of column j or +DBL_MAX if the column has no upper bound */ double ub; switch (lpx_get_col_type(lp, j)) { case LPX_FR: case LPX_LO: ub = +DBL_MAX; break; case LPX_UP: case LPX_DB: case LPX_FX: ub = lpx_get_col_ub(lp, j); break; default: xassert(lp != lp); } return ub; } static int is_binary(LPX *lp, int j) { /* this routine checks if variable x[j] is binary */ return lpx_get_col_kind(lp, j) == LPX_IV && lpx_get_col_type(lp, j) == LPX_DB && lpx_get_col_lb(lp, j) == 0.0 && lpx_get_col_ub(lp, j) == 1.0; } static double eval_lf_min(LPX *lp, int len, int ind[], double val[]) { /* this routine computes the minimum of a specified linear form sum a[j]*x[j] j using the formula: min = sum a[j]*lb[j] + sum a[j]*ub[j], j in J+ j in J- where J+ = {j: a[j] > 0}, J- = {j: a[j] < 0}, lb[j] and ub[j] are lower and upper bound of variable x[j], resp. */ int j, t; double lb, ub, sum; sum = 0.0; for (t = 1; t <= len; t++) { j = ind[t]; if (val[t] > 0.0) { lb = get_col_lb(lp, j); if (lb == -DBL_MAX) { sum = -DBL_MAX; break; } sum += val[t] * lb; } else if (val[t] < 0.0) { ub = get_col_ub(lp, j); if (ub == +DBL_MAX) { sum = -DBL_MAX; break; } sum += val[t] * ub; } else xassert(val != val); } return sum; } static double eval_lf_max(LPX *lp, int len, int ind[], double val[]) { /* this routine computes the maximum of a specified linear form sum a[j]*x[j] j using the formula: max = sum a[j]*ub[j] + sum a[j]*lb[j], j in J+ j in J- where J+ = {j: a[j] > 0}, J- = {j: a[j] < 0}, lb[j] and ub[j] are lower and upper bound of variable x[j], resp. */ int j, t; double lb, ub, sum; sum = 0.0; for (t = 1; t <= len; t++) { j = ind[t]; if (val[t] > 0.0) { ub = get_col_ub(lp, j); if (ub == +DBL_MAX) { sum = +DBL_MAX; break; } sum += val[t] * ub; } else if (val[t] < 0.0) { lb = get_col_lb(lp, j); if (lb == -DBL_MAX) { sum = +DBL_MAX; break; } sum += val[t] * lb; } else xassert(val != val); } return sum; } /*---------------------------------------------------------------------- -- probing - determine logical relation between binary variables. -- -- This routine tentatively sets a binary variable to 0 and then to 1 -- and examines whether another binary variable is caused to be fixed. -- -- The examination is based only on one row (constraint), which is the -- following: -- -- L <= sum a[j]*x[j] <= U. (1) -- j -- -- Let x[p] be a probing variable, x[q] be an examined variable. Then -- (1) can be written as: -- -- L <= sum a[j]*x[j] + a[p]*x[p] + a[q]*x[q] <= U, (2) -- j in J' -- -- where J' = {j: j != p and j != q}. -- -- Let -- -- L' = L - a[p]*x[p], (3) -- -- U' = U - a[p]*x[p], (4) -- -- where x[p] is assumed to be fixed at 0 or 1. So (2) can be rewritten -- as follows: -- -- L' <= sum a[j]*x[j] + a[q]*x[q] <= U', (5) -- j in J' -- -- from where we have: -- -- L' - sum a[j]*x[j] <= a[q]*x[q] <= U' - sum a[j]*x[j]. (6) -- j in J' j in J' -- -- Thus, -- -- min a[q]*x[q] = L' - MAX, (7) -- -- max a[q]*x[q] = U' - MIN, (8) -- -- where -- -- MIN = min sum a[j]*x[j], (9) -- j in J' -- -- MAX = max sum a[j]*x[j]. (10) -- j in J' -- -- Formulae (7) and (8) allows determining implied lower and upper -- bounds of x[q]. -- -- Parameters len, val, L and U specify the constraint (1). -- -- Parameters lf_min and lf_max specify implied lower and upper bounds -- of the linear form (1). It is assumed that these bounds are computed -- with the routines eval_lf_min and eval_lf_max (see above). -- -- Parameter p specifies the probing variable x[p], which is set to 0 -- (if set is 0) or to 1 (if set is 1). -- -- Parameter q specifies the examined variable x[q]. -- -- On exit the routine returns one of the following codes: -- -- 0 - there is no logical relation between x[p] and x[q]; -- 1 - x[q] can take only on value 0; -- 2 - x[q] can take only on value 1. */ static int probing(int len, double val[], double L, double U, double lf_min, double lf_max, int p, int set, int q) { double temp; xassert(1 <= p && p < q && q <= len); /* compute L' (3) */ if (L != -DBL_MAX && set) L -= val[p]; /* compute U' (4) */ if (U != +DBL_MAX && set) U -= val[p]; /* compute MIN (9) */ if (lf_min != -DBL_MAX) { if (val[p] < 0.0) lf_min -= val[p]; if (val[q] < 0.0) lf_min -= val[q]; } /* compute MAX (10) */ if (lf_max != +DBL_MAX) { if (val[p] > 0.0) lf_max -= val[p]; if (val[q] > 0.0) lf_max -= val[q]; } /* compute implied lower bound of x[q]; see (7), (8) */ if (val[q] > 0.0) { if (L == -DBL_MAX || lf_max == +DBL_MAX) temp = -DBL_MAX; else temp = (L - lf_max) / val[q]; } else { if (U == +DBL_MAX || lf_min == -DBL_MAX) temp = -DBL_MAX; else temp = (U - lf_min) / val[q]; } if (temp > 0.001) return 2; /* compute implied upper bound of x[q]; see (7), (8) */ if (val[q] > 0.0) { if (U == +DBL_MAX || lf_min == -DBL_MAX) temp = +DBL_MAX; else temp = (U - lf_min) / val[q]; } else { if (L == -DBL_MAX || lf_max == +DBL_MAX) temp = +DBL_MAX; else temp = (L - lf_max) / val[q]; } if (temp < 0.999) return 1; /* there is no logical relation between x[p] and x[q] */ return 0; } struct COG { /* conflict graph; it represents logical relations between binary variables and has a vertex for each binary variable and its complement, and an edge between two vertices when at most one of the variables represented by the vertices can equal one in an optimal solution */ int n; /* number of variables */ int nb; /* number of binary variables represented in the graph (note that not all binary variables can be represented); vertices which correspond to binary variables have numbers 1, ..., nb while vertices which correspond to complements of binary variables have numbers nb+1, ..., nb+nb */ int ne; /* number of edges in the graph */ int *vert; /* int vert[1+n]; */ /* if x[j] is a binary variable represented in the graph, vert[j] is the vertex number corresponding to x[j]; otherwise vert[j] is zero */ int *orig; /* int list[1:nb]; */ /* if vert[j] = k > 0, then orig[k] = j */ unsigned char *a; /* adjacency matrix of the graph having 2*nb rows and columns; only strict lower triangle is stored in dense packed form */ }; /*---------------------------------------------------------------------- -- lpx_create_cog - create the conflict graph. -- -- SYNOPSIS -- -- #include "glplpx.h" -- void *lpx_create_cog(LPX *lp); -- -- DESCRIPTION -- -- The routine lpx_create_cog creates the conflict graph for a given -- problem instance. -- -- RETURNS -- -- If the graph has been created, the routine returns a pointer to it. -- Otherwise the routine returns NULL. */ #define MAX_NB 4000 #define MAX_ROW_LEN 500 static void lpx_add_cog_edge(void *_cog, int i, int j); static void *lpx_create_cog(LPX *lp) { struct COG *cog = NULL; int m, n, nb, i, j, p, q, len, *ind, *vert, *orig; double L, U, lf_min, lf_max, *val; xprintf("Creating the conflict graph...\n"); m = lpx_get_num_rows(lp); n = lpx_get_num_cols(lp); /* determine which binary variables should be included in the conflict graph */ nb = 0; vert = xcalloc(1+n, sizeof(int)); for (j = 1; j <= n; j++) vert[j] = 0; orig = xcalloc(1+n, sizeof(int)); ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); for (i = 1; i <= m; i++) { L = get_row_lb(lp, i); U = get_row_ub(lp, i); if (L == -DBL_MAX && U == +DBL_MAX) continue; len = lpx_get_mat_row(lp, i, ind, val); if (len > MAX_ROW_LEN) continue; lf_min = eval_lf_min(lp, len, ind, val); lf_max = eval_lf_max(lp, len, ind, val); for (p = 1; p <= len; p++) { if (!is_binary(lp, ind[p])) continue; for (q = p+1; q <= len; q++) { if (!is_binary(lp, ind[q])) continue; if (probing(len, val, L, U, lf_min, lf_max, p, 0, q) || probing(len, val, L, U, lf_min, lf_max, p, 1, q)) { /* there is a logical relation */ /* include the first variable in the graph */ j = ind[p]; if (vert[j] == 0) nb++, vert[j] = nb, orig[nb] = j; /* incude the second variable in the graph */ j = ind[q]; if (vert[j] == 0) nb++, vert[j] = nb, orig[nb] = j; } } } } /* if the graph is either empty or has too many vertices, do not create it */ if (nb == 0 || nb > MAX_NB) { xprintf("The conflict graph is either empty or too big\n"); xfree(vert); xfree(orig); goto done; } /* create the conflict graph */ cog = xmalloc(sizeof(struct COG)); cog->n = n; cog->nb = nb; cog->ne = 0; cog->vert = vert; cog->orig = orig; len = nb + nb; /* number of vertices */ len = (len * (len - 1)) / 2; /* number of entries in triangle */ len = (len + (CHAR_BIT - 1)) / CHAR_BIT; /* bytes needed */ cog->a = xmalloc(len); memset(cog->a, 0, len); for (j = 1; j <= nb; j++) { /* add edge between variable and its complement */ lpx_add_cog_edge(cog, +orig[j], -orig[j]); } for (i = 1; i <= m; i++) { L = get_row_lb(lp, i); U = get_row_ub(lp, i); if (L == -DBL_MAX && U == +DBL_MAX) continue; len = lpx_get_mat_row(lp, i, ind, val); if (len > MAX_ROW_LEN) continue; lf_min = eval_lf_min(lp, len, ind, val); lf_max = eval_lf_max(lp, len, ind, val); for (p = 1; p <= len; p++) { if (!is_binary(lp, ind[p])) continue; for (q = p+1; q <= len; q++) { if (!is_binary(lp, ind[q])) continue; /* set x[p] to 0 and examine x[q] */ switch (probing(len, val, L, U, lf_min, lf_max, p, 0, q)) { case 0: /* no logical relation */ break; case 1: /* x[p] = 0 implies x[q] = 0 */ lpx_add_cog_edge(cog, -ind[p], +ind[q]); break; case 2: /* x[p] = 0 implies x[q] = 1 */ lpx_add_cog_edge(cog, -ind[p], -ind[q]); break; default: xassert(lp != lp); } /* set x[p] to 1 and examine x[q] */ switch (probing(len, val, L, U, lf_min, lf_max, p, 1, q)) { case 0: /* no logical relation */ break; case 1: /* x[p] = 1 implies x[q] = 0 */ lpx_add_cog_edge(cog, +ind[p], +ind[q]); break; case 2: /* x[p] = 1 implies x[q] = 1 */ lpx_add_cog_edge(cog, +ind[p], -ind[q]); break; default: xassert(lp != lp); } } } } xprintf("The conflict graph has 2*%d vertices and %d edges\n", cog->nb, cog->ne); done: xfree(ind); xfree(val); return cog; } /*---------------------------------------------------------------------- -- lpx_add_cog_edge - add edge to the conflict graph. -- -- SYNOPSIS -- -- #include "glplpx.h" -- void lpx_add_cog_edge(void *cog, int i, int j); -- -- DESCRIPTION -- -- The routine lpx_add_cog_edge adds an edge to the conflict graph. -- The edge connects x[i] (if i > 0) or its complement (if i < 0) and -- x[j] (if j > 0) or its complement (if j < 0), where i and j are -- original ordinal numbers of corresponding variables. */ static void lpx_add_cog_edge(void *_cog, int i, int j) { struct COG *cog = _cog; int k; xassert(i != j); /* determine indices of corresponding vertices */ if (i > 0) { xassert(1 <= i && i <= cog->n); i = cog->vert[i]; xassert(i != 0); } else { i = -i; xassert(1 <= i && i <= cog->n); i = cog->vert[i]; xassert(i != 0); i += cog->nb; } if (j > 0) { xassert(1 <= j && j <= cog->n); j = cog->vert[j]; xassert(j != 0); } else { j = -j; xassert(1 <= j && j <= cog->n); j = cog->vert[j]; xassert(j != 0); j += cog->nb; } /* only lower triangle is stored, so we need i > j */ if (i < j) k = i, i = j, j = k; k = ((i - 1) * (i - 2)) / 2 + (j - 1); cog->a[k / CHAR_BIT] |= (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); cog->ne++; return; } /*---------------------------------------------------------------------- -- MAXIMUM WEIGHT CLIQUE -- -- Two subroutines sub() and wclique() below are intended to find a -- maximum weight clique in a given undirected graph. These subroutines -- are slightly modified version of the program WCLIQUE developed by -- Patric Ostergard and based -- on ideas from the article "P. R. J. Ostergard, A new algorithm for -- the maximum-weight clique problem, submitted for publication", which -- in turn is a generalization of the algorithm for unweighted graphs -- presented in "P. R. J. Ostergard, A fast algorithm for the maximum -- clique problem, submitted for publication". -- -- USED WITH PERMISSION OF THE AUTHOR OF THE ORIGINAL CODE. */ struct dsa { /* dynamic storage area */ int n; /* number of vertices */ int *wt; /* int wt[0:n-1]; */ /* weights */ unsigned char *a; /* adjacency matrix (packed lower triangle without main diag.) */ int record; /* weight of best clique */ int rec_level; /* number of vertices in best clique */ int *rec; /* int rec[0:n-1]; */ /* best clique so far */ int *clique; /* int clique[0:n-1]; */ /* table for pruning */ int *set; /* int set[0:n-1]; */ /* current clique */ }; #define n (dsa->n) #define wt (dsa->wt) #define a (dsa->a) #define record (dsa->record) #define rec_level (dsa->rec_level) #define rec (dsa->rec) #define clique (dsa->clique) #define set (dsa->set) #if 0 static int is_edge(struct dsa *dsa, int i, int j) { /* if there is arc (i,j), the routine returns true; otherwise false; 0 <= i, j < n */ int k; xassert(0 <= i && i < n); xassert(0 <= j && j < n); if (i == j) return 0; if (i < j) k = i, i = j, j = k; k = (i * (i - 1)) / 2 + j; return a[k / CHAR_BIT] & (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); } #else #define is_edge(dsa, i, j) ((i) == (j) ? 0 : \ (i) > (j) ? is_edge1(i, j) : is_edge1(j, i)) #define is_edge1(i, j) is_edge2(((i) * ((i) - 1)) / 2 + (j)) #define is_edge2(k) (a[(k) / CHAR_BIT] & \ (unsigned char)(1 << ((CHAR_BIT - 1) - (k) % CHAR_BIT))) #endif static void sub(struct dsa *dsa, int ct, int table[], int level, int weight, int l_weight) { int i, j, k, curr_weight, left_weight, *p1, *p2, *newtable; newtable = xcalloc(n, sizeof(int)); if (ct <= 0) { /* 0 or 1 elements left; include these */ if (ct == 0) { set[level++] = table[0]; weight += l_weight; } if (weight > record) { record = weight; rec_level = level; for (i = 0; i < level; i++) rec[i] = set[i]; } goto done; } for (i = ct; i >= 0; i--) { if ((level == 0) && (i < ct)) goto done; k = table[i]; if ((level > 0) && (clique[k] <= (record - weight))) goto done; /* prune */ set[level] = k; curr_weight = weight + wt[k]; l_weight -= wt[k]; if (l_weight <= (record - curr_weight)) goto done; /* prune */ p1 = newtable; p2 = table; left_weight = 0; while (p2 < table + i) { j = *p2++; if (is_edge(dsa, j, k)) { *p1++ = j; left_weight += wt[j]; } } if (left_weight <= (record - curr_weight)) continue; sub(dsa, p1 - newtable - 1, newtable, level + 1, curr_weight, left_weight); } done: xfree(newtable); return; } static int wclique(int _n, int w[], unsigned char _a[], int sol[]) { struct dsa _dsa, *dsa = &_dsa; int i, j, p, max_wt, max_nwt, wth, *used, *nwt, *pos; glp_long timer; n = _n; wt = &w[1]; a = _a; record = 0; rec_level = 0; rec = &sol[1]; clique = xcalloc(n, sizeof(int)); set = xcalloc(n, sizeof(int)); used = xcalloc(n, sizeof(int)); nwt = xcalloc(n, sizeof(int)); pos = xcalloc(n, sizeof(int)); /* start timer */ timer = xtime(); /* order vertices */ for (i = 0; i < n; i++) { nwt[i] = 0; for (j = 0; j < n; j++) if (is_edge(dsa, i, j)) nwt[i] += wt[j]; } for (i = 0; i < n; i++) used[i] = 0; for (i = n-1; i >= 0; i--) { max_wt = -1; max_nwt = -1; for (j = 0; j < n; j++) { if ((!used[j]) && ((wt[j] > max_wt) || (wt[j] == max_wt && nwt[j] > max_nwt))) { max_wt = wt[j]; max_nwt = nwt[j]; p = j; } } pos[i] = p; used[p] = 1; for (j = 0; j < n; j++) if ((!used[j]) && (j != p) && (is_edge(dsa, p, j))) nwt[j] -= wt[p]; } /* main routine */ wth = 0; for (i = 0; i < n; i++) { wth += wt[pos[i]]; sub(dsa, i, pos, 0, 0, wth); clique[pos[i]] = record; #if 0 if (utime() >= timer + 5.0) #else if (xdifftime(xtime(), timer) >= 5.0 - 0.001) #endif { /* print current record and reset timer */ xprintf("level = %d (%d); best = %d\n", i+1, n, record); #if 0 timer = utime(); #else timer = xtime(); #endif } } xfree(clique); xfree(set); xfree(used); xfree(nwt); xfree(pos); /* return the solution found */ for (i = 1; i <= rec_level; i++) sol[i]++; return rec_level; } #undef n #undef wt #undef a #undef record #undef rec_level #undef rec #undef clique #undef set /*---------------------------------------------------------------------- -- lpx_clique_cut - generate cluque cut. -- -- SYNOPSIS -- -- #include "glplpx.h" -- int lpx_clique_cut(LPX *lp, void *cog, int ind[], double val[]); -- -- DESCRIPTION -- -- The routine lpx_clique_cut generates a clique cut using the conflict -- graph specified by the parameter cog. -- -- If a violated clique cut has been found, it has the following form: -- -- sum{j in J} a[j]*x[j] <= b. -- -- Variable indices j in J are stored in elements ind[1], ..., ind[len] -- while corresponding constraint coefficients are stored in elements -- val[1], ..., val[len], where len is returned on exit. The right-hand -- side b is stored in element val[0]. -- -- RETURNS -- -- If the cutting plane has been successfully generated, the routine -- returns 1 <= len <= n, which is the number of non-zero coefficients -- in the inequality constraint. Otherwise, the routine returns zero. */ static int lpx_clique_cut(LPX *lp, void *_cog, int ind[], double val[]) { struct COG *cog = _cog; int n = lpx_get_num_cols(lp); int j, t, v, card, temp, len = 0, *w, *sol; double x, sum, b, *vec; /* allocate working arrays */ w = xcalloc(1 + 2 * cog->nb, sizeof(int)); sol = xcalloc(1 + 2 * cog->nb, sizeof(int)); vec = xcalloc(1+n, sizeof(double)); /* assign weights to vertices of the conflict graph */ for (t = 1; t <= cog->nb; t++) { j = cog->orig[t]; x = lpx_get_col_prim(lp, j); temp = (int)(100.0 * x + 0.5); if (temp < 0) temp = 0; if (temp > 100) temp = 100; w[t] = temp; w[cog->nb + t] = 100 - temp; } /* find a clique of maximum weight */ card = wclique(2 * cog->nb, w, cog->a, sol); /* compute the clique weight for unscaled values */ sum = 0.0; for ( t = 1; t <= card; t++) { v = sol[t]; xassert(1 <= v && v <= 2 * cog->nb); if (v <= cog->nb) { /* vertex v corresponds to binary variable x[j] */ j = cog->orig[v]; x = lpx_get_col_prim(lp, j); sum += x; } else { /* vertex v corresponds to the complement of x[j] */ j = cog->orig[v - cog->nb]; x = lpx_get_col_prim(lp, j); sum += 1.0 - x; } } /* if the sum of binary variables and their complements in the clique greater than 1, the clique cut is violated */ if (sum >= 1.01) { /* construct the inquality */ for (j = 1; j <= n; j++) vec[j] = 0; b = 1.0; for (t = 1; t <= card; t++) { v = sol[t]; if (v <= cog->nb) { /* vertex v corresponds to binary variable x[j] */ j = cog->orig[v]; xassert(1 <= j && j <= n); vec[j] += 1.0; } else { /* vertex v corresponds to the complement of x[j] */ j = cog->orig[v - cog->nb]; xassert(1 <= j && j <= n); vec[j] -= 1.0; b -= 1.0; } } xassert(len == 0); for (j = 1; j <= n; j++) { if (vec[j] != 0.0) { len++; ind[len] = j, val[len] = vec[j]; } } ind[0] = 0, val[0] = b; } /* free working arrays */ xfree(w); xfree(sol); xfree(vec); /* return to the calling program */ return len; } /*---------------------------------------------------------------------- -- lpx_delete_cog - delete the conflict graph. -- -- SYNOPSIS -- -- #include "glplpx.h" -- void lpx_delete_cog(void *cog); -- -- DESCRIPTION -- -- The routine lpx_delete_cog deletes the conflict graph, which the -- parameter cog points to, freeing all the memory allocated to this -- object. */ static void lpx_delete_cog(void *_cog) { struct COG *cog = _cog; xfree(cog->vert); xfree(cog->orig); xfree(cog->a); xfree(cog); } /**********************************************************************/ void *ios_clq_init(glp_tree *tree) { /* initialize clique cut generator */ glp_prob *mip = tree->mip; xassert(mip != NULL); return lpx_create_cog(mip); } /*********************************************************************** * NAME * * ios_clq_gen - generate clique cuts * * SYNOPSIS * * #include "glpios.h" * void ios_clq_gen(glp_tree *tree, void *gen); * * DESCRIPTION * * The routine ios_clq_gen generates clique cuts for the current point * and adds them to the clique pool. */ void ios_clq_gen(glp_tree *tree, void *gen) { int n = lpx_get_num_cols(tree->mip); int len, *ind; double *val; xassert(gen != NULL); ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); len = lpx_clique_cut(tree->mip, gen, ind, val); if (len > 0) { /* xprintf("len = %d\n", len); */ glp_ios_add_row(tree, NULL, GLP_RF_CLQ, 0, len, ind, val, GLP_UP, val[0]); } xfree(ind); xfree(val); return; } /**********************************************************************/ void ios_clq_term(void *gen) { /* terminate clique cut generator */ xassert(gen != NULL); lpx_delete_cog(gen); return; } /* eof */ igraph/src/igraph_pmt_off.h0000644000176000001440000000416312325527073015512 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef ATOMIC #undef ATOMIC #endif #ifdef ATOMIC_IO #undef ATOMIC_IO #endif #ifdef BASE #undef BASE #endif #ifdef BASE_EPSILON #undef BASE_EPSILON #endif #ifdef CONCAT2 #undef CONCAT2 #endif #ifdef CONCAT2x #undef CONCAT2x #endif #ifdef CONCAT3 #undef CONCAT3 #endif #ifdef CONCAT3x #undef CONCAT3x #endif #ifdef CONCAT4 #undef CONCAT4 #endif #ifdef CONCAT4x #undef CONCAT4x #endif #ifdef FP #undef FP #endif #ifdef FUNCTION #undef FUNCTION #endif #ifdef IN_FORMAT #undef IN_FORMAT #endif #ifdef MULTIPLICITY #undef MULTIPLICITY #endif #ifdef ONE #undef ONE #endif #ifdef OUT_FORMAT #undef OUT_FORMAT #endif #ifdef SHORT #undef SHORT #endif #ifdef TYPE #undef TYPE #endif #ifdef ZERO #undef ZERO #endif #ifdef HEAPMORE #undef HEAPMORE #endif #ifdef HEAPLESS #undef HEAPLESS #endif #ifdef HEAPMOREEQ #undef HEAPMOREEQ #endif #ifdef HEAPLESSEQ #undef HEAPLESSEQ #endif #ifdef SUM #undef SUM #endif #ifdef SQ #undef SQ #endif #ifdef PROD #undef PROD #endif #ifdef NOTORDERED #undef NOTORDERED #endif #ifdef EQ #undef EQ #endif #ifdef DIFF #undef DIFF #endif #ifdef DIV #undef DIV #endif #ifdef NOABS #undef NOABS #endif #ifdef PRINTFUNC #undef PRINTFUNC #endif #ifdef FPRINTFUNC #undef PRINTFUNC #endif #ifdef UNSIGNED #undef UNSIGNED #endif igraph/src/random.c0000644000176000001440000016260512325527074014010 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include #include #include "igraph_math.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_memory.h" /** * \section about_rngs * *
* About random numbers in igraph, use cases * * * Some algorithms in igraph, e.g. the generation of random graphs, * require random number generators (RNGs). Prior to version 0.6 * igraph did not have a sophisticated way to deal with random number * generators at the C level, but this has changed. From version 0.6 * different and multiple random number generators are supported. * *
* */ /** * \section rng_use_cases * *
Use cases * *
Normal (default) use * * If the user does not use any of the RNG functions explicitly, but calls * some of the randomized igraph functions, then a default RNG is set * up the first time an igraph function needs random numbers. The * seed of this RNG is the output of the time(0) function * call, using the time function from the standard C * library. This ensures that igraph creates a different random graph, * each time the C program is called. * * * * The created default generator is stored internally and can be * queried with the \ref igraph_rng_default() function. * *
* *
Reproducible simulations * * If reproducible results are needed, then the user should set the * seed of the default random number generator explicitly, using the * \ref igraph_rng_seed() function on the default generator, \ref * igraph_rng_default(). When setting the seed to the same number, * igraph generates exactly the same random graph (or series of random * graphs). * *
* *
Changing the default generator * * By default igraph uses the \ref igraph_rng_default() random number * generator. This can be changed any time by calling \ref * igraph_rng_set_default(), with an already initialized random number * generator. Note that the old (replaced) generator is not * destroyed, so no memory is deallocated. * *
* *
Using multiple generators * * igraph also provides functions to set up multiple random number * generators, using the \ref igraph_rng_init() function, and then * generating random numbers from them, e.g. with \ref igraph_rng_get_integer() * and/or \ref igraph_rng_get_unif() calls. * * * * Note that initializing a new random number generator is * independent of the generator that the igraph functions themselves * use. If you want to replace that, then please use \ref * igraph_rng_set_default(). * *
* *
Example * * \example examples/simple/random_seed.c * *
* *
*/ /* ------------------------------------ */ typedef struct { int i, j; long int x[31]; } igraph_i_rng_glibc2_state_t; unsigned long int igraph_i_rng_glibc2_get(int *i, int *j, int n, long int *x) { unsigned long int k; x[*i] += x[*j]; k = (x[*i] >> 1) & 0x7FFFFFFF; (*i)++; if (*i == n) { *i = 0; } (*j)++ ; if (*j == n) { *j = 0; } return k; } unsigned long int igraph_rng_glibc2_get(void *vstate) { igraph_i_rng_glibc2_state_t *state = (igraph_i_rng_glibc2_state_t*) vstate; return igraph_i_rng_glibc2_get(&state->i, &state->j, 31, state->x); } igraph_real_t igraph_rng_glibc2_get_real(void *state) { return igraph_rng_glibc2_get(state) / 2147483648.0; } /* this function is independent of the bit size */ void igraph_i_rng_glibc2_init(long int *x, int n, unsigned long int s) { int i; if (s==0) { s=1; } x[0] = (long) s; for (i=1 ; ix, 31, seed); state->i=3; state->j=0; for (i=0;i<10*31; i++) { igraph_rng_glibc2_get(state); } return 0; } int igraph_rng_glibc2_init(void **state) { igraph_i_rng_glibc2_state_t *st; st=igraph_Calloc(1, igraph_i_rng_glibc2_state_t); if (!st) { IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM); } (*state)=st; igraph_rng_glibc2_seed(st, 0); return 0; } void igraph_rng_glibc2_destroy(void *vstate) { igraph_i_rng_glibc2_state_t *state = (igraph_i_rng_glibc2_state_t*) vstate; igraph_Free(state); } /** * \var igraph_rngtype_glibc2 * \brief The random number generator type introduced in GNU libc 2 * * It is a linear feedback shift register generator with a 128-byte * buffer. This generator was the default prior to igraph version 0.6, * at least on systems relying on GNU libc. * * This generator was ported from the GNU Scientific Library. */ const igraph_rng_type_t igraph_rngtype_glibc2 = { /* name= */ "LIBC", /* min= */ 0, /* max= */ RAND_MAX, /* init= */ igraph_rng_glibc2_init, /* destroy= */ igraph_rng_glibc2_destroy, /* seed= */ igraph_rng_glibc2_seed, /* get= */ igraph_rng_glibc2_get, /* get_real= */ igraph_rng_glibc2_get_real, /* get_norm= */ 0, /* get_geom= */ 0, /* get_binom= */ 0, /* get_exp= */ 0 }; /* ------------------------------------ */ typedef struct { unsigned long int x; } igraph_i_rng_rand_state_t; unsigned long int igraph_rng_rand_get(void *vstate) { igraph_i_rng_rand_state_t *state = vstate; state->x = (1103515245 * state->x + 12345) & 0x7fffffffUL; return state->x; } igraph_real_t igraph_rng_rand_get_real(void *vstate) { return igraph_rng_rand_get (vstate) / 2147483648.0 ; } int igraph_rng_rand_seed(void *vstate, unsigned long int seed) { igraph_i_rng_rand_state_t *state = vstate; state->x = seed; return 0; } int igraph_rng_rand_init(void **state) { igraph_i_rng_rand_state_t *st; st=igraph_Calloc(1, igraph_i_rng_rand_state_t); if (!st) { IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM); } (*state)=st; igraph_rng_rand_seed(st, 0); return 0; } void igraph_rng_rand_destroy(void *vstate) { igraph_i_rng_rand_state_t *state = (igraph_i_rng_rand_state_t*) vstate; igraph_Free(state); } /** * \var igraph_rngtype_rand * \brief The old BSD rand/stand random number generator * * The sequence is * x_{n+1} = (a x_n + c) mod m * with a = 1103515245, c = 12345 and m = 2^31 = 2147483648. The seed * specifies the initial value, x_1. * * The theoretical value of x_{10001} is 1910041713. * * The period of this generator is 2^31. * * This generator is not very good -- the low bits of successive * numbers are correlated. * * This generator was ported from the GNU Scientific Library. */ const igraph_rng_type_t igraph_rngtype_rand = { /* name= */ "RAND", /* min= */ 0, /* max= */ 0x7fffffffUL, /* init= */ igraph_rng_rand_init, /* destroy= */ igraph_rng_rand_destroy, /* seed= */ igraph_rng_rand_seed, /* get= */ igraph_rng_rand_get, /* get_real= */ igraph_rng_rand_get_real, /* get_norm= */ 0, /* get_geom= */ 0, /* get_binom= */ 0, /* get_exp= */ 0 }; /* ------------------------------------ */ #define N 624 /* Period parameters */ #define M 397 /* most significant w-r bits */ static const unsigned long UPPER_MASK = 0x80000000UL; /* least significant r bits */ static const unsigned long LOWER_MASK = 0x7fffffffUL; typedef struct { unsigned long mt[N]; int mti; } igraph_i_rng_mt19937_state_t; unsigned long int igraph_rng_mt19937_get(void *vstate) { igraph_i_rng_mt19937_state_t *state = vstate; unsigned long k ; unsigned long int *const mt = state->mt; #define MAGIC(y) (((y)&0x1) ? 0x9908b0dfUL : 0) if (state->mti >= N) { /* generate N words at one time */ int kk; for (kk = 0; kk < N - M; kk++) { unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y); } for (; kk < N - 1; kk++) { unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y); } { unsigned long y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ MAGIC(y); } state->mti = 0; } #undef MAGIC /* Tempering */ k = mt[state->mti]; k ^= (k >> 11); k ^= (k << 7) & 0x9d2c5680UL; k ^= (k << 15) & 0xefc60000UL; k ^= (k >> 18); state->mti++; return k; } igraph_real_t igraph_rng_mt19937_get_real(void *vstate) { return igraph_rng_mt19937_get (vstate) / 4294967296.0 ; } int igraph_rng_mt19937_seed(void *vstate, unsigned long int seed) { igraph_i_rng_mt19937_state_t *state = vstate; int i; memset(state, 0, sizeof(igraph_i_rng_mt19937_state_t)); if (seed == 0) { seed = 4357; /* the default seed is 4357 */ } state->mt[0]= seed & 0xffffffffUL; for (i = 1; i < N; i++) { /* See Knuth's "Art of Computer Programming" Vol. 2, 3rd Ed. p.106 for multiplier. */ state->mt[i] = (1812433253UL * (state->mt[i-1] ^ (state->mt[i-1] >> 30)) + (unsigned long) i); state->mt[i] &= 0xffffffffUL; } state->mti = i; return 0; } int igraph_rng_mt19937_init(void **state) { igraph_i_rng_mt19937_state_t *st; st=igraph_Calloc(1, igraph_i_rng_mt19937_state_t); if (!st) { IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM); } (*state)=st; igraph_rng_mt19937_seed(st, 0); return 0; } void igraph_rng_mt19937_destroy(void *vstate) { igraph_i_rng_mt19937_state_t *state = (igraph_i_rng_mt19937_state_t*) vstate; igraph_Free(state); } /** * \var igraph_rngtype_mt19937 * \brief The MT19937 random number generator * * The MT19937 generator of Makoto Matsumoto and Takuji Nishimura is a * variant of the twisted generalized feedback shift-register * algorithm, and is known as the “Mersenne Twister†generator. It has * a Mersenne prime period of 2^19937 - 1 (about 10^6000) and is * equi-distributed in 623 dimensions. It has passed the diehard * statistical tests. It uses 624 words of state per generator and is * comparable in speed to the other generators. The original generator * used a default seed of 4357 and choosing s equal to zero in * gsl_rng_set reproduces this. Later versions switched to 5489 as the * default seed, you can choose this explicitly via igraph_rng_seed * instead if you require it. * * For more information see, * Makoto Matsumoto and Takuji Nishimura, “Mersenne Twister: A * 623-dimensionally equidistributed uniform pseudorandom number * generatorâ€. ACM Transactions on Modeling and Computer Simulation, * Vol. 8, No. 1 (Jan. 1998), Pages 3–30 * * The generator igraph_rngtype_mt19937 uses the second revision of the * seeding procedure published by the two authors above in 2002. The * original seeding procedures could cause spurious artifacts for some * seed values. * * This generator was ported from the GNU Scientific Library. */ const igraph_rng_type_t igraph_rngtype_mt19937 = { /* name= */ "MT19937", /* min= */ 0, /* max= */ 0xffffffffUL, /* init= */ igraph_rng_mt19937_init, /* destroy= */ igraph_rng_mt19937_destroy, /* seed= */ igraph_rng_mt19937_seed, /* get= */ igraph_rng_mt19937_get, /* get_real= */ igraph_rng_mt19937_get_real, /* get_norm= */ 0, /* get_geom= */ 0, /* get_binom= */ 0, /* get_exp= */ 0 }; #undef N #undef M /* ------------------------------------ */ #ifndef USING_R igraph_i_rng_mt19937_state_t igraph_i_rng_default_state; #define addr(a) (&a) /** * \var igraph_i_rng_default * The default igraph random number generator * * This generator is used by all builtin igraph functions that need to * generate random numbers; e.g. all random graph generators. * * You can use \ref igraph_i_rng_default with \ref igraph_rng_seed() * to set its seed. * * You can change the default generator using the \ref * igraph_rng_set_default() function. */ IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = { addr(igraph_rngtype_mt19937), addr(igraph_i_rng_default_state), /* def= */ 1 }; #undef addr /** * \function igraph_rng_set_default * Set the default igraph random number generator * * \param rng The random number generator to use as default from now * on. Calling \ref igraph_rng_destroy() on it, while it is still * being used as the default will result craches and/or * unpredictable results. * * Time complexity: O(1). */ void igraph_rng_set_default(igraph_rng_t *rng) { igraph_i_rng_default = (*rng); } #endif /* ------------------------------------ */ #ifdef USING_R double unif_rand(void); double norm_rand(void); double exp_rand(void); double Rf_rgeom(double); double Rf_rbinom(double, double); int igraph_rng_R_init(void **state) { IGRAPH_ERROR("R RNG error, unsupported function called", IGRAPH_EINTERNAL); return 0; } void igraph_rng_R_destroy(void *state) { igraph_error("R RNG error, unsupported function called", __FILE__, __LINE__, IGRAPH_EINTERNAL); } int igraph_rng_R_seed(void *state, unsigned long int seed) { IGRAPH_ERROR("R RNG error, unsupported function called", IGRAPH_EINTERNAL); return 0; } unsigned long int igraph_rng_R_get(void *state) { return (unsigned long) (unif_rand() * 0x7FFFFFFFUL); } igraph_real_t igraph_rng_R_get_real(void *state) { return unif_rand(); } igraph_real_t igraph_rng_R_get_norm(void *state) { return norm_rand(); } igraph_real_t igraph_rng_R_get_geom(void *state, igraph_real_t p) { return Rf_rgeom(p); } igraph_real_t igraph_rng_R_get_binom(void *state, long int n, igraph_real_t p) { return Rf_rbinom(n, p); } igraph_real_t igraph_rng_R_get_exp(void *state, igraph_real_t rate) { igraph_real_t scale = 1.0 / rate; if (!IGRAPH_FINITE(scale) || scale <= 0.0) { if (scale == 0.0) { return 0.0; } return IGRAPH_NAN; } return scale * exp_rand(); } igraph_rng_type_t igraph_rngtype_R = { /* name= */ "GNU R", /* min= */ 0, /* max= */ 0x7FFFFFFFUL, /* init= */ igraph_rng_R_init, /* destroy= */ igraph_rng_R_destroy, /* seed= */ igraph_rng_R_seed, /* get= */ igraph_rng_R_get, /* get_real= */ igraph_rng_R_get_real, /* get_norm= */ igraph_rng_R_get_norm, /* get_geom= */ igraph_rng_R_get_geom, /* get_binom= */ igraph_rng_R_get_binom, /* get_exp= */ igraph_rng_R_get_exp }; IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = { &igraph_rngtype_R, 0, /* def= */ 1 }; #endif /* ------------------------------------ */ /** * \function igraph_rng_default * Query the default random number generator. * * \return A pointer to the default random number generator. * * \sa igraph_rng_set_default() */ igraph_rng_t *igraph_rng_default() { return &igraph_i_rng_default; } /* ------------------------------------ */ double igraph_norm_rand(igraph_rng_t *rng); double igraph_rgeom(igraph_rng_t *rng, double p); double igraph_rbinom(igraph_rng_t *rng, double nin, double pp); double igraph_rexp(igraph_rng_t *rng, double rate); /** * \function igraph_rng_init * Initialize a random number generator * * This function allocates memory for a random number generator, with * the given type, and sets its seed to the default. * * \param rng Pointer to an uninitialized RNG. * \param type The type of the RNG, please see the documentation for * the supported types. * \return Error code. * * Time complexity: depends on the type of the generator, but usually * it should be O(1). */ int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type) { rng->type=type; IGRAPH_CHECK(rng->type->init(&rng->state)); return 0; } /** * \function igraph_rng_destroy * Deallocate memory associated with a random number generator * * \param rng The RNG to destroy. Do not destroy an RNG that is used * as the default igraph RNG. * * Time complexity: O(1). */ void igraph_rng_destroy(igraph_rng_t *rng) { rng->type->destroy(rng->state); } /** * \function igraph_rng_seed * Set the seed of a random number generator * * \param rng The RNG. * \param seed The new seed. * \return Error code. * * Time complexity: usually O(1), but may depend on the type of the * RNG. */ int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed) { const igraph_rng_type_t *type=rng->type; rng->def=0; IGRAPH_CHECK(type->seed(rng->state, seed)); return 0; } /** * \function igraph_rng_max * Query the maximum possible integer for a random number generator * * \param rng The RNG. * \return The largest possible integer that can be generated by * calling \ref igraph_rng_get_integer() on the RNG. * * Time complexity: O(1). */ unsigned long int igraph_rng_max(igraph_rng_t *rng) { const igraph_rng_type_t *type=rng->type; return type->max; } /** * \function igraph_rng_min * Query the minimum possible integer for a random number generator * * \param rng The RNG. * \return The smallest possible integer that can be generated by * calling \ref igraph_rng_get_integer() on the RNG. * * Time complexity: O(1). */ unsigned long int igraph_rng_min(igraph_rng_t *rng) { const igraph_rng_type_t *type=rng->type; return type->min; } /** * \function igraph_rng_name * Query the type of a random number generator * * \param rng The RNG. * \return The name of the type of the generator. Do not deallocate or * change the returned string pointer. * * Time complexity: O(1). */ const char *igraph_rng_name(igraph_rng_t *rng) { const igraph_rng_type_t *type=rng->type; return type->name; } /** * \function igraph_rng_get_integer * Generate an integer random number from an interval * * \param rng Pointer to the RNG to use for the generation. Use \ref * igraph_rng_default() here to use the default igraph RNG. * \param l Lower limit, inclusive, it can be negative as well. * \param h Upper limit, inclusive, it can be negative as well, but it * should be at least l. * \return The generated random integer. * * Time complexity: depends on the generator, but should be usually * O(1). */ long int igraph_rng_get_integer(igraph_rng_t *rng, long int l, long int h) { const igraph_rng_type_t *type=rng->type; if (type->get_real) { return (long int)(type->get_real(rng->state)*(h-l+1)+l); } else if (type->get) { unsigned long int max=type->max; return (long int)(type->get(rng->state) / ((double)max+1)*(h-l+1)+l); } IGRAPH_ERROR("Internal random generator error", IGRAPH_EINTERNAL); return 0; } /** * \function igraph_rng_get_normal * Normally distributed random numbers * * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() * here to use the default igraph RNG. * \param m The mean. * \param s Standard deviation. * \return The generated normally distributed random number. * * Time complexity: depends on the type of the RNG. */ igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, igraph_real_t m, igraph_real_t s) { const igraph_rng_type_t *type=rng->type; if (type->get_norm) { return type->get_norm(rng->state)*s+m; } else { return igraph_norm_rand(rng)*s+m; } } /** * \function igraph_rng_get_unif * Generate real, uniform random numbers from an interval * * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() * here to use the default igraph RNG. * \param l The lower bound, it can be negative. * \param h The upper bound, it can be negative, but it has to be * larger than the lower bound. * \return The generated uniformly distributed random number. * * Time complexity: depends on the type of the RNG. */ igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, igraph_real_t l, igraph_real_t h) { const igraph_rng_type_t *type=rng->type; if (type->get_real) { return type->get_real(rng->state)*(h-l)+l; } else if (type->get) { unsigned long int max=type->max; return type->get(rng->state)/((double)max+1)*(double)(h-l)+l; } IGRAPH_ERROR("Internal random generator error", IGRAPH_EINTERNAL); return 0; } /** * \function igraph_rng_get_unif01 * Generate real, uniform random number from the unit interval * * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() * here to use the default igraph RNG. * \return The generated uniformly distributed random number. * * Time complexity: depends on the type of the RNG. */ igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng) { const igraph_rng_type_t *type=rng->type; if (type->get_real) { return type->get_real(rng->state); } else if (type->get) { unsigned long int max=type->max; return type->get(rng->state)/((double)max+1); } IGRAPH_ERROR("Internal random generator error", IGRAPH_EINTERNAL); return 0; } /** * \function igraph_rng_get_geom * Generate geometrically distributed random numbers * * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() * here to use the default igraph RNG. * \param p The probability of success in each trial. Must be larger * than zero and smaller or equal to 1. * \return The generated geometrically distributed random number. * * Time complexity: depends on the type of the RNG. */ igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p) { const igraph_rng_type_t *type=rng->type; if (type->get_geom) { return type->get_geom(rng->state, p); } else { return igraph_rgeom(rng, p); } } /** * \function igraph_rng_get_binom * Generate binomially distributed random numbers * * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() * here to use the default igraph RNG. * \param n Number of observations. * \param p Probability of an event. * \return The generated binomially distributed random number. * * Time complexity: depends on the type of the RNG. */ igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n, igraph_real_t p) { const igraph_rng_type_t *type=rng->type; if (type->get_binom) { return type->get_binom(rng->state, n, p); } else { return igraph_rbinom(rng, n, p); } } unsigned long int igraph_rng_get_int31(igraph_rng_t *rng) { const igraph_rng_type_t *type=rng->type; unsigned long int max=type->max; if (type->get && max==0x7FFFFFFFUL) { return type->get(rng->state); } else if (type->get_real) { return (unsigned long int) (type->get_real(rng->state)*0x7FFFFFFFUL); } else { return (unsigned long int) (igraph_rng_get_unif01(rng)*0x7FFFFFFFUL); } } igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate) { const igraph_rng_type_t *type=rng->type; if (type->get_exp) { return type->get_exp(rng->state, rate); } else { return igraph_rexp(rng, rate); } } #ifndef HAVE_EXPM1 #ifndef USING_R /* R provides a replacement */ /* expm1 replacement */ double expm1 (double x) { if (fabs(x) < M_LN2) { /* Compute the Taylor series S = x + (1/2!) x^2 + (1/3!) x^3 + ... */ double i = 1.0; double sum = x; double term = x / 1.0; do { term *= x / ++i; sum += term; } while (fabs(term) > fabs(sum) * 2.22e-16); return sum; } return expl(x) - 1.0L; } #endif #endif #ifndef HAVE_RINT #ifndef USING_R /* R provides a replacement */ /* rint replacement */ double rint (double x) { return ( (x<0.) ? -floor(-x+.5) : floor(x+.5) ); } #endif #endif #ifndef HAVE_RINTF float rintf (float x) { return ( (x<(float)0.) ? -(float)floor(-x+.5) : (float)floor(x+.5) ); } #endif /* * \ingroup internal * * This function appends the rest of the needed random number to the * result vector. */ int igraph_i_random_sample_alga(igraph_vector_t *res, igraph_integer_t l, igraph_integer_t h, igraph_integer_t length) { igraph_real_t N=h-l+1; igraph_real_t n=length; igraph_real_t top=N-n; igraph_real_t Nreal=N; igraph_real_t S=0; igraph_real_t V, quot; l=l-1; while (n>=2) { V=RNG_UNIF01(); S=1; quot=top/Nreal; while (quot>V) { S+=1; top=-1.0+top; Nreal=-1.0+Nreal; quot=(quot*top)/Nreal; } l+=S; igraph_vector_push_back(res, l); /* allocated */ Nreal=-1.0+Nreal; n=-1+n; } S=floor(round(Nreal)*RNG_UNIF01()); l+=S+1; igraph_vector_push_back(res, l); /* allocated */ return 0; } /** * \ingroup nongraph * \function igraph_random_sample * \brief Generates an increasing random sequence of integers. * *
* This function generates an increasing sequence of random integer * numbers from a given interval. The algorithm is taken literally * from (Vitter 1987). This method can be used for generating numbers from a * \em very large interval. It is primarily created for randomly * selecting some edges from the sometimes huge set of possible edges * in a large graph. * * Note that the type of the lower and the upper limit is \c igraph_real_t, * not \c igraph_integer_t. This does not mean that you can pass fractional * numbers there; these values must still be integral, but we need the * longer range of \c igraph_real_t in several places in the library * (for instance, when generating Erdos-Renyi graphs). * \param res Pointer to an initialized vector. This will hold the * result. It will be resized to the proper size. * \param l The lower limit of the generation interval (inclusive). This must * be less than or equal to the upper limit, and it must be integral. * Passing a fractional number here results in undefined behaviour. * \param h The upper limit of the generation interval (inclusive). This must * be greater than or equal to the lower limit, and it must be integral. * Passing a fractional number here results in undefined behaviour. * \param length The number of random integers to generate. * \return The error code \c IGRAPH_EINVAL is returned in each of the * following cases: (1) The given lower limit is greater than the * given upper limit, i.e. \c l > \c h. (2) Assuming that * \c l < \c h and N is the sample size, the above error code is * returned if N > |\c h - \c l|, i.e. the sample size exceeds the * size of the candidate pool. * * Time complexity: according to (Vitter 1987), the expected * running time is O(length). * * * Reference: * \clist * \cli (Vitter 1987) * J. S. Vitter. An efficient algorithm for sequential random sampling. * \emb ACM Transactions on Mathematical Software, \eme 13(1):58--67, 1987. * \endclist * * \example examples/simple/igraph_random_sample.c */ int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, igraph_integer_t length) { igraph_real_t N=h-l+1; igraph_real_t n=length; int retval; igraph_real_t nreal=length; igraph_real_t ninv=1.0/nreal; igraph_real_t Nreal=N; igraph_real_t Vprime; igraph_real_t qu1=-n+1+N; igraph_real_t qu1real=-nreal+1.0+Nreal; igraph_real_t negalphainv=-13; igraph_real_t threshold=-negalphainv*n; igraph_real_t S; /* getting back some sense of sanity */ if (l > h) IGRAPH_ERROR("Lower limit is greater than upper limit", IGRAPH_EINVAL); /* now we know that l <= h */ if (length > N) IGRAPH_ERROR("Sample size exceeds size of candidate pool", IGRAPH_EINVAL); /* treat rare cases quickly */ if (l==h) { IGRAPH_CHECK(igraph_vector_resize(res, 1)); VECTOR(*res)[0] = l; return 0; } if (length==N) { long int i = 0; IGRAPH_CHECK(igraph_vector_resize(res, length)); for (i = 0; i < length; i++) { VECTOR(*res)[i] = l++; } return 0; } igraph_vector_clear(res); IGRAPH_CHECK(igraph_vector_reserve(res, length)); RNG_BEGIN(); Vprime=exp(log(RNG_UNIF01())*ninv); l=l-1; while (n>1 && threshold < N) { igraph_real_t X, U; igraph_real_t limit, t; igraph_real_t negSreal, y1, y2, top, bottom; igraph_real_t nmin1inv=1.0/(-1.0+nreal); while (1) { while(1) { X=Nreal*(-Vprime+1.0); S=floor(X); // if (S==0) { S=1; } if (S S) { bottom=-nreal+Nreal; limit=-S+N; } else { bottom=-1.0+negSreal+Nreal; limit=qu1; } for (t=-1+N; t>=limit; t--) { y2=(y2*top)/bottom; top=-1.0+top; bottom=-1.0+bottom; } if (Nreal/(-X+Nreal) >= y1*exp(log(y2)*nmin1inv)) { Vprime=exp(log(RNG_UNIF01())*nmin1inv); break; } Vprime=exp(log(RNG_UNIF01())*ninv); } l+=S+1; igraph_vector_push_back(res, l); /* allocated */ N=-S+(-1+N); Nreal=negSreal+(-1.0+Nreal); n=-1+n; nreal=-1.0+nreal; ninv=nmin1inv; qu1=-S+qu1; qu1real=negSreal+qu1real; threshold=threshold+negalphainv; } if (n>1) { retval=igraph_i_random_sample_alga(res, (igraph_integer_t) l+1, (igraph_integer_t) h, (igraph_integer_t) n); } else { retval=0; S=floor(N*Vprime); l+=S+1; igraph_vector_push_back(res, l); /* allocated */ } RNG_END(); return retval; } #ifdef USING_R /* These are never called. But they are correct, nevertheless */ double igraph_norm_rand(igraph_rng_t *rng) { return norm_rand(); } double igraph_rgeom(igraph_rng_t *rng, double p) { return Rf_rgeom(p); } double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) { return Rf_rbinom(nin, pp); } double igraph_rexp(igraph_rng_t *rng, double rate) { igraph_real_t scale = 1.0 / rate; if (!IGRAPH_FINITE(scale) || scale <= 0.0) { if (scale == 0.0) { return 0.0; } return IGRAPH_NAN; } return scale * exp_rand(); } #else /* * Mathlib : A C Library of Special Functions * Copyright (C) 1998 Ross Ihaka * Copyright (C) 2000 The R Development Core Team * based on AS 111 (C) 1977 Royal Statistical Society * and on AS 241 (C) 1988 Royal Statistical Society * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * SYNOPSIS * * double qnorm5(double p, double mu, double sigma, * int lower_tail, int log_p) * {qnorm (..) is synonymous and preferred inside R} * * DESCRIPTION * * Compute the quantile function for the normal distribution. * * For small to moderate probabilities, algorithm referenced * below is used to obtain an initial approximation which is * polished with a final Newton step. * * For very large arguments, an algorithm of Wichura is used. * * REFERENCE * * Beasley, J. D. and S. G. Springer (1977). * Algorithm AS 111: The percentage points of the normal distribution, * Applied Statistics, 26, 118-121. * * Wichura, M.J. (1988). * Algorithm AS 241: The Percentage Points of the Normal Distribution. * Applied Statistics, 37, 477-484. */ /* * Mathlib : A C Library of Special Functions * Copyright (C) 1998-2004 The R Development Core Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* Private header file for use during compilation of Mathlib */ #ifndef MATHLIB_PRIVATE_H #define MATHLIB_PRIVATE_H #ifdef _MSC_VER # define ML_POSINF IGRAPH_INFINITY # define ML_NEGINF -IGRAPH_INFINITY # define ML_NAN IGRAPH_NAN #else # define ML_POSINF (1.0 / 0.0) # define ML_NEGINF ((-1.0) / 0.0) # define ML_NAN (0.0 / 0.0) #endif #define ML_ERROR(x) /* nothing */ #define ML_UNDERFLOW (DBL_MIN * DBL_MIN) #define ML_VALID(x) (!ISNAN(x)) #define ME_NONE 0 /* no error */ #define ME_DOMAIN 1 /* argument out of domain */ #define ME_RANGE 2 /* value out of range */ #define ME_NOCONV 4 /* process did not converge */ #define ME_PRECISION 8 /* does not have "full" precision */ #define ME_UNDERFLOW 16 /* and underflow occurred (important for IEEE)*/ #define ML_ERR_return_NAN { ML_ERROR(ME_DOMAIN); return ML_NAN; } /* Wilcoxon Rank Sum Distribution */ #define WILCOX_MAX 50 /* Wilcoxon Signed Rank Distribution */ #define SIGNRANK_MAX 50 /* Formerly private part of Mathlib.h */ /* always remap internal functions */ #define bd0 Rf_bd0 #define chebyshev_eval Rf_chebyshev_eval #define chebyshev_init Rf_chebyshev_init #define i1mach Rf_i1mach #define gammalims Rf_gammalims #define lfastchoose Rf_lfastchoose #define lgammacor Rf_lgammacor #define stirlerr Rf_stirlerr /* Chebyshev Series */ int chebyshev_init(double*, int, double); double chebyshev_eval(double, const double *, const int); /* Gamma and Related Functions */ void gammalims(double*, double*); double lgammacor(double); /* log(gamma) correction */ double stirlerr(double); /* Stirling expansion "error" */ double lfastchoose(double, double); double bd0(double, double); /* Consider adding these two to the API (Rmath.h): */ double dbinom_raw(double, double, double, double, int); double dpois_raw (double, double, int); double pnchisq_raw(double, double, double, double, double, int); int i1mach(int); /* From toms708.c */ void bratio(double a, double b, double x, double y, double *w, double *w1, int *ierr); #endif /* MATHLIB_PRIVATE_H */ /* Utilities for `dpq' handling (density/probability/quantile) */ /* give_log in "d"; log_p in "p" & "q" : */ #define give_log log_p /* "DEFAULT" */ /* --------- */ #define R_D__0 (log_p ? ML_NEGINF : 0.) /* 0 */ #define R_D__1 (log_p ? 0. : 1.) /* 1 */ #define R_DT_0 (lower_tail ? R_D__0 : R_D__1) /* 0 */ #define R_DT_1 (lower_tail ? R_D__1 : R_D__0) /* 1 */ #define R_D_Lval(p) (lower_tail ? (p) : (1 - (p))) /* p */ #define R_D_Cval(p) (lower_tail ? (1 - (p)) : (p)) /* 1 - p */ #define R_D_val(x) (log_p ? log(x) : (x)) /* x in pF(x,..) */ #define R_D_qIv(p) (log_p ? exp(p) : (p)) /* p in qF(p,..) */ #define R_D_exp(x) (log_p ? (x) : exp(x)) /* exp(x) */ #define R_D_log(p) (log_p ? (p) : log(p)) /* log(p) */ #define R_D_Clog(p) (log_p ? log1p(-(p)) : (1 - (p)))/* [log](1-p) */ /* log(1-exp(x)): R_D_LExp(x) == (log1p(- R_D_qIv(x))) but even more stable:*/ #define R_D_LExp(x) (log_p ? R_Log1_Exp(x) : log1p(-x)) /*till 1.8.x: * #define R_DT_val(x) R_D_val(R_D_Lval(x)) * #define R_DT_Cval(x) R_D_val(R_D_Cval(x)) */ #define R_DT_val(x) (lower_tail ? R_D_val(x) : R_D_Clog(x)) #define R_DT_Cval(x) (lower_tail ? R_D_Clog(x) : R_D_val(x)) /*#define R_DT_qIv(p) R_D_Lval(R_D_qIv(p)) * p in qF ! */ #define R_DT_qIv(p) (log_p ? (lower_tail ? exp(p) : - expm1(p)) \ : R_D_Lval(p)) /*#define R_DT_CIv(p) R_D_Cval(R_D_qIv(p)) * 1 - p in qF */ #define R_DT_CIv(p) (log_p ? (lower_tail ? -expm1(p) : exp(p)) \ : R_D_Cval(p)) #define R_DT_exp(x) R_D_exp(R_D_Lval(x)) /* exp(x) */ #define R_DT_Cexp(x) R_D_exp(R_D_Cval(x)) /* exp(1 - x) */ #define R_DT_log(p) (lower_tail? R_D_log(p) : R_D_LExp(p))/* log(p) in qF */ #define R_DT_Clog(p) (lower_tail? R_D_LExp(p): R_D_log(p))/* log(1-p) in qF*/ #define R_DT_Log(p) (lower_tail? (p) : R_Log1_Exp(p)) /* == R_DT_log when we already "know" log_p == TRUE :*/ #define R_Q_P01_check(p) \ if ((log_p && p > 0) || \ (!log_p && (p < 0 || p > 1)) ) \ ML_ERR_return_NAN /* additions for density functions (C.Loader) */ #define R_D_fexp(f,x) (give_log ? -0.5*log(f)+(x) : exp(x)/sqrt(f)) #define R_D_forceint(x) floor((x) + 0.5) #define R_D_nonint(x) (fabs((x) - floor((x)+0.5)) > 1e-7) /* [neg]ative or [non int]eger : */ #define R_D_negInonint(x) (x < 0. || R_D_nonint(x)) #define R_D_nonint_check(x) \ if(R_D_nonint(x)) { \ MATHLIB_WARNING("non-integer x = %f", x); \ return R_D__0; \ } double igraph_qnorm5(double p, double mu, double sigma, int lower_tail, int log_p) { double p_, q, r, val; #ifdef IEEE_754 if (ISNAN(p) || ISNAN(mu) || ISNAN(sigma)) return p + mu + sigma; #endif if (p == R_DT_0) return ML_NEGINF; if (p == R_DT_1) return ML_POSINF; R_Q_P01_check(p); if(sigma < 0) ML_ERR_return_NAN; if(sigma == 0) return mu; p_ = R_DT_qIv(p);/* real lower_tail prob. p */ q = p_ - 0.5; /*-- use AS 241 --- */ /* double ppnd16_(double *p, long *ifault)*/ /* ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3 Produces the normal deviate Z corresponding to a given lower tail area of P; Z is accurate to about 1 part in 10**16. (original fortran code used PARAMETER(..) for the coefficients and provided hash codes for checking them...) */ if (fabs(q) <= .425) {/* 0.075 <= p <= 0.925 */ r = .180625 - q * q; val = q * (((((((r * 2509.0809287301226727 + 33430.575583588128105) * r + 67265.770927008700853) * r + 45921.953931549871457) * r + 13731.693765509461125) * r + 1971.5909503065514427) * r + 133.14166789178437745) * r + 3.387132872796366608) / (((((((r * 5226.495278852854561 + 28729.085735721942674) * r + 39307.89580009271061) * r + 21213.794301586595867) * r + 5394.1960214247511077) * r + 687.1870074920579083) * r + 42.313330701600911252) * r + 1.); } else { /* closer than 0.075 from {0,1} boundary */ /* r = min(p, 1-p) < 0.075 */ if (q > 0) r = R_DT_CIv(p);/* 1-p */ else r = p_;/* = R_DT_Iv(p) ^= p */ r = sqrt(- ((log_p && ((lower_tail && q <= 0) || (!lower_tail && q > 0))) ? p : /* else */ log(r))); /* r = sqrt(-log(r)) <==> min(p, 1-p) = exp( - r^2 ) */ if (r <= 5.) { /* <==> min(p,1-p) >= exp(-25) ~= 1.3888e-11 */ r += -1.6; val = (((((((r * 7.7454501427834140764e-4 + .0227238449892691845833) * r + .24178072517745061177) * r + 1.27045825245236838258) * r + 3.64784832476320460504) * r + 5.7694972214606914055) * r + 4.6303378461565452959) * r + 1.42343711074968357734) / (((((((r * 1.05075007164441684324e-9 + 5.475938084995344946e-4) * r + .0151986665636164571966) * r + .14810397642748007459) * r + .68976733498510000455) * r + 1.6763848301838038494) * r + 2.05319162663775882187) * r + 1.); } else { /* very close to 0 or 1 */ r += -5.; val = (((((((r * 2.01033439929228813265e-7 + 2.71155556874348757815e-5) * r + .0012426609473880784386) * r + .026532189526576123093) * r + .29656057182850489123) * r + 1.7848265399172913358) * r + 5.4637849111641143699) * r + 6.6579046435011037772) / (((((((r * 2.04426310338993978564e-15 + 1.4215117583164458887e-7)* r + 1.8463183175100546818e-5) * r + 7.868691311456132591e-4) * r + .0148753612908506148525) * r + .13692988092273580531) * r + .59983220655588793769) * r + 1.); } if(q < 0.0) val = -val; /* return (q >= 0.)? r : -r ;*/ } return mu + sigma * val; } double fsign(double x, double y) { #ifdef IEEE_754 if (ISNAN(x) || ISNAN(y)) return x + y; #endif return ((y >= 0) ? fabs(x) : -fabs(x)); } int imax2(int x, int y) { return (x < y) ? y : x; } int imin2(int x, int y) { return (x < y) ? x : y; } #ifdef HAVE_WORKING_ISFINITE /* isfinite is defined in according to C99 */ # define R_FINITE(x) isfinite(x) #elif HAVE_WORKING_FINITE /* include header needed to define finite() */ # ifdef HAVE_IEEE754_H # include /* newer Linuxen */ # else # ifdef HAVE_IEEEFP_H # include /* others [Solaris], .. */ # endif # endif # define R_FINITE(x) finite(x) #else # define R_FINITE(x) R_finite(x) #endif int R_finite(double x) { #ifdef HAVE_WORKING_ISFINITE return isfinite(x); #elif HAVE_WORKING_FINITE return finite(x); #else /* neither finite nor isfinite work. Do we really need the AIX exception? */ # ifdef _AIX # include return FINITE(x); # elif defined(_MSC_VER) return _finite(x); #else return (!isnan(x) & (x != 1/0.0) & (x != -1.0/0.0)); # endif #endif } int R_isnancpp(double x) { return (isnan(x)!=0); } #ifdef __cplusplus int R_isnancpp(double); /* in arithmetic.c */ # define ISNAN(x) R_isnancpp(x) #else # define ISNAN(x) (isnan(x)!=0) #endif double igraph_norm_rand(igraph_rng_t *rng) { double u1; #define BIG 134217728 /* 2^27 */ /* unif_rand() alone is not of high enough precision */ u1 = igraph_rng_get_unif01(rng); u1 = (int)(BIG*u1) + igraph_rng_get_unif01(rng); return igraph_qnorm5(u1/BIG, 0.0, 1.0, 1, 0); } /* * Mathlib : A C Library of Special Functions * Copyright (C) 1998 Ross Ihaka * Copyright (C) 2000-2002 the R Development Core Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * SYNOPSIS * * #include * double exp_rand(void); * * DESCRIPTION * * Random variates from the standard exponential distribution. * * REFERENCE * * Ahrens, J.H. and Dieter, U. (1972). * Computer methods for sampling from the exponential and * normal distributions. * Comm. ACM, 15, 873-882. */ double igraph_exp_rand(igraph_rng_t *rng) { /* q[k-1] = sum(log(2)^k / k!) k=1,..,n, */ /* The highest n (here 8) is determined by q[n-1] = 1.0 */ /* within standard precision */ const double q[] = { 0.6931471805599453, 0.9333736875190459, 0.9888777961838675, 0.9984959252914960, 0.9998292811061389, 0.9999833164100727, 0.9999985691438767, 0.9999998906925558, 0.9999999924734159, 0.9999999995283275, 0.9999999999728814, 0.9999999999985598, 0.9999999999999289, 0.9999999999999968, 0.9999999999999999, 1.0000000000000000 }; double a, u, ustar, umin; int i; a = 0.; /* precaution if u = 0 is ever returned */ u = igraph_rng_get_unif01(rng); while(u <= 0.0 || u >= 1.0) u = igraph_rng_get_unif01(rng); for (;;) { u += u; if (u > 1.0) break; a += q[0]; } u -= 1.; if (u <= q[0]) return a + u; i = 0; ustar = igraph_rng_get_unif01(rng); umin = ustar; do { ustar = igraph_rng_get_unif01(rng); if (ustar < umin) umin = ustar; i++; } while (u > q[i]); return a + umin * q[0]; } /* * Mathlib : A C Library of Special Functions * Copyright (C) 1998 Ross Ihaka * Copyright (C) 2000-2001 The R Development Core Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * SYNOPSIS * * #include * double rpois(double lambda) * * DESCRIPTION * * Random variates from the Poisson distribution. * * REFERENCE * * Ahrens, J.H. and Dieter, U. (1982). * Computer generation of Poisson deviates * from modified normal distributions. * ACM Trans. Math. Software 8, 163-179. */ #define a0 -0.5 #define a1 0.3333333 #define a2 -0.2500068 #define a3 0.2000118 #define a4 -0.1661269 #define a5 0.1421878 #define a6 -0.1384794 #define a7 0.1250060 #define one_7 0.1428571428571428571 #define one_12 0.0833333333333333333 #define one_24 0.0416666666666666667 #define repeat for(;;) #define FALSE 0 #define TRUE 1 #define M_1_SQRT_2PI 0.398942280401432677939946059934 /* 1/sqrt(2pi) */ double igraph_rpois(igraph_rng_t *rng, double mu) { /* Factorial Table (0:9)! */ const double fact[10] = { 1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880. }; /* These are static --- persistent between calls for same mu : */ static IGRAPH_THREAD_LOCAL int l, m; static IGRAPH_THREAD_LOCAL double b1, b2, c, c0, c1, c2, c3; static IGRAPH_THREAD_LOCAL double pp[36], p0, p, q, s, d, omega; static IGRAPH_THREAD_LOCAL double big_l;/* integer "w/o overflow" */ static IGRAPH_THREAD_LOCAL double muprev = 0., muprev2 = 0.;/*, muold = 0.*/ /* Local Vars [initialize some for -Wall]: */ double del, difmuk= 0., E= 0., fk= 0., fx, fy, g, px, py, t, u= 0., v, x; double pois = -1.; int k, kflag, big_mu, new_big_mu = FALSE; if (!R_FINITE(mu)) ML_ERR_return_NAN; if (mu <= 0.) return 0.; big_mu = mu >= 10.; if(big_mu) new_big_mu = FALSE; if (!(big_mu && mu == muprev)) {/* maybe compute new persistent par.s */ if (big_mu) { new_big_mu = TRUE; /* Case A. (recalculation of s,d,l because mu has changed): * The Poisson probabilities pk exceed the discrete normal * probabilities fk whenever k >= m(mu). */ muprev = mu; s = sqrt(mu); d = 6. * mu * mu; big_l = floor(mu - 1.1484); /* = an upper bound to m(mu) for all mu >= 10.*/ } else { /* Small mu ( < 10) -- not using normal approx. */ /* Case B. (start new table and calculate p0 if necessary) */ /*muprev = 0.;-* such that next time, mu != muprev ..*/ if (mu != muprev) { muprev = mu; m = imax2(1, (int) mu); l = 0; /* pp[] is already ok up to pp[l] */ q = p0 = p = exp(-mu); } repeat { /* Step U. uniform sample for inversion method */ u = igraph_rng_get_unif01(rng); if (u <= p0) return 0.; /* Step T. table comparison until the end pp[l] of the pp-table of cumulative Poisson probabilities (0.458 > ~= pp[9](= 0.45792971447) for mu=10 ) */ if (l != 0) { for (k = (u <= 0.458) ? 1 : imin2(l, m); k <= l; k++) if (u <= pp[k]) return (double)k; if (l == 35) /* u > pp[35] */ continue; } /* Step C. creation of new Poisson probabilities p[l..] and their cumulatives q =: pp[k] */ l++; for (k = l; k <= 35; k++) { p *= mu / k; q += p; pp[k] = q; if (u <= q) { l = k; return (double)k; } } l = 35; } /* end(repeat) */ }/* mu < 10 */ } /* end {initialize persistent vars} */ /* Only if mu >= 10 : ----------------------- */ /* Step N. normal sample */ g = mu + s * igraph_norm_rand(rng);/* norm_rand() ~ N(0,1), standard normal */ if (g >= 0.) { pois = floor(g); /* Step I. immediate acceptance if pois is large enough */ if (pois >= big_l) return pois; /* Step S. squeeze acceptance */ fk = pois; difmuk = mu - fk; u = igraph_rng_get_unif01(rng); /* ~ U(0,1) - sample */ if (d * u >= difmuk * difmuk * difmuk) return pois; } /* Step P. preparations for steps Q and H. (recalculations of parameters if necessary) */ if (new_big_mu || mu != muprev2) { /* Careful! muprev2 is not always == muprev because one might have exited in step I or S */ muprev2 = mu; omega = M_1_SQRT_2PI / s; /* The quantities b1, b2, c3, c2, c1, c0 are for the Hermite * approximations to the discrete normal probabilities fk. */ b1 = one_24 / mu; b2 = 0.3 * b1 * b1; c3 = one_7 * b1 * b2; c2 = b2 - 15. * c3; c1 = b1 - 6. * b2 + 45. * c3; c0 = 1. - b1 + 3. * b2 - 15. * c3; c = 0.1069 / mu; /* guarantees majorization by the 'hat'-function. */ } if (g >= 0.) { /* 'Subroutine' F is called (kflag=0 for correct return) */ kflag = 0; goto Step_F; } repeat { /* Step E. Exponential Sample */ E = igraph_exp_rand(rng);/* ~ Exp(1) (standard exponential) */ /* sample t from the laplace 'hat' (if t <= -0.6744 then pk < fk for all mu >= 10.) */ u = 2 * igraph_rng_get_unif01(rng) - 1.; t = 1.8 + fsign(E, u); if (t > -0.6744) { pois = floor(mu + s * t); fk = pois; difmuk = mu - fk; /* 'subroutine' F is called (kflag=1 for correct return) */ kflag = 1; Step_F: /* 'subroutine' F : calculation of px,py,fx,fy. */ if (pois < 10) { /* use factorials from table fact[] */ px = -mu; py = pow(mu, pois) / fact[(int)pois]; } else { /* Case pois >= 10 uses polynomial approximation a0-a7 for accuracy when advisable */ del = one_12 / fk; del = del * (1. - 4.8 * del * del); v = difmuk / fk; if (fabs(v) <= 0.25) px = fk * v * v * (((((((a7 * v + a6) * v + a5) * v + a4) * v + a3) * v + a2) * v + a1) * v + a0) - del; else /* |v| > 1/4 */ px = fk * log(1. + v) - difmuk - del; py = M_1_SQRT_2PI / sqrt(fk); } x = (0.5 - difmuk) / s; x *= x;/* x^2 */ fx = -0.5 * x; fy = omega * (((c3 * x + c2) * x + c1) * x + c0); if (kflag > 0) { /* Step H. Hat acceptance (E is repeated on rejection) */ if (c * fabs(u) <= py * exp(px + E) - fy * exp(fx + E)) break; } else /* Step Q. Quotient acceptance (rare case) */ if (fy - u * fy <= py * exp(px - fx)) break; }/* t > -.67.. */ } return pois; } double igraph_rgeom(igraph_rng_t *rng, double p) { if (ISNAN(p) || p <= 0 || p > 1) ML_ERR_return_NAN; return igraph_rpois(rng, igraph_exp_rand(rng) * ((1 - p) / p)); } /* This is from nmath/rbinom.c */ #define repeat for(;;) double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) { /* FIXME: These should become THREAD_specific globals : */ static IGRAPH_THREAD_LOCAL double c, fm, npq, p1, p2, p3, p4, qn; static IGRAPH_THREAD_LOCAL double xl, xll, xlr, xm, xr; static IGRAPH_THREAD_LOCAL double psave = -1.0; static IGRAPH_THREAD_LOCAL int nsave = -1; static IGRAPH_THREAD_LOCAL int m; double f, f1, f2, u, v, w, w2, x, x1, x2, z, z2; double p, q, np, g, r, al, alv, amaxp, ffm, ynorm; int i,ix,k, n; if (!R_FINITE(nin)) ML_ERR_return_NAN; n = floor(nin + 0.5); if (n != nin) ML_ERR_return_NAN; if (!R_FINITE(pp) || /* n=0, p=0, p=1 are not errors */ n < 0 || pp < 0. || pp > 1.) ML_ERR_return_NAN; if (n == 0 || pp == 0.) return 0; if (pp == 1.) return n; p = fmin(pp, 1. - pp); q = 1. - p; np = n * p; r = p / q; g = r * (n + 1); /* Setup, perform only when parameters change [using static (globals): */ /* FIXING: Want this thread safe -- use as little (thread globals) as possible */ if (pp != psave || n != nsave) { psave = pp; nsave = n; if (np < 30.0) { /* inverse cdf logic for mean less than 30 */ qn = pow(q, (double) n); goto L_np_small; } else { ffm = np + p; m = ffm; fm = m; npq = np * q; p1 = (int)(2.195 * sqrt(npq) - 4.6 * q) + 0.5; xm = fm + 0.5; xl = xm - p1; xr = xm + p1; c = 0.134 + 20.5 / (15.3 + fm); al = (ffm - xl) / (ffm - xl * p); xll = al * (1.0 + 0.5 * al); al = (xr - ffm) / (xr * q); xlr = al * (1.0 + 0.5 * al); p2 = p1 * (1.0 + c + c); p3 = p2 + c / xll; p4 = p3 + c / xlr; } } else if (n == nsave) { if (np < 30.0) goto L_np_small; } /*-------------------------- np = n*p >= 30 : ------------------- */ repeat { u = igraph_rng_get_unif01(rng) * p4; v = igraph_rng_get_unif01(rng); /* triangular region */ if (u <= p1) { ix = xm - p1 * v + u; goto finis; } /* parallelogram region */ if (u <= p2) { x = xl + (u - p1) / c; v = v * c + 1.0 - fabs(xm - x) / p1; if (v > 1.0 || v <= 0.) continue; ix = x; } else { if (u > p3) { /* right tail */ ix = xr - log(v) / xlr; if (ix > n) continue; v = v * (u - p3) * xlr; } else {/* left tail */ ix = xl + log(v) / xll; if (ix < 0) continue; v = v * (u - p2) * xll; } } /* determine appropriate way to perform accept/reject test */ k = abs(ix - m); if (k <= 20 || k >= npq / 2 - 1) { /* explicit evaluation */ f = 1.0; if (m < ix) { for (i = m + 1; i <= ix; i++) f *= (g / i - r); } else if (m != ix) { for (i = ix + 1; i <= m; i++) f /= (g / i - r); } if (v <= f) goto finis; } else { /* squeezing using upper and lower bounds on log(f(x)) */ amaxp = (k / npq) * ((k * (k / 3. + 0.625) + 0.1666666666666) / npq + 0.5); ynorm = -k * k / (2.0 * npq); alv = log(v); if (alv < ynorm - amaxp) goto finis; if (alv <= ynorm + amaxp) { /* Stirling's formula to machine accuracy */ /* for the final acceptance/rejection test */ x1 = ix + 1; f1 = fm + 1.0; z = n + 1 - fm; w = n - ix + 1.0; z2 = z * z; x2 = x1 * x1; f2 = f1 * f1; w2 = w * w; if (alv <= xm * log(f1 / x1) + (n - m + 0.5) * log(z / w) + (ix - m) * log(w * p / (x1 * q)) + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / f2) / f2) / f2) / f2) / f1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / z2) / z2) / z2) / z2) / z / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / x2) / x2) / x2) / x2) / x1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / w2) / w2) / w2) / w2) / w / 166320.) goto finis; } } } L_np_small: /*---------------------- np = n*p < 30 : ------------------------- */ repeat { ix = 0; f = qn; u = igraph_rng_get_unif01(rng); repeat { if (u < f) goto finis; if (ix > 110) break; u -= f; ix++; f *= (g / ix - r); } } finis: if (psave > 0.5) ix = n - ix; return (double)ix; } igraph_real_t igraph_rexp(igraph_rng_t *rng, double rate) { igraph_real_t scale = 1.0 / rate; if (!IGRAPH_FINITE(scale) || scale <= 0.0) { if (scale == 0.0) { return 0.0; } return IGRAPH_NAN; } return scale * igraph_exp_rand(rng); } #endif /********************************************************** * Testing purposes * *********************************************************/ /* int main() { */ /* int i; */ /* RNG_BEGIN(); */ /* for (i=0; i<1000; i++) { */ /* printf("%li ", RNG_INTEGER(1,10)); */ /* } */ /* printf("\n"); */ /* for (i=0; i<1000; i++) { */ /* printf("%f ", RNG_UNIF(0,1)); */ /* } */ /* printf("\n"); */ /* for (i=0; i<1000; i++) { */ /* printf("%f ", RNG_NORMAL(0,5)); */ /* } */ /* printf("\n"); */ /* RNG_END(); */ /* return 0; */ /* } */ igraph/src/prpack.h0000644000176000001440000000302012325527074013776 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_PRPACK #define IGRAPH_PRPACK #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_iterators.h" #include "igraph_interface.h" __BEGIN_DECLS int igraph_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vector_t *reset, const igraph_vector_t *weights); __END_DECLS #endif igraph/src/igraph_hrg_types.cc0000644000176000001440000032045412325527073016226 0ustar ripleyusers// *********************************************************************** // *** COPYRIGHT NOTICE ************************************************** // rbtree - red-black tree (self-balancing binary tree data structure) // Copyright (C) 2004 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // *********************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | // http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science // AND Santa Fe Institute // Created : Spring 2004 // Modified : many, many times // // *********************************************************************** #include "hrg_rbtree.h" #include "hrg_dendro.h" #include "hrg_graph.h" #include "hrg_splittree_eq.h" #include "hrg_graph_simp.h" #include "igraph_hrg.h" #include "igraph_constructors.h" #include "igraph_random.h" using namespace fitHRG; // ******** Red-Black Tree Methods *************************************** rbtree::rbtree() { root = new elementrb; leaf = new elementrb; leaf->parent = root; root->left = leaf; root->right = leaf; support = 0; } rbtree::~rbtree() { if (root != NULL && (root->left != leaf || root->right != leaf)) { deleteSubTree(root); } if (root) delete root; delete leaf; support = 0; root = 0; leaf = 0; } void rbtree::deleteTree() { if (root != NULL) { deleteSubTree(root); } } // does not leak memory void rbtree::deleteSubTree(elementrb *z) { if (z->left != leaf) { deleteSubTree(z->left); } if (z->right != leaf) { deleteSubTree(z->right); } delete z; } // ******** Search Functions ********************************************* // public search function - if there exists a elementrb in the tree // with key=searchKey, it returns TRUE and foundNode is set to point // to the found node; otherwise, it sets foundNode=NULL and returns // FALSE elementrb* rbtree::findItem(const int searchKey) { elementrb *current=root; // empty tree; bail out if (current->key==-1) { return NULL; } while (current != leaf) { // left-or-right? if (searchKey < current->key) { // try moving down-left if (current->left != leaf) { current = current->left; } else { // failure; bail out return NULL; } } else { // left-or-right? if (searchKey > current->key) { // try moving down-left if (current->right != leaf) { current = current->right; } else { // failure; bail out return NULL; } } else { // found (searchKey==current->key) return current; } } } return NULL; } int rbtree::returnValue(const int searchKey) { elementrb* test = findItem(searchKey); if (!test) { return 0; } else { return test->value; } } // ******** Return Item Functions **************************************** int* rbtree::returnArrayOfKeys() { int* array; array = new int [support]; bool flag_go = true; int index = 0; elementrb *curr; if (support == 1) { array[0] = root->key; } else if (support == 2) { array[0] = root->key; if (root->left == leaf) { array[1] = root->right->key; } else { array[1] = root->left->key; } } else { for (int i=0; imark = 1; while (flag_go) { // - is it time, and is left child the leaf node? if (curr->mark == 1 && curr->left == leaf) { curr->mark = 2; } // - is it time, and is right child the leaf node? if (curr->mark == 2 && curr->right == leaf) { curr->mark = 3; } if (curr->mark == 1) { // - go left curr->mark = 2; curr = curr->left; curr->mark = 1; } else if (curr->mark == 2) { // - else go right curr->mark = 3; curr = curr->right; curr->mark = 1; } else { // - else go up a level curr->mark = 0; array[index++] = curr->key; curr = curr->parent; if (curr == NULL) { flag_go = false; } } } } return array; } list* rbtree::returnListOfKeys() { keyValuePair *curr, *prev; list *head=0, *tail=0, *newlist; curr = returnTreeAsList(); while (curr != NULL) { newlist = new list; newlist->x = curr->x; if (head == NULL) { head = newlist; tail = head; } else { tail->next = newlist; tail = newlist; } prev = curr; curr = curr->next; delete prev; prev = NULL; } return head; } keyValuePair* rbtree::returnTreeAsList() { // pre-order traversal keyValuePair *head, *tail; head = new keyValuePair; head->x = root->key; head->y = root->value; tail = head; if (root->left != leaf) { tail = returnSubtreeAsList(root->left, tail); } if (root->right != leaf) { tail = returnSubtreeAsList(root->right, tail); } if (head->x == -1) { return NULL; /* empty tree */ } else { return head; } } keyValuePair* rbtree::returnSubtreeAsList(elementrb *z, keyValuePair *head) { keyValuePair *newnode, *tail; newnode = new keyValuePair; newnode->x = z->key; newnode->y = z->value; head->next = newnode; tail = newnode; if (z->left != leaf) { tail = returnSubtreeAsList(z->left, tail); } if (z->right != leaf) { tail = returnSubtreeAsList(z->right, tail); } return tail; } keyValuePair rbtree::returnMaxKey() { keyValuePair themax; elementrb *current; current = root; // search to bottom-right corner of tree while (current->right != leaf) { current = current->right; } themax.x = current->key; themax.y = current->value; return themax; } keyValuePair rbtree::returnMinKey() { keyValuePair themin; elementrb *current; current = root; // search to bottom-left corner of tree while (current->left != leaf) { current = current->left; } themin.x = current->key; themin.y = current->value; return themin; } // private functions for deleteItem() (although these could easily be // made public, I suppose) elementrb* rbtree::returnMinKey(elementrb *z) { elementrb *current; current = z; // search to bottom-right corner of tree while (current->left != leaf) { current = current->left; } return current; } elementrb* rbtree::returnSuccessor(elementrb *z) { elementrb *current, *w; w = z; // if right-subtree exists, return min of it if (w->right != leaf) { return returnMinKey(w->right); } // else search up in tree current = w->parent; while ((current!=NULL) && (w==current->right)) { w = current; // move up in tree until find a non-right-child current = current->parent; } return current; } int rbtree::returnNodecount() { return support; } // ******** Insert Functions ********************************************* // public insert function void rbtree::insertItem(int newKey, int newValue) { // first we check to see if newKey is already present in the tree; // if so, we do nothing; if not, we must find where to insert the // key elementrb *newNode, *current; // find newKey in tree; return pointer to it O(log k) current = findItem(newKey); if (current == NULL) { newNode = new elementrb; // elementrb for the rbtree newNode->key = newKey; newNode->value = newValue; newNode->color = true; // new nodes are always RED newNode->parent = NULL; // new node initially has no parent newNode->left = leaf; // left leaf newNode->right = leaf; // right leaf support++; // increment node count in rbtree // must now search for where to insert newNode, i.e., find the // correct parent and set the parent and child to point to each // other properly current = root; if (current->key==-1) { // insert as root delete root; // delete old root root = newNode; // set root to newNode leaf->parent = newNode; // set leaf's parent current = leaf; // skip next loop } // search for insertion point while (current != leaf) { // left-or-right? if (newKey < current->key) { // try moving down-left if (current->left != leaf) { current = current->left; } else { // else found new parent newNode->parent = current; // set parent current->left = newNode; // set child current = leaf; // exit search } } else { // try moving down-right if (current->right != leaf) { current = current->right; } else { // else found new parent newNode->parent = current; // set parent current->right = newNode; // set child current = leaf; // exit search } } } // now do the house-keeping necessary to preserve the red-black // properties insertCleanup(newNode); } return; } // private house-keeping function for insertion void rbtree::insertCleanup(elementrb *z) { // fix now if z is root if (z->parent==NULL) { z->color = false; return; } elementrb *temp; // while z is not root and z's parent is RED while (z->parent!=NULL && z->parent->color) { if (z->parent == z->parent->parent->left) { // z's parent is LEFT-CHILD temp = z->parent->parent->right; // grab z's uncle if (temp->color) { z->parent->color = false; // color z's parent BLACK (Case 1) temp->color = false; // color z's uncle BLACK (Case 1) z->parent->parent->color = true; // color z's grandpar. RED (Case 1) z = z->parent->parent; // set z = z's grandparent (Case 1) } else { if (z == z->parent->right) { // z is RIGHT-CHILD z = z->parent; // set z = z's parent (Case 2) rotateLeft(z); // perform left-rotation (Case 2) } z->parent->color = false; // color z's parent BLACK (Case 3) z->parent->parent->color = true; // color z's grandpar. RED (Case 3) rotateRight(z->parent->parent); // perform right-rotation (Case 3) } } else { // z's parent is RIGHT-CHILD temp = z->parent->parent->left; // grab z's uncle if (temp->color) { z->parent->color = false; // color z's parent BLACK (Case 1) temp->color = false; // color z's uncle BLACK (Case 1) z->parent->parent->color = true; // color z's grandpar. RED (Case 1) z = z->parent->parent; // set z = z's grandparent (Case 1) } else { if (z == z->parent->left) { // z is LEFT-CHILD z = z->parent; // set z = z's parent (Case 2) rotateRight(z); // perform right-rotation (Case 2) } z->parent->color = false; // color z's parent BLACK (Case 3) z->parent->parent->color = true; // color z's grandpar. RED (Case 3) rotateLeft(z->parent->parent); // perform left-rotation (Case 3) } } } root->color = false; // color the root BLACK return; } // ******** Delete // ******** Functions ********************************************* void rbtree::replaceItem(int key, int newValue) { elementrb* ptr; ptr = findItem(key); ptr->value = newValue; return; } void rbtree::incrementValue(int key) { elementrb* ptr; ptr = findItem(key); ptr->value = 1+ptr->value; return; } // public delete function void rbtree::deleteItem(int killKey) { elementrb *x, *y, *z; z = findItem(killKey); if (z == NULL) { return; } // item not present; bail out if (support==1) { // attempt to delete the root root->key = -1; // restore root node to default state root->value = -1; root->color = false; root->parent = NULL; root->left = leaf; root->right = leaf; support--; // set support to zero return; // exit - no more work to do } if (z != NULL) { support--; // decrement node count if ((z->left == leaf) || (z->right==leaf)) { y = z; // case of less than two children, // set y to be z } else { y = returnSuccessor(z); // set y to be z's key-successor } if (y->left!=leaf) { x = y->left; // pick y's one child (left-child) } else { x = y->right; // (right-child) } x->parent = y->parent; // make y's child's parent be y's parent if (y->parent==NULL) { root = x; // if y is the root, x is now root } else { if (y == y->parent->left) { // decide y's relationship with y's parent y->parent->left = x; // replace x as y's parent's left child } else { y->parent->right = x; // replace x as y's parent's left child } } if (y!=z) { // insert y into z's spot z->key = y->key; // copy y data into z z->value = y->value; } // do house-keeping to maintain balance if (y->color==false) { deleteCleanup(x); } delete y; y = NULL; } return; } void rbtree::deleteCleanup(elementrb *x) { elementrb *w, *t; // until x is the root, or x is RED while ((x != root) && (x->color==false)) { if (x==x->parent->left) { // branch on x being a LEFT-CHILD w = x->parent->right; // grab x's sibling if (w->color==true) { // if x's sibling is RED w->color = false; // color w BLACK (case 1) x->parent->color = true; // color x's parent RED (case 1) rotateLeft(x->parent); // left rotation on x's parent (case 1) w = x->parent->right; // make w be x's right sibling (case 1) } if ((w->left->color==false) && (w->right->color==false)) { w->color = true; // color w RED (case 2) x = x->parent; // examine x's parent (case 2) } else { if (w->right->color==false) { w->left->color = false; // color w's left child BLACK (case 3) w->color = true; // color w RED (case 3) t = x->parent; // store x's parent (case 3) rotateRight(w); // right rotation on w (case 3) x->parent = t; // restore x's parent (case 3) w = x->parent->right; // make w be x's right sibling (case 3) } w->color = x->parent->color; // w's color := x's parent's (case 4) x->parent->color = false; // color x's parent BLACK (case 4) w->right->color = false; // color w's right child BLACK (case 4) rotateLeft(x->parent); // left rotation on x's parent (case 4) x = root; // finished work. bail out (case 4) } } else { // x is RIGHT-CHILD w = x->parent->left; // grab x's sibling if (w->color==true) { // if x's sibling is RED w->color = false; // color w BLACK (case 1) x->parent->color = true; // color x's parent RED (case 1) rotateRight(x->parent); // right rotation on x's parent (case 1) w = x->parent->left; // make w be x's left sibling (case 1) } if ((w->right->color==false) && (w->left->color==false)) { w->color = true; // color w RED (case 2) x= x->parent; // examine x's parent (case 2) } else { if (w->left->color==false) { w->right->color = false; // color w's right child BLACK (case 3) w->color = true; // color w RED (case 3) t = x->parent; // store x's parent (case 3) rotateLeft(w); // left rotation on w (case 3) x->parent = t; // restore x's parent (case 3) w = x->parent->left; // make w be x's left sibling (case 3) } w->color = x->parent->color; // w's color := x's parent's (case 4) x->parent->color = false; // color x's parent BLACK (case 4) w->left->color = false; // color w's left child BLACK (case 4) rotateRight(x->parent); // right rotation on x's parent (case 4) x = root; // x is now the root (case 4) } } } x->color = false; // color x (the root) BLACK (exit) return; } // ******** Rotation Functions ****************************************** void rbtree::rotateLeft(elementrb *x) { elementrb *y; // do pointer-swapping operations for left-rotation y = x->right; // grab right child x->right = y->left; // make x's RIGHT-CHILD be y's LEFT-CHILD y->left->parent = x; // make x be y's LEFT-CHILD's parent y->parent = x->parent; // make y's new parent be x's old parent if (x->parent==NULL) { root = y; // if x was root, make y root } else { // if x is LEFT-CHILD, make y be x's parent's if (x == x->parent->left) { x->parent->left = y; // left-child } else { x->parent->right = y; // right-child } } y->left = x; // make x be y's LEFT-CHILD x->parent = y; // make y be x's parent return; } void rbtree::rotateRight(elementrb *y) { elementrb *x; // do pointer-swapping operations for right-rotation x = y->left; // grab left child y->left = x->right; // replace left child yith x's right subtree x->right->parent = y; // replace y as x's right subtree's parent x->parent = y->parent; // make x's new parent be y's old parent // if y was root, make x root if (y->parent==NULL) { root = x; } else { // if y is RIGHT-CHILD, make x be y's parent's if (y == y->parent->right) { // right-child y->parent->right = x; } else { // left-child y->parent->left = x; } } x->right = y; // make y be x's RIGHT-CHILD y->parent = x; // make x be y's parent return; } // *********************************************************************** // *** COPYRIGHT NOTICE ************************************************** // dendro.h - hierarchical random graph (hrg) data structure // Copyright (C) 2005-2009 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // *********************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | // http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark E.J. Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science // AND Santa Fe Institute // Created : 26 October 2005 - 7 December 2005 // Modified : 23 December 2007 (cleaned up for public consumption) // // *********************************************************************** // // Maximum likelihood dendrogram data structure. This is the heart of // the HRG algorithm: all manipulations are done here and all data is // stored here. The data structure uses the separate graph data // structure to store the basic adjacency information (in a // dangerously mutable way). // // *********************************************************************** // ******** Dendrogram Methods ******************************************* dendro::dendro(): root(0), internal(0), leaf(0), d(0), splithist(0), paths(0), ctree(0), cancestor(0), g(0) { } dendro::~dendro() { list *curr, *prev; if (g) { delete g; g =0; } // O(m) if (internal) { delete [] internal; internal =0; } // O(n) if (leaf) { delete [] leaf; leaf =0; } // O(n) if (d) { delete d; d =0; } // O(n) if (splithist){ delete splithist; splithist=0; } // potentially long if (paths) { for (int i=0; inext; delete prev; prev = 0; } paths[i] = 0; } delete [] paths; } paths=0; if (ctree) { delete [] ctree; ctree = 0; } // O(n) if (cancestor){ delete [] cancestor; cancestor = 0; } // O(n) } // ********************************************************************* void dendro::binarySearchInsert(elementd* x, elementd* y) { if (y->p < x->p) { // go to left subtree if (x->L == NULL) { // check if left subtree is empty x->L = y; // make x left child y->M = x; // make y parent of child return; } else { binarySearchInsert(x->L, y); } } else { // go to right subtree if (x->R == NULL) { // check if right subtree is empty x->R = y; // make x right child y->M = x; // make y parent of child return; } else { binarySearchInsert(x->R, y); } } return; } // ********************************************************************** list* dendro::binarySearchFind(const double v) { list *head = NULL, *tail = NULL, *newlist; elementd *current = root; bool flag_stopSearch = false; while (!flag_stopSearch) { // continue until we're finished newlist = new list; // add this node to the path newlist->x = current->label; if (current == root) { head = newlist; tail = head; } else { tail->next = newlist; tail = newlist; } if (v < current->p) { // now try left subtree if (current->L->type == GRAPH) { flag_stopSearch = true; } else { current = current->L; } } else { // else try right subtree if (current->R->type == GRAPH) { flag_stopSearch = true; } else { current = current->R; } } } return head; } // *********************************************************************** string dendro::buildSplit(elementd* thisNode) { // A "split" is defined as the bipartition of vertices into the sets // of leaves below the internal vertex in the tree (denoted by "C"), // and those above it (denoted as "M"). For simplicity, we represent // this bipartition as a character string of length n, where the ith // character denotes the partition membership (C,M) of the ith leaf // node. bool flag_go = true; const short int k = 1+DENDRO+GRAPH; elementd* curr; split sp; sp.initializeSplit(n); // default split string O(n) curr = thisNode; // - set start node as top this sub-tree curr->type = k+1; // - initialize in-order tree traversal while (flag_go) { // - is it time, and is left child a graph node? if (curr->type == k+1 && curr->L->type == GRAPH) { sp.s[curr->L->index] = 'C'; // - mark this leaf curr->type = k+2; } // - is it time, and is right child a graph node? if (curr->type == k+2 && curr->R->type == GRAPH) { sp.s[curr->R->index] = 'C'; // - mark this leaf curr->type = k+3; } if (curr->type == k+1) { // - go left curr->type = k+2; curr = curr->L; curr->type = k+1; } else if (curr->type == k+2) { // - else go right curr->type = k+3; curr = curr->R; curr->type = k+1; } else { // - else go up a level curr->type = DENDRO; if (curr->index == thisNode->index || curr->M == NULL) { flag_go = false; curr = NULL; } else { curr = curr->M; } } } // any leaf that was not already marked must be in the remainder of // the tree for (int i=0; inumNodes(); // size of graph leaf = new elementd [n]; // allocate memory for G, O(n) internal = new elementd [n-1]; // allocate memory for D, O(n) d = new interns(n-2); // allocate memory for internal // edges of D, O(n) for (int i=0; ilabel = 0; root->index = 0; root->p = RNG_UNIF01(); // insert remaining internal vertices, O(n log n) for (int i=1; i<(n-1); i++) { internal[i].label = i; internal[i].index = i; internal[i].p = RNG_UNIF01(); binarySearchInsert(root, &internal[i]); } // --- Hang leaf nodes off end of dendrogram O(n log n) // To impose this random hierarchical relationship on G, we first // take a random permutation of the leaf vertices and then replace // the NULLs at the bottom of the tree in-order with the leafs. As a // hack to ensure that we can find the leafs later using a binary // search, we assign each of them the p value of their parent, // perturbed slightly so as to preserve the binary search property. block* array; array = new block [n]; for (int i=0; i leaf for each leaf O(n log n) // Using the binary search property, we can find each leaf node in // O(log n) time. The binarySearchFind() function returns the list // of internal node indices that the search crossed, in the order of // root -> ... -> leaf, for use in the subsequent few operations. if (paths != NULL) { list *curr, *prev; for (int i=0; inext; delete prev; prev = NULL; } paths[i] = NULL; } delete [] paths; } paths = NULL; paths = new list* [n]; for (int i=0; igetNeighborList(i); while (curr != NULL) { ancestor = findCommonAncestor(paths, i, curr->x); ancestor->e += 1; curr = curr->next; } } for (int i=0; i<(n-1); i++) { internal[i].e /= 2; } // --- Count n for each internal node O(n log n) // To tabulate the number of leafs in each subtree rooted at an // internal node, we use the path information computed above. for (int i=0; iM; while (ancestor != NULL) { ancestor->n++; ancestor = ancestor->M; } } // --- Label all internal vertices O(n log n) // We want to label each internal vertex with the smallest leaf // index of its children. This will allow us to collapse many // leaf-orderings into a single dendrogram structure that is // independent of child-exhanges (since these have no impact on the // likelihood of the hierarchical structure). To do this, we loop // over the leaf vertices from smallest to largest and walk along // that leaf's path from the root. If we find an unlabeled internal // node, then we mark it with this leaf's index. for (int i=0; ilabel == -1 || ancestor->label > leaf[i].label) { ancestor->label = leaf[i].label; } ancestor = ancestor->M; } } // --- Exchange children to enforce order-property O(n) // We state that the order-property requires that an internal node's // label is the smallest index of its left subtree. The dendrogram // so far doesn't reflect this, so we need to step through each // internal vertex and make that adjustment (swapping nL and nR if // we make a change). elementd *tempe; for (int i=0; i<(n-1); i++) { if (internal[i].L->label > internal[i].label) { tempe = internal[i].L; internal[i].L = internal[i].R; internal[i].R = tempe; } } // --- Tabulate internal dendrogram edges O(n^2) // For the MCMC moves later on, we'll need to be able to choose, // uniformly at random, an internal edge of the dendrogram to // manipulate. There are always n-2 of them, and we can find them // simply by scanning across the internal vertices and observing // which have children that are also internal vertices. Note: very // important that the order property be enforced before this step is // taken; otherwise, the internal edges wont reflect the actual // dendrogram structure. for (int i=0; i<(n-1); i++) { if (internal[i].L->type == DENDRO) { d->addEdge(i, internal[i].L->index, LEFT); } if (internal[i].R->type == DENDRO) { d->addEdge(i, internal[i].R->index, RIGHT); } } // --- Clear memory for paths O(n log n) // Now that we're finished using the paths, we need to deallocate // them manually. list *current, *previous; for (int i=0; inext; delete previous; previous = NULL; } paths[i] = NULL; } delete [] paths; paths = NULL; // --- Compute p_i for each internal node O(n) // Each internal node's p_i = e_i / (nL_i*nR_i), and now that we // have each of those pieces, we may calculate this value for each // internal node. Given these, we can then calculate the // log-likelihood of the entire dendrogram structure \log(L) = // \sum_{i=1}^{n} ( ( e_i \log[p_i] ) + ( (nL_i*nR_i - e_i) // \log[1-p_i] ) ) L = 0.0; double dL; int nL_nR, ei; for (int i=0; i<(n-1); i++) { nL_nR = internal[i].L->n*internal[i].R->n; ei = internal[i].e; internal[i].p = (double)(ei) / (double)(nL_nR); if (ei == 0 || ei == nL_nR) { dL = 0.0; } else { dL = ei * log(internal[i].p) + (nL_nR - ei) * log(1.0-internal[i].p); } internal[i].logL = dL; L += dL; } for (int i=0; i<(n-1); i++) { if (internal[i].label > internal[i].L->label) { tempe = internal[i].L; internal[i].L = internal[i].R; internal[i].R = tempe; } } // Dendrogram is now built return; } // *********************************************************************** void dendro::clearDendrograph() { // Clear out the memory and references used by the dendrograph // structure - this is intended to be called just before an // importDendrogramStructure call so as to avoid memory leaks and // overwriting the references therein. if (g != NULL) { delete g; g = NULL; } // O(m) if (leaf != NULL) { delete [] leaf; leaf = NULL; } // O(n) if (internal != NULL) { delete [] internal; internal = NULL; } // O(n) if (d != NULL) { delete d; d = NULL; } // O(n) root = NULL; return; } // ********************************************************************** int dendro::computeEdgeCount(const int a, const short int atype, const int b, const short int btype) { // This function computes the number of edges that cross between the // subtree internal[a] and the subtree internal[b]. To do this, we // use an array A[1..n] integers which take values -1 if A[i] is in // the subtree defined by internal[a], +1 if A[i] is in the subtree // internal[b], and 0 otherwise. Taking the smaller of the two sets, // we then scan over the edges attached to that set of vertices and // count the number of endpoints we see in the other set. bool flag_go = true; int nA, nB; int count = 0; const short int k = 1+DENDRO+GRAPH; elementd* curr; // First, we push the leaf nodes in the L and R subtrees into // balanced binary tree structures so that we can search them // quickly later on. if (atype == GRAPH) { // default case, subtree A is size 1 // insert single node as member of left subtree subtreeL.insertItem(a,-1); nA = 1; // } else { // explore subtree A, O(|A|) curr = &internal[a]; curr->type = k+1; nA = 0; while (flag_go) { if (curr->index == internal[a].M->index) { internal[a].type = DENDRO; flag_go = false; } else { // - is it time, and is left child a graph node? if (curr->type == k+1 && curr->L->type == GRAPH) { subtreeL.insertItem(curr->L->index, -1); curr->type = k+2; nA++; } // - is it time, and is right child a graph node? if (curr->type == k+2 && curr->R->type == GRAPH) { subtreeL.insertItem(curr->R->index, -1); curr->type = k+3; nA++; } if (curr->type == k+1) { // - go left curr->type = k+2; curr = curr->L; curr->type = k+1; } else if (curr->type == k+2) { // - else go right curr->type = k+3; curr = curr->R; curr->type = k+1; } else { // - else go up a level curr->type = DENDRO; curr = curr->M; if (curr == NULL) { flag_go = false; } } } } } if (btype == GRAPH) { // default case, subtree A is size 1 // insert node as single member of right subtree subtreeR.insertItem(b,1); nB = 1; } else { flag_go = true; // explore subtree B, O(|B|) curr = &internal[b]; curr->type = k+1; nB = 0; while (flag_go) { if (curr->index == internal[b].M->index) { internal[b].type = DENDRO; flag_go = false; } else { // - is it time, and is left child a graph node? if (curr->type == k+1 && curr->L->type == GRAPH) { subtreeR.insertItem(curr->L->index, 1); curr->type = k+2; nB++; } // - is it time, and is right child a graph node? if (curr->type == k+2 && curr->R->type == GRAPH) { subtreeR.insertItem(curr->R->index, 1); curr->type = k+3; nB++; } if (curr->type == k+1) { // - look left curr->type = k+2; curr = curr->L; curr->type = k+1; } else if (curr->type == k+2) { // - look right curr->type = k+3; curr = curr->R; curr->type = k+1; } else { // - else go up a level curr->type = DENDRO; curr = curr->M; if (curr == NULL) { flag_go = false; } } } } } // Now, we take the smaller subtree and ask how many of its // emerging edges have their partner in the other subtree. O(|A| log // |A|) time edge* current; int* treeList; if (nA < nB) { // subtreeL is smaller treeList = subtreeL.returnArrayOfKeys(); for (int i=0; igetNeighborList(treeList[i]); // loop over each of its neighbors v_j while (current != NULL) { // to see if v_j is in A if (subtreeR.findItem(current->x) != NULL) { count++; } current = current->next; } subtreeL.deleteItem(treeList[i]); } delete [] treeList; treeList = subtreeR.returnArrayOfKeys(); for (int i=0; igetNeighborList(treeList[i]); // loop over each of its neighbors v_j while (current != NULL) { // to see if v_j is in B if (subtreeL.findItem(current->x) != NULL) { count++; } current = current->next; } subtreeR.deleteItem(treeList[i]); } delete [] treeList; treeList = subtreeL.returnArrayOfKeys(); for (int i=0; ireturnArrayOfKeys(); tot = splithist->returnTotal(); leng = splithist->returnNodecount(); for (int i=0; ireturnValue(array[i]) / tot) < 0.5) { splithist->deleteItem(array[i]); } } delete [] array; array = NULL; return; } // ********************************************************************** elementd* dendro::findCommonAncestor(list** paths, const int i, const int j) { list* headOne = paths[i]; list* headTwo = paths[j]; elementd* lastStep = NULL; while (headOne->x == headTwo->x) { lastStep = &internal[headOne->x]; headOne = headOne->next; headTwo = headTwo->next; if (headOne == NULL || headTwo == NULL) { break; } } return lastStep; // Returns address of an internal node; do not deallocate } // ********************************************************************** int dendro::getConsensusSize() { string *array; double value, tot; int numSplits, numCons; numSplits = splithist->returnNodecount(); array = splithist->returnArrayOfKeys(); tot = splithist->returnTotal(); numCons = 0; for (int i=0; ireturnValue(array[i]); if (value / tot > 0.5) { numCons++; } } delete [] array; array = NULL; return numCons; } // ********************************************************************** splittree* dendro::getConsensusSplits() { string *array; splittree *consensusTree; double value, tot; consensusTree = new splittree; int numSplits; // We look at all of the splits in our split histogram and add any // one that's in the majority to our consensusTree, which we then // return (note that consensusTree needs to be deallocated by the // user). numSplits = splithist->returnNodecount(); array = splithist->returnArrayOfKeys(); tot = splithist->returnTotal(); for (int i=0; ireturnValue(array[i]); if (value / tot > 0.5) { consensusTree->insertItem(array[i], value / tot); } } delete [] array; array = NULL; return consensusTree; } // *********************************************************************** double dendro::getLikelihood() { return L; } // *********************************************************************** void dendro::getSplitList(splittree* split_tree) { string sp; for (int i=0; i<(n-1); i++) { sp = d->getSplit(i); if (!sp.empty() && sp[1] != '-') { split_tree->insertItem(sp,0.0); } } return; } // *********************************************************************** double dendro::getSplitTotalWeight() { if (splithist) { return splithist->returnTotal(); } else { return 0; } } // *********************************************************************** bool dendro::importDendrogramStructure(const igraph_hrg_t *hrg) { n=igraph_hrg_size(hrg); // allocate memory for G, O(n) leaf = new elementd[n]; // allocate memory for D, O(n) internal = new elementd[n-1]; // allocate memory for internal edges of D, O(n) d = new interns(n-2); // initialize leaf nodes for (int i=0; ilabel=0; for (int i=1; ileft)[i]; int R=VECTOR(hrg->right)[i]; if (L < 0) { internal[i].L = &internal[-L-1]; internal[-L-1].M = &internal[i]; } else { internal[i].L = &leaf[L]; leaf[L].M = &internal[i]; } if (R < 0) { internal[i].R = &internal[-R-1]; internal[-R-1].M = &internal[i]; } else { internal[i].R = &leaf[R]; leaf[R].M = &internal[i]; } internal[i].p = VECTOR(hrg->prob)[i]; internal[i].e = VECTOR(hrg->edges)[i]; internal[i].n = VECTOR(hrg->vertices)[i]; internal[i].index = i; } // --- Label all internal vertices O(n log n) elementd *curr; for (int i=0; ilabel == -1 || curr->label > leaf[i].label) { curr->label = leaf[i].label; } curr = curr -> M; } } // --- Exchange children to enforce order-property O(n) elementd *tempe; for (int i=0; ilabel > internal[i].label) { tempe = internal[i].L; internal[i].L = internal[i].R; internal[i].R = tempe; } } // --- Tabulate internal dendrogram edges O(n) for (int i=0; i<(n-1); i++) { if (internal[i].L->type == DENDRO) { d->addEdge(i, internal[i].L->index, LEFT); } if (internal[i].R->type == DENDRO) { d->addEdge(i, internal[i].R->index, RIGHT); } } // --- Compute p_i for each internal node O(n) // Each internal node's p_i = e_i / (nL_i*nR_i), and now that we // have each of those pieces, we may calculate this value for each // internal node. Given these, we can then calculate the // log-likelihood of the entire dendrogram structure // \log(L) = \sum_{i=1}^{n} ( ( e_i \log[p_i] ) + // ( (nL_i*nR_i - e_i) \log[1-p_i] ) ) L = 0.0; double dL; int nL_nR, ei; for (int i=0; i<(n-1); i++) { nL_nR = internal[i].L->n*internal[i].R->n; ei = internal[i].e; if (ei == 0 || ei == nL_nR) { dL = 0.0; } else { dL = (double)(ei) * log(internal[i].p) + (double)(nL_nR - ei) * log(1.0-internal[i].p); } internal[i].logL = dL; L += dL; } return true; } // *********************************************************************** void dendro::makeRandomGraph() { if (g != NULL) { delete g; } g = NULL; g = new graph(n); list *curr, *prev; if (paths) { for (int i=0; inext; delete prev; prev = NULL; } paths[i] = NULL; } delete [] paths; } // build paths from root O(n d) paths = new list* [n]; for (int i=0; ip) { if (!(g->doesLinkExist(i,j))) { g->addLink(i,j); } if (!(g->doesLinkExist(j,i))) { g->addLink(j,i); } } } } for (int i=0; inext; delete prev; prev = NULL; } paths[i] = NULL; } delete [] paths; // delete paths data structure O(n log n) paths = NULL; return; } // ********************************************************************** bool dendro::monteCarloMove(double& delta, bool& ftaken, const double T) { // A single MC move begins with the selection of a random internal // edge (a,b) of the dendrogram. This also determines the three // subtrees i, j, k that we will rearrange, and we choose uniformly // from among the options. // // If (a,b) is a left-edge, then we have ((i,j),k), and moves // ((i,j),k) -> ((i,k),j) (alpha move) // -> (i,(j,k)) + enforce order-property for (j,k) (beta move) // // If (a,b) is a right-edge, then we have (i,(j,k)), and moves // (i,(j,k)) -> ((i,k),j) (alpha move) // -> ((i,j),k) (beta move) // // For each of these moves, we need to know what the change in // likelihood will be, so that we can determine with what // probability we execute the move. elementd *temp; ipair *tempPair; int x, y, e_x, e_y, n_i, n_j, n_k, n_x, n_y; short int t; double p_x, p_y, L_x, L_y, dLogL; string new_split; // The remainder of the code executes a single MCMC move, where we // sample the dendrograms proportionally to their likelihoods (i.e., // temperature=1, if you're comparing it to the usual MCMC // framework). delta = 0.0; ftaken = false; tempPair = d->getRandomEdge(); // returns address; no need to deallocate x = tempPair->x; // copy contents of referenced random edge y = tempPair->y; // into local variables t = tempPair->t; if (t == LEFT) { if (RNG_UNIF01() < 0.5) { // ## LEFT ALPHA move: ((i,j),k) -> ((i,k),j) // We need to calculate the change in the likelihood (dLogL) // that would result from this move. Most of the information // needed to do this is already available, the exception being // e_ik, the number of edges that span the i and k subtrees. I // use a slow algorithm O(n) to do this, since I don't know of a // better way at this point. (After several attempts to find a // faster method, no luck.) n_i = internal[y].L->n; n_j = internal[y].R->n; n_k = internal[x].R->n; n_y = n_i*n_k; e_y = computeEdgeCount(internal[y].L->index, internal[y].L->type, internal[x].R->index, internal[x].R->type); p_y = (double)(e_y) / (double)(n_y); if (e_y == 0 || e_y == n_y) { L_y = 0.0; } else { L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0-p_y); } n_x = (n_i+n_k)*n_j; e_x = internal[x].e + internal[y].e - e_y; // e_yj p_x = (double)(e_x) / (double)(n_x); if (e_x == 0 || e_x == n_x) { L_x = 0.0; } else { L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0-p_x); } dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T*dLogL))) { // make LEFT ALPHA move ftaken = true; d->swapEdges(x, internal[x].R->index, RIGHT, y, internal[y].R->index, RIGHT); temp = internal[x].R; // - swap j and k internal[x].R = internal[y].R; internal[y].R = temp; internal[x].R->M = &internal[x]; // - adjust parent pointers internal[y].R->M = &internal[y]; internal[y].n = n_i + n_k; // - update n for [y] internal[x].e = e_x; // - update e_i for [x] and [y] internal[y].e = e_y; internal[x].p = p_x; // - update p_i for [x] and [y] internal[y].p = p_y; internal[x].logL = L_x; // - update L_i for [x] and [y] internal[y].logL = L_y; // - order-property maintained L += dLogL; // - update LogL delta = dLogL; } } else { // ## LEFT BETA move: ((i,j),k) -> (i,(j,k)) n_i = internal[y].L->n; n_j = internal[y].R->n; n_k = internal[x].R->n; n_y = n_j*n_k; e_y = computeEdgeCount(internal[y].R->index, internal[y].R->type, internal[x].R->index, internal[x].R->type); p_y = (double)(e_y) / (double)(n_y); if (e_y == 0 || e_y == n_y) { L_y = 0.0; } else { L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0-p_y); } n_x = (n_j+n_k)*n_i; e_x = internal[x].e + internal[y].e - e_y; // e_yj p_x = (double)(e_x) / (double)(n_x); if (e_x == 0 || e_x == n_x) { L_x = 0.0; } else { L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0-p_x); } dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T*dLogL))) { // make LEFT BETA move ftaken = true; d->swapEdges(y, internal[y].L->index, LEFT, y, internal[y].R->index, RIGHT); temp = internal[y].L; // - swap L and R of [y] internal[y].L = internal[y].R; internal[y].R = temp; d->swapEdges(x, internal[x].R->index, RIGHT, y, internal[y].R->index, RIGHT); temp = internal[x].R; // - swap i and k internal[x].R = internal[y].R; internal[y].R = temp; internal[x].R->M = &internal[x]; // - adjust parent pointers internal[y].R->M = &internal[y]; d->swapEdges(x, internal[x].L->index, LEFT, x, internal[x].R->index, RIGHT); temp = internal[x].L; // - swap L and R of [x] internal[x].L = internal[x].R; internal[x].R = temp; internal[y].n = n_j + n_k; // - update n internal[x].e = e_x; // - update e_i internal[y].e = e_y; internal[x].p = p_x; // - update p_i internal[y].p = p_y; internal[x].logL = L_x; // - update logL_i internal[y].logL = L_y; if (internal[y].R->label < internal[y].L->label) { // - enforce order-property if necessary d->swapEdges(y, internal[y].L->index, LEFT, y, internal[y].R->index, RIGHT); temp = internal[y].L; internal[y].L = internal[y].R; internal[y].R = temp; } // internal[y].label = internal[y].L->label; L += dLogL; // - update LogL delta = dLogL; } } } else { // right-edge: t == RIGHT if (RNG_UNIF01() < 0.5) { // alpha move: (i,(j,k)) -> ((i,k),j) n_i = internal[x].L->n; n_j = internal[y].L->n; n_k = internal[y].R->n; n_y = n_i*n_k; e_y = computeEdgeCount(internal[x].L->index, internal[x].L->type, internal[y].R->index, internal[y].R->type); p_y = (double)(e_y) / (double)(n_y); if (e_y == 0 || e_y == n_y) { L_y = 0.0; } else { L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0-p_y); } n_x = (n_i+n_k)*n_j; e_x = internal[x].e + internal[y].e - e_y; // e_yj p_x = (double)(e_x) / (double)(n_x); if (e_x == 0 || e_x == n_x) { L_x = 0.0; } else { L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0-p_x); } dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T*dLogL))) { // make RIGHT ALPHA move ftaken = true; d->swapEdges(x, internal[x].L->index, LEFT, x, internal[x].R->index, RIGHT); temp = internal[x].L; // - swap L and R of [x] internal[x].L = internal[x].R; internal[x].R = temp; d->swapEdges(y, internal[y].L->index, LEFT, x, internal[x].R->index, RIGHT); temp = internal[y].L; // - swap i and j internal[y].L = internal[x].R; internal[x].R = temp; internal[x].R->M = &internal[x]; // - adjust parent pointers internal[y].L->M = &internal[y]; internal[y].n = n_i + n_k; // - update n internal[x].e = e_x; // - update e_i internal[y].e = e_y; internal[x].p = p_x; // - update p_i internal[y].p = p_y; internal[x].logL = L_x; // - update logL_i internal[y].logL = L_y; internal[y].label = internal[x].label; // - update order property L += dLogL; // - update LogL delta = dLogL; } } else { // beta move: (i,(j,k)) -> ((i,j),k) n_i = internal[x].L->n; n_j = internal[y].L->n; n_k = internal[y].R->n; n_y = n_i*n_j; e_y = computeEdgeCount(internal[x].L->index, internal[x].L->type, internal[y].L->index, internal[y].L->type); p_y = (double)(e_y) / (double)(n_y); if (e_y == 0 || e_y == n_y) { L_y = 0.0; } else { L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0-p_y); } n_x = (n_i+n_j)*n_k; e_x = internal[x].e + internal[y].e - e_y; // e_yk p_x = (double)(e_x) / (double)(n_x); if (e_x == 0 || e_x == n_x) { L_x = 0.0; } else { L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0-p_x); } dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T*dLogL))) { // make RIGHT BETA move ftaken = true; d->swapEdges(x, internal[x].L->index, LEFT, x, internal[x].R->index, RIGHT); temp = internal[x].L; // - swap L and R of [x] internal[x].L = internal[x].R; internal[x].R = temp; d->swapEdges(x, internal[x].R->index, RIGHT, y, internal[y].R->index, RIGHT); temp = internal[x].R; // - swap i and k internal[x].R = internal[y].R; internal[y].R = temp; internal[x].R->M = &internal[x]; // - adjust parent pointers internal[y].R->M = &internal[y]; d->swapEdges(y, internal[y].L->index, LEFT, y, internal[y].R->index, RIGHT); temp = internal[y].L; // - swap L and R of [y] internal[y].L = internal[y].R; internal[y].R = temp; internal[y].n = n_i + n_j; // - update n internal[x].e = e_x; // - update e_i internal[y].e = e_y; internal[x].p = p_x; // - update p_i internal[y].p = p_y; internal[x].logL = L_x; // - update logL_i internal[y].logL = L_y; internal[y].label = internal[x].label; // - order-property L += dLogL; // - update LogL delta = dLogL; } } } return true; } // ********************************************************************** void dendro::refreshLikelihood() { // recalculates the log-likelihood of the dendrogram structure L = 0.0; double dL; int nL_nR, ei; for (int i=0; i<(n-1); i++) { nL_nR = internal[i].L->n*internal[i].R->n; ei = internal[i].e; internal[i].p = (double)(ei) / (double)(nL_nR); if (ei == 0 || ei == nL_nR) { dL = 0.0; } else { dL = ei * log(internal[i].p) + (nL_nR - ei) * log(1.0-internal[i].p); } internal[i].logL = dL; L += dL; } return; } // ********************************************************************** void dendro::QsortMain (block* array, int left, int right) { if (right > left) { int pivot = left; int part = QsortPartition(array, left, right, pivot); QsortMain(array, left, part-1); QsortMain(array, part+1, right ); } return; } int dendro::QsortPartition (block* array, int left, int right, int index) { block p_value, temp; p_value.x = array[index].x; p_value.y = array[index].y; // swap(array[p_value], array[right]) temp.x = array[right].x; temp.y = array[right].y; array[right].x = array[index].x; array[right].y = array[index].y; array[index].x = temp.x; array[index].y = temp.y; int stored = left; for (int i=left; inumNodes(); // First, cull the split hist so that only splits with weight >= 0.5 // remain cullSplitHist(); int treesize = splithist->returnNodecount(); // Now, initialize the various arrays we use to keep track of the // internal structure of the consensus tree. ctree = new cnode[treesize]; cancestor = new int[n]; for (int i=0; i=0; i--) { // First, we get a list of all the splits with this exactly i Ms curr = splithist->returnTheseSplits(i); // Now we loop over that list while (curr != NULL) { splithist->deleteItem(curr->x); // add weight to this internal node ctree[ii].weight = curr->y; // examine each letter of this split for (int j=0; jx[j] == 'C') { // - node is child of this internal node if (cancestor[j] == -1) { // - first time this leaf has ever been seen newChild = new child; newChild->type = GRAPH; newChild->index = j; newChild->next = NULL; // - attach child to list if (ctree[ii].lastChild == NULL) { ctree[ii].children = newChild; ctree[ii].lastChild = newChild; ctree[ii].degree = 1; } else { ctree[ii].lastChild->next = newChild; ctree[ii].lastChild = newChild; ctree[ii].degree += 1; } } else { // - this leaf has been seen before // If the parent of the ancestor of this leaf is the // current internal node then this leaf is already a // descendant of this internal node, and we can move on; // otherwise, we need to add that ancestor to this // internal node's child list, and update various // relations if (ctree[cancestor[j]].parent != ii) { ctree[cancestor[j]].parent = ii; newChild = new child; newChild->type = DENDRO; newChild->index = cancestor[j]; newChild->next = NULL; // - attach child to list if (ctree[ii].lastChild == NULL) { ctree[ii].children = newChild; ctree[ii].lastChild = newChild; ctree[ii].degree = 1; } else { ctree[ii].lastChild->next = newChild; ctree[ii].lastChild = newChild; ctree[ii].degree += 1; } } } // note new ancestry for this leaf cancestor[j] = ii; } } // update internal node index ii++; prev = curr; curr = curr->next; delete prev; } } // Return the consensus tree igraph_vector_resize(parents, ii + orig_nodes); if (weights) { igraph_vector_resize(weights, ii); } for (int i=0; itype == GRAPH) { VECTOR(*parents)[sit->index] = orig_nodes + i; } sat=sit; sit=sit->next; delete sat; } if (weights) { VECTOR(*weights)[i] = ctree[i].weight; } ctree[i].children=0; } // Plus the isolate nodes for (int i=0; iindex; int ri=internal[i].R->index; VECTOR(hrg->left )[i] = internal[i].L->type == DENDRO ? -li-1 : li; VECTOR(hrg->right)[i] = internal[i].R->type == DENDRO ? -ri-1 : ri; VECTOR(hrg->prob )[i] = internal[i].p; VECTOR(hrg->edges)[i] = internal[i].e; VECTOR(hrg->vertices)[i] = internal[i].n; } } void dendro::recordGraphStructure(igraph_t *graph) { igraph_vector_t edges; int no_of_nodes=g->numNodes(); int no_of_edges=g->numLinks() / 2; int idx=0; igraph_vector_init(&edges, no_of_edges*2); IGRAPH_FINALLY(igraph_vector_destroy, &edges); for (int i=0; igetNeighborList(i); while (curr) { if (i < curr->x) { VECTOR(edges)[idx++] = i; VECTOR(edges)[idx++] = curr->x; } curr = curr->next; } } igraph_create(graph, &edges, no_of_nodes, /* directed= */ 0); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } // ********************************************************************** list* dendro::reversePathToRoot(const int leafIndex) { list *head, *subhead, *newlist; head = subhead = newlist = NULL; elementd *current = &leaf[leafIndex]; // continue until we're finished while (current != NULL) { // add this node to the path newlist = new list; newlist->x = current->index; newlist->next = NULL; if (head == NULL) { head = newlist; } else { subhead = head; head = newlist; head->next = subhead; } current = current->M; } return head; } // *********************************************************************** bool dendro::sampleSplitLikelihoods(int &sample_num) { // In order to compute the majority agreement dendrogram at // equilibrium, we need to calculate the leaf partition defined by // each split (internal edge) of the tree. Because splits are only // defined on a Cayley tree, the buildSplit() function returns the // default "--...--" string for the root and the root's left // child. When tabulating the frequency of splits, one of these // needs to be excluded. IGRAPH_UNUSED(sample_num); string* array; int k; double tot; string new_split; // To decompose the tree into its splits, we simply loop over all // the internal nodes and replace the old split for the ith internal // node with its new split. This is a bit time consuming to do // O(n^2), so try not to do this very often. Once the decomposition // is had, we insert them into the split histogram, which tracks the // cumulative weight for each respective split observed. if (splithist == NULL) { splithist = new splittree; } for (int i=0; i<(n-1); i++) { new_split = buildSplit(&internal[i]); d->replaceSplit(i, new_split); if (!new_split.empty() && new_split[1] != '-') { if (!splithist->insertItem(new_split, 1.0)) { return false; } } } splithist->finishedThisRound(); // For large graphs, the split histogram can get extremely large, so // we need to employ some measures to prevent it from swamping the // available memory. When the number of splits exceeds a threshold // (say, a million), we progressively delete splits that have a // weight less than a rising (k*0.001 of the total weight) fraction // of the splits, on the assumption that losing such weight is // unlikely to effect the ultimate split statistics. This deletion // procedure is slow O(m lg m), but should only happen very rarely. int split_max = n*500; int leng; if (splithist->returnNodecount() > split_max) { k=1; while (splithist->returnNodecount() > split_max) { array = splithist->returnArrayOfKeys(); tot = splithist->returnTotal(); leng = splithist->returnNodecount(); for (int i=0; ireturnValue(array[i]) / tot) < k*0.001) { splithist->deleteItem(array[i]); } } delete [] array; array = NULL; k++; } } return true; } void dendro::sampleAdjacencyLikelihoods() { // Here, we sample the probability values associated with every // adjacency in A, weighted by their likelihood. The weighted // histogram is stored in the graph data structure, so we simply // need to add an observation to each node-pair that corresponds to // the associated branch point's probability and the dendrogram's // overall likelihood. double nn; double norm = ((double)(n) * (double)(n)) / 4.0; if (L > 0.0) { L = 0.0; } elementd* ancestor; list *currL, *prevL; if (paths != NULL) { for (int i=0; inext; delete prevL; prevL = NULL; } paths[i] = NULL; } delete [] paths; } paths = NULL; paths = new list* [n]; for (int i=0; iL->n) * (double)(ancestor->R->n)) / norm; // add obs of ->p to (i,j) histogram, and g->addAdjacencyObs(i, j, ancestor->p, nn); // add obs of ->p to (j,i) histogram g->addAdjacencyObs(j, i, ancestor->p, nn); } } // finish-up: upate total weight in histograms g->addAdjacencyEnd(); return; } void dendro::resetDendrograph() { // Reset the dendrograph structure for the next trial if (leaf != NULL) { delete [] leaf; leaf = NULL; } // O(n) if (internal != NULL) { delete [] internal; internal = NULL; } // O(n) if (d != NULL) { delete d; d = NULL; } // O(n) root = NULL; if (paths != NULL) { list *curr, *prev; for (int i=0; inext; delete prev; prev = NULL; } paths[i] = NULL; } delete [] paths; } paths = NULL; L = 1.0; return; } // ********************************************************************** // *** COPYRIGHT NOTICE ************************************************* // graph.h - graph data structure for hierarchical random graphs // Copyright (C) 2005-2008 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // ********************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | // http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark E.J. Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science // AND Santa Fe Institute // Created : 8 November 2005 // Modified : 23 December 2007 (cleaned up for public consumption) // // *********************************************************************** // // Graph data structure for hierarchical random graphs. The basic // structure is an adjacency list of edges; however, many additional // pieces of metadata are stored as well. Each node stores its // external name, its degree and (if assigned) its group index. // // *********************************************************************** // ******** Constructor / Destructor ************************************* graph::graph(const int size, bool predict) : predict(predict) { n = size; m = 0; nodes = new vert [n]; nodeLink = new edge* [n]; nodeLinkTail = new edge* [n]; for (int i=0; inext; delete prev; } } delete [] nodeLink; nodeLink = NULL; delete [] nodeLinkTail; nodeLinkTail = NULL; delete [] nodes; nodes = NULL; if (predict) { for (int i=0; i= 0 && i < n && j >= 0 && j < n) { newedge = new edge; newedge->x = j; if (nodeLink[i] == NULL) { // first neighbor nodeLink[i] = newedge; nodeLinkTail[i] = newedge; nodes[i].degree = 1; } else { // subsequent neighbor nodeLinkTail[i]->next = newedge; nodeLinkTail[i] = newedge; nodes[i].degree++; } // increment edge count m++; return true; } else { return false; } } // *********************************************************************** bool graph::addAdjacencyObs(const int i, const int j, const double probability, const double size) { // Adds the observation obs to the histogram of the edge (i,j) // Note: user must manually add observation to edge (j,i) by calling // this function with that argument if (bin_resolution > 0.0 && probability >= 0.0 && probability <= 1.0 && size >= 0.0 && size <= 1.0 && i >= 0 && i < n && j >= 0 && j < n) { int index = (int)(probability/bin_resolution + 0.5); if (index < 0) { index = 0; } else if (index > num_bins) { index = num_bins; } // Add the weight to the proper probability bin if (A[i][j][index] < 0.5) { A[i][j][index] = 1.0; } else { A[i][j][index] += 1.0; } return true; } return false; } // ********************************************************************** void graph::addAdjacencyEnd() { // We need to also keep a running total of how much weight has been added // to the histogram, and the number of observations in the histogram. if (obs_count==0) { total_weight = 1.0; obs_count = 1; } else { total_weight += 1.0; obs_count++; } return; } bool graph::doesLinkExist(const int i, const int j) { // This function determines if the edge (i,j) already exists in the // adjacency list of v_i edge* curr; if (i >= 0 && i < n && j >= 0 && j < n) { curr = nodeLink[i]; while (curr != NULL) { if (curr->x == j) { return true; } curr = curr->next; } } return false; } // ********************************************************************** int graph::getDegree(const int i) { if (i >= 0 && i < n) { return nodes[i].degree; } else { return -1; } } string graph::getName(const int i) { if (i >= 0 && i < n) { return nodes[i].name; } else { return ""; } } // NOTE: Returns address; deallocation of returned object is dangerous edge* graph::getNeighborList(const int i) { if (i >= 0 && i < n) { return nodeLink[i]; } else { return NULL; } } double* graph::getAdjacencyHist(const int i, const int j) { if (i >= 0 && i < n && j >= 0 && j < n) { return A[i][j]; } else { return NULL; } } // ********************************************************************** double graph::getAdjacencyAverage(const int i, const int j) { double average = 0.0; if (i != j) { for (int k=0; k 0.0) { average += (A[i][j][k] / total_weight)*((double)(k)*bin_resolution); } } } return average; } int graph::numLinks() { return m; } int graph::numNodes() { return n; } double graph::getBinResolution() { return bin_resolution; } int graph::getNumBins() { return num_bins; } double graph::getTotalWeight() { return total_weight; } // *********************************************************************** void graph::resetAllAdjacencies() { for (int i=0; i= 0 && i < n && j >= 0 && j < n) { for (int k=0; knext; delete prev; } nodeLink[i] = NULL; nodeLinkTail[i] = NULL; nodes[i].degree = 0; } m = 0; return; } // ********************************************************************** void graph::setAdjacencyHistograms(const int bin_count) { // For all possible adjacencies, setup an edge histograms num_bins = bin_count+1; bin_resolution = 1.0 / (double)(bin_count); for (int i=0; i= 0 && i < n) { nodes[i].name = text; return true; } else { return false; } } // ********************************************************************** interns::interns(const int n) { q = n; count = 0; edgelist = new ipair [q]; splitlist = new string [q+1]; indexLUT = new int* [q+1]; for (int i=0; i<(q+1); i++) { indexLUT[i] = new int [2]; indexLUT[i][0] = indexLUT[i][1] = -1; } } interns::~interns() { delete [] edgelist; delete [] splitlist; for (int i=0; i<(q+1); i++) { delete [] indexLUT[i]; } delete [] indexLUT; } // *********************************************************************** // NOTE: Returns an address to another object -- do not deallocate ipair* interns::getEdge(const int i) { return &edgelist[i]; } // *********************************************************************** // NOTE: Returns an address to another object -- do not deallocate ipair* interns::getRandomEdge() { return &edgelist[(int)(floor((double)(q)*RNG_UNIF01()))]; } // *********************************************************************** string interns::getSplit(const int i) { if (i >= 0 && i <= q) { return splitlist[i]; } else { return ""; } } // ********************************************************************** bool interns::addEdge(const int new_x, const int new_y, const short int new_type) { // This function adds a new edge (i,j,t,sp) to the list of internal // edges. After checking that the inputs fall in the appropriate // range of values, it records the new edgelist index in the // indexLUT and then puts the input values into that edgelist // location. if (count < q && new_x >= 0 && new_x < (q+1) && new_y >= 0 && new_y < (q+2) && (new_type == LEFT || new_type == RIGHT)) { if (new_type == LEFT) { indexLUT[new_x][0] = count; } else { indexLUT[new_x][1] = count; } edgelist[count].x = new_x; edgelist[count].y = new_y; edgelist[count].t = new_type; count++; return true; } else { return false; } } // ********************************************************************** bool interns::replaceSplit(const int i, const string sp) { // When an internal edge is changed, its split must be replaced as // well. This function provides that access; it stores the split // defined by an internal edge (x,y) at the location [y], which // is unique. if (i >= 0 && i <= q) { splitlist[i] = sp; return true; } return false; } // *********************************************************************** bool interns::swapEdges(const int one_x, const int one_y, const short int one_type, const int two_x, const int two_y, const short int two_type) { // The moves on the dendrogram always swap edges, either of which // (or both, or neither) can by internal edges. So, this function // mirrors that operation for the internal edgelist and indexLUT. int index, jndex, temp; bool one_isInternal = false; bool two_isInternal = false; if (one_x >= 0 && one_x < (q+1) && two_x >= 0 && two_x < (q+1) && (two_type == LEFT || two_type == RIGHT) && one_y >= 0 && one_y < (q+2) && two_y >= 0 && two_y < (q+2) && (one_type == LEFT || one_type == RIGHT)) { if (one_type == LEFT) { temp = 0; } else { temp = 1; } if (indexLUT[one_x][temp] > -1) { one_isInternal = true; } if (two_type == LEFT) { temp = 0; } else { temp = 1; } if (indexLUT[two_x][temp] > -1) { two_isInternal = true; } if (one_isInternal && two_isInternal) { if (one_type == LEFT) { index = indexLUT[one_x][0]; } else { index = indexLUT[one_x][1]; } if (two_type == LEFT) { jndex = indexLUT[two_x][0]; } else { jndex = indexLUT[two_x][1]; } temp = edgelist[index].y; edgelist[index].y = edgelist[jndex].y; edgelist[jndex].y = temp; } else if (one_isInternal) { if (one_type == LEFT) { index = indexLUT[one_x][0]; indexLUT[one_x][0] = -1; } else { index = indexLUT[one_x][1]; indexLUT[one_x][1] = -1; } edgelist[index].x = two_x; edgelist[index].t = two_type; if (two_type == LEFT) { indexLUT[two_x][0] = index; } else { indexLUT[two_x][1] = index; } // add new } else if (two_isInternal) { if (two_type == LEFT) { index = indexLUT[two_x][0]; indexLUT[two_x][0] = -1; } else { index = indexLUT[two_x][1]; indexLUT[two_x][1] = -1; } edgelist[index].x = one_x; edgelist[index].t = one_type; if (one_type == LEFT) { indexLUT[one_x][0] = index; } else { indexLUT[one_x][1] = index; } // add new } else { ; } // else neither is internal return true; } else { return false; } } // ******** Red-Black Tree Methods *************************************** splittree::splittree() { root = new elementsp; leaf = new elementsp; leaf->parent = root; root->left = leaf; root->right = leaf; support = 0; total_weight = 0.0; total_count = 0; } splittree::~splittree() { if (root != NULL && (root->left != leaf || root->right != leaf)) { deleteSubTree(root); root = NULL; } support = 0; total_weight = 0.0; total_count = 0; if (root) delete root; delete leaf; root = NULL; leaf = NULL; } void splittree::deleteTree() { if (root != NULL) { deleteSubTree(root); root = NULL; } return; } void splittree::deleteSubTree(elementsp *z) { if (z->left != leaf) { deleteSubTree(z->left); z->left = NULL; } if (z->right != leaf) { deleteSubTree(z->right); z->right = NULL; } delete z; /* No point in setting z to NULL here because z is passed by value */ /* z = NULL; */ return; } // ******** Reset Functions ********************************************* // O(n lg n) void splittree::clearTree() { string *array = returnArrayOfKeys(); for (int i=0; isplit.empty()) { return NULL; } // empty tree; bail out while (current != leaf) { if (searchKey.compare(current->split) < 0) { // left-or-right? // try moving down-left if (current->left != leaf) { current = current->left; } else { // failure; bail out return NULL; } } else { if (searchKey.compare(current->split) > 0) { // left-or-right? if (current->right != leaf) { // try moving down-left current = current->right; } else { // failure; bail out return NULL; } } else { // found (searchKey==current->split) return current; } } } return NULL; } double splittree::returnValue(const string searchKey) { elementsp* test = findItem(searchKey); if (test == NULL) { return 0.0; } else { return test->weight; } } // ******** Return Item Functions *************************************** // public function which returns the tree, via pre-order traversal, as // a linked list string* splittree::returnArrayOfKeys() { string* array; array = new string [support]; bool flag_go = true; int index = 0; elementsp *curr; if (support == 1) { array[0] = root->split; } else if (support == 2) { array[0] = root->split; if (root->left == leaf) { array[1] = root->right->split; } else { array[1] = root->left->split; } } else { for (int i=0; imark = 1; while (flag_go) { // - is it time, and is left child the leaf node? if (curr->mark == 1 && curr->left == leaf) { curr->mark = 2; } // - is it time, and is right child the leaf node? if (curr->mark == 2 && curr->right == leaf) { curr->mark = 3; } if (curr->mark == 1) { // - go left curr->mark = 2; curr = curr->left; curr->mark = 1; } else if (curr->mark == 2) { // - else go right curr->mark = 3; curr = curr->right; curr->mark = 1; } else { // - else go up a level curr->mark = 0; array[index++] = curr->split; curr = curr->parent; if (curr == NULL) { flag_go = false; } } } } return array; } slist* splittree::returnListOfKeys() { keyValuePairSplit *curr, *prev; slist *head = NULL, *tail = NULL, *newlist; curr = returnTreeAsList(); while (curr != NULL) { newlist = new slist; newlist->x = curr->x; if (head == NULL) { head = newlist; tail = head; } else { tail->next = newlist; tail = newlist; } prev = curr; curr = curr->next; delete prev; prev = NULL; } return head; } // pre-order traversal keyValuePairSplit* splittree::returnTreeAsList() { keyValuePairSplit *head, *tail; head = new keyValuePairSplit; head->x = root->split; head->y = root->weight; head->c = root->count; tail = head; if (root->left != leaf) { tail = returnSubtreeAsList(root->left, tail); } if (root->right != leaf) { tail = returnSubtreeAsList(root->right, tail); } if (head->x.empty()) { return NULL; /* empty tree */ } else { return head; } } keyValuePairSplit* splittree::returnSubtreeAsList(elementsp *z, keyValuePairSplit *head) { keyValuePairSplit *newnode, *tail; newnode = new keyValuePairSplit; newnode->x = z->split; newnode->y = z->weight; newnode->c = z->count; head->next = newnode; tail = newnode; if (z->left != leaf) { tail = returnSubtreeAsList(z->left, tail); } if (z->right != leaf) { tail = returnSubtreeAsList(z->right, tail); } return tail; } keyValuePairSplit splittree::returnMaxKey() { keyValuePairSplit themax; elementsp *current; current = root; // search to bottom-right corner of tree while (current->right != leaf) { current = current->right; } themax.x = current->split; themax.y = current->weight; return themax; } keyValuePairSplit splittree::returnMinKey() { keyValuePairSplit themin; elementsp *current; current = root; // search to bottom-left corner of tree while (current->left != leaf) { current = current->left; } themin.x = current->split; themin.y = current->weight; return themin; } // private functions for deleteItem() (although these could easily be // made public, I suppose) elementsp* splittree::returnMinKey(elementsp *z) { elementsp *current; current = z; // search to bottom-right corner of tree while (current->left != leaf) { current = current->left; } // return pointer to the minimum return current; } elementsp* splittree::returnSuccessor(elementsp *z) { elementsp *current, *w; w = z; // if right-subtree exists, return min of it if (w->right != leaf) { return returnMinKey(w->right); } // else search up in tree // move up in tree until find a non-right-child current = w->parent; while ((current!=NULL) && (w==current->right)) { w = current; current = current->parent; } return current; } int splittree::returnNodecount() { return support; } keyValuePairSplit* splittree::returnTheseSplits(const int target) { keyValuePairSplit *head, *curr, *prev, *newhead, *newtail, *newpair; int count, len; head = returnTreeAsList(); prev = newhead = newtail = newpair = NULL; curr = head; while (curr != NULL) { count = 0; len = curr->x.size(); for (int i=0; ix[i] == 'M') { count++; } } if (count == target && curr->x[1] != '*') { newpair = new keyValuePairSplit; newpair->x = curr->x; newpair->y = curr->y; newpair->next = NULL; if (newhead == NULL) { newhead = newpair; newtail = newpair; } else { newtail->next = newpair; newtail = newpair; } } prev = curr; curr = curr->next; delete prev; prev = NULL; } return newhead; } double splittree::returnTotal() { return total_weight; } // ******** Insert Functions ********************************************* void splittree::finishedThisRound() { // We need to also keep a running total of how much weight has been // added to the histogram. if (total_count == 0) { total_weight = 1.0; total_count = 1; } else { total_weight += 1.0; total_count++; } return; } // public insert function bool splittree::insertItem(string newKey, double newValue) { // first we check to see if newKey is already present in the tree; // if so, we do nothing; if not, we must find where to insert the // key elementsp *newNode, *current; // find newKey in tree; return pointer to it O(log k) current = findItem(newKey); if (current != NULL) { current->weight += 1.0; // And finally, we keep track of how many observations went into // the histogram current->count++; return true; } else { newNode = new elementsp; // elementsp for the splittree newNode->split = newKey; // store newKey newNode->weight = newValue; // store newValue newNode->color = true; // new nodes are always RED newNode->parent = NULL; // new node initially has no parent newNode->left = leaf; // left leaf newNode->right = leaf; // right leaf newNode->count = 1; support++; // increment node count in splittree // must now search for where to insert newNode, i.e., find the // correct parent and set the parent and child to point to each // other properly current = root; if (current->split.empty()) { // insert as root delete root; // delete old root root = newNode; // set root to newNode leaf->parent = newNode; // set leaf's parent current = leaf; // skip next loop } // search for insertion point while (current != leaf) { // left-or-right? if (newKey.compare(current->split) < 0) { // try moving down-left if (current->left != leaf) { current = current->left; } else { // else found new parent newNode->parent = current; // set parent current->left = newNode; // set child current = leaf; // exit search } } else { // if (current->right != leaf) { // try moving down-right current = current->right; } else { // else found new parent newNode->parent = current; // set parent current->right = newNode; // set child current = leaf; // exit search } } } // now do the house-keeping necessary to preserve the red-black // properties insertCleanup(newNode); } return true; } // private house-keeping function for insertion void splittree::insertCleanup(elementsp *z) { // fix now if z is root if (z->parent==NULL) { z->color = false; return; } elementsp *temp; // while z is not root and z's parent is RED while (z->parent!=NULL && z->parent->color) { if (z->parent == z->parent->parent->left) { // z's parent is LEFT-CHILD temp = z->parent->parent->right; // grab z's uncle if (temp->color) { z->parent->color = false; // color z's parent BLACK (Case 1) temp->color = false; // color z's uncle BLACK (Case 1) z->parent->parent->color = true; // color z's grandpa RED (Case 1) z = z->parent->parent; // set z = z's grandpa (Case 1) } else { if (z == z->parent->right) { // z is RIGHT-CHILD z = z->parent; // set z = z's parent (Case 2) rotateLeft(z); // perform left-rotation (Case 2) } z->parent->color = false; // color z's parent BLACK (Case 3) z->parent->parent->color = true; // color z's grandpa RED (Case 3) rotateRight(z->parent->parent); // perform right-rotation (Case 3) } } else { // z's parent is RIGHT-CHILD temp = z->parent->parent->left; // grab z's uncle if (temp->color) { z->parent->color = false; // color z's parent BLACK (Case 1) temp->color = false; // color z's uncle BLACK (Case 1) z->parent->parent->color = true; // color z's grandpa RED (Case 1) z = z->parent->parent; // set z = z's grandpa (Case 1) } else { if (z == z->parent->left) { // z is LEFT-CHILD z = z->parent; // set z = z's parent (Case 2) rotateRight(z); // perform right-rotation (Case 2) } z->parent->color = false; // color z's parent BLACK (Case 3) z->parent->parent->color = true; // color z's grandpa RED (Case 3) rotateLeft(z->parent->parent); // perform left-rotation (Case 3) } } } root->color = false; // color the root BLACK return; } // ******** Delete Functions ******************************************** // public delete function void splittree::deleteItem(string killKey) { elementsp *x, *y, *z; z = findItem(killKey); if (z == NULL) { return; } // item not present; bail out if (support==1) { // -- attempt to delete the root root->split = ""; // restore root node to default state root->weight = 0.0; // root->color = false; // root->parent = NULL; // root->left = leaf; // root->right = leaf; // support--; // set support to zero total_weight = 0.0; // set total weight to zero total_count--; // return; // exit - no more work to do } if (z != NULL) { support--; // decrement node count if ((z->left == leaf) || (z->right==leaf)) { // case of less than two children y = z; // set y to be z } else { y = returnSuccessor(z); // set y to be z's key-successor } if (y->left!=leaf) { x = y->left; // pick y's one child (left-child) } else { x = y->right; // (right-child) } x->parent = y->parent; // make y's child's parent be y's parent if (y->parent==NULL) { root = x; // if y is the root, x is now root } else { if (y == y->parent->left) {// decide y's relationship with y's parent y->parent->left = x; // replace x as y's parent's left child } else { y->parent->right = x; } // replace x as y's parent's left child } if (y!=z) { // insert y into z's spot z->split = y->split; // copy y data into z z->weight = y->weight; // z->count = y->count; // } // // do house-keeping to maintain balance if (y->color==false) { deleteCleanup(x); } delete y; // deallocate y y = NULL; // point y to NULL for safety } // return; } void splittree::deleteCleanup(elementsp *x) { elementsp *w, *t; // until x is the root, or x is RED while ((x != root) && (x->color==false)) { if (x==x->parent->left) { // branch on x being a LEFT-CHILD w = x->parent->right; // grab x's sibling if (w->color==true) { // if x's sibling is RED w->color = false; // color w BLACK (case 1) x->parent->color = true; // color x's parent RED (case 1) rotateLeft(x->parent); // left rotation on x's parent (case 1) w = x->parent->right; // make w be x's right sibling (case 1) } if ((w->left->color==false) && (w->right->color==false)) { w->color = true; // color w RED (case 2) x = x->parent; // examine x's parent (case 2) } else { // if (w->right->color==false) { w->left->color = false; // color w's left child BLACK (case 3) w->color = true; // color w RED (case 3) t = x->parent; // store x's parent rotateRight(w); // right rotation on w (case 3) x->parent = t; // restore x's parent w = x->parent->right; // make w be x's right sibling (case 3) } // w->color = x->parent->color; // w's color := x's parent's (case 4) x->parent->color = false; // color x's parent BLACK (case 4) w->right->color = false; // color w's right child BLACK (case 4) rotateLeft(x->parent); // left rotation on x's parent (case 4) x = root; // finished work. bail out (case 4) } // } else { // x is RIGHT-CHILD w = x->parent->left; // grab x's sibling if (w->color==true) { // if x's sibling is RED w->color = false; // color w BLACK (case 1) x->parent->color = true; // color x's parent RED (case 1) rotateRight(x->parent); // right rotation on x's parent (case 1) w = x->parent->left; // make w be x's left sibling (case 1) } if ((w->right->color==false) && (w->left->color==false)) { w->color = true; // color w RED (case 2) x= x->parent; // examine x's parent (case 2) } else { // if (w->left->color==false) { // w->right->color = false; // color w's right child BLACK (case 3) w->color = true; // color w RED (case 3) t = x->parent; // store x's parent rotateLeft(w); // left rotation on w (case 3) x->parent = t; // restore x's parent w = x->parent->left; // make w be x's left sibling (case 3) } // w->color = x->parent->color; // w's color := x's parent's (case 4) x->parent->color = false; // color x's parent BLACK (case 4) w->left->color = false; // color w's left child BLACK (case 4) rotateRight(x->parent); // right rotation on x's parent (case 4) x = root; // x is now the root (case 4) } } } x->color = false; // color x (the root) BLACK (exit) return; } // ******** Rotation Functions ******************************************* void splittree::rotateLeft(elementsp *x) { elementsp *y; // do pointer-swapping operations for left-rotation y = x->right; // grab right child x->right = y->left; // make x's RIGHT-CHILD be y's LEFT-CHILD y->left->parent = x; // make x be y's LEFT-CHILD's parent y->parent = x->parent; // make y's new parent be x's old parent if (x->parent==NULL) { root = y; // if x was root, make y root } else { // if (x == x->parent->left) { // if x is LEFT-CHILD, make y be x's parent's x->parent->left = y; // left-child } else { x->parent->right = y; // right-child } } y->left = x; // make x be y's LEFT-CHILD x->parent = y; // make y be x's parent return; } void splittree::rotateRight(elementsp *y) { elementsp *x; // do pointer-swapping operations for right-rotation x = y->left; // grab left child y->left = x->right; // replace left child yith x's right subtree x->right->parent = y; // replace y as x's right subtree's parent x->parent = y->parent; // make x's new parent be y's old parent if (y->parent==NULL) { root = x; // if y was root, make x root } else { if (y == y->parent->right) { // if y is R-CHILD, make x be y's parent's y->parent->right = x; // right-child } else { y->parent->left = x; // left-child } } x->right = y; // make y be x's RIGHT-CHILD y->parent = x; // make x be y's parent return; } // *********************************************************************** // *** COPYRIGHT NOTICE ************************************************** // graph_simp.h - graph data structure // Copyright (C) 2006-2008 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // *********************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | // http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark E.J. Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science // AND Santa Fe Institute // Created : 21 June 2006 // Modified : 23 December 2007 (cleaned up for public consumption) // // ************************************************************************ // ******** Constructor / Destructor ************************************* simpleGraph::simpleGraph(const int size): n(size), m(0), num_groups(0) { nodes = new simpleVert [n]; nodeLink = new simpleEdge* [n]; nodeLinkTail = new simpleEdge* [n]; A = new double* [n]; for (int i=0; inext; delete prev; } } curr = NULL; prev = NULL; if (E != NULL) { delete [] E; E = NULL; } delete [] A; A = NULL; delete [] nodeLink; nodeLink = NULL; delete [] nodeLinkTail; nodeLinkTail = NULL; delete [] nodes; nodes = NULL; } // *********************************************************************** bool simpleGraph::addGroup(const int i, const int group_index) { if (i >= 0 && i < n) { nodes[i].group_true = group_index; return true; } else { return false; } } // *********************************************************************** bool simpleGraph::addLink(const int i, const int j) { // Adds the directed edge (i,j) to the adjacency list for v_i simpleEdge* newedge; if (i >= 0 && i < n && j >= 0 && j < n) { A[i][j] = 1.0; newedge = new simpleEdge; newedge->x = j; if (nodeLink[i] == NULL) { // first neighbor nodeLink[i] = newedge; nodeLinkTail[i] = newedge; nodes[i].degree = 1; } else { // subsequent neighbor nodeLinkTail[i]->next = newedge; nodeLinkTail[i] = newedge; nodes[i].degree++; } m++; // increment edge count newedge = NULL; return true; } else { return false; } } // *********************************************************************** bool simpleGraph::doesLinkExist(const int i, const int j) { // This function determines if the edge (i,j) already exists in the // adjacency list of v_i if (i >= 0 && i < n && j >= 0 && j < n) { if (A[i][j] > 0.1) { return true; } else { return false; } } else { return false; } return false; } // ********************************************************************** double simpleGraph::getAdjacency(const int i, const int j) { if (i >= 0 && i < n && j >= 0 && j < n) { return A[i][j]; } else { return -1.0; } } int simpleGraph::getDegree(const int i) { if (i >= 0 && i < n) { return nodes[i].degree; } else { return -1; } } int simpleGraph::getGroupLabel(const int i) { if (i >= 0 && i < n) { return nodes[i].group_true; } else { return -1; } } string simpleGraph::getName(const int i) { if (i >= 0 && i < n) { return nodes[i].name; } else { return ""; } } // NOTE: The following three functions return addresses; deallocation // of returned object is dangerous simpleEdge* simpleGraph::getNeighborList(const int i) { if (i >= 0 && i < n) { return nodeLink[i]; } else { return NULL; } } // END-NOTE // ********************************************************************* int simpleGraph::getNumGroups() { return num_groups; } int simpleGraph::getNumLinks() { return m; } int simpleGraph::getNumNodes() { return n; } simpleVert* simpleGraph::getNode(const int i) { if (i >= 0 && i= 0 && i < n) { nodes[i].name = text; return true; } else { return false; } } // ********************************************************************** void simpleGraph::QsortMain (block* array, int left, int right) { if (right > left) { int pivot = left; int part = QsortPartition(array, left, right, pivot); QsortMain(array, left, part-1); QsortMain(array, part+1, right ); } return; } int simpleGraph::QsortPartition (block* array, int left, int right, int index) { block p_value, temp; p_value.x = array[index].x; p_value.y = array[index].y; // swap(array[p_value], array[right]) temp.x = array[right].x; temp.y = array[right].y; array[right].x = array[index].x; array[right].y = array[index].y; array[index].x = temp.x; array[index].y = temp.y; int stored = left; for (int i=left; icp || !S->parent) return (NULL) ; n = A->n ; N = cs_calloc (1, sizeof (csn)) ; /* allocate result */ c = cs_malloc (2*n, sizeof (CS_INT)) ; /* get CS_INT workspace */ x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get CS_ENTRY workspace */ cp = S->cp ; pinv = S->pinv ; parent = S->parent ; C = pinv ? cs_symperm (A, pinv, 1) : ((cs *) A) ; E = pinv ? C : NULL ; /* E is alias for A, or a copy E=A(p,p) */ if (!N || !c || !x || !C) return (cs_ndone (N, E, c, x, 0)) ; s = c + n ; Cp = C->p ; Ci = C->i ; Cx = C->x ; N->L = L = cs_spalloc (n, n, cp [n], 1, 0) ; /* allocate result */ if (!L) return (cs_ndone (N, E, c, x, 0)) ; Lp = L->p ; Li = L->i ; Lx = L->x ; for (k = 0 ; k < n ; k++) Lp [k] = c [k] = cp [k] ; for (k = 0 ; k < n ; k++) /* compute L(k,:) for L*L' = C */ { /* --- Nonzero pattern of L(k,:) ------------------------------------ */ top = cs_ereach (C, k, parent, s, c) ; /* find pattern of L(k,:) */ x [k] = 0 ; /* x (0:k) is now zero */ for (p = Cp [k] ; p < Cp [k+1] ; p++) /* x = full(triu(C(:,k))) */ { if (Ci [p] <= k) x [Ci [p]] = Cx [p] ; } d = x [k] ; /* d = C(k,k) */ x [k] = 0 ; /* clear x for k+1st iteration */ /* --- Triangular solve --------------------------------------------- */ for ( ; top < n ; top++) /* solve L(0:k-1,0:k-1) * x = C(:,k) */ { i = s [top] ; /* s [top..n-1] is pattern of L(k,:) */ lki = x [i] / Lx [Lp [i]] ; /* L(k,i) = x (i) / L(i,i) */ x [i] = 0 ; /* clear x for k+1st iteration */ for (p = Lp [i] + 1 ; p < c [i] ; p++) { x [Li [p]] -= Lx [p] * lki ; } d -= lki * CS_CONJ (lki) ; /* d = d - L(k,i)*L(k,i) */ p = c [i]++ ; Li [p] = k ; /* store L(k,i) in column i */ Lx [p] = CS_CONJ (lki) ; } /* --- Compute L(k,k) ----------------------------------------------- */ if (CS_REAL (d) <= 0 || CS_IMAG (d) != 0) return (cs_ndone (N, E, c, x, 0)) ; /* not pos def */ p = c [k]++ ; Li [p] = k ; /* store L(k,k) = sqrt (d) in column k */ Lx [p] = sqrt (d) ; } Lp [n] = cp [n] ; /* finalize L */ return (cs_ndone (N, E, c, x, 1)) ; /* success: free E,s,x; return N */ } igraph/src/igraph_iterators.h0000644000176000001440000002772412325527073016104 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_ITERATORS_H #define IGRAPH_ITERATORS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Vertex selectors */ /* -------------------------------------------------- */ #define IGRAPH_VS_ALL 0 #define IGRAPH_VS_ADJ 1 #define IGRAPH_VS_NONE 2 #define IGRAPH_VS_1 3 #define IGRAPH_VS_VECTORPTR 4 #define IGRAPH_VS_VECTOR 5 #define IGRAPH_VS_SEQ 6 #define IGRAPH_VS_NONADJ 7 typedef struct igraph_vs_t { int type; union { igraph_integer_t vid; /* single vertex */ const igraph_vector_t *vecptr; /* vector of vertices */ struct { igraph_integer_t vid; igraph_neimode_t mode; } adj; /* adjacent vertices */ struct { igraph_integer_t from; igraph_integer_t to; } seq; /* sequence of vertices from:to */ } data; } igraph_vs_t; int igraph_vs_all(igraph_vs_t *vs); igraph_vs_t igraph_vss_all(void); int igraph_vs_adj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode); igraph_vs_t igraph_vss_adj(igraph_integer_t vid, igraph_neimode_t mode); int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode); int igraph_vs_none(igraph_vs_t *vs); igraph_vs_t igraph_vss_none(void); int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); igraph_vs_t igraph_vss_1(igraph_integer_t vid); int igraph_vs_vector(igraph_vs_t *vs, const igraph_vector_t *v); igraph_vs_t igraph_vss_vector(const igraph_vector_t *v); int igraph_vs_vector_small(igraph_vs_t *vs, ...); int igraph_vs_vector_copy(igraph_vs_t *vs, const igraph_vector_t *v); int igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); void igraph_vs_destroy(igraph_vs_t *vs); igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs); int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, igraph_vector_t *v); int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, igraph_integer_t *result); int igraph_vs_type(const igraph_vs_t *vs); /* -------------------------------------------------- */ /* Vertex iterators */ /* -------------------------------------------------- */ #define IGRAPH_VIT_SEQ 0 #define IGRAPH_VIT_VECTOR 1 #define IGRAPH_VIT_VECTORPTR 2 typedef struct igraph_vit_t { int type; long int pos; long int start; long int end; const igraph_vector_t *vec; } igraph_vit_t; /** * \section IGRAPH_VIT Stepping over the vertices * * After creating an iterator with \ref igraph_vit_create(), it * points to the first vertex in the vertex determined by the vertex * selector (if there is any). The \ref IGRAPH_VIT_NEXT() macro steps * to the next vertex, \ref IGRAPH_VIT_END() checks whether there are * more vertices to visit, \ref IGRAPH_VIT_SIZE() gives the total size * of the vertices visited so far and to be visited. \ref * IGRAPH_VIT_RESET() resets the iterator, it will point to the first * vertex again. Finally \ref IGRAPH_VIT_GET() gives the current vertex * pointed to by the iterator (call this only if \ref IGRAPH_VIT_END() * is false). * * * Here is an example on how to step over the neighbors of vertex 0: * * igraph_vs_t vs; * igraph_vit_t vit; * ... * igraph_vs_adj(&vs, 0, IGRAPH_ALL); * igraph_vit_create(&graph, vs, &vit); * while (!IGRAPH_VIT_END(vit)) { * printf(" %li", (long int) IGRAPH_VIT_GET(vit)); * IGRAPH_VIT_NEXT(vit); * } * printf("\n"); * ... * igraph_vit_destroy(&vit); * igraph_vs_destroy(&vs); * * */ /** * \define IGRAPH_VIT_NEXT * \brief Next vertex. * * Steps the iterator to the next vertex. Only call this function if * \ref IGRAPH_VIT_END() returns false. * \param vit The vertex iterator to step. * * Time complexity: O(1). */ #define IGRAPH_VIT_NEXT(vit) (++((vit).pos)) /** * \define IGRAPH_VIT_END * \brief Are we at the end? * * Checks whether there are more vertices to step to. * \param vit The vertex iterator to check. * \return Logical value, if true there are no more vertices to step * to. * * Time complexity: O(1). */ #define IGRAPH_VIT_END(vit) ((vit).pos >= (vit).end) /** * \define IGRAPH_VIT_SIZE * \brief Size of a vertex iterator. * * Gives the number of vertices in a vertex iterator. * \param vit The vertex iterator. * \return The number of vertices. * * Time complexity: O(1). */ #define IGRAPH_VIT_SIZE(vit) ((vit).end - (vit).start) /** * \define IGRAPH_VIT_RESET * \brief Reset a vertex iterator. * * Resets a vertex iterator. After calling this macro the iterator * will point to the first vertex. * \param vit The vertex iterator. * * Time complexity: O(1). */ #define IGRAPH_VIT_RESET(vit) ((vit).pos = (vit).start) /** * \define IGRAPH_VIT_GET * \brief Query the current position. * * Gives the vertex id of the current vertex pointed to by the * iterator. * \param vit The vertex iterator. * \return The vertex id of the current vertex. * * Time complexity: O(1). */ #define IGRAPH_VIT_GET(vit) \ ((igraph_integer_t)(((vit).type == IGRAPH_VIT_SEQ) ? (vit).pos : \ VECTOR(*(vit).vec)[(vit).pos])) int igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_vit_t *vit); void igraph_vit_destroy(const igraph_vit_t *vit); int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v); /* -------------------------------------------------- */ /* Edge Selectors */ /* -------------------------------------------------- */ #define IGRAPH_ES_ALL 0 #define IGRAPH_ES_ALLFROM 1 #define IGRAPH_ES_ALLTO 2 #define IGRAPH_ES_INCIDENT 3 #define IGRAPH_ES_NONE 4 #define IGRAPH_ES_1 5 #define IGRAPH_ES_VECTORPTR 6 #define IGRAPH_ES_VECTOR 7 #define IGRAPH_ES_SEQ 8 #define IGRAPH_ES_PAIRS 9 #define IGRAPH_ES_PATH 10 #define IGRAPH_ES_MULTIPAIRS 11 typedef struct igraph_es_t { int type; union { igraph_integer_t vid; igraph_integer_t eid; const igraph_vector_t *vecptr; struct { igraph_integer_t vid; igraph_neimode_t mode; } incident; struct { igraph_integer_t from; igraph_integer_t to; } seq; struct { const igraph_vector_t *ptr; igraph_bool_t mode; } path; } data; } igraph_es_t; int igraph_es_all(igraph_es_t *es, igraph_edgeorder_type_t order); igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); int igraph_es_adj(igraph_es_t *es, igraph_integer_t vid, igraph_neimode_t mode); /* deprecated */ int igraph_es_incident(igraph_es_t *es, igraph_integer_t vid, igraph_neimode_t mode); int igraph_es_none(igraph_es_t *es); igraph_es_t igraph_ess_none(void); int igraph_es_1(igraph_es_t *es, igraph_integer_t eid); igraph_es_t igraph_ess_1(igraph_integer_t eid); int igraph_es_vector(igraph_es_t *es, const igraph_vector_t *v); igraph_es_t igraph_ess_vector(const igraph_vector_t *v); int igraph_es_fromto(igraph_es_t *es, igraph_vs_t from, igraph_vs_t to); int igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v); int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed); int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...); int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed); int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed); int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...); void igraph_es_destroy(igraph_es_t *es); igraph_bool_t igraph_es_is_all(const igraph_es_t *es); int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, igraph_vector_t *v); int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result); int igraph_es_type(const igraph_es_t *es); /* -------------------------------------------------- */ /* Edge Iterators */ /* -------------------------------------------------- */ #define IGRAPH_EIT_SEQ 0 #define IGRAPH_EIT_VECTOR 1 #define IGRAPH_EIT_VECTORPTR 2 typedef struct igraph_eit_t { int type; long int pos; long int start; long int end; const igraph_vector_t *vec; } igraph_eit_t; /** * \section IGRAPH_EIT Stepping over the edges * * Just like for vertex iterators, macros are provided for * stepping over a sequence of edges: \ref IGRAPH_EIT_NEXT() goes to * the next edge, \ref IGRAPH_EIT_END() checks whether there are more * edges to visit, \ref IGRAPH_EIT_SIZE() gives the number of edges in * the edge sequence, \ref IGRAPH_EIT_RESET() resets the iterator to * the first edge and \ref IGRAPH_EIT_GET() returns the id of the * current edge. */ /** * \define IGRAPH_EIT_NEXT * \brief Next edge. * * Steps the iterator to the next edge. Call this function only if * \ref IGRAPH_EIT_END() returns false. * \param eit The edge iterator to step. * * Time complexity: O(1). */ #define IGRAPH_EIT_NEXT(eit) (++((eit).pos)) /** * \define IGRAPH_EIT_END * \brief Are we at the end? * * Checks whether there are more edges to step to. * \param wit The edge iterator to check. * \return Logical value, if true there are no more edges * to step to. * * Time complexity: O(1). */ #define IGRAPH_EIT_END(eit) ((eit).pos >= (eit).end) /** * \define IGRAPH_EIT_SIZE * \brief Number of edges in the iterator. * * Gives the number of edges in an edge iterator. * \param eit The edge iterator. * \return The number of edges. * * Time complexity: O(1). */ #define IGRAPH_EIT_SIZE(eit) ((eit).end - (eit).start) /** * \define IGRAPH_EIT_RESET * \brief Reset an edge iterator. * * Resets an edge iterator. After calling this macro the iterator will * point to the first edge. * \param eit The edge iterator. * * Time complexity: O(1). */ #define IGRAPH_EIT_RESET(eit) ((eit).pos = (eit).start) /** * \define IGRAPH_EIT_GET * \brief Query an edge iterator. * * Gives the edge id of the current edge pointed to by an iterator. * \param eit The edge iterator. * \return The id of the current edge. * * Time complexity: O(1). */ #define IGRAPH_EIT_GET(eit) \ (igraph_integer_t)((((eit).type == IGRAPH_EIT_SEQ) ? (eit).pos : \ VECTOR(*(eit).vec)[(eit).pos])) int igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit); void igraph_eit_destroy(const igraph_eit_t *eit); int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v); __END_DECLS #endif igraph/src/drl_Node_3d.h0000644000176000001440000000441712325527073014644 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __NODE_H__ #define __NODE_H__ // The node class contains information about a given node for // use by the density server process. // structure coord used to pass position information between // density server and graph class namespace drl3d { class Node { public: bool fixed; // if true do not change the // position of this node int id; float x,y,z; float sub_x,sub_y,sub_z; float energy; public: Node( int node_id ) { x = y = z = 0.0; fixed = false; id = node_id; } ~Node() { } }; } // namespace drl3d #endif //__NODE_H__ igraph/src/revolver_cit.c0000644000176000001440000055331112325527074015231 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_revolver.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_interface.h" #include "igraph_iterators.h" #include "igraph_structural.h" #include "config.h" #include /***********************************************/ /* in-degree */ /***********************************************/ int igraph_revolver_d(const igraph_t *graph, igraph_integer_t niter, igraph_vector_t *kernel, igraph_vector_t *sd, igraph_vector_t *norm, igraph_vector_t *cites, igraph_vector_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, igraph_real_t *logmax, const igraph_vector_t *debug, igraph_vector_ptr_t *debugres) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t st; long int i; igraph_integer_t maxdegree; IGRAPH_VECTOR_INIT_FINALLY(&st, no_of_nodes); for (i=0; i1) { MATRIX(ntkl, 0, 0)=1; } else { MATRIX(ntkl, 0, 1)=1; } if (logmax) { *logmax=0.0; } for (node=0; node=0; k++) { long int shnode=node+1-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; MATRIX(ntkl, deg, k-1)--; if (MATRIX(ntkl, deg, k-1)==0) { MATRIX(*normfact, deg, k-1) += (edges-MATRIX(ch, deg, k-1)); } MATRIX(ntkl, deg, k) += 1; if (MATRIX(ntkl, deg, k)==1) { MATRIX(ch, deg, k)=edges; } } } /* Make normfact up to date, calculate mean, sd */ for (i=0; i1) { VECTOR(*st)[0]=MATRIX(*kernel, 0, 0); } else { VECTOR(*st)[0]=MATRIX(*kernel, 0, 1); } for (node=1; node= 0; k++) { long int shnode=node-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; VECTOR(*st)[node] += -MATRIX(*kernel, deg, k-1)+MATRIX(*kernel, deg, k); } } igraph_vector_destroy(&neis); igraph_vector_destroy(&indegree); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_revolver_exp_ad(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pmaxind, igraph_integer_t pagebins) { long int maxind=pmaxind, agebins=pagebins; long int no_of_nodes=igraph_vcount(graph); long int binwidth=no_of_nodes/agebins+1; igraph_vector_t indegree; igraph_vector_t outdegree; igraph_vector_t cumst; igraph_matrix_t ntkl; igraph_matrix_t ch; igraph_vector_t neis; long int node, i, j, k; IGRAPH_MATRIX_INIT_FINALLY(&ntkl, maxind+1, agebins); IGRAPH_MATRIX_INIT_FINALLY(&ch, maxind+1, agebins); IGRAPH_VECTOR_INIT_FINALLY(&cumst, no_of_nodes+1); IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&outdegree, no_of_nodes); IGRAPH_CHECK(igraph_degree(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); /* create cumulative sum of dt/S(t) */ VECTOR(cumst)[0]=0; for (i=0; i=0; k++) { long int shnode=node-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; MATRIX(ntkl, deg, k-1) -= 1; MATRIX(*expected, deg, k-1) += (MATRIX(ntkl, deg, k-1)+1) * (VECTOR(cumst)[node]-VECTOR(cumst)[(long int)MATRIX(ch, deg, k-1)]); MATRIX(ch, deg, k-1)=node; MATRIX(ntkl, deg, k) += 1; MATRIX(*expected, deg, k) += (MATRIX(ntkl, deg, k)-1) * (VECTOR(cumst)[node]-VECTOR(cumst)[(long int)MATRIX(ch, deg, k)]); MATRIX(ch, deg, k)=node; } } /* complete res */ for (i=0; i1) { ARRAY3(ntkl, (long int)VECTOR(*cats)[0], 0, 0)=1; } else { ARRAY3(ntkl, (long int)VECTOR(*cats)[0], 0, 1)=1; } if (logmax) { *logmax=0.0; } for (node=0; node=0; k++) { long int shnode=node+1-binwidth*k+1; long int cidx=(long int) VECTOR(*cats)[shnode]; long int deg=(long int) VECTOR(indegree)[shnode]; ARRAY3(ntkl, cidx, deg, k-1) -= 1; if (ARRAY3(ntkl, cidx, deg, k-1)==0) { ARRAY3(*normfact, cidx, deg, k-1) += (edges-ARRAY3(ch, cidx, deg, k-1)); } ARRAY3(ntkl, cidx, deg, k) += 1; if (ARRAY3(ntkl, cidx, deg, k)==1) { ARRAY3(ch, cidx, deg, k)=edges; } } } /* Make normfact up to date, calculate mean, sd */ for (k=0; k 1 ? 0 : 1); for (node=1; node= 0; k++) { long int shnode=node-binwidth*k+1; long int cidx=(long int) VECTOR(*cats)[shnode]; long int deg=(long int) VECTOR(indegree)[shnode]; VECTOR(*st)[node] += -ARRAY3(*kernel, cidx, deg, k-1) + ARRAY3(*kernel, cidx, deg, k); } } igraph_vector_destroy(&neis); igraph_vector_destroy(&indegree); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_revolver_exp_ade(const igraph_t *graph, igraph_array3_t *expected, const igraph_array3_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxind, igraph_integer_t pagebins) { /* TODO */ return 0; } int igraph_revolver_error_ade(const igraph_t *graph, const igraph_array3_t *kernel, const igraph_vector_t *st, const igraph_vector_t *cats, igraph_integer_t pnocats, igraph_integer_t pmaxdegree, igraph_integer_t pagebins, igraph_real_t *logprob, igraph_real_t *lognull) { long int agebins=pagebins; long int no_of_nodes=igraph_vcount(graph); long int binwidth=no_of_nodes/agebins+1; igraph_vector_t indegree; igraph_vector_t neis; long int node, i; igraph_real_t rlogprob, rlognull, *mylogprob=logprob, *mylognull=lognull; IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (!logprob) { mylogprob=&rlogprob; } if (!lognull) { mylognull=&rlognull; } *mylogprob=0; *mylognull=0; for (node=0; node=0; k++) { long int shnode=node+1-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i= 0; k++) { long int shnode=node-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i=0; k++) { long int shnode=node+1-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i= 0; k++) { long int shnode=node-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i=0; k++) { long int shnode=node+1-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i= 0; k++) { long int shnode=node-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t)(i-window), IGRAPH_OUT)); for (j=0; j maxdegree) { maxdegree=(igraph_integer_t) VECTOR(st)[to]; } } } igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); for (i=0; i= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node+1-window), IGRAPH_OUT)); for (i=0; i=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window), IGRAPH_OUT)); for (i=0; i= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window+1), IGRAPH_OUT)); for (i=0; i=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (i-window), IGRAPH_OUT)); for (j=0; j maxdegree) { maxdegree=(igraph_integer_t) VECTOR(st)[to]; } } } igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); for (i=0; i1 ? 0 : 1, 0)=1; if (logmax) { *logmax=0.0; } for (node=0; node= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node+1-window), IGRAPH_OUT)); for (i=0; i=0; k++) { long int shnode=node+1-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; MATRIX(ntk, k-1, deg)--; if (MATRIX(ntk, k-1, deg)==0) { MATRIX(*normfact, k-1, deg) += (edges-MATRIX(ch, k-1, deg)); } MATRIX(ntk, k, deg) += 1; if (MATRIX(ntk, k, deg)==1) { MATRIX(ch, k, deg)=edges; } } } /* Make normfact up to date, calculate mean, sd */ for (i=0; i1 ? 0 : 1, 0); for (node=1; node=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window), IGRAPH_OUT)); for (i=0; i= 0; k++) { long int shnode=node-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; VECTOR(*st)[node] += -MATRIX(*kernel, k-1, deg)+MATRIX(*kernel, k, deg); } } igraph_vector_destroy(&neis); igraph_vector_destroy(&indegree); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_revolver_exp_ar(const igraph_t *graph, igraph_matrix_t *expected, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t agebins, igraph_integer_t window, igraph_integer_t pmaxind) { /* TODO */ return 0; } int igraph_revolver_error_ar(const igraph_t *graph, const igraph_matrix_t *kernel, const igraph_vector_t *st, igraph_integer_t pagebins, igraph_integer_t pwindow, igraph_integer_t maxind, igraph_real_t *logprob, igraph_real_t *lognull) { long int no_of_nodes=igraph_vcount(graph); long int agebins=pagebins; long int window=pwindow; long int binwidth=no_of_nodes/agebins+1; igraph_vector_t indegree; igraph_vector_t neis; long int node; long int i; igraph_real_t rlogprob, rlognull, *mylogprob=logprob, *mylognull=lognull; IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (!mylogprob) { mylogprob=&rlogprob; } if (!mylognull) { mylognull=&rlognull; } *mylogprob=0; *mylognull=0; for (node=0; node= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window+1), IGRAPH_OUT)); for (i=0; i1 ? 0 : 1)=1; if (logmax) { *logmax=0.0; } for (node=0; node=0; k++) { long int shnode=node+1-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; MATRIX(ntkl, deg, k-1) -= 1; if (MATRIX(ntkl, deg, k-1)==0) { for (j=0; j1 ? 0 : 1); } VECTOR(*st)[0]=MATRIX(allst, (long int) VECTOR(*cats)[0], 0); for (node=1; node= 0; k++) { long int shnode=node-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; for (j=0; j=0; k++) { long int shnode=node+1-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i= 0; k++) { long int shnode=node-binwidth*k+1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) shnode, IGRAPH_OUT)); for (i=0; i=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (i-window), IGRAPH_OUT)); for (j=0; j maxdegree) { maxdegree=(igraph_integer_t) VECTOR(st)[to]; } } } igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); IGRAPH_PROGRESS("Revolver di", 0, NULL); for (i=0; i= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node+1-window), IGRAPH_OUT)); for (i=0; i=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window), IGRAPH_OUT)); for (i=0; i= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window+1), IGRAPH_OUT)); for (i=0; i=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (i-window), IGRAPH_OUT)); for (j=0; j maxdegree) { maxdegree=(igraph_integer_t) VECTOR(st)[to]; } } } igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); for (i=0; i1 ? 0 : 1)=1; if (logmax) { *logmax=0.0; } for (node=0; node= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node+1-window), IGRAPH_OUT)); for (i=0; i=0; k++) { long int shnode=node+1-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; MATRIX(ntkl, deg, k-1) -= 1; if (MATRIX(ntkl, deg, k-1)==0) { for (j=0; j1 ? 0 : 1); } VECTOR(*st)[0]=MATRIX(allst, (long int) VECTOR(*cats)[0], 0); for (node=1; node=0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window), IGRAPH_OUT)); for (i=0; i= 0; k++) { long int shnode=node-binwidth*k+1; long int deg=(long int) VECTOR(indegree)[shnode]; for (j=0; j= 0) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) (node-window+1), IGRAPH_OUT)); for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_ERROR_H #define IGRAPH_ERROR_H #include #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* This file contains the igraph error handling. * Most bits are taken literally from the GSL library (with the GSL_ * prefix renamed to IGRAPH_), as I couldn't find a better way to do * them. */ /** * \section errorhandlingbasics Error handling basics * * \a igraph functions can run into various problems preventing them * from normal operation. The user might have supplied invalid arguments, * e.g. a non-square matrix when a square-matrix was expected, or the program * has run out of memory while some more memory allocation is required, etc. * * * By default \a igraph aborts the program when it runs into an * error. While this behavior might be good enough for smaller programs, * it is without doubt avoidable in larger projects. Please read further * if your project requires more sophisticated error handling. You can * safely skip the rest of this chapter otherwise. * */ /** * \section errorhandlers Error handlers * * * If \a igraph runs into an error - an invalid argument was supplied * to a function, or we've ran out of memory - the control is * transferred to the \emb error handler \eme function. * * The default error handler is \ref igraph_error_handler_abort which * prints an error message and aborts the program. * * * The \ref igraph_set_error_handler() function can be used to set a new * error handler function of type \ref igraph_error_handler_t; see the * documentation of this type for details. * * * There are two other predefined error handler functions, * \ref igraph_error_handler_ignore and \ref igraph_error_handler_printignore. * These deallocate the temporarily allocated memory (more about this * later) and return with the error code. The latter also prints an * error message. If you use these error handlers you need to take * care about possible errors yourself by checking the return value of * (almost) every non-void \a igraph function. * * Independently of the error handler installed, all functions in the * library do their best to leave their arguments * \em semantically unchanged if an error * happens. By semantically we mean that the implementation of an * object supplied as an argument might change, but its * \quote meaning \endquote in most cases does not. The rare occasions * when this rule is violated are documented in this manual. * */ /** * \section errorcodes Error codes * * Every \a igraph function which can fail return a * single integer error code. Some functions are very simple and * cannot run into any error, these may return other types, or * \type void as well. The error codes are defined by the * \ref igraph_error_type_t enumeration. * */ /** * \section writing_error_handlers Writing error handlers * * * The contents of the rest of this chapter might be useful only * for those who want to create an interface to \a igraph from another * language. Most readers can safely skip to the next chapter. * * * * You can write and install error handlers simply by defining a * function of type \ref igraph_error_handler_t and calling * \ref igraph_set_error_handler(). This feature is useful for interface * writers, as \a igraph will have the chance to * signal errors the appropriate way, eg. the R interface defines an * error handler which calls the error() * function, as required by R, while the Python interface has an error * handler which raises an exception according to the Python way. * * * If you want to write an error handler, your error handler should * call \ref IGRAPH_FINALLY_FREE() to deallocate all temporary memory to * prevent memory leaks. * */ /** * \section error_handling_internals Error handling internals * * * If an error happens, the functions in the library call the * \ref IGRAPH_ERROR macro with a textual description of the error and an * \a igraph error code. This macro calls (through the \ref * igraph_error() function) the installed error handler. Another useful * macro is \ref IGRAPH_CHECK(). This checks the return value of its * argument, which is normally a function call, and calls \ref * IGRAPH_ERROR if it is not \c IGRAPH_SUCCESS. * */ /** * \section deallocating_memory Deallocating memory * * * If a function runs into an error (and the program is not aborted) * the error handler should deallocate all temporary memory. This is * done by storing the address and the destroy function of all temporary * objects in a stack. The \ref IGRAPH_FINALLY function declares an object as * temporary by placing its address in the stack. If an \a igraph function returns * with success it calls \ref IGRAPH_FINALLY_CLEAN() with the * number of objects to remove from the stack. If an error happens * however, the error handler should call \ref IGRAPH_FINALLY_FREE() to * deallocate each object added to the stack. This means that the * temporary objects allocated in the calling function (and etc.) will * be freed as well. * */ /** * \section writing_functions_error_handling Writing \a igraph functions with * proper error handling * * * There are some simple rules to keep in order to have functions * behaving well in erroneous situations. First, check the arguments * of the functions and call \ref IGRAPH_ERROR if they are invalid. Second, * call \ref IGRAPH_FINALLY on each dynamically allocated object and call * \ref IGRAPH_FINALLY_CLEAN() with the proper argument before returning. Third, use * \ref IGRAPH_CHECK on all \a igraph function calls which can generate errors. * * * The size of the stack used for this bookkeeping is fixed, and * small. If you want to allocate several objects, write a destroy * function which can deallocate all of these. See the * adjlist.c file in the * \a igraph source for an example. * * * For some functions these mechanisms are simply not flexible * enough. These functions should define their own error handlers and * restore the error handler before they return. * */ /** * \section error_handling_threads Error handling and threads * * * It is likely that the \a igraph error handling * method is \em not thread-safe, mainly because of * the static global stack which is used to store the address of the * temporarily allocated objects. This issue might be addressed in a * later version of \a igraph. * */ /** * \typedef igraph_error_handler_t * \brief Type of error handler functions. * * This is the type of the error handler functions. * \param reason Textual description of the error. * \param file The source file in which the error is noticed. * \param line The number of the line in the source file which triggered * the error * \param igraph_errno The \a igraph error code. */ typedef void igraph_error_handler_t (const char * reason, const char * file, int line, int igraph_errno); /** * \var igraph_error_handler_abort * \brief Abort program in case of error. * * The default error handler, prints an error message and aborts the * program. */ extern igraph_error_handler_t igraph_error_handler_abort; /** * \var igraph_error_handler_ignore * \brief Ignore errors. * * This error handler frees the temporarily allocated memory and returns * with the error code. */ extern igraph_error_handler_t igraph_error_handler_ignore; /** * \var igraph_error_handler_printignore * \brief Print and ignore errors. * * Frees temporarily allocated memory, prints an error message to the * standard error and returns with the error code. */ extern igraph_error_handler_t igraph_error_handler_printignore; /** * \function igraph_set_error_handler * \brief Set a new error handler. * * Installs a new error handler. If called with 0, it installs the * default error handler (which is currently * \ref igraph_error_handler_abort). * \param new_handler The error handler function to install. * \return The old error handler function. This should be saved and * restored if \p new_handler is not needed any * more. */ igraph_error_handler_t* igraph_set_error_handler(igraph_error_handler_t* new_handler); /** * \typedef igraph_error_type_t * \brief Error code type. * These are the possible values returned by \a igraph functions. * Note that these are interesting only if you defined an error handler * with \ref igraph_set_error_handler(). Otherwise the program is aborted * and the function causing the error never returns. * * \enumval IGRAPH_SUCCESS The function successfully completed its task. * \enumval IGRAPH_FAILURE Something went wrong. You'll almost never * meet this error as normally more specific error codes are used. * \enumval IGRAPH_ENOMEM There wasn't enough memory to allocate * on the heap. * \enumval IGRAPH_PARSEERROR A parse error was found in a file. * \enumval IGRAPH_EINVAL A parameter's value is invalid. Eg. negative * number was specified as the number of vertices. * \enumval IGRAPH_EXISTS A graph/vertex/edge attribute is already * installed with the given name. * \enumval IGRAPH_EINVEVECTOR Invalid vector of vertex ids. A vertex id * is either negative or bigger than the number of vertices minus one. * \enumval IGRAPH_EINVVID Invalid vertex id, negative or too big. * \enumval IGRAPH_NONSQUARE A non-square matrix was received while a * square matrix was expected. * \enumval IGRAPH_EINVMODE Invalid mode parameter. * \enumval IGRAPH_EFILE A file operation failed. Eg. a file doesn't exist, * or the user has no rights to open it. * \enumval IGRAPH_UNIMPLEMENTED Attempted to call an unimplemented or * disabled (at compile-time) function. * \enumval IGRAPH_DIVERGED A numeric algorithm failed to converge. * \enumval IGRAPH_ARPACK_PROD Matrix-vector product failed. * \enumval IGRAPH_ARPACK_NPOS N must be positive. * \enumval IGRAPH_ARPACK_NEVNPOS NEV must be positive. * \enumval IGRAPH_ARPACK_NCVSMALL NCV must be bigger. * \enumval IGRAPH_ARPACK_NONPOSI Maximum number of iterations should be positive. * \enumval IGRAPH_ARPACK_WHICHINV Invalid WHICH parameter. * \enumval IGRAPH_ARPACK_BMATINV Invalid BMAT parameter. * \enumval IGRAPH_ARPACK_WORKLSMALL WORKL is too small. * \enumval IGRAPH_ARPACK_TRIDERR LAPACK error in tridiagonal eigenvalue calculation. * \enumval IGRAPH_ARPACK_ZEROSTART Starting vector is zero. * \enumval IGRAPH_ARPACK_MODEINV MODE is invalid. * \enumval IGRAPH_ARPACK_MODEBMAT MODE and BMAT are not compatible. * \enumval IGRAPH_ARPACK_ISHIFT ISHIFT must be 0 or 1. * \enumval IGRAPH_ARPACK_NEVBE NEV and WHICH='BE' are incompatible. * \enumval IGRAPH_ARPACK_NOFACT Could not build an Arnoldi factorization. * \enumval IGRAPH_ARPACK_FAILED No eigenvalues to sufficient accuracy. * \enumval IGRAPH_ARPACK_HOWMNY HOWMNY is invalid. * \enumval IGRAPH_ARPACK_HOWMNYS HOWMNY='S' is not implemented. * \enumval IGRAPH_ARPACK_EVDIFF Different number of converged Ritz values. * \enumval IGRAPH_ARPACK_SHUR Error from calculation of a real Schur form. * \enumval IGRAPH_ARPACK_LAPACK LAPACK (dtrevc) error for calculating eigenvectors. * \enumval IGRAPH_ARPACK_UNKNOWN Unknown ARPACK error. * \enumval IGRAPH_ENEGLOOP Negative loop detected while calculating shortest paths. * \enumval IGRAPH_EINTERNAL Internal error, likely a bug in igraph. * \enumval IGRAPH_EDIVZERO Big integer division by zero. * \enumval IGARPH_GLP_EBOUND GLPK error (GLP_EBOUND). * \enumval IGARPH_GLP_EROOT GLPK error (GLP_EROOT). * \enumval IGARPH_GLP_ENOPFS GLPK error (GLP_ENOPFS). * \enumval IGARPH_GLP_ENODFS GLPK error (GLP_ENODFS). * \enumval IGARPH_GLP_EFAIL GLPK error (GLP_EFAIL). * \enumval IGARPH_GLP_EMIPGAP GLPK error (GLP_EMIPGAP). * \enumval IGARPH_GLP_ETMLIM GLPK error (GLP_ETMLIM). * \enumval IGARPH_GLP_ESTOP GLPK error (GLP_ESTOP). * \enumval IGRAPH_EATTRIBUTES Attribute handler error. The user is not * expected to find this; it is signalled if some igraph function is * not using the attribute handler interface properly. * \enumval IGRAPH_EATTRCOMBINE Unimplemented attribute combination * method for the given attribute type. * \enumval IGRAPH_ELAPACK A LAPACK call resulted an error. * \enumval IGRAPH_EDRL Internal error in the DrL layout generator. * \enumval IGRAPH_EOVERFLOW Integer or double overflow. * \enumval IGRAPH_EGLP Internal GLPK error. * \enumval IGRAPH_CPUTIME CPU time exceeded. * \enumval IGRAPH_EUNDERFLOW Integer or double underflow. */ typedef enum { IGRAPH_SUCCESS = 0, IGRAPH_FAILURE = 1, IGRAPH_ENOMEM = 2, IGRAPH_PARSEERROR = 3, IGRAPH_EINVAL = 4, IGRAPH_EXISTS = 5, IGRAPH_EINVEVECTOR = 6, IGRAPH_EINVVID = 7, IGRAPH_NONSQUARE = 8, IGRAPH_EINVMODE = 9, IGRAPH_EFILE = 10, IGRAPH_UNIMPLEMENTED = 12, IGRAPH_INTERRUPTED = 13, IGRAPH_DIVERGED = 14, IGRAPH_ARPACK_PROD = 15, IGRAPH_ARPACK_NPOS = 16, IGRAPH_ARPACK_NEVNPOS = 17, IGRAPH_ARPACK_NCVSMALL = 18, IGRAPH_ARPACK_NONPOSI = 19, IGRAPH_ARPACK_WHICHINV = 20, IGRAPH_ARPACK_BMATINV = 21, IGRAPH_ARPACK_WORKLSMALL= 22, IGRAPH_ARPACK_TRIDERR = 23, IGRAPH_ARPACK_ZEROSTART = 24, IGRAPH_ARPACK_MODEINV = 25, IGRAPH_ARPACK_MODEBMAT = 26, IGRAPH_ARPACK_ISHIFT = 27, IGRAPH_ARPACK_NEVBE = 28, IGRAPH_ARPACK_NOFACT = 29, IGRAPH_ARPACK_FAILED = 30, IGRAPH_ARPACK_HOWMNY = 31, IGRAPH_ARPACK_HOWMNYS = 32, IGRAPH_ARPACK_EVDIFF = 33, IGRAPH_ARPACK_SHUR = 34, IGRAPH_ARPACK_LAPACK = 35, IGRAPH_ARPACK_UNKNOWN = 36, IGRAPH_ENEGLOOP = 37, IGRAPH_EINTERNAL = 38, IGRAPH_ARPACK_MAXIT = 39, IGRAPH_ARPACK_NOSHIFT = 40, IGRAPH_ARPACK_REORDER = 41, IGRAPH_EDIVZERO = 42, IGRAPH_GLP_EBOUND = 43, IGRAPH_GLP_EROOT = 44, IGRAPH_GLP_ENOPFS = 45, IGRAPH_GLP_ENODFS = 46, IGRAPH_GLP_EFAIL = 47, IGRAPH_GLP_EMIPGAP = 48, IGRAPH_GLP_ETMLIM = 49, IGRAPH_GLP_ESTOP = 50, IGRAPH_EATTRIBUTES = 51, IGRAPH_EATTRCOMBINE = 52, IGRAPH_ELAPACK = 53, IGRAPH_EDRL = 54, IGRAPH_EOVERFLOW = 55, IGRAPH_EGLP = 56, IGRAPH_CPUTIME = 57, IGRAPH_EUNDERFLOW = 58 } igraph_error_type_t; /** * \define IGRAPH_ERROR * \brief Trigger an error. * * \a igraph functions usually use this macro when they notice an error. * It calls * \ref igraph_error() with the proper parameters and if that returns * the macro returns the "calling" function as well, with the error * code. If for some (suspicious) reason you want to call the error * handler without returning from the current function, call * \ref igraph_error() directly. * \param reason Textual description of the error. This should be * something more descriptive than the text associated with the error * code. Eg. if the error code is \c IGRAPH_EINVAL, * its associated text (see \ref igraph_strerror()) is "Invalid * value" and this string should explain which parameter was invalid * and maybe why. * \param igraph_errno The \a igraph error code. */ #define IGRAPH_ERROR(reason,igraph_errno) \ do { \ igraph_error (reason, __FILE__, __LINE__, igraph_errno) ; \ return igraph_errno ; \ } while (0) /** * \function igraph_error * \brief Trigger an error. * * \a igraph functions usually call this function (most often via the * \ref IGRAPH_ERROR macro) if they notice an error. * It calls the currently installed error handler function with the * supplied arguments. * * \param reason Textual description of the error. * \param file The source file in which the error was noticed. * \param line The number of line in the source file which triggered the * error. * \param igraph_errno The \a igraph error code. * \return the error code (if it returns) * * \sa igraph_errorf(). */ int igraph_error(const char *reason, const char *file, int line, int igraph_errno); /** * \function igraph_errorf * \brief Trigger an error, printf-like version. * * \param reason Textual description of the error, interpreted as * a printf format string. * \param file The source file in which the error was noticed. * \param line The line in the source file which triggered the error. * \param igraph_errno The \a igraph error code. * \param ... Additional parameters, the values to substitute into the * format string. * * \sa igraph_error(). */ int igraph_errorf(const char *reason, const char *file, int line, int igraph_errno, ...); int igraph_errorvf(const char *reason, const char *file, int line, int igraph_errno, va_list ap); /** * \function igraph_strerror * \brief Textual description of an error. * * This is a simple utility function, it gives a short general textual * description for an \a igraph error code. * * \param igraph_errno The \a igraph error code. * \return pointer to the textual description of the error code. */ const char* igraph_strerror(const int igraph_errno); #define IGRAPH_ERROR_SELECT_2(a,b) ((a) != IGRAPH_SUCCESS ? (a) : ((b) != IGRAPH_SUCCESS ? (b) : IGRAPH_SUCCESS)) #define IGRAPH_ERROR_SELECT_3(a,b,c) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_2(b,c)) #define IGRAPH_ERROR_SELECT_4(a,b,c,d) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_3(b,c,d)) #define IGRAPH_ERROR_SELECT_5(a,b,c,d,e) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_4(b,c,d,e)) /* Now comes the more convenient error handling macro arsenal. * Ideas taken from exception.{h,c} by Laurent Deniau see * http://cern.ch/Laurent.Deniau/html/oopc/oopc.html#Exceptions for more * information. We don't use the exception handling code though. */ struct igraph_i_protectedPtr { int all; void *ptr; void (*func)(void*); }; typedef void igraph_finally_func_t (void*); void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr); /** * \function IGRAPH_FINALLY_CLEAN * \brief Signal clean deallocation of objects. * * Removes the specified number of objects from the stack of * temporarily allocated objects. Most often this is called just * before returning from a function. * \param num The number of objects to remove from the bookkeeping * stack. */ void IGRAPH_FINALLY_CLEAN(int num); /** * \function IGRAPH_FINALLY_FREE * \brief Deallocate all registered objects. * * Calls the destroy function for all objects in the stack of * temporarily allocated objects. This is usually called only from an * error handler. It is \em not appropriate to use it * instead of destroying each unneeded object of a function, as it * destroys the temporary objects of the caller function (and so on) * as well. */ void IGRAPH_FINALLY_FREE(void); /** * \function IGRAPH_FINALLY_STACK_SIZE * \brief Returns the number of registered objects. * * Returns the number of objects in the stack of temporarily allocated * objects. This function is handy if you write an own igraph routine and * you want to make sure it handles errors properly. A properly written * igraph routine should not leave pointers to temporarily allocated objects * in the finally stack, because otherwise an \ref IGRAPH_FINALLY_FREE call * in another igraph function would result in freeing these objects as well * (and this is really hard to debug, since the error will be not in that * function that shows erroneous behaviour). Therefore, it is advised to * write your own test cases and examine \ref IGRAPH_FINALLY_STACK_SIZE * before and after your test cases - the numbers should be equal. */ int IGRAPH_FINALLY_STACK_SIZE(void); /** * \define IGRAPH_FINALLY_STACK_EMPTY * \brief Returns true if there are no registered objects, false otherwise. * * This is just a shorthand notation for checking that * \ref IGRAPH_FINALLY_STACK_SIZE is zero. */ #define IGRAPH_FINALLY_STACK_EMPTY (IGRAPH_FINALLY_STACK_SIZE() == 0) /** * \define IGRAPH_FINALLY * \brief Register an object for deallocation. * \param func The address of the function which is normally called to * destroy the object. * \param ptr Pointer to the object itself. * * This macro places the address of an object, together with the * address of its destructor in a stack. This stack is used if an * error happens to deallocate temporarily allocated objects to * prevent memory leaks. */ #define IGRAPH_FINALLY(func,ptr) \ IGRAPH_FINALLY_REAL((igraph_finally_func_t*)(func), (ptr)) #if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3) # define IGRAPH_UNLIKELY(a) __builtin_expect((a), 0) # define IGRAPH_LIKELY(a) __builtin_expect((a), 1) #else # define IGRAPH_UNLIKELY(a) a # define IGRAPH_LIKELY(a) a #endif /** * \define IGRAPH_CHECK * \brief Check the return value of a function call. * * \param a An expression, usually a function call. * * Executes the expression and checks its value. If this is not * \c IGRAPH_SUCCESS, it calls \ref IGRAPH_ERROR with * the value as the error code. Here is an example usage: * \verbatim IGRAPH_CHECK(vector_push_back(&v, 100)); \endverbatim * * There is only one reason to use this macro when writing * \a igraph functions. If the user installs an error handler which * returns to the auxiliary calling code (like \ref * igraph_error_handler_ignore and \ref * igraph_error_handler_printignore), and the \a igraph function * signalling the error is called from another \a igraph function * then we need to make sure that the error is propagated back to * the auxiliary (ie. non-igraph) calling function. This is achieved * by using IGRAPH_CHECK on every \a igraph * call which can return an error code. */ #define IGRAPH_CHECK(a) do { \ int igraph_i_ret=(a); \ if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ IGRAPH_ERROR("", igraph_i_ret); \ } } while (0) /** * \section about_igraph_warnings Warning messages * * * Igraph also supports warning messages in addition to error * messages. Warning messages typically do not terminate the * program, but they are usually crucial to the user. * * * * Igraph warning are handled similarly to errors. There is a * separate warning handler function that is called whenever * an igraph function triggers a warning. This handler can be * set by the \ref igraph_set_warning_handler() function. There are * two predefined simple warning handlers, * \ref igraph_warning_handler_ignore() and * \ref igraph_warning_handler_print(), the latter being the default. * * * * To trigger a warning, igraph functions typically use the * \ref IGRAPH_WARNING() macro, the \ref igraph_warning() function, * or if more flexibility is needed, \ref igraph_warningf(). * */ /** * \typedef igraph_warning_handler_t * Type of igraph warning handler functions * * Currently it is defined to have the same type as * \ref igraph_error_handler_t, although the last (error code) * argument is not used. */ typedef igraph_error_handler_t igraph_warning_handler_t; /** * \function igraph_set_warning_handler * Install a warning handler * * Install the supplied warning handler function. * \param new_handler The new warning handler function to install. * Supply a null pointer here to uninstall the current * warning handler, without installing a new one. * \return The current warning handler function. */ igraph_warning_handler_t* igraph_set_warning_handler(igraph_warning_handler_t* new_handler); extern igraph_warning_handler_t igraph_warning_handler_ignore; extern igraph_warning_handler_t igraph_warning_handler_print; /** * \function igraph_warning * Trigger a warning * * Call this function if you want to trigger a warning from within * a function that uses igraph. * \param reason Textual description of the warning. * \param file The source file in which the warning was noticed. * \param line The number of line in the source file which triggered the * warning. * \param igraph_errno Warnings could have potentially error codes as well, * but this is currently not used in igraph. * \return The supplied error code. */ int igraph_warning(const char *reason, const char *file, int line, int igraph_errno); /** * \function igraph_warningf * Trigger a warning, more flexible printf-like syntax * * This function is similar to \ref igraph_warning(), but * uses a printf-like syntax. It substitutes the additional arguments * into the \p reason template string and calls \ref igraph_warning(). * \param reason Textual description of the warning, a template string * with the same syntax as the standard printf C library function. * \param file The source file in which the warning was noticed. * \param line The number of line in the source file which triggered the * warning. * \param igraph_errno Warnings could have potentially error codes as well, * but this is currently not used in igraph. * \param ... The additional arguments to be substituted into the * template string. * \return The supplied error code. */ int igraph_warningf(const char *reason, const char *file, int line, int igraph_errno, ...); /** * \define IGRAPH_WARNING * Trigger a warning. * * This is the usual way of triggering a warning from an igraph * function. It calls \ref igraph_warning(). * \param reason The warning message. */ #define IGRAPH_WARNING(reason) \ do { \ igraph_warning(reason, __FILE__, __LINE__, -1); \ } while (0) __END_DECLS #endif igraph/src/foreign-ncol-lexer.l0000644000176000001440000000606112325372071016224 0ustar ripleyusers/* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-ncol-header.h" #include "foreign-ncol-parser.h" #define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); %} %option noyywrap %option prefix="igraph_ncol_yy" %option outfile="lex.yy.c" %option nounput %option noinput %option reentrant %option bison-bridge %option bison-locations alnum [^ \t\n\r] %% /* ------------------------------------------------whitespace------*/ [ \t]* { } /* ---------------------------------------------------newline------*/ \n\r|\r\n|\n|\r { return NEWLINE; } /* ----------------------------------------------alphanumeric------*/ {alnum}+ { return ALNUM; } <> { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return NEWLINE; } } %% igraph/src/spectral_properties.c0000644000176000001440000003065612325527074016621 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=8 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_structural.h" #include "igraph_interface.h" #include "config.h" #include int igraph_i_weighted_laplacian(const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, igraph_bool_t normalized, const igraph_vector_t *weights) { igraph_eit_t edgeit; int no_of_nodes=(int) igraph_vcount(graph); int no_of_edges=(int) igraph_ecount(graph); igraph_bool_t directed=igraph_is_directed(graph); igraph_vector_t degree; long int i; if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL); } if (res) { IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); igraph_matrix_null(res); } if (sparseres) { int nz=directed ? no_of_edges + no_of_nodes : no_of_edges * 2 + no_of_nodes; igraph_sparsemat_init(sparseres, no_of_nodes, no_of_nodes, nz); } IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); if (directed) { if (!normalized) { while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); long int from=IGRAPH_FROM(graph, edge); long int to =IGRAPH_TO (graph, edge); igraph_real_t weight=VECTOR(*weights)[edge]; if (from != to) { if (res) { MATRIX(*res, from, to) -= weight; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int)to, -weight)); } VECTOR(degree)[from] += weight; } IGRAPH_EIT_NEXT(edgeit); } /* And the diagonal */ for (i=0; i 0 ? 1 : 0; if (res) { MATRIX(*res, i, i) = t; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, t)); } } IGRAPH_EIT_RESET(edgeit); while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); long int from=IGRAPH_FROM(graph, edge); long int to =IGRAPH_TO (graph, edge); igraph_real_t weight=VECTOR(*weights)[edge]; if (from != to) { igraph_real_t t=weight / VECTOR(degree)[from]; if (res) { MATRIX(*res, from, to) -= t; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to, -t)); } } IGRAPH_EIT_NEXT(edgeit); } } } else /* undirected */ { if (!normalized) { while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); long int from=IGRAPH_FROM(graph, edge); long int to =IGRAPH_TO (graph, edge); igraph_real_t weight=VECTOR(*weights)[edge]; if (from != to) { if (res) { MATRIX(*res, from, to) -= weight; MATRIX(*res, to, from) -= weight; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to, -weight)); IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) to, (int) from, -weight)); } VECTOR(degree)[from] += weight; VECTOR(degree)[to] += weight; } IGRAPH_EIT_NEXT(edgeit); } /* And the diagonal */ for (i=0; i 0 ? 1 : 0; if (res) { MATRIX(*res, i, i) = t; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, t)); } VECTOR(degree)[i] = sqrt(VECTOR(degree)[i]); } IGRAPH_EIT_RESET(edgeit); while (!IGRAPH_EIT_END(edgeit)) { long int edge=IGRAPH_EIT_GET(edgeit); long int from=IGRAPH_FROM(graph, edge); long int to =IGRAPH_TO (graph, edge); igraph_real_t weight=VECTOR(*weights)[edge]; if (from != to) { double diff = weight / (VECTOR(degree)[from] * VECTOR(degree)[to]); if (res) { MATRIX(*res, from, to) -= diff; MATRIX(*res, to, from) -= diff; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to, -diff)); IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) to, (int) from, -diff)); } } IGRAPH_EIT_NEXT(edgeit); } } } igraph_vector_destroy(°ree); igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_laplacian * \brief Returns the Laplacian matrix of a graph * * * The graph Laplacian matrix is similar to an adjacency matrix but * contains -1's instead of 1's and the vertex degrees are included in * the diagonal. So the result for edge i--j is -1 if i!=j and is equal * to the degree of vertex i if i==j. igraph_laplacian will work on a * directed graph; in this case, the diagonal will contain the out-degrees. * Loop edges will be ignored. * * * The normalized version of the Laplacian matrix has 1 in the diagonal and * -1/sqrt(d[i]d[j]) if there is an edge from i to j. * * * The first version of this function was written by Vincent Matossian. * \param graph Pointer to the graph to convert. * \param res Pointer to an initialized matrix object, the result is * stored here. It will be resized if needed. * If it is a null pointer, then it is ignored. * At least one of \p res and \p sparseres must be a non-null pointer. * \param sparseres Pointer to an initialized sparse matrix object, the * result is stored here, if it is not a null pointer. * At least one of \p res and \p sparseres must be a non-null pointer. * \param normalized Whether to create a normalized Laplacian matrix. * \param weights An optional vector containing edge weights, to calculate * the weighted Laplacian matrix. Set it to a null pointer to * calculate the unweighted Laplacian. * \return Error code. * * Time complexity: O(|V||V|), * |V| is the * number of vertices in the graph. * * \example examples/simple/igraph_laplacian.c */ int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, igraph_bool_t normalized, const igraph_vector_t *weights) { igraph_eit_t edgeit; int no_of_nodes=(int) igraph_vcount(graph); int no_of_edges=(int) igraph_ecount(graph); igraph_bool_t directed=igraph_is_directed(graph); int from, to; igraph_integer_t ffrom, fto; igraph_vector_t degree; int i; if (!res && !sparseres) { IGRAPH_ERROR("Laplacian: give at least one of `res' or `sparseres'", IGRAPH_EINVAL); } if (weights) { return igraph_i_weighted_laplacian(graph, res, sparseres, normalized, weights); } if (res) { IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); igraph_matrix_null(res); } if (sparseres) { int nz=directed ? no_of_edges + no_of_nodes : no_of_edges * 2 + no_of_nodes; IGRAPH_CHECK(igraph_sparsemat_resize(sparseres, no_of_nodes, no_of_nodes, nz)); } IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_NO_LOOPS)); if (directed){ if (!normalized) { for (i=0;i0 ? 1 : 0; if (res) { MATRIX(*res, i, i) = t; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, t)); } if (VECTOR(degree)[i] > 0) VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i]; } while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); from=ffrom; to=fto; if (from != to) { if (res) { MATRIX(*res, from, to) -= VECTOR(degree)[from]; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -VECTOR(degree)[from])); } } IGRAPH_EIT_NEXT(edgeit); } } } else { if (!normalized) { for(i=0;i0 ? 1: 0; if (res) { MATRIX(*res, i, i) = t; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, t)); } VECTOR(degree)[i] = sqrt(VECTOR(degree)[i]); } while (!IGRAPH_EIT_END(edgeit)) { igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); from=ffrom; to=fto; if (from != to) { double diff = 1.0 / (VECTOR(degree)[from] * VECTOR(degree)[to]); if (res) { MATRIX(*res, from, to) -= diff; MATRIX(*res, to, from) -= diff; } if (sparseres) { IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -diff)); IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -diff)); } } IGRAPH_EIT_NEXT(edgeit); } } } igraph_vector_destroy(°ree); igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(2); return 0; } igraph/src/Ray.h0000755000176000001440000000071412325527072013261 0ustar ripleyusers/** Ray.h */ #ifndef RAY_H #define RAY_H #include "RayVector.h" #include "Point.h" namespace igraph { class Ray { public: Ray(); Ray(const Point& rOrigin, const Vector& rDirection); Ray(const Point& rOrigin, const Point& rEndPoint); ~Ray(); void Origin(Point vPoint); const Point& Origin() const; const Vector& Direction() const; void Direction(Vector vDirection); private: Vector mDirection; Point mOrigin; }; } // namespace igraph #endif igraph/src/dgetv0.f0000644000176000001440000003205312325527073013714 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdgetv0 c c\Description: c Generate a random initial residual vector for the Arnoldi process. c Force the residual vector to be in the range of the operator OP. c c\Usage: c call igraphdgetv0 c ( IDO, BMAT, ITRY, INITV, N, J, V, LDV, RESID, RNORM, c IPNTR, WORKD, IERR ) c c\Arguments c IDO Integer. (INPUT/OUTPUT) c Reverse communication flag. IDO must be zero on the first c call to igraphdgetv0. c ------------------------------------------------------------- c IDO = 0: first call to the reverse communication interface c IDO = -1: compute Y = OP * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c This is for the initialization phase to force the c starting vector into the range of OP. c IDO = 2: compute Y = B * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c IDO = 99: done c ------------------------------------------------------------- c c BMAT Character*1. (INPUT) c BMAT specifies the type of the matrix B in the (generalized) c eigenvalue problem A*x = lambda*B*x. c B = 'I' -> standard eigenvalue problem A*x = lambda*x c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x c c ITRY Integer. (INPUT) c ITRY counts the number of times that igraphdgetv0 is called. c It should be set to 1 on the initial call to igraphdgetv0. c c INITV Logical variable. (INPUT) c .TRUE. => the initial residual vector is given in RESID. c .FALSE. => generate a random initial residual vector. c c N Integer. (INPUT) c Dimension of the problem. c c J Integer. (INPUT) c Index of the residual vector to be generated, with respect to c the Arnoldi process. J > 1 in case of a "restart". c c V Double precision N by J array. (INPUT) c The first J-1 columns of V contain the current Arnoldi basis c if this is a "restart". c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c RESID Double precision array of length N. (INPUT/OUTPUT) c Initial residual vector to be generated. If RESID is c provided, force RESID into the range of the operator OP. c c RNORM Double precision scalar. (OUTPUT) c B-norm of the generated residual. c c IPNTR Integer array of length 3. (OUTPUT) c c WORKD Double precision work array of length 2*N. (REVERSE COMMUNICATION). c On exit, WORK(1:N) = B*RESID to be used in SSAITR. c c IERR Integer. (OUTPUT) c = 0: Normal exit. c = -1: Cannot generate a nontrivial restarted residual vector c in the range of the operator OP. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c c\Routines called: c igraphsecond ARPACK utility routine for timing. c igraphdvout ARPACK utility routine for vector output. c dlarnv LAPACK routine for generating a random vector. c dgemv Level 2 BLAS routine for matrix vector multiplication. c dcopy Level 1 BLAS that copies one vector to another. c ddot Level 1 BLAS that computes the scalar product of two vectors. c dnrm2 Level 1 BLAS that computes the norm of a vector. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\SCCS Information: @(#) c FILE: getv0.F SID: 2.6 DATE OF SID: 8/27/96 RELEASE: 2 c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdgetv0 & ( ido, bmat, itry, initv, n, j, v, ldv, resid, rnorm, & ipntr, workd, ierr ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat*1 logical initv integer ido, ierr, itry, j, ldv, n Double precision & rnorm c c %-----------------% c | Array Arguments | c %-----------------% c integer ipntr(3) Double precision & resid(n), v(ldv,j), workd(2*n) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %------------------------% c | Local Scalars & Arrays | c %------------------------% c logical first, inits, orth integer idist, iseed(4), iter, msglvl, jj Double precision & rnorm0 save first, iseed, inits, iter, msglvl, orth, rnorm0 c c %----------------------% c | External Subroutines | c %----------------------% c external dlarnv, igraphdvout, dcopy, dgemv, igraphsecond c c %--------------------% c | External Functions | c %--------------------% c Double precision & ddot, dnrm2 external ddot, dnrm2 c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic abs, sqrt c c %-----------------% c | Data Statements | c %-----------------% c data inits /.true./ c c %-----------------------% c | Executable Statements | c %-----------------------% c c c %-----------------------------------% c | Initialize the seed of the LAPACK | c | random number generator | c %-----------------------------------% c if (inits) then iseed(1) = 1 iseed(2) = 3 iseed(3) = 5 iseed(4) = 7 inits = .false. end if c if (ido .eq. 0) then c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = mgetv0 c ierr = 0 iter = 0 first = .FALSE. orth = .FALSE. c c %-----------------------------------------------------% c | Possibly generate a random starting vector in RESID | c | Use a LAPACK random number generator used by the | c | matrix generation routines. | c | idist = 1: uniform (0,1) distribution; | c | idist = 2: uniform (-1,1) distribution; | c | idist = 3: normal (0,1) distribution; | c %-----------------------------------------------------% c if (.not.initv) then idist = 2 call dlarnv (idist, iseed, n, resid) end if c c %----------------------------------------------------------% c | Force the starting vector into the range of OP to handle | c | the generalized problem when B is possibly (singular). | c %----------------------------------------------------------% c call igraphsecond (t2) if (bmat .eq. 'G') then nopx = nopx + 1 ipntr(1) = 1 ipntr(2) = n + 1 call dcopy (n, resid, 1, workd, 1) ido = -1 go to 9000 end if end if c c %-----------------------------------------% c | Back from computing OP*(initial-vector) | c %-----------------------------------------% c if (first) go to 20 c c %-----------------------------------------------% c | Back from computing B*(orthogonalized-vector) | c %-----------------------------------------------% c if (orth) go to 40 c if (bmat .eq. 'G') then call igraphsecond (t3) tmvopx = tmvopx + (t3 - t2) end if c c %------------------------------------------------------% c | Starting vector is now in the range of OP; r = OP*r; | c | Compute B-norm of starting vector. | c %------------------------------------------------------% c call igraphsecond (t2) first = .TRUE. if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, workd(n+1), 1, resid, 1) ipntr(1) = n + 1 ipntr(2) = 1 ido = 2 go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd, 1) end if c 20 continue c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c first = .FALSE. if (bmat .eq. 'G') then rnorm0 = ddot (n, resid, 1, workd, 1) rnorm0 = sqrt(abs(rnorm0)) else if (bmat .eq. 'I') then rnorm0 = dnrm2(n, resid, 1) end if rnorm = rnorm0 c c %---------------------------------------------% c | Exit if this is the very first Arnoldi step | c %---------------------------------------------% c if (j .eq. 1) go to 50 c c %---------------------------------------------------------------- c | Otherwise need to B-orthogonalize the starting vector against | c | the current Arnoldi basis using Gram-Schmidt with iter. ref. | c | This is the case where an invariant subspace is encountered | c | in the middle of the Arnoldi factorization. | c | | c | s = V^{T}*B*r; r = r - V*s; | c | | c | Stopping criteria used for iter. ref. is discussed in | c | Parlett's book, page 107 and in Gragg & Reichel TOMS paper. | c %---------------------------------------------------------------% c orth = .TRUE. 30 continue c call dgemv ('T', n, j-1, one, v, ldv, workd, 1, & zero, workd(n+1), 1) call dgemv ('N', n, j-1, -one, v, ldv, workd(n+1), 1, & one, resid, 1) c c %----------------------------------------------------------% c | Compute the B-norm of the orthogonalized starting vector | c %----------------------------------------------------------% c call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, resid, 1, workd(n+1), 1) ipntr(1) = n + 1 ipntr(2) = 1 ido = 2 go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd, 1) end if c 40 continue c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c if (bmat .eq. 'G') then rnorm = ddot (n, resid, 1, workd, 1) rnorm = sqrt(abs(rnorm)) else if (bmat .eq. 'I') then rnorm = dnrm2(n, resid, 1) end if c c %--------------------------------------% c | Check for further orthogonalization. | c %--------------------------------------% c if (msglvl .gt. 2) then call igraphdvout (logfil, 1, rnorm0, ndigit, & '_getv0: re-orthonalization ; rnorm0 is') call igraphdvout (logfil, 1, rnorm, ndigit, & '_getv0: re-orthonalization ; rnorm is') end if c if (rnorm .gt. 0.717*rnorm0) go to 50 c iter = iter + 1 if (iter .le. 1) then c c %-----------------------------------% c | Perform iterative refinement step | c %-----------------------------------% c rnorm0 = rnorm go to 30 else c c %------------------------------------% c | Iterative refinement step "failed" | c %------------------------------------% c do 45 jj = 1, n resid(jj) = zero 45 continue rnorm = zero ierr = -1 end if c 50 continue c if (msglvl .gt. 0) then call igraphdvout (logfil, 1, rnorm, ndigit, & '_getv0: B-norm of initial / restarted starting vector') end if if (msglvl .gt. 2) then call igraphdvout (logfil, n, resid, ndigit, & '_getv0: initial / restarted starting vector') end if ido = 99 c call igraphsecond (t1) tgetv0 = tgetv0 + (t1 - t0) c 9000 continue return c c %---------------% c | End of igraphdgetv0 | c %---------------% c end igraph/src/glpnpp01.c0000644000176000001440000007106312325527073014165 0ustar ripleyusers/* glpnpp01.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpnpp.h" NPP *npp_create_wksp(void) { /* create LP/MIP preprocessor workspace */ NPP *npp; npp = xmalloc(sizeof(NPP)); npp->orig_dir = 0; npp->orig_m = npp->orig_n = npp->orig_nnz = 0; npp->pool = dmp_create_pool(); npp->name = npp->obj = NULL; npp->c0 = 0.0; npp->nrows = npp->ncols = 0; npp->r_head = npp->r_tail = NULL; npp->c_head = npp->c_tail = NULL; npp->stack = dmp_create_pool(); npp->top = NULL; #if 0 /* 16/XII-2009 */ memset(&npp->count, 0, sizeof(npp->count)); #endif npp->m = npp->n = npp->nnz = 0; npp->row_ref = npp->col_ref = NULL; npp->sol = npp->scaling = 0; npp->p_stat = npp->d_stat = npp->t_stat = npp->i_stat = 0; npp->r_stat = NULL; /*npp->r_prim =*/ npp->r_pi = NULL; npp->c_stat = NULL; npp->c_value = /*npp->c_dual =*/ NULL; return npp; } void npp_insert_row(NPP *npp, NPPROW *row, int where) { /* insert row to the row list */ if (where == 0) { /* insert row to the beginning of the row list */ row->prev = NULL; row->next = npp->r_head; if (row->next == NULL) npp->r_tail = row; else row->next->prev = row; npp->r_head = row; } else { /* insert row to the end of the row list */ row->prev = npp->r_tail; row->next = NULL; if (row->prev == NULL) npp->r_head = row; else row->prev->next = row; npp->r_tail = row; } return; } void npp_remove_row(NPP *npp, NPPROW *row) { /* remove row from the row list */ if (row->prev == NULL) npp->r_head = row->next; else row->prev->next = row->next; if (row->next == NULL) npp->r_tail = row->prev; else row->next->prev = row->prev; return; } void npp_activate_row(NPP *npp, NPPROW *row) { /* make row active */ if (!row->temp) { row->temp = 1; /* move the row to the beginning of the row list */ npp_remove_row(npp, row); npp_insert_row(npp, row, 0); } return; } void npp_deactivate_row(NPP *npp, NPPROW *row) { /* make row inactive */ if (row->temp) { row->temp = 0; /* move the row to the end of the row list */ npp_remove_row(npp, row); npp_insert_row(npp, row, 1); } return; } void npp_insert_col(NPP *npp, NPPCOL *col, int where) { /* insert column to the column list */ if (where == 0) { /* insert column to the beginning of the column list */ col->prev = NULL; col->next = npp->c_head; if (col->next == NULL) npp->c_tail = col; else col->next->prev = col; npp->c_head = col; } else { /* insert column to the end of the column list */ col->prev = npp->c_tail; col->next = NULL; if (col->prev == NULL) npp->c_head = col; else col->prev->next = col; npp->c_tail = col; } return; } void npp_remove_col(NPP *npp, NPPCOL *col) { /* remove column from the column list */ if (col->prev == NULL) npp->c_head = col->next; else col->prev->next = col->next; if (col->next == NULL) npp->c_tail = col->prev; else col->next->prev = col->prev; return; } void npp_activate_col(NPP *npp, NPPCOL *col) { /* make column active */ if (!col->temp) { col->temp = 1; /* move the column to the beginning of the column list */ npp_remove_col(npp, col); npp_insert_col(npp, col, 0); } return; } void npp_deactivate_col(NPP *npp, NPPCOL *col) { /* make column inactive */ if (col->temp) { col->temp = 0; /* move the column to the end of the column list */ npp_remove_col(npp, col); npp_insert_col(npp, col, 1); } return; } NPPROW *npp_add_row(NPP *npp) { /* add new row to the current problem */ NPPROW *row; row = dmp_get_atom(npp->pool, sizeof(NPPROW)); row->i = ++(npp->nrows); row->name = NULL; row->lb = -DBL_MAX, row->ub = +DBL_MAX; row->ptr = NULL; row->temp = 0; npp_insert_row(npp, row, 1); return row; } NPPCOL *npp_add_col(NPP *npp) { /* add new column to the current problem */ NPPCOL *col; col = dmp_get_atom(npp->pool, sizeof(NPPCOL)); col->j = ++(npp->ncols); col->name = NULL; #if 0 col->kind = GLP_CV; #else col->is_int = 0; #endif col->lb = col->ub = col->coef = 0.0; col->ptr = NULL; col->temp = 0; npp_insert_col(npp, col, 1); return col; } NPPAIJ *npp_add_aij(NPP *npp, NPPROW *row, NPPCOL *col, double val) { /* add new element to the constraint matrix */ NPPAIJ *aij; aij = dmp_get_atom(npp->pool, sizeof(NPPAIJ)); aij->row = row; aij->col = col; aij->val = val; aij->r_prev = NULL; aij->r_next = row->ptr; aij->c_prev = NULL; aij->c_next = col->ptr; if (aij->r_next != NULL) aij->r_next->r_prev = aij; if (aij->c_next != NULL) aij->c_next->c_prev = aij; row->ptr = col->ptr = aij; return aij; } int npp_row_nnz(NPP *npp, NPPROW *row) { /* count number of non-zero coefficients in row */ NPPAIJ *aij; int nnz; xassert(npp == npp); nnz = 0; for (aij = row->ptr; aij != NULL; aij = aij->r_next) nnz++; return nnz; } int npp_col_nnz(NPP *npp, NPPCOL *col) { /* count number of non-zero coefficients in column */ NPPAIJ *aij; int nnz; xassert(npp == npp); nnz = 0; for (aij = col->ptr; aij != NULL; aij = aij->c_next) nnz++; return nnz; } void *npp_push_tse(NPP *npp, int (*func)(NPP *npp, void *info), int size) { /* push new entry to the transformation stack */ NPPTSE *tse; tse = dmp_get_atom(npp->stack, sizeof(NPPTSE)); tse->func = func; tse->info = dmp_get_atom(npp->stack, size); tse->link = npp->top; npp->top = tse; return tse->info; } #if 1 /* 23/XII-2009 */ void npp_erase_row(NPP *npp, NPPROW *row) { /* erase row content to make it empty */ NPPAIJ *aij; while (row->ptr != NULL) { aij = row->ptr; row->ptr = aij->r_next; if (aij->c_prev == NULL) aij->col->ptr = aij->c_next; else aij->c_prev->c_next = aij->c_next; if (aij->c_next == NULL) ; else aij->c_next->c_prev = aij->c_prev; dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); } return; } #endif void npp_del_row(NPP *npp, NPPROW *row) { /* remove row from the current problem */ #if 0 /* 23/XII-2009 */ NPPAIJ *aij; #endif if (row->name != NULL) dmp_free_atom(npp->pool, row->name, strlen(row->name)+1); #if 0 /* 23/XII-2009 */ while (row->ptr != NULL) { aij = row->ptr; row->ptr = aij->r_next; if (aij->c_prev == NULL) aij->col->ptr = aij->c_next; else aij->c_prev->c_next = aij->c_next; if (aij->c_next == NULL) ; else aij->c_next->c_prev = aij->c_prev; dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); } #else npp_erase_row(npp, row); #endif npp_remove_row(npp, row); dmp_free_atom(npp->pool, row, sizeof(NPPROW)); return; } void npp_del_col(NPP *npp, NPPCOL *col) { /* remove column from the current problem */ NPPAIJ *aij; if (col->name != NULL) dmp_free_atom(npp->pool, col->name, strlen(col->name)+1); while (col->ptr != NULL) { aij = col->ptr; col->ptr = aij->c_next; if (aij->r_prev == NULL) aij->row->ptr = aij->r_next; else aij->r_prev->r_next = aij->r_next; if (aij->r_next == NULL) ; else aij->r_next->r_prev = aij->r_prev; dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); } npp_remove_col(npp, col); dmp_free_atom(npp->pool, col, sizeof(NPPCOL)); return; } void npp_del_aij(NPP *npp, NPPAIJ *aij) { /* remove element from the constraint matrix */ if (aij->r_prev == NULL) aij->row->ptr = aij->r_next; else aij->r_prev->r_next = aij->r_next; if (aij->r_next == NULL) ; else aij->r_next->r_prev = aij->r_prev; if (aij->c_prev == NULL) aij->col->ptr = aij->c_next; else aij->c_prev->c_next = aij->c_next; if (aij->c_next == NULL) ; else aij->c_next->c_prev = aij->c_prev; dmp_free_atom(npp->pool, aij, sizeof(NPPAIJ)); return; } void npp_load_prob(NPP *npp, glp_prob *orig, int names, int sol, int scaling) { /* load original problem into the preprocessor workspace */ int m = orig->m; int n = orig->n; NPPROW **link; int i, j; double dir; xassert(names == GLP_OFF || names == GLP_ON); xassert(sol == GLP_SOL || sol == GLP_IPT || sol == GLP_MIP); xassert(scaling == GLP_OFF || scaling == GLP_ON); if (sol == GLP_MIP) xassert(!scaling); npp->orig_dir = orig->dir; if (npp->orig_dir == GLP_MIN) dir = +1.0; else if (npp->orig_dir == GLP_MAX) dir = -1.0; else xassert(npp != npp); npp->orig_m = m; npp->orig_n = n; npp->orig_nnz = orig->nnz; if (names && orig->name != NULL) { npp->name = dmp_get_atom(npp->pool, strlen(orig->name)+1); strcpy(npp->name, orig->name); } if (names && orig->obj != NULL) { npp->obj = dmp_get_atom(npp->pool, strlen(orig->obj)+1); strcpy(npp->obj, orig->obj); } npp->c0 = dir * orig->c0; /* load rows */ link = xcalloc(1+m, sizeof(NPPROW *)); for (i = 1; i <= m; i++) { GLPROW *rrr = orig->row[i]; NPPROW *row; link[i] = row = npp_add_row(npp); xassert(row->i == i); if (names && rrr->name != NULL) { row->name = dmp_get_atom(npp->pool, strlen(rrr->name)+1); strcpy(row->name, rrr->name); } if (!scaling) { if (rrr->type == GLP_FR) row->lb = -DBL_MAX, row->ub = +DBL_MAX; else if (rrr->type == GLP_LO) row->lb = rrr->lb, row->ub = +DBL_MAX; else if (rrr->type == GLP_UP) row->lb = -DBL_MAX, row->ub = rrr->ub; else if (rrr->type == GLP_DB) row->lb = rrr->lb, row->ub = rrr->ub; else if (rrr->type == GLP_FX) row->lb = row->ub = rrr->lb; else xassert(rrr != rrr); } else { double rii = rrr->rii; if (rrr->type == GLP_FR) row->lb = -DBL_MAX, row->ub = +DBL_MAX; else if (rrr->type == GLP_LO) row->lb = rrr->lb * rii, row->ub = +DBL_MAX; else if (rrr->type == GLP_UP) row->lb = -DBL_MAX, row->ub = rrr->ub * rii; else if (rrr->type == GLP_DB) row->lb = rrr->lb * rii, row->ub = rrr->ub * rii; else if (rrr->type == GLP_FX) row->lb = row->ub = rrr->lb * rii; else xassert(rrr != rrr); } } /* load columns and constraint coefficients */ for (j = 1; j <= n; j++) { GLPCOL *ccc = orig->col[j]; GLPAIJ *aaa; NPPCOL *col; col = npp_add_col(npp); xassert(col->j == j); if (names && ccc->name != NULL) { col->name = dmp_get_atom(npp->pool, strlen(ccc->name)+1); strcpy(col->name, ccc->name); } if (sol == GLP_MIP) #if 0 col->kind = ccc->kind; #else col->is_int = (char)(ccc->kind == GLP_IV); #endif if (!scaling) { if (ccc->type == GLP_FR) col->lb = -DBL_MAX, col->ub = +DBL_MAX; else if (ccc->type == GLP_LO) col->lb = ccc->lb, col->ub = +DBL_MAX; else if (ccc->type == GLP_UP) col->lb = -DBL_MAX, col->ub = ccc->ub; else if (ccc->type == GLP_DB) col->lb = ccc->lb, col->ub = ccc->ub; else if (ccc->type == GLP_FX) col->lb = col->ub = ccc->lb; else xassert(ccc != ccc); col->coef = dir * ccc->coef; for (aaa = ccc->ptr; aaa != NULL; aaa = aaa->c_next) npp_add_aij(npp, link[aaa->row->i], col, aaa->val); } else { double sjj = ccc->sjj; if (ccc->type == GLP_FR) col->lb = -DBL_MAX, col->ub = +DBL_MAX; else if (ccc->type == GLP_LO) col->lb = ccc->lb / sjj, col->ub = +DBL_MAX; else if (ccc->type == GLP_UP) col->lb = -DBL_MAX, col->ub = ccc->ub / sjj; else if (ccc->type == GLP_DB) col->lb = ccc->lb / sjj, col->ub = ccc->ub / sjj; else if (ccc->type == GLP_FX) col->lb = col->ub = ccc->lb / sjj; else xassert(ccc != ccc); col->coef = dir * ccc->coef * sjj; for (aaa = ccc->ptr; aaa != NULL; aaa = aaa->c_next) npp_add_aij(npp, link[aaa->row->i], col, aaa->row->rii * aaa->val * sjj); } } xfree(link); /* keep solution indicator and scaling option */ npp->sol = sol; npp->scaling = scaling; return; } void npp_build_prob(NPP *npp, glp_prob *prob) { /* build resultant (preprocessed) problem */ NPPROW *row; NPPCOL *col; NPPAIJ *aij; int i, j, type, len, *ind; double dir, *val; glp_erase_prob(prob); glp_set_prob_name(prob, npp->name); glp_set_obj_name(prob, npp->obj); glp_set_obj_dir(prob, npp->orig_dir); if (npp->orig_dir == GLP_MIN) dir = +1.0; else if (npp->orig_dir == GLP_MAX) dir = -1.0; else xassert(npp != npp); glp_set_obj_coef(prob, 0, dir * npp->c0); /* build rows */ for (row = npp->r_head; row != NULL; row = row->next) { row->temp = i = glp_add_rows(prob, 1); glp_set_row_name(prob, i, row->name); if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) type = GLP_FR; else if (row->ub == +DBL_MAX) type = GLP_LO; else if (row->lb == -DBL_MAX) type = GLP_UP; else if (row->lb != row->ub) type = GLP_DB; else type = GLP_FX; glp_set_row_bnds(prob, i, type, row->lb, row->ub); } /* build columns and the constraint matrix */ ind = xcalloc(1+prob->m, sizeof(int)); val = xcalloc(1+prob->m, sizeof(double)); for (col = npp->c_head; col != NULL; col = col->next) { j = glp_add_cols(prob, 1); glp_set_col_name(prob, j, col->name); #if 0 glp_set_col_kind(prob, j, col->kind); #else glp_set_col_kind(prob, j, col->is_int ? GLP_IV : GLP_CV); #endif if (col->lb == -DBL_MAX && col->ub == +DBL_MAX) type = GLP_FR; else if (col->ub == +DBL_MAX) type = GLP_LO; else if (col->lb == -DBL_MAX) type = GLP_UP; else if (col->lb != col->ub) type = GLP_DB; else type = GLP_FX; glp_set_col_bnds(prob, j, type, col->lb, col->ub); glp_set_obj_coef(prob, j, dir * col->coef); len = 0; for (aij = col->ptr; aij != NULL; aij = aij->c_next) { len++; ind[len] = aij->row->temp; val[len] = aij->val; } glp_set_mat_col(prob, j, len, ind, val); } xfree(ind); xfree(val); /* resultant problem has been built */ npp->m = prob->m; npp->n = prob->n; npp->nnz = prob->nnz; npp->row_ref = xcalloc(1+npp->m, sizeof(int)); npp->col_ref = xcalloc(1+npp->n, sizeof(int)); for (row = npp->r_head, i = 0; row != NULL; row = row->next) npp->row_ref[++i] = row->i; for (col = npp->c_head, j = 0; col != NULL; col = col->next) npp->col_ref[++j] = col->j; /* transformed problem segment is no longer needed */ dmp_delete_pool(npp->pool), npp->pool = NULL; npp->name = npp->obj = NULL; npp->c0 = 0.0; npp->r_head = npp->r_tail = NULL; npp->c_head = npp->c_tail = NULL; return; } void npp_postprocess(NPP *npp, glp_prob *prob) { /* postprocess solution from the resultant problem */ GLPROW *row; GLPCOL *col; NPPTSE *tse; int i, j, k; double dir; xassert(npp->orig_dir == prob->dir); if (npp->orig_dir == GLP_MIN) dir = +1.0; else if (npp->orig_dir == GLP_MAX) dir = -1.0; else xassert(npp != npp); xassert(npp->m == prob->m); xassert(npp->n == prob->n); xassert(npp->nnz == prob->nnz); /* copy solution status */ if (npp->sol == GLP_SOL) { npp->p_stat = prob->pbs_stat; npp->d_stat = prob->dbs_stat; } else if (npp->sol == GLP_IPT) npp->t_stat = prob->ipt_stat; else if (npp->sol == GLP_MIP) npp->i_stat = prob->mip_stat; else xassert(npp != npp); /* allocate solution arrays */ if (npp->sol == GLP_SOL) { if (npp->r_stat == NULL) npp->r_stat = xcalloc(1+npp->nrows, sizeof(char)); for (i = 1; i <= npp->nrows; i++) npp->r_stat[i] = 0; if (npp->c_stat == NULL) npp->c_stat = xcalloc(1+npp->ncols, sizeof(char)); for (j = 1; j <= npp->ncols; j++) npp->c_stat[j] = 0; } #if 0 if (npp->r_prim == NULL) npp->r_prim = xcalloc(1+npp->nrows, sizeof(double)); for (i = 1; i <= npp->nrows; i++) npp->r_prim[i] = DBL_MAX; #endif if (npp->c_value == NULL) npp->c_value = xcalloc(1+npp->ncols, sizeof(double)); for (j = 1; j <= npp->ncols; j++) npp->c_value[j] = DBL_MAX; if (npp->sol != GLP_MIP) { if (npp->r_pi == NULL) npp->r_pi = xcalloc(1+npp->nrows, sizeof(double)); for (i = 1; i <= npp->nrows; i++) npp->r_pi[i] = DBL_MAX; #if 0 if (npp->c_dual == NULL) npp->c_dual = xcalloc(1+npp->ncols, sizeof(double)); for (j = 1; j <= npp->ncols; j++) npp->c_dual[j] = DBL_MAX; #endif } /* copy solution components from the resultant problem */ if (npp->sol == GLP_SOL) { for (i = 1; i <= npp->m; i++) { row = prob->row[i]; k = npp->row_ref[i]; npp->r_stat[k] = (char)row->stat; /*npp->r_prim[k] = row->prim;*/ npp->r_pi[k] = dir * row->dual; } for (j = 1; j <= npp->n; j++) { col = prob->col[j]; k = npp->col_ref[j]; npp->c_stat[k] = (char)col->stat; npp->c_value[k] = col->prim; /*npp->c_dual[k] = dir * col->dual;*/ } } else if (npp->sol == GLP_IPT) { for (i = 1; i <= npp->m; i++) { row = prob->row[i]; k = npp->row_ref[i]; /*npp->r_prim[k] = row->pval;*/ npp->r_pi[k] = dir * row->dval; } for (j = 1; j <= npp->n; j++) { col = prob->col[j]; k = npp->col_ref[j]; npp->c_value[k] = col->pval; /*npp->c_dual[k] = dir * col->dval;*/ } } else if (npp->sol == GLP_MIP) { #if 0 for (i = 1; i <= npp->m; i++) { row = prob->row[i]; k = npp->row_ref[i]; /*npp->r_prim[k] = row->mipx;*/ } #endif for (j = 1; j <= npp->n; j++) { col = prob->col[j]; k = npp->col_ref[j]; npp->c_value[k] = col->mipx; } } else xassert(npp != npp); /* perform postprocessing to construct solution to the original problem */ for (tse = npp->top; tse != NULL; tse = tse->link) { xassert(tse->func != NULL); xassert(tse->func(npp, tse->info) == 0); } return; } void npp_unload_sol(NPP *npp, glp_prob *orig) { /* store solution to the original problem */ GLPROW *row; GLPCOL *col; int i, j; double dir; xassert(npp->orig_dir == orig->dir); if (npp->orig_dir == GLP_MIN) dir = +1.0; else if (npp->orig_dir == GLP_MAX) dir = -1.0; else xassert(npp != npp); xassert(npp->orig_m == orig->m); xassert(npp->orig_n == orig->n); xassert(npp->orig_nnz == orig->nnz); if (npp->sol == GLP_SOL) { /* store basic solution */ orig->valid = 0; orig->pbs_stat = npp->p_stat; orig->dbs_stat = npp->d_stat; orig->obj_val = orig->c0; orig->some = 0; for (i = 1; i <= orig->m; i++) { row = orig->row[i]; row->stat = npp->r_stat[i]; if (!npp->scaling) { /*row->prim = npp->r_prim[i];*/ row->dual = dir * npp->r_pi[i]; } else { /*row->prim = npp->r_prim[i] / row->rii;*/ row->dual = dir * npp->r_pi[i] * row->rii; } if (row->stat == GLP_BS) row->dual = 0.0; else if (row->stat == GLP_NL) { xassert(row->type == GLP_LO || row->type == GLP_DB); row->prim = row->lb; } else if (row->stat == GLP_NU) { xassert(row->type == GLP_UP || row->type == GLP_DB); row->prim = row->ub; } else if (row->stat == GLP_NF) { xassert(row->type == GLP_FR); row->prim = 0.0; } else if (row->stat == GLP_NS) { xassert(row->type == GLP_FX); row->prim = row->lb; } else xassert(row != row); } for (j = 1; j <= orig->n; j++) { col = orig->col[j]; col->stat = npp->c_stat[j]; if (!npp->scaling) { col->prim = npp->c_value[j]; /*col->dual = dir * npp->c_dual[j];*/ } else { col->prim = npp->c_value[j] * col->sjj; /*col->dual = dir * npp->c_dual[j] / col->sjj;*/ } if (col->stat == GLP_BS) col->dual = 0.0; #if 1 else if (col->stat == GLP_NL) { xassert(col->type == GLP_LO || col->type == GLP_DB); col->prim = col->lb; } else if (col->stat == GLP_NU) { xassert(col->type == GLP_UP || col->type == GLP_DB); col->prim = col->ub; } else if (col->stat == GLP_NF) { xassert(col->type == GLP_FR); col->prim = 0.0; } else if (col->stat == GLP_NS) { xassert(col->type == GLP_FX); col->prim = col->lb; } else xassert(col != col); #endif orig->obj_val += col->coef * col->prim; } #if 1 /* compute primal values of inactive rows */ for (i = 1; i <= orig->m; i++) { row = orig->row[i]; if (row->stat == GLP_BS) { GLPAIJ *aij; double temp; temp = 0.0; for (aij = row->ptr; aij != NULL; aij = aij->r_next) temp += aij->val * aij->col->prim; row->prim = temp; } } /* compute reduced costs of active columns */ for (j = 1; j <= orig->n; j++) { col = orig->col[j]; if (col->stat != GLP_BS) { GLPAIJ *aij; double temp; temp = col->coef; for (aij = col->ptr; aij != NULL; aij = aij->c_next) temp -= aij->val * aij->row->dual; col->dual = temp; } } #endif } else if (npp->sol == GLP_IPT) { /* store interior-point solution */ orig->ipt_stat = npp->t_stat; orig->ipt_obj = orig->c0; for (i = 1; i <= orig->m; i++) { row = orig->row[i]; if (!npp->scaling) { /*row->pval = npp->r_prim[i];*/ row->dval = dir * npp->r_pi[i]; } else { /*row->pval = npp->r_prim[i] / row->rii;*/ row->dval = dir * npp->r_pi[i] * row->rii; } } for (j = 1; j <= orig->n; j++) { col = orig->col[j]; if (!npp->scaling) { col->pval = npp->c_value[j]; /*col->dval = dir * npp->c_dual[j];*/ } else { col->pval = npp->c_value[j] * col->sjj; /*col->dval = dir * npp->c_dual[j] / col->sjj;*/ } orig->ipt_obj += col->coef * col->pval; } #if 1 /* compute row primal values */ for (i = 1; i <= orig->m; i++) { row = orig->row[i]; { GLPAIJ *aij; double temp; temp = 0.0; for (aij = row->ptr; aij != NULL; aij = aij->r_next) temp += aij->val * aij->col->pval; row->pval = temp; } } /* compute column dual values */ for (j = 1; j <= orig->n; j++) { col = orig->col[j]; { GLPAIJ *aij; double temp; temp = col->coef; for (aij = col->ptr; aij != NULL; aij = aij->c_next) temp -= aij->val * aij->row->dval; col->dval = temp; } } #endif } else if (npp->sol == GLP_MIP) { /* store MIP solution */ xassert(!npp->scaling); orig->mip_stat = npp->i_stat; orig->mip_obj = orig->c0; #if 0 for (i = 1; i <= orig->m; i++) { row = orig->row[i]; /*row->mipx = npp->r_prim[i];*/ } #endif for (j = 1; j <= orig->n; j++) { col = orig->col[j]; col->mipx = npp->c_value[j]; if (col->kind == GLP_IV) xassert(col->mipx == floor(col->mipx)); orig->mip_obj += col->coef * col->mipx; } #if 1 /* compute row primal values */ for (i = 1; i <= orig->m; i++) { row = orig->row[i]; { GLPAIJ *aij; double temp; temp = 0.0; for (aij = row->ptr; aij != NULL; aij = aij->r_next) temp += aij->val * aij->col->mipx; row->mipx = temp; } } #endif } else xassert(npp != npp); return; } void npp_delete_wksp(NPP *npp) { /* delete LP/MIP preprocessor workspace */ if (npp->pool != NULL) dmp_delete_pool(npp->pool); if (npp->stack != NULL) dmp_delete_pool(npp->stack); if (npp->row_ref != NULL) xfree(npp->row_ref); if (npp->col_ref != NULL) xfree(npp->col_ref); if (npp->r_stat != NULL) xfree(npp->r_stat); #if 0 if (npp->r_prim != NULL) xfree(npp->r_prim); #endif if (npp->r_pi != NULL) xfree(npp->r_pi); if (npp->c_stat != NULL) xfree(npp->c_stat); if (npp->c_value != NULL) xfree(npp->c_value); #if 0 if (npp->c_dual != NULL) xfree(npp->c_dual); #endif xfree(npp); return; } /* eof */ igraph/src/config.h0000644000176000001440000000000012325527072013754 0ustar ripleyusersigraph/src/cattributes.c0000644000176000001440000034032712325527072015056 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_attributes.h" #include "igraph_memory.h" #include "config.h" #include "igraph_math.h" #include "igraph_interface.h" #include "igraph_random.h" #include /* An attribute is either a numeric vector (vector_t) or a string vector (strvector_t). The attribute itself is stored in a struct igraph_attribute_record_t, there is one such object for each attribute. The igraph_t has a pointer to an array of three vector_ptr_t's which contains pointers to igraph_i_cattribute_t's. Graph attributes are first, then vertex and edge attributes. */ igraph_bool_t igraph_i_cattribute_find(const igraph_vector_ptr_t *ptrvec, const char *name, long int *idx) { long int i, n=igraph_vector_ptr_size(ptrvec); igraph_bool_t l=0; for (i=0; !l && iname, name); } if (idx) { *idx=i-1; } return l; } typedef struct igraph_i_cattributes_t { igraph_vector_ptr_t gal; igraph_vector_ptr_t val; igraph_vector_ptr_t eal; } igraph_i_cattributes_t; int igraph_i_cattributes_copy_attribute_record(igraph_attribute_record_t **newrec, const igraph_attribute_record_t *rec) { igraph_vector_t *num, *newnum; igraph_strvector_t *str, *newstr; *newrec=igraph_Calloc(1, igraph_attribute_record_t); if (!(*newrec)) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, *newrec); (*newrec)->type=rec->type; (*newrec)->name=strdup(rec->name); if (!(*newrec)->name) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (void*)(*newrec)->name); if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { num=(igraph_vector_t *)rec->value; newnum=igraph_Calloc(1, igraph_vector_t); if (!newnum) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newnum); IGRAPH_CHECK(igraph_vector_copy(newnum, num)); IGRAPH_FINALLY(igraph_vector_destroy, newnum); (*newrec)->value=newnum; } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { str=(igraph_strvector_t*)rec->value; newstr=igraph_Calloc(1, igraph_strvector_t); if (!newstr) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newstr); IGRAPH_CHECK(igraph_strvector_copy(newstr, str)); IGRAPH_FINALLY(igraph_strvector_destroy, newstr); (*newrec)->value=newstr; } IGRAPH_FINALLY_CLEAN(4); return 0; } int igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { igraph_attribute_record_t *attr_rec; long int i, n; igraph_i_cattributes_t *nattr; n = attr ? igraph_vector_ptr_size(attr) : 0; nattr=igraph_Calloc(1, igraph_i_cattributes_t); if (!nattr) { IGRAPH_ERROR("Can't init attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nattr); IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->gal, n)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->gal); IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->val, 0)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->val); IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->eal, 0)); IGRAPH_FINALLY_CLEAN(3); for (i=0; igal)[i] = attr_rec; } graph->attr=nattr; return 0; } void igraph_i_cattribute_destroy(igraph_t *graph) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *als[3]= { &attr->gal, &attr->val, &attr->eal }; long int i, n, a; igraph_vector_t *num; igraph_strvector_t *str; igraph_vector_bool_t *boolvec; igraph_attribute_record_t *rec; for (a=0; a<3; a++) { n=igraph_vector_ptr_size(als[a]); for (i=0; itype == IGRAPH_ATTRIBUTE_NUMERIC) { num=(igraph_vector_t*)rec->value; igraph_vector_destroy(num); igraph_free(num); } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { str=(igraph_strvector_t*)rec->value; igraph_strvector_destroy(str); igraph_free(str); } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { boolvec=(igraph_vector_bool_t*)rec->value; igraph_vector_bool_destroy(boolvec); igraph_free(boolvec); } igraph_free((char*)rec->name); igraph_free(rec); } } } igraph_vector_ptr_destroy(&attr->gal); igraph_vector_ptr_destroy(&attr->val); igraph_vector_ptr_destroy(&attr->eal); igraph_free(graph->attr); graph->attr=0; } /* Almost the same as destroy, but we might have null pointers */ void igraph_i_cattribute_copy_free(igraph_i_cattributes_t *attr) { igraph_vector_ptr_t *als[3] = { &attr->gal, &attr->val, &attr->eal }; long int i, n, a; igraph_vector_t *num; igraph_strvector_t *str; igraph_attribute_record_t *rec; for (a=0; a<3; a++) { n=igraph_vector_ptr_size(als[a]); for (i=0; itype == IGRAPH_ATTRIBUTE_NUMERIC) { num=(igraph_vector_t*)rec->value; igraph_vector_destroy(num); igraph_free(num); } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { str=(igraph_strvector_t*)rec->value; igraph_strvector_destroy(str); igraph_free(str); } igraph_free((char*)rec->name); igraph_free(rec); } } } /* No reference counting here. If you use attributes in C you should know what you're doing. */ int igraph_i_cattribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { igraph_i_cattributes_t *attrfrom=from->attr, *attrto; igraph_vector_ptr_t *alto[3], *alfrom[3]={ &attrfrom->gal, &attrfrom->val, &attrfrom->eal }; long int i, n, a; igraph_bool_t copy[3] = { ga, va, ea }; to->attr=attrto=igraph_Calloc(1, igraph_i_cattributes_t); if (!attrto) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, attrto); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->gal, 0); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->val, 0); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->eal, 0); IGRAPH_FINALLY_CLEAN(3); IGRAPH_FINALLY(igraph_i_cattribute_copy_free, attrto); alto[0]=&attrto->gal; alto[1]=&attrto->val; alto[2]=&attrto->eal; for (a=0; a<3; a++) { if (copy[a]) { n=igraph_vector_ptr_size(alfrom[a]); IGRAPH_CHECK(igraph_vector_ptr_resize(alto[a], n)); igraph_vector_ptr_null(alto[a]); for (i=0; iattr; igraph_vector_ptr_t *val=&attr->val; long int length=igraph_vector_ptr_size(val); long int nattrno=nattr==NULL ? 0 : igraph_vector_ptr_size(nattr); long int origlen=igraph_vcount(graph)-nv; long int newattrs=0, i; igraph_vector_t news; /* First add the new attributes if any */ newattrs=0; IGRAPH_VECTOR_INIT_FINALLY(&news, 0); for (i=0; iname; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, nname, &j); if (!l) { newattrs++; IGRAPH_CHECK(igraph_vector_push_back(&news, i)); } else { /* check types */ if (nattr_entry->type != ((igraph_attribute_record_t*)VECTOR(*val)[j])->type) { IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL); } } } /* Add NA/empty string vectors for the existing vertices */ if (newattrs != 0) { for (i=0; itype; if (!newrec) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newrec); newrec->type=type; newrec->name=strdup(tmp->name); if (!newrec->name) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)newrec->name); if (type==IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *newnum=igraph_Calloc(1, igraph_vector_t); if (!newnum) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newnum); IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); newrec->value=newnum; igraph_vector_fill(newnum, IGRAPH_NAN); } else if (type==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *newstr=igraph_Calloc(1, igraph_strvector_t); if (!newstr) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newstr); IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); newrec->value=newstr; } IGRAPH_CHECK(igraph_vector_ptr_push_back(val, newrec)); IGRAPH_FINALLY_CLEAN(4); } length=igraph_vector_ptr_size(val); } /* Now append the new values */ for (i=0; iname; long int j; igraph_bool_t l=0; if (nattr) { l=igraph_i_cattribute_find(nattr, name, &j); } if (l) { /* This attribute is present in nattr */ igraph_vector_t *oldnum, *newnum; igraph_strvector_t *oldstr, *newstr; newrec=VECTOR(*nattr)[j]; oldnum=(igraph_vector_t*)oldrec->value; newnum=(igraph_vector_t*)newrec->value; oldstr=(igraph_strvector_t*)oldrec->value; newstr=(igraph_strvector_t*)newrec->value; if (oldrec->type != newrec->type) { IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL); } switch (oldrec->type) { case IGRAPH_ATTRIBUTE_NUMERIC: if (nv != igraph_vector_size(newnum)) { IGRAPH_ERROR("Invalid numeric attribute length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_append(oldnum, newnum)); break; case IGRAPH_ATTRIBUTE_STRING: if (nv != igraph_strvector_size(newstr)) { IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_strvector_append(oldstr, newstr)); break; default: IGRAPH_WARNING("Invalid attribute type"); break; } } else { /* No such attribute, append NA's */ igraph_vector_t *oldnum=(igraph_vector_t *)oldrec->value; igraph_strvector_t *oldstr=(igraph_strvector_t*)oldrec->value; switch (oldrec->type) { case IGRAPH_ATTRIBUTE_NUMERIC: IGRAPH_CHECK(igraph_vector_resize(oldnum, origlen+nv)); for (j=origlen; jname); if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *numv= (igraph_vector_t*) rec->value; igraph_vector_destroy(numv); igraph_Free(numv); } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strv= (igraph_strvector_t*) rec->value; igraph_strvector_destroy(strv); igraph_Free(strv); } igraph_Free(rec); } igraph_vector_ptr_clear(v); } int igraph_i_cattribute_permute_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx) { if (graph==newgraph) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int valno=igraph_vector_ptr_size(val); long int i; for (i=0; itype; igraph_vector_t *num, *newnum; igraph_strvector_t *str, *newstr; switch (type) { case IGRAPH_ATTRIBUTE_NUMERIC: num=(igraph_vector_t*) oldrec->value; newnum=igraph_Calloc(1, igraph_vector_t); if (!newnum) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); igraph_vector_index(num, newnum, idx); oldrec->value=newnum; igraph_vector_destroy(num); igraph_Free(num); IGRAPH_FINALLY_CLEAN(1); break; case IGRAPH_ATTRIBUTE_STRING: str=(igraph_strvector_t*)oldrec->value; newstr=igraph_Calloc(1, igraph_strvector_t); if (!newstr) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); IGRAPH_FINALLY(igraph_strvector_destroy, newstr); igraph_strvector_index(str, newstr, idx); oldrec->value=newstr; igraph_strvector_destroy(str); igraph_Free(str); IGRAPH_FINALLY_CLEAN(1); break; default: IGRAPH_WARNING("Unknown edge attribute ignored"); } } } else { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int valno=igraph_vector_ptr_size(val); long int i; /* New vertex attributes */ igraph_i_cattributes_t *new_attr=newgraph->attr; igraph_vector_ptr_t *new_val=&new_attr->val; if (igraph_vector_ptr_size(new_val) != 0) { IGRAPH_ERROR("Vertex attributes were already copied", IGRAPH_EATTRIBUTES); } IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val); for (i=0; itype; igraph_vector_t *num, *newnum; igraph_strvector_t *str, *newstr; /* The record itself */ igraph_attribute_record_t *new_rec= igraph_Calloc(1, igraph_attribute_record_t); if (!new_rec) { IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM); } new_rec->name = strdup(oldrec->name); new_rec->type = oldrec->type; VECTOR(*new_val)[i]=new_rec; /* The data */ switch (type) { case IGRAPH_ATTRIBUTE_NUMERIC: num=(igraph_vector_t*)oldrec->value; newnum=igraph_Calloc(1, igraph_vector_t); if (!newnum) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); igraph_vector_index(num, newnum, idx); new_rec->value=newnum; IGRAPH_FINALLY_CLEAN(1); break; case IGRAPH_ATTRIBUTE_STRING: str=(igraph_strvector_t*)oldrec->value; newstr=igraph_Calloc(1, igraph_strvector_t); if (!newstr) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); IGRAPH_FINALLY(igraph_strvector_destroy, newstr); igraph_strvector_index(str, newstr, idx); new_rec->value=newstr; IGRAPH_FINALLY_CLEAN(1); break; default: IGRAPH_WARNING("Unknown vertex attribute ignored"); } } } IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_real_t nan=IGRAPH_NAN; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); for (i=0; i 0 ? VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ] : nan; for (j=1; jvalue = newv; return 0; } int igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_real_t nan=IGRAPH_NAN; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); for (i=0; i 0 ? VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ] : nan; for (j=1; j m) { m=val; } } VECTOR(*newv)[i]=m; } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; return 0; } int igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_real_t nan=IGRAPH_NAN; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); RNG_BEGIN(); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_real_t nan=IGRAPH_NAN; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_real_t nan=IGRAPH_NAN; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv=oldrec->value; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_real_t nan=IGRAPH_NAN; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); for (i=0; i 0 ? 0.0 : nan; for (j=0; j0) { s=s/n; } VECTOR(*newv)[i]=s; } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; return 0; } typedef int igraph_cattributes_combine_num_t(const igraph_vector_t *input, igraph_real_t *output); typedef int igraph_cattributes_combine_str_t(const igraph_strvector_t *input, char **output); int igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, const igraph_vector_ptr_t *merges, igraph_cattributes_combine_num_t *func) { const igraph_vector_t *oldv=oldrec->value; long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_vector_t *newv=igraph_Calloc(1, igraph_vector_t); igraph_vector_t values; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); IGRAPH_VECTOR_INIT_FINALLY(&values, 0); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv=oldrec->value; long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_strvector_t *newv=igraph_Calloc(1, igraph_strvector_t); if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); IGRAPH_FINALLY(igraph_strvector_destroy, newv); RNG_BEGIN(); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv=oldrec->value; long int i, newlen=igraph_vector_ptr_size(merges); igraph_strvector_t *newv=igraph_Calloc(1, igraph_strvector_t); if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); IGRAPH_FINALLY(igraph_strvector_destroy, newv); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv=oldrec->value; long int i, newlen=igraph_vector_ptr_size(merges); igraph_strvector_t *newv=igraph_Calloc(1, igraph_strvector_t); if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); IGRAPH_FINALLY(igraph_strvector_destroy, newv); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv=oldrec->value; long int i, newlen=igraph_vector_ptr_size(merges); igraph_strvector_t *newv=igraph_Calloc(1, igraph_strvector_t); if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); IGRAPH_FINALLY(igraph_strvector_destroy, newv); for (i=0; ivalue = newv; return 0; } int igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, const igraph_vector_ptr_t *merges, igraph_cattributes_combine_str_t *func) { const igraph_strvector_t *oldv=oldrec->value; long int newlen=igraph_vector_ptr_size(merges); long int i; igraph_strvector_t *newv=igraph_Calloc(1, igraph_strvector_t); igraph_strvector_t values; if (!newv) { IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); IGRAPH_FINALLY(igraph_strvector_destroy, newv); IGRAPH_CHECK(igraph_strvector_init(newv, 0)); IGRAPH_FINALLY(igraph_strvector_destroy, &values); for (i=0; ivalue = newv; return 0; } int igraph_i_cattribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { igraph_i_cattributes_t *attr=graph->attr; igraph_i_cattributes_t *toattr=newgraph->attr; igraph_vector_ptr_t *val=&attr->val; igraph_vector_ptr_t *new_val=&toattr->val; long int valno=igraph_vector_ptr_size(val); long int i, j, keepno=0; int *TODO; void **funcs; TODO=igraph_Calloc(valno, int); if (!TODO) { IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, TODO); funcs=igraph_Calloc(valno, void*); if (!funcs) { IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, funcs); for (i=0; iname; igraph_attribute_combination_type_t todo; void *voidfunc; igraph_attribute_combination_query(comb, name, &todo, &voidfunc); TODO[i]=todo; funcs[i]=voidfunc; if (todo != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { keepno++; } } IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, keepno)); IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val); for (i=0, j=0; iname; igraph_attribute_combination_type_t todo= (igraph_attribute_combination_type_t) (TODO[i]); igraph_attribute_type_t type=oldrec->type; igraph_cattributes_combine_num_t *numfunc= (igraph_cattributes_combine_num_t*) funcs[i]; igraph_cattributes_combine_str_t *strfunc= (igraph_cattributes_combine_str_t*) funcs[i]; if (todo==IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || todo==IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { continue; } newrec=igraph_Calloc(1, igraph_attribute_record_t); if (!newrec) { IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); } newrec->name = strdup(name); newrec->type = type; VECTOR(*new_val)[j] = newrec; if (type==IGRAPH_ATTRIBUTE_NUMERIC) { switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, numfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_PROD: IGRAPH_CHECK(igraph_i_cattributes_cn_prod(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MIN: IGRAPH_CHECK(igraph_i_cattributes_cn_min(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MAX: IGRAPH_CHECK(igraph_i_cattributes_cn_max(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: IGRAPH_CHECK(igraph_i_cattributes_cn_random(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_FIRST: IGRAPH_CHECK(igraph_i_cattributes_cn_first(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_LAST: IGRAPH_CHECK(igraph_i_cattributes_cn_last(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MEAN: IGRAPH_CHECK(igraph_i_cattributes_cn_mean(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: IGRAPH_ERROR("Median calculation not implemented", IGRAPH_UNIMPLEMENTED); break; case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: IGRAPH_ERROR("Cannot concatenate numeric attributes", IGRAPH_EATTRCOMBINE); break; default: IGRAPH_ERROR("Unknown attribute_combination", IGRAPH_UNIMPLEMENTED); break; } } else if (type==IGRAPH_ATTRIBUTE_STRING) { switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, strfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_PROD: IGRAPH_ERROR("Cannot multiply strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MIN: IGRAPH_ERROR("Cannot find minimum of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MAX: IGRAPH_ERROR("Cannot find maximum of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MEAN: IGRAPH_ERROR("Cannot calculate mean of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: IGRAPH_ERROR("Cannot calculate median of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: IGRAPH_CHECK(igraph_i_cattributes_sn_random(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_FIRST: IGRAPH_CHECK(igraph_i_cattributes_sn_first(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_LAST: IGRAPH_CHECK(igraph_i_cattributes_sn_last(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: IGRAPH_CHECK(igraph_i_cattributes_sn_concat(oldrec, newrec, merges)); break; default: IGRAPH_ERROR("Unknown attribute_combination", IGRAPH_UNIMPLEMENTED); break; } } else { IGRAPH_ERROR("Unknown attribute type, this should not happen", IGRAPH_UNIMPLEMENTED); } j++; } igraph_free(funcs); igraph_free(TODO); IGRAPH_FINALLY_CLEAN(2); return 0; } /* void igraph_i_cattribute_delete_vertices(igraph_t *graph, */ /* const igraph_vector_t *eidx, */ /* const igraph_vector_t *vidx) { */ /* igraph_i_cattributes_t *attr=graph->attr; */ /* igraph_vector_ptr_t *val=&attr->val; */ /* igraph_vector_ptr_t *eal=&attr->eal; */ /* long int valno=igraph_vector_ptr_size(val); */ /* long int ealno=igraph_vector_ptr_size(eal); */ /* long int i; */ /* long int origlen, newlen; */ /* /\* Vertices *\/ */ /* origlen=igraph_vector_size(vidx); */ /* newlen=0; */ /* for (i=0; i0) { */ /* newlen++; */ /* } */ /* } */ /* for (i=0; itype; */ /* igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */ /* igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */ /* switch (type) { */ /* case IGRAPH_ATTRIBUTE_NUMERIC: */ /* igraph_vector_permdelete(num, vidx, origlen-newlen); */ /* break; */ /* case IGRAPH_ATTRIBUTE_STRING: */ /* igraph_strvector_permdelete(str, vidx, origlen-newlen); */ /* break; */ /* default: */ /* IGRAPH_WARNING("Unknown vertex attribute ignored"); */ /* } */ /* } */ /* /\* Edges *\/ */ /* origlen=igraph_vector_size(eidx); */ /* newlen=0; */ /* for (i=0; i0) { */ /* newlen++; */ /* } */ /* } */ /* for (i=0; itype; */ /* igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */ /* igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */ /* switch (type) { */ /* case IGRAPH_ATTRIBUTE_NUMERIC: */ /* igraph_vector_permdelete(num, eidx, origlen-newlen); */ /* break; */ /* case IGRAPH_ATTRIBUTE_STRING: */ /* igraph_strvector_permdelete(str, eidx, origlen-newlen); */ /* break; */ /* default: */ /* IGRAPH_WARNING("Unknown edge attribute ignored"); */ /* } */ /* } */ /* } */ int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, igraph_vector_ptr_t *nattr) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int ealno=igraph_vector_ptr_size(eal); long int ne=igraph_vector_size(edges)/2; long int origlen=igraph_ecount(graph)-ne; long int nattrno= nattr == 0 ? 0 : igraph_vector_ptr_size(nattr); igraph_vector_t news; long int newattrs, i; /* First add the new attributes if any */ newattrs=0; IGRAPH_VECTOR_INIT_FINALLY(&news, 0); for (i=0; iname; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, nname, &j); if (!l) { newattrs++; IGRAPH_CHECK(igraph_vector_push_back(&news, i)); } else { /* check types */ if (nattr_entry->type != ((igraph_attribute_record_t*)VECTOR(*eal)[j])->type) { IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL); } } } /* Add NA/empty string vectors for the existing vertices */ if (newattrs != 0) { for (i=0; itype; if (!newrec) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newrec); newrec->type=type; newrec->name=strdup(tmp->name); if (!newrec->name) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)newrec->name); if (type==IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *newnum=igraph_Calloc(1, igraph_vector_t); if (!newnum) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newnum); IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); newrec->value=newnum; igraph_vector_fill(newnum, IGRAPH_NAN); } else if (type==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *newstr=igraph_Calloc(1, igraph_strvector_t); if (!newstr) { IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newstr); IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); newrec->value=newstr; } IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, newrec)); IGRAPH_FINALLY_CLEAN(4); } ealno=igraph_vector_ptr_size(eal); } /* Now append the new values */ for (i=0; iname; long int j; igraph_bool_t l=0; if (nattr) { l=igraph_i_cattribute_find(nattr, name, &j); } if (l) { /* This attribute is present in nattr */ igraph_vector_t *oldnum, *newnum; igraph_strvector_t *oldstr, *newstr; newrec=VECTOR(*nattr)[j]; oldnum=(igraph_vector_t*)oldrec->value; newnum=(igraph_vector_t*)newrec->value; oldstr=(igraph_strvector_t*)oldrec->value; newstr=(igraph_strvector_t*)newrec->value; if (oldrec->type != newrec->type) { IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL); } switch (oldrec->type) { case IGRAPH_ATTRIBUTE_NUMERIC: if (ne != igraph_vector_size(newnum)) { IGRAPH_ERROR("Invalid numeric attribute length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_append(oldnum, newnum)); break; case IGRAPH_ATTRIBUTE_STRING: if (ne != igraph_strvector_size(newstr)) { IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_strvector_append(oldstr, newstr)); break; default: IGRAPH_WARNING("Invalid attribute type"); break; } } else { /* No such attribute, append NA's */ igraph_vector_t *oldnum=(igraph_vector_t *)oldrec->value; igraph_strvector_t *oldstr=(igraph_strvector_t*)oldrec->value; switch (oldrec->type) { case IGRAPH_ATTRIBUTE_NUMERIC: IGRAPH_CHECK(igraph_vector_resize(oldnum, origlen+ne)); for (j=origlen; jattr; */ /* igraph_vector_ptr_t *eal=&attr->eal; */ /* long int ealno=igraph_vector_ptr_size(eal); */ /* long int i; */ /* long int origlen=igraph_vector_size(idx), newlen; */ /* newlen=0; */ /* for (i=0; i0) { */ /* newlen++; */ /* } */ /* } */ /* for (i=0; itype; */ /* igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */ /* igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */ /* switch (type) { */ /* case IGRAPH_ATTRIBUTE_NUMERIC: */ /* igraph_vector_permdelete(num, idx, origlen-newlen); */ /* break; */ /* case IGRAPH_ATTRIBUTE_STRING: */ /* igraph_strvector_permdelete(str, idx, origlen-newlen); */ /* break; */ /* default: */ /* IGRAPH_WARNING("Unknown edge attribute ignored"); */ /* } */ /* } */ /* } */ int igraph_i_cattribute_permute_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx) { if (graph == newgraph) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int ealno=igraph_vector_ptr_size(eal); long int i; for (i=0; itype; igraph_vector_t *num, *newnum; igraph_strvector_t *str, *newstr; switch (type) { case IGRAPH_ATTRIBUTE_NUMERIC: num=(igraph_vector_t*) oldrec->value; newnum=igraph_Calloc(1, igraph_vector_t); if (!newnum) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); igraph_vector_index(num, newnum, idx); oldrec->value=newnum; igraph_vector_destroy(num); igraph_Free(num); IGRAPH_FINALLY_CLEAN(1); break; case IGRAPH_ATTRIBUTE_STRING: str=(igraph_strvector_t*)oldrec->value; newstr=igraph_Calloc(1, igraph_strvector_t); if (!newstr) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); IGRAPH_FINALLY(igraph_strvector_destroy, newstr); igraph_strvector_index(str, newstr, idx); oldrec->value=newstr; igraph_strvector_destroy(str); igraph_Free(str); IGRAPH_FINALLY_CLEAN(1); break; default: IGRAPH_WARNING("Unknown edge attribute ignored"); } } } else { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int ealno=igraph_vector_ptr_size(eal); long int i; /* New edge attributes */ igraph_i_cattributes_t *new_attr=newgraph->attr; igraph_vector_ptr_t *new_eal=&new_attr->eal; IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno)); IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal); for (i=0; itype; igraph_vector_t *num, *newnum; igraph_strvector_t *str, *newstr; /* The record itself */ igraph_attribute_record_t *new_rec= igraph_Calloc(1, igraph_attribute_record_t); if (!new_rec) { IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM); } new_rec->name = strdup(oldrec->name); new_rec->type = oldrec->type; VECTOR(*new_eal)[i] = new_rec; switch (type) { case IGRAPH_ATTRIBUTE_NUMERIC: num=(igraph_vector_t*) oldrec->value; newnum=igraph_Calloc(1, igraph_vector_t); if (!newnum) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); igraph_vector_index(num, newnum, idx); new_rec->value=newnum; IGRAPH_FINALLY_CLEAN(1); break; case IGRAPH_ATTRIBUTE_STRING: str=(igraph_strvector_t*)oldrec->value; newstr=igraph_Calloc(1, igraph_strvector_t); if (!newstr) { IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); IGRAPH_FINALLY(igraph_strvector_destroy, newstr); igraph_strvector_index(str, newstr, idx); new_rec->value=newstr; IGRAPH_FINALLY_CLEAN(1); break; default: IGRAPH_WARNING("Unknown edge attribute ignored"); } } IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_cattribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { igraph_i_cattributes_t *attr=graph->attr; igraph_i_cattributes_t *toattr=newgraph->attr; igraph_vector_ptr_t *eal=&attr->eal; igraph_vector_ptr_t *new_eal=&toattr->eal; long int ealno=igraph_vector_ptr_size(eal); long int i, j, keepno=0; int *TODO; void **funcs; TODO=igraph_Calloc(ealno, int); if (!TODO) { IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, TODO); funcs=igraph_Calloc(ealno, void*); if (!funcs) { IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, funcs); for (i=0; iname; igraph_attribute_combination_type_t todo; void *voidfunc; igraph_attribute_combination_query(comb, name, &todo, &voidfunc); TODO[i]=todo; funcs[i]=voidfunc; if (todo != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { keepno++; } } IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, keepno)); IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal); for (i=0, j=0; iname; igraph_attribute_combination_type_t todo= (igraph_attribute_combination_type_t) (TODO[i]); igraph_attribute_type_t type=oldrec->type; igraph_cattributes_combine_num_t *numfunc= (igraph_cattributes_combine_num_t*) funcs[i]; igraph_cattributes_combine_str_t *strfunc= (igraph_cattributes_combine_str_t*) funcs[i]; if (todo==IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || todo==IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { continue; } newrec=igraph_Calloc(1, igraph_attribute_record_t); if (!newrec) { IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); } newrec->name = strdup(name); newrec->type = type; VECTOR(*new_eal)[j] = newrec; if (type==IGRAPH_ATTRIBUTE_NUMERIC) { switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, numfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_PROD: IGRAPH_CHECK(igraph_i_cattributes_cn_prod(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MIN: IGRAPH_CHECK(igraph_i_cattributes_cn_min(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MAX: IGRAPH_CHECK(igraph_i_cattributes_cn_max(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: IGRAPH_CHECK(igraph_i_cattributes_cn_random(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_FIRST: IGRAPH_CHECK(igraph_i_cattributes_cn_first(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_LAST: IGRAPH_CHECK(igraph_i_cattributes_cn_last(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MEAN: IGRAPH_CHECK(igraph_i_cattributes_cn_mean(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: IGRAPH_ERROR("Median calculation not implemented", IGRAPH_UNIMPLEMENTED); break; case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: IGRAPH_ERROR("Cannot concatenate numeric attributes", IGRAPH_EATTRCOMBINE); break; default: IGRAPH_ERROR("Unknown attribute_combination", IGRAPH_UNIMPLEMENTED); break; } } else if (type==IGRAPH_ATTRIBUTE_STRING) { switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, strfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_PROD: IGRAPH_ERROR("Cannot multiply strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MIN: IGRAPH_ERROR("Cannot find minimum of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MAX: IGRAPH_ERROR("Cannot find maximum of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MEAN: IGRAPH_ERROR("Cannot calculate mean of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: IGRAPH_ERROR("Cannot calculate median of strings", IGRAPH_EATTRCOMBINE); break; case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: IGRAPH_CHECK(igraph_i_cattributes_sn_random(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_FIRST: IGRAPH_CHECK(igraph_i_cattributes_sn_first(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_LAST: IGRAPH_CHECK(igraph_i_cattributes_sn_last(oldrec, newrec, merges)); break; case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: IGRAPH_CHECK(igraph_i_cattributes_sn_concat(oldrec, newrec, merges)); break; default: IGRAPH_ERROR("Unknown attribute_combination", IGRAPH_UNIMPLEMENTED); break; } } else { IGRAPH_ERROR("Unknown attribute type, this should not happen", IGRAPH_UNIMPLEMENTED); } j++; } igraph_free(funcs); igraph_free(TODO); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_cattribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, igraph_vector_t *gtypes, igraph_strvector_t *vnames, igraph_vector_t *vtypes, igraph_strvector_t *enames, igraph_vector_t *etypes) { igraph_strvector_t *names[3] = { gnames, vnames, enames }; igraph_vector_t *types[3] = { gtypes, vtypes, etypes }; igraph_i_cattributes_t *at=graph->attr; igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal }; long int i,j; for (i=0; i<3; i++) { igraph_strvector_t *n=names[i]; igraph_vector_t *t=types[i]; igraph_vector_ptr_t *al=attr[i]; long int len=igraph_vector_ptr_size(al); if (n) { IGRAPH_CHECK(igraph_strvector_resize(n, len)); } if (t) { IGRAPH_CHECK(igraph_vector_resize(t, len)); } for (j=0; jname; igraph_attribute_type_t type=rec->type; if (n) { IGRAPH_CHECK(igraph_strvector_set(n, j, name)); } if (t) { VECTOR(*t)[j]=type; } } } return 0; } igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name) { igraph_i_cattributes_t *at=graph->attr; igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal }; long int attrnum; switch (type) { case IGRAPH_ATTRIBUTE_GRAPH: attrnum=0; break; case IGRAPH_ATTRIBUTE_VERTEX: attrnum=1; break; case IGRAPH_ATTRIBUTE_EDGE: attrnum=2; break; default: IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL); break; } return igraph_i_cattribute_find(attr[attrnum], name, 0); } int igraph_i_cattribute_gettype(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name) { long int attrnum; igraph_attribute_record_t *rec; igraph_i_cattributes_t *at=graph->attr; igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal }; igraph_vector_ptr_t *al; long int j; igraph_bool_t l=0; switch (elemtype) { case IGRAPH_ATTRIBUTE_GRAPH: attrnum=0; break; case IGRAPH_ATTRIBUTE_VERTEX: attrnum=1; break; case IGRAPH_ATTRIBUTE_EDGE: attrnum=2; break; default: IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL); break; } al=attr[attrnum]; l=igraph_i_cattribute_find(al, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*al)[j]; *type=rec->type; return 0; } int igraph_i_cattribute_get_numeric_graph_attr(const igraph_t *graph, const char *name, igraph_vector_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_attribute_record_t *rec; igraph_vector_t *num; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*gal)[j]; num=(igraph_vector_t*)rec->value; IGRAPH_CHECK(igraph_vector_resize(value, 1)); VECTOR(*value)[0]=VECTOR(*num)[0]; return 0; } int igraph_i_cattribute_get_bool_graph_attr(const igraph_t *graph, const char *name, igraph_vector_bool_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_attribute_record_t *rec; igraph_vector_bool_t *log; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*gal)[j]; log=(igraph_vector_bool_t*)rec->value; IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); VECTOR(*value)[0]=VECTOR(*log)[0]; return 0; } int igraph_i_cattribute_get_string_graph_attr(const igraph_t *graph, const char *name, igraph_strvector_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_attribute_record_t *rec; igraph_strvector_t *str; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*gal)[j]; str=(igraph_strvector_t*)rec->value; IGRAPH_CHECK(igraph_strvector_resize(value, 1)); IGRAPH_CHECK(igraph_strvector_set(value, 0, STR(*str,0))); return 0; } int igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_attribute_record_t *rec; igraph_vector_t *num; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*val)[j]; num=(igraph_vector_t*)rec->value; if (igraph_vs_is_all(&vs)) { igraph_vector_clear(value); IGRAPH_CHECK(igraph_vector_append(value, num)); } else { igraph_vit_t it; long int i=0; IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); IGRAPH_FINALLY(igraph_vit_destroy, &it); IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_VIT_SIZE(it))); for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) { long int v=IGRAPH_VIT_GET(it); VECTOR(*value)[i]=VECTOR(*num)[v]; } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_bool_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_attribute_record_t *rec; igraph_vector_bool_t *log; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*val)[j]; log=(igraph_vector_bool_t*)rec->value; if (igraph_vs_is_all(&vs)) { igraph_vector_bool_clear(value); IGRAPH_CHECK(igraph_vector_bool_append(value, log)); } else { igraph_vit_t it; long int i=0; IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); IGRAPH_FINALLY(igraph_vit_destroy, &it); IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_VIT_SIZE(it))); for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) { long int v=IGRAPH_VIT_GET(it); VECTOR(*value)[i]=VECTOR(*log)[v]; } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_cattribute_get_string_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_strvector_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_attribute_record_t *rec; igraph_strvector_t *str; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*val)[j]; str=(igraph_strvector_t*)rec->value; if (igraph_vs_is_all(&vs)) { igraph_strvector_resize(value, 0); IGRAPH_CHECK(igraph_strvector_append(value, str)); } else { igraph_vit_t it; long int i=0; IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); IGRAPH_FINALLY(igraph_vit_destroy, &it); IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_VIT_SIZE(it))); for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) { long int v=IGRAPH_VIT_GET(it); char *s; igraph_strvector_get(str, v, &s); IGRAPH_CHECK(igraph_strvector_set(value, i, s)); } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_cattribute_get_numeric_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_attribute_record_t *rec; igraph_vector_t *num; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*eal)[j]; num=(igraph_vector_t*)rec->value; if (igraph_es_is_all(&es)) { igraph_vector_clear(value); IGRAPH_CHECK(igraph_vector_append(value, num)); } else { igraph_eit_t it; long int i=0; IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_EIT_SIZE(it))); for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) { long int e=IGRAPH_EIT_GET(it); VECTOR(*value)[i]=VECTOR(*num)[e]; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_cattribute_get_string_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_strvector_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_attribute_record_t *rec; igraph_strvector_t *str; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*eal)[j]; str=(igraph_strvector_t*)rec->value; if (igraph_es_is_all(&es)) { igraph_strvector_resize(value, 0); IGRAPH_CHECK(igraph_strvector_append(value, str)); } else { igraph_eit_t it; long int i=0; IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_EIT_SIZE(it))); for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) { long int e=IGRAPH_EIT_GET(it); char *s; igraph_strvector_get(str, e, &s); IGRAPH_CHECK(igraph_strvector_set(value, i, s)); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_cattribute_get_bool_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_attribute_record_t *rec; igraph_vector_bool_t *log; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (!l) { IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec=VECTOR(*eal)[j]; log=(igraph_vector_bool_t*)rec->value; if (igraph_es_is_all(&es)) { igraph_vector_bool_clear(value); IGRAPH_CHECK(igraph_vector_bool_append(value, log)); } else { igraph_eit_t it; long int i=0; IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_EIT_SIZE(it))); for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) { long int e=IGRAPH_EIT_GET(it); VECTOR(*value)[i]=VECTOR(*log)[e]; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } /* -------------------------------------- */ const igraph_attribute_table_t igraph_cattribute_table={ &igraph_i_cattribute_init, &igraph_i_cattribute_destroy, &igraph_i_cattribute_copy, &igraph_i_cattribute_add_vertices, &igraph_i_cattribute_permute_vertices, &igraph_i_cattribute_combine_vertices, &igraph_i_cattribute_add_edges, &igraph_i_cattribute_permute_edges, &igraph_i_cattribute_combine_edges, &igraph_i_cattribute_get_info, &igraph_i_cattribute_has_attr, &igraph_i_cattribute_gettype, &igraph_i_cattribute_get_numeric_graph_attr, &igraph_i_cattribute_get_string_graph_attr, &igraph_i_cattribute_get_bool_graph_attr, &igraph_i_cattribute_get_numeric_vertex_attr, &igraph_i_cattribute_get_string_vertex_attr, &igraph_i_cattribute_get_bool_vertex_attr, &igraph_i_cattribute_get_numeric_edge_attr, &igraph_i_cattribute_get_string_edge_attr, &igraph_i_cattribute_get_bool_edge_attr }; /* -------------------------------------- */ /** * \section cattributes * There is an experimental attribute handler that can be used * from C code. In this section we show how this works. This attribute * handler is by default not attached (the default is no attribute * handler), so we first need to attach it: * * igraph_i_set_attribute_table(&igraph_cattribute_table); * * * Now the attribute functions are available. Please note that * the attribute handler must be attached before you call any other * igraph functions, otherwise you might end up with graphs without * attributes and an active attribute handler, which might cause * unexpected program behaviour. The rule is that you attach the * attribute handler in the beginning of your * main() and never touch it again. (Detaching * the attribute handler might lead to memory leaks.) * * It is not currently possible to have attribute handlers on a * per-graph basis. All graphs in an application must be managed with * the same attribute handler. (Including the default case when there * is no attribute handler at all. * * The C attribute handler supports attaching real numbers and * character strings as attributes. No vectors are allowed, ie. every * vertex might have an attribute called name, but it is * not possible to have a coords graph (or other) * attribute which is a vector of numbers. * * \example examples/simple/cattributes.c * \example examples/simple/cattributes2.c * \example examples/simple/cattributes3.c * \example examples/simple/cattributes4.c */ /** * \function igraph_cattribute_GAN * Query a numeric graph attribute. * * Returns the value of the given numeric graph attribute. * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute to query. * \return The value of the attribute. * * \sa \ref GAN for a simpler interface. * * Time complexity: O(Ag), the number of graph attributes. */ igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_attribute_record_t *rec; igraph_vector_t *num; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*gal)[j]; num=(igraph_vector_t*)rec->value; return VECTOR(*num)[0]; } /** * \function igraph_cattribute_GAB * Query a boolean graph attribute. * * Returns the value of the given numeric graph attribute. * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute to query. * \return The value of the attribute. * * \sa \ref GAB for a simpler interface. * * Time complexity: O(Ag), the number of graph attributes. */ igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_attribute_record_t *rec; igraph_vector_bool_t *log; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*gal)[j]; log=(igraph_vector_bool_t*)rec->value; return VECTOR(*log)[0]; } /** * \function igraph_cattribute_GAS * Query a string graph attribute. * * Returns a const pointer to the string graph attribute * specified in \p name. * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute to query. * \return The value of the attribute. * * \sa \ref GAS for a simpler interface. * * Time complexity: O(Ag), the number of graph attributes. */ const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_attribute_record_t *rec; igraph_strvector_t *str; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*gal)[j]; str=(igraph_strvector_t*)rec->value; return STR(*str, 0); } /** * \function igraph_cattribute_VAN * Query a numeric vertex attribute. * * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute. * \param vid The id of the queried vertex. * \return The value of the attribute. * * \sa \ref VAN macro for a simpler interface. * * Time complexity: O(Av), the number of vertex attributes. */ igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name, igraph_integer_t vid) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_attribute_record_t *rec; igraph_vector_t *num; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*val)[j]; num=(igraph_vector_t*)rec->value; return VECTOR(*num)[(long int)vid]; } /** * \function igraph_cattribute_VAB * Query a boolean vertex attribute. * * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute. * \param vid The id of the queried vertex. * \return The value of the attribute. * * \sa \ref VAB macro for a simpler interface. * * Time complexity: O(Av), the number of vertex attributes. */ igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name, igraph_integer_t vid) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_attribute_record_t *rec; igraph_vector_bool_t *log; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*val)[j]; log=(igraph_vector_bool_t*)rec->value; return VECTOR(*log)[(long int)vid]; } /** * \function igraph_cattribute_VAS * Query a string vertex attribute. * * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute. * \param vid The id of the queried vertex. * \return The value of the attribute. * * \sa The macro \ref VAS for a simpler interface. * * Time complexity: O(Av), the number of vertex attributes. */ const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name, igraph_integer_t vid) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_attribute_record_t *rec; igraph_strvector_t *str; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*val)[j]; str=(igraph_strvector_t*)rec->value; return STR(*str, (long int)vid); } /** * \function igraph_cattribute_EAN * Query a numeric edge attribute. * * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute. * \param eid The id of the queried edge. * \return The value of the attribute. * * \sa \ref EAN for an easier interface. * * Time complexity: O(Ae), the number of edge attributes. */ igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name, igraph_integer_t eid) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_attribute_record_t *rec; igraph_vector_t *num; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*eal)[j]; num=(igraph_vector_t*)rec->value; return VECTOR(*num)[(long int)eid]; } /** * \function igraph_cattribute_EAB * Query a boolean edge attribute. * * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute. * \param eid The id of the queried edge. * \return The value of the attribute. * * \sa \ref EAB for an easier interface. * * Time complexity: O(Ae), the number of edge attributes. */ igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name, igraph_integer_t eid) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_attribute_record_t *rec; igraph_vector_bool_t *log; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*eal)[j]; log=(igraph_vector_bool_t*)rec->value; return VECTOR(*log)[(long int)eid]; } /** * \function igraph_cattribute_EAS * Query a string edge attribute. * * The attribute must exist, otherwise an error is triggered. * \param graph The input graph. * \param name The name of the attribute. * \param eid The id of the queried edge. * \return The value of the attribute. * * \se \ref EAS if you want to type less. * * Time complexity: O(Ae), the number of edge attributes. */ const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, igraph_integer_t eid) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_attribute_record_t *rec; igraph_strvector_t *str; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (!l) { igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); return 0; } rec=VECTOR(*eal)[j]; str=(igraph_strvector_t*)rec->value; return STR(*str, (long int)eid); } /** * \function igraph_cattribute_VANV * Query a numeric vertex attribute for many vertices * * \param graph The input graph. * \param name The name of the attribute. * \param vids The vertices to query. * \param result Pointer to an initialized vector, the result is * stored here. It will be resized, if needed. * \return Error code. * * Time complexity: O(v), where v is the number of vertices in 'vids'. */ int igraph_cattribute_VANV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_t *result) { return igraph_i_cattribute_get_numeric_vertex_attr(graph, name, vids, result); } /** * \function igraph_cattribute_VABV * Query a boolean vertex attribute for many vertices * * \param graph The input graph. * \param name The name of the attribute. * \param vids The vertices to query. * \param result Pointer to an initialized boolean vector, the result is * stored here. It will be resized, if needed. * \return Error code. * * Time complexity: O(v), where v is the number of vertices in 'vids'. */ int igraph_cattribute_VABV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_bool_t *result) { return igraph_i_cattribute_get_bool_vertex_attr(graph, name, vids, result); } /** * \function igraph_cattribute_EANV * Query a numeric edge attribute for many edges * * \param graph The input graph. * \param name The name of the attribute. * \param eids The edges to query. * \param result Pointer to an initialized vector, the result is * stored here. It will be resized, if needed. * \return Error code. * * Time complexity: O(e), where e is the number of edges in 'eids'. */ int igraph_cattribute_EANV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_t *result) { return igraph_i_cattribute_get_numeric_edge_attr(graph, name, eids, result); } /** * \function igraph_cattribute_EABV * Query a boolean edge attribute for many edges * * \param graph The input graph. * \param name The name of the attribute. * \param eids The edges to query. * \param result Pointer to an initialized boolean vector, the result is * stored here. It will be resized, if needed. * \return Error code. * * Time complexity: O(e), where e is the number of edges in 'eids'. */ int igraph_cattribute_EABV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_bool_t *result) { return igraph_i_cattribute_get_bool_edge_attr(graph, name, eids, result); } /** * \function igraph_cattribute_VASV * Query a string vertex attribute for many vertices * * \param graph The input graph. * \param name The name of the attribute. * \param vids The vertices to query. * \param result Pointer to an initialized string vector, the result * is stored here. It will be resized, if needed. * \return Error code. * * Time complexity: O(v), where v is the number of vertices in 'vids'. * (We assume that the string attributes have a bounded length.) */ int igraph_cattribute_VASV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_strvector_t *result) { return igraph_i_cattribute_get_string_vertex_attr(graph, name, vids, result); } /** * \function igraph_cattribute_EASV * Query a string edge attribute for many edges * * \param graph The input graph. * \param name The name of the attribute. * \param vids The edges to query. * \param result Pointer to an initialized string vector, the result * is stored here. It will be resized, if needed. * \return Error code. * * Time complexity: O(e), where e is the number of edges in * 'eids'. (We assume that the string attributes have a bounded length.) */ int igraph_cattribute_EASV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_strvector_t *result) { return igraph_i_cattribute_get_string_edge_attr(graph, name, eids, result); } /** * \function igraph_cattribute_list * List all attributes * * See \ref igraph_attribute_type_t for the various attribute types. * \param graph The input graph. * \param gnames String vector, the names of the graph attributes. * \param gtypes Numeric vector, the types of the graph attributes. * \param vnames String vector, the names of the vertex attributes. * \param vtypes Numeric vector, the types of the vertex attributes. * \param enames String vector, the names of the edge attributes. * \param etypes Numeric vector, the types of the edge attributes. * \return Error code. * * Naturally, the string vector with the attribute names and the * numeric vector with the attribute types are in the right order, * i.e. the first name corresponds to the first type, etc. * * Time complexity: O(Ag+Av+Ae), the number of all attributes. */ int igraph_cattribute_list(const igraph_t *graph, igraph_strvector_t *gnames, igraph_vector_t *gtypes, igraph_strvector_t *vnames, igraph_vector_t *vtypes, igraph_strvector_t *enames, igraph_vector_t *etypes) { return igraph_i_cattribute_get_info(graph, gnames, gtypes, vnames, vtypes, enames, etypes); } /** * \function igraph_cattribute_has_attr * Checks whether a (graph, vertex or edge) attribute exists * * \param graph The graph. * \param type The type of the attribute, \c IGRAPH_ATTRIBUTE_GRAPH, * \c IGRAPH_ATTRIBUTE_VERTEX or \c IGRAPH_ATTRIBUTE_EDGE. * \param name Character constant, the name of the attribute. * \return Logical value, TRUE if the attribute exists, FALSE otherwise. * * Time complexity: O(A), the number of (graph, vertex or edge) * attributes, assuming attribute names are not too long. */ igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name) { return igraph_i_cattribute_has_attr(graph, type, name); } /** * \function igraph_cattribute_GAN_set * Set a numeric graph attribute * * \param graph The graph. * \param name Name of the graph attribute. If there is no such * attribute yet, then it will be added. * \param value The (new) value of the graph attribute. * \return Error code. * * \se \ref SETGAN if you want to type less. * * Time complexity: O(1). */ int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, igraph_real_t value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*gal)[j]; if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_t *num=(igraph_vector_t *)rec->value; VECTOR(*num)[0]=value; } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_t *num; if (!rec) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; num=igraph_Calloc(1, igraph_vector_t); if (!num) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); IGRAPH_VECTOR_INIT_FINALLY(num, 1); VECTOR(*num)[0]=value; rec->value=num; IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_GAB_set * Set a boolean graph attribute * * \param graph The graph. * \param name Name of the graph attribute. If there is no such * attribute yet, then it will be added. * \param value The (new) value of the graph attribute. * \return Error code. * * \se \ref SETGAN if you want to type less. * * Time complexity: O(1). */ int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, igraph_bool_t value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*gal)[j]; if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_bool_t *log=(igraph_vector_bool_t *)rec->value; VECTOR(*log)[0]=value; } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_bool_t *log; if (!rec) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_BOOLEAN; log=igraph_Calloc(1, igraph_vector_bool_t); if (!log) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); IGRAPH_CHECK(igraph_vector_bool_init(log, 1)); IGRAPH_FINALLY(igraph_vector_bool_destroy, log); VECTOR(*log)[0]=value; rec->value=log; IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_GAS_set * Set a string graph attribute. * * \param graph The graph. * \param name Name of the graph attribute. If there is no such * attribute yet, then it will be added. * \param value The (new) value of the graph attribute. It will be * copied. * \return Error code. * * \se \ref SETGAS if you want to type less. * * Time complexity: O(1). */ int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, const char *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*gal)[j]; if (rec->type != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_strvector_t *str=(igraph_strvector_t*)rec->value; IGRAPH_CHECK(igraph_strvector_set(str, 0, value)); } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_strvector_t *str; if (!rec) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_STRING; str=igraph_Calloc(1, igraph_strvector_t); if (!str) { IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, 1); IGRAPH_CHECK(igraph_strvector_set(str, 0, value)); rec->value=str; IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_VAN_set * Set a numeric vertex attribute * * The attribute will be added if not present already. If present it * will be overwritten. The same \p value is set for all vertices * included in \p vid. * \param graph The graph. * \param name Name of the attribute. * \param vid Vertices for which to set the attribute. * \param value The (new) value of the attribute. * \return Error code. * * \sa \ref SETVAN for a simpler way. * * Time complexity: O(n), the number of vertices if the attribute is * new, O(|vid|) otherwise. */ int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_real_t value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*val)[j]; if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_t *num=(igraph_vector_t*)rec->value; VECTOR(*num)[(long int)vid]=value; } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_t *num; if (!rec) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; num=igraph_Calloc(1, igraph_vector_t); if (!num) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); IGRAPH_VECTOR_INIT_FINALLY(num, igraph_vcount(graph)); igraph_vector_fill(num, IGRAPH_NAN); VECTOR(*num)[(long int)vid]=value; rec->value=num; IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_VAB_set * Set a boolean vertex attribute * * The attribute will be added if not present already. If present it * will be overwritten. The same \p value is set for all vertices * included in \p vid. * \param graph The graph. * \param name Name of the attribute. * \param vid Vertices for which to set the attribute. * \param value The (new) value of the attribute. * \return Error code. * * \sa \ref SETVAB for a simpler way. * * Time complexity: O(n), the number of vertices if the attribute is * new, O(|vid|) otherwise. */ int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_bool_t value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*val)[j]; if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_bool_t *log=(igraph_vector_bool_t*)rec->value; VECTOR(*log)[(long int)vid]=value; } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_bool_t *log; if (!rec) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_BOOLEAN; log=igraph_Calloc(1, igraph_vector_bool_t); if (!log) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); IGRAPH_CHECK(igraph_vector_bool_init(log, igraph_vcount(graph))); IGRAPH_FINALLY(igraph_vector_bool_destroy, log); igraph_vector_bool_fill(log, 0); VECTOR(*log)[(long int)vid]=value; rec->value=log; IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_VAS_set * Set a string vertex attribute * * The attribute will be added if not present already. If present it * will be overwritten. The same \p value is set for all vertices * included in \p vid. * \param graph The graph. * \param name Name of the attribute. * \param vid Vertices for which to set the attribute. * \param value The (new) value of the attribute. * \return Error code. * * \sa \ref SETVAS for a simpler way. * * Time complexity: O(n*l), n is the number of vertices, l is the * length of the string to set. If the attribute if not new then only * O(|vid|*l). */ int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, igraph_integer_t vid, const char *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*val)[j]; if (rec->type != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_strvector_t *str=(igraph_strvector_t*)rec->value; IGRAPH_CHECK(igraph_strvector_set(str, vid, value)); } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_strvector_t *str; if (!rec) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_STRING; str=igraph_Calloc(1, igraph_strvector_t); if (!str) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_vcount(graph)); IGRAPH_CHECK(igraph_strvector_set(str, vid, value)); rec->value=str; IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_EAN_set * Set a numeric edge attribute * * The attribute will be added if not present already. If present it * will be overwritten. The same \p value is set for all edges * included in \p vid. * \param graph The graph. * \param name Name of the attribute. * \param eid Edges for which to set the attribute. * \param value The (new) value of the attribute. * \return Error code. * * \sa \ref SETEAN for a simpler way. * * Time complexity: O(e), the number of edges if the attribute is * new, O(|eid|) otherwise. */ int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_real_t value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*eal)[j]; if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_t *num=(igraph_vector_t*)rec->value; VECTOR(*num)[(long int)eid]=value; } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_t *num; if (!rec) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; num=igraph_Calloc(1, igraph_vector_t); if (!num) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); IGRAPH_VECTOR_INIT_FINALLY(num, igraph_ecount(graph)); igraph_vector_fill(num, IGRAPH_NAN); VECTOR(*num)[(long int)eid]=value; rec->value=num; IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_EAB_set * Set a boolean edge attribute * * The attribute will be added if not present already. If present it * will be overwritten. The same \p value is set for all edges * included in \p vid. * \param graph The graph. * \param name Name of the attribute. * \param eid Edges for which to set the attribute. * \param value The (new) value of the attribute. * \return Error code. * * \sa \ref SETEAB for a simpler way. * * Time complexity: O(e), the number of edges if the attribute is * new, O(|eid|) otherwise. */ int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_bool_t value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*eal)[j]; if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_bool_t *log=(igraph_vector_bool_t*)rec->value; VECTOR(*log)[(long int)eid]=value; } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_bool_t *log; if (!rec) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_BOOLEAN; log=igraph_Calloc(1, igraph_vector_bool_t); if (!log) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); IGRAPH_CHECK(igraph_vector_bool_init(log, igraph_ecount(graph))); IGRAPH_FINALLY(igraph_vector_bool_destroy, log); igraph_vector_bool_fill(log, 0); VECTOR(*log)[(long int)eid]=value; rec->value=log; IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_EAS_set * Set a string edge attribute * * The attribute will be added if not present already. If present it * will be overwritten. The same \p value is set for all edges * included in \p vid. * \param graph The graph. * \param name Name of the attribute. * \param eid Edges for which to set the attribute. * \param value The (new) value of the attribute. * \return Error code. * * \sa \ref SETEAS for a simpler way. * * Time complexity: O(e*l), n is the number of edges, l is the * length of the string to set. If the attribute if not new then only * O(|eid|*l). */ int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, igraph_integer_t eid, const char *value) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (l) { igraph_attribute_record_t *rec=VECTOR(*eal)[j]; if (rec->type != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_strvector_t *str=(igraph_strvector_t*)rec->value; IGRAPH_CHECK(igraph_strvector_set(str, eid, value)); } } else { igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_strvector_t *str; if (!rec) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); rec->type=IGRAPH_ATTRIBUTE_STRING; str=igraph_Calloc(1, igraph_strvector_t); if (!str) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_ecount(graph)); IGRAPH_CHECK(igraph_strvector_set(str, eid, value)); rec->value=str; IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_VAN_setv * Set a numeric vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. * \param name Name of the attribute. * \param v The new attribute values. The length of this vector must * match the number of vertices. * \return Error code. * * \sa \ref SETVANV for a simpler way. * * Time complexity: O(n), the number of vertices. */ int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); /* Check length first */ if (igraph_vector_size(v) != igraph_vcount(graph)) { IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL); } if (l) { /* Already present, check type */ igraph_attribute_record_t *rec=VECTOR(*val)[j]; igraph_vector_t *num=(igraph_vector_t *)rec->value; if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); } igraph_vector_clear(num); IGRAPH_CHECK(igraph_vector_append(num, v)); } else { /* Add it */ igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_t *num; if (!rec) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); num=igraph_Calloc(1, igraph_vector_t); if (!num) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); rec->value=num; IGRAPH_CHECK(igraph_vector_copy(num, v)); IGRAPH_FINALLY(igraph_vector_destroy, num); IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_VAB_setv * Set a boolean vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. * \param name Name of the attribute. * \param v The new attribute values. The length of this boolean vector must * match the number of vertices. * \return Error code. * * \sa \ref SETVANV for a simpler way. * * Time complexity: O(n), the number of vertices. */ int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); /* Check length first */ if (igraph_vector_bool_size(v) != igraph_vcount(graph)) { IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL); } if (l) { /* Already present, check type */ igraph_attribute_record_t *rec=VECTOR(*val)[j]; igraph_vector_bool_t *log=(igraph_vector_bool_t *)rec->value; if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); } igraph_vector_bool_clear(log); IGRAPH_CHECK(igraph_vector_bool_append(log, v)); } else { /* Add it */ igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_bool_t *log; if (!rec) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->type=IGRAPH_ATTRIBUTE_BOOLEAN; rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); log=igraph_Calloc(1, igraph_vector_bool_t); if (!log) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); rec->value=log; IGRAPH_CHECK(igraph_vector_bool_copy(log, v)); IGRAPH_FINALLY(igraph_vector_destroy, log); IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_VAS_setv * Set a string vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. * \param name Name of the attribute. * \param sv String vector, the new attribute values. The length of this vector must * match the number of vertices. * \return Error code. * * \sa \ref SETVASV for a simpler way. * * Time complexity: O(n+l), n is the number of vertices, l is the * total length of the strings. */ int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); /* Check length first */ if (igraph_strvector_size(sv) != igraph_vcount(graph)) { IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL); } if (l) { /* Already present, check type */ igraph_attribute_record_t *rec=VECTOR(*val)[j]; igraph_strvector_t *str=(igraph_strvector_t *)rec->value; if (rec->type != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); } igraph_strvector_clear(str); IGRAPH_CHECK(igraph_strvector_append(str, sv)); } else { /* Add it */ igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_strvector_t *str; if (!rec) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->type=IGRAPH_ATTRIBUTE_STRING; rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); str=igraph_Calloc(1, igraph_strvector_t); if (!str) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); rec->value=str; IGRAPH_CHECK(igraph_strvector_copy(str, sv)); IGRAPH_FINALLY(igraph_strvector_destroy, str); IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_EAN_setv * Set a numeric edge attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. * \param name Name of the attribute. * \param v The new attribute values. The length of this vector must * match the number of edges. * \return Error code. * * \sa \ref SETEANV for a simpler way. * * Time complexity: O(e), the number of edges. */ int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); /* Check length first */ if (igraph_vector_size(v) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL); } if (l) { /* Already present, check type */ igraph_attribute_record_t *rec=VECTOR(*eal)[j]; igraph_vector_t *num=(igraph_vector_t *)rec->value; if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); } igraph_vector_clear(num); IGRAPH_CHECK(igraph_vector_append(num, v)); } else { /* Add it */ igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_t *num; if (!rec) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); num=igraph_Calloc(1, igraph_vector_t); if (!num) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); rec->value=num; IGRAPH_CHECK(igraph_vector_copy(num, v)); IGRAPH_FINALLY(igraph_vector_destroy, num); IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_EAB_setv * Set a boolean edge attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. * \param name Name of the attribute. * \param v The new attribute values. The length of this vector must * match the number of edges. * \return Error code. * * \sa \ref SETEABV for a simpler way. * * Time complexity: O(e), the number of edges. */ int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); /* Check length first */ if (igraph_vector_bool_size(v) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL); } if (l) { /* Already present, check type */ igraph_attribute_record_t *rec=VECTOR(*eal)[j]; igraph_vector_bool_t *log=(igraph_vector_bool_t *)rec->value; if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); } igraph_vector_bool_clear(log); IGRAPH_CHECK(igraph_vector_bool_append(log, v)); } else { /* Add it */ igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_vector_bool_t *log; if (!rec) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->type=IGRAPH_ATTRIBUTE_BOOLEAN; rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); log=igraph_Calloc(1, igraph_vector_bool_t); if (!log) { IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); rec->value=log; IGRAPH_CHECK(igraph_vector_bool_copy(log, v)); IGRAPH_FINALLY(igraph_vector_bool_destroy, log); IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } /** * \function igraph_cattribute_EAS_setv * Set a string edge attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. * \param name Name of the attribute. * \param sv String vector, the new attribute values. The length of this vector must * match the number of edges. * \return Error code. * * \sa \ref SETEASV for a simpler way. * * Time complexity: O(e+l), e is the number of edges, l is the * total length of the strings. */ int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); /* Check length first */ if (igraph_strvector_size(sv) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL); } if (l) { /* Already present, check type */ igraph_attribute_record_t *rec=VECTOR(*eal)[j]; igraph_strvector_t *str=(igraph_strvector_t *)rec->value; if (rec->type != IGRAPH_ATTRIBUTE_STRING) { IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); } igraph_strvector_clear(str); IGRAPH_CHECK(igraph_strvector_append(str, sv)); } else { /* Add it */ igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); igraph_strvector_t *str; if (!rec) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, rec); rec->type=IGRAPH_ATTRIBUTE_STRING; rec->name=strdup(name); if (!rec->name) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (char*)rec->name); str=igraph_Calloc(1, igraph_strvector_t); if (!str) { IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); rec->value=str; IGRAPH_CHECK(igraph_strvector_copy(str, sv)); IGRAPH_FINALLY(igraph_strvector_destroy, str); IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } return 0; } void igraph_i_cattribute_free_rec(igraph_attribute_record_t *rec) { if (rec->type==IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *num=(igraph_vector_t*)rec->value; igraph_vector_destroy(num); } else if (rec->type==IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *str=(igraph_strvector_t*)rec->value; igraph_strvector_destroy(str); } igraph_Free(rec->name); igraph_Free(rec->value); igraph_Free(rec); } /** * \function igraph_cattribute_remove_g * Remove a graph attribute * * \param graph The graph object. * \param name Name of the graph attribute to remove. * * \sa \ref DELGA for a simpler way. * */ void igraph_cattribute_remove_g(igraph_t *graph, const char *name) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *gal=&attr->gal; long int j; igraph_bool_t l=igraph_i_cattribute_find(gal, name, &j); if (l) { igraph_i_cattribute_free_rec(VECTOR(*gal)[j]); igraph_vector_ptr_remove(gal, j); } else { IGRAPH_WARNING("Cannot remove non-existent graph attribute"); } } /** * \function igraph_cattribute_remove_v * Remove a vertex attribute * * \param graph The graph object. * \param name Name of the vertex attribute to remove. * * \sa \ref DELVA for a simpler way. * */ void igraph_cattribute_remove_v(igraph_t *graph, const char *name) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *val=&attr->val; long int j; igraph_bool_t l=igraph_i_cattribute_find(val, name, &j); if (l) { igraph_i_cattribute_free_rec(VECTOR(*val)[j]); igraph_vector_ptr_remove(val, j); } else { IGRAPH_WARNING("Cannot remove non-existent graph attribute"); } } /** * \function igraph_cattribute_remove_e * Remove an edge attribute * * \param graph The graph object. * \param name Name of the edge attribute to remove. * * \sa \ref DELEA for a simpler way. * */ void igraph_cattribute_remove_e(igraph_t *graph, const char *name) { igraph_i_cattributes_t *attr=graph->attr; igraph_vector_ptr_t *eal=&attr->eal; long int j; igraph_bool_t l=igraph_i_cattribute_find(eal, name, &j); if (l) { igraph_i_cattribute_free_rec(VECTOR(*eal)[j]); igraph_vector_ptr_remove(eal, j); } else { IGRAPH_WARNING("Cannot remove non-existent graph attribute"); } } /** * \function igraph_cattribute_remove_all * Remove all graph/vertex/edge attributes * * \param graph The graph object. * \param g Boolean, whether to remove graph attributes. * \param v Boolean, whether to remove vertex attributes. * \param e Boolean, whether to remove edge attributes. * * \sa \ref DELGAS, \ref DELVAS, \ref DELEAS, \ref DELALL for simpler * ways. */ void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, igraph_bool_t v, igraph_bool_t e) { igraph_i_cattributes_t *attr=graph->attr; if (g) { igraph_vector_ptr_t *gal=&attr->gal; long int i, n=igraph_vector_ptr_size(gal); for (i=0;ival; long int i, n=igraph_vector_ptr_size(val); for (i=0;ieal; long int i, n=igraph_vector_ptr_size(eal); for (i=0;i #include "prpack_utils.h" #include #include #include using namespace prpack; using namespace std; #ifdef PRPACK_IGRAPH_SUPPORT #include "igraph_error.h" #endif #if defined(_WIN32) || defined(_WIN64) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #include #endif double prpack_utils::get_time() { LARGE_INTEGER t, freq; QueryPerformanceCounter(&t); QueryPerformanceFrequency(&freq); return double(t.QuadPart)/double(freq.QuadPart); } #else #include #include #include double prpack_utils::get_time() { struct timeval t; gettimeofday(&t, 0); return (t.tv_sec*1.0 + t.tv_usec/1000000.0); } #endif // Fails and outputs 'msg' if 'condition' is false. void prpack_utils::validate(const bool condition, const string& msg) { if (!condition) { #ifdef PRPACK_IGRAPH_SUPPORT igraph_error("Internal error in PRPACK", __FILE__, __LINE__, IGRAPH_EINTERNAL); #else cerr << msg << endl; exit(-1); #endif } } // Permute a vector. double* prpack_utils::permute(const int length, const double* a, const int* coding) { double* ret = new double[length]; for (int i = 0; i < length; ++i) ret[coding[i]] = a[i]; return ret; } igraph/src/cs_randperm.c0000644000176000001440000000347712325527073015025 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma clang diagnostic ignored "-Wsign-conversion" #include "cs.h" /* return a random permutation vector, the identity perm, or p = n-1:-1:0. * seed = -1 means p = n-1:-1:0. seed = 0 means p = identity. otherwise * p = random permutation. */ CS_INT *cs_randperm (CS_INT n, CS_INT seed) { CS_INT *p, k, j, t ; if (seed == 0) return (NULL) ; /* return p = NULL (identity) */ p = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ if (!p) return (NULL) ; /* out of memory */ for (k = 0 ; k < n ; k++) p [k] = n-k-1 ; if (seed == -1) return (p) ; /* return reverse permutation */ srand (seed) ; /* get new random number seed */ for (k = 0 ; k < n ; k++) { j = k + (rand ( ) % (n-k)) ; /* j = rand CS_INT in range k to n-1 */ t = p [j] ; /* swap p[k] and p[j] */ p [j] = p [k] ; p [k] = t ; } return (p) ; } igraph/src/bliss_heap.hh0000644000176000001440000000226412325372072015004 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_HEAP_HH #define BLISS_HEAP_HH namespace igraph { class Heap { #if defined(CONSISTENCY_CHECKS) unsigned int N; #endif unsigned int n; unsigned int *array; void upheap(unsigned int k); void downheap(unsigned int k); public: Heap() {array = 0; n = 0; } ~Heap(); void init(unsigned int size); bool is_empty() const {return(n==0); } void clear() {n = 0;} void insert(unsigned int v); unsigned int remove(); }; } #endif igraph/src/glpmpl06.c0000644000176000001440000007543712325527073014176 0ustar ripleyusers/* glpmpl06.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #pragma clang diagnostic ignored "-Wself-assign" #endif #define _GLPSTD_ERRNO #define _GLPSTD_STDIO #include "glpmpl.h" #include "glpsql.h" /**********************************************************************/ #define CSV_FIELD_MAX 50 /* maximal number of fields in record */ #define CSV_FDLEN_MAX 100 /* maximal field length */ struct csv { /* comma-separated values file */ int mode; /* 'R' = reading; 'W' = writing */ char *fname; /* name of csv file */ FILE *fp; /* stream assigned to csv file */ jmp_buf jump; /* address for non-local go to in case of error */ int count; /* record count */ /*--------------------------------------------------------------*/ /* used only for input csv file */ int c; /* current character or EOF */ int what; /* current marker: */ #define CSV_EOF 0 /* end-of-file */ #define CSV_EOR 1 /* end-of-record */ #define CSV_NUM 2 /* floating-point number */ #define CSV_STR 3 /* character string */ char field[CSV_FDLEN_MAX+1]; /* current field just read */ int nf; /* number of fields in the csv file */ int ref[1+CSV_FIELD_MAX]; /* ref[k] = k', if k-th field of the csv file corresponds to k'-th field in the table statement; if ref[k] = 0, k-th field of the csv file is ignored */ #if 1 /* 01/VI-2010 */ int nskip; /* number of comment records preceding the header record */ #endif }; #undef read_char static void read_char(struct csv *csv) { /* read character from csv data file */ int c; xassert(csv->c != EOF); if (csv->c == '\n') csv->count++; loop: c = fgetc(csv->fp); if (ferror(csv->fp)) { xprintf("%s:%d: read error - %s\n", csv->fname, csv->count, strerror(errno)); longjmp(csv->jump, 0); } if (feof(csv->fp)) { if (csv->c == '\n') { csv->count--; c = EOF; } else { xprintf("%s:%d: warning: missing final end-of-line\n", csv->fname, csv->count); c = '\n'; } } else if (c == '\r') goto loop; else if (c == '\n') ; else if (iscntrl(c)) { xprintf("%s:%d: invalid control character 0x%02X\n", csv->fname, csv->count, c); longjmp(csv->jump, 0); } csv->c = c; return; } static void read_field(struct csv *csv) { /* read field from csv data file */ /* check for end of file */ if (csv->c == EOF) { csv->what = CSV_EOF; strcpy(csv->field, "EOF"); goto done; } /* check for end of record */ if (csv->c == '\n') { csv->what = CSV_EOR; strcpy(csv->field, "EOR"); read_char(csv); if (csv->c == ',') err1: { xprintf("%s:%d: empty field not allowed\n", csv->fname, csv->count); longjmp(csv->jump, 0); } if (csv->c == '\n') { xprintf("%s:%d: empty record not allowed\n", csv->fname, csv->count); longjmp(csv->jump, 0); } #if 1 /* 01/VI-2010 */ /* skip comment records; may appear only before the very first record containing field names */ if (csv->c == '#' && csv->count == 1) { while (csv->c == '#') { while (csv->c != '\n') read_char(csv); read_char(csv); csv->nskip++; } } #endif goto done; } /* skip comma before next field */ if (csv->c == ',') read_char(csv); /* read field */ if (csv->c == '\'' || csv->c == '"') { /* read a field enclosed in quotes */ int quote = csv->c, len = 0; csv->what = CSV_STR; /* skip opening quote */ read_char(csv); /* read field characters within quotes */ for (;;) { /* check for closing quote and read it */ if (csv->c == quote) { read_char(csv); if (csv->c == quote) ; else if (csv->c == ',' || csv->c == '\n') break; else { xprintf("%s:%d: invalid field\n", csv->fname, csv->count); longjmp(csv->jump, 0); } } /* check the current field length */ if (len == CSV_FDLEN_MAX) err2: { xprintf("%s:%d: field too long\n", csv->fname, csv->count); longjmp(csv->jump, 0); } /* add the current character to the field */ csv->field[len++] = (char)csv->c; /* read the next character */ read_char(csv); } /* the field has been read */ if (len == 0) goto err1; csv->field[len] = '\0'; } else { /* read a field not enclosed in quotes */ int len = 0; double temp; csv->what = CSV_NUM; while (!(csv->c == ',' || csv->c == '\n')) { /* quotes within the field are not allowed */ if (csv->c == '\'' || csv->c == '"') { xprintf("%s:%d: invalid use of single or double quote wi" "thin field\n", csv->fname, csv->count); longjmp(csv->jump, 0); } /* check the current field length */ if (len == CSV_FDLEN_MAX) goto err2; /* add the current character to the field */ csv->field[len++] = (char)csv->c; /* read the next character */ read_char(csv); } /* the field has been read */ if (len == 0) goto err1; csv->field[len] = '\0'; /* check the field type */ if (str2num(csv->field, &temp)) csv->what = CSV_STR; } done: return; } static struct csv *csv_open_file(TABDCA *dca, int mode) { /* open csv data file */ struct csv *csv; /* create control structure */ csv = xmalloc(sizeof(struct csv)); csv->mode = mode; csv->fname = NULL; csv->fp = NULL; if (setjmp(csv->jump)) goto fail; csv->count = 0; csv->c = '\n'; csv->what = 0; csv->field[0] = '\0'; csv->nf = 0; /* try to open the csv data file */ if (mpl_tab_num_args(dca) < 2) { xprintf("csv_driver: file name not specified\n"); longjmp(csv->jump, 0); } csv->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1); strcpy(csv->fname, mpl_tab_get_arg(dca, 2)); if (mode == 'R') { /* open the file for reading */ int k; csv->fp = fopen(csv->fname, "r"); if (csv->fp == NULL) { xprintf("csv_driver: unable to open %s - %s\n", csv->fname, strerror(errno)); longjmp(csv->jump, 0); } #if 1 /* 01/VI-2010 */ csv->nskip = 0; #endif /* skip fake new-line */ read_field(csv); xassert(csv->what == CSV_EOR); /* read field names */ xassert(csv->nf == 0); for (;;) { read_field(csv); if (csv->what == CSV_EOR) break; if (csv->what != CSV_STR) { xprintf("%s:%d: invalid field name\n", csv->fname, csv->count); longjmp(csv->jump, 0); } if (csv->nf == CSV_FIELD_MAX) { xprintf("%s:%d: too many fields\n", csv->fname, csv->count); longjmp(csv->jump, 0); } csv->nf++; /* find corresponding field in the table statement */ for (k = mpl_tab_num_flds(dca); k >= 1; k--) { if (strcmp(mpl_tab_get_name(dca, k), csv->field) == 0) break; } csv->ref[csv->nf] = k; } /* find dummy RECNO field in the table statement */ for (k = mpl_tab_num_flds(dca); k >= 1; k--) if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break; csv->ref[0] = k; } else if (mode == 'W') { /* open the file for writing */ int k, nf; csv->fp = fopen(csv->fname, "w"); if (csv->fp == NULL) { xprintf("csv_driver: unable to create %s - %s\n", csv->fname, strerror(errno)); longjmp(csv->jump, 0); } /* write field names */ nf = mpl_tab_num_flds(dca); for (k = 1; k <= nf; k++) fprintf(csv->fp, "%s%c", mpl_tab_get_name(dca, k), k < nf ? ',' : '\n'); csv->count++; } else xassert(mode != mode); /* the file has been open */ return csv; fail: /* the file cannot be open */ if (csv->fname != NULL) xfree(csv->fname); if (csv->fp != NULL) fclose(csv->fp); xfree(csv); return NULL; } static int csv_read_record(TABDCA *dca, struct csv *csv) { /* read next record from csv data file */ int k, ret = 0; xassert(csv->mode == 'R'); if (setjmp(csv->jump)) { ret = 1; goto done; } /* read dummy RECNO field */ if (csv->ref[0] > 0) #if 0 /* 01/VI-2010 */ mpl_tab_set_num(dca, csv->ref[0], csv->count-1); #else mpl_tab_set_num(dca, csv->ref[0], csv->count-csv->nskip-1); #endif /* read fields */ for (k = 1; k <= csv->nf; k++) { read_field(csv); if (csv->what == CSV_EOF) { /* end-of-file reached */ xassert(k == 1); ret = -1; goto done; } else if (csv->what == CSV_EOR) { /* end-of-record reached */ int lack = csv->nf - k + 1; if (lack == 1) xprintf("%s:%d: one field missing\n", csv->fname, csv->count); else xprintf("%s:%d: %d fields missing\n", csv->fname, csv->count, lack); longjmp(csv->jump, 0); } else if (csv->what == CSV_NUM) { /* floating-point number */ if (csv->ref[k] > 0) { double num; xassert(str2num(csv->field, &num) == 0); mpl_tab_set_num(dca, csv->ref[k], num); } } else if (csv->what == CSV_STR) { /* character string */ if (csv->ref[k] > 0) mpl_tab_set_str(dca, csv->ref[k], csv->field); } else xassert(csv != csv); } /* now there must be NL */ read_field(csv); xassert(csv->what != CSV_EOF); if (csv->what != CSV_EOR) { xprintf("%s:%d: too many fields\n", csv->fname, csv->count); longjmp(csv->jump, 0); } done: return ret; } static int csv_write_record(TABDCA *dca, struct csv *csv) { /* write next record to csv data file */ int k, nf, ret = 0; const char *c; xassert(csv->mode == 'W'); nf = mpl_tab_num_flds(dca); for (k = 1; k <= nf; k++) { switch (mpl_tab_get_type(dca, k)) { case 'N': fprintf(csv->fp, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); break; case 'S': fputc('"', csv->fp); for (c = mpl_tab_get_str(dca, k); *c != '\0'; c++) { if (*c == '"') fputc('"', csv->fp), fputc('"', csv->fp); else fputc(*c, csv->fp); } fputc('"', csv->fp); break; default: xassert(dca != dca); } fputc(k < nf ? ',' : '\n', csv->fp); } csv->count++; if (ferror(csv->fp)) { xprintf("%s:%d: write error - %s\n", csv->fname, csv->count, strerror(errno)); ret = 1; } return ret; } static int csv_close_file(TABDCA *dca, struct csv *csv) { /* close csv data file */ int ret = 0; xassert(dca == dca); if (csv->mode == 'W') { fflush(csv->fp); if (ferror(csv->fp)) { xprintf("%s:%d: write error - %s\n", csv->fname, csv->count, strerror(errno)); ret = 1; } } xfree(csv->fname); fclose(csv->fp); xfree(csv); return ret; } /**********************************************************************/ #define DBF_FIELD_MAX 50 /* maximal number of fields in record */ #define DBF_FDLEN_MAX 100 /* maximal field length */ struct dbf { /* xBASE data file */ int mode; /* 'R' = reading; 'W' = writing */ char *fname; /* name of xBASE file */ FILE *fp; /* stream assigned to xBASE file */ jmp_buf jump; /* address for non-local go to in case of error */ int offset; /* offset of a byte to be read next */ int count; /* record count */ int nf; /* number of fields */ int ref[1+DBF_FIELD_MAX]; /* ref[k] = k', if k-th field of the csv file corresponds to k'-th field in the table statement; if ref[k] = 0, k-th field of the csv file is ignored */ int type[1+DBF_FIELD_MAX]; /* type[k] is type of k-th field */ int len[1+DBF_FIELD_MAX]; /* len[k] is length of k-th field */ int prec[1+DBF_FIELD_MAX]; /* prec[k] is precision of k-th field */ }; static int read_byte(struct dbf *dbf) { /* read byte from xBASE data file */ int b; b = fgetc(dbf->fp); if (ferror(dbf->fp)) { xprintf("%s:0x%X: read error - %s\n", dbf->fname, dbf->offset, strerror(errno)); longjmp(dbf->jump, 0); } if (feof(dbf->fp)) { xprintf("%s:0x%X: unexpected end of file\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } xassert(0x00 <= b && b <= 0xFF); dbf->offset++; return b; } static void read_header(TABDCA *dca, struct dbf *dbf) { /* read xBASE data file header */ int b, j, k, recl; char name[10+1]; /* (ignored) */ for (j = 1; j <= 10; j++) read_byte(dbf); /* length of each record, in bytes */ recl = read_byte(dbf); recl += read_byte(dbf) << 8; /* (ignored) */ for (j = 1; j <= 20; j++) read_byte(dbf); /* field descriptor array */ xassert(dbf->nf == 0); for (;;) { /* check for end of array */ b = read_byte(dbf); if (b == 0x0D) break; if (dbf->nf == DBF_FIELD_MAX) { xprintf("%s:0x%X: too many fields\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } dbf->nf++; /* field name */ name[0] = (char)b; for (j = 1; j < 10; j++) { b = read_byte(dbf); name[j] = (char)b; } name[10] = '\0'; b = read_byte(dbf); if (b != 0x00) { xprintf("%s:0x%X: invalid field name\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } /* find corresponding field in the table statement */ for (k = mpl_tab_num_flds(dca); k >= 1; k--) if (strcmp(mpl_tab_get_name(dca, k), name) == 0) break; dbf->ref[dbf->nf] = k; /* field type */ b = read_byte(dbf); if (!(b == 'C' || b == 'N')) { xprintf("%s:0x%X: invalid field type\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } dbf->type[dbf->nf] = b; /* (ignored) */ for (j = 1; j <= 4; j++) read_byte(dbf); /* field length */ b = read_byte(dbf); if (b == 0) { xprintf("%s:0x%X: invalid field length\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } if (b > DBF_FDLEN_MAX) { xprintf("%s:0x%X: field too long\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } dbf->len[dbf->nf] = b; recl -= b; /* (ignored) */ for (j = 1; j <= 15; j++) read_byte(dbf); } if (recl != 1) { xprintf("%s:0x%X: invalid file header\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } /* find dummy RECNO field in the table statement */ for (k = mpl_tab_num_flds(dca); k >= 1; k--) if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break; dbf->ref[0] = k; return; } static void parse_third_arg(TABDCA *dca, struct dbf *dbf) { /* parse xBASE file format (third argument) */ int j, k, temp; const char *arg; dbf->nf = mpl_tab_num_flds(dca); arg = mpl_tab_get_arg(dca, 3), j = 0; for (k = 1; k <= dbf->nf; k++) { /* parse specification of k-th field */ if (arg[j] == '\0') { xprintf("xBASE driver: field %s: specification missing\n", mpl_tab_get_name(dca, k)); longjmp(dbf->jump, 0); } /* parse field type */ if (arg[j] == 'C' || arg[j] == 'N') dbf->type[k] = arg[j], j++; else { xprintf("xBASE driver: field %s: invalid field type\n", mpl_tab_get_name(dca, k)); longjmp(dbf->jump, 0); } /* check for left parenthesis */ if (arg[j] == '(') j++; else err: { xprintf("xBASE driver: field %s: invalid field format\n", mpl_tab_get_name(dca, k)); longjmp(dbf->jump, 0); } /* parse field length */ temp = 0; while (isdigit(arg[j])) { if (temp > DBF_FDLEN_MAX) break; temp = 10 * temp + (arg[j] - '0'), j++; } if (!(1 <= temp && temp <= DBF_FDLEN_MAX)) { xprintf("xBASE driver: field %s: invalid field length\n", mpl_tab_get_name(dca, k)); longjmp(dbf->jump, 0); } dbf->len[k] = temp; /* parse optional field precision */ if (dbf->type[k] == 'N' && arg[j] == ',') { j++; temp = 0; while (isdigit(arg[j])) { if (temp > dbf->len[k]) break; temp = 10 * temp + (arg[j] - '0'), j++; } if (temp > dbf->len[k]) { xprintf("xBASE driver: field %s: invalid field precision" "\n", mpl_tab_get_name(dca, k)); longjmp(dbf->jump, 0); } dbf->prec[k] = temp; } else dbf->prec[k] = 0; /* check for right parenthesis */ if (arg[j] == ')') j++; else goto err; } /* ignore other specifications */ return; } static void write_byte(struct dbf *dbf, int b) { /* write byte to xBASE data file */ fputc(b, dbf->fp); dbf->offset++; return; } static void write_header(TABDCA *dca, struct dbf *dbf) { /* write xBASE data file header */ int j, k, temp; const char *name; /* version number */ write_byte(dbf, 0x03 /* file without DBT */); /* date of last update (YYMMDD) */ write_byte(dbf, 70 /* 1970 */); write_byte(dbf, 1 /* January */); write_byte(dbf, 1 /* 1st */); /* number of records (unknown so far) */ for (j = 1; j <= 4; j++) write_byte(dbf, 0xFF); /* length of the header, in bytes */ temp = 32 + dbf->nf * 32 + 1; write_byte(dbf, temp); write_byte(dbf, temp >> 8); /* length of each record, in bytes */ temp = 1; for (k = 1; k <= dbf->nf; k++) temp += dbf->len[k]; write_byte(dbf, temp); write_byte(dbf, temp >> 8); /* (reserved) */ for (j = 1; j <= 20; j++) write_byte(dbf, 0x00); /* field descriptor array */ for (k = 1; k <= dbf->nf; k++) { /* field name (terminated by 0x00) */ name = mpl_tab_get_name(dca, k); for (j = 0; j < 10 && name[j] != '\0'; j++) write_byte(dbf, name[j]); for (j = j; j < 11; j++) write_byte(dbf, 0x00); /* field type */ write_byte(dbf, dbf->type[k]); /* (reserved) */ for (j = 1; j <= 4; j++) write_byte(dbf, 0x00); /* field length */ write_byte(dbf, dbf->len[k]); /* field precision */ write_byte(dbf, dbf->prec[k]); /* (reserved) */ for (j = 1; j <= 14; j++) write_byte(dbf, 0x00); } /* end of header */ write_byte(dbf, 0x0D); return; } static struct dbf *dbf_open_file(TABDCA *dca, int mode) { /* open xBASE data file */ struct dbf *dbf; /* create control structure */ dbf = xmalloc(sizeof(struct dbf)); dbf->mode = mode; dbf->fname = NULL; dbf->fp = NULL; if (setjmp(dbf->jump)) goto fail; dbf->offset = 0; dbf->count = 0; dbf->nf = 0; /* try to open the xBASE data file */ if (mpl_tab_num_args(dca) < 2) { xprintf("xBASE driver: file name not specified\n"); longjmp(dbf->jump, 0); } dbf->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1); strcpy(dbf->fname, mpl_tab_get_arg(dca, 2)); if (mode == 'R') { /* open the file for reading */ dbf->fp = fopen(dbf->fname, "rb"); if (dbf->fp == NULL) { xprintf("xBASE driver: unable to open %s - %s\n", dbf->fname, strerror(errno)); longjmp(dbf->jump, 0); } read_header(dca, dbf); } else if (mode == 'W') { /* open the file for writing */ if (mpl_tab_num_args(dca) < 3) { xprintf("xBASE driver: file format not specified\n"); longjmp(dbf->jump, 0); } parse_third_arg(dca, dbf); dbf->fp = fopen(dbf->fname, "wb"); if (dbf->fp == NULL) { xprintf("xBASE driver: unable to create %s - %s\n", dbf->fname, strerror(errno)); longjmp(dbf->jump, 0); } write_header(dca, dbf); } else xassert(mode != mode); /* the file has been open */ return dbf; fail: /* the file cannot be open */ if (dbf->fname != NULL) xfree(dbf->fname); if (dbf->fp != NULL) fclose(dbf->fp); xfree(dbf); return NULL; } static int dbf_read_record(TABDCA *dca, struct dbf *dbf) { /* read next record from xBASE data file */ int b, j, k, ret = 0; char buf[DBF_FDLEN_MAX+1]; xassert(dbf->mode == 'R'); if (setjmp(dbf->jump)) { ret = 1; goto done; } /* check record flag */ b = read_byte(dbf); if (b == 0x1A) { /* end of data */ ret = -1; goto done; } if (b != 0x20) { xprintf("%s:0x%X: invalid record flag\n", dbf->fname, dbf->offset); longjmp(dbf->jump, 0); } /* read dummy RECNO field */ if (dbf->ref[0] > 0) mpl_tab_set_num(dca, dbf->ref[0], dbf->count+1); /* read fields */ for (k = 1; k <= dbf->nf; k++) { /* read k-th field */ for (j = 0; j < dbf->len[k]; j++) buf[j] = (char)read_byte(dbf); buf[dbf->len[k]] = '\0'; /* set field value */ if (dbf->type[k] == 'C') { /* character field */ if (dbf->ref[k] > 0) mpl_tab_set_str(dca, dbf->ref[k], strtrim(buf)); } else if (dbf->type[k] == 'N') { /* numeric field */ if (dbf->ref[k] > 0) { double num; strspx(buf); xassert(str2num(buf, &num) == 0); mpl_tab_set_num(dca, dbf->ref[k], num); } } else xassert(dbf != dbf); } /* increase record count */ dbf->count++; done: return ret; } static int dbf_write_record(TABDCA *dca, struct dbf *dbf) { /* write next record to xBASE data file */ int j, k, ret = 0; char buf[255+1]; xassert(dbf->mode == 'W'); if (setjmp(dbf->jump)) { ret = 1; goto done; } /* record flag */ write_byte(dbf, 0x20); xassert(dbf->nf == mpl_tab_num_flds(dca)); for (k = 1; k <= dbf->nf; k++) { if (dbf->type[k] == 'C') { /* character field */ const char *str; if (mpl_tab_get_type(dca, k) == 'N') { sprintf(buf, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); str = buf; } else if (mpl_tab_get_type(dca, k) == 'S') str = mpl_tab_get_str(dca, k); else xassert(dca != dca); if ((int)strlen(str) > dbf->len[k]) { xprintf("xBASE driver: field %s: cannot convert %.15s..." " to field format\n", mpl_tab_get_name(dca, k), str); longjmp(dbf->jump, 0); } for (j = 0; j < dbf->len[k] && str[j] != '\0'; j++) write_byte(dbf, str[j]); for (j = j; j < dbf->len[k]; j++) write_byte(dbf, ' '); } else if (dbf->type[k] == 'N') { /* numeric field */ double num = mpl_tab_get_num(dca, k); if (fabs(num) > 1e20) err: { xprintf("xBASE driver: field %s: cannot convert %g to fi" "eld format\n", mpl_tab_get_name(dca, k), num); longjmp(dbf->jump, 0); } sprintf(buf, "%*.*f", dbf->len[k], dbf->prec[k], num); xassert(strlen(buf) < sizeof(buf)); if ((int)strlen(buf) != dbf->len[k]) goto err; for (j = 0; j < dbf->len[k]; j++) write_byte(dbf, buf[j]); } else xassert(dbf != dbf); } /* increase record count */ dbf->count++; done: return ret; } static int dbf_close_file(TABDCA *dca, struct dbf *dbf) { /* close xBASE data file */ int ret = 0; xassert(dca == dca); if (dbf->mode == 'W') { if (setjmp(dbf->jump)) { ret = 1; goto skip; } /* end-of-file flag */ write_byte(dbf, 0x1A); /* number of records */ dbf->offset = 4; if (fseek(dbf->fp, dbf->offset, SEEK_SET)) { xprintf("%s:0x%X: seek error - %s\n", dbf->fname, dbf->offset, strerror(errno)); longjmp(dbf->jump, 0); } write_byte(dbf, dbf->count); write_byte(dbf, dbf->count >> 8); write_byte(dbf, dbf->count >> 16); write_byte(dbf, dbf->count >> 24); fflush(dbf->fp); if (ferror(dbf->fp)) { xprintf("%s:0x%X: write error - %s\n", dbf->fname, dbf->offset, strerror(errno)); longjmp(dbf->jump, 0); } skip: ; } xfree(dbf->fname); fclose(dbf->fp); xfree(dbf); return ret; } /**********************************************************************/ #define TAB_CSV 1 #define TAB_XBASE 2 #define TAB_ODBC 3 #define TAB_MYSQL 4 void mpl_tab_drv_open(MPL *mpl, int mode) { TABDCA *dca = mpl->dca; xassert(dca->id == 0); xassert(dca->link == NULL); xassert(dca->na >= 1); if (strcmp(dca->arg[1], "CSV") == 0) { dca->id = TAB_CSV; dca->link = csv_open_file(dca, mode); } else if (strcmp(dca->arg[1], "xBASE") == 0) { dca->id = TAB_XBASE; dca->link = dbf_open_file(dca, mode); } else if (strcmp(dca->arg[1], "ODBC") == 0 || strcmp(dca->arg[1], "iODBC") == 0) { dca->id = TAB_ODBC; dca->link = db_iodbc_open(dca, mode); } else if (strcmp(dca->arg[1], "MySQL") == 0) { dca->id = TAB_MYSQL; dca->link = db_mysql_open(dca, mode); } else xprintf("Invalid table driver `%s'\n", dca->arg[1]); if (dca->link == NULL) error(mpl, "error on opening table %s", mpl->stmt->u.tab->name); return; } int mpl_tab_drv_read(MPL *mpl) { TABDCA *dca = mpl->dca; int ret; switch (dca->id) { case TAB_CSV: ret = csv_read_record(dca, dca->link); break; case TAB_XBASE: ret = dbf_read_record(dca, dca->link); break; case TAB_ODBC: ret = db_iodbc_read(dca, dca->link); break; case TAB_MYSQL: ret = db_mysql_read(dca, dca->link); break; default: xassert(dca != dca); } if (ret > 0) error(mpl, "error on reading data from table %s", mpl->stmt->u.tab->name); return ret; } void mpl_tab_drv_write(MPL *mpl) { TABDCA *dca = mpl->dca; int ret; switch (dca->id) { case TAB_CSV: ret = csv_write_record(dca, dca->link); break; case TAB_XBASE: ret = dbf_write_record(dca, dca->link); break; case TAB_ODBC: ret = db_iodbc_write(dca, dca->link); break; case TAB_MYSQL: ret = db_mysql_write(dca, dca->link); break; default: xassert(dca != dca); } if (ret) error(mpl, "error on writing data to table %s", mpl->stmt->u.tab->name); return; } void mpl_tab_drv_close(MPL *mpl) { TABDCA *dca = mpl->dca; int ret; switch (dca->id) { case TAB_CSV: ret = csv_close_file(dca, dca->link); break; case TAB_XBASE: ret = dbf_close_file(dca, dca->link); break; case TAB_ODBC: ret = db_iodbc_close(dca, dca->link); break; case TAB_MYSQL: ret = db_mysql_close(dca, dca->link); break; default: xassert(dca != dca); } dca->id = 0; dca->link = NULL; if (ret) error(mpl, "error on closing table %s", mpl->stmt->u.tab->name); return; } /* eof */ igraph/src/igraph_components.h0000644000176000001440000000443012325527073016242 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_COMPONENTS_H #define IGRAPH_COMPONENTS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_datatype.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Components */ /* -------------------------------------------------- */ int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t *no, igraph_connectedness_t mode); int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, igraph_connectedness_t mode); void igraph_decompose_destroy(igraph_vector_ptr_t *complist); int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, igraph_connectedness_t mode, long int maxcompno, long int minelements); int igraph_articulation_points(const igraph_t *graph, igraph_vector_t *res); int igraph_biconnected_components(const igraph_t *graph, igraph_integer_t *no, igraph_vector_ptr_t *tree_edges, igraph_vector_ptr_t *component_edges, igraph_vector_ptr_t *components, igraph_vector_t *articulation_points); __END_DECLS #endif igraph/src/gengraph_random.cpp0000644000176000001440000001731412325527073016216 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define RNG_C #ifdef RCSID static const char rcsid[] = "$Id: random.cpp,v 1.15 2003/05/14 03:04:45 wilder Exp wilder $"; #endif //________________________________________________________________________ // See the header file random.h for a description of the contents of this // file as well as references and credits. #include #include "gengraph_random.h" using namespace std; using namespace KW_RNG; //________________________________________________________________________ // RNG::RNOR generates normal variates with rejection. // nfix() generates variates after rejection in RNOR. // Despite rejection, this method is much faster than Box-Muller. // double RNG::nfix(slong h, ulong i) // { // const double r = 3.442620f; // The starting of the right tail // static double x, y; // for(;;) { // x = h * wn[i]; // // If i == 0, handle the base strip // if (i==0){ // do { // x = -log(rand_open01()) * 0.2904764; // .2904764 is 1/r // y = -log(rand_open01()); // } while (y + y < x * x); // return ((h > 0) ? r + x : -r - x); // } // // If i > 0, handle the wedges of other strips // if (fn[i] + rand_open01() * (fn[i - 1] - fn[i]) < exp(-.5 * x * x) ) // return x; // // start all over // h = rand_int32(); // i = h & 127; // if ((ulong) abs((sint) h) < kn[i]) // return (h * wn[i]); // } // } // RNG::nfix // // __________________________________________________________________________ // // RNG::RNOR generates exponential variates with rejection. // // efix() generates variates after rejection in REXP. // double RNG::efix(ulong j, ulong i) // { // double x; // for (;;) // { // if (i == 0) // return (7.69711 - log(rand_open01())); // x = j * we[i]; // if (fe[i] + rand_open01() * (fe[i - 1] - fe[i]) < exp(-x)) // return (x); // j = rand_int32(); // i = (j & 255); // if (j < ke[i]) // return (j * we[i]); // } // } // RNG::efix // // __________________________________________________________________________ // // This procedure creates the tables used by RNOR and REXP // void RNG::zigset() // { // const double m1 = 2147483648.0; // 2^31 // const double m2 = 4294967296.0; // 2^32 // const double vn = 9.91256303526217e-3; // const double ve = 3.949659822581572e-3; // double dn = 3.442619855899, tn = dn; // double de = 7.697117470131487, te = de; // int i; // // Set up tables for RNOR // double q = vn / exp(-.5 * dn * dn); // kn[0] = (ulong) ((dn / q) * m1); // kn[1] = 0; // wn[0] = q / m1; // wn[127] = dn / m1; // fn[0]=1.; // fn[127] = exp(-.5 * dn * dn); // for(i = 126; i >= 1; i--) // { // dn = sqrt(-2 * log(vn / dn + exp(-.5 * dn * dn))); // kn[i + 1] = (ulong) ((dn / tn) * m1); // tn = dn; // fn[i] = exp(-.5 * dn * dn); // wn[i] = dn / m1; // } // // Set up tables for REXP // q = ve / exp(-de); // ke[0] = (ulong) ((de / q) * m2); // ke[1] = 0; // we[0] = q / m2; // we[255] = de / m2; // fe[0] = 1.; // fe[255] = exp(-de); // for (i = 254; i >= 1; i--) // { // de = -log(ve / de + exp(-de)); // ke[i+1] = (ulong) ((de / te) * m2); // te = de; // fe[i] = exp(-de); // we[i] = de / m2; // } // } // RNG::zigset // // __________________________________________________________________________ // // Generate a gamma variate with parameters 'shape' and 'scale' // double RNG::gamma(double shape, double scale) // { // if (shape < 1) // return gamma(shape + 1, scale) * pow(rand_open01(), 1.0 / shape); // const double d = shape - 1.0 / 3.0; // const double c = 1.0 / sqrt(9.0 * d); // double x, v, u; // for (;;) { // do { // x = RNOR(); // v = 1.0 + c * x; // } while (v <= 0.0); // v = v * v * v; // u = rand_open01(); // if (u < 1.0 - 0.0331 * x * x * x * x) // return (d * v / scale); // if (log(u) < 0.5 * x * x + d * (1.0 - v + log(v))) // return (d * v / scale); // } // } // RNG::gamma // // __________________________________________________________________________ // // gammalog returns the logarithm of the gamma function. From Numerical // // Recipes. // double gammalog(double xx) // { // static double cof[6]={ // 76.18009172947146, -86.50532032941677, 24.01409824083091, // -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5}; // double x = xx; // double y = xx; // double tmp = x + 5.5; // tmp -= (x + 0.5) * log(tmp); // double ser=1.000000000190015; // for (int j=0; j<=5; j++) // ser += cof[j] / ++y; // return -tmp + log(2.5066282746310005 * ser / x); // } // // __________________________________________________________________________ // // Generate a Poisson variate // // This is essentially the algorithm from Numerical Recipes // double RNG::poisson(double lambda) // { // static double sq, alxm, g, oldm = -1.0; // double em, t, y; // if (lambda < 12.0) { // if (lambda != oldm) { // oldm = lambda; // g = exp(-lambda); // } // em = -1; // t = 1.0; // do { // ++em; // t *= rand_open01(); // } while (t > g); // } else { // if (lambda != oldm) { // oldm = lambda; // sq = sqrt(2.0 * lambda); // alxm = log(lambda); // g = lambda * alxm - gammalog(lambda + 1.0); // } // do { // do { // y = tan(PI * rand_open01()); // em = sq * y + lambda; // } while (em < 0.0); // em = floor(em); // t = 0.9 * (1.0 + y * y) * exp(em * alxm - gammalog(em + 1.0)-g); // } while (rand_open01() > t); // } // return em; // } // RNG::poisson // // __________________________________________________________________________ // // Generate a binomial variate // // This is essentially the algorithm from Numerical Recipes // int RNG::binomial(double pp, int n) // { // if(n==0) return 0; // if(pp==0.0) return 0; // if(pp==1.0) return n; // double p = (pp<0.5 ? pp : 1.0-pp); // double am = n*p; // int bnl = 0; // if(n<25) { // for(int j=n; j--; ) if(rand_closed01()= en + 1.0); // em = floor(em); // t = 1.2 * sq * (1 + y * y) * exp(oldg - gammalog(em + 1.0) - // gammalog(en - em + 1.0) + em * log(p) + (en - em) * log(pc)); // } while (rand_closed01() > t); // bnl = int(em); // } // if (p!=pp) bnl=n-bnl; // return bnl; // } // RNG::binomial // __________________________________________________________________________ // rng.C igraph/src/amd_control.c0000644000176000001440000000370112325527072015016 0ustar ripleyusers/* ========================================================================= */ /* === AMD_control ========================================================= */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* User-callable. Prints the control parameters for AMD. See amd.h * for details. If the Control array is not present, the defaults are * printed instead. */ #include "amd_internal.h" GLOBAL void AMD_control ( double Control [ ] ) { double alpha ; Int aggressive ; if (Control != (double *) NULL) { alpha = Control [AMD_DENSE] ; aggressive = Control [AMD_AGGRESSIVE] != 0 ; } else { alpha = AMD_DEFAULT_DENSE ; aggressive = AMD_DEFAULT_AGGRESSIVE ; } PRINTF (("\nAMD version %d.%d.%d, %s: approximate minimum degree ordering\n" " dense row parameter: %g\n", AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE, alpha)) ; if (alpha < 0) { PRINTF ((" no rows treated as dense\n")) ; } else { PRINTF (( " (rows with more than max (%g * sqrt (n), 16) entries are\n" " considered \"dense\", and placed last in output permutation)\n", alpha)) ; } if (aggressive) { PRINTF ((" aggressive absorption: yes\n")) ; } else { PRINTF ((" aggressive absorption: no\n")) ; } PRINTF ((" size of AMD integer: %d\n\n", sizeof (Int))) ; } igraph/src/glpipm.c0000644000176000001440000011423512325527073014013 0ustar ripleyusers/* glpipm.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpipm.h" #include "glpmat.h" #define ITER_MAX 100 /* maximal number of iterations */ struct csa { /* common storage area */ /*--------------------------------------------------------------*/ /* LP data */ int m; /* number of rows (equality constraints) */ int n; /* number of columns (structural variables) */ int *A_ptr; /* int A_ptr[1+m+1]; */ int *A_ind; /* int A_ind[A_ptr[m+1]]; */ double *A_val; /* double A_val[A_ptr[m+1]]; */ /* mxn-matrix A in storage-by-rows format */ double *b; /* double b[1+m]; */ /* m-vector b of right-hand sides */ double *c; /* double c[1+n]; */ /* n-vector c of objective coefficients; c[0] is constant term of the objective function */ /*--------------------------------------------------------------*/ /* LP solution */ double *x; /* double x[1+n]; */ double *y; /* double y[1+m]; */ double *z; /* double z[1+n]; */ /* current point in primal-dual space; the best point on exit */ /*--------------------------------------------------------------*/ /* control parameters */ const glp_iptcp *parm; /*--------------------------------------------------------------*/ /* working arrays and variables */ double *D; /* double D[1+n]; */ /* diagonal nxn-matrix D = X*inv(Z), where X = diag(x[j]) and Z = diag(z[j]) */ int *P; /* int P[1+m+m]; */ /* permutation mxm-matrix P used to minimize fill-in in Cholesky factorization */ int *S_ptr; /* int S_ptr[1+m+1]; */ int *S_ind; /* int S_ind[S_ptr[m+1]]; */ double *S_val; /* double S_val[S_ptr[m+1]]; */ double *S_diag; /* double S_diag[1+m]; */ /* symmetric mxm-matrix S = P*A*D*A'*P' whose upper triangular part without diagonal elements is stored in S_ptr, S_ind, and S_val in storage-by-rows format, diagonal elements are stored in S_diag */ int *U_ptr; /* int U_ptr[1+m+1]; */ int *U_ind; /* int U_ind[U_ptr[m+1]]; */ double *U_val; /* double U_val[U_ptr[m+1]]; */ double *U_diag; /* double U_diag[1+m]; */ /* upper triangular mxm-matrix U defining Cholesky factorization S = U'*U; its non-diagonal elements are stored in U_ptr, U_ind, U_val in storage-by-rows format, diagonal elements are stored in U_diag */ int iter; /* iteration number (0, 1, 2, ...); iter = 0 corresponds to the initial point */ double obj; /* current value of the objective function */ double rpi; /* relative primal infeasibility rpi = ||A*x-b||/(1+||b||) */ double rdi; /* relative dual infeasibility rdi = ||A'*y+z-c||/(1+||c||) */ double gap; /* primal-dual gap = |c'*x-b'*y|/(1+|c'*x|) which is a relative difference between primal and dual objective functions */ double phi; /* merit function phi = ||A*x-b||/max(1,||b||) + + ||A'*y+z-c||/max(1,||c||) + + |c'*x-b'*y|/max(1,||b||,||c||) */ double mu; /* duality measure mu = x'*z/n (used as barrier parameter) */ double rmu; /* rmu = max(||A*x-b||,||A'*y+z-c||)/mu */ double rmu0; /* the initial value of rmu on iteration 0 */ double *phi_min; /* double phi_min[1+ITER_MAX]; */ /* phi_min[k] = min(phi[k]), where phi[k] is the value of phi on k-th iteration, 0 <= k <= iter */ int best_iter; /* iteration number, on which the value of phi reached its best (minimal) value */ double *best_x; /* double best_x[1+n]; */ double *best_y; /* double best_y[1+m]; */ double *best_z; /* double best_z[1+n]; */ /* best point (in the sense of the merit function phi) which has been reached on iteration iter_best */ double best_obj; /* objective value at the best point */ double *dx_aff; /* double dx_aff[1+n]; */ double *dy_aff; /* double dy_aff[1+m]; */ double *dz_aff; /* double dz_aff[1+n]; */ /* affine scaling direction */ double alfa_aff_p, alfa_aff_d; /* maximal primal and dual stepsizes in affine scaling direction, on which x and z are still non-negative */ double mu_aff; /* duality measure mu_aff = x_aff'*z_aff/n in the boundary point x_aff' = x+alfa_aff_p*dx_aff, z_aff' = z+alfa_aff_d*dz_aff */ double sigma; /* Mehrotra's heuristic parameter (0 <= sigma <= 1) */ double *dx_cc; /* double dx_cc[1+n]; */ double *dy_cc; /* double dy_cc[1+m]; */ double *dz_cc; /* double dz_cc[1+n]; */ /* centering corrector direction */ double *dx; /* double dx[1+n]; */ double *dy; /* double dy[1+m]; */ double *dz; /* double dz[1+n]; */ /* final combined direction dx = dx_aff+dx_cc, dy = dy_aff+dy_cc, dz = dz_aff+dz_cc */ double alfa_max_p; double alfa_max_d; /* maximal primal and dual stepsizes in combined direction, on which x and z are still non-negative */ }; /*********************************************************************** * initialize - allocate and initialize common storage area * * This routine allocates and initializes the common storage area (CSA) * used by interior-point method routines. */ static void initialize(struct csa *csa) { int m = csa->m; int n = csa->n; int i; if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Matrix A has %d non-zeros\n", csa->A_ptr[m+1]-1); csa->D = xcalloc(1+n, sizeof(double)); /* P := I */ csa->P = xcalloc(1+m+m, sizeof(int)); for (i = 1; i <= m; i++) csa->P[i] = csa->P[m+i] = i; /* S := A*A', symbolically */ csa->S_ptr = xcalloc(1+m+1, sizeof(int)); csa->S_ind = adat_symbolic(m, n, csa->P, csa->A_ptr, csa->A_ind, csa->S_ptr); if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Matrix S = A*A' has %d non-zeros (upper triangle)\n", csa->S_ptr[m+1]-1 + m); /* determine P using specified ordering algorithm */ if (csa->parm->ord_alg == GLP_ORD_NONE) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Original ordering is being used\n"); for (i = 1; i <= m; i++) csa->P[i] = csa->P[m+i] = i; } else if (csa->parm->ord_alg == GLP_ORD_QMD) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Minimum degree ordering (QMD)...\n"); min_degree(m, csa->S_ptr, csa->S_ind, csa->P); } else if (csa->parm->ord_alg == GLP_ORD_AMD) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Approximate minimum degree ordering (AMD)...\n"); amd_order1(m, csa->S_ptr, csa->S_ind, csa->P); } else if (csa->parm->ord_alg == GLP_ORD_SYMAMD) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Approximate minimum degree ordering (SYMAMD)...\n") ; symamd_ord(m, csa->S_ptr, csa->S_ind, csa->P); } else xassert(csa != csa); /* S := P*A*A'*P', symbolically */ xfree(csa->S_ind); csa->S_ind = adat_symbolic(m, n, csa->P, csa->A_ptr, csa->A_ind, csa->S_ptr); csa->S_val = xcalloc(csa->S_ptr[m+1], sizeof(double)); csa->S_diag = xcalloc(1+m, sizeof(double)); /* compute Cholesky factorization S = U'*U, symbolically */ if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Computing Cholesky factorization S = L*L'...\n"); csa->U_ptr = xcalloc(1+m+1, sizeof(int)); csa->U_ind = chol_symbolic(m, csa->S_ptr, csa->S_ind, csa->U_ptr); if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Matrix L has %d non-zeros\n", csa->U_ptr[m+1]-1 + m); csa->U_val = xcalloc(csa->U_ptr[m+1], sizeof(double)); csa->U_diag = xcalloc(1+m, sizeof(double)); csa->iter = 0; csa->obj = 0.0; csa->rpi = 0.0; csa->rdi = 0.0; csa->gap = 0.0; csa->phi = 0.0; csa->mu = 0.0; csa->rmu = 0.0; csa->rmu0 = 0.0; csa->phi_min = xcalloc(1+ITER_MAX, sizeof(double)); csa->best_iter = 0; csa->best_x = xcalloc(1+n, sizeof(double)); csa->best_y = xcalloc(1+m, sizeof(double)); csa->best_z = xcalloc(1+n, sizeof(double)); csa->best_obj = 0.0; csa->dx_aff = xcalloc(1+n, sizeof(double)); csa->dy_aff = xcalloc(1+m, sizeof(double)); csa->dz_aff = xcalloc(1+n, sizeof(double)); csa->alfa_aff_p = 0.0; csa->alfa_aff_d = 0.0; csa->mu_aff = 0.0; csa->sigma = 0.0; csa->dx_cc = xcalloc(1+n, sizeof(double)); csa->dy_cc = xcalloc(1+m, sizeof(double)); csa->dz_cc = xcalloc(1+n, sizeof(double)); csa->dx = csa->dx_aff; csa->dy = csa->dy_aff; csa->dz = csa->dz_aff; csa->alfa_max_p = 0.0; csa->alfa_max_d = 0.0; return; } /*********************************************************************** * A_by_vec - compute y = A*x * * This routine computes matrix-vector product y = A*x, where A is the * constraint matrix. */ static void A_by_vec(struct csa *csa, double x[], double y[]) { /* compute y = A*x */ int m = csa->m; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int i, t, beg, end; double temp; for (i = 1; i <= m; i++) { temp = 0.0; beg = A_ptr[i], end = A_ptr[i+1]; for (t = beg; t < end; t++) temp += A_val[t] * x[A_ind[t]]; y[i] = temp; } return; } /*********************************************************************** * AT_by_vec - compute y = A'*x * * This routine computes matrix-vector product y = A'*x, where A' is a * matrix transposed to the constraint matrix A. */ static void AT_by_vec(struct csa *csa, double x[], double y[]) { /* compute y = A'*x, where A' is transposed to A */ int m = csa->m; int n = csa->n; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int i, j, t, beg, end; double temp; for (j = 1; j <= n; j++) y[j] = 0.0; for (i = 1; i <= m; i++) { temp = x[i]; if (temp == 0.0) continue; beg = A_ptr[i], end = A_ptr[i+1]; for (t = beg; t < end; t++) y[A_ind[t]] += A_val[t] * temp; } return; } /*********************************************************************** * decomp_NE - numeric factorization of matrix S = P*A*D*A'*P' * * This routine implements numeric phase of Cholesky factorization of * the matrix S = P*A*D*A'*P', which is a permuted matrix of the normal * equation system. Matrix D is assumed to be already computed. */ static void decomp_NE(struct csa *csa) { adat_numeric(csa->m, csa->n, csa->P, csa->A_ptr, csa->A_ind, csa->A_val, csa->D, csa->S_ptr, csa->S_ind, csa->S_val, csa->S_diag); chol_numeric(csa->m, csa->S_ptr, csa->S_ind, csa->S_val, csa->S_diag, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag); return; } /*********************************************************************** * solve_NE - solve normal equation system * * This routine solves the normal equation system: * * A*D*A'*y = h. * * It is assumed that the matrix A*D*A' has been previously factorized * by the routine decomp_NE. * * On entry the array y contains the vector of right-hand sides h. On * exit this array contains the computed vector of unknowns y. * * Once the vector y has been computed the routine checks for numeric * stability. If the residual vector: * * r = A*D*A'*y - h * * is relatively small, the routine returns zero, otherwise non-zero is * returned. */ static int solve_NE(struct csa *csa, double y[]) { int m = csa->m; int n = csa->n; int *P = csa->P; int i, j, ret = 0; double *h, *r, *w; /* save vector of right-hand sides h */ h = xcalloc(1+m, sizeof(double)); for (i = 1; i <= m; i++) h[i] = y[i]; /* solve normal equation system (A*D*A')*y = h */ /* since S = P*A*D*A'*P' = U'*U, then A*D*A' = P'*U'*U*P, so we have inv(A*D*A') = P'*inv(U)*inv(U')*P */ /* w := P*h */ w = xcalloc(1+m, sizeof(double)); for (i = 1; i <= m; i++) w[i] = y[P[i]]; /* w := inv(U')*w */ ut_solve(m, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag, w); /* w := inv(U)*w */ u_solve(m, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag, w); /* y := P'*w */ for (i = 1; i <= m; i++) y[i] = w[P[m+i]]; xfree(w); /* compute residual vector r = A*D*A'*y - h */ r = xcalloc(1+m, sizeof(double)); /* w := A'*y */ w = xcalloc(1+n, sizeof(double)); AT_by_vec(csa, y, w); /* w := D*w */ for (j = 1; j <= n; j++) w[j] *= csa->D[j]; /* r := A*w */ A_by_vec(csa, w, r); xfree(w); /* r := r - h */ for (i = 1; i <= m; i++) r[i] -= h[i]; /* check for numeric stability */ for (i = 1; i <= m; i++) { if (fabs(r[i]) / (1.0 + fabs(h[i])) > 1e-4) { ret = 1; break; } } xfree(h); xfree(r); return ret; } /*********************************************************************** * solve_NS - solve Newtonian system * * This routine solves the Newtonian system: * * A*dx = p * * A'*dy + dz = q * * Z*dx + X*dz = r * * where X = diag(x[j]), Z = diag(z[j]), by reducing it to the normal * equation system: * * (A*inv(Z)*X*A')*dy = A*inv(Z)*(X*q-r)+p * * (it is assumed that the matrix A*inv(Z)*X*A' has been factorized by * the routine decomp_NE). * * Once vector dy has been computed the routine computes vectors dx and * dz as follows: * * dx = inv(Z)*(X*(A'*dy-q)+r) * * dz = inv(X)*(r-Z*dx) * * The routine solve_NS returns the same code which was reported by the * routine solve_NE (see above). */ static int solve_NS(struct csa *csa, double p[], double q[], double r[], double dx[], double dy[], double dz[]) { int m = csa->m; int n = csa->n; double *x = csa->x; double *z = csa->z; int i, j, ret; double *w = dx; /* compute the vector of right-hand sides A*inv(Z)*(X*q-r)+p for the normal equation system */ for (j = 1; j <= n; j++) w[j] = (x[j] * q[j] - r[j]) / z[j]; A_by_vec(csa, w, dy); for (i = 1; i <= m; i++) dy[i] += p[i]; /* solve the normal equation system to compute vector dy */ ret = solve_NE(csa, dy); /* compute vectors dx and dz */ AT_by_vec(csa, dy, dx); for (j = 1; j <= n; j++) { dx[j] = (x[j] * (dx[j] - q[j]) + r[j]) / z[j]; dz[j] = (r[j] - z[j] * dx[j]) / x[j]; } return ret; } /*********************************************************************** * initial_point - choose initial point using Mehrotra's heuristic * * This routine chooses a starting point using a heuristic proposed in * the paper: * * S. Mehrotra. On the implementation of a primal-dual interior point * method. SIAM J. on Optim., 2(4), pp. 575-601, 1992. * * The starting point x in the primal space is chosen as a solution of * the following least squares problem: * * minimize ||x|| * * subject to A*x = b * * which can be computed explicitly as follows: * * x = A'*inv(A*A')*b * * Similarly, the starting point (y, z) in the dual space is chosen as * a solution of the following least squares problem: * * minimize ||z|| * * subject to A'*y + z = c * * which can be computed explicitly as follows: * * y = inv(A*A')*A*c * * z = c - A'*y * * However, some components of the vectors x and z may be non-positive * or close to zero, so the routine uses a Mehrotra's heuristic to find * a more appropriate starting point. */ static void initial_point(struct csa *csa) { int m = csa->m; int n = csa->n; double *b = csa->b; double *c = csa->c; double *x = csa->x; double *y = csa->y; double *z = csa->z; double *D = csa->D; int i, j; double dp, dd, ex, ez, xz; /* factorize A*A' */ for (j = 1; j <= n; j++) D[j] = 1.0; decomp_NE(csa); /* x~ = A'*inv(A*A')*b */ for (i = 1; i <= m; i++) y[i] = b[i]; solve_NE(csa, y); AT_by_vec(csa, y, x); /* y~ = inv(A*A')*A*c */ A_by_vec(csa, c, y); solve_NE(csa, y); /* z~ = c - A'*y~ */ AT_by_vec(csa, y,z); for (j = 1; j <= n; j++) z[j] = c[j] - z[j]; /* use Mehrotra's heuristic in order to choose more appropriate starting point with positive components of vectors x and z */ dp = dd = 0.0; for (j = 1; j <= n; j++) { if (dp < -1.5 * x[j]) dp = -1.5 * x[j]; if (dd < -1.5 * z[j]) dd = -1.5 * z[j]; } /* note that b = 0 involves x = 0, and c = 0 involves y = 0 and z = 0, so we need to be careful */ if (dp == 0.0) dp = 1.5; if (dd == 0.0) dd = 1.5; ex = ez = xz = 0.0; for (j = 1; j <= n; j++) { ex += (x[j] + dp); ez += (z[j] + dd); xz += (x[j] + dp) * (z[j] + dd); } dp += 0.5 * (xz / ez); dd += 0.5 * (xz / ex); for (j = 1; j <= n; j++) { x[j] += dp; z[j] += dd; xassert(x[j] > 0.0 && z[j] > 0.0); } return; } /*********************************************************************** * basic_info - perform basic computations at the current point * * This routine computes the following quantities at the current point: * * 1) value of the objective function: * * F = c'*x + c[0] * * 2) relative primal infeasibility: * * rpi = ||A*x-b|| / (1+||b||) * * 3) relative dual infeasibility: * * rdi = ||A'*y+z-c|| / (1+||c||) * * 4) primal-dual gap (relative difference between the primal and the * dual objective function values): * * gap = |c'*x-b'*y| / (1+|c'*x|) * * 5) merit function: * * phi = ||A*x-b|| / max(1,||b||) + ||A'*y+z-c|| / max(1,||c||) + * * + |c'*x-b'*y| / max(1,||b||,||c||) * * 6) duality measure: * * mu = x'*z / n * * 7) the ratio of infeasibility to mu: * * rmu = max(||A*x-b||,||A'*y+z-c||) / mu * * where ||*|| denotes euclidian norm, *' denotes transposition. */ static void basic_info(struct csa *csa) { int m = csa->m; int n = csa->n; double *b = csa->b; double *c = csa->c; double *x = csa->x; double *y = csa->y; double *z = csa->z; int i, j; double norm1, bnorm, norm2, cnorm, cx, by, *work, temp; /* compute value of the objective function */ temp = c[0]; for (j = 1; j <= n; j++) temp += c[j] * x[j]; csa->obj = temp; /* norm1 = ||A*x-b|| */ work = xcalloc(1+m, sizeof(double)); A_by_vec(csa, x, work); norm1 = 0.0; for (i = 1; i <= m; i++) norm1 += (work[i] - b[i]) * (work[i] - b[i]); norm1 = sqrt(norm1); xfree(work); /* bnorm = ||b|| */ bnorm = 0.0; for (i = 1; i <= m; i++) bnorm += b[i] * b[i]; bnorm = sqrt(bnorm); /* compute relative primal infeasibility */ csa->rpi = norm1 / (1.0 + bnorm); /* norm2 = ||A'*y+z-c|| */ work = xcalloc(1+n, sizeof(double)); AT_by_vec(csa, y, work); norm2 = 0.0; for (j = 1; j <= n; j++) norm2 += (work[j] + z[j] - c[j]) * (work[j] + z[j] - c[j]); norm2 = sqrt(norm2); xfree(work); /* cnorm = ||c|| */ cnorm = 0.0; for (j = 1; j <= n; j++) cnorm += c[j] * c[j]; cnorm = sqrt(cnorm); /* compute relative dual infeasibility */ csa->rdi = norm2 / (1.0 + cnorm); /* by = b'*y */ by = 0.0; for (i = 1; i <= m; i++) by += b[i] * y[i]; /* cx = c'*x */ cx = 0.0; for (j = 1; j <= n; j++) cx += c[j] * x[j]; /* compute primal-dual gap */ csa->gap = fabs(cx - by) / (1.0 + fabs(cx)); /* compute merit function */ csa->phi = 0.0; csa->phi += norm1 / (bnorm > 1.0 ? bnorm : 1.0); csa->phi += norm2 / (cnorm > 1.0 ? cnorm : 1.0); temp = 1.0; if (temp < bnorm) temp = bnorm; if (temp < cnorm) temp = cnorm; csa->phi += fabs(cx - by) / temp; /* compute duality measure */ temp = 0.0; for (j = 1; j <= n; j++) temp += x[j] * z[j]; csa->mu = temp / (double)n; /* compute the ratio of infeasibility to mu */ csa->rmu = (norm1 > norm2 ? norm1 : norm2) / csa->mu; return; } /*********************************************************************** * make_step - compute next point using Mehrotra's technique * * This routine computes the next point using the predictor-corrector * technique proposed in the paper: * * S. Mehrotra. On the implementation of a primal-dual interior point * method. SIAM J. on Optim., 2(4), pp. 575-601, 1992. * * At first, the routine computes so called affine scaling (predictor) * direction (dx_aff,dy_aff,dz_aff) which is a solution of the system: * * A*dx_aff = b - A*x * * A'*dy_aff + dz_aff = c - A'*y - z * * Z*dx_aff + X*dz_aff = - X*Z*e * * where (x,y,z) is the current point, X = diag(x[j]), Z = diag(z[j]), * e = (1,...,1)'. * * Then, the routine computes the centering parameter sigma, using the * following Mehrotra's heuristic: * * alfa_aff_p = inf{0 <= alfa <= 1 | x+alfa*dx_aff >= 0} * * alfa_aff_d = inf{0 <= alfa <= 1 | z+alfa*dz_aff >= 0} * * mu_aff = (x+alfa_aff_p*dx_aff)'*(z+alfa_aff_d*dz_aff)/n * * sigma = (mu_aff/mu)^3 * * where alfa_aff_p is the maximal stepsize along the affine scaling * direction in the primal space, alfa_aff_d is the maximal stepsize * along the same direction in the dual space. * * After determining sigma the routine computes so called centering * (corrector) direction (dx_cc,dy_cc,dz_cc) which is the solution of * the system: * * A*dx_cc = 0 * * A'*dy_cc + dz_cc = 0 * * Z*dx_cc + X*dz_cc = sigma*mu*e - X*Z*e * * Finally, the routine computes the combined direction * * (dx,dy,dz) = (dx_aff,dy_aff,dz_aff) + (dx_cc,dy_cc,dz_cc) * * and determines maximal primal and dual stepsizes along the combined * direction: * * alfa_max_p = inf{0 <= alfa <= 1 | x+alfa*dx >= 0} * * alfa_max_d = inf{0 <= alfa <= 1 | z+alfa*dz >= 0} * * In order to prevent the next point to be too close to the boundary * of the positive ortant, the routine decreases maximal stepsizes: * * alfa_p = gamma_p * alfa_max_p * * alfa_d = gamma_d * alfa_max_d * * where gamma_p and gamma_d are scaling factors, and computes the next * point: * * x_new = x + alfa_p * dx * * y_new = y + alfa_d * dy * * z_new = z + alfa_d * dz * * which becomes the current point on the next iteration. */ static int make_step(struct csa *csa) { int m = csa->m; int n = csa->n; double *b = csa->b; double *c = csa->c; double *x = csa->x; double *y = csa->y; double *z = csa->z; double *dx_aff = csa->dx_aff; double *dy_aff = csa->dy_aff; double *dz_aff = csa->dz_aff; double *dx_cc = csa->dx_cc; double *dy_cc = csa->dy_cc; double *dz_cc = csa->dz_cc; double *dx = csa->dx; double *dy = csa->dy; double *dz = csa->dz; int i, j, ret = 0; double temp, gamma_p, gamma_d, *p, *q, *r; /* allocate working arrays */ p = xcalloc(1+m, sizeof(double)); q = xcalloc(1+n, sizeof(double)); r = xcalloc(1+n, sizeof(double)); /* p = b - A*x */ A_by_vec(csa, x, p); for (i = 1; i <= m; i++) p[i] = b[i] - p[i]; /* q = c - A'*y - z */ AT_by_vec(csa, y,q); for (j = 1; j <= n; j++) q[j] = c[j] - q[j] - z[j]; /* r = - X * Z * e */ for (j = 1; j <= n; j++) r[j] = - x[j] * z[j]; /* solve the first Newtonian system */ if (solve_NS(csa, p, q, r, dx_aff, dy_aff, dz_aff)) { ret = 1; goto done; } /* alfa_aff_p = inf{0 <= alfa <= 1 | x + alfa*dx_aff >= 0} */ /* alfa_aff_d = inf{0 <= alfa <= 1 | z + alfa*dz_aff >= 0} */ csa->alfa_aff_p = csa->alfa_aff_d = 1.0; for (j = 1; j <= n; j++) { if (dx_aff[j] < 0.0) { temp = - x[j] / dx_aff[j]; if (csa->alfa_aff_p > temp) csa->alfa_aff_p = temp; } if (dz_aff[j] < 0.0) { temp = - z[j] / dz_aff[j]; if (csa->alfa_aff_d > temp) csa->alfa_aff_d = temp; } } /* mu_aff = (x+alfa_aff_p*dx_aff)' * (z+alfa_aff_d*dz_aff) / n */ temp = 0.0; for (j = 1; j <= n; j++) temp += (x[j] + csa->alfa_aff_p * dx_aff[j]) * (z[j] + csa->alfa_aff_d * dz_aff[j]); csa->mu_aff = temp / (double)n; /* sigma = (mu_aff/mu)^3 */ temp = csa->mu_aff / csa->mu; csa->sigma = temp * temp * temp; /* p = 0 */ for (i = 1; i <= m; i++) p[i] = 0.0; /* q = 0 */ for (j = 1; j <= n; j++) q[j] = 0.0; /* r = sigma * mu * e - X * Z * e */ for (j = 1; j <= n; j++) r[j] = csa->sigma * csa->mu - dx_aff[j] * dz_aff[j]; /* solve the second Newtonian system with the same coefficients but with altered right-hand sides */ if (solve_NS(csa, p, q, r, dx_cc, dy_cc, dz_cc)) { ret = 1; goto done; } /* (dx,dy,dz) = (dx_aff,dy_aff,dz_aff) + (dx_cc,dy_cc,dz_cc) */ for (j = 1; j <= n; j++) dx[j] = dx_aff[j] + dx_cc[j]; for (i = 1; i <= m; i++) dy[i] = dy_aff[i] + dy_cc[i]; for (j = 1; j <= n; j++) dz[j] = dz_aff[j] + dz_cc[j]; /* alfa_max_p = inf{0 <= alfa <= 1 | x + alfa*dx >= 0} */ /* alfa_max_d = inf{0 <= alfa <= 1 | z + alfa*dz >= 0} */ csa->alfa_max_p = csa->alfa_max_d = 1.0; for (j = 1; j <= n; j++) { if (dx[j] < 0.0) { temp = - x[j] / dx[j]; if (csa->alfa_max_p > temp) csa->alfa_max_p = temp; } if (dz[j] < 0.0) { temp = - z[j] / dz[j]; if (csa->alfa_max_d > temp) csa->alfa_max_d = temp; } } /* determine scale factors (not implemented yet) */ gamma_p = 0.90; gamma_d = 0.90; /* compute the next point */ for (j = 1; j <= n; j++) { x[j] += gamma_p * csa->alfa_max_p * dx[j]; xassert(x[j] > 0.0); } for (i = 1; i <= m; i++) y[i] += gamma_d * csa->alfa_max_d * dy[i]; for (j = 1; j <= n; j++) { z[j] += gamma_d * csa->alfa_max_d * dz[j]; xassert(z[j] > 0.0); } done: /* free working arrays */ xfree(p); xfree(q); xfree(r); return ret; } /*********************************************************************** * terminate - deallocate common storage area * * This routine frees all memory allocated to the common storage area * used by interior-point method routines. */ static void terminate(struct csa *csa) { xfree(csa->D); xfree(csa->P); xfree(csa->S_ptr); xfree(csa->S_ind); xfree(csa->S_val); xfree(csa->S_diag); xfree(csa->U_ptr); xfree(csa->U_ind); xfree(csa->U_val); xfree(csa->U_diag); xfree(csa->phi_min); xfree(csa->best_x); xfree(csa->best_y); xfree(csa->best_z); xfree(csa->dx_aff); xfree(csa->dy_aff); xfree(csa->dz_aff); xfree(csa->dx_cc); xfree(csa->dy_cc); xfree(csa->dz_cc); return; } /*********************************************************************** * ipm_main - main interior-point method routine * * This is a main routine of the primal-dual interior-point method. * * The routine ipm_main returns one of the following codes: * * 0 - optimal solution found; * 1 - problem has no feasible (primal or dual) solution; * 2 - no convergence; * 3 - iteration limit exceeded; * 4 - numeric instability on solving Newtonian system. * * In case of non-zero return code the routine returns the best point, * which has been reached during optimization. */ static int ipm_main(struct csa *csa) { int m = csa->m; int n = csa->n; int i, j, status; double temp; /* choose initial point using Mehrotra's heuristic */ if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Guessing initial point...\n"); initial_point(csa); /* main loop starts here */ if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Optimization begins...\n"); for (;;) { /* perform basic computations at the current point */ basic_info(csa); /* save initial value of rmu */ if (csa->iter == 0) csa->rmu0 = csa->rmu; /* accumulate values of min(phi[k]) and save the best point */ xassert(csa->iter <= ITER_MAX); if (csa->iter == 0 || csa->phi_min[csa->iter-1] > csa->phi) { csa->phi_min[csa->iter] = csa->phi; csa->best_iter = csa->iter; for (j = 1; j <= n; j++) csa->best_x[j] = csa->x[j]; for (i = 1; i <= m; i++) csa->best_y[i] = csa->y[i]; for (j = 1; j <= n; j++) csa->best_z[j] = csa->z[j]; csa->best_obj = csa->obj; } else csa->phi_min[csa->iter] = csa->phi_min[csa->iter-1]; /* display information at the current point */ if (csa->parm->msg_lev >= GLP_MSG_ON) xprintf("%3d: obj = %17.9e; rpi = %8.1e; rdi = %8.1e; gap =" " %8.1e\n", csa->iter, csa->obj, csa->rpi, csa->rdi, csa->gap); /* check if the current point is optimal */ if (csa->rpi < 1e-8 && csa->rdi < 1e-8 && csa->gap < 1e-8) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("OPTIMAL SOLUTION FOUND\n"); status = 0; break; } /* check if the problem has no feasible solution */ temp = 1e5 * csa->phi_min[csa->iter]; if (temp < 1e-8) temp = 1e-8; if (csa->phi >= temp) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO FEASIBLE PRIMAL/DUAL SOLUTION\n") ; status = 1; break; } /* check for very slow convergence or divergence */ if (((csa->rpi >= 1e-8 || csa->rdi >= 1e-8) && csa->rmu / csa->rmu0 >= 1e6) || (csa->iter >= 30 && csa->phi_min[csa->iter] >= 0.5 * csa->phi_min[csa->iter - 30])) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("NO CONVERGENCE; SEARCH TERMINATED\n"); status = 2; break; } /* check for maximal number of iterations */ if (csa->iter == ITER_MAX) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED\n"); status = 3; break; } /* start the next iteration */ csa->iter++; /* factorize normal equation system */ for (j = 1; j <= n; j++) csa->D[j] = csa->x[j] / csa->z[j]; decomp_NE(csa); /* compute the next point using Mehrotra's predictor-corrector technique */ if (make_step(csa)) { if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("NUMERIC INSTABILITY; SEARCH TERMINATED\n"); status = 4; break; } } /* restore the best point */ if (status != 0) { for (j = 1; j <= n; j++) csa->x[j] = csa->best_x[j]; for (i = 1; i <= m; i++) csa->y[i] = csa->best_y[i]; for (j = 1; j <= n; j++) csa->z[j] = csa->best_z[j]; if (csa->parm->msg_lev >= GLP_MSG_ALL) xprintf("Best point %17.9e was reached on iteration %d\n", csa->best_obj, csa->best_iter); } /* return to the calling program */ return status; } /*********************************************************************** * NAME * * ipm_solve - core LP solver based on the interior-point method * * SYNOPSIS * * #include "glpipm.h" * int ipm_solve(glp_prob *P, const glp_iptcp *parm); * * DESCRIPTION * * The routine ipm_solve is a core LP solver based on the primal-dual * interior-point method. * * The routine assumes the following standard formulation of LP problem * to be solved: * * minimize * * F = c[0] + c[1]*x[1] + c[2]*x[2] + ... + c[n]*x[n] * * subject to linear constraints * * a[1,1]*x[1] + a[1,2]*x[2] + ... + a[1,n]*x[n] = b[1] * * a[2,1]*x[1] + a[2,2]*x[2] + ... + a[2,n]*x[n] = b[2] * * . . . . . . * * a[m,1]*x[1] + a[m,2]*x[2] + ... + a[m,n]*x[n] = b[m] * * and non-negative variables * * x[1] >= 0, x[2] >= 0, ..., x[n] >= 0 * * where: * F is the objective function; * x[1], ..., x[n] are (structural) variables; * c[0] is a constant term of the objective function; * c[1], ..., c[n] are objective coefficients; * a[1,1], ..., a[m,n] are constraint coefficients; * b[1], ..., b[n] are right-hand sides. * * The solution is three vectors x, y, and z, which are stored by the * routine in the arrays x, y, and z, respectively. These vectors * correspond to the best primal-dual point found during optimization. * They are approximate solution of the following system (which is the * Karush-Kuhn-Tucker optimality conditions): * * A*x = b (primal feasibility condition) * * A'*y + z = c (dual feasibility condition) * * x'*z = 0 (primal-dual complementarity condition) * * x >= 0, z >= 0 (non-negativity condition) * * where: * x[1], ..., x[n] are primal (structural) variables; * y[1], ..., y[m] are dual variables (Lagrange multipliers) for * equality constraints; * z[1], ..., z[n] are dual variables (Lagrange multipliers) for * non-negativity constraints. * * RETURNS * * 0 LP has been successfully solved. * * GLP_ENOCVG * No convergence. * * GLP_EITLIM * Iteration limit exceeded. * * GLP_EINSTAB * Numeric instability on solving Newtonian system. * * In case of non-zero return code the routine returns the best point, * which has been reached during optimization. */ int ipm_solve(glp_prob *P, const glp_iptcp *parm) { struct csa _dsa, *csa = &_dsa; int m = P->m; int n = P->n; int nnz = P->nnz; GLPROW *row; GLPCOL *col; GLPAIJ *aij; int i, j, loc, ret, *A_ind, *A_ptr; double dir, *A_val, *b, *c, *x, *y, *z; xassert(m > 0); xassert(n > 0); /* allocate working arrays */ A_ptr = xcalloc(1+m+1, sizeof(int)); A_ind = xcalloc(1+nnz, sizeof(int)); A_val = xcalloc(1+nnz, sizeof(double)); b = xcalloc(1+m, sizeof(double)); c = xcalloc(1+n, sizeof(double)); x = xcalloc(1+n, sizeof(double)); y = xcalloc(1+m, sizeof(double)); z = xcalloc(1+n, sizeof(double)); /* prepare rows and constraint coefficients */ loc = 1; for (i = 1; i <= m; i++) { row = P->row[i]; xassert(row->type == GLP_FX); b[i] = row->lb * row->rii; A_ptr[i] = loc; for (aij = row->ptr; aij != NULL; aij = aij->r_next) { A_ind[loc] = aij->col->j; A_val[loc] = row->rii * aij->val * aij->col->sjj; loc++; } } A_ptr[m+1] = loc; xassert(loc-1 == nnz); /* prepare columns and objective coefficients */ if (P->dir == GLP_MIN) dir = +1.0; else if (P->dir == GLP_MAX) dir = -1.0; else xassert(P != P); c[0] = dir * P->c0; for (j = 1; j <= n; j++) { col = P->col[j]; xassert(col->type == GLP_LO && col->lb == 0.0); c[j] = dir * col->coef * col->sjj; } /* allocate and initialize the common storage area */ csa->m = m; csa->n = n; csa->A_ptr = A_ptr; csa->A_ind = A_ind; csa->A_val = A_val; csa->b = b; csa->c = c; csa->x = x; csa->y = y; csa->z = z; csa->parm = parm; initialize(csa); /* solve LP with the interior-point method */ ret = ipm_main(csa); /* deallocate the common storage area */ terminate(csa); /* determine solution status */ if (ret == 0) { /* optimal solution found */ P->ipt_stat = GLP_OPT; ret = 0; } else if (ret == 1) { /* problem has no feasible (primal or dual) solution */ P->ipt_stat = GLP_NOFEAS; ret = 0; } else if (ret == 2) { /* no convergence */ P->ipt_stat = GLP_INFEAS; ret = GLP_ENOCVG; } else if (ret == 3) { /* iteration limit exceeded */ P->ipt_stat = GLP_INFEAS; ret = GLP_EITLIM; } else if (ret == 4) { /* numeric instability on solving Newtonian system */ P->ipt_stat = GLP_INFEAS; ret = GLP_EINSTAB; } else xassert(ret != ret); /* store row solution components */ for (i = 1; i <= m; i++) { row = P->row[i]; row->pval = row->lb; row->dval = dir * y[i] * row->rii; } /* store column solution components */ P->ipt_obj = P->c0; for (j = 1; j <= n; j++) { col = P->col[j]; col->pval = x[j] * col->sjj; col->dval = dir * z[j] / col->sjj; P->ipt_obj += col->coef * col->pval; } /* free working arrays */ xfree(A_ptr); xfree(A_ind); xfree(A_val); xfree(b); xfree(c); xfree(x); xfree(y); xfree(z); return ret; } /* eof */ igraph/src/dsesrt.f0000644000176000001440000001242612325527073014031 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsesrt c c\Description: c Sort the array X in the order specified by WHICH and optionally c apply the permutation to the columns of the matrix A. c c\Usage: c call igraphdsesrt c ( WHICH, APPLY, N, X, NA, A, LDA) c c\Arguments c WHICH Character*2. (Input) c 'LM' -> X is sorted into increasing order of magnitude. c 'SM' -> X is sorted into decreasing order of magnitude. c 'LA' -> X is sorted into increasing order of algebraic. c 'SA' -> X is sorted into decreasing order of algebraic. c c APPLY Logical. (Input) c APPLY = .TRUE. -> apply the sorted order to A. c APPLY = .FALSE. -> do not apply the sorted order to A. c c N Integer. (INPUT) c Dimension of the array X. c c X Double precision array of length N. (INPUT/OUTPUT) c The array to be sorted. c c NA Integer. (INPUT) c Number of rows of the matrix A. c c A Double precision array of length NA by N. (INPUT/OUTPUT) c c LDA Integer. (INPUT) c Leading dimension of A. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Routines c dswap Level 1 BLAS that swaps the contents of two vectors. c c\Authors c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c 12/15/93: Version ' 2.1'. c Adapted from the sort routine in LANSO and c the ARPACK code igraphdsortr c c\SCCS Information: @(#) c FILE: sesrt.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsesrt (which, apply, n, x, na, a, lda) c c %------------------% c | Scalar Arguments | c %------------------% c character*2 which logical apply integer lda, n, na c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & x(0:n-1), a(lda, 0:n-1) c c %---------------% c | Local Scalars | c %---------------% c integer i, igap, j Double precision & temp c c %----------------------% c | External Subroutines | c %----------------------% c external dswap c c %-----------------------% c | Executable Statements | c %-----------------------% c igap = n / 2 c if (which .eq. 'SA') then c c X is sorted into decreasing order of algebraic. c 10 continue if (igap .eq. 0) go to 9000 do 30 i = igap, n-1 j = i-igap 20 continue c if (j.lt.0) go to 30 c if (x(j).lt.x(j+igap)) then temp = x(j) x(j) = x(j+igap) x(j+igap) = temp if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) else go to 30 endif j = j-igap go to 20 30 continue igap = igap / 2 go to 10 c else if (which .eq. 'SM') then c c X is sorted into decreasing order of magnitude. c 40 continue if (igap .eq. 0) go to 9000 do 60 i = igap, n-1 j = i-igap 50 continue c if (j.lt.0) go to 60 c if (abs(x(j)).lt.abs(x(j+igap))) then temp = x(j) x(j) = x(j+igap) x(j+igap) = temp if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) else go to 60 endif j = j-igap go to 50 60 continue igap = igap / 2 go to 40 c else if (which .eq. 'LA') then c c X is sorted into increasing order of algebraic. c 70 continue if (igap .eq. 0) go to 9000 do 90 i = igap, n-1 j = i-igap 80 continue c if (j.lt.0) go to 90 c if (x(j).gt.x(j+igap)) then temp = x(j) x(j) = x(j+igap) x(j+igap) = temp if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) else go to 90 endif j = j-igap go to 80 90 continue igap = igap / 2 go to 70 c else if (which .eq. 'LM') then c c X is sorted into increasing order of magnitude. c 100 continue if (igap .eq. 0) go to 9000 do 120 i = igap, n-1 j = i-igap 110 continue c if (j.lt.0) go to 120 c if (abs(x(j)).gt.abs(x(j+igap))) then temp = x(j) x(j) = x(j+igap) x(j+igap) = temp if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) else go to 120 endif j = j-igap go to 110 120 continue igap = igap / 2 go to 100 end if c 9000 continue return c c %---------------% c | End of igraphdsesrt | c %---------------% c end igraph/src/memory.c0000644000176000001440000000440412325527073014027 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_memory.h" #include "config.h" /** * \function igraph_free * Deallocate memory that was allocated by igraph functions * * Some igraph functions return a pointer vector (igraph_vector_ptr_t) * containing pointers to other igraph or other data types. These data * types are dynamically allocated and have to be deallocated * manually, if the user does not need them any more. This can be done * by calling igraph_free on them. * * * Here is a complete example on how to use \c igraph_free properly. * * * * int main(void) * { * igraph_t graph; * igraph_vector_ptr_t seps; * long int i; * * igraph_famous(&graph, "tutte"); * igraph_vector_ptr_init(&seps, 0); * igraph_minimum_size_separators(&graph, &seps); * * for (i=0; i * * * * \param p Pointer to the piece of memory to be deallocated. * \return Error code, currently always zero, meaning success. * * Time complexity: platform dependent, ideally it should be O(1). */ int igraph_free(void *p) { igraph_Free(p); return 0; } igraph/src/glpapi16.c0000644000176000001440000002562112325527073014146 0ustar ripleyusers/* glpapi16.c (graph and network analysis routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" #include "glpnet.h" /*********************************************************************** * NAME * * glp_weak_comp - find all weakly connected components of graph * * SYNOPSIS * * int glp_weak_comp(glp_graph *G, int v_num); * * DESCRIPTION * * The routine glp_weak_comp finds all weakly connected components of * the specified graph. * * The parameter v_num specifies an offset of the field of type int * in the vertex data block, to which the routine stores the number of * a (weakly) connected component containing that vertex. If v_num < 0, * no component numbers are stored. * * The components are numbered in arbitrary order from 1 to nc, where * nc is the total number of components found, 0 <= nc <= |V|. * * RETURNS * * The routine returns nc, the total number of components found. */ int glp_weak_comp(glp_graph *G, int v_num) { glp_vertex *v; glp_arc *a; int f, i, j, nc, nv, pos1, pos2, *prev, *next, *list; if (v_num >= 0 && v_num > G->v_size - (int)sizeof(int)) xerror("glp_weak_comp: v_num = %d; invalid offset\n", v_num); nv = G->nv; if (nv == 0) { nc = 0; goto done; } /* allocate working arrays */ prev = xcalloc(1+nv, sizeof(int)); next = xcalloc(1+nv, sizeof(int)); list = xcalloc(1+nv, sizeof(int)); /* if vertex i is unlabelled, prev[i] is the index of previous unlabelled vertex, and next[i] is the index of next unlabelled vertex; if vertex i is labelled, then prev[i] < 0, and next[i] is the connected component number */ /* initially all vertices are unlabelled */ f = 1; for (i = 1; i <= nv; i++) prev[i] = i - 1, next[i] = i + 1; next[nv] = 0; /* main loop (until all vertices have been labelled) */ nc = 0; while (f != 0) { /* take an unlabelled vertex */ i = f; /* and remove it from the list of unlabelled vertices */ f = next[i]; if (f != 0) prev[f] = 0; /* label the vertex; it begins a new component */ prev[i] = -1, next[i] = ++nc; /* breadth first search */ list[1] = i, pos1 = pos2 = 1; while (pos1 <= pos2) { /* dequeue vertex i */ i = list[pos1++]; /* consider all arcs incoming to vertex i */ for (a = G->v[i]->in; a != NULL; a = a->h_next) { /* vertex j is adjacent to vertex i */ j = a->tail->i; if (prev[j] >= 0) { /* vertex j is unlabelled */ /* remove it from the list of unlabelled vertices */ if (prev[j] == 0) f = next[j]; else next[prev[j]] = next[j]; if (next[j] == 0) ; else prev[next[j]] = prev[j]; /* label the vertex */ prev[j] = -1, next[j] = nc; /* and enqueue it for further consideration */ list[++pos2] = j; } } /* consider all arcs outgoing from vertex i */ for (a = G->v[i]->out; a != NULL; a = a->t_next) { /* vertex j is adjacent to vertex i */ j = a->head->i; if (prev[j] >= 0) { /* vertex j is unlabelled */ /* remove it from the list of unlabelled vertices */ if (prev[j] == 0) f = next[j]; else next[prev[j]] = next[j]; if (next[j] == 0) ; else prev[next[j]] = prev[j]; /* label the vertex */ prev[j] = -1, next[j] = nc; /* and enqueue it for further consideration */ list[++pos2] = j; } } } } /* store component numbers */ if (v_num >= 0) { for (i = 1; i <= nv; i++) { v = G->v[i]; memcpy((char *)v->data + v_num, &next[i], sizeof(int)); } } /* free working arrays */ xfree(prev); xfree(next); xfree(list); done: return nc; } /*********************************************************************** * NAME * * glp_strong_comp - find all strongly connected components of graph * * SYNOPSIS * * int glp_strong_comp(glp_graph *G, int v_num); * * DESCRIPTION * * The routine glp_strong_comp finds all strongly connected components * of the specified graph. * * The parameter v_num specifies an offset of the field of type int * in the vertex data block, to which the routine stores the number of * a strongly connected component containing that vertex. If v_num < 0, * no component numbers are stored. * * The components are numbered in arbitrary order from 1 to nc, where * nc is the total number of components found, 0 <= nc <= |V|. However, * the component numbering has the property that for every arc (i->j) * in the graph the condition num(i) >= num(j) holds. * * RETURNS * * The routine returns nc, the total number of components found. */ int glp_strong_comp(glp_graph *G, int v_num) { glp_vertex *v; glp_arc *a; int i, k, last, n, na, nc, *icn, *ip, *lenr, *ior, *ib, *lowl, *numb, *prev; if (v_num >= 0 && v_num > G->v_size - (int)sizeof(int)) xerror("glp_strong_comp: v_num = %d; invalid offset\n", v_num); n = G->nv; if (n == 0) { nc = 0; goto done; } na = G->na; icn = xcalloc(1+na, sizeof(int)); ip = xcalloc(1+n, sizeof(int)); lenr = xcalloc(1+n, sizeof(int)); ior = xcalloc(1+n, sizeof(int)); ib = xcalloc(1+n, sizeof(int)); lowl = xcalloc(1+n, sizeof(int)); numb = xcalloc(1+n, sizeof(int)); prev = xcalloc(1+n, sizeof(int)); k = 1; for (i = 1; i <= n; i++) { v = G->v[i]; ip[i] = k; for (a = v->out; a != NULL; a = a->t_next) icn[k++] = a->head->i; lenr[i] = k - ip[i]; } xassert(na == k-1); nc = mc13d(n, icn, ip, lenr, ior, ib, lowl, numb, prev); if (v_num >= 0) { xassert(ib[1] == 1); for (k = 1; k <= nc; k++) { last = (k < nc ? ib[k+1] : n+1); xassert(ib[k] < last); for (i = ib[k]; i < last; i++) { v = G->v[ior[i]]; memcpy((char *)v->data + v_num, &k, sizeof(int)); } } } xfree(icn); xfree(ip); xfree(lenr); xfree(ior); xfree(ib); xfree(lowl); xfree(numb); xfree(prev); done: return nc; } /*********************************************************************** * NAME * * glp_top_sort - topological sorting of acyclic digraph * * SYNOPSIS * * int glp_top_sort(glp_graph *G, int v_num); * * DESCRIPTION * * The routine glp_top_sort performs topological sorting of vertices of * the specified acyclic digraph. * * The parameter v_num specifies an offset of the field of type int in * the vertex data block, to which the routine stores the vertex number * assigned. If v_num < 0, vertex numbers are not stored. * * The vertices are numbered from 1 to n, where n is the total number * of vertices in the graph. The vertex numbering has the property that * for every arc (i->j) in the graph the condition num(i) < num(j) * holds. Special case num(i) = 0 means that vertex i is not assigned a * number, because the graph is *not* acyclic. * * RETURNS * * If the graph is acyclic and therefore all the vertices have been * assigned numbers, the routine glp_top_sort returns zero. Otherwise, * if the graph is not acyclic, the routine returns the number of * vertices which have not been numbered, i.e. for which num(i) = 0. */ static int top_sort(glp_graph *G, int num[]) { glp_arc *a; int i, j, cnt, top, *stack, *indeg; /* allocate working arrays */ indeg = xcalloc(1+G->nv, sizeof(int)); stack = xcalloc(1+G->nv, sizeof(int)); /* determine initial indegree of each vertex; push into the stack the vertices having zero indegree */ top = 0; for (i = 1; i <= G->nv; i++) { num[i] = indeg[i] = 0; for (a = G->v[i]->in; a != NULL; a = a->h_next) indeg[i]++; if (indeg[i] == 0) stack[++top] = i; } /* assign numbers to vertices in the sorted order */ cnt = 0; while (top > 0) { /* pull vertex i from the stack */ i = stack[top--]; /* it has zero indegree in the current graph */ xassert(indeg[i] == 0); /* so assign it a next number */ xassert(num[i] == 0); num[i] = ++cnt; /* remove vertex i from the current graph, update indegree of its adjacent vertices, and push into the stack new vertices whose indegree becomes zero */ for (a = G->v[i]->out; a != NULL; a = a->t_next) { j = a->head->i; /* there exists arc (i->j) in the graph */ xassert(indeg[j] > 0); indeg[j]--; if (indeg[j] == 0) stack[++top] = j; } } /* free working arrays */ xfree(indeg); xfree(stack); return G->nv - cnt; } int glp_top_sort(glp_graph *G, int v_num) { glp_vertex *v; int i, cnt, *num; if (v_num >= 0 && v_num > G->v_size - (int)sizeof(int)) xerror("glp_top_sort: v_num = %d; invalid offset\n", v_num); if (G->nv == 0) { cnt = 0; goto done; } num = xcalloc(1+G->nv, sizeof(int)); cnt = top_sort(G, num); if (v_num >= 0) { for (i = 1; i <= G->nv; i++) { v = G->v[i]; memcpy((char *)v->data + v_num, &num[i], sizeof(int)); } } xfree(num); done: return cnt; } /* eof */ igraph/src/error.c0000644000176000001440000000437112325527073013653 0ustar ripleyusers/* error.c * * Copyright (C) 2010-2011 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "error.h" static char *plfit_i_error_strings[] = { "No error", "Failed", "Invalid value", "Underflow", "Overflow", "Not enough memory" }; #ifndef USING_R static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_abort; #else /* This is overwritten, anyway */ static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_ignore; #endif const char* plfit_strerror(const int plfit_errno) { return plfit_i_error_strings[plfit_errno]; } plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler) { plfit_error_handler_t* old_handler = plfit_error_handler; plfit_error_handler = new_handler; return old_handler; } void plfit_error(const char *reason, const char *file, int line, int plfit_errno) { plfit_error_handler(reason, file, line, plfit_errno); } #ifndef USING_R void plfit_error_handler_abort(const char *reason, const char *file, int line, int plfit_errno) { fprintf(stderr, "Error at %s:%i : %s, %s\n", file, line, reason, plfit_strerror(plfit_errno)); abort(); } #endif #ifndef USING_R void plfit_error_handler_printignore(const char *reason, const char *file, int line, int plfit_errno) { fprintf(stderr, "Error at %s:%i : %s, %s\n", file, line, reason, plfit_strerror(plfit_errno)); } #endif void plfit_error_handler_ignore(const char *reason, const char *file, int line, int plfit_errno) { } igraph/src/igraph_hrg.h0000644000176000001440000000760212325527073014641 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_HRG_H #define IGRAPH_HRG_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_datatype.h" __BEGIN_DECLS /** * \struct igraph_hrg_t * Data structure to store a hierarchical random graph * * A hierarchical random graph (HRG) can be given as a binary tree, * where the internal vertices are labeled with real numbers. * * Note that you don't necessarily have to know this * internal representation for using the HRG functions, just pass the * HRG objects created by one igraph function, to another igraph * function. * * * It has the following members: * \member left Vector that contains the left children of the internal * tree vertices. The first vertex is always the root vertex, so * the first element of the vector is the left child of the root * vertex. Internal vertices are denoted with negative numbers, * starting from -1 and going down, i.e. the root vertex is * -1. Leaf vertices are denoted by non-negative number, starting * from zero and up. * \member right Vector that contains the right children of the * vertices, with the same encoding as the \c left vector. * \member prob The connection probabilities attached to the internal * vertices, the first number belongs to the root vertex * (i.e. internal vertex -1), the second to internal vertex -2, * etc. * \member edges The number of edges in the subtree below the given * internal vertex. * \member vertices The number of vertices in the subtree below the * given internal vertex, including itself. */ typedef struct igraph_hrg_t { igraph_vector_t left, right, prob, edges, vertices; } igraph_hrg_t; int igraph_hrg_init(igraph_hrg_t *hrg, int n); void igraph_hrg_destroy(igraph_hrg_t *hrg); int igraph_hrg_size(const igraph_hrg_t *hrg); int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize); int igraph_hrg_fit(const igraph_t *graph, igraph_hrg_t *hrg, igraph_bool_t start, int steps); int igraph_hrg_sample(const igraph_t *graph, igraph_t *sample, igraph_vector_ptr_t *samples, igraph_hrg_t *hrg, igraph_bool_t start); int igraph_hrg_game(igraph_t *graph, const igraph_hrg_t *hrg); int igraph_hrg_dendrogram(igraph_t *graph, const igraph_hrg_t *hrg); int igraph_hrg_consensus(const igraph_t *graph, igraph_vector_t *parents, igraph_vector_t *weights, igraph_hrg_t *hrg, igraph_bool_t start, int num_samples); int igraph_hrg_predict(const igraph_t *graph, igraph_vector_t *edges, igraph_vector_t *prob, igraph_hrg_t *hrg, igraph_bool_t start, int num_samples, int num_bins); int igraph_hrg_create(igraph_hrg_t *hrg, const igraph_t *graph, const igraph_vector_t *prob); __END_DECLS #endif /* IGRAPH_HRG_H */ igraph/src/dsapps.f0000644000176000001440000004431212325527073014016 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsapps c c\Description: c Given the Arnoldi factorization c c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, c c apply NP shifts implicitly resulting in c c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q c c where Q is an orthogonal matrix of order KEV+NP. Q is the product of c rotations resulting from the NP bulge chasing sweeps. The updated Arnoldi c factorization becomes: c c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. c c\Usage: c call igraphdsapps c ( N, KEV, NP, SHIFT, V, LDV, H, LDH, RESID, Q, LDQ, WORKD ) c c\Arguments c N Integer. (INPUT) c Problem size, i.e. dimension of matrix A. c c KEV Integer. (INPUT) c INPUT: KEV+NP is the size of the input matrix H. c OUTPUT: KEV is the size of the updated matrix HNEW. c c NP Integer. (INPUT) c Number of implicit shifts to be applied. c c SHIFT Double precision array of length NP. (INPUT) c The shifts to be applied. c c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) c INPUT: V contains the current KEV+NP Arnoldi vectors. c OUTPUT: VNEW = V(1:n,1:KEV); the updated Arnoldi vectors c are in the first KEV columns of V. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c H Double precision (KEV+NP) by 2 array. (INPUT/OUTPUT) c INPUT: H contains the symmetric tridiagonal matrix of the c Arnoldi factorization with the subdiagonal in the 1st column c starting at H(2,1) and the main diagonal in the 2nd column. c OUTPUT: H contains the updated tridiagonal matrix in the c KEV leading submatrix. c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c RESID Double precision array of length (N). (INPUT/OUTPUT) c INPUT: RESID contains the the residual vector r_{k+p}. c OUTPUT: RESID is the updated residual vector rnew_{k}. c c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) c Work array used to accumulate the rotations during the bulge c chase sweep. c c LDQ Integer. (INPUT) c Leading dimension of Q exactly as declared in the calling c program. c c WORKD Double precision work array of length 2*N. (WORKSPACE) c Distributed array used in the application of the accumulated c orthogonal matrix Q. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c c\Routines called: c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdvout ARPACK utility routine that prints vectors. c dlamch LAPACK routine that determines machine constants. c dlartg LAPACK Givens rotation construction routine. c dlacpy LAPACK matrix copy routine. c dlaset LAPACK matrix initialization routine. c dgemv Level 2 BLAS routine for matrix vector multiplication. c daxpy Level 1 BLAS that computes a vector triad. c dcopy Level 1 BLAS that copies one vector to another. c dscal Level 1 BLAS that scales a vector. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c 12/16/93: Version ' 2.1' c c\SCCS Information: @(#) c FILE: sapps.F SID: 2.5 DATE OF SID: 4/19/96 RELEASE: 2 c c\Remarks c 1. In this version, each shift is applied to all the subblocks of c the tridiagonal matrix H and not just to the submatrix that it c comes from. This routine assumes that the subdiagonal elements c of H that are stored in h(1:kev+np,1) are nonegative upon input c and enforce this condition upon output. This version incorporates c deflation. See code for documentation. c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsapps & ( n, kev, np, shift, v, ldv, h, ldh, resid, q, ldq, workd ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c integer kev, ldh, ldq, ldv, n, np c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & h(ldh,2), q(ldq,kev+np), resid(n), shift(np), & v(ldv,kev+np), workd(2*n) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c integer i, iend, istart, itop, j, jj, kplusp, msglvl logical first Double precision & a1, a2, a3, a4, big, c, epsmch, f, g, r, s save epsmch, first c c c %----------------------% c | External Subroutines | c %----------------------% c external daxpy, dcopy, dscal, dlacpy, dlartg, dlaset, & igraphdvout, igraphivout, igraphsecond, dgemv c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlamch external dlamch c c %----------------------% c | Intrinsics Functions | c %----------------------% c intrinsic abs c c %----------------% c | Data statments | c %----------------% c data first / .true. / c c %-----------------------% c | Executable Statements | c %-----------------------% c if (first) then epsmch = dlamch('Epsilon-Machine') first = .false. end if itop = 1 c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = msapps c kplusp = kev + np c c %----------------------------------------------% c | Initialize Q to the identity matrix of order | c | kplusp used to accumulate the rotations. | c %----------------------------------------------% c call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) c c %----------------------------------------------% c | Quick return if there are no shifts to apply | c %----------------------------------------------% c if (np .eq. 0) go to 9000 c c %----------------------------------------------------------% c | Apply the np shifts implicitly. Apply each shift to the | c | whole matrix and not just to the submatrix from which it | c | comes. | c %----------------------------------------------------------% c do 90 jj = 1, np c istart = itop c c %----------------------------------------------------------% c | Check for splitting and deflation. Currently we consider | c | an off-diagonal element h(i+1,1) negligible if | c | h(i+1,1) .le. epsmch*( |h(i,2)| + |h(i+1,2)| ) | c | for i=1:KEV+NP-1. | c | If above condition tests true then we set h(i+1,1) = 0. | c | Note that h(1:KEV+NP,1) are assumed to be non negative. | c %----------------------------------------------------------% c 20 continue c c %------------------------------------------------% c | The following loop exits early if we encounter | c | a negligible off diagonal element. | c %------------------------------------------------% c do 30 i = istart, kplusp-1 big = abs(h(i,2)) + abs(h(i+1,2)) if (h(i+1,1) .le. epsmch*big) then if (msglvl .gt. 0) then call igraphivout (logfil, 1, i, ndigit, & '_sapps: deflation at row/column no.') call igraphivout (logfil, 1, jj, ndigit, & '_sapps: occured before shift number.') call igraphdvout (logfil, 1, h(i+1,1), ndigit, & '_sapps: the corresponding off diagonal element') end if h(i+1,1) = zero iend = i go to 40 end if 30 continue iend = kplusp 40 continue c if (istart .lt. iend) then c c %--------------------------------------------------------% c | Construct the plane rotation G'(istart,istart+1,theta) | c | that attempts to drive h(istart+1,1) to zero. | c %--------------------------------------------------------% c f = h(istart,2) - shift(jj) g = h(istart+1,1) call dlartg (f, g, c, s, r) c c %-------------------------------------------------------% c | Apply rotation to the left and right of H; | c | H <- G' * H * G, where G = G(istart,istart+1,theta). | c | This will create a "bulge". | c %-------------------------------------------------------% c a1 = c*h(istart,2) + s*h(istart+1,1) a2 = c*h(istart+1,1) + s*h(istart+1,2) a4 = c*h(istart+1,2) - s*h(istart+1,1) a3 = c*h(istart+1,1) - s*h(istart,2) h(istart,2) = c*a1 + s*a2 h(istart+1,2) = c*a4 - s*a3 h(istart+1,1) = c*a3 + s*a4 c c %----------------------------------------------------% c | Accumulate the rotation in the matrix Q; Q <- Q*G | c %----------------------------------------------------% c do 60 j = 1, min(istart+jj,kplusp) a1 = c*q(j,istart) + s*q(j,istart+1) q(j,istart+1) = - s*q(j,istart) + c*q(j,istart+1) q(j,istart) = a1 60 continue c c c %----------------------------------------------% c | The following loop chases the bulge created. | c | Note that the previous rotation may also be | c | done within the following loop. But it is | c | kept separate to make the distinction among | c | the bulge chasing sweeps and the first plane | c | rotation designed to drive h(istart+1,1) to | c | zero. | c %----------------------------------------------% c do 70 i = istart+1, iend-1 c c %----------------------------------------------% c | Construct the plane rotation G'(i,i+1,theta) | c | that zeros the i-th bulge that was created | c | by G(i-1,i,theta). g represents the bulge. | c %----------------------------------------------% c f = h(i,1) g = s*h(i+1,1) c c %----------------------------------% c | Final update with G(i-1,i,theta) | c %----------------------------------% c h(i+1,1) = c*h(i+1,1) call dlartg (f, g, c, s, r) c c %-------------------------------------------% c | The following ensures that h(1:iend-1,1), | c | the first iend-2 off diagonal of elements | c | H, remain non negative. | c %-------------------------------------------% c if (r .lt. zero) then r = -r c = -c s = -s end if c c %--------------------------------------------% c | Apply rotation to the left and right of H; | c | H <- G * H * G', where G = G(i,i+1,theta) | c %--------------------------------------------% c h(i,1) = r c a1 = c*h(i,2) + s*h(i+1,1) a2 = c*h(i+1,1) + s*h(i+1,2) a3 = c*h(i+1,1) - s*h(i,2) a4 = c*h(i+1,2) - s*h(i+1,1) c h(i,2) = c*a1 + s*a2 h(i+1,2) = c*a4 - s*a3 h(i+1,1) = c*a3 + s*a4 c c %----------------------------------------------------% c | Accumulate the rotation in the matrix Q; Q <- Q*G | c %----------------------------------------------------% c do 50 j = 1, min( j+jj, kplusp ) a1 = c*q(j,i) + s*q(j,i+1) q(j,i+1) = - s*q(j,i) + c*q(j,i+1) q(j,i) = a1 50 continue c 70 continue c end if c c %--------------------------% c | Update the block pointer | c %--------------------------% c istart = iend + 1 c c %------------------------------------------% c | Make sure that h(iend,1) is non-negative | c | If not then set h(iend,1) <-- -h(iend,1) | c | and negate the last column of Q. | c | We have effectively carried out a | c | similarity on transformation H | c %------------------------------------------% c if (h(iend,1) .lt. zero) then h(iend,1) = -h(iend,1) call dscal(kplusp, -one, q(1,iend), 1) end if c c %--------------------------------------------------------% c | Apply the same shift to the next block if there is any | c %--------------------------------------------------------% c if (iend .lt. kplusp) go to 20 c c %-----------------------------------------------------% c | Check if we can increase the the start of the block | c %-----------------------------------------------------% c do 80 i = itop, kplusp-1 if (h(i+1,1) .gt. zero) go to 90 itop = itop + 1 80 continue c c %-----------------------------------% c | Finished applying the jj-th shift | c %-----------------------------------% c 90 continue c c %------------------------------------------% c | All shifts have been applied. Check for | c | more possible deflation that might occur | c | after the last shift is applied. | c %------------------------------------------% c do 100 i = itop, kplusp-1 big = abs(h(i,2)) + abs(h(i+1,2)) if (h(i+1,1) .le. epsmch*big) then if (msglvl .gt. 0) then call igraphivout (logfil, 1, i, ndigit, & '_sapps: deflation at row/column no.') call igraphdvout (logfil, 1, h(i+1,1), ndigit, & '_sapps: the corresponding off diagonal element') end if h(i+1,1) = zero end if 100 continue c c %-------------------------------------------------% c | Compute the (kev+1)-st column of (V*Q) and | c | temporarily store the result in WORKD(N+1:2*N). | c | This is not necessary if h(kev+1,1) = 0. | c %-------------------------------------------------% c if ( h(kev+1,1) .gt. zero ) & call dgemv ('N', n, kplusp, one, v, ldv, & q(1,kev+1), 1, zero, workd(n+1), 1) c c %-------------------------------------------------------% c | Compute column 1 to kev of (V*Q) in backward order | c | taking advantage that Q is an upper triangular matrix | c | with lower bandwidth np. | c | Place results in v(:,kplusp-kev:kplusp) temporarily. | c %-------------------------------------------------------% c do 130 i = 1, kev call dgemv ('N', n, kplusp-i+1, one, v, ldv, & q(1,kev-i+1), 1, zero, workd, 1) call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) 130 continue c c %-------------------------------------------------% c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | c %-------------------------------------------------% c call dlacpy ('All', n, kev, v(1,np+1), ldv, v, ldv) c c %--------------------------------------------% c | Copy the (kev+1)-st column of (V*Q) in the | c | appropriate place if h(kev+1,1) .ne. zero. | c %--------------------------------------------% c if ( h(kev+1,1) .gt. zero ) & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) c c %-------------------------------------% c | Update the residual vector: | c | r <- sigmak*r + betak*v(:,kev+1) | c | where | c | sigmak = (e_{kev+p}'*Q)*e_{kev} | c | betak = e_{kev+1}'*H*e_{kev} | c %-------------------------------------% c call dscal (n, q(kplusp,kev), resid, 1) if (h(kev+1,1) .gt. zero) & call daxpy (n, h(kev+1,1), v(1,kev+1), 1, resid, 1) c if (msglvl .gt. 1) then call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, & '_sapps: sigmak of the updated residual vector') call igraphdvout (logfil, 1, h(kev+1,1), ndigit, & '_sapps: betak of the updated residual vector') call igraphdvout (logfil, kev, h(1,2), ndigit, & '_sapps: updated main diagonal of H for next iteration') if (kev .gt. 1) then call igraphdvout (logfil, kev-1, h(2,1), ndigit, & '_sapps: updated sub diagonal of H for next iteration') end if end if c call igraphsecond (t1) tsapps = tsapps + (t1 - t0) c 9000 continue return c c %---------------% c | End of igraphdsapps | c %---------------% c end igraph/src/RayVector.h0000755000176000001440000000224212325527072014442 0ustar ripleyusers/** Vector.h */ #ifndef VECTOR_H #define VECTOR_H #include "Point.h" namespace igraph { class Vector { public: Vector(); Vector(const Point& vStartPoint, const Point& vEndPoint); Vector(double vI, double vJ, double vK); ~Vector(); Vector Normalize() const; // returns a unit vector of this vector void NormalizeThis(); void ReverseDirection(); bool IsSameDirection(const Vector& rVector) const; void I(double vI); double I() const; void J(double vJ); double J() const; void K(double vK); double K() const; double Dot(const Vector& rVector) const; // returns the dot product of this and rVector Vector Cross(const Vector& rVector) const; // returns the cross product of this and rVector Vector operator+ (Vector vRhs) const; // returns the sum of two vectors Vector operator- (Vector vRhs) const; // returns the difference of two vectors Point operator+ (Point vRhs) const; // returns the sum of a vector and a Point Vector operator* (double vRhs) const; // returns multiplication of a scalar with a vector Point ToPoint() const; // converts a vector to a point double Magnitude() const; private: double mI, mJ, mK; }; } // namespace igraph #endif igraph/src/infomap_Node.cc0000644000176000001440000000413412325527073015260 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "infomap_Node.h" Node::Node(){ exit = 0.0; size = 0.0; selfLink = 0.0; } Node::Node(int nodenr, double tpweight){ teleportWeight = tpweight; exit = 0.0; size = 0.0; selfLink = 0.0; members.push_back(nodenr); // members = [nodenr] } void cpyNode(Node *newNode, Node *oldNode){ newNode->exit = oldNode->exit; newNode->size = oldNode->size; newNode->teleportWeight = oldNode->teleportWeight; newNode->danglingSize = oldNode->danglingSize; int Nmembers = oldNode->members.size(); newNode->members = vector(Nmembers); for (int i=0;imembers[i] = oldNode->members[i]; newNode->selfLink = oldNode->selfLink; int NoutLinks = oldNode->outLinks.size(); newNode->outLinks = vector >(NoutLinks); for (int i=0;ioutLinks[i].first = oldNode->outLinks[i].first; newNode->outLinks[i].second = oldNode->outLinks[i].second; } int NinLinks = oldNode->inLinks.size(); newNode->inLinks = vector >(NinLinks); for (int i=0;iinLinks[i].first = oldNode->inLinks[i].first; newNode->inLinks[i].second = oldNode->inLinks[i].second; } } igraph/src/operators.c0000644000176000001440000011550712325527073014544 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_operators.h" #include "igraph_error.h" #include "igraph_memory.h" #include "igraph_interrupt_internal.h" #include "igraph_interface.h" #include "igraph_constructors.h" #include "igraph_adjlist.h" #include "igraph_attributes.h" #include "igraph_conversion.h" #include "igraph_qsort.h" #include #include "config.h" /** * \function igraph_disjoint_union * \brief Creates the union of two disjoint graphs * * * First the vertices of the second graph will be relabeled with new * vertex ids to have two disjoint sets of vertex ids, then the union * of the two graphs will be formed. * If the two graphs have |V1| and |V2| vertices and |E1| and |E2| * edges respectively then the new graph will have |V1|+|V2| vertices * and |E1|+|E2| edges. * * * Both graphs need to have the same directedness, ie. either both * directed or both undirected. * * * The current version of this function cannot handle graph, vertex * and edge attributes, they will be lost. * * \param res Pointer to an uninitialized graph object, the result * will stored here. * \param left The first graph. * \param right The second graph. * \return Error code. * \sa \ref igraph_disjoint_union_many() for creating the disjoint union * of more than two graphs, \ref igraph_union() for non-disjoint * union. * * Time complexity: O(|V1|+|V2|+|E1|+|E2|). * * \example examples/simple/igraph_disjoint_union.c */ int igraph_disjoint_union(igraph_t *res, const igraph_t *left, const igraph_t *right) { long int no_of_nodes_left=igraph_vcount(left); long int no_of_nodes_right=igraph_vcount(right); long int no_of_edges_left=igraph_ecount(left); long int no_of_edges_right=igraph_ecount(right); igraph_vector_t edges; igraph_bool_t directed_left=igraph_is_directed(left); igraph_integer_t from, to; long int i; if (directed_left != igraph_is_directed(right)) { IGRAPH_ERROR("Cannot union directed and undirected graphs", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, 2*(no_of_edges_left+no_of_edges_right))); for (i=0; i * First the vertices in the graphs will be relabeled with new vertex * ids to have pairwise disjoint vertex id sets and then the union of * the graphs is formed. * The number of vertices and edges in the result is the total number * of vertices and edges in the graphs. * * * Both graphs need to have the same directedness, ie. either both * directed or both undirected. * * * The current version of this function cannot handle graph, vertex * and edge attributes, they will be lost. * * \param res Pointer to an uninitialized graph object, the result of * the operation will be stored here. * \param graphs Pointer vector, contains pointers to initialized * graph objects. * \return Error code. * \sa \ref igraph_disjoint_union() for an easier syntax if you have * only two graphs, \ref igraph_union_many() for non-disjoint union. * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges in the result. */ int igraph_disjoint_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs) { long int no_of_graphs=igraph_vector_ptr_size(graphs); igraph_bool_t directed=1; igraph_vector_t edges; long int no_of_edges=0; long int shift=0; igraph_t *graph; long int i, j; igraph_integer_t from, to; if (no_of_graphs != 0) { graph=VECTOR(*graphs)[0]; directed=igraph_is_directed(graph); for (i=0; i from2) { return 1; } else { long int to1=VECTOR(*edgelist)[edge1+1]; long int to2=VECTOR(*edgelist)[edge2+1]; if (to1 < to2) { return -1; } else if (to1 > to2) { return 1; } else { return 0; } } } #define IGRAPH_MODE_UNION 1 #define IGRAPH_MODE_INTERSECTION 2 int igraph_i_merge(igraph_t *res, int mode, const igraph_t *left, const igraph_t *right, igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { long int no_of_nodes_left=igraph_vcount(left); long int no_of_nodes_right=igraph_vcount(right); long int no_of_nodes; long int no_edges_left=igraph_ecount(left); long int no_edges_right=igraph_ecount(right); igraph_bool_t directed=igraph_is_directed(left); igraph_vector_t edges; igraph_vector_t edges1, edges2; igraph_vector_long_t order1, order2; long int i, j, eptr=0; long int idx1, idx2, edge1=-1, edge2=-1, from1=-1, from2=-1, to1=-1, to2=-1; igraph_bool_t l; if (directed != igraph_is_directed(right)) { IGRAPH_ERROR("Cannot make union or intersection of directed " "and undirected graph", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&edges1, no_edges_left*2); IGRAPH_VECTOR_INIT_FINALLY(&edges2, no_edges_right*2); IGRAPH_CHECK(igraph_vector_long_init(&order1, no_edges_left)); IGRAPH_FINALLY(igraph_vector_long_destroy, &order1); IGRAPH_CHECK(igraph_vector_long_init(&order2, no_edges_right)); IGRAPH_FINALLY(igraph_vector_long_destroy, &order2); if (edge_map1) { switch (mode) { case IGRAPH_MODE_UNION: IGRAPH_CHECK(igraph_vector_resize(edge_map1, no_edges_left)); break; case IGRAPH_MODE_INTERSECTION: igraph_vector_clear(edge_map1); break; } } if (edge_map2) { switch (mode) { case IGRAPH_MODE_UNION: IGRAPH_CHECK(igraph_vector_resize(edge_map2, no_edges_right)); break; case IGRAPH_MODE_INTERSECTION: igraph_vector_clear(edge_map2); break; } } no_of_nodes=no_of_nodes_left > no_of_nodes_right ? no_of_nodes_left : no_of_nodes_right; /* We merge the two edge lists. We need to sort them first. For undirected graphs, we also need to make sure that for every edge, that larger (non-smaller) vertex id is in the second column. */ IGRAPH_CHECK(igraph_get_edgelist(left, &edges1, /*bycol=*/ 0)); IGRAPH_CHECK(igraph_get_edgelist(right, &edges2, /*bycol=*/ 0)); if (!directed) { for (i=0, j=0; i VECTOR(edges1)[j+1]) { long int tmp=VECTOR(edges1)[j]; VECTOR(edges1)[j]=VECTOR(edges1)[j+1]; VECTOR(edges1)[j+1]=tmp; } } for (i=0, j=0; i VECTOR(edges2)[j+1]) { long int tmp=VECTOR(edges2)[j]; VECTOR(edges2)[j]=VECTOR(edges2)[j+1]; VECTOR(edges2)[j+1]=tmp; } } } for (i=0; i= no_edges_right || (idx1 < no_edges_left && from1 < from2) || (idx1 < no_edges_left && from1 == from2 && to1 < to2)) { /* Edge from first graph */ if (mode==IGRAPH_MODE_UNION) { IGRAPH_CHECK(igraph_vector_push_back(&edges, from1)); IGRAPH_CHECK(igraph_vector_push_back(&edges, to1)); if (edge_map1) { VECTOR(*edge_map1)[edge1]=eptr; } eptr++; } INC1(); } else if (idx1 >= no_edges_left || (idx2 < no_edges_right && from2 < from1) || (idx2 < no_edges_right && from1 == from2 && to2 < to1)) { /* Edge from second graph */ if (mode==IGRAPH_MODE_UNION) { IGRAPH_CHECK(igraph_vector_push_back(&edges, from2)); IGRAPH_CHECK(igraph_vector_push_back(&edges, to2)); if (edge_map2) { VECTOR(*edge_map2)[edge2]=eptr; } eptr++; } INC2(); } else { /* Edge from both */ IGRAPH_CHECK(igraph_vector_push_back(&edges, from1)); IGRAPH_CHECK(igraph_vector_push_back(&edges, to1)); if (mode==IGRAPH_MODE_UNION) { if (edge_map1) { VECTOR(*edge_map1)[edge1]=eptr; } if (edge_map2) { VECTOR(*edge_map2)[edge2]=eptr; } } else if (mode==IGRAPH_MODE_INTERSECTION) { if (edge_map1) { IGRAPH_CHECK(igraph_vector_push_back(edge_map1, edge1)); } if (edge_map2) { IGRAPH_CHECK(igraph_vector_push_back(edge_map2, edge2)); } } eptr++; INC1(); INC2(); } CONT(); } #undef INC1 #undef INC2 igraph_vector_long_destroy(&order2); igraph_vector_long_destroy(&order1); igraph_vector_destroy(&edges2); igraph_vector_destroy(&edges1); IGRAPH_FINALLY_CLEAN(4); IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_intersection * \brief Collect the common edges from two graphs. * * * The result graph contains only edges present both in the first and * the second graph. The number of vertices in the result graph is the * same as the larger from the two arguments. * * \param res Pointer to an uninitialized graph object. This will * contain the result of the operation. * \param left The first operand, a graph object. * \param right The second operand, a graph object. * \param edge_map1 Null pointer, or an initialized \type igraph_vector_t. * If the latter, then a mapping from the edges of the result graph, to * the edges of the \p left input graph is stored here. * \param edge_map2 Null pointer, or an \type igraph_vector_t. The same * as \p edge_map1, but for the \p right input graph. * \return Error code. * \sa \ref igraph_intersection_many() to calculate the intersection * of many graphs at once, \ref igraph_union(), \ref * igraph_difference() for other operators. * * Time complexity: O(|V|+|E|), |V| is the number of nodes, |E| * is the number of edges in the smaller graph of the two. (The one * containing less vertices is considered smaller.) * * \example examples/simple/igraph_intersection.c */ int igraph_intersection(igraph_t *res, const igraph_t *left, const igraph_t *right, igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { return igraph_i_merge(res, IGRAPH_MODE_INTERSECTION, left, right, edge_map1, edge_map2); } void igraph_i_union_many_free(igraph_vector_ptr_t *v) { long int i, n=igraph_vector_ptr_size(v); for (i=0; i * This function calculates the intersection of the graphs stored in * the \c graphs argument. Only those edges will be included in the * result graph which are part of every graph in \c graphs. * * * The number of vertices in the result graph will be the maximum * number of vertices in the argument graphs. * * \param res Pointer to an uninitialized graph object, the result of * the operation will be stored here. * \param graphs Pointer vector, contains pointers to graphs objects, * the operands of the intersection operator. * \param edgemaps If not a null pointer, then it must be an initialized * pointer vector and the mappings of edges from the graphs to the * result graph will be stored here, in the same order as * \p graphs. Each mapping is stored in a separate * \type igraph_vector_t object. For the edges that are not in * the intersection, -1 is stored. * \return Error code. * \sa \ref igraph_intersection() for the intersection of two graphs, * \ref igraph_union_many(), \ref igraph_union() and \ref * igraph_difference() for other operators. * * Time complexity: O(|V|+|E|), |V| is the number of vertices, * |E| is the number of edges in the smallest graph (ie. the graph having * the less vertices). */ int igraph_intersection_many(igraph_t *res, const igraph_vector_ptr_t *graphs, igraph_vector_ptr_t *edgemaps) { long int no_of_graphs=igraph_vector_ptr_size(graphs); long int no_of_nodes=0; igraph_bool_t directed=1; igraph_vector_t edges; igraph_vector_ptr_t edge_vects, order_vects; long int i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto=-1; igraph_vector_long_t no_edges; igraph_bool_t allne= no_of_graphs == 0 ? 0 : 1, allsame=0; long int idx=0; /* Check directedness */ if (no_of_graphs != 0) { directed=igraph_is_directed(VECTOR(*graphs)[0]); } for (i=1; i no_of_nodes) { no_of_nodes=n; } VECTOR(no_edges)[i] = igraph_ecount(VECTOR(*graphs)[i]); allne = allne && VECTOR(no_edges)[i] > 0; } if (edgemaps) { for (i=0; i VECTOR(*edges)[j+1]) { long int tmp=VECTOR(*edges)[j]; VECTOR(*edges)[j]=VECTOR(*edges)[j+1]; VECTOR(*edges)[j+1]=tmp; } } } for (k=0; k tailfrom || (from==tailfrom && to > tailto)) { igraph_vector_long_pop_back(VECTOR(order_vects)[j]); if (igraph_vector_long_empty(VECTOR(order_vects)[j])) { allne=0; break; } } else { break; } } if (from != tailfrom || to != tailto) { allsame=0; } } /* Add the edge, if the smallest tail element was present in all graphs. */ if (allsame) { IGRAPH_CHECK(igraph_vector_push_back(&edges, tailfrom)); IGRAPH_CHECK(igraph_vector_push_back(&edges, tailto)); } /* Drop edges matching the smalles tail elements from the order vectors, build edge maps */ if (allne) { for (j=0; j 0) { igraph_i_union_many_free2(&order_vects); igraph_i_union_many_free(&edge_vects); IGRAPH_FINALLY_CLEAN(2); } igraph_vector_long_destroy(&no_edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); if (edgemaps) { IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_union * \brief Calculates the union of two graphs. * * * The number of vertices in the result is that of the larger graph * from the two arguments. The result graph contains edges which are * present in at least one of the operand graphs. * * \param res Pointer to an uninitialized graph object, the result * will be stored here. * \param left The first graph. * \param right The second graph. * \param edge_map1 Pointer to an initialized vector or a null pointer. * If not a null pointer, it will contain a mapping from the edges * of the first argument graph (\p left) to the edges of the * result graph. * \param edge_map2 The same as \p edge_map1, but for the second * graph, \p right. * \return Error code. * \sa \ref igraph_union_many() for the union of many graphs, * \ref igraph_intersection() and \ref igraph_difference() for other * operators. * * Time complexity: O(|V|+|E|), |V| is the number of * vertices, |E| the number of edges in the result graph. * * \example examples/simple/igraph_union.c */ int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { return igraph_i_merge(res, IGRAPH_MODE_UNION, left, right, edge_map1, edge_map2); } /** * \function igraph_union_many * \brief Creates the union of many graphs. * * * The result graph will contain as many vertices as the largest graph * among the arguments does, and an edge will be included in it if it * is part of at least one operand graph. * * * The directedness of the operand graphs must be the same. * * \param res Pointer to an uninitialized graph object, this will * contain the result. * \param graphs Pointer vector, contains pointers to the operands of * the union operator, graph objects of course. * \param edgemaps If not a null pointer, then it must be an initialized * pointer vector and the mappings of edges from the graphs to the * result graph will be stored here, in the same order as * \p graphs. Each mapping is stored in a separate * \type igraph_vector_t object. * \return Error code. * \sa \ref igraph_union() for the union of two graphs, \ref * igraph_intersection_many(), \ref igraph_intersection() and \ref * igraph_difference for other operators. * * * Time complexity: O(|V|+|E|), |V| is the number of vertices * in largest graph and |E| is the number of edges in the result graph. * * \example examples/simple/igraph_union.c */ int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, igraph_vector_ptr_t *edgemaps) { long int no_of_graphs=igraph_vector_ptr_size(graphs); long int no_of_nodes=0; igraph_bool_t directed=1; igraph_vector_t edges; igraph_vector_ptr_t edge_vects, order_vects; igraph_vector_long_t no_edges; long int i, j, tailfrom= no_of_graphs > 0 ? 0 : -1, tailto=-1; long int idx=0; /* Check directedness */ if (no_of_graphs != 0) { directed=igraph_is_directed(VECTOR(*graphs)[0]); no_of_nodes=igraph_vcount(VECTOR(*graphs)[0]); } for (i=1; i no_of_nodes) { no_of_nodes=n; } VECTOR(no_edges)[i] = igraph_ecount(VECTOR(*graphs)[i]); } if (edgemaps) { for (i=0; i VECTOR(*edges)[j+1]) { long int tmp=VECTOR(*edges)[j]; VECTOR(*edges)[j]=VECTOR(*edges)[j+1]; VECTOR(*edges)[j+1]=tmp; } } } for (k=0; k= 0) { /* Get the largest tail element */ tailfrom = tailto = -1; for (j=0; j tailfrom || (from == tailfrom && to > tailto)) { tailfrom = from; tailto = to; } } } if (tailfrom < 0) { continue; } /* add the edge */ IGRAPH_CHECK(igraph_vector_push_back(&edges, tailfrom)); IGRAPH_CHECK(igraph_vector_push_back(&edges, tailto)); /* update edge lists, we just modify the 'order' vectors */ for (j=0; j 0) { igraph_i_union_many_free2(&order_vects); igraph_i_union_many_free(&edge_vects); IGRAPH_FINALLY_CLEAN(2); } igraph_vector_long_destroy(&no_edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); if (edgemaps) { IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_difference * \brief Calculate the difference of two graphs * * * The number of vertices in the result is the number of vertices in * the original graph, ie. the left, first operand. In the results * graph only edges will be included from \c orig which are not * present in \c sub. * * \param res Pointer to an uninitialized graph object, the result * will be stored here. * \param orig The left operand of the operator, a graph object. * \param sub The right operand of the operator, a graph object. * \return Error code. * \sa \ref igraph_intersection() and \ref igraph_union() for other * operators. * * Time complexity: O(|V|+|E|), |V| is the number vertices in * the smaller graph, |E| is the * number of edges in the result graph. * * \example examples/simple/igraph_difference.c */ int igraph_difference(igraph_t *res, const igraph_t *orig, const igraph_t *sub) { /* Quite nasty, but we will use that an edge adjacency list contains the vertices according to the order of the vertex ids at the "other" end of the edge. */ long int no_of_nodes_orig=igraph_vcount(orig); long int no_of_nodes_sub =igraph_vcount(sub); long int no_of_nodes=no_of_nodes_orig; long int smaller_nodes; igraph_bool_t directed=igraph_is_directed(orig); igraph_vector_t edges; igraph_vector_t edge_ids; igraph_vector_t *nei1, *nei2; igraph_inclist_t inc_orig, inc_sub; long int i; igraph_integer_t v1, v2; if (directed != igraph_is_directed(sub)) { IGRAPH_ERROR("Cannot subtract directed and undirected graphs", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edge_ids, 0); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_inclist_init(orig, &inc_orig, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_inclist_destroy, &inc_orig); IGRAPH_CHECK(igraph_inclist_init(sub, &inc_sub, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_inclist_destroy, &inc_sub); smaller_nodes=no_of_nodes_orig > no_of_nodes_sub ? no_of_nodes_sub : no_of_nodes_orig; for (i=0; i=0 && n2>=0) { e1=(long int) VECTOR(*nei1)[n1]; e2=(long int) VECTOR(*nei2)[n2]; v1=IGRAPH_OTHER(orig, e1, i); v2=IGRAPH_OTHER(sub, e2, i); if (!directed && v1v2) { IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); n1--; } else if (v2>v1) { n2--; } else { n1--; n2--; } } /* Copy remaining edges */ while (n1>=0) { e1=(long int) VECTOR(*nei1)[n1]; v1=IGRAPH_OTHER(orig, e1, i); if (directed || v1 >= i) { IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); } n1--; } } /* copy remaining edges, use the previous value of 'i' */ for (; i=0) { e1=(long int) VECTOR(*nei1)[n1]; v1=IGRAPH_OTHER(orig, e1, i); if (directed || v1 >= i) { IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); } n1--; } } igraph_inclist_destroy(&inc_sub); igraph_inclist_destroy(&inc_orig); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); /* Attributes */ if (orig->attr) { IGRAPH_I_ATTRIBUTE_DESTROY(res); IGRAPH_I_ATTRIBUTE_COPY(res, orig, /*graph=*/1, /*vertex=*/1, /*edge=*/0); IGRAPH_CHECK(igraph_i_attribute_permute_edges(orig, res, &edge_ids)); } igraph_vector_destroy(&edge_ids); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_complementer * \brief Create the complementer of a graph * * The complementer graph means that all edges which are * not part of the original graph will be included in the result. * * \param res Pointer to an uninitialized graph object. * \param graph The original graph. * \param loops Whether to add loop edges to the complementer graph. * \return Error code. * \sa \ref igraph_union(), \ref igraph_intersection() and \ref * igraph_difference(). * * Time complexity: O(|V|+|E1|+|E2|), |V| is the number of * vertices in the graph, |E1| is the number of edges in the original * and |E2| in the complementer graph. * * \example examples/simple/igraph_complementer.c */ int igraph_complementer(igraph_t *res, const igraph_t *graph, igraph_bool_t loops) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t edges; igraph_vector_t neis; long int i, j; long int zero=0, *limit; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (igraph_is_directed(graph)) { limit=&zero; } else { limit=&i; } for (i=0; i=*limit; j--) { if (igraph_vector_empty(&neis) || j>igraph_vector_tail(&neis)) { IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } else { igraph_vector_pop_back(&neis); } } } else { for (j=no_of_nodes-1; j>=*limit; j--) { if (igraph_vector_empty(&neis) || j>igraph_vector_tail(&neis)) { if (i!=j) { IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } } else { igraph_vector_pop_back(&neis); } } } } IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, igraph_is_directed(graph))); igraph_vector_destroy(&edges); igraph_vector_destroy(&neis); IGRAPH_I_ATTRIBUTE_DESTROY(res); IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/1, /*edge=*/0); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_compose * \brief Calculates the composition of two graphs * * The composition of graphs contains the same number of vertices as * the bigger graph of the two operands. It contains an (i,j) edge if * and only if there is a k vertex, such that the first graphs * contains an (i,k) edge and the second graph a (k,j) edge. * * This is of course exactly the composition of two * binary relations. * * Two two graphs must have the same directedness, * otherwise the function returns with an error message. * Note that for undirected graphs the two relations are by definition * symmetric. * * \param res Pointer to an uninitialized graph object, the result * will be stored here. * \param g1 The firs operand, a graph object. * \param g2 The second operand, another graph object. * \param edge_map1 If not a null pointer, then it must be a pointer * to an initialized vector, and a mapping from the edges of * the result graph to the edges of the first graph is stored * here. * \param edge_map1 If not a null pointer, then it must be a pointer * to an initialized vector, and a mapping from the edges of * the result graph to the edges of the second graph is stored * here. * \return Error code. * * Time complexity: O(|V|*d1*d2), |V| is the number of vertices in the * first graph, d1 and d2 the average degree in the first and second * graphs. * * \example examples/simple/igraph_compose.c */ int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { long int no_of_nodes_left=igraph_vcount(g1); long int no_of_nodes_right=igraph_vcount(g2); long int no_of_nodes; igraph_bool_t directed=igraph_is_directed(g1); igraph_vector_t edges; igraph_vector_t neis1, neis2; long int i; if (directed != igraph_is_directed(g2)) { IGRAPH_ERROR("Cannot compose directed and undirected graph", IGRAPH_EINVAL); } no_of_nodes= no_of_nodes_left > no_of_nodes_right ? no_of_nodes_left : no_of_nodes_right; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&neis1, 0); IGRAPH_VECTOR_INIT_FINALLY(&neis2, 0); if (edge_map1) { igraph_vector_clear(edge_map1); } if (edge_map2) { igraph_vector_clear(edge_map2); } for (i=0; i using namespace prpack; prpack_result::prpack_result() { x = NULL; } prpack_result::~prpack_result() { delete[] x; } igraph/src/bliss_graph.cc0000644000176000001440000020005512325527072015156 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include #include #include #include #include #include #include "bliss_defs.hh" #include "bliss_timer.hh" #include "bliss_graph.hh" #include "bliss_partition.hh" #include // INT_MAX, etc #include "igraph_datatype.h" #include "igraph_interface.h" #include "igraph_topology.h" #include "igraph_statusbar.h" using namespace std; extern bool bliss_verbose; // extern FILE *bliss_verbstr; namespace igraph { static const bool should_not_happen = false; /*------------------------------------------------------------------------- * * Constructor and destructor routines for the abstract graph class * *-------------------------------------------------------------------------*/ AbstractGraph::AbstractGraph() { /* Initialize stuff */ first_path_labeling = 0; first_path_labeling_inv = 0; best_path_labeling = 0; best_path_labeling_inv = 0; first_path_automorphism = 0; best_path_automorphism = 0; //certificate = 0; in_search = false; } AbstractGraph::~AbstractGraph() { if(first_path_labeling) { free(first_path_labeling); first_path_labeling = 0; } if(first_path_labeling_inv) { free(first_path_labeling_inv); first_path_labeling_inv = 0; } if(best_path_labeling) { free(best_path_labeling); best_path_labeling = 0; } if(best_path_labeling_inv) { free(best_path_labeling_inv); best_path_labeling_inv = 0; } if(first_path_automorphism) { free(first_path_automorphism); first_path_automorphism = 0; } if(best_path_automorphism) { free(best_path_automorphism); best_path_automorphism = 0; } //if(certificate) { // free(certificate); certificate = 0; } while(!long_prune_fixed.empty()) { delete long_prune_fixed.back(); long_prune_fixed.pop_back(); } while(!long_prune_mcrs.empty()) { delete long_prune_mcrs.back(); long_prune_mcrs.pop_back(); } } /*------------------------------------------------------------------------- * * Routines for refinement to equitable partition * *-------------------------------------------------------------------------*/ void AbstractGraph::refine_to_equitable() { assert(p.splitting_queue.is_empty()); for(Cell *cell = p.first_cell; cell; cell = cell->next) { p.add_in_splitting_queue(cell); } return do_refine_to_equitable(); } void AbstractGraph::refine_to_equitable(Cell *cell1) { DEBUG_ASSERT(cell1->length == 1); #ifdef EXPENSIVE_CONSISTENCY_CHECKS for(Cell *cell = p.first_cell; cell; cell = cell->next) { assert(cell->in_splitting_queue == false); assert(cell->in_neighbour_heap == false); } #endif assert(p.splitting_queue.is_empty()); p.add_in_splitting_queue(cell1); return do_refine_to_equitable(); } void AbstractGraph::refine_to_equitable(Cell *cell1, Cell *cell2) { DEBUG_ASSERT(cell1->length == 1); DEBUG_ASSERT(cell2->length == 1); #ifdef EXPENSIVE_CONSISTENCY_CHECKS for(Cell *cell = p.first_cell; cell; cell = cell->next) { assert(cell->in_splitting_queue == false); assert(cell->in_neighbour_heap == false); } #endif assert(p.splitting_queue.is_empty()); p.add_in_splitting_queue(cell1); p.add_in_splitting_queue(cell2); return do_refine_to_equitable(); } void AbstractGraph::do_refine_to_equitable() { assert(!p.splitting_queue.is_empty()); assert(neighbour_heap.is_empty()); eqref_hash.reset(); while(!p.splitting_queue.is_empty()) { Cell *cell = p.splitting_queue.pop_front(); DEBUG_ASSERT(cell->in_splitting_queue); cell->in_splitting_queue = false; if(cell->length == 1) { if(in_search) { if(first_path_automorphism) { /* Build the (potential) automorphism on-the-fly */ assert(first_path_labeling_inv); first_path_automorphism[first_path_labeling_inv[cell->first]] = p.elements[cell->first]; } if(best_path_automorphism) { /* Build the (potential) automorphism on-the-fly */ assert(best_path_labeling_inv); best_path_automorphism[best_path_labeling_inv[cell->first]] = p.elements[cell->first]; } } bool worse = split_neighbourhood_of_unit_cell(cell); if(in_search && worse) goto worse_exit; } else { split_neighbourhood_of_cell(cell); } } eqref_worse_than_certificate = false; return; worse_exit: /* Clear splitting_queue */ p.clear_splitting_queue(); eqref_worse_than_certificate = true; return; } /*------------------------------------------------------------------------- * * Routines for handling the canonical labeling * *-------------------------------------------------------------------------*/ void AbstractGraph::update_labeling(unsigned int * const labeling) { const unsigned int N = get_nof_vertices(); unsigned int *ep = p.elements; for(unsigned int i = 0; i < N; i++, ep++) labeling[*ep] = i; } void AbstractGraph::update_labeling_and_its_inverse(unsigned int * const labeling, unsigned int * const labeling_inv) { const unsigned int N = get_nof_vertices(); unsigned int *ep = p.elements; unsigned int *clip = labeling_inv; for(unsigned int i = 0; i < N; ) { labeling[*ep] = i; i++; *clip = *ep; ep++; clip++; } } /*------------------------------------------------------------------------- * * Routines for handling automorphisms * *-------------------------------------------------------------------------*/ void AbstractGraph::reset_permutation(unsigned int *perm) { const unsigned int N = get_nof_vertices(); for(unsigned int i = 0; i < N; i++, perm++) *perm = i; } bool AbstractGraph::is_automorphism(unsigned int * const perm) { IGRAPH_UNUSED(perm); assert(should_not_happen); return false; } /*------------------------------------------------------------------------- * * Long prune code * *-------------------------------------------------------------------------*/ void AbstractGraph::long_prune_init() { const unsigned int N = get_nof_vertices(); long_prune_temp.clear(); long_prune_temp.resize(N); #ifdef DEBUG for(unsigned int i = 0; i < N; i++) assert(long_prune_temp[i] == false); #endif const unsigned int nof_fitting_in_max_mem = (long_prune_options_max_mem * 1024 * 1024) / (((N * 2) / 8)+1); long_prune_max_stored_autss = long_prune_options_max_stored_auts; /* Had some problems with g++ in using (a(N)); long_prune_mcrs.push_back(new std::vector(N)); } long_prune_begin = 0; long_prune_end = 0; } void AbstractGraph::long_prune_swap(const unsigned int i, const unsigned int j) { assert(long_prune_begin <= long_prune_end); assert(i >= long_prune_begin); assert(i < long_prune_end); assert(j >= long_prune_begin); assert(j < long_prune_end); const unsigned int real_i = i % long_prune_max_stored_autss; const unsigned int real_j = j % long_prune_max_stored_autss; std::vector * tmp = long_prune_fixed[real_i]; long_prune_fixed[real_i] = long_prune_fixed[real_j]; long_prune_fixed[real_j] = tmp; tmp = long_prune_mcrs[real_i]; long_prune_mcrs[real_i] = long_prune_mcrs[real_j]; long_prune_mcrs[real_j] = tmp; } std::vector &AbstractGraph::long_prune_get_fixed(const unsigned int index) { assert(long_prune_begin <= long_prune_end); assert(index >= long_prune_begin); assert(index < long_prune_end); return *long_prune_fixed[index % long_prune_max_stored_autss]; } std::vector &AbstractGraph::long_prune_get_mcrs(const unsigned int index) { assert(long_prune_begin <= long_prune_end); assert(index >= long_prune_begin); assert(index < long_prune_end); return *long_prune_mcrs[index % long_prune_max_stored_autss]; } void AbstractGraph::long_prune_add_automorphism(const unsigned int *aut) { if(long_prune_max_stored_autss == 0) return; const unsigned int N = get_nof_vertices(); #ifdef DEBUG assert(long_prune_temp.size() == N); for(unsigned int i = 0; i < N; i++) assert(long_prune_temp[i] == false); #endif DEBUG_ASSERT(long_prune_fixed.size() == long_prune_mcrs.size()); assert(long_prune_begin <= long_prune_end); if(long_prune_end - long_prune_begin == long_prune_max_stored_autss) { long_prune_begin++; } long_prune_end++; std::vector &fixed = long_prune_get_fixed(long_prune_end-1); std::vector &mcrs = long_prune_get_mcrs(long_prune_end-1); for(unsigned int i = 0; i < N; i++) { fixed[i] = (aut[i] == i); if(!long_prune_temp[i]) { mcrs[i] = true; unsigned int j = aut[i]; while(j != i) { assert(i <= j); long_prune_temp[j] = true; j = aut[j]; } } else { mcrs[i] = false; } long_prune_temp[i] = false; } #ifdef DEBUG for(unsigned int i = 0; i < N; i++) assert(long_prune_temp[i] == false); #endif } /*------------------------------------------------------------------------- * * Routines for handling orbit information * *-------------------------------------------------------------------------*/ void AbstractGraph::update_orbit_information(Orbit &o, const unsigned int *p) { const unsigned int N = get_nof_vertices(); for(unsigned int i = 0; i < N; i++) if(p[i] != i) o.merge_orbits(i, p[i]); } /*------------------------------------------------------------------------- * * Print a permutation in cycle notation * *-------------------------------------------------------------------------*/ void AbstractGraph::print_permutation(FILE *fp, const unsigned int *perm) { const unsigned int N = get_nof_vertices(); for(unsigned int i = 0; i < N; i++) { unsigned int j = perm[i]; if(j == i) continue; bool is_first = true; while(j != i) { if(j < i) { is_first = false; break; } j = perm[j]; } if(!is_first) continue; fprintf(fp, "(%u,", i); j = perm[i]; while(j != i) { fprintf(fp, "%u", j); j = perm[j]; if(j != i) fprintf(fp, ","); } fprintf(fp, ")"); } } /*------------------------------------------------------------------------- * * The actual backtracking search * *-------------------------------------------------------------------------*/ typedef struct { int split_element; unsigned int split_cell_first; unsigned int refinement_stack_size; unsigned int certificate_index; bool in_first_path; bool in_best_path; bool equal_to_first_path; int cmp_to_best_path; bool needs_long_prune; unsigned int long_prune_begin; std::set > long_prune_redundant; EqrefHash eqref_hash; unsigned int subcertificate_length; } LevelInfo; typedef struct t_path_info { unsigned int splitting_element; unsigned int certificate_index; unsigned int subcertificate_length; EqrefHash eqref_hash; } PathInfo; void AbstractGraph::search(const bool canonical, Stats &stats) { const unsigned int N = get_nof_vertices(); // const bool write_automorphisms = 0; unsigned int all_same_level = UINT_MAX; p.graph = this; /* * Must be done! */ remove_duplicate_edges(); /* * Reset search statistics */ stats.group_size.assign(1); stats.nof_nodes = 1; stats.nof_leaf_nodes = 1; stats.nof_bad_nodes = 0; stats.nof_canupdates = 0; stats.nof_generators = 0; stats.max_level = 0; if(first_path_labeling) { free(first_path_labeling); first_path_labeling = 0; } if(first_path_labeling_inv) { free(first_path_labeling_inv); first_path_labeling_inv = 0; } if(first_path_automorphism) { free(first_path_automorphism); first_path_automorphism = 0; } if(best_path_labeling) { free(best_path_labeling); best_path_labeling = 0; } if(best_path_labeling_inv) { free(best_path_labeling_inv); best_path_labeling_inv = 0; } if(best_path_automorphism) { free(best_path_automorphism); best_path_automorphism = 0; } if(N == 0) return; p.init(N); neighbour_heap.init(N); in_search = false; p.level = 0; Timer t1; t1.start(); make_initial_equitable_partition(); #if defined(VERIFY_EQUITABLEDNESS) assert(is_equitable()); #endif t1.stop(); igraph_statusf("Initial partition computed in %.2fs", 0, t1.get_duration()); /* * Allocate space for the labelings */ if(first_path_labeling) free(first_path_labeling); first_path_labeling = (unsigned int*)calloc(N, sizeof(unsigned int)); if(best_path_labeling) free(best_path_labeling); best_path_labeling = (unsigned int*)calloc(N, sizeof(unsigned int)); /* * Are there any non-singleton cells? */ if(p.is_discrete()) { update_labeling(best_path_labeling); return; } //p.print_signature(stderr); fprintf(stderr, "\n"); /* * Allocate space for the inverses of the labelings */ if(first_path_labeling_inv) free(first_path_labeling_inv); first_path_labeling_inv = (unsigned int*)calloc(N, sizeof(unsigned int)); if(best_path_labeling_inv) free(best_path_labeling_inv); best_path_labeling_inv = (unsigned int*)calloc(N, sizeof(unsigned int)); /* * Allocate space for the automorphisms */ if(first_path_automorphism) free(first_path_automorphism); first_path_automorphism = (unsigned int*)malloc(N * sizeof(unsigned int)); if(best_path_automorphism) free(best_path_automorphism); best_path_automorphism = (unsigned int*)malloc(N * sizeof(unsigned int)); /* * Initialize orbit information */ first_path_orbits.init(N); best_path_orbits.init(N); /* * Initialize certificate memory */ initialize_certificate(); //assert(certificate); assert(certificate_index == 0); LevelInfo info; std::vector search_stack; std::vector first_path_info; std::vector best_path_info; search_stack.clear(); p.refinement_stack.clean(); assert(neighbour_heap.is_empty()); /* * Initialize long prune */ long_prune_init(); /* * Build the first level info */ info.split_cell_first = find_next_cell_to_be_splitted(p.first_cell)->first; info.split_element = -1; info.refinement_stack_size = p.refinement_stack.size(); info.certificate_index = 0; info.in_first_path = false; info.in_best_path = false; info.long_prune_begin = 0; search_stack.push_back(info); /* * Set status and global flags for search related procedures */ in_search = true; refine_compare_certificate = false; stats.nof_leaf_nodes = 0; #ifdef PRINT_SEARCH_TREE_DOT dotty_output = fopen("debug_stree.dot", "w"); fprintf(dotty_output, "digraph stree {\n"); fprintf(dotty_output, "\"n\" [label=\""); fprintf(dotty_output, "M"); //p.print(dotty_output); fprintf(dotty_output, "\"];\n"); #endif p.consistency_check(); /* * The actual backtracking search */ while(!search_stack.empty()) { info = search_stack.back(); search_stack.pop_back(); p.consistency_check(); /* * Restore partition, certificate index, and split cell */ p.unrefine(p.level, info.refinement_stack_size); assert(info.certificate_index <= certificate_size); certificate_index = info.certificate_index; certificate_current_path.resize(certificate_index); Cell * const cell = p.element_to_cell_map[p.elements[info.split_cell_first]]; assert(cell->length > 1); p.consistency_check(); if(p.level > 0 && !info.in_first_path) { if(info.split_element == -1) { info.needs_long_prune = true; } else if(info.needs_long_prune) { info.needs_long_prune = false; /* THIS IS A QUITE HORRIBLE HACK! */ unsigned int begin = (info.long_prune_begin>long_prune_begin)?info.long_prune_begin:long_prune_begin; for(unsigned int i = begin; i < long_prune_end; i++) { const std::vector &fixed = long_prune_get_fixed(i); bool fixes_all = true; for(unsigned int l = 0; l < p.level; l++) { if(fixed[search_stack[l].split_element] == false) { fixes_all = false; break; } } if(!fixes_all) { long_prune_swap(begin, i); begin++; info.long_prune_begin = begin; continue; } const std::vector &mcrs = long_prune_get_mcrs(i); unsigned int *ep = p.elements + cell->first; for(unsigned int j = cell->length; j > 0; j--, ep++) { if(mcrs[*ep] == false) { info.long_prune_redundant.insert(*ep); } } } } } /* * Find the next smallest element in cell */ unsigned int next_split_element = UINT_MAX; unsigned int *next_split_element_pos = 0; unsigned int *ep = p.elements + cell->first; if(info.in_first_path) { /* Find the next larger splitting element that is a mor */ for(unsigned int i = cell->length; i > 0; i--, ep++) { if((int)(*ep) > info.split_element && *ep < next_split_element && first_path_orbits.is_minimal_representative(*ep)) { next_split_element = *ep; next_split_element_pos = ep; } } } else if(info.in_best_path) { /* Find the next larger splitting element that is a mor */ for(unsigned int i = cell->length; i > 0; i--, ep++) { if((int)(*ep) > info.split_element && *ep < next_split_element && best_path_orbits.is_minimal_representative(*ep) && (info.long_prune_redundant.find(*ep) == info.long_prune_redundant.end())) { next_split_element = *ep; next_split_element_pos = ep; } } } else { /* Find the next larger splitting element */ for(unsigned int i = cell->length; i > 0; i--, ep++) { if((int)(*ep) > info.split_element && *ep < next_split_element && (info.long_prune_redundant.find(*ep) == info.long_prune_redundant.end())) { next_split_element = *ep; next_split_element_pos = ep; } } } if(next_split_element == UINT_MAX) { /* * No more splitting elements (unexplored children) in the cell */ /* Update group size if required */ if(info.in_first_path == true) { const unsigned int index = first_path_orbits.orbit_size(first_path_info[p.level].splitting_element); stats.group_size.multiply(index); /* * Update all_same_level */ if(index == cell->length && all_same_level == p.level+1) all_same_level = p.level; igraph_statusf("Level %u: orbits=%u, index=%u/%u, " "all_same_level=%u", 0, p.level, first_path_orbits.nof_orbits(), index, cell->length, all_same_level); } /* Backtrack to the previous level */ p.level--; continue; } /* Split on smallest */ info.split_element = next_split_element; /* * Save the current search situation */ search_stack.push_back(info); /* * No more in the first path */ info.in_first_path = false; /* * No more in the best path */ info.in_best_path = false; p.level++; stats.nof_nodes++; if(p.level > stats.max_level) stats.max_level = p.level; p.consistency_check(); /* * Move the split element to be the last in the cell */ *next_split_element_pos = p.elements[cell->first + cell->length - 1]; p.in_pos[*next_split_element_pos] = next_split_element_pos; p.elements[cell->first + cell->length - 1] = next_split_element; p.in_pos[next_split_element] = p.elements+ cell->first + cell->length -1; /* * Split the cell in two: * the last element in the cell (split element) forms a singleton cell */ Cell * const new_cell = p.aux_split_in_two(cell, cell->length - 1); p.element_to_cell_map[p.elements[new_cell->first]] = new_cell; p.consistency_check(); /* const bool prev_equal_to_first_path = info.equal_to_first_path; const int prev_cmp_to_best_path = info.cmp_to_best_path; */ //assert(!(!info.equal_to_first_path && info.cmp_to_best_path < 0)); if(!first_path_info.empty()) { refine_equal_to_first = info.equal_to_first_path; if(refine_equal_to_first) refine_first_path_subcertificate_end = first_path_info[p.level-1].certificate_index + first_path_info[p.level-1].subcertificate_length; if(canonical) { refine_cmp_to_best = info.cmp_to_best_path; if(refine_cmp_to_best == 0) refine_best_path_subcertificate_end = best_path_info[p.level-1].certificate_index + best_path_info[p.level-1].subcertificate_length; } else refine_cmp_to_best = -1; } /* * Refine the new partition to equitable */ if(cell->length == 1) refine_to_equitable(cell, new_cell); else refine_to_equitable(new_cell); p.consistency_check(); #ifdef PRINT_SEARCH_TREE_DOT fprintf(dotty_output, "\"n"); for(unsigned int i = 0; i < search_stack.size(); i++) { fprintf(dotty_output, "%u", search_stack[i].split_element); if(i < search_stack.size() - 1) fprintf(dotty_output, "."); } fprintf(dotty_output, "\""); fprintf(dotty_output, " [label=\""); fprintf(dotty_output, "%u",cell->first); /*p.print(dotty_output);*/ fprintf(dotty_output, "\"]"); if(!first_path_info.empty() && canonical && refine_cmp_to_best > 0) { fprintf(dotty_output, "[color=green]"); } fprintf(dotty_output, ";\n"); fprintf(dotty_output, "\"n"); for(unsigned int i = 0; i < search_stack.size() - 1; i++) { fprintf(dotty_output, "%u", search_stack[i].split_element); if(i < search_stack.size() - 2) fprintf(dotty_output, "."); } fprintf(dotty_output, "\" -> \"n"); for(unsigned int i = 0; i < search_stack.size(); i++) { fprintf(dotty_output, "%u", search_stack[i].split_element); if(i < search_stack.size() - 1) fprintf(dotty_output, "."); } fprintf(dotty_output, "\" [label=\"%d\"];\n", next_split_element); #endif /* if(prev_cmp_to_best_path == 0 && refine_cmp_to_best < 0) fprintf(stderr, "BP- "); if(prev_cmp_to_best_path == 0 && refine_cmp_to_best > 0) fprintf(stderr, "BP+ "); */ if(p.is_discrete()) { /* Update statistics */ stats.nof_leaf_nodes++; /* if(stats.nof_leaf_nodes % 100 == 0) { fprintf(stdout, "Nodes: %lu, Leafs: %lu, Bad: %lu\n", stats.nof_nodes, stats.nof_leaf_nodes, stats.nof_bad_nodes); fflush(stdout); } */ } if(!first_path_info.empty()) { /* We are no longer on the first path */ assert(best_path_info.size() > 0); assert(certificate_current_path.size() >= certificate_index); const unsigned int subcertificate_length = certificate_current_path.size() - certificate_index; if(refine_equal_to_first) { /* Was equal to the first path so far */ assert(first_path_info.size() >= p.level); PathInfo &first_pinfo = first_path_info[p.level-1]; assert(first_pinfo.certificate_index == certificate_index); if(subcertificate_length != first_pinfo.subcertificate_length) { refine_equal_to_first = false; } else if(first_pinfo.eqref_hash.cmp(eqref_hash) != 0) { refine_equal_to_first = false; } } if(canonical && (refine_cmp_to_best == 0)) { /* Was equal to the best path so far */ assert(best_path_info.size() >= p.level); PathInfo &best_pinfo = best_path_info[p.level-1]; assert(best_pinfo.certificate_index == certificate_index); if(subcertificate_length < best_pinfo.subcertificate_length) { refine_cmp_to_best = -1; //fprintf(stderr, "BSCL- "); } else if(subcertificate_length > best_pinfo.subcertificate_length) { refine_cmp_to_best = 1; //fprintf(stderr, "BSCL+ "); } else if(best_pinfo.eqref_hash.cmp(eqref_hash) > 0) { refine_cmp_to_best = -1; //fprintf(stderr, "BHL- "); } else if(best_pinfo.eqref_hash.cmp(eqref_hash) < 0) { refine_cmp_to_best = 1; //fprintf(stderr, "BHL+ "); } } if(refine_equal_to_first == false && (!canonical || (refine_cmp_to_best < 0))) { /* Backtrack */ #ifdef PRINT_SEARCH_TREE_DOT fprintf(dotty_output, "\"n"); for(unsigned int i = 0; i < search_stack.size(); i++) { fprintf(dotty_output, "%u", search_stack[i].split_element); if(i < search_stack.size() - 1) fprintf(dotty_output, "."); } fprintf(dotty_output, "\" [color=red];\n"); #endif stats.nof_bad_nodes++; if(search_stack.back().equal_to_first_path == true && p.level > all_same_level) { assert(all_same_level >= 1); for(unsigned int i = all_same_level; i < search_stack.size(); i++) { search_stack[i].equal_to_first_path = false; } } while(!search_stack.empty()) { p.level--; LevelInfo &info2 = search_stack.back(); if(!(info2.equal_to_first_path == false && (!canonical || (info2.cmp_to_best_path < 0)))) break; search_stack.pop_back(); } continue; } } #if defined(VERIFY_EQUITABLEDNESS) /* The new partition should be equitable */ assert(is_equitable()); #endif info.equal_to_first_path = refine_equal_to_first; info.cmp_to_best_path = refine_cmp_to_best; certificate_index = certificate_current_path.size(); search_stack.back().eqref_hash = eqref_hash; search_stack.back().subcertificate_length = certificate_index - info.certificate_index; if(!p.is_discrete()) { /* * An internal, non-leaf node */ /* Build the next node info */ /* Find the next cell to be splitted */ assert(cell == p.element_to_cell_map[p.elements[info.split_cell_first]]); Cell * const next_split_cell = find_next_cell_to_be_splitted(cell); assert(next_split_cell); /* Copy current info to the search stack */ search_stack.push_back(info); LevelInfo &new_info = search_stack.back(); new_info.split_cell_first = next_split_cell->first; new_info.split_element = -1; new_info.certificate_index = certificate_index; new_info.refinement_stack_size = p.refinement_stack.size(); new_info.long_prune_redundant.clear(); new_info.long_prune_begin = info.long_prune_begin; continue; } /* * A leaf node */ assert(certificate_index == certificate_size); if(first_path_info.empty()) { /* The first path, update first_path and best_path */ //fprintf(stdout, "Level %u: FIRST\n", p.level); fflush(stdout); stats.nof_canupdates++; /* * Update labelings and their inverses */ update_labeling_and_its_inverse(first_path_labeling, first_path_labeling_inv); update_labeling_and_its_inverse(best_path_labeling, best_path_labeling_inv); /* * Reset automorphism array */ reset_permutation(first_path_automorphism); reset_permutation(best_path_automorphism); /* * Reset orbit information */ first_path_orbits.reset(); best_path_orbits.reset(); /* * Reset group size */ stats.group_size.assign(1); /* * Reset all_same_level */ all_same_level = p.level; /* * Mark the current path to be the first and best one and save it */ const unsigned int base_size = search_stack.size(); assert(p.level == base_size); best_path_info.clear(); //fprintf(stdout, " New base is: "); for(unsigned int i = 0; i < base_size; i++) { search_stack[i].in_first_path = true; search_stack[i].in_best_path = true; search_stack[i].equal_to_first_path = true; search_stack[i].cmp_to_best_path = 0; PathInfo path_info; path_info.splitting_element = search_stack[i].split_element; path_info.certificate_index = search_stack[i].certificate_index; path_info.eqref_hash = search_stack[i].eqref_hash; path_info.subcertificate_length = search_stack[i].subcertificate_length; first_path_info.push_back(path_info); best_path_info.push_back(path_info); //fprintf(stdout, "%u ", search_stack[i].split_element); } //fprintf(stdout, "\n"); fflush(stdout); certificate_first_path = certificate_current_path; certificate_best_path = certificate_current_path; refine_compare_certificate = true; /* * Backtrack to the previous level */ p.level--; continue; } DEBUG_ASSERT(first_path_info.size() > 0); //fprintf(stdout, "Level %u: LEAF %d %d\n", p.level, info.equal_to_first_path, info.cmp_to_best_path); fflush(stdout); if(info.equal_to_first_path) { /* * An automorphism found: aut[i] = elements[first_path_labeling[i]] */ assert(!info.in_first_path); //fprintf(stdout, "A"); fflush(stdout); #ifdef PRINT_SEARCH_TREE_DOT fprintf(dotty_output, "\"n"); for(unsigned int i = 0; i < search_stack.size(); i++) { fprintf(dotty_output, "%u", search_stack[i].split_element); if(i < search_stack.size() - 1) fprintf(dotty_output, "."); } fprintf(dotty_output, "\" [color=blue];\n"); #endif #if defined(DEBUG) /* Verify that the automorphism is correctly built */ for(unsigned int i = 0; i < N; i++) assert(first_path_automorphism[i] == p.elements[first_path_labeling[i]]); #endif #if defined(VERIFY_AUTOMORPHISMS) /* Verify that it really is an automorphism */ assert(is_automorphism(first_path_automorphism)); #endif long_prune_add_automorphism(first_path_automorphism); /* * Update orbit information */ update_orbit_information(first_path_orbits, first_path_automorphism); /* * Compute backjumping level */ unsigned int backjumping_level = 0; for(unsigned int i = search_stack.size(); i > 0; i--) { const unsigned int split_element = search_stack[backjumping_level].split_element; if(first_path_automorphism[split_element] != split_element) break; backjumping_level++; } assert(backjumping_level < p.level); /* * Go back to backjumping_level */ p.level = backjumping_level; search_stack.resize(p.level + 1); // if(write_automorphisms) // { // print_permutation(stdout, first_path_automorphism); // fprintf(stdout, "\n"); // } stats.nof_generators++; continue; } assert(canonical); assert(info.cmp_to_best_path >= 0); if(info.cmp_to_best_path > 0) { /* * A new, better representative found */ //fprintf(stdout, "Level %u: NEW BEST\n", p.level); fflush(stdout); stats.nof_canupdates++; /* * Update canonical labeling and its inverse */ update_labeling_and_its_inverse(best_path_labeling, best_path_labeling_inv); /* Reset best path automorphism */ reset_permutation(best_path_automorphism); /* Reset best path orbit structure */ best_path_orbits.reset(); /* * Mark the current path to be the best one and save it */ const unsigned int base_size = search_stack.size(); assert(p.level == base_size); best_path_info.clear(); //fprintf(stdout, " New base is: "); for(unsigned int i = 0; i < base_size; i++) { search_stack[i].cmp_to_best_path = 0; search_stack[i].in_best_path = true; PathInfo path_info; path_info.splitting_element = search_stack[i].split_element; path_info.certificate_index = search_stack[i].certificate_index; path_info.eqref_hash = search_stack[i].eqref_hash; path_info.subcertificate_length = search_stack[i].subcertificate_length; best_path_info.push_back(path_info); //fprintf(stdout, "%u ", search_stack[i].split_element); } certificate_best_path = certificate_current_path; //fprintf(stdout, "\n"); fflush(stdout); /* * Backtrack to the previous level */ p.level--; continue; } { //fprintf(stderr, "BAUT "); /* * Equal to the previous best path */ #if defined(DEBUG) /* Verify that the automorphism is correctly built */ for(unsigned int i = 0; i < N; i++) assert(best_path_automorphism[i] == p.elements[best_path_labeling[i]]); #endif #if defined(VERIFY_AUTOMORPHISMS) /* Verify that it really is an automorphism */ assert(is_automorphism(best_path_automorphism)); #endif unsigned int gca_level_with_first = 0; for(unsigned int i = search_stack.size(); i > 0; i--) { if((int)first_path_info[gca_level_with_first].splitting_element != search_stack[gca_level_with_first].split_element) break; gca_level_with_first++; } assert(gca_level_with_first < p.level); unsigned int gca_level_with_best = 0; for(unsigned int i = search_stack.size(); i > 0; i--) { if((int)best_path_info[gca_level_with_best].splitting_element != search_stack[gca_level_with_best].split_element) break; gca_level_with_best++; } assert(gca_level_with_best < p.level); long_prune_add_automorphism(best_path_automorphism); /* * Update orbit information */ update_orbit_information(best_path_orbits, best_path_automorphism); /* * Update orbit information */ const unsigned int nof_old_orbits = first_path_orbits.nof_orbits(); update_orbit_information(first_path_orbits, best_path_automorphism); if(nof_old_orbits != first_path_orbits.nof_orbits()) { // if(write_automorphisms) // { // print_permutation(stdout, best_path_automorphism); // fprintf(stdout, "\n"); // } stats.nof_generators++; } /* * Compute backjumping level */ unsigned int backjumping_level = p.level - 1; if(!first_path_orbits.is_minimal_representative(search_stack[gca_level_with_first].split_element)) { backjumping_level = gca_level_with_first; /*fprintf(stderr, "bj1: %u %u\n", p.level, backjumping_level);*/ } else { assert(!best_path_orbits.is_minimal_representative(search_stack[gca_level_with_best].split_element)); backjumping_level = gca_level_with_best; /*fprintf(stderr, "bj2: %u %u\n", p.level, backjumping_level);*/ } /* Backtrack */ search_stack.resize(backjumping_level + 1); p.level = backjumping_level; continue; } } #ifdef PRINT_SEARCH_TREE_DOT fprintf(dotty_output, "}\n"); fclose(dotty_output); #endif } void AbstractGraph::find_automorphisms(Stats &stats) { search(false, stats); if(first_path_labeling) { free(first_path_labeling); first_path_labeling = 0; } if(best_path_labeling) { free(best_path_labeling); best_path_labeling = 0; } } const unsigned int *AbstractGraph::canonical_form(Stats &stats) { search(true, stats); return best_path_labeling; } /*------------------------------------------------------------------------- * * Routines for undirected graphs * *-------------------------------------------------------------------------*/ Graph::Vertex::Vertex() { label = 1; nof_edges = 0; } Graph::Vertex::~Vertex() { ; } void Graph::Vertex::add_edge(const unsigned int other_vertex) { edges.push_back(other_vertex); nof_edges++; DEBUG_ASSERT(nof_edges == edges.size()); } void Graph::Vertex::remove_duplicate_edges(bool * const duplicate_array) { for(std::vector::iterator iter = edges.begin(); iter != edges.end(); ) { const unsigned int dest_vertex = *iter; if(duplicate_array[dest_vertex] == true) { /* A duplicate edge found! */ iter = edges.erase(iter); nof_edges--; DEBUG_ASSERT(nof_edges == edges.size()); } else { /* Not seen earlier, mark as seen */ duplicate_array[dest_vertex] = true; iter++; } } /* Clear duplicate_array */ for(std::vector::iterator iter = edges.begin(); iter != edges.end(); iter++) { duplicate_array[*iter] = false; } } /*------------------------------------------------------------------------- * * Constructor and destructor for undirected graphs * *-------------------------------------------------------------------------*/ Graph::Graph(const unsigned int nof_vertices) { vertices.resize(nof_vertices); sh = sh_flm; } Graph::~Graph() { ; } unsigned int Graph::add_vertex(const unsigned int new_label) { const unsigned int new_vertex_num = vertices.size(); vertices.resize(new_vertex_num + 1); vertices.back().label = new_label; return new_vertex_num; } void Graph::add_edge(const unsigned int vertex1, const unsigned int vertex2) { //fprintf(stderr, "(%u,%u) ", vertex1, vertex2); assert(vertex1 < vertices.size()); assert(vertex2 < vertices.size()); vertices[vertex1].add_edge(vertex2); vertices[vertex2].add_edge(vertex1); } void Graph::change_label(const unsigned int vertex, const unsigned int new_label) { assert(vertex < vertices.size()); vertices[vertex].label = new_label; } /*------------------------------------------------------------------------- * * Read graph in the DIMACS format * *-------------------------------------------------------------------------*/ // Graph *Graph::read_dimacs(FILE *fp) // { // Graph *g = 0; // unsigned int nof_vertices, nof_edges; // unsigned int line_num = 1; // int c; // /* read comments and problem line*/ // while(1) { // c = getc(fp); // if(c == 'c') { // while((c = getc(fp)) != '\n') { // if(c == EOF) { // fprintf(stderr, "error in line %u: not in DIMACS format\n", // line_num); // goto error_exit; // } // } // line_num++; // continue; // } // if(c == 'p') { // if(fscanf(fp, " edge %u %u\n", &nof_vertices, &nof_edges) != 2) { // fprintf(stderr, "error in line %u: not in DIMACS format\n", // line_num); // goto error_exit; } // line_num++; // break; // } // fprintf(stderr, "error in line %u: not in DIMACS format\n", line_num); // goto error_exit; // } // if(nof_vertices <= 0) { // fprintf(stderr, "error: no vertices\n"); // goto error_exit; // } // #if 0 // if(nof_edges <= 0) { // fprintf(stderr, "error: no edges\n"); // goto error_exit; // } // #endif // if(bliss_verbose) { // fprintf(bliss_verbstr, "Instance has %d vertices and %d edges\n", // nof_vertices, nof_edges); // fflush(bliss_verbstr); // } // g = new Graph(nof_vertices); // // // // Read vertex labels // // // if(bliss_verbose) { // fprintf(bliss_verbstr, "Reading vertex labels...\n"); // fflush(bliss_verbstr); } // while(1) { // c = getc(fp); // if(c != 'n') { // ungetc(c, fp); // break; // } // ungetc(c, fp); // unsigned int vertex, label; // if(fscanf(fp, "n %u %u\n", &vertex, &label) != 2) { // fprintf(stderr, "error in line %u: not in DIMACS format\n", // line_num); // goto error_exit; // } // if(vertex > nof_vertices) { // fprintf(stderr, "error in line %u: not in DIMACS format\n", // line_num); // goto error_exit; // } // line_num++; // g->change_label(vertex - 1, label); // } // if(bliss_verbose) { // fprintf(bliss_verbstr, "Done\n"); // fflush(bliss_verbstr); } // // // // Read edges // // // if(bliss_verbose) { // fprintf(bliss_verbstr, "Reading edges...\n"); // fflush(bliss_verbstr); } // for(unsigned i = 0; i < nof_edges; i++) { // unsigned int from, to; // if(fscanf(fp, "e %u %u\n", &from, &to) != 2) { // fprintf(stderr, "error in line %u: not in DIMACS format\n", // line_num); // goto error_exit; // } // if(from > nof_vertices || to > nof_vertices) { // fprintf(stderr, "error in line %u: not in DIMACS format\n", // line_num); // goto error_exit; // } // line_num++; // g->add_edge(from - 1, to - 1); // } // if(bliss_verbose) { // fprintf(bliss_verbstr, "Done\n"); // fflush(bliss_verbstr); // } // return g; // error_exit: // if(g) // delete g; // return 0; // } Graph *Graph::from_igraph(const igraph_t *graph) { unsigned int nof_vertices= (unsigned int)igraph_vcount(graph); unsigned int nof_edges= (unsigned int)igraph_ecount(graph); Graph *g=new Graph(nof_vertices); // for (unsigned int i=0; ichange_label(i, i); // } for (unsigned int i=0; iadd_edge((unsigned int)IGRAPH_FROM(graph, i), (unsigned int)IGRAPH_TO(graph, i)); } return g; } void Graph::print_dimacs(FILE *fp) { unsigned int nof_edges = 0; for(unsigned int i = 0; i < get_nof_vertices(); i++) { Vertex &v = vertices[i]; for(std::vector::const_iterator ei = v.edges.begin(); ei != v.edges.end(); ei++) { const unsigned int dest_i = *ei; if(dest_i < i) continue; nof_edges++; } } fprintf(fp, "p edge %u %u\n", get_nof_vertices(), nof_edges); for(unsigned int i = 0; i < get_nof_vertices(); i++) { Vertex &v = vertices[i]; if(v.label != 1) { fprintf(fp, "n %u %u\n", i+1, v.label); } } for(unsigned int i = 0; i < get_nof_vertices(); i++) { Vertex &v = vertices[i]; for(std::vector::const_iterator ei = v.edges.begin(); ei != v.edges.end(); ei++) { const unsigned int dest_i = *ei; if(dest_i < i) continue; fprintf(fp, "e %u %u\n", i+1, dest_i+1); } } } Graph *Graph::permute(const unsigned int *perm) { Graph *g = new Graph(get_nof_vertices()); for(unsigned int i = 0; i < get_nof_vertices(); i++) { Vertex &v = vertices[i]; Vertex &permuted_v = g->vertices[perm[i]]; permuted_v.label = v.label; for(std::vector::const_iterator ei = v.edges.begin(); ei != v.edges.end(); ei++) { const unsigned int dest_v = *ei; permuted_v.add_edge(perm[dest_v]); } std::sort(permuted_v.edges.begin(), permuted_v.edges.end()); } return g; } /*------------------------------------------------------------------------- * * Print graph in graphviz format * *-------------------------------------------------------------------------*/ void Graph::to_dot(const char *file_name) { FILE *fp = fopen(file_name, "w"); if(fp) to_dot(fp); fclose(fp); } void Graph::to_dot(FILE *fp) { remove_duplicate_edges(); fprintf(fp, "graph g {\n"); unsigned int vnum = 0; for(std::vector::iterator vi = vertices.begin(); vi != vertices.end(); vi++, vnum++) { Vertex &v = *vi; fprintf(fp, "v%u [label=\"%u:%u\"];\n", vnum, vnum, v.label); for(std::vector::const_iterator ei = v.edges.begin(); ei != v.edges.end(); ei++) { const unsigned int vnum2 = *ei; if(vnum2 > vnum) fprintf(fp, "v%u -- v%u\n", vnum, vnum2); } } fprintf(fp, "}\n"); } void Graph::remove_duplicate_edges() { bool *duplicate_array = (bool*)calloc(vertices.size(), sizeof(bool)); for(std::vector::iterator vi = vertices.begin(); vi != vertices.end(); vi++) { #ifdef EXPENSIVE_CONSISTENCY_CHECKS for(unsigned int i = 0; i < vertices.size(); i++) assert(duplicate_array[i] == false); #endif Vertex &v = *vi; v.remove_duplicate_edges(duplicate_array); } free(duplicate_array); } /*------------------------------------------------------------------------- * * Partition independent invariants * *-------------------------------------------------------------------------*/ unsigned int Graph::label_invariant(Graph *g, unsigned int v) { DEBUG_ASSERT(v < g->vertices.size()); return g->vertices[v].label; } unsigned int Graph::degree_invariant(Graph *g, unsigned int v) { DEBUG_ASSERT(v < g->vertices.size()); DEBUG_ASSERT(g->vertices[v].edges.size() == g->vertices[v].nof_edges); return g->vertices[v].nof_edges; } /*------------------------------------------------------------------------- * * Refine the partition p according to a partition independent invariant * *-------------------------------------------------------------------------*/ bool Graph::refine_according_to_invariant(unsigned int (*inv)(Graph * const g, unsigned int v)) { bool refined = false; for(Cell *cell = p.first_cell; cell; ) { assert(cell->max_ival == 0); assert(cell->max_ival_count == 0); Cell * const next_cell = cell->next; if(cell->length == 1) { cell = next_cell; continue; } const unsigned int *ep = p.elements + cell->first; for(unsigned int i = cell->length; i > 0; i--, ep++) { unsigned int ival = inv(this, *ep); p.invariant_values[*ep] = ival; if(ival > cell->max_ival) { cell->max_ival = ival; cell->max_ival_count = 1; } else if(ival == cell->max_ival) { cell->max_ival_count++; } } Cell * const last_new_cell = p.zplit_cell(cell, true); refined = (last_new_cell != cell); cell = next_cell; } return refined; } /*------------------------------------------------------------------------- * * Split the neighbourhood of a cell according to the equitable invariant * *-------------------------------------------------------------------------*/ void Graph::split_neighbourhood_of_cell(Cell * const cell) { DEBUG_ASSERT(neighbour_heap.is_empty()); DEBUG_ASSERT(cell->length > 1); eqref_hash.update(cell->first); eqref_hash.update(cell->length); unsigned int *ep = p.elements + cell->first; for(unsigned int i = cell->length; i > 0; i--) { const Vertex &v = vertices[*ep]; ep++; std::vector::const_iterator ei = v.edges.begin(); for(unsigned int j = v.nof_edges; j > 0; j--) { const unsigned int dest_vertex = *ei++; Cell * const neighbour_cell = p.element_to_cell_map[dest_vertex]; if(neighbour_cell->length == 1) continue; const unsigned int ival = p.invariant_values[dest_vertex] + 1; p.invariant_values[dest_vertex] = ival; if(ival > neighbour_cell->max_ival) { neighbour_cell->max_ival = ival; neighbour_cell->max_ival_count = 1; } else if(ival == neighbour_cell->max_ival) { neighbour_cell->max_ival_count++; } if(!neighbour_cell->in_neighbour_heap) { neighbour_cell->in_neighbour_heap = true; neighbour_heap.insert(neighbour_cell->first); } } } while(!neighbour_heap.is_empty()) { const unsigned int start = neighbour_heap.remove(); Cell * const neighbour_cell = p.element_to_cell_map[p.elements[start]]; DEBUG_ASSERT(neighbour_cell->first == start); DEBUG_ASSERT(neighbour_cell->in_neighbour_heap); neighbour_cell->in_neighbour_heap = false; DEBUG_ASSERT(neighbour_cell->length > 1); DEBUG_ASSERT(neighbour_cell->max_ival >= 1); DEBUG_ASSERT(neighbour_cell->max_ival_count >= 1); eqref_hash.update(neighbour_cell->first); eqref_hash.update(neighbour_cell->length); eqref_hash.update(neighbour_cell->max_ival); eqref_hash.update(neighbour_cell->max_ival_count); Cell * const last_new_cell = p.zplit_cell(neighbour_cell, true); /* Update hash */ const Cell *c = neighbour_cell; while(1) { eqref_hash.update(c->first); eqref_hash.update(c->length); if(c == last_new_cell) break; c = c->next; } } } bool Graph::split_neighbourhood_of_unit_cell(Cell * const unit_cell) { DEBUG_ASSERT(neighbour_heap.is_empty()); DEBUG_ASSERT(unit_cell->length == 1); DEBUG_ASSERT(p.element_to_cell_map[p.elements[unit_cell->first]] == unit_cell); DEBUG_ASSERT(p.in_pos[p.elements[unit_cell->first]] == p.elements + unit_cell->first); eqref_hash.update(0x87654321); eqref_hash.update(unit_cell->first); eqref_hash.update(1); const Vertex &v = vertices[p.elements[unit_cell->first]]; std::vector::const_iterator ei = v.edges.begin(); for(unsigned int j = v.nof_edges; j > 0; j--) { const unsigned int dest_vertex = *ei++; Cell * const neighbour_cell = p.element_to_cell_map[dest_vertex]; DEBUG_ASSERT(*p.in_pos[dest_vertex] == dest_vertex); if(neighbour_cell->length == 1) { DEBUG_ASSERT(!neighbour_cell->in_neighbour_heap); if(in_search) { neighbour_cell->in_neighbour_heap = true; neighbour_heap.insert(neighbour_cell->first); } continue; } if(!neighbour_cell->in_neighbour_heap) { neighbour_cell->in_neighbour_heap = true; neighbour_heap.insert(neighbour_cell->first); } neighbour_cell->max_ival_count++; DEBUG_ASSERT(neighbour_cell->max_ival_count <= neighbour_cell->length); unsigned int * const swap_position = p.elements + neighbour_cell->first + neighbour_cell->length - neighbour_cell->max_ival_count; DEBUG_ASSERT(p.in_pos[dest_vertex] <= swap_position); *p.in_pos[dest_vertex] = *swap_position; p.in_pos[*swap_position] = p.in_pos[dest_vertex]; *swap_position = dest_vertex; p.in_pos[dest_vertex] = swap_position; } while(!neighbour_heap.is_empty()) { const unsigned int start = neighbour_heap.remove(); Cell *neighbour_cell = p.element_to_cell_map[p.elements[start]]; DEBUG_ASSERT(neighbour_cell->in_neighbour_heap); neighbour_cell->in_neighbour_heap = false; #ifdef DEBUG assert(neighbour_cell->first == start); if(neighbour_cell->length == 1) { assert(neighbour_cell->max_ival_count == 0); } else { assert(neighbour_cell->max_ival_count > 0); assert(neighbour_cell->max_ival_count <= neighbour_cell->length); } #endif eqref_hash.update(neighbour_cell->first); eqref_hash.update(neighbour_cell->length); eqref_hash.update(neighbour_cell->max_ival_count); if(neighbour_cell->length > 1 && neighbour_cell->max_ival_count != neighbour_cell->length) { p.consistency_check(); Cell * const new_cell = p.aux_split_in_two(neighbour_cell, neighbour_cell->length - neighbour_cell->max_ival_count); unsigned int *ep = p.elements + new_cell->first; unsigned int * const lp = p.elements+new_cell->first+new_cell->length; while(ep < lp) { DEBUG_ASSERT(p.in_pos[*ep] == ep); p.element_to_cell_map[*ep] = new_cell; ep++; } neighbour_cell->max_ival_count = 0; p.consistency_check(); /* update hash */ eqref_hash.update(neighbour_cell->first); eqref_hash.update(neighbour_cell->length); eqref_hash.update(0); eqref_hash.update(new_cell->first); eqref_hash.update(new_cell->length); eqref_hash.update(1); /* Add cells in splitting_queue */ DEBUG_ASSERT(!new_cell->in_splitting_queue); if(neighbour_cell->in_splitting_queue) { /* Both cells must be included in splitting_queue in order to have refinement to equitable partition */ p.add_in_splitting_queue(new_cell); } else { Cell *min_cell, *max_cell; if(neighbour_cell->length <= new_cell->length) { min_cell = neighbour_cell; max_cell = new_cell; } else { min_cell = new_cell; max_cell = neighbour_cell; } /* Put the smaller cell in splitting_queue */ p.add_in_splitting_queue(min_cell); if(max_cell->length == 1) { /* Put the "larger" cell also in splitting_queue */ p.add_in_splitting_queue(max_cell); } } /* Update pointer for certificate generation */ neighbour_cell = new_cell; } else neighbour_cell->max_ival_count = 0; /* * Build certificate if required */ if(in_search) { for(unsigned int i = neighbour_cell->first, j = neighbour_cell->length, c_index = certificate_current_path.size(); j > 0; j--, i++, c_index += 2) { if(refine_compare_certificate) { if(refine_equal_to_first) { if(c_index >= refine_first_path_subcertificate_end) refine_equal_to_first = false; else if(certificate_first_path[c_index] != unit_cell->first) refine_equal_to_first = false; else if(certificate_first_path[c_index+1] != i) refine_equal_to_first = false; } if(refine_cmp_to_best == 0) { if(c_index >= refine_best_path_subcertificate_end) { refine_cmp_to_best = 1; } else if(unit_cell->first>certificate_best_path[c_index]) { refine_cmp_to_best = 1; } else if(unit_cell->first certificate_best_path[c_index+1]) { refine_cmp_to_best = 1; } else if(i < certificate_best_path[c_index+1]) { refine_cmp_to_best = -1; } } if((refine_equal_to_first == false) && (refine_cmp_to_best < 0)) goto worse_exit; } certificate_current_path.push_back(unit_cell->first); certificate_current_path.push_back(i); } } /* if(in_search) */ } /* while(!neighbour_heap.is_empty()) */ return false; worse_exit: while(!neighbour_heap.is_empty()) { const unsigned int start = neighbour_heap.remove(); Cell * const neighbour_cell = p.element_to_cell_map[p.elements[start]]; DEBUG_ASSERT(neighbour_cell->in_neighbour_heap); neighbour_cell->in_neighbour_heap = false; neighbour_cell->max_ival_count = 0; } return true; } /*------------------------------------------------------------------------- * * Check whether the current partition p is equitable * Slow: use only for debugging purposes * Side effect: resets max_ival and max_ival_count fields in cells * *-------------------------------------------------------------------------*/ bool Graph::is_equitable() { bool result = true; /* * Max ival and max_ival_count are used for counting purposes, * they should be reset... */ for(Cell *cell = p.first_cell; cell; cell = cell->next) { assert(cell->prev_next_ptr && *(cell->prev_next_ptr) == cell); assert(cell->max_ival == 0); assert(cell->max_ival_count == 0); } for(Cell *cell = p.first_cell; cell; cell = cell->next) { if(cell->length == 1) continue; unsigned int *ep = p.elements + cell->first; Vertex &first_vertex = vertices[*ep++]; /* Count edges of the first vertex for cells in max_ival */ std::vector::const_iterator ei = first_vertex.edges.begin(); for(unsigned int j = first_vertex.nof_edges; j > 0; j--) { p.element_to_cell_map[*ei++]->max_ival++; } /* Count and compare edges of the other vertices */ for(unsigned int i = cell->length; i > 1; i--) { Vertex &vertex = vertices[*ep++]; std::vector::const_iterator ei = vertex.edges.begin(); for(unsigned int j = vertex.nof_edges; j > 0; j--) { p.element_to_cell_map[*ei++]->max_ival_count++; } for(Cell *cell2 = p.first_cell; cell2; cell2 = cell2->next) { if(cell2->max_ival != cell2->max_ival_count) { result = false; goto done; } cell2->max_ival_count = 0; } } /* Reset max_ival */ for(Cell *cell2 = p.first_cell; cell2; cell2 = cell2->next) { cell2->max_ival = 0; assert(cell2->max_ival_count == 0); } } done: for(Cell *cell = p.first_cell; cell; cell = cell->next) { cell->max_ival = 0; cell->max_ival_count = 0; } return result; } /*------------------------------------------------------------------------- * * Build the initial equitable partition * *-------------------------------------------------------------------------*/ void Graph::make_initial_equitable_partition() { refine_according_to_invariant(&label_invariant); p.clear_splitting_queue(); //p.print_signature(stderr); fprintf(stderr, "\n"); refine_according_to_invariant(°ree_invariant); p.clear_splitting_queue(); //p.print_signature(stderr); fprintf(stderr, "\n"); /* To do: add loop invariant */ refine_to_equitable(); p.refinement_stack.clean(); //p.print_signature(stderr); fprintf(stderr, "\n"); } /*------------------------------------------------------------------------- * * Find the next cell to be splitted * *-------------------------------------------------------------------------*/ Cell *Graph::find_next_cell_to_be_splitted(Cell *cell) { assert(!p.is_discrete()); switch(sh) { case sh_f: return sh_first(cell); case sh_fs: return sh_first_smallest(cell); case sh_fl: return sh_first_largest(cell); case sh_fm: return sh_first_max_neighbours(cell); case sh_fsm: return sh_first_smallest_max_neighbours(cell); case sh_flm: return sh_first_largest_max_neighbours(cell); default: assert(false && "Unknown splitting heuristics"); return 0; } } /* First nonsingleton cell */ Cell *Graph::sh_first(Cell *cell) { IGRAPH_UNUSED(cell); return p.first_nonsingleton_cell; } /* First smallest nonsingleton cell. */ Cell *Graph::sh_first_smallest(Cell *cell) { Cell *best_cell = 0; unsigned int best_size = UINT_MAX; for(cell = p.first_nonsingleton_cell; cell; cell = cell->next_nonsingleton) { assert(cell->length > 1); if(cell->length < best_size) { best_size = cell->length; best_cell = cell; } } assert(best_cell); return best_cell; } /* First largest nonsingleton cell. */ Cell *Graph::sh_first_largest(Cell *cell) { Cell *best_cell = 0; unsigned int best_size = 0; for(cell = p.first_nonsingleton_cell; cell; cell = cell->next_nonsingleton) { assert(cell->length > 1); if(cell->length > best_size) { best_size = cell->length; best_cell = cell; } } assert(best_cell); return best_cell; } /* First nonsingleton cell with max number of neighbouring * nonsingleton cells. * Assumes that the partition p is equitable. * Messes up in_neighbour_heap and max_ival fields of cells * (assumes they are false/0). */ Cell *Graph::sh_first_max_neighbours(Cell *cell) { Cell *best_cell = 0; int best_value = -1; for(cell = p.first_nonsingleton_cell; cell; cell = cell->next_nonsingleton) { assert(cell->length > 1); const Vertex &v = vertices[p.elements[cell->first]]; std::vector::const_iterator ei = v.edges.begin(); std::list neighbour_cells_visited; for(unsigned int j = v.nof_edges; j > 0; j--) { const unsigned int dest_vertex = *ei++; Cell * const neighbour_cell = p.element_to_cell_map[dest_vertex]; if(neighbour_cell->length == 1) continue; neighbour_cell->max_ival++; if(neighbour_cell->in_neighbour_heap) continue; neighbour_cell->in_neighbour_heap = true; neighbour_cells_visited.push_back(neighbour_cell); } int value = 0; while(!neighbour_cells_visited.empty()) { Cell * const neighbour_cell = neighbour_cells_visited.front(); neighbour_cells_visited.pop_front(); assert(neighbour_cell->in_neighbour_heap); neighbour_cell->in_neighbour_heap = false; if(neighbour_cell->max_ival != neighbour_cell->length) value++; neighbour_cell->max_ival = 0; } if(value > best_value) { best_value = value; best_cell = cell; } } assert(best_cell); return best_cell; } /* First smallest nonsingleton cell with max number of neighbouring * nonsingleton cells. * Assumes that the partition p is equitable. * Messes up in_neighbour_heap and max_ival fields of cells * (assumes they are false). */ Cell *Graph::sh_first_smallest_max_neighbours(Cell *cell) { Cell *best_cell = 0; int best_value = -1; int best_size = INT_MAX; for(cell = p.first_nonsingleton_cell; cell; cell = cell->next_nonsingleton) { assert(cell->length > 1); const Vertex &v = vertices[p.elements[cell->first]]; std::vector::const_iterator ei = v.edges.begin(); std::list neighbour_cells_visited; for(unsigned int j = v.nof_edges; j > 0; j--) { const unsigned int dest_vertex = *ei++; Cell * const neighbour_cell = p.element_to_cell_map[dest_vertex]; if(neighbour_cell->length == 1) continue; neighbour_cell->max_ival++; if(neighbour_cell->in_neighbour_heap) continue; neighbour_cell->in_neighbour_heap = true; neighbour_cells_visited.push_back(neighbour_cell); } int value = 0; while(!neighbour_cells_visited.empty()) { Cell * const neighbour_cell = neighbour_cells_visited.front(); neighbour_cells_visited.pop_front(); assert(neighbour_cell->in_neighbour_heap); neighbour_cell->in_neighbour_heap = false; if(neighbour_cell->max_ival != neighbour_cell->length) value++; neighbour_cell->max_ival = 0; } if((value > best_value) || (value == best_value && (int)cell->length < best_size)) { best_value = value; best_size = cell->length; best_cell = cell; } } assert(best_cell); return best_cell; } /* First largest nonsingleton cell with max number of neighbouring * nonsingleton cells. * Assumes that the partition p is equitable. * Messes up in_neighbour_heap and max_ival fields of cells * (assumes they are false/0). */ Cell *Graph::sh_first_largest_max_neighbours(Cell *cell) { Cell *best_cell = 0; int best_value = -1; int best_size = -1; for(cell = p.first_nonsingleton_cell; cell; cell = cell->next_nonsingleton) { assert(cell->length > 1); const Vertex &v = vertices[p.elements[cell->first]]; std::vector::const_iterator ei = v.edges.begin(); std::list neighbour_cells_visited; for(unsigned int j = v.nof_edges; j > 0; j--) { const unsigned int dest_vertex = *ei++; Cell * const neighbour_cell = p.element_to_cell_map[dest_vertex]; if(neighbour_cell->length == 1) continue; neighbour_cell->max_ival++; if(neighbour_cell->in_neighbour_heap) continue; neighbour_cell->in_neighbour_heap = true; neighbour_cells_visited.push_back(neighbour_cell); } int value = 0; while(!neighbour_cells_visited.empty()) { Cell * const neighbour_cell = neighbour_cells_visited.front(); neighbour_cells_visited.pop_front(); assert(neighbour_cell->in_neighbour_heap); neighbour_cell->in_neighbour_heap = false; if(neighbour_cell->max_ival != neighbour_cell->length) value++; neighbour_cell->max_ival = 0; } if((value > best_value) || (value == best_value && (int)cell->length > best_size)) { best_value = value; best_size = cell->length; best_cell = cell; } } assert(best_cell); return best_cell; } /*------------------------------------------------------------------------- * * Initialize the certificate size and memory * *-------------------------------------------------------------------------*/ void Graph::initialize_certificate() { certificate_size = 0; for(Cell *cell = p.first_cell; cell; cell = cell->next) { if(cell->length > 1) { certificate_size += vertices[p.elements[cell->first]].nof_edges * 2 * cell->length; } } //if(certificate) // free(certificate); //certificate = (unsigned int*)malloc(certificate_size * sizeof(unsigned int)); certificate_index = 0; certificate_current_path.clear(); certificate_first_path.clear(); certificate_best_path.clear(); } /*------------------------------------------------------------------------- * * Check whether perm is an automorphism * *-------------------------------------------------------------------------*/ bool Graph::is_automorphism(unsigned int * const perm) { std::set > edges1; std::set > edges2; bool result = true; for(unsigned int i = 0; i < vertices.size(); i++) { Vertex &v1 = vertices[i]; edges1.clear(); for(std::vector::iterator ei = v1.edges.begin(); ei != v1.edges.end(); ei++) edges1.insert(perm[*ei]); Vertex &v2 = vertices[perm[i]]; edges2.clear(); for(std::vector::iterator ei = v2.edges.begin(); ei != v2.edges.end(); ei++) edges2.insert(*ei); if(!(edges1 == edges2)) { result = false; goto done; } } done: return result; } } igraph/src/cs_counts.c0000644000176000001440000000730112325527073014516 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* column counts of LL'=A or LL'=A'A, given parent & post ordering */ #define HEAD(k,j) (ata ? head [k] : j) #define NEXT(J) (ata ? next [J] : -1) static void init_ata (cs *AT, const CS_INT *post, CS_INT *w, CS_INT **head, CS_INT **next) { CS_INT i, k, p, m = AT->n, n = AT->m, *ATp = AT->p, *ATi = AT->i ; *head = w+4*n, *next = w+5*n+1 ; for (k = 0 ; k < n ; k++) w [post [k]] = k ; /* invert post */ for (i = 0 ; i < m ; i++) { for (k = n, p = ATp[i] ; p < ATp[i+1] ; p++) k = CS_MIN (k, w [ATi[p]]); (*next) [i] = (*head) [k] ; /* place row i in linked list k */ (*head) [k] = i ; } } CS_INT *cs_counts (const cs *A, const CS_INT *parent, const CS_INT *post, CS_INT ata) { CS_INT i, j, k, n, m, J, s, p, q, jleaf, *ATp, *ATi, *maxfirst, *prevleaf, *ancestor, *head = NULL, *next = NULL, *colcount, *w, *first, *delta ; cs *AT ; if (!CS_CSC (A) || !parent || !post) return (NULL) ; /* check inputs */ m = A->m ; n = A->n ; s = 4*n + (ata ? (n+m+1) : 0) ; delta = colcount = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ w = cs_malloc (s, sizeof (CS_INT)) ; /* get workspace */ AT = cs_transpose (A, 0) ; /* AT = A' */ if (!AT || !colcount || !w) return (cs_idone (colcount, AT, w, 0)) ; ancestor = w ; maxfirst = w+n ; prevleaf = w+2*n ; first = w+3*n ; for (k = 0 ; k < s ; k++) w [k] = -1 ; /* clear workspace w [0..s-1] */ for (k = 0 ; k < n ; k++) /* find first [j] */ { j = post [k] ; delta [j] = (first [j] == -1) ? 1 : 0 ; /* delta[j]=1 if j is a leaf */ for ( ; j != -1 && first [j] == -1 ; j = parent [j]) first [j] = k ; } ATp = AT->p ; ATi = AT->i ; if (ata) init_ata (AT, post, w, &head, &next) ; for (i = 0 ; i < n ; i++) ancestor [i] = i ; /* each node in its own set */ for (k = 0 ; k < n ; k++) { j = post [k] ; /* j is the kth node in postordered etree */ if (parent [j] != -1) delta [parent [j]]-- ; /* j is not a root */ for (J = HEAD (k,j) ; J != -1 ; J = NEXT (J)) /* J=j for LL'=A case */ { for (p = ATp [J] ; p < ATp [J+1] ; p++) { i = ATi [p] ; q = cs_leaf (i, j, first, maxfirst, prevleaf, ancestor, &jleaf); if (jleaf >= 1) delta [j]++ ; /* A(i,j) is in skeleton */ if (jleaf == 2) delta [q]-- ; /* account for overlap in q */ } } if (parent [j] != -1) ancestor [j] = parent [j] ; } for (j = 0 ; j < n ; j++) /* sum up delta's of each child */ { if (parent [j] != -1) colcount [parent [j]] += colcount [j] ; } return (cs_idone (colcount, AT, w, 1)) ; /* success: free workspace */ } igraph/src/gengraph_definitions.h0000644000176000001440000001064312325527073016714 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DEFINITIONS_H #define DEFINITIONS_H #include #include #include namespace gengraph { // Max line size in files #define FBUFF_SIZE 1000000 // disable lousy VC++ warnings #ifdef _ATL_VER_ #pragma warning(disable : 4127) #endif //_ATL_VER_ // Verbose #define VERBOSE_NONE 0 #define VERBOSE_SOME 1 #define VERBOSE_LOTS 2 int VERBOSE(); void SET_VERBOSE(int v); // Random number generator void my_srandom(int); int my_random(); int my_binomial(double pp, int n); double my_random01(); // (0,1] #define MY_RAND_MAX 0x7FFFFFFF // IPv4 address direct translation into 32-bit uint + special IP defs typedef unsigned int ip_addr; #define IP_NONE 0x7FFFFFFF #define IP_STAR 0x00000000 #define IP_MYSELF 0x7F000001 // Compatibility #ifdef _WIN32 #define strcasecmp _stricmp #endif //inline double round(double x) throw () { return (floor(0.5+x)); } // No assert #ifndef _DEBUG #ifndef NDEBUG #define NDEBUG #endif //NDEBUG #endif //_DEBUG // Min & Max #ifndef min #define defmin(type) inline type min(type a, type b) { return ab ? a : b; } defmax(int) defmax(double) defmax(unsigned long) #endif //max // Traceroute Sampling #define MODE_USP 0 #define MODE_ASP 1 #define MODE_RSP 2 // Debug definitions //#define PERFORMANCE_MONITOR //#define OPT_ISOLATED // Max Int #ifndef MAX_INT #define MAX_INT 0x7FFFFFFF #endif //MAX_INT //Edge type typedef struct { int from; int to; } edge; // Tag Int #define TAG_INT 0x40000000 // Oldies .... #define S_VECTOR_RAW //********************* // Routine definitions //********************* /* log(1+x) inline double logp(double x) { if(fabs(x)<1e-6) return x+0.5*x*x+0.333333333333333*x*x*x; else return log(1.0+x); } //*/ //Fast search or replace inline int* fast_rpl(int *m, const int a, const int b) { while(*m!=a) m++; *m = b; return m; } inline int* fast_search(int *m, const int size, const int a) { int *p = m+size; while(m != p--) if(*p == a) return p; return NULL; } // Lovely percentage print // inline void print_percent(double yo, FILE *f = stderr) { // int arf = int(100.0*yo); // if(double(arf)>100.0*yo) arf--; // if(arf<100) fprintf(f," "); // if(arf<10) fprintf(f," "); // fprintf(f,"%d.%d%%",arf,int(1000.0*yo-double(10*arf))); // } // Skips non-numerical chars, then numerical chars, then non-numerical chars. inline char skip_int(char* &c) { while(*c<'0' || *c>'9') c++; while(*c>='0' && *c<='9') c++; while(*c!=0 && (*c<'0' || *c>'9')) c++; return *c; } // distance+1 modulo 255 for breadth-first search inline unsigned char next_dist(const unsigned char c) { return c==255 ? 1 : c+1; } inline unsigned char prev_dist(const unsigned char c) { return c==1 ? 255 : c-1; } // 1/(RANDMAX+1) #define inv_RANDMAX (1.0/(1.0+double(MY_RAND_MAX))) // random number in ]0,1[, _very_ accurate around 0 inline double random_float() { int r=my_random(); double mul=inv_RANDMAX; while(r<=0x7FFFFF) { r<<=8; r+=(my_random()&0xFF); mul*=(1.0/256.0); } return double(r)*mul; } // Return true with probability p. Very accurate when p is small. #define test_proba(p) (random_float()<(p)) // Random bit generator, sparwise. static int _random_bits_stored = 0; static int _random_bits = 0; inline int random_bit() { register int a = _random_bits; _random_bits = a >> 1; if(_random_bits_stored--) return a&0x1; a = my_random(); _random_bits = a >> 1; _random_bits_stored = 30; return a&0x1; } // Hash Profiling (see hash.h) void _hash_prof(); } // namespace gengraph #endif //DEFINITIONS_H igraph/src/glpmpl01.c0000644000176000001440000052734212325527073014166 0ustar ripleyusers/* glpmpl01.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #define _GLPSTD_STDIO #include "glpmpl.h" #define dmp_get_atomv dmp_get_atom /**********************************************************************/ /* * * PROCESSING MODEL SECTION * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- enter_context - enter current token into context queue. -- -- This routine enters the current token into the context queue. */ void enter_context(MPL *mpl) { char *image, *s; if (mpl->token == T_EOF) image = "_|_"; else if (mpl->token == T_STRING) image = "'...'"; else image = mpl->image; xassert(0 <= mpl->c_ptr && mpl->c_ptr < CONTEXT_SIZE); mpl->context[mpl->c_ptr++] = ' '; if (mpl->c_ptr == CONTEXT_SIZE) mpl->c_ptr = 0; for (s = image; *s != '\0'; s++) { mpl->context[mpl->c_ptr++] = *s; if (mpl->c_ptr == CONTEXT_SIZE) mpl->c_ptr = 0; } return; } /*---------------------------------------------------------------------- -- print_context - print current content of context queue. -- -- This routine prints current content of the context queue. */ void print_context(MPL *mpl) { int c; while (mpl->c_ptr > 0) { mpl->c_ptr--; c = mpl->context[0]; memmove(mpl->context, mpl->context+1, CONTEXT_SIZE-1); mpl->context[CONTEXT_SIZE-1] = (char)c; } xprintf("Context: %s%.*s\n", mpl->context[0] == ' ' ? "" : "...", CONTEXT_SIZE, mpl->context); return; } /*---------------------------------------------------------------------- -- get_char - scan next character from input text file. -- -- This routine scans a next ASCII character from the input text file. -- In case of end-of-file, the character is assigned EOF. */ void get_char(MPL *mpl) { int c; if (mpl->c == EOF) goto done; if (mpl->c == '\n') mpl->line++; c = read_char(mpl); if (c == EOF) { if (mpl->c == '\n') mpl->line--; else warning(mpl, "final NL missing before end of file"); } else if (c == '\n') ; else if (isspace(c)) c = ' '; else if (iscntrl(c)) { enter_context(mpl); error(mpl, "control character 0x%02X not allowed", c); } mpl->c = c; done: return; } /*---------------------------------------------------------------------- -- append_char - append character to current token. -- -- This routine appends the current character to the current token and -- then scans a next character. */ void append_char(MPL *mpl) { xassert(0 <= mpl->imlen && mpl->imlen <= MAX_LENGTH); if (mpl->imlen == MAX_LENGTH) { switch (mpl->token) { case T_NAME: enter_context(mpl); error(mpl, "symbolic name %s... too long", mpl->image); case T_SYMBOL: enter_context(mpl); error(mpl, "symbol %s... too long", mpl->image); case T_NUMBER: enter_context(mpl); error(mpl, "numeric literal %s... too long", mpl->image); case T_STRING: enter_context(mpl); error(mpl, "string literal too long"); default: xassert(mpl != mpl); } } mpl->image[mpl->imlen++] = (char)mpl->c; mpl->image[mpl->imlen] = '\0'; get_char(mpl); return; } /*---------------------------------------------------------------------- -- get_token - scan next token from input text file. -- -- This routine scans a next token from the input text file using the -- standard finite automation technique. */ void get_token(MPL *mpl) { /* save the current token */ mpl->b_token = mpl->token; mpl->b_imlen = mpl->imlen; strcpy(mpl->b_image, mpl->image); mpl->b_value = mpl->value; /* if the next token is already scanned, make it current */ if (mpl->f_scan) { mpl->f_scan = 0; mpl->token = mpl->f_token; mpl->imlen = mpl->f_imlen; strcpy(mpl->image, mpl->f_image); mpl->value = mpl->f_value; goto done; } loop: /* nothing has been scanned so far */ mpl->token = 0; mpl->imlen = 0; mpl->image[0] = '\0'; mpl->value = 0.0; /* skip any uninteresting characters */ while (mpl->c == ' ' || mpl->c == '\n') get_char(mpl); /* recognize and construct the token */ if (mpl->c == EOF) { /* end-of-file reached */ mpl->token = T_EOF; } else if (mpl->c == '#') { /* comment; skip anything until end-of-line */ while (mpl->c != '\n' && mpl->c != EOF) get_char(mpl); goto loop; } else if (!mpl->flag_d && (isalpha(mpl->c) || mpl->c == '_')) { /* symbolic name or reserved keyword */ mpl->token = T_NAME; while (isalnum(mpl->c) || mpl->c == '_') append_char(mpl); if (strcmp(mpl->image, "and") == 0) mpl->token = T_AND; else if (strcmp(mpl->image, "by") == 0) mpl->token = T_BY; else if (strcmp(mpl->image, "cross") == 0) mpl->token = T_CROSS; else if (strcmp(mpl->image, "diff") == 0) mpl->token = T_DIFF; else if (strcmp(mpl->image, "div") == 0) mpl->token = T_DIV; else if (strcmp(mpl->image, "else") == 0) mpl->token = T_ELSE; else if (strcmp(mpl->image, "if") == 0) mpl->token = T_IF; else if (strcmp(mpl->image, "in") == 0) mpl->token = T_IN; #if 1 /* 21/VII-2006 */ else if (strcmp(mpl->image, "Infinity") == 0) mpl->token = T_INFINITY; #endif else if (strcmp(mpl->image, "inter") == 0) mpl->token = T_INTER; else if (strcmp(mpl->image, "less") == 0) mpl->token = T_LESS; else if (strcmp(mpl->image, "mod") == 0) mpl->token = T_MOD; else if (strcmp(mpl->image, "not") == 0) mpl->token = T_NOT; else if (strcmp(mpl->image, "or") == 0) mpl->token = T_OR; else if (strcmp(mpl->image, "s") == 0 && mpl->c == '.') { mpl->token = T_SPTP; append_char(mpl); if (mpl->c != 't') sptp: { enter_context(mpl); error(mpl, "keyword s.t. incomplete"); } append_char(mpl); if (mpl->c != '.') goto sptp; append_char(mpl); } else if (strcmp(mpl->image, "symdiff") == 0) mpl->token = T_SYMDIFF; else if (strcmp(mpl->image, "then") == 0) mpl->token = T_THEN; else if (strcmp(mpl->image, "union") == 0) mpl->token = T_UNION; else if (strcmp(mpl->image, "within") == 0) mpl->token = T_WITHIN; } else if (!mpl->flag_d && isdigit(mpl->c)) { /* numeric literal */ mpl->token = T_NUMBER; /* scan integer part */ while (isdigit(mpl->c)) append_char(mpl); /* scan optional fractional part */ if (mpl->c == '.') { append_char(mpl); if (mpl->c == '.') { /* hmm, it is not the fractional part, it is dots that follow the integer part */ mpl->imlen--; mpl->image[mpl->imlen] = '\0'; mpl->f_dots = 1; goto conv; } frac: while (isdigit(mpl->c)) append_char(mpl); } /* scan optional decimal exponent */ if (mpl->c == 'e' || mpl->c == 'E') { append_char(mpl); if (mpl->c == '+' || mpl->c == '-') append_char(mpl); if (!isdigit(mpl->c)) { enter_context(mpl); error(mpl, "numeric literal %s incomplete", mpl->image); } while (isdigit(mpl->c)) append_char(mpl); } /* there must be no letter following the numeric literal */ if (isalpha(mpl->c) || mpl->c == '_') { enter_context(mpl); error(mpl, "symbol %s%c... should be enclosed in quotes", mpl->image, mpl->c); } conv: /* convert numeric literal to floating-point */ if (str2num(mpl->image, &mpl->value)) err: { enter_context(mpl); error(mpl, "cannot convert numeric literal %s to floating-p" "oint number", mpl->image); } } else if (mpl->c == '\'' || mpl->c == '"') { /* character string */ int quote = mpl->c; mpl->token = T_STRING; get_char(mpl); for (;;) { if (mpl->c == '\n' || mpl->c == EOF) { enter_context(mpl); error(mpl, "unexpected end of line; string literal incom" "plete"); } if (mpl->c == quote) { get_char(mpl); if (mpl->c != quote) break; } append_char(mpl); } } else if (!mpl->flag_d && mpl->c == '+') mpl->token = T_PLUS, append_char(mpl); else if (!mpl->flag_d && mpl->c == '-') mpl->token = T_MINUS, append_char(mpl); else if (mpl->c == '*') { mpl->token = T_ASTERISK, append_char(mpl); if (mpl->c == '*') mpl->token = T_POWER, append_char(mpl); } else if (mpl->c == '/') { mpl->token = T_SLASH, append_char(mpl); if (mpl->c == '*') { /* comment sequence */ get_char(mpl); for (;;) { if (mpl->c == EOF) { /* do not call enter_context at this point */ error(mpl, "unexpected end of file; comment sequence " "incomplete"); } else if (mpl->c == '*') { get_char(mpl); if (mpl->c == '/') break; } else get_char(mpl); } get_char(mpl); goto loop; } } else if (mpl->c == '^') mpl->token = T_POWER, append_char(mpl); else if (mpl->c == '<') { mpl->token = T_LT, append_char(mpl); if (mpl->c == '=') mpl->token = T_LE, append_char(mpl); else if (mpl->c == '>') mpl->token = T_NE, append_char(mpl); #if 1 /* 11/II-2008 */ else if (mpl->c == '-') mpl->token = T_INPUT, append_char(mpl); #endif } else if (mpl->c == '=') { mpl->token = T_EQ, append_char(mpl); if (mpl->c == '=') append_char(mpl); } else if (mpl->c == '>') { mpl->token = T_GT, append_char(mpl); if (mpl->c == '=') mpl->token = T_GE, append_char(mpl); #if 1 /* 14/VII-2006 */ else if (mpl->c == '>') mpl->token = T_APPEND, append_char(mpl); #endif } else if (mpl->c == '!') { mpl->token = T_NOT, append_char(mpl); if (mpl->c == '=') mpl->token = T_NE, append_char(mpl); } else if (mpl->c == '&') { mpl->token = T_CONCAT, append_char(mpl); if (mpl->c == '&') mpl->token = T_AND, append_char(mpl); } else if (mpl->c == '|') { mpl->token = T_BAR, append_char(mpl); if (mpl->c == '|') mpl->token = T_OR, append_char(mpl); } else if (!mpl->flag_d && mpl->c == '.') { mpl->token = T_POINT, append_char(mpl); if (mpl->f_dots) { /* dots; the first dot was read on the previous call to the scanner, so the current character is the second dot */ mpl->token = T_DOTS; mpl->imlen = 2; strcpy(mpl->image, ".."); mpl->f_dots = 0; } else if (mpl->c == '.') mpl->token = T_DOTS, append_char(mpl); else if (isdigit(mpl->c)) { /* numeric literal that begins with the decimal point */ mpl->token = T_NUMBER, append_char(mpl); goto frac; } } else if (mpl->c == ',') mpl->token = T_COMMA, append_char(mpl); else if (mpl->c == ':') { mpl->token = T_COLON, append_char(mpl); if (mpl->c == '=') mpl->token = T_ASSIGN, append_char(mpl); } else if (mpl->c == ';') mpl->token = T_SEMICOLON, append_char(mpl); else if (mpl->c == '(') mpl->token = T_LEFT, append_char(mpl); else if (mpl->c == ')') mpl->token = T_RIGHT, append_char(mpl); else if (mpl->c == '[') mpl->token = T_LBRACKET, append_char(mpl); else if (mpl->c == ']') mpl->token = T_RBRACKET, append_char(mpl); else if (mpl->c == '{') mpl->token = T_LBRACE, append_char(mpl); else if (mpl->c == '}') mpl->token = T_RBRACE, append_char(mpl); #if 1 /* 11/II-2008 */ else if (mpl->c == '~') mpl->token = T_TILDE, append_char(mpl); #endif else if (isalnum(mpl->c) || strchr("+-._", mpl->c) != NULL) { /* symbol */ xassert(mpl->flag_d); mpl->token = T_SYMBOL; while (isalnum(mpl->c) || strchr("+-._", mpl->c) != NULL) append_char(mpl); switch (str2num(mpl->image, &mpl->value)) { case 0: mpl->token = T_NUMBER; break; case 1: goto err; case 2: break; default: xassert(mpl != mpl); } } else { enter_context(mpl); error(mpl, "character %c not allowed", mpl->c); } /* enter the current token into the context queue */ enter_context(mpl); /* reset the flag, which may be set by indexing_expression() and is used by expression_list() */ mpl->flag_x = 0; done: return; } /*---------------------------------------------------------------------- -- unget_token - return current token back to input stream. -- -- This routine returns the current token back to the input stream, so -- the previously scanned token becomes the current one. */ void unget_token(MPL *mpl) { /* save the current token, which becomes the next one */ xassert(!mpl->f_scan); mpl->f_scan = 1; mpl->f_token = mpl->token; mpl->f_imlen = mpl->imlen; strcpy(mpl->f_image, mpl->image); mpl->f_value = mpl->value; /* restore the previous token, which becomes the current one */ mpl->token = mpl->b_token; mpl->imlen = mpl->b_imlen; strcpy(mpl->image, mpl->b_image); mpl->value = mpl->b_value; return; } /*---------------------------------------------------------------------- -- is_keyword - check if current token is given non-reserved keyword. -- -- If the current token is given (non-reserved) keyword, this routine -- returns non-zero. Otherwise zero is returned. */ int is_keyword(MPL *mpl, char *keyword) { return mpl->token == T_NAME && strcmp(mpl->image, keyword) == 0; } /*---------------------------------------------------------------------- -- is_reserved - check if current token is reserved keyword. -- -- If the current token is a reserved keyword, this routine returns -- non-zero. Otherwise zero is returned. */ int is_reserved(MPL *mpl) { return mpl->token == T_AND && mpl->image[0] == 'a' || mpl->token == T_BY || mpl->token == T_CROSS || mpl->token == T_DIFF || mpl->token == T_DIV || mpl->token == T_ELSE || mpl->token == T_IF || mpl->token == T_IN || mpl->token == T_INTER || mpl->token == T_LESS || mpl->token == T_MOD || mpl->token == T_NOT && mpl->image[0] == 'n' || mpl->token == T_OR && mpl->image[0] == 'o' || mpl->token == T_SYMDIFF || mpl->token == T_THEN || mpl->token == T_UNION || mpl->token == T_WITHIN; } /*---------------------------------------------------------------------- -- make_code - generate pseudo-code (basic routine). -- -- This routine generates specified pseudo-code. It is assumed that all -- other translator routines use this basic routine. */ CODE *make_code(MPL *mpl, int op, OPERANDS *arg, int type, int dim) { CODE *code; DOMAIN *domain; DOMAIN_BLOCK *block; ARG_LIST *e; /* generate pseudo-code */ code = alloc(CODE); code->op = op; code->vflag = 0; /* is inherited from operand(s) */ /* copy operands and also make them referring to the pseudo-code being generated, because the latter becomes the parent for all its operands */ memset(&code->arg, '?', sizeof(OPERANDS)); switch (op) { case O_NUMBER: code->arg.num = arg->num; break; case O_STRING: code->arg.str = arg->str; break; case O_INDEX: code->arg.index.slot = arg->index.slot; code->arg.index.next = arg->index.next; break; case O_MEMNUM: case O_MEMSYM: for (e = arg->par.list; e != NULL; e = e->next) { xassert(e->x != NULL); xassert(e->x->up == NULL); e->x->up = code; code->vflag |= e->x->vflag; } code->arg.par.par = arg->par.par; code->arg.par.list = arg->par.list; break; case O_MEMSET: for (e = arg->set.list; e != NULL; e = e->next) { xassert(e->x != NULL); xassert(e->x->up == NULL); e->x->up = code; code->vflag |= e->x->vflag; } code->arg.set.set = arg->set.set; code->arg.set.list = arg->set.list; break; case O_MEMVAR: for (e = arg->var.list; e != NULL; e = e->next) { xassert(e->x != NULL); xassert(e->x->up == NULL); e->x->up = code; code->vflag |= e->x->vflag; } code->arg.var.var = arg->var.var; code->arg.var.list = arg->var.list; #if 1 /* 15/V-2010 */ code->arg.var.suff = arg->var.suff; #endif break; #if 1 /* 15/V-2010 */ case O_MEMCON: for (e = arg->con.list; e != NULL; e = e->next) { xassert(e->x != NULL); xassert(e->x->up == NULL); e->x->up = code; code->vflag |= e->x->vflag; } code->arg.con.con = arg->con.con; code->arg.con.list = arg->con.list; code->arg.con.suff = arg->con.suff; break; #endif case O_TUPLE: case O_MAKE: for (e = arg->list; e != NULL; e = e->next) { xassert(e->x != NULL); xassert(e->x->up == NULL); e->x->up = code; code->vflag |= e->x->vflag; } code->arg.list = arg->list; break; case O_SLICE: xassert(arg->slice != NULL); code->arg.slice = arg->slice; break; case O_IRAND224: case O_UNIFORM01: case O_NORMAL01: case O_GMTIME: code->vflag = 1; break; case O_CVTNUM: case O_CVTSYM: case O_CVTLOG: case O_CVTTUP: case O_CVTLFM: case O_PLUS: case O_MINUS: case O_NOT: case O_ABS: case O_CEIL: case O_FLOOR: case O_EXP: case O_LOG: case O_LOG10: case O_SQRT: case O_SIN: case O_COS: case O_ATAN: case O_ROUND: case O_TRUNC: case O_CARD: case O_LENGTH: /* unary operation */ xassert(arg->arg.x != NULL); xassert(arg->arg.x->up == NULL); arg->arg.x->up = code; code->vflag |= arg->arg.x->vflag; code->arg.arg.x = arg->arg.x; break; case O_ADD: case O_SUB: case O_LESS: case O_MUL: case O_DIV: case O_IDIV: case O_MOD: case O_POWER: case O_ATAN2: case O_ROUND2: case O_TRUNC2: case O_UNIFORM: if (op == O_UNIFORM) code->vflag = 1; case O_NORMAL: if (op == O_NORMAL) code->vflag = 1; case O_CONCAT: case O_LT: case O_LE: case O_EQ: case O_GE: case O_GT: case O_NE: case O_AND: case O_OR: case O_UNION: case O_DIFF: case O_SYMDIFF: case O_INTER: case O_CROSS: case O_IN: case O_NOTIN: case O_WITHIN: case O_NOTWITHIN: case O_SUBSTR: case O_STR2TIME: case O_TIME2STR: /* binary operation */ xassert(arg->arg.x != NULL); xassert(arg->arg.x->up == NULL); arg->arg.x->up = code; code->vflag |= arg->arg.x->vflag; xassert(arg->arg.y != NULL); xassert(arg->arg.y->up == NULL); arg->arg.y->up = code; code->vflag |= arg->arg.y->vflag; code->arg.arg.x = arg->arg.x; code->arg.arg.y = arg->arg.y; break; case O_DOTS: case O_FORK: case O_SUBSTR3: /* ternary operation */ xassert(arg->arg.x != NULL); xassert(arg->arg.x->up == NULL); arg->arg.x->up = code; code->vflag |= arg->arg.x->vflag; xassert(arg->arg.y != NULL); xassert(arg->arg.y->up == NULL); arg->arg.y->up = code; code->vflag |= arg->arg.y->vflag; if (arg->arg.z != NULL) { xassert(arg->arg.z->up == NULL); arg->arg.z->up = code; code->vflag |= arg->arg.z->vflag; } code->arg.arg.x = arg->arg.x; code->arg.arg.y = arg->arg.y; code->arg.arg.z = arg->arg.z; break; case O_MIN: case O_MAX: /* n-ary operation */ for (e = arg->list; e != NULL; e = e->next) { xassert(e->x != NULL); xassert(e->x->up == NULL); e->x->up = code; code->vflag |= e->x->vflag; } code->arg.list = arg->list; break; case O_SUM: case O_PROD: case O_MINIMUM: case O_MAXIMUM: case O_FORALL: case O_EXISTS: case O_SETOF: case O_BUILD: /* iterated operation */ domain = arg->loop.domain; xassert(domain != NULL); if (domain->code != NULL) { xassert(domain->code->up == NULL); domain->code->up = code; code->vflag |= domain->code->vflag; } for (block = domain->list; block != NULL; block = block->next) { xassert(block->code != NULL); xassert(block->code->up == NULL); block->code->up = code; code->vflag |= block->code->vflag; } if (arg->loop.x != NULL) { xassert(arg->loop.x->up == NULL); arg->loop.x->up = code; code->vflag |= arg->loop.x->vflag; } code->arg.loop.domain = arg->loop.domain; code->arg.loop.x = arg->loop.x; break; default: xassert(op != op); } /* set other attributes of the pseudo-code */ code->type = type; code->dim = dim; code->up = NULL; code->valid = 0; memset(&code->value, '?', sizeof(VALUE)); return code; } /*---------------------------------------------------------------------- -- make_unary - generate pseudo-code for unary operation. -- -- This routine generates pseudo-code for unary operation. */ CODE *make_unary(MPL *mpl, int op, CODE *x, int type, int dim) { CODE *code; OPERANDS arg; xassert(x != NULL); arg.arg.x = x; code = make_code(mpl, op, &arg, type, dim); return code; } /*---------------------------------------------------------------------- -- make_binary - generate pseudo-code for binary operation. -- -- This routine generates pseudo-code for binary operation. */ CODE *make_binary(MPL *mpl, int op, CODE *x, CODE *y, int type, int dim) { CODE *code; OPERANDS arg; xassert(x != NULL); xassert(y != NULL); arg.arg.x = x; arg.arg.y = y; code = make_code(mpl, op, &arg, type, dim); return code; } /*---------------------------------------------------------------------- -- make_ternary - generate pseudo-code for ternary operation. -- -- This routine generates pseudo-code for ternary operation. */ CODE *make_ternary(MPL *mpl, int op, CODE *x, CODE *y, CODE *z, int type, int dim) { CODE *code; OPERANDS arg; xassert(x != NULL); xassert(y != NULL); /* third operand can be NULL */ arg.arg.x = x; arg.arg.y = y; arg.arg.z = z; code = make_code(mpl, op, &arg, type, dim); return code; } /*---------------------------------------------------------------------- -- numeric_literal - parse reference to numeric literal. -- -- This routine parses primary expression using the syntax: -- -- ::= */ CODE *numeric_literal(MPL *mpl) { CODE *code; OPERANDS arg; xassert(mpl->token == T_NUMBER); arg.num = mpl->value; code = make_code(mpl, O_NUMBER, &arg, A_NUMERIC, 0); get_token(mpl /* */); return code; } /*---------------------------------------------------------------------- -- string_literal - parse reference to string literal. -- -- This routine parses primary expression using the syntax: -- -- ::= */ CODE *string_literal(MPL *mpl) { CODE *code; OPERANDS arg; xassert(mpl->token == T_STRING); arg.str = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(arg.str, mpl->image); code = make_code(mpl, O_STRING, &arg, A_SYMBOLIC, 0); get_token(mpl /* */); return code; } /*---------------------------------------------------------------------- -- create_arg_list - create empty operands list. -- -- This routine creates operands list, which is initially empty. */ ARG_LIST *create_arg_list(MPL *mpl) { ARG_LIST *list; xassert(mpl == mpl); list = NULL; return list; } /*---------------------------------------------------------------------- -- expand_arg_list - append operand to operands list. -- -- This routine appends new operand to specified operands list. */ ARG_LIST *expand_arg_list(MPL *mpl, ARG_LIST *list, CODE *x) { ARG_LIST *tail, *temp; xassert(x != NULL); /* create new operands list entry */ tail = alloc(ARG_LIST); tail->x = x; tail->next = NULL; /* and append it to the operands list */ if (list == NULL) list = tail; else { for (temp = list; temp->next != NULL; temp = temp->next); temp->next = tail; } return list; } /*---------------------------------------------------------------------- -- arg_list_len - determine length of operands list. -- -- This routine returns the number of operands in operands list. */ int arg_list_len(MPL *mpl, ARG_LIST *list) { ARG_LIST *temp; int len; xassert(mpl == mpl); len = 0; for (temp = list; temp != NULL; temp = temp->next) len++; return len; } /*---------------------------------------------------------------------- -- subscript_list - parse subscript list. -- -- This routine parses subscript list using the syntax: -- -- ::= -- ::= , -- ::= */ ARG_LIST *subscript_list(MPL *mpl) { ARG_LIST *list; CODE *x; list = create_arg_list(mpl); for (;;) { /* parse subscript expression */ x = expression_5(mpl); /* convert it to symbolic type, if necessary */ if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); /* check that now the expression is of symbolic type */ if (x->type != A_SYMBOLIC) error(mpl, "subscript expression has invalid type"); xassert(x->dim == 0); /* and append it to the subscript list */ list = expand_arg_list(mpl, list, x); /* check a token that follows the subscript expression */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_RBRACKET) break; else error(mpl, "syntax error in subscript list"); } return list; } #if 1 /* 15/V-2010 */ /*---------------------------------------------------------------------- -- object_reference - parse reference to named object. -- -- This routine parses primary expression using the syntax: -- -- ::= -- ::= -- ::= [ ] -- ::= -- ::= [ ] -- ::= -- ::= [ ] -- -- ::= -- ::= [ ] -- -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= | .lb | .ub | .status | .val | .dual */ CODE *object_reference(MPL *mpl) { AVLNODE *node; DOMAIN_SLOT *slot; SET *set; PARAMETER *par; VARIABLE *var; CONSTRAINT *con; ARG_LIST *list; OPERANDS arg; CODE *code; char *name; int dim, suff; /* find the object in the symbolic name table */ xassert(mpl->token == T_NAME); node = avl_find_node(mpl->tree, mpl->image); if (node == NULL) error(mpl, "%s not defined", mpl->image); /* check the object type and obtain its dimension */ switch (avl_get_node_type(node)) { case A_INDEX: /* dummy index */ slot = (DOMAIN_SLOT *)avl_get_node_link(node); name = slot->name; dim = 0; break; case A_SET: /* model set */ set = (SET *)avl_get_node_link(node); name = set->name; dim = set->dim; /* if a set object is referenced in its own declaration and the dimen attribute is not specified yet, use dimen 1 by default */ if (set->dimen == 0) set->dimen = 1; break; case A_PARAMETER: /* model parameter */ par = (PARAMETER *)avl_get_node_link(node); name = par->name; dim = par->dim; break; case A_VARIABLE: /* model variable */ var = (VARIABLE *)avl_get_node_link(node); name = var->name; dim = var->dim; break; case A_CONSTRAINT: /* model constraint or objective */ con = (CONSTRAINT *)avl_get_node_link(node); name = con->name; dim = con->dim; break; default: xassert(node != node); } get_token(mpl /* */); /* parse optional subscript list */ if (mpl->token == T_LBRACKET) { /* subscript list is specified */ if (dim == 0) error(mpl, "%s cannot be subscripted", name); get_token(mpl /* [ */); list = subscript_list(mpl); if (dim != arg_list_len(mpl, list)) error(mpl, "%s must have %d subscript%s rather than %d", name, dim, dim == 1 ? "" : "s", arg_list_len(mpl, list)); xassert(mpl->token == T_RBRACKET); get_token(mpl /* ] */); } else { /* subscript list is not specified */ if (dim != 0) error(mpl, "%s must be subscripted", name); list = create_arg_list(mpl); } /* parse optional suffix */ if (!mpl->flag_s && avl_get_node_type(node) == A_VARIABLE) suff = DOT_NONE; else suff = DOT_VAL; if (mpl->token == T_POINT) { get_token(mpl /* . */); if (mpl->token != T_NAME) error(mpl, "invalid use of period"); if (!(avl_get_node_type(node) == A_VARIABLE || avl_get_node_type(node) == A_CONSTRAINT)) error(mpl, "%s cannot have a suffix", name); if (strcmp(mpl->image, "lb") == 0) suff = DOT_LB; else if (strcmp(mpl->image, "ub") == 0) suff = DOT_UB; else if (strcmp(mpl->image, "status") == 0) suff = DOT_STATUS; else if (strcmp(mpl->image, "val") == 0) suff = DOT_VAL; else if (strcmp(mpl->image, "dual") == 0) suff = DOT_DUAL; else error(mpl, "suffix .%s invalid", mpl->image); get_token(mpl /* suffix */); } /* generate pseudo-code to take value of the object */ switch (avl_get_node_type(node)) { case A_INDEX: arg.index.slot = slot; arg.index.next = slot->list; code = make_code(mpl, O_INDEX, &arg, A_SYMBOLIC, 0); slot->list = code; break; case A_SET: arg.set.set = set; arg.set.list = list; code = make_code(mpl, O_MEMSET, &arg, A_ELEMSET, set->dimen); break; case A_PARAMETER: arg.par.par = par; arg.par.list = list; if (par->type == A_SYMBOLIC) code = make_code(mpl, O_MEMSYM, &arg, A_SYMBOLIC, 0); else code = make_code(mpl, O_MEMNUM, &arg, A_NUMERIC, 0); break; case A_VARIABLE: if (!mpl->flag_s && (suff == DOT_STATUS || suff == DOT_VAL || suff == DOT_DUAL)) error(mpl, "invalid reference to status, primal value, o" "r dual value of variable %s above solve statement", var->name); arg.var.var = var; arg.var.list = list; arg.var.suff = suff; code = make_code(mpl, O_MEMVAR, &arg, suff == DOT_NONE ? A_FORMULA : A_NUMERIC, 0); break; case A_CONSTRAINT: if (!mpl->flag_s && (suff == DOT_STATUS || suff == DOT_VAL || suff == DOT_DUAL)) error(mpl, "invalid reference to status, primal value, o" "r dual value of %s %s above solve statement", con->type == A_CONSTRAINT ? "constraint" : "objective" , con->name); arg.con.con = con; arg.con.list = list; arg.con.suff = suff; code = make_code(mpl, O_MEMCON, &arg, A_NUMERIC, 0); break; default: xassert(node != node); } return code; } #endif /*---------------------------------------------------------------------- -- numeric_argument - parse argument passed to built-in function. -- -- This routine parses an argument passed to numeric built-in function -- using the syntax: -- -- ::= */ CODE *numeric_argument(MPL *mpl, char *func) { CODE *x; x = expression_5(mpl); /* convert the argument to numeric type, if necessary */ if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); /* check that now the argument is of numeric type */ if (x->type != A_NUMERIC) error(mpl, "argument for %s has invalid type", func); xassert(x->dim == 0); return x; } #if 1 /* 15/VII-2006 */ CODE *symbolic_argument(MPL *mpl, char *func) { CODE *x; x = expression_5(mpl); /* convert the argument to symbolic type, if necessary */ if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); /* check that now the argument is of symbolic type */ if (x->type != A_SYMBOLIC) error(mpl, "argument for %s has invalid type", func); xassert(x->dim == 0); return x; } #endif #if 1 /* 15/VII-2006 */ CODE *elemset_argument(MPL *mpl, char *func) { CODE *x; x = expression_9(mpl); if (x->type != A_ELEMSET) error(mpl, "argument for %s has invalid type", func); xassert(x->dim > 0); return x; } #endif /*---------------------------------------------------------------------- -- function_reference - parse reference to built-in function. -- -- This routine parses primary expression using the syntax: -- -- ::= abs ( ) -- ::= ceil ( ) -- ::= floor ( ) -- ::= exp ( ) -- ::= log ( ) -- ::= log10 ( ) -- ::= max ( ) -- ::= min ( ) -- ::= sqrt ( ) -- ::= sin ( ) -- ::= cos ( ) -- ::= atan ( ) -- ::= atan2 ( , ) -- ::= round ( ) -- ::= round ( , ) -- ::= trunc ( ) -- ::= trunc ( , ) -- ::= Irand224 ( ) -- ::= Uniform01 ( ) -- ::= Uniform ( , ) -- ::= Normal01 ( ) -- ::= Normal ( , ) -- ::= card ( ) -- ::= length ( ) -- ::= substr ( , ) -- ::= substr ( , , ) -- ::= str2time ( , ) -- ::= time2str ( , ) -- ::= gmtime ( ) -- ::= -- ::= , */ CODE *function_reference(MPL *mpl) { CODE *code; OPERANDS arg; int op; char func[15+1]; /* determine operation code */ xassert(mpl->token == T_NAME); if (strcmp(mpl->image, "abs") == 0) op = O_ABS; else if (strcmp(mpl->image, "ceil") == 0) op = O_CEIL; else if (strcmp(mpl->image, "floor") == 0) op = O_FLOOR; else if (strcmp(mpl->image, "exp") == 0) op = O_EXP; else if (strcmp(mpl->image, "log") == 0) op = O_LOG; else if (strcmp(mpl->image, "log10") == 0) op = O_LOG10; else if (strcmp(mpl->image, "sqrt") == 0) op = O_SQRT; else if (strcmp(mpl->image, "sin") == 0) op = O_SIN; else if (strcmp(mpl->image, "cos") == 0) op = O_COS; else if (strcmp(mpl->image, "atan") == 0) op = O_ATAN; else if (strcmp(mpl->image, "min") == 0) op = O_MIN; else if (strcmp(mpl->image, "max") == 0) op = O_MAX; else if (strcmp(mpl->image, "round") == 0) op = O_ROUND; else if (strcmp(mpl->image, "trunc") == 0) op = O_TRUNC; else if (strcmp(mpl->image, "Irand224") == 0) op = O_IRAND224; else if (strcmp(mpl->image, "Uniform01") == 0) op = O_UNIFORM01; else if (strcmp(mpl->image, "Uniform") == 0) op = O_UNIFORM; else if (strcmp(mpl->image, "Normal01") == 0) op = O_NORMAL01; else if (strcmp(mpl->image, "Normal") == 0) op = O_NORMAL; else if (strcmp(mpl->image, "card") == 0) op = O_CARD; else if (strcmp(mpl->image, "length") == 0) op = O_LENGTH; else if (strcmp(mpl->image, "substr") == 0) op = O_SUBSTR; else if (strcmp(mpl->image, "str2time") == 0) op = O_STR2TIME; else if (strcmp(mpl->image, "time2str") == 0) op = O_TIME2STR; else if (strcmp(mpl->image, "gmtime") == 0) op = O_GMTIME; else error(mpl, "function %s unknown", mpl->image); /* save symbolic name of the function */ strcpy(func, mpl->image); xassert(strlen(func) < sizeof(func)); get_token(mpl /* */); /* check the left parenthesis that follows the function name */ xassert(mpl->token == T_LEFT); get_token(mpl /* ( */); /* parse argument list */ if (op == O_MIN || op == O_MAX) { /* min and max allow arbitrary number of arguments */ arg.list = create_arg_list(mpl); /* parse argument list */ for (;;) { /* parse argument and append it to the operands list */ arg.list = expand_arg_list(mpl, arg.list, numeric_argument(mpl, func)); /* check a token that follows the argument */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_RIGHT) break; else error(mpl, "syntax error in argument list for %s", func); } } else if (op == O_IRAND224 || op == O_UNIFORM01 || op == O_NORMAL01 || op == O_GMTIME) { /* Irand224, Uniform01, Normal01, gmtime need no arguments */ if (mpl->token != T_RIGHT) error(mpl, "%s needs no arguments", func); } else if (op == O_UNIFORM || op == O_NORMAL) { /* Uniform and Normal need two arguments */ /* parse the first argument */ arg.arg.x = numeric_argument(mpl, func); /* check a token that follows the first argument */ if (mpl->token == T_COMMA) ; else if (mpl->token == T_RIGHT) error(mpl, "%s needs two arguments", func); else error(mpl, "syntax error in argument for %s", func); get_token(mpl /* , */); /* parse the second argument */ arg.arg.y = numeric_argument(mpl, func); /* check a token that follows the second argument */ if (mpl->token == T_COMMA) error(mpl, "%s needs two argument", func); else if (mpl->token == T_RIGHT) ; else error(mpl, "syntax error in argument for %s", func); } else if (op == O_ATAN || op == O_ROUND || op == O_TRUNC) { /* atan, round, and trunc need one or two arguments */ /* parse the first argument */ arg.arg.x = numeric_argument(mpl, func); /* parse the second argument, if specified */ if (mpl->token == T_COMMA) { switch (op) { case O_ATAN: op = O_ATAN2; break; case O_ROUND: op = O_ROUND2; break; case O_TRUNC: op = O_TRUNC2; break; default: xassert(op != op); } get_token(mpl /* , */); arg.arg.y = numeric_argument(mpl, func); } /* check a token that follows the last argument */ if (mpl->token == T_COMMA) error(mpl, "%s needs one or two arguments", func); else if (mpl->token == T_RIGHT) ; else error(mpl, "syntax error in argument for %s", func); } else if (op == O_SUBSTR) { /* substr needs two or three arguments */ /* parse the first argument */ arg.arg.x = symbolic_argument(mpl, func); /* check a token that follows the first argument */ if (mpl->token == T_COMMA) ; else if (mpl->token == T_RIGHT) error(mpl, "%s needs two or three arguments", func); else error(mpl, "syntax error in argument for %s", func); get_token(mpl /* , */); /* parse the second argument */ arg.arg.y = numeric_argument(mpl, func); /* parse the third argument, if specified */ if (mpl->token == T_COMMA) { op = O_SUBSTR3; get_token(mpl /* , */); arg.arg.z = numeric_argument(mpl, func); } /* check a token that follows the last argument */ if (mpl->token == T_COMMA) error(mpl, "%s needs two or three arguments", func); else if (mpl->token == T_RIGHT) ; else error(mpl, "syntax error in argument for %s", func); } else if (op == O_STR2TIME) { /* str2time needs two arguments, both symbolic */ /* parse the first argument */ arg.arg.x = symbolic_argument(mpl, func); /* check a token that follows the first argument */ if (mpl->token == T_COMMA) ; else if (mpl->token == T_RIGHT) error(mpl, "%s needs two arguments", func); else error(mpl, "syntax error in argument for %s", func); get_token(mpl /* , */); /* parse the second argument */ arg.arg.y = symbolic_argument(mpl, func); /* check a token that follows the second argument */ if (mpl->token == T_COMMA) error(mpl, "%s needs two argument", func); else if (mpl->token == T_RIGHT) ; else error(mpl, "syntax error in argument for %s", func); } else if (op == O_TIME2STR) { /* time2str needs two arguments, numeric and symbolic */ /* parse the first argument */ arg.arg.x = numeric_argument(mpl, func); /* check a token that follows the first argument */ if (mpl->token == T_COMMA) ; else if (mpl->token == T_RIGHT) error(mpl, "%s needs two arguments", func); else error(mpl, "syntax error in argument for %s", func); get_token(mpl /* , */); /* parse the second argument */ arg.arg.y = symbolic_argument(mpl, func); /* check a token that follows the second argument */ if (mpl->token == T_COMMA) error(mpl, "%s needs two argument", func); else if (mpl->token == T_RIGHT) ; else error(mpl, "syntax error in argument for %s", func); } else { /* other functions need one argument */ if (op == O_CARD) arg.arg.x = elemset_argument(mpl, func); else if (op == O_LENGTH) arg.arg.x = symbolic_argument(mpl, func); else arg.arg.x = numeric_argument(mpl, func); /* check a token that follows the argument */ if (mpl->token == T_COMMA) error(mpl, "%s needs one argument", func); else if (mpl->token == T_RIGHT) ; else error(mpl, "syntax error in argument for %s", func); } /* make pseudo-code to call the built-in function */ if (op == O_SUBSTR || op == O_SUBSTR3 || op == O_TIME2STR) code = make_code(mpl, op, &arg, A_SYMBOLIC, 0); else code = make_code(mpl, op, &arg, A_NUMERIC, 0); /* the reference ends with the right parenthesis */ xassert(mpl->token == T_RIGHT); get_token(mpl /* ) */); return code; } /*---------------------------------------------------------------------- -- create_domain - create empty domain. -- -- This routine creates empty domain, which is initially empty, i.e. -- has no domain blocks. */ DOMAIN *create_domain(MPL *mpl) { DOMAIN *domain; domain = alloc(DOMAIN); domain->list = NULL; domain->code = NULL; return domain; } /*---------------------------------------------------------------------- -- create_block - create empty domain block. -- -- This routine creates empty domain block, which is initially empty, -- i.e. has no domain slots. */ DOMAIN_BLOCK *create_block(MPL *mpl) { DOMAIN_BLOCK *block; block = alloc(DOMAIN_BLOCK); block->list = NULL; block->code = NULL; block->backup = NULL; block->next = NULL; return block; } /*---------------------------------------------------------------------- -- append_block - append domain block to specified domain. -- -- This routine adds given domain block to the end of the block list of -- specified domain. */ void append_block(MPL *mpl, DOMAIN *domain, DOMAIN_BLOCK *block) { DOMAIN_BLOCK *temp; xassert(mpl == mpl); xassert(domain != NULL); xassert(block != NULL); xassert(block->next == NULL); if (domain->list == NULL) domain->list = block; else { for (temp = domain->list; temp->next != NULL; temp = temp->next); temp->next = block; } return; } /*---------------------------------------------------------------------- -- append_slot - create and append new slot to domain block. -- -- This routine creates new domain slot and adds it to the end of slot -- list of specified domain block. -- -- The parameter name is symbolic name of the dummy index associated -- with the slot (the character string must be allocated). NULL means -- the dummy index is not explicitly specified. -- -- The parameter code is pseudo-code for computing symbolic value, at -- which the dummy index is bounded. NULL means the dummy index is free -- in the domain scope. */ DOMAIN_SLOT *append_slot(MPL *mpl, DOMAIN_BLOCK *block, char *name, CODE *code) { DOMAIN_SLOT *slot, *temp; xassert(block != NULL); slot = alloc(DOMAIN_SLOT); slot->name = name; slot->code = code; slot->value = NULL; slot->list = NULL; slot->next = NULL; if (block->list == NULL) block->list = slot; else { for (temp = block->list; temp->next != NULL; temp = temp->next); temp->next = slot; } return slot; } /*---------------------------------------------------------------------- -- expression_list - parse expression list. -- -- This routine parses a list of one or more expressions enclosed into -- the parentheses using the syntax: -- -- ::= ( ) -- ::= -- ::= , -- -- Note that this construction may have three different meanings: -- -- 1. If consists of only one expression, is a parenthesized expression, which may be of any -- valid type (not necessarily 1-tuple). -- -- 2. If consists of several expressions separated by -- commae, where no expression is undeclared symbolic name, is a n-tuple. -- -- 3. If consists of several expressions separated by -- commae, where at least one expression is undeclared symbolic name -- (that denotes a dummy index), is a slice and -- can be only used as constituent of indexing expression. */ #define max_dim 20 /* maximal number of components allowed within parentheses */ CODE *expression_list(MPL *mpl) { CODE *code; OPERANDS arg; struct { char *name; CODE *code; } list[1+max_dim]; int flag_x, next_token, dim, j, slice = 0; xassert(mpl->token == T_LEFT); /* the flag, which allows recognizing undeclared symbolic names as dummy indices, will be automatically reset by get_token(), so save it before scanning the next token */ flag_x = mpl->flag_x; get_token(mpl /* ( */); /* parse */ for (dim = 1; ; dim++) { if (dim > max_dim) error(mpl, "too many components within parentheses"); /* current component of can be either dummy index or expression */ if (mpl->token == T_NAME) { /* symbolic name is recognized as dummy index only if: the flag, which allows that, is set, and the name is followed by comma or right parenthesis, and the name is undeclared */ get_token(mpl /* */); next_token = mpl->token; unget_token(mpl); if (!(flag_x && (next_token == T_COMMA || next_token == T_RIGHT) && avl_find_node(mpl->tree, mpl->image) == NULL)) { /* this is not dummy index */ goto expr; } /* all dummy indices within the same slice must have unique symbolic names */ for (j = 1; j < dim; j++) { if (list[j].name != NULL && strcmp(list[j].name, mpl->image) == 0) error(mpl, "duplicate dummy index %s not allowed", mpl->image); } /* current component of is dummy index */ list[dim].name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(list[dim].name, mpl->image); list[dim].code = NULL; get_token(mpl /* */); /* is a slice, because at least one dummy index has appeared */ slice = 1; /* note that the context ( ) is not allowed, i.e. in this case is considered as a parenthesized expression */ if (dim == 1 && mpl->token == T_RIGHT) error(mpl, "%s not defined", list[dim].name); } else expr: { /* current component of is expression */ code = expression_13(mpl); /* if the current expression is followed by comma or it is not the very first expression, entire is n-tuple or slice, in which case the current expression should be converted to symbolic type, if necessary */ if (mpl->token == T_COMMA || dim > 1) { if (code->type == A_NUMERIC) code = make_unary(mpl, O_CVTSYM, code, A_SYMBOLIC, 0); /* now the expression must be of symbolic type */ if (code->type != A_SYMBOLIC) error(mpl, "component expression has invalid type"); xassert(code->dim == 0); } list[dim].name = NULL; list[dim].code = code; } /* check a token that follows the current component */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_RIGHT) break; else error(mpl, "right parenthesis missing where expected"); } /* generate pseudo-code for */ if (dim == 1 && !slice) { /* is a parenthesized expression */ code = list[1].code; } else if (!slice) { /* is a n-tuple */ arg.list = create_arg_list(mpl); for (j = 1; j <= dim; j++) arg.list = expand_arg_list(mpl, arg.list, list[j].code); code = make_code(mpl, O_TUPLE, &arg, A_TUPLE, dim); } else { /* is a slice */ arg.slice = create_block(mpl); for (j = 1; j <= dim; j++) append_slot(mpl, arg.slice, list[j].name, list[j].code); /* note that actually pseudo-codes with op = O_SLICE are never evaluated */ code = make_code(mpl, O_SLICE, &arg, A_TUPLE, dim); } get_token(mpl /* ) */); /* if is a slice, there must be the keyword 'in', which follows the right parenthesis */ if (slice && mpl->token != T_IN) error(mpl, "keyword in missing where expected"); /* if the slice flag is set and there is the keyword 'in', which follows , the latter must be a slice */ if (flag_x && mpl->token == T_IN && !slice) { if (dim == 1) error(mpl, "syntax error in indexing expression"); else error(mpl, "0-ary slice not allowed"); } return code; } /*---------------------------------------------------------------------- -- literal set - parse literal set. -- -- This routine parses literal set using the syntax: -- -- ::= { } -- ::= -- ::= , -- ::= -- -- It is assumed that the left curly brace and the very first member -- expression that follows it are already parsed. The right curly brace -- remains unscanned on exit. */ CODE *literal_set(MPL *mpl, CODE *code) { OPERANDS arg; int j; xassert(code != NULL); arg.list = create_arg_list(mpl); /* parse */ for (j = 1; ; j++) { /* all member expressions must be n-tuples; so, if the current expression is not n-tuple, convert it to 1-tuple */ if (code->type == A_NUMERIC) code = make_unary(mpl, O_CVTSYM, code, A_SYMBOLIC, 0); if (code->type == A_SYMBOLIC) code = make_unary(mpl, O_CVTTUP, code, A_TUPLE, 1); /* now the expression must be n-tuple */ if (code->type != A_TUPLE) error(mpl, "member expression has invalid type"); /* all member expressions must have identical dimension */ if (arg.list != NULL && arg.list->x->dim != code->dim) error(mpl, "member %d has %d component%s while member %d ha" "s %d component%s", j-1, arg.list->x->dim, arg.list->x->dim == 1 ? "" : "s", j, code->dim, code->dim == 1 ? "" : "s"); /* append the current expression to the member list */ arg.list = expand_arg_list(mpl, arg.list, code); /* check a token that follows the current expression */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_RBRACE) break; else error(mpl, "syntax error in literal set"); /* parse the next expression that follows the comma */ code = expression_5(mpl); } /* generate pseudo-code for */ code = make_code(mpl, O_MAKE, &arg, A_ELEMSET, arg.list->x->dim); return code; } /*---------------------------------------------------------------------- -- indexing_expression - parse indexing expression. -- -- This routine parses indexing expression using the syntax: -- -- ::= -- ::= { } -- ::= { : } -- ::= -- ::= , -- ::= -- ::= in -- ::= in -- ::= -- ::= ( ) -- ::= -- ::= -- -- This routine creates domain for , where each -- domain block corresponds to , and each domain slot -- corresponds to individual indexing position. */ DOMAIN *indexing_expression(MPL *mpl) { DOMAIN *domain; DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; CODE *code; xassert(mpl->token == T_LBRACE); get_token(mpl /* { */); if (mpl->token == T_RBRACE) error(mpl, "empty indexing expression not allowed"); /* create domain to be constructed */ domain = create_domain(mpl); /* parse either or that follows the left brace */ for (;;) { /* domain block for is not created yet */ block = NULL; /* pseudo-code for is not generated yet */ code = NULL; /* check a token, which begins with */ if (mpl->token == T_NAME) { /* it is a symbolic name */ int next_token; char *name; /* symbolic name is recognized as dummy index only if it is followed by the keyword 'in' and not declared */ get_token(mpl /* */); next_token = mpl->token; unget_token(mpl); if (!(next_token == T_IN && avl_find_node(mpl->tree, mpl->image) == NULL)) { /* this is not dummy index; the symbolic name begins an expression, which is either or the very first in */ goto expr; } /* create domain block with one slot, which is assigned the dummy index */ block = create_block(mpl); name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(name, mpl->image); append_slot(mpl, block, name, NULL); get_token(mpl /* */); /* the keyword 'in' is already checked above */ xassert(mpl->token == T_IN); get_token(mpl /* in */); /* that follows the keyword 'in' will be parsed below */ } else if (mpl->token == T_LEFT) { /* it is the left parenthesis; parse expression that begins with this parenthesis (the flag is set in order to allow recognizing slices; see the routine expression_list) */ mpl->flag_x = 1; code = expression_9(mpl); if (code->op != O_SLICE) { /* this is either or the very first in */ goto expr; } /* this is a slice; besides the corresponding domain block is already created by expression_list() */ block = code->arg.slice; code = NULL; /* is not parsed yet */ /* the keyword 'in' following the slice is already checked by expression_list() */ xassert(mpl->token == T_IN); get_token(mpl /* in */); /* that follows the keyword 'in' will be parsed below */ } expr: /* parse expression that follows either the keyword 'in' (in which case it can be as well as the very first in ); note that this expression can be already parsed above */ if (code == NULL) code = expression_9(mpl); /* check the type of the expression just parsed */ if (code->type != A_ELEMSET) { /* it is not and therefore it can only be the very first in ; however, then there must be no dummy index neither slice between the left brace and this expression */ if (block != NULL) error(mpl, "domain expression has invalid type"); /* parse the rest part of and make this set be , i.e. the construction {a, b, c} is parsed as it were written as {A}, where A = {a, b, c} is a temporary elemental set */ code = literal_set(mpl, code); } /* now pseudo-code for has been built */ xassert(code != NULL); xassert(code->type == A_ELEMSET); xassert(code->dim > 0); /* if domain block for the current is still not created, create it for fake slice of the same dimension as */ if (block == NULL) { int j; block = create_block(mpl); for (j = 1; j <= code->dim; j++) append_slot(mpl, block, NULL, NULL); } /* number of indexing positions in must be the same as dimension of n-tuples in basic set */ { int dim = 0; for (slot = block->list; slot != NULL; slot = slot->next) dim++; if (dim != code->dim) error(mpl,"%d %s specified for set of dimension %d", dim, dim == 1 ? "index" : "indices", code->dim); } /* store pseudo-code for in the domain block */ xassert(block->code == NULL); block->code = code; /* and append the domain block to the domain */ append_block(mpl, domain, block); /* the current has been completely parsed; include all its dummy indices into the symbolic name table to make them available for referencing from expressions; implicit declarations of dummy indices remain valid while the corresponding domain scope is valid */ for (slot = block->list; slot != NULL; slot = slot->next) if (slot->name != NULL) { AVLNODE *node; xassert(avl_find_node(mpl->tree, slot->name) == NULL); node = avl_insert_node(mpl->tree, slot->name); avl_set_node_type(node, A_INDEX); avl_set_node_link(node, (void *)slot); } /* check a token that follows */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_COLON || mpl->token == T_RBRACE) break; else error(mpl, "syntax error in indexing expression"); } /* parse that follows the colon */ if (mpl->token == T_COLON) { get_token(mpl /* : */); code = expression_13(mpl); /* convert the expression to logical type, if necessary */ if (code->type == A_SYMBOLIC) code = make_unary(mpl, O_CVTNUM, code, A_NUMERIC, 0); if (code->type == A_NUMERIC) code = make_unary(mpl, O_CVTLOG, code, A_LOGICAL, 0); /* now the expression must be of logical type */ if (code->type != A_LOGICAL) error(mpl, "expression following colon has invalid type"); xassert(code->dim == 0); domain->code = code; /* the right brace must follow the logical expression */ if (mpl->token != T_RBRACE) error(mpl, "syntax error in indexing expression"); } get_token(mpl /* } */); return domain; } /*---------------------------------------------------------------------- -- close_scope - close scope of indexing expression. -- -- The routine closes the scope of indexing expression specified by its -- domain and thereby makes all dummy indices introduced in the indexing -- expression no longer available for referencing. */ void close_scope(MPL *mpl, DOMAIN *domain) { DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; AVLNODE *node; xassert(domain != NULL); /* remove all dummy indices from the symbolic names table */ for (block = domain->list; block != NULL; block = block->next) { for (slot = block->list; slot != NULL; slot = slot->next) { if (slot->name != NULL) { node = avl_find_node(mpl->tree, slot->name); xassert(node != NULL); xassert(avl_get_node_type(node) == A_INDEX); avl_delete_node(mpl->tree, node); } } } return; } /*---------------------------------------------------------------------- -- iterated_expression - parse iterated expression. -- -- This routine parses primary expression using the syntax: -- -- ::= -- ::= sum -- ::= prod -- ::= min -- ::= max -- ::= exists -- -- ::= forall -- -- ::= setof -- -- Note that parsing "integrand" depends on the iterated operator. */ #if 1 /* 07/IX-2008 */ static void link_up(CODE *code) { /* if we have something like sum{(i+1,j,k-1) in E} x[i,j,k], where i and k are dummy indices defined out of the iterated expression, we should link up pseudo-code for computing i+1 and k-1 to pseudo-code for computing the iterated expression; this is needed to invalidate current value of the iterated expression once i or k have been changed */ DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; for (block = code->arg.loop.domain->list; block != NULL; block = block->next) { for (slot = block->list; slot != NULL; slot = slot->next) { if (slot->code != NULL) { xassert(slot->code->up == NULL); slot->code->up = code; } } } return; } #endif CODE *iterated_expression(MPL *mpl) { CODE *code; OPERANDS arg; int op; char opstr[8]; /* determine operation code */ xassert(mpl->token == T_NAME); if (strcmp(mpl->image, "sum") == 0) op = O_SUM; else if (strcmp(mpl->image, "prod") == 0) op = O_PROD; else if (strcmp(mpl->image, "min") == 0) op = O_MINIMUM; else if (strcmp(mpl->image, "max") == 0) op = O_MAXIMUM; else if (strcmp(mpl->image, "forall") == 0) op = O_FORALL; else if (strcmp(mpl->image, "exists") == 0) op = O_EXISTS; else if (strcmp(mpl->image, "setof") == 0) op = O_SETOF; else error(mpl, "operator %s unknown", mpl->image); strcpy(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); get_token(mpl /* */); /* check the left brace that follows the operator name */ xassert(mpl->token == T_LBRACE); /* parse indexing expression that controls iterating */ arg.loop.domain = indexing_expression(mpl); /* parse "integrand" expression and generate pseudo-code */ switch (op) { case O_SUM: case O_PROD: case O_MINIMUM: case O_MAXIMUM: arg.loop.x = expression_3(mpl); /* convert the integrand to numeric type, if necessary */ if (arg.loop.x->type == A_SYMBOLIC) arg.loop.x = make_unary(mpl, O_CVTNUM, arg.loop.x, A_NUMERIC, 0); /* now the integrand must be of numeric type or linear form (the latter is only allowed for the sum operator) */ if (!(arg.loop.x->type == A_NUMERIC || op == O_SUM && arg.loop.x->type == A_FORMULA)) err: error(mpl, "integrand following %s{...} has invalid type" , opstr); xassert(arg.loop.x->dim == 0); /* generate pseudo-code */ code = make_code(mpl, op, &arg, arg.loop.x->type, 0); break; case O_FORALL: case O_EXISTS: arg.loop.x = expression_12(mpl); /* convert the integrand to logical type, if necessary */ if (arg.loop.x->type == A_SYMBOLIC) arg.loop.x = make_unary(mpl, O_CVTNUM, arg.loop.x, A_NUMERIC, 0); if (arg.loop.x->type == A_NUMERIC) arg.loop.x = make_unary(mpl, O_CVTLOG, arg.loop.x, A_LOGICAL, 0); /* now the integrand must be of logical type */ if (arg.loop.x->type != A_LOGICAL) goto err; xassert(arg.loop.x->dim == 0); /* generate pseudo-code */ code = make_code(mpl, op, &arg, A_LOGICAL, 0); break; case O_SETOF: arg.loop.x = expression_5(mpl); /* convert the integrand to 1-tuple, if necessary */ if (arg.loop.x->type == A_NUMERIC) arg.loop.x = make_unary(mpl, O_CVTSYM, arg.loop.x, A_SYMBOLIC, 0); if (arg.loop.x->type == A_SYMBOLIC) arg.loop.x = make_unary(mpl, O_CVTTUP, arg.loop.x, A_TUPLE, 1); /* now the integrand must be n-tuple */ if (arg.loop.x->type != A_TUPLE) goto err; xassert(arg.loop.x->dim > 0); /* generate pseudo-code */ code = make_code(mpl, op, &arg, A_ELEMSET, arg.loop.x->dim); break; default: xassert(op != op); } /* close the scope of the indexing expression */ close_scope(mpl, arg.loop.domain); #if 1 /* 07/IX-2008 */ link_up(code); #endif return code; } /*---------------------------------------------------------------------- -- domain_arity - determine arity of domain. -- -- This routine returns arity of specified domain, which is number of -- its free dummy indices. */ int domain_arity(MPL *mpl, DOMAIN *domain) { DOMAIN_BLOCK *block; DOMAIN_SLOT *slot; int arity; xassert(mpl == mpl); arity = 0; for (block = domain->list; block != NULL; block = block->next) for (slot = block->list; slot != NULL; slot = slot->next) if (slot->code == NULL) arity++; return arity; } /*---------------------------------------------------------------------- -- set_expression - parse set expression. -- -- This routine parses primary expression using the syntax: -- -- ::= { } -- ::= */ CODE *set_expression(MPL *mpl) { CODE *code; OPERANDS arg; xassert(mpl->token == T_LBRACE); get_token(mpl /* { */); /* check a token that follows the left brace */ if (mpl->token == T_RBRACE) { /* it is the right brace, so the resultant is an empty set of dimension 1 */ arg.list = NULL; /* generate pseudo-code to build the resultant set */ code = make_code(mpl, O_MAKE, &arg, A_ELEMSET, 1); get_token(mpl /* } */); } else { /* the next token begins an indexing expression */ unget_token(mpl); arg.loop.domain = indexing_expression(mpl); arg.loop.x = NULL; /* integrand is not used */ /* close the scope of the indexing expression */ close_scope(mpl, arg.loop.domain); /* generate pseudo-code to build the resultant set */ code = make_code(mpl, O_BUILD, &arg, A_ELEMSET, domain_arity(mpl, arg.loop.domain)); #if 1 /* 07/IX-2008 */ link_up(code); #endif } return code; } /*---------------------------------------------------------------------- -- branched_expression - parse conditional expression. -- -- This routine parses primary expression using the syntax: -- -- ::= -- ::= if then -- ::= if then -- else -- ::= */ CODE *branched_expression(MPL *mpl) { CODE *code, *x, *y, *z; xassert(mpl->token == T_IF); get_token(mpl /* if */); /* parse that follows 'if' */ x = expression_13(mpl); /* convert the expression to logical type, if necessary */ if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); /* now the expression must be of logical type */ if (x->type != A_LOGICAL) error(mpl, "expression following if has invalid type"); xassert(x->dim == 0); /* the keyword 'then' must follow the logical expression */ if (mpl->token != T_THEN) error(mpl, "keyword then missing where expected"); get_token(mpl /* then */); /* parse that follows 'then' and check its type */ y = expression_9(mpl); if (!(y->type == A_NUMERIC || y->type == A_SYMBOLIC || y->type == A_ELEMSET || y->type == A_FORMULA)) error(mpl, "expression following then has invalid type"); /* if the expression that follows the keyword 'then' is elemental set, the keyword 'else' cannot be omitted; otherwise else-part is optional */ if (mpl->token != T_ELSE) { if (y->type == A_ELEMSET) error(mpl, "keyword else missing where expected"); z = NULL; goto skip; } get_token(mpl /* else */); /* parse that follow 'else' and check its type */ z = expression_9(mpl); if (!(z->type == A_NUMERIC || z->type == A_SYMBOLIC || z->type == A_ELEMSET || z->type == A_FORMULA)) error(mpl, "expression following else has invalid type"); /* convert to identical types, if necessary */ if (y->type == A_FORMULA || z->type == A_FORMULA) { if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type == A_NUMERIC) y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0); if (z->type == A_SYMBOLIC) z = make_unary(mpl, O_CVTNUM, z, A_NUMERIC, 0); if (z->type == A_NUMERIC) z = make_unary(mpl, O_CVTLFM, z, A_FORMULA, 0); } if (y->type == A_SYMBOLIC || z->type == A_SYMBOLIC) { if (y->type == A_NUMERIC) y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0); if (z->type == A_NUMERIC) z = make_unary(mpl, O_CVTSYM, z, A_SYMBOLIC, 0); } /* now both expressions must have identical types */ if (y->type != z->type) error(mpl, "expressions following then and else have incompati" "ble types"); /* and identical dimensions */ if (y->dim != z->dim) error(mpl, "expressions following then and else have different" " dimensions %d and %d, respectively", y->dim, z->dim); skip: /* generate pseudo-code to perform branching */ code = make_ternary(mpl, O_FORK, x, y, z, y->type, y->dim); return code; } /*---------------------------------------------------------------------- -- primary_expression - parse primary expression. -- -- This routine parses primary expression using the syntax: -- -- ::= -- ::= Infinity -- ::= -- ::= -- ::= -- ::= [ ] -- ::= -- ::= [ ] -- ::= -- ::= [ ] -- ::= ( ) -- ::= ( ) -- ::= -- ::= { } -- ::= -- ::= -- -- For complete list of syntactic rules for see -- comments to the corresponding parsing routines. */ CODE *primary_expression(MPL *mpl) { CODE *code; if (mpl->token == T_NUMBER) { /* parse numeric literal */ code = numeric_literal(mpl); } #if 1 /* 21/VII-2006 */ else if (mpl->token == T_INFINITY) { /* parse "infinity" */ OPERANDS arg; arg.num = DBL_MAX; code = make_code(mpl, O_NUMBER, &arg, A_NUMERIC, 0); get_token(mpl /* Infinity */); } #endif else if (mpl->token == T_STRING) { /* parse string literal */ code = string_literal(mpl); } else if (mpl->token == T_NAME) { int next_token; get_token(mpl /* */); next_token = mpl->token; unget_token(mpl); /* check a token that follows */ switch (next_token) { case T_LBRACKET: /* parse reference to subscripted object */ code = object_reference(mpl); break; case T_LEFT: /* parse reference to built-in function */ code = function_reference(mpl); break; case T_LBRACE: /* parse iterated expression */ code = iterated_expression(mpl); break; default: /* parse reference to unsubscripted object */ code = object_reference(mpl); break; } } else if (mpl->token == T_LEFT) { /* parse parenthesized expression */ code = expression_list(mpl); } else if (mpl->token == T_LBRACE) { /* parse set expression */ code = set_expression(mpl); } else if (mpl->token == T_IF) { /* parse conditional expression */ code = branched_expression(mpl); } else if (is_reserved(mpl)) { /* other reserved keywords cannot be used here */ error(mpl, "invalid use of reserved keyword %s", mpl->image); } else error(mpl, "syntax error in expression"); return code; } /*---------------------------------------------------------------------- -- error_preceding - raise error if preceding operand has wrong type. -- -- This routine is called to raise error if operand that precedes some -- infix operator has invalid type. */ void error_preceding(MPL *mpl, char *opstr) { error(mpl, "operand preceding %s has invalid type", opstr); /* no return */ } /*---------------------------------------------------------------------- -- error_following - raise error if following operand has wrong type. -- -- This routine is called to raise error if operand that follows some -- infix operator has invalid type. */ void error_following(MPL *mpl, char *opstr) { error(mpl, "operand following %s has invalid type", opstr); /* no return */ } /*---------------------------------------------------------------------- -- error_dimension - raise error if operands have different dimension. -- -- This routine is called to raise error if two operands of some infix -- operator have different dimension. */ void error_dimension(MPL *mpl, char *opstr, int dim1, int dim2) { error(mpl, "operands preceding and following %s have different di" "mensions %d and %d, respectively", opstr, dim1, dim2); /* no return */ } /*---------------------------------------------------------------------- -- expression_0 - parse expression of level 0. -- -- This routine parses expression of level 0 using the syntax: -- -- ::= */ CODE *expression_0(MPL *mpl) { CODE *code; code = primary_expression(mpl); return code; } /*---------------------------------------------------------------------- -- expression_1 - parse expression of level 1. -- -- This routine parses expression of level 1 using the syntax: -- -- ::= -- ::= -- ::= -- ::= ^ | ** */ CODE *expression_1(MPL *mpl) { CODE *x, *y; char opstr[8]; x = expression_0(mpl); if (mpl->token == T_POWER) { strcpy(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type != A_NUMERIC) error_preceding(mpl, opstr); get_token(mpl /* ^ | ** */); if (mpl->token == T_PLUS || mpl->token == T_MINUS) y = expression_2(mpl); else y = expression_1(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type != A_NUMERIC) error_following(mpl, opstr); x = make_binary(mpl, O_POWER, x, y, A_NUMERIC, 0); } return x; } /*---------------------------------------------------------------------- -- expression_2 - parse expression of level 2. -- -- This routine parses expression of level 2 using the syntax: -- -- ::= -- ::= + -- ::= - */ CODE *expression_2(MPL *mpl) { CODE *x; if (mpl->token == T_PLUS) { get_token(mpl /* + */); x = expression_1(mpl); if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) error_following(mpl, "+"); x = make_unary(mpl, O_PLUS, x, x->type, 0); } else if (mpl->token == T_MINUS) { get_token(mpl /* - */); x = expression_1(mpl); if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) error_following(mpl, "-"); x = make_unary(mpl, O_MINUS, x, x->type, 0); } else x = expression_1(mpl); return x; } /*---------------------------------------------------------------------- -- expression_3 - parse expression of level 3. -- -- This routine parses expression of level 3 using the syntax: -- -- ::= -- ::= * -- ::= / -- ::= div -- ::= mod */ CODE *expression_3(MPL *mpl) { CODE *x, *y; x = expression_2(mpl); for (;;) { if (mpl->token == T_ASTERISK) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) error_preceding(mpl, "*"); get_token(mpl /* * */); y = expression_2(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (!(y->type == A_NUMERIC || y->type == A_FORMULA)) error_following(mpl, "*"); if (x->type == A_FORMULA && y->type == A_FORMULA) error(mpl, "multiplication of linear forms not allowed"); if (x->type == A_NUMERIC && y->type == A_NUMERIC) x = make_binary(mpl, O_MUL, x, y, A_NUMERIC, 0); else x = make_binary(mpl, O_MUL, x, y, A_FORMULA, 0); } else if (mpl->token == T_SLASH) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) error_preceding(mpl, "/"); get_token(mpl /* / */); y = expression_2(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type != A_NUMERIC) error_following(mpl, "/"); if (x->type == A_NUMERIC) x = make_binary(mpl, O_DIV, x, y, A_NUMERIC, 0); else x = make_binary(mpl, O_DIV, x, y, A_FORMULA, 0); } else if (mpl->token == T_DIV) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type != A_NUMERIC) error_preceding(mpl, "div"); get_token(mpl /* div */); y = expression_2(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type != A_NUMERIC) error_following(mpl, "div"); x = make_binary(mpl, O_IDIV, x, y, A_NUMERIC, 0); } else if (mpl->token == T_MOD) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type != A_NUMERIC) error_preceding(mpl, "mod"); get_token(mpl /* mod */); y = expression_2(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type != A_NUMERIC) error_following(mpl, "mod"); x = make_binary(mpl, O_MOD, x, y, A_NUMERIC, 0); } else break; } return x; } /*---------------------------------------------------------------------- -- expression_4 - parse expression of level 4. -- -- This routine parses expression of level 4 using the syntax: -- -- ::= -- ::= + -- ::= - -- ::= less */ CODE *expression_4(MPL *mpl) { CODE *x, *y; x = expression_3(mpl); for (;;) { if (mpl->token == T_PLUS) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) error_preceding(mpl, "+"); get_token(mpl /* + */); y = expression_3(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (!(y->type == A_NUMERIC || y->type == A_FORMULA)) error_following(mpl, "+"); if (x->type == A_NUMERIC && y->type == A_FORMULA) x = make_unary(mpl, O_CVTLFM, x, A_FORMULA, 0); if (x->type == A_FORMULA && y->type == A_NUMERIC) y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0); x = make_binary(mpl, O_ADD, x, y, x->type, 0); } else if (mpl->token == T_MINUS) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (!(x->type == A_NUMERIC || x->type == A_FORMULA)) error_preceding(mpl, "-"); get_token(mpl /* - */); y = expression_3(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (!(y->type == A_NUMERIC || y->type == A_FORMULA)) error_following(mpl, "-"); if (x->type == A_NUMERIC && y->type == A_FORMULA) x = make_unary(mpl, O_CVTLFM, x, A_FORMULA, 0); if (x->type == A_FORMULA && y->type == A_NUMERIC) y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0); x = make_binary(mpl, O_SUB, x, y, x->type, 0); } else if (mpl->token == T_LESS) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type != A_NUMERIC) error_preceding(mpl, "less"); get_token(mpl /* less */); y = expression_3(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type != A_NUMERIC) error_following(mpl, "less"); x = make_binary(mpl, O_LESS, x, y, A_NUMERIC, 0); } else break; } return x; } /*---------------------------------------------------------------------- -- expression_5 - parse expression of level 5. -- -- This routine parses expression of level 5 using the syntax: -- -- ::= -- ::= & */ CODE *expression_5(MPL *mpl) { CODE *x, *y; x = expression_4(mpl); for (;;) { if (mpl->token == T_CONCAT) { if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); if (x->type != A_SYMBOLIC) error_preceding(mpl, "&"); get_token(mpl /* & */); y = expression_4(mpl); if (y->type == A_NUMERIC) y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0); if (y->type != A_SYMBOLIC) error_following(mpl, "&"); x = make_binary(mpl, O_CONCAT, x, y, A_SYMBOLIC, 0); } else break; } return x; } /*---------------------------------------------------------------------- -- expression_6 - parse expression of level 6. -- -- This routine parses expression of level 6 using the syntax: -- -- ::= -- ::= .. -- ::= .. by -- */ CODE *expression_6(MPL *mpl) { CODE *x, *y, *z; x = expression_5(mpl); if (mpl->token == T_DOTS) { if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type != A_NUMERIC) error_preceding(mpl, ".."); get_token(mpl /* .. */); y = expression_5(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type != A_NUMERIC) error_following(mpl, ".."); if (mpl->token == T_BY) { get_token(mpl /* by */); z = expression_5(mpl); if (z->type == A_SYMBOLIC) z = make_unary(mpl, O_CVTNUM, z, A_NUMERIC, 0); if (z->type != A_NUMERIC) error_following(mpl, "by"); } else z = NULL; x = make_ternary(mpl, O_DOTS, x, y, z, A_ELEMSET, 1); } return x; } /*---------------------------------------------------------------------- -- expression_7 - parse expression of level 7. -- -- This routine parses expression of level 7 using the syntax: -- -- ::= -- ::= cross */ CODE *expression_7(MPL *mpl) { CODE *x, *y; x = expression_6(mpl); for (;;) { if (mpl->token == T_CROSS) { if (x->type != A_ELEMSET) error_preceding(mpl, "cross"); get_token(mpl /* cross */); y = expression_6(mpl); if (y->type != A_ELEMSET) error_following(mpl, "cross"); x = make_binary(mpl, O_CROSS, x, y, A_ELEMSET, x->dim + y->dim); } else break; } return x; } /*---------------------------------------------------------------------- -- expression_8 - parse expression of level 8. -- -- This routine parses expression of level 8 using the syntax: -- -- ::= -- ::= inter */ CODE *expression_8(MPL *mpl) { CODE *x, *y; x = expression_7(mpl); for (;;) { if (mpl->token == T_INTER) { if (x->type != A_ELEMSET) error_preceding(mpl, "inter"); get_token(mpl /* inter */); y = expression_7(mpl); if (y->type != A_ELEMSET) error_following(mpl, "inter"); if (x->dim != y->dim) error_dimension(mpl, "inter", x->dim, y->dim); x = make_binary(mpl, O_INTER, x, y, A_ELEMSET, x->dim); } else break; } return x; } /*---------------------------------------------------------------------- -- expression_9 - parse expression of level 9. -- -- This routine parses expression of level 9 using the syntax: -- -- ::= -- ::= union -- ::= diff -- ::= symdiff */ CODE *expression_9(MPL *mpl) { CODE *x, *y; x = expression_8(mpl); for (;;) { if (mpl->token == T_UNION) { if (x->type != A_ELEMSET) error_preceding(mpl, "union"); get_token(mpl /* union */); y = expression_8(mpl); if (y->type != A_ELEMSET) error_following(mpl, "union"); if (x->dim != y->dim) error_dimension(mpl, "union", x->dim, y->dim); x = make_binary(mpl, O_UNION, x, y, A_ELEMSET, x->dim); } else if (mpl->token == T_DIFF) { if (x->type != A_ELEMSET) error_preceding(mpl, "diff"); get_token(mpl /* diff */); y = expression_8(mpl); if (y->type != A_ELEMSET) error_following(mpl, "diff"); if (x->dim != y->dim) error_dimension(mpl, "diff", x->dim, y->dim); x = make_binary(mpl, O_DIFF, x, y, A_ELEMSET, x->dim); } else if (mpl->token == T_SYMDIFF) { if (x->type != A_ELEMSET) error_preceding(mpl, "symdiff"); get_token(mpl /* symdiff */); y = expression_8(mpl); if (y->type != A_ELEMSET) error_following(mpl, "symdiff"); if (x->dim != y->dim) error_dimension(mpl, "symdiff", x->dim, y->dim); x = make_binary(mpl, O_SYMDIFF, x, y, A_ELEMSET, x->dim); } else break; } return x; } /*---------------------------------------------------------------------- -- expression_10 - parse expression of level 10. -- -- This routine parses expression of level 10 using the syntax: -- -- ::= -- ::= -- ::= < | <= | = | == | >= | > | <> | != | in | not in | ! in | -- within | not within | ! within */ CODE *expression_10(MPL *mpl) { CODE *x, *y; int op = -1; char opstr[16]; x = expression_9(mpl); strcpy(opstr, ""); switch (mpl->token) { case T_LT: op = O_LT; break; case T_LE: op = O_LE; break; case T_EQ: op = O_EQ; break; case T_GE: op = O_GE; break; case T_GT: op = O_GT; break; case T_NE: op = O_NE; break; case T_IN: op = O_IN; break; case T_WITHIN: op = O_WITHIN; break; case T_NOT: strcpy(opstr, mpl->image); get_token(mpl /* not | ! */); if (mpl->token == T_IN) op = O_NOTIN; else if (mpl->token == T_WITHIN) op = O_NOTWITHIN; else error(mpl, "invalid use of %s", opstr); strcat(opstr, " "); break; default: goto done; } strcat(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); switch (op) { case O_EQ: case O_NE: #if 1 /* 02/VIII-2008 */ case O_LT: case O_LE: case O_GT: case O_GE: #endif if (!(x->type == A_NUMERIC || x->type == A_SYMBOLIC)) error_preceding(mpl, opstr); get_token(mpl /* */); y = expression_9(mpl); if (!(y->type == A_NUMERIC || y->type == A_SYMBOLIC)) error_following(mpl, opstr); if (x->type == A_NUMERIC && y->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); if (x->type == A_SYMBOLIC && y->type == A_NUMERIC) y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0); x = make_binary(mpl, op, x, y, A_LOGICAL, 0); break; #if 0 /* 02/VIII-2008 */ case O_LT: case O_LE: case O_GT: case O_GE: if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type != A_NUMERIC) error_preceding(mpl, opstr); get_token(mpl /* */); y = expression_9(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type != A_NUMERIC) error_following(mpl, opstr); x = make_binary(mpl, op, x, y, A_LOGICAL, 0); break; #endif case O_IN: case O_NOTIN: if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0); if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTTUP, x, A_TUPLE, 1); if (x->type != A_TUPLE) error_preceding(mpl, opstr); get_token(mpl /* */); y = expression_9(mpl); if (y->type != A_ELEMSET) error_following(mpl, opstr); if (x->dim != y->dim) error_dimension(mpl, opstr, x->dim, y->dim); x = make_binary(mpl, op, x, y, A_LOGICAL, 0); break; case O_WITHIN: case O_NOTWITHIN: if (x->type != A_ELEMSET) error_preceding(mpl, opstr); get_token(mpl /* */); y = expression_9(mpl); if (y->type != A_ELEMSET) error_following(mpl, opstr); if (x->dim != y->dim) error_dimension(mpl, opstr, x->dim, y->dim); x = make_binary(mpl, op, x, y, A_LOGICAL, 0); break; default: xassert(op != op); } done: return x; } /*---------------------------------------------------------------------- -- expression_11 - parse expression of level 11. -- -- This routine parses expression of level 11 using the syntax: -- -- ::= -- ::= not -- ::= ! */ CODE *expression_11(MPL *mpl) { CODE *x; char opstr[8]; if (mpl->token == T_NOT) { strcpy(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); get_token(mpl /* not | ! */); x = expression_10(mpl); if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); if (x->type != A_LOGICAL) error_following(mpl, opstr); x = make_unary(mpl, O_NOT, x, A_LOGICAL, 0); } else x = expression_10(mpl); return x; } /*---------------------------------------------------------------------- -- expression_12 - parse expression of level 12. -- -- This routine parses expression of level 12 using the syntax: -- -- ::= -- ::= and -- ::= && */ CODE *expression_12(MPL *mpl) { CODE *x, *y; char opstr[8]; x = expression_11(mpl); for (;;) { if (mpl->token == T_AND) { strcpy(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); if (x->type != A_LOGICAL) error_preceding(mpl, opstr); get_token(mpl /* and | && */); y = expression_11(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type == A_NUMERIC) y = make_unary(mpl, O_CVTLOG, y, A_LOGICAL, 0); if (y->type != A_LOGICAL) error_following(mpl, opstr); x = make_binary(mpl, O_AND, x, y, A_LOGICAL, 0); } else break; } return x; } /*---------------------------------------------------------------------- -- expression_13 - parse expression of level 13. -- -- This routine parses expression of level 13 using the syntax: -- -- ::= -- ::= or -- ::= || */ CODE *expression_13(MPL *mpl) { CODE *x, *y; char opstr[8]; x = expression_12(mpl); for (;;) { if (mpl->token == T_OR) { strcpy(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); if (x->type == A_SYMBOLIC) x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0); if (x->type == A_NUMERIC) x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0); if (x->type != A_LOGICAL) error_preceding(mpl, opstr); get_token(mpl /* or | || */); y = expression_12(mpl); if (y->type == A_SYMBOLIC) y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0); if (y->type == A_NUMERIC) y = make_unary(mpl, O_CVTLOG, y, A_LOGICAL, 0); if (y->type != A_LOGICAL) error_following(mpl, opstr); x = make_binary(mpl, O_OR, x, y, A_LOGICAL, 0); } else break; } return x; } /*---------------------------------------------------------------------- -- set_statement - parse set statement. -- -- This routine parses set statement using the syntax: -- -- ::= set -- ; -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= , dimen -- ::= , within -- ::= , := -- ::= , default -- -- Commae in are optional and may be omitted anywhere. */ SET *set_statement(MPL *mpl) { SET *set; int dimen_used = 0; xassert(is_keyword(mpl, "set")); get_token(mpl /* set */); /* symbolic name must follow the keyword 'set' */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "symbolic name missing where expected"); /* there must be no other object with the same name */ if (avl_find_node(mpl->tree, mpl->image) != NULL) error(mpl, "%s multiply declared", mpl->image); /* create model set */ set = alloc(SET); set->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(set->name, mpl->image); set->alias = NULL; set->dim = 0; set->domain = NULL; set->dimen = 0; set->within = NULL; set->assign = NULL; set->option = NULL; set->gadget = NULL; set->data = 0; set->array = NULL; get_token(mpl /* */); /* parse optional alias */ if (mpl->token == T_STRING) { set->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(set->alias, mpl->image); get_token(mpl /* */); } /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { set->domain = indexing_expression(mpl); set->dim = domain_arity(mpl, set->domain); } /* include the set name in the symbolic names table */ { AVLNODE *node; node = avl_insert_node(mpl->tree, set->name); avl_set_node_type(node, A_SET); avl_set_node_link(node, (void *)set); } /* parse the list of optional attributes */ for (;;) { if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_SEMICOLON) break; if (is_keyword(mpl, "dimen")) { /* dimension of set members */ int dimen; get_token(mpl /* dimen */); if (!(mpl->token == T_NUMBER && 1.0 <= mpl->value && mpl->value <= 20.0 && floor(mpl->value) == mpl->value)) error(mpl, "dimension must be integer between 1 and 20"); dimen = (int)(mpl->value + 0.5); if (dimen_used) error(mpl, "at most one dimension attribute allowed"); if (set->dimen > 0) error(mpl, "dimension %d conflicts with dimension %d alr" "eady determined", dimen, set->dimen); set->dimen = dimen; dimen_used = 1; get_token(mpl /* */); } else if (mpl->token == T_WITHIN || mpl->token == T_IN) { /* restricting superset */ WITHIN *within, *temp; if (mpl->token == T_IN && !mpl->as_within) { warning(mpl, "keyword in understood as within"); mpl->as_within = 1; } get_token(mpl /* within */); /* create new restricting superset list entry and append it to the within-list */ within = alloc(WITHIN); within->code = NULL; within->next = NULL; if (set->within == NULL) set->within = within; else { for (temp = set->within; temp->next != NULL; temp = temp->next); temp->next = within; } /* parse an expression that follows 'within' */ within->code = expression_9(mpl); if (within->code->type != A_ELEMSET) error(mpl, "expression following within has invalid type" ); xassert(within->code->dim > 0); /* check/set dimension of set members */ if (set->dimen == 0) set->dimen = within->code->dim; if (set->dimen != within->code->dim) error(mpl, "set expression following within must have di" "mension %d rather than %d", set->dimen, within->code->dim); } else if (mpl->token == T_ASSIGN) { /* assignment expression */ if (!(set->assign == NULL && set->option == NULL && set->gadget == NULL)) err: error(mpl, "at most one := or default/data allowed"); get_token(mpl /* := */); /* parse an expression that follows ':=' */ set->assign = expression_9(mpl); if (set->assign->type != A_ELEMSET) error(mpl, "expression following := has invalid type"); xassert(set->assign->dim > 0); /* check/set dimension of set members */ if (set->dimen == 0) set->dimen = set->assign->dim; if (set->dimen != set->assign->dim) error(mpl, "set expression following := must have dimens" "ion %d rather than %d", set->dimen, set->assign->dim); } else if (is_keyword(mpl, "default")) { /* expression for default value */ if (!(set->assign == NULL && set->option == NULL)) goto err; get_token(mpl /* := */); /* parse an expression that follows 'default' */ set->option = expression_9(mpl); if (set->option->type != A_ELEMSET) error(mpl, "expression following default has invalid typ" "e"); xassert(set->option->dim > 0); /* check/set dimension of set members */ if (set->dimen == 0) set->dimen = set->option->dim; if (set->dimen != set->option->dim) error(mpl, "set expression following default must have d" "imension %d rather than %d", set->dimen, set->option->dim); } #if 1 /* 12/XII-2008 */ else if (is_keyword(mpl, "data")) { /* gadget to initialize the set by data from plain set */ GADGET *gadget; AVLNODE *node; int i, k, fff[20]; if (!(set->assign == NULL && set->gadget == NULL)) goto err; get_token(mpl /* data */); set->gadget = gadget = alloc(GADGET); /* set name must follow the keyword 'data' */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "set name missing where expected"); /* find the set in the symbolic name table */ node = avl_find_node(mpl->tree, mpl->image); if (node == NULL) error(mpl, "%s not defined", mpl->image); if (avl_get_node_type(node) != A_SET) err1: error(mpl, "%s not a plain set", mpl->image); gadget->set = avl_get_node_link(node); if (gadget->set->dim != 0) goto err1; if (gadget->set == set) error(mpl, "set cannot be initialized by itself"); /* check and set dimensions */ if (set->dim >= gadget->set->dimen) err2: error(mpl, "dimension of %s too small", mpl->image); if (set->dimen == 0) set->dimen = gadget->set->dimen - set->dim; if (set->dim + set->dimen > gadget->set->dimen) goto err2; else if (set->dim + set->dimen < gadget->set->dimen) error(mpl, "dimension of %s too big", mpl->image); get_token(mpl /* set name */); /* left parenthesis must follow the set name */ if (mpl->token == T_LEFT) get_token(mpl /* ( */); else error(mpl, "left parenthesis missing where expected"); /* parse permutation of component numbers */ for (k = 0; k < gadget->set->dimen; k++) fff[k] = 0; k = 0; for (;;) { if (mpl->token != T_NUMBER) error(mpl, "component number missing where expected"); if (str2int(mpl->image, &i) != 0) err3: error(mpl, "component number must be integer between " "1 and %d", gadget->set->dimen); if (!(1 <= i && i <= gadget->set->dimen)) goto err3; if (fff[i-1] != 0) error(mpl, "component %d multiply specified", i); gadget->ind[k++] = i, fff[i-1] = 1; xassert(k <= gadget->set->dimen); get_token(mpl /* number */); if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_RIGHT) break; else error(mpl, "syntax error in data attribute"); } if (k < gadget->set->dimen) error(mpl, "there are must be %d components rather than " "%d", gadget->set->dimen, k); get_token(mpl /* ) */); } #endif else error(mpl, "syntax error in set statement"); } /* close the domain scope */ if (set->domain != NULL) close_scope(mpl, set->domain); /* if dimension of set members is still unknown, set it to 1 */ if (set->dimen == 0) set->dimen = 1; /* the set statement has been completely parsed */ xassert(mpl->token == T_SEMICOLON); get_token(mpl /* ; */); return set; } /*---------------------------------------------------------------------- -- parameter_statement - parse parameter statement. -- -- This routine parses parameter statement using the syntax: -- -- ::= param -- ; -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= , integer -- ::= , binary -- ::= , symbolic -- ::= , -- ::= , in -- ::= , := -- ::= , default -- ::= < | <= | = | == | >= | > | <> | != -- -- Commae in are optional and may be omitted anywhere. */ PARAMETER *parameter_statement(MPL *mpl) { PARAMETER *par; int integer_used = 0, binary_used = 0, symbolic_used = 0; xassert(is_keyword(mpl, "param")); get_token(mpl /* param */); /* symbolic name must follow the keyword 'param' */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "symbolic name missing where expected"); /* there must be no other object with the same name */ if (avl_find_node(mpl->tree, mpl->image) != NULL) error(mpl, "%s multiply declared", mpl->image); /* create model parameter */ par = alloc(PARAMETER); par->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(par->name, mpl->image); par->alias = NULL; par->dim = 0; par->domain = NULL; par->type = A_NUMERIC; par->cond = NULL; par->in = NULL; par->assign = NULL; par->option = NULL; par->data = 0; par->defval = NULL; par->array = NULL; get_token(mpl /* */); /* parse optional alias */ if (mpl->token == T_STRING) { par->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(par->alias, mpl->image); get_token(mpl /* */); } /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { par->domain = indexing_expression(mpl); par->dim = domain_arity(mpl, par->domain); } /* include the parameter name in the symbolic names table */ { AVLNODE *node; node = avl_insert_node(mpl->tree, par->name); avl_set_node_type(node, A_PARAMETER); avl_set_node_link(node, (void *)par); } /* parse the list of optional attributes */ for (;;) { if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_SEMICOLON) break; if (is_keyword(mpl, "integer")) { if (integer_used) error(mpl, "at most one integer allowed"); if (par->type == A_SYMBOLIC) error(mpl, "symbolic parameter cannot be integer"); if (par->type != A_BINARY) par->type = A_INTEGER; integer_used = 1; get_token(mpl /* integer */); } else if (is_keyword(mpl, "binary")) bin: { if (binary_used) error(mpl, "at most one binary allowed"); if (par->type == A_SYMBOLIC) error(mpl, "symbolic parameter cannot be binary"); par->type = A_BINARY; binary_used = 1; get_token(mpl /* binary */); } else if (is_keyword(mpl, "logical")) { if (!mpl->as_binary) { warning(mpl, "keyword logical understood as binary"); mpl->as_binary = 1; } goto bin; } else if (is_keyword(mpl, "symbolic")) { if (symbolic_used) error(mpl, "at most one symbolic allowed"); if (par->type != A_NUMERIC) error(mpl, "integer or binary parameter cannot be symbol" "ic"); /* the parameter may be referenced from expressions given in the same parameter declaration, so its type must be completed before parsing that expressions */ if (!(par->cond == NULL && par->in == NULL && par->assign == NULL && par->option == NULL)) error(mpl, "keyword symbolic must precede any other para" "meter attributes"); par->type = A_SYMBOLIC; symbolic_used = 1; get_token(mpl /* symbolic */); } else if (mpl->token == T_LT || mpl->token == T_LE || mpl->token == T_EQ || mpl->token == T_GE || mpl->token == T_GT || mpl->token == T_NE) { /* restricting condition */ CONDITION *cond, *temp; char opstr[8]; /* create new restricting condition list entry and append it to the conditions list */ cond = alloc(CONDITION); switch (mpl->token) { case T_LT: cond->rho = O_LT, strcpy(opstr, mpl->image); break; case T_LE: cond->rho = O_LE, strcpy(opstr, mpl->image); break; case T_EQ: cond->rho = O_EQ, strcpy(opstr, mpl->image); break; case T_GE: cond->rho = O_GE, strcpy(opstr, mpl->image); break; case T_GT: cond->rho = O_GT, strcpy(opstr, mpl->image); break; case T_NE: cond->rho = O_NE, strcpy(opstr, mpl->image); break; default: xassert(mpl->token != mpl->token); } xassert(strlen(opstr) < sizeof(opstr)); cond->code = NULL; cond->next = NULL; if (par->cond == NULL) par->cond = cond; else { for (temp = par->cond; temp->next != NULL; temp = temp->next); temp->next = cond; } #if 0 /* 13/VIII-2008 */ if (par->type == A_SYMBOLIC && !(cond->rho == O_EQ || cond->rho == O_NE)) error(mpl, "inequality restriction not allowed"); #endif get_token(mpl /* rho */); /* parse an expression that follows relational operator */ cond->code = expression_5(mpl); if (!(cond->code->type == A_NUMERIC || cond->code->type == A_SYMBOLIC)) error(mpl, "expression following %s has invalid type", opstr); xassert(cond->code->dim == 0); /* convert to the parameter type, if necessary */ if (par->type != A_SYMBOLIC && cond->code->type == A_SYMBOLIC) cond->code = make_unary(mpl, O_CVTNUM, cond->code, A_NUMERIC, 0); if (par->type == A_SYMBOLIC && cond->code->type != A_SYMBOLIC) cond->code = make_unary(mpl, O_CVTSYM, cond->code, A_SYMBOLIC, 0); } else if (mpl->token == T_IN || mpl->token == T_WITHIN) { /* restricting superset */ WITHIN *in, *temp; if (mpl->token == T_WITHIN && !mpl->as_in) { warning(mpl, "keyword within understood as in"); mpl->as_in = 1; } get_token(mpl /* in */); /* create new restricting superset list entry and append it to the in-list */ in = alloc(WITHIN); in->code = NULL; in->next = NULL; if (par->in == NULL) par->in = in; else { for (temp = par->in; temp->next != NULL; temp = temp->next); temp->next = in; } /* parse an expression that follows 'in' */ in->code = expression_9(mpl); if (in->code->type != A_ELEMSET) error(mpl, "expression following in has invalid type"); xassert(in->code->dim > 0); if (in->code->dim != 1) error(mpl, "set expression following in must have dimens" "ion 1 rather than %d", in->code->dim); } else if (mpl->token == T_ASSIGN) { /* assignment expression */ if (!(par->assign == NULL && par->option == NULL)) err: error(mpl, "at most one := or default allowed"); get_token(mpl /* := */); /* parse an expression that follows ':=' */ par->assign = expression_5(mpl); /* the expression must be of numeric/symbolic type */ if (!(par->assign->type == A_NUMERIC || par->assign->type == A_SYMBOLIC)) error(mpl, "expression following := has invalid type"); xassert(par->assign->dim == 0); /* convert to the parameter type, if necessary */ if (par->type != A_SYMBOLIC && par->assign->type == A_SYMBOLIC) par->assign = make_unary(mpl, O_CVTNUM, par->assign, A_NUMERIC, 0); if (par->type == A_SYMBOLIC && par->assign->type != A_SYMBOLIC) par->assign = make_unary(mpl, O_CVTSYM, par->assign, A_SYMBOLIC, 0); } else if (is_keyword(mpl, "default")) { /* expression for default value */ if (!(par->assign == NULL && par->option == NULL)) goto err; get_token(mpl /* default */); /* parse an expression that follows 'default' */ par->option = expression_5(mpl); if (!(par->option->type == A_NUMERIC || par->option->type == A_SYMBOLIC)) error(mpl, "expression following default has invalid typ" "e"); xassert(par->option->dim == 0); /* convert to the parameter type, if necessary */ if (par->type != A_SYMBOLIC && par->option->type == A_SYMBOLIC) par->option = make_unary(mpl, O_CVTNUM, par->option, A_NUMERIC, 0); if (par->type == A_SYMBOLIC && par->option->type != A_SYMBOLIC) par->option = make_unary(mpl, O_CVTSYM, par->option, A_SYMBOLIC, 0); } else error(mpl, "syntax error in parameter statement"); } /* close the domain scope */ if (par->domain != NULL) close_scope(mpl, par->domain); /* the parameter statement has been completely parsed */ xassert(mpl->token == T_SEMICOLON); get_token(mpl /* ; */); return par; } /*---------------------------------------------------------------------- -- variable_statement - parse variable statement. -- -- This routine parses variable statement using the syntax: -- -- ::= var -- ; -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= , integer -- ::= , binary -- ::= , -- ::= >= | <= | = | == -- -- Commae in are optional and may be omitted anywhere. */ VARIABLE *variable_statement(MPL *mpl) { VARIABLE *var; int integer_used = 0, binary_used = 0; xassert(is_keyword(mpl, "var")); if (mpl->flag_s) error(mpl, "variable statement must precede solve statement"); get_token(mpl /* var */); /* symbolic name must follow the keyword 'var' */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "symbolic name missing where expected"); /* there must be no other object with the same name */ if (avl_find_node(mpl->tree, mpl->image) != NULL) error(mpl, "%s multiply declared", mpl->image); /* create model variable */ var = alloc(VARIABLE); var->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(var->name, mpl->image); var->alias = NULL; var->dim = 0; var->domain = NULL; var->type = A_NUMERIC; var->lbnd = NULL; var->ubnd = NULL; var->array = NULL; get_token(mpl /* */); /* parse optional alias */ if (mpl->token == T_STRING) { var->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(var->alias, mpl->image); get_token(mpl /* */); } /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { var->domain = indexing_expression(mpl); var->dim = domain_arity(mpl, var->domain); } /* include the variable name in the symbolic names table */ { AVLNODE *node; node = avl_insert_node(mpl->tree, var->name); avl_set_node_type(node, A_VARIABLE); avl_set_node_link(node, (void *)var); } /* parse the list of optional attributes */ for (;;) { if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_SEMICOLON) break; if (is_keyword(mpl, "integer")) { if (integer_used) error(mpl, "at most one integer allowed"); if (var->type != A_BINARY) var->type = A_INTEGER; integer_used = 1; get_token(mpl /* integer */); } else if (is_keyword(mpl, "binary")) bin: { if (binary_used) error(mpl, "at most one binary allowed"); var->type = A_BINARY; binary_used = 1; get_token(mpl /* binary */); } else if (is_keyword(mpl, "logical")) { if (!mpl->as_binary) { warning(mpl, "keyword logical understood as binary"); mpl->as_binary = 1; } goto bin; } else if (is_keyword(mpl, "symbolic")) error(mpl, "variable cannot be symbolic"); else if (mpl->token == T_GE) { /* lower bound */ if (var->lbnd != NULL) { if (var->lbnd == var->ubnd) error(mpl, "both fixed value and lower bound not allo" "wed"); else error(mpl, "at most one lower bound allowed"); } get_token(mpl /* >= */); /* parse an expression that specifies the lower bound */ var->lbnd = expression_5(mpl); if (var->lbnd->type == A_SYMBOLIC) var->lbnd = make_unary(mpl, O_CVTNUM, var->lbnd, A_NUMERIC, 0); if (var->lbnd->type != A_NUMERIC) error(mpl, "expression following >= has invalid type"); xassert(var->lbnd->dim == 0); } else if (mpl->token == T_LE) { /* upper bound */ if (var->ubnd != NULL) { if (var->ubnd == var->lbnd) error(mpl, "both fixed value and upper bound not allo" "wed"); else error(mpl, "at most one upper bound allowed"); } get_token(mpl /* <= */); /* parse an expression that specifies the upper bound */ var->ubnd = expression_5(mpl); if (var->ubnd->type == A_SYMBOLIC) var->ubnd = make_unary(mpl, O_CVTNUM, var->ubnd, A_NUMERIC, 0); if (var->ubnd->type != A_NUMERIC) error(mpl, "expression following <= has invalid type"); xassert(var->ubnd->dim == 0); } else if (mpl->token == T_EQ) { /* fixed value */ char opstr[8]; if (!(var->lbnd == NULL && var->ubnd == NULL)) { if (var->lbnd == var->ubnd) error(mpl, "at most one fixed value allowed"); else if (var->lbnd != NULL) error(mpl, "both lower bound and fixed value not allo" "wed"); else error(mpl, "both upper bound and fixed value not allo" "wed"); } strcpy(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); get_token(mpl /* = | == */); /* parse an expression that specifies the fixed value */ var->lbnd = expression_5(mpl); if (var->lbnd->type == A_SYMBOLIC) var->lbnd = make_unary(mpl, O_CVTNUM, var->lbnd, A_NUMERIC, 0); if (var->lbnd->type != A_NUMERIC) error(mpl, "expression following %s has invalid type", opstr); xassert(var->lbnd->dim == 0); /* indicate that the variable is fixed, not bounded */ var->ubnd = var->lbnd; } else if (mpl->token == T_LT || mpl->token == T_GT || mpl->token == T_NE) error(mpl, "strict bound not allowed"); else error(mpl, "syntax error in variable statement"); } /* close the domain scope */ if (var->domain != NULL) close_scope(mpl, var->domain); /* the variable statement has been completely parsed */ xassert(mpl->token == T_SEMICOLON); get_token(mpl /* ; */); return var; } /*---------------------------------------------------------------------- -- constraint_statement - parse constraint statement. -- -- This routine parses constraint statement using the syntax: -- -- ::= -- : ; -- ::= -- ::= subject to -- ::= subj to -- ::= s.t. -- ::= -- ::= -- ::= -- ::= -- ::= , >= -- ::= , <= -- ::= , = -- ::= , <= , <= -- ::= , >= , >= -- ::= -- -- Commae in are optional and may be omitted anywhere. */ CONSTRAINT *constraint_statement(MPL *mpl) { CONSTRAINT *con; CODE *first, *second, *third; int rho; char opstr[8]; if (mpl->flag_s) error(mpl, "constraint statement must precede solve statement") ; if (is_keyword(mpl, "subject")) { get_token(mpl /* subject */); if (!is_keyword(mpl, "to")) error(mpl, "keyword subject to incomplete"); get_token(mpl /* to */); } else if (is_keyword(mpl, "subj")) { get_token(mpl /* subj */); if (!is_keyword(mpl, "to")) error(mpl, "keyword subj to incomplete"); get_token(mpl /* to */); } else if (mpl->token == T_SPTP) get_token(mpl /* s.t. */); /* the current token must be symbolic name of constraint */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "symbolic name missing where expected"); /* there must be no other object with the same name */ if (avl_find_node(mpl->tree, mpl->image) != NULL) error(mpl, "%s multiply declared", mpl->image); /* create model constraint */ con = alloc(CONSTRAINT); con->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(con->name, mpl->image); con->alias = NULL; con->dim = 0; con->domain = NULL; con->type = A_CONSTRAINT; con->code = NULL; con->lbnd = NULL; con->ubnd = NULL; con->array = NULL; get_token(mpl /* */); /* parse optional alias */ if (mpl->token == T_STRING) { con->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(con->alias, mpl->image); get_token(mpl /* */); } /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { con->domain = indexing_expression(mpl); con->dim = domain_arity(mpl, con->domain); } /* include the constraint name in the symbolic names table */ { AVLNODE *node; node = avl_insert_node(mpl->tree, con->name); avl_set_node_type(node, A_CONSTRAINT); avl_set_node_link(node, (void *)con); } /* the colon must precede the first expression */ if (mpl->token != T_COLON) error(mpl, "colon missing where expected"); get_token(mpl /* : */); /* parse the first expression */ first = expression_5(mpl); if (first->type == A_SYMBOLIC) first = make_unary(mpl, O_CVTNUM, first, A_NUMERIC, 0); if (!(first->type == A_NUMERIC || first->type == A_FORMULA)) error(mpl, "expression following colon has invalid type"); xassert(first->dim == 0); /* relational operator must follow the first expression */ if (mpl->token == T_COMMA) get_token(mpl /* , */); switch (mpl->token) { case T_LE: case T_GE: case T_EQ: break; case T_LT: case T_GT: case T_NE: error(mpl, "strict inequality not allowed"); case T_SEMICOLON: error(mpl, "constraint must be equality or inequality"); default: goto err; } rho = mpl->token; strcpy(opstr, mpl->image); xassert(strlen(opstr) < sizeof(opstr)); get_token(mpl /* rho */); /* parse the second expression */ second = expression_5(mpl); if (second->type == A_SYMBOLIC) second = make_unary(mpl, O_CVTNUM, second, A_NUMERIC, 0); if (!(second->type == A_NUMERIC || second->type == A_FORMULA)) error(mpl, "expression following %s has invalid type", opstr); xassert(second->dim == 0); /* check a token that follow the second expression */ if (mpl->token == T_COMMA) { get_token(mpl /* , */); if (mpl->token == T_SEMICOLON) goto err; } if (mpl->token == T_LT || mpl->token == T_LE || mpl->token == T_EQ || mpl->token == T_GE || mpl->token == T_GT || mpl->token == T_NE) { /* it is another relational operator, therefore the constraint is double inequality */ if (rho == T_EQ || mpl->token != rho) error(mpl, "double inequality must be ... <= ... <= ... or " "... >= ... >= ..."); /* the first expression cannot be linear form */ if (first->type == A_FORMULA) error(mpl, "leftmost expression in double inequality cannot" " be linear form"); get_token(mpl /* rho */); /* parse the third expression */ third = expression_5(mpl); if (third->type == A_SYMBOLIC) third = make_unary(mpl, O_CVTNUM, second, A_NUMERIC, 0); if (!(third->type == A_NUMERIC || third->type == A_FORMULA)) error(mpl, "rightmost expression in double inequality const" "raint has invalid type"); xassert(third->dim == 0); /* the third expression also cannot be linear form */ if (third->type == A_FORMULA) error(mpl, "rightmost expression in double inequality canno" "t be linear form"); } else { /* the constraint is equality or single inequality */ third = NULL; } /* close the domain scope */ if (con->domain != NULL) close_scope(mpl, con->domain); /* convert all expressions to linear form, if necessary */ if (first->type != A_FORMULA) first = make_unary(mpl, O_CVTLFM, first, A_FORMULA, 0); if (second->type != A_FORMULA) second = make_unary(mpl, O_CVTLFM, second, A_FORMULA, 0); if (third != NULL) third = make_unary(mpl, O_CVTLFM, third, A_FORMULA, 0); /* arrange expressions in the constraint */ if (third == NULL) { /* the constraint is equality or single inequality */ switch (rho) { case T_LE: /* first <= second */ con->code = first; con->lbnd = NULL; con->ubnd = second; break; case T_GE: /* first >= second */ con->code = first; con->lbnd = second; con->ubnd = NULL; break; case T_EQ: /* first = second */ con->code = first; con->lbnd = second; con->ubnd = second; break; default: xassert(rho != rho); } } else { /* the constraint is double inequality */ switch (rho) { case T_LE: /* first <= second <= third */ con->code = second; con->lbnd = first; con->ubnd = third; break; case T_GE: /* first >= second >= third */ con->code = second; con->lbnd = third; con->ubnd = first; break; default: xassert(rho != rho); } } /* the constraint statement has been completely parsed */ if (mpl->token != T_SEMICOLON) err: error(mpl, "syntax error in constraint statement"); get_token(mpl /* ; */); return con; } /*---------------------------------------------------------------------- -- objective_statement - parse objective statement. -- -- This routine parses objective statement using the syntax: -- -- ::= : -- ; -- ::= minimize -- ::= maximize -- ::= -- ::= -- ::= -- ::= -- ::= */ CONSTRAINT *objective_statement(MPL *mpl) { CONSTRAINT *obj; int type; if (is_keyword(mpl, "minimize")) type = A_MINIMIZE; else if (is_keyword(mpl, "maximize")) type = A_MAXIMIZE; else xassert(mpl != mpl); if (mpl->flag_s) error(mpl, "objective statement must precede solve statement"); get_token(mpl /* minimize | maximize */); /* symbolic name must follow the verb 'minimize' or 'maximize' */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "symbolic name missing where expected"); /* there must be no other object with the same name */ if (avl_find_node(mpl->tree, mpl->image) != NULL) error(mpl, "%s multiply declared", mpl->image); /* create model objective */ obj = alloc(CONSTRAINT); obj->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(obj->name, mpl->image); obj->alias = NULL; obj->dim = 0; obj->domain = NULL; obj->type = type; obj->code = NULL; obj->lbnd = NULL; obj->ubnd = NULL; obj->array = NULL; get_token(mpl /* */); /* parse optional alias */ if (mpl->token == T_STRING) { obj->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(obj->alias, mpl->image); get_token(mpl /* */); } /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { obj->domain = indexing_expression(mpl); obj->dim = domain_arity(mpl, obj->domain); } /* include the constraint name in the symbolic names table */ { AVLNODE *node; node = avl_insert_node(mpl->tree, obj->name); avl_set_node_type(node, A_CONSTRAINT); avl_set_node_link(node, (void *)obj); } /* the colon must precede the objective expression */ if (mpl->token != T_COLON) error(mpl, "colon missing where expected"); get_token(mpl /* : */); /* parse the objective expression */ obj->code = expression_5(mpl); if (obj->code->type == A_SYMBOLIC) obj->code = make_unary(mpl, O_CVTNUM, obj->code, A_NUMERIC, 0); if (obj->code->type == A_NUMERIC) obj->code = make_unary(mpl, O_CVTLFM, obj->code, A_FORMULA, 0); if (obj->code->type != A_FORMULA) error(mpl, "expression following colon has invalid type"); xassert(obj->code->dim == 0); /* close the domain scope */ if (obj->domain != NULL) close_scope(mpl, obj->domain); /* the objective statement has been completely parsed */ if (mpl->token != T_SEMICOLON) error(mpl, "syntax error in objective statement"); get_token(mpl /* ; */); return obj; } #if 1 /* 11/II-2008 */ /*********************************************************************** * table_statement - parse table statement * * This routine parses table statement using the syntax: * * ::= *
::= * * ::= * table
IN : * [ ] , ; * ::= * ::= * ::= * ::= * ::= , * ::= * ::= <- * ::= * ::= , * ::= * ::= , * ::= * ::= ~ * * ::= * table
OUT : * ; * ::= * ::= * ::= , * ::= * ::= ~ */ TABLE *table_statement(MPL *mpl) { TABLE *tab; TABARG *last_arg, *arg; TABFLD *last_fld, *fld; TABIN *last_in, *in; TABOUT *last_out, *out; AVLNODE *node; int nflds; char name[MAX_LENGTH+1]; xassert(is_keyword(mpl, "table")); get_token(mpl /* solve */); /* symbolic name must follow the keyword table */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "symbolic name missing where expected"); /* there must be no other object with the same name */ if (avl_find_node(mpl->tree, mpl->image) != NULL) error(mpl, "%s multiply declared", mpl->image); /* create data table */ tab = alloc(TABLE); tab->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(tab->name, mpl->image); get_token(mpl /* */); /* parse optional alias */ if (mpl->token == T_STRING) { tab->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(tab->alias, mpl->image); get_token(mpl /* */); } else tab->alias = NULL; /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { /* this is output table */ tab->type = A_OUTPUT; tab->u.out.domain = indexing_expression(mpl); if (!is_keyword(mpl, "OUT")) error(mpl, "keyword OUT missing where expected"); get_token(mpl /* OUT */); } else { /* this is input table */ tab->type = A_INPUT; if (!is_keyword(mpl, "IN")) error(mpl, "keyword IN missing where expected"); get_token(mpl /* IN */); } /* parse argument list */ tab->arg = last_arg = NULL; for (;;) { /* create argument list entry */ arg = alloc(TABARG); /* parse argument expression */ if (mpl->token == T_COMMA || mpl->token == T_COLON || mpl->token == T_SEMICOLON) error(mpl, "argument expression missing where expected"); arg->code = expression_5(mpl); /* convert the result to symbolic type, if necessary */ if (arg->code->type == A_NUMERIC) arg->code = make_unary(mpl, O_CVTSYM, arg->code, A_SYMBOLIC, 0); /* check that now the result is of symbolic type */ if (arg->code->type != A_SYMBOLIC) error(mpl, "argument expression has invalid type"); /* add the entry to the end of the list */ arg->next = NULL; if (last_arg == NULL) tab->arg = arg; else last_arg->next = arg; last_arg = arg; /* argument expression has been parsed */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_COLON || mpl->token == T_SEMICOLON) break; } xassert(tab->arg != NULL); /* argument list must end with colon */ if (mpl->token == T_COLON) get_token(mpl /* : */); else error(mpl, "colon missing where expected"); /* parse specific part of the table statement */ switch (tab->type) { case A_INPUT: goto input_table; case A_OUTPUT: goto output_table; default: xassert(tab != tab); } input_table: /* parse optional set name */ if (mpl->token == T_NAME) { node = avl_find_node(mpl->tree, mpl->image); if (node == NULL) error(mpl, "%s not defined", mpl->image); if (avl_get_node_type(node) != A_SET) error(mpl, "%s not a set", mpl->image); tab->u.in.set = (SET *)avl_get_node_link(node); if (tab->u.in.set->assign != NULL) error(mpl, "%s needs no data", mpl->image); if (tab->u.in.set->dim != 0) error(mpl, "%s must be a simple set", mpl->image); get_token(mpl /* */); if (mpl->token == T_INPUT) get_token(mpl /* <- */); else error(mpl, "delimiter <- missing where expected"); } else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else tab->u.in.set = NULL; /* parse field list */ tab->u.in.fld = last_fld = NULL; nflds = 0; if (mpl->token == T_LBRACKET) get_token(mpl /* [ */); else error(mpl, "field list missing where expected"); for (;;) { /* create field list entry */ fld = alloc(TABFLD); /* parse field name */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "field name missing where expected"); fld->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1); strcpy(fld->name, mpl->image); get_token(mpl /* */); /* add the entry to the end of the list */ fld->next = NULL; if (last_fld == NULL) tab->u.in.fld = fld; else last_fld->next = fld; last_fld = fld; nflds++; /* field name has been parsed */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_RBRACKET) break; else error(mpl, "syntax error in field list"); } /* check that the set dimen is equal to the number of fields */ if (tab->u.in.set != NULL && tab->u.in.set->dimen != nflds) error(mpl, "there must be %d field%s rather than %d", tab->u.in.set->dimen, tab->u.in.set->dimen == 1 ? "" : "s", nflds); get_token(mpl /* ] */); /* parse optional input list */ tab->u.in.list = last_in = NULL; while (mpl->token == T_COMMA) { get_token(mpl /* , */); /* create input list entry */ in = alloc(TABIN); /* parse parameter name */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "parameter name missing where expected"); node = avl_find_node(mpl->tree, mpl->image); if (node == NULL) error(mpl, "%s not defined", mpl->image); if (avl_get_node_type(node) != A_PARAMETER) error(mpl, "%s not a parameter", mpl->image); in->par = (PARAMETER *)avl_get_node_link(node); if (in->par->dim != nflds) error(mpl, "%s must have %d subscript%s rather than %d", mpl->image, nflds, nflds == 1 ? "" : "s", in->par->dim); if (in->par->assign != NULL) error(mpl, "%s needs no data", mpl->image); get_token(mpl /* */); /* parse optional field name */ if (mpl->token == T_TILDE) { get_token(mpl /* ~ */); /* parse field name */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "field name missing where expected"); xassert(strlen(mpl->image) < sizeof(name)); strcpy(name, mpl->image); get_token(mpl /* */); } else { /* field name is the same as the parameter name */ xassert(strlen(in->par->name) < sizeof(name)); strcpy(name, in->par->name); } /* assign field name */ in->name = dmp_get_atomv(mpl->pool, strlen(name)+1); strcpy(in->name, name); /* add the entry to the end of the list */ in->next = NULL; if (last_in == NULL) tab->u.in.list = in; else last_in->next = in; last_in = in; } goto end_of_table; output_table: /* parse output list */ tab->u.out.list = last_out = NULL; for (;;) { /* create output list entry */ out = alloc(TABOUT); /* parse expression */ if (mpl->token == T_COMMA || mpl->token == T_SEMICOLON) error(mpl, "expression missing where expected"); if (mpl->token == T_NAME) { xassert(strlen(mpl->image) < sizeof(name)); strcpy(name, mpl->image); } else name[0] = '\0'; out->code = expression_5(mpl); /* parse optional field name */ if (mpl->token == T_TILDE) { get_token(mpl /* ~ */); /* parse field name */ if (mpl->token == T_NAME) ; else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "field name missing where expected"); xassert(strlen(mpl->image) < sizeof(name)); strcpy(name, mpl->image); get_token(mpl /* */); } /* assign field name */ if (name[0] == '\0') error(mpl, "field name required"); out->name = dmp_get_atomv(mpl->pool, strlen(name)+1); strcpy(out->name, name); /* add the entry to the end of the list */ out->next = NULL; if (last_out == NULL) tab->u.out.list = out; else last_out->next = out; last_out = out; /* output item has been parsed */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else if (mpl->token == T_SEMICOLON) break; else error(mpl, "syntax error in output list"); } /* close the domain scope */ close_scope(mpl,tab->u.out.domain); end_of_table: /* the table statement must end with semicolon */ if (mpl->token != T_SEMICOLON) error(mpl, "syntax error in table statement"); get_token(mpl /* ; */); return tab; } #endif /*---------------------------------------------------------------------- -- solve_statement - parse solve statement. -- -- This routine parses solve statement using the syntax: -- -- ::= solve ; -- -- The solve statement can be used at most once. */ void *solve_statement(MPL *mpl) { xassert(is_keyword(mpl, "solve")); if (mpl->flag_s) error(mpl, "at most one solve statement allowed"); mpl->flag_s = 1; get_token(mpl /* solve */); /* semicolon must follow solve statement */ if (mpl->token != T_SEMICOLON) error(mpl, "syntax error in solve statement"); get_token(mpl /* ; */); return NULL; } /*---------------------------------------------------------------------- -- check_statement - parse check statement. -- -- This routine parses check statement using the syntax: -- -- ::= check : ; -- ::= -- ::= -- -- If is omitted, colon following it may also be omitted. */ CHECK *check_statement(MPL *mpl) { CHECK *chk; xassert(is_keyword(mpl, "check")); /* create check descriptor */ chk = alloc(CHECK); chk->domain = NULL; chk->code = NULL; get_token(mpl /* check */); /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { chk->domain = indexing_expression(mpl); #if 0 if (mpl->token != T_COLON) error(mpl, "colon missing where expected"); #endif } /* skip optional colon */ if (mpl->token == T_COLON) get_token(mpl /* : */); /* parse logical expression */ chk->code = expression_13(mpl); if (chk->code->type != A_LOGICAL) error(mpl, "expression has invalid type"); xassert(chk->code->dim == 0); /* close the domain scope */ if (chk->domain != NULL) close_scope(mpl, chk->domain); /* the check statement has been completely parsed */ if (mpl->token != T_SEMICOLON) error(mpl, "syntax error in check statement"); get_token(mpl /* ; */); return chk; } #if 1 /* 15/V-2010 */ /*---------------------------------------------------------------------- -- display_statement - parse display statement. -- -- This routine parses display statement using the syntax: -- -- ::= display : ; -- ::= display ; -- ::= -- ::= -- ::= -- ::= , -- ::= -- ::= -- ::= [ ] -- ::= -- ::= [ ] -- ::= -- ::= [ ] -- ::= -- ::= [ ] -- ::= */ DISPLAY *display_statement(MPL *mpl) { DISPLAY *dpy; DISPLAY1 *entry, *last_entry; xassert(is_keyword(mpl, "display")); /* create display descriptor */ dpy = alloc(DISPLAY); dpy->domain = NULL; dpy->list = last_entry = NULL; get_token(mpl /* display */); /* parse optional indexing expression */ if (mpl->token == T_LBRACE) dpy->domain = indexing_expression(mpl); /* skip optional colon */ if (mpl->token == T_COLON) get_token(mpl /* : */); /* parse display list */ for (;;) { /* create new display entry */ entry = alloc(DISPLAY1); entry->type = 0; entry->next = NULL; /* and append it to the display list */ if (dpy->list == NULL) dpy->list = entry; else last_entry->next = entry; last_entry = entry; /* parse display entry */ if (mpl->token == T_NAME) { AVLNODE *node; int next_token; get_token(mpl /* */); next_token = mpl->token; unget_token(mpl); if (!(next_token == T_COMMA || next_token == T_SEMICOLON)) { /* symbolic name begins expression */ goto expr; } /* display entry is dummy index or model object */ node = avl_find_node(mpl->tree, mpl->image); if (node == NULL) error(mpl, "%s not defined", mpl->image); entry->type = avl_get_node_type(node); switch (avl_get_node_type(node)) { case A_INDEX: entry->u.slot = (DOMAIN_SLOT *)avl_get_node_link(node); break; case A_SET: entry->u.set = (SET *)avl_get_node_link(node); break; case A_PARAMETER: entry->u.par = (PARAMETER *)avl_get_node_link(node); break; case A_VARIABLE: entry->u.var = (VARIABLE *)avl_get_node_link(node); if (!mpl->flag_s) error(mpl, "invalid reference to variable %s above" " solve statement", entry->u.var->name); break; case A_CONSTRAINT: entry->u.con = (CONSTRAINT *)avl_get_node_link(node); if (!mpl->flag_s) error(mpl, "invalid reference to %s %s above solve" " statement", entry->u.con->type == A_CONSTRAINT ? "constraint" : "objective", entry->u.con->name); break; default: xassert(node != node); } get_token(mpl /* */); } else expr: { /* display entry is expression */ entry->type = A_EXPRESSION; entry->u.code = expression_13(mpl); } /* check a token that follows the entry parsed */ if (mpl->token == T_COMMA) get_token(mpl /* , */); else break; } /* close the domain scope */ if (dpy->domain != NULL) close_scope(mpl, dpy->domain); /* the display statement has been completely parsed */ if (mpl->token != T_SEMICOLON) error(mpl, "syntax error in display statement"); get_token(mpl /* ; */); return dpy; } #endif /*---------------------------------------------------------------------- -- printf_statement - parse printf statement. -- -- This routine parses print statement using the syntax: -- -- ::= ; -- ::= > ; -- ::= >> ; -- ::= printf : -- ::= printf -- ::= -- ::= -- ::= -- ::= -- ::= , -- ::= -- ::= */ PRINTF *printf_statement(MPL *mpl) { PRINTF *prt; PRINTF1 *entry, *last_entry; xassert(is_keyword(mpl, "printf")); /* create printf descriptor */ prt = alloc(PRINTF); prt->domain = NULL; prt->fmt = NULL; prt->list = last_entry = NULL; get_token(mpl /* printf */); /* parse optional indexing expression */ if (mpl->token == T_LBRACE) { prt->domain = indexing_expression(mpl); #if 0 if (mpl->token != T_COLON) error(mpl, "colon missing where expected"); #endif } /* skip optional colon */ if (mpl->token == T_COLON) get_token(mpl /* : */); /* parse expression for format string */ prt->fmt = expression_5(mpl); /* convert it to symbolic type, if necessary */ if (prt->fmt->type == A_NUMERIC) prt->fmt = make_unary(mpl, O_CVTSYM, prt->fmt, A_SYMBOLIC, 0); /* check that now the expression is of symbolic type */ if (prt->fmt->type != A_SYMBOLIC) error(mpl, "format expression has invalid type"); /* parse printf list */ while (mpl->token == T_COMMA) { get_token(mpl /* , */); /* create new printf entry */ entry = alloc(PRINTF1); entry->code = NULL; entry->next = NULL; /* and append it to the printf list */ if (prt->list == NULL) prt->list = entry; else last_entry->next = entry; last_entry = entry; /* parse printf entry */ entry->code = expression_9(mpl); if (!(entry->code->type == A_NUMERIC || entry->code->type == A_SYMBOLIC || entry->code->type == A_LOGICAL)) error(mpl, "only numeric, symbolic, or logical expression a" "llowed"); } /* close the domain scope */ if (prt->domain != NULL) close_scope(mpl, prt->domain); #if 1 /* 14/VII-2006 */ /* parse optional redirection */ prt->fname = NULL, prt->app = 0; if (mpl->token == T_GT || mpl->token == T_APPEND) { prt->app = (mpl->token == T_APPEND); get_token(mpl /* > or >> */); /* parse expression for file name string */ prt->fname = expression_5(mpl); /* convert it to symbolic type, if necessary */ if (prt->fname->type == A_NUMERIC) prt->fname = make_unary(mpl, O_CVTSYM, prt->fname, A_SYMBOLIC, 0); /* check that now the expression is of symbolic type */ if (prt->fname->type != A_SYMBOLIC) error(mpl, "file name expression has invalid type"); } #endif /* the printf statement has been completely parsed */ if (mpl->token != T_SEMICOLON) error(mpl, "syntax error in printf statement"); get_token(mpl /* ; */); return prt; } /*---------------------------------------------------------------------- -- for_statement - parse for statement. -- -- This routine parses for statement using the syntax: -- -- ::= for -- ::= for { } -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= */ FOR *for_statement(MPL *mpl) { FOR *fur; STATEMENT *stmt, *last_stmt; xassert(is_keyword(mpl, "for")); /* create for descriptor */ fur = alloc(FOR); fur->domain = NULL; fur->list = last_stmt = NULL; get_token(mpl /* for */); /* parse indexing expression */ if (mpl->token != T_LBRACE) error(mpl, "indexing expression missing where expected"); fur->domain = indexing_expression(mpl); /* skip optional colon */ if (mpl->token == T_COLON) get_token(mpl /* : */); /* parse for statement body */ if (mpl->token != T_LBRACE) { /* parse simple statement */ fur->list = simple_statement(mpl, 1); } else { /* parse compound statement */ get_token(mpl /* { */); while (mpl->token != T_RBRACE) { /* parse statement */ stmt = simple_statement(mpl, 1); /* and append it to the end of the statement list */ if (last_stmt == NULL) fur->list = stmt; else last_stmt->next = stmt; last_stmt = stmt; } get_token(mpl /* } */); } /* close the domain scope */ xassert(fur->domain != NULL); close_scope(mpl, fur->domain); /* the for statement has been completely parsed */ return fur; } /*---------------------------------------------------------------------- -- end_statement - parse end statement. -- -- This routine parses end statement using the syntax: -- -- ::= end ; */ void end_statement(MPL *mpl) { if (!mpl->flag_d && is_keyword(mpl, "end") || mpl->flag_d && is_literal(mpl, "end")) { get_token(mpl /* end */); if (mpl->token == T_SEMICOLON) get_token(mpl /* ; */); else warning(mpl, "no semicolon following end statement; missing" " semicolon inserted"); } else warning(mpl, "unexpected end of file; missing end statement in" "serted"); if (mpl->token != T_EOF) warning(mpl, "some text detected beyond end statement; text ig" "nored"); return; } /*---------------------------------------------------------------------- -- simple_statement - parse simple statement. -- -- This routine parses simple statement using the syntax: -- -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= -- ::= -- -- If the flag spec is set, some statements cannot be used. */ STATEMENT *simple_statement(MPL *mpl, int spec) { STATEMENT *stmt; stmt = alloc(STATEMENT); stmt->line = mpl->line; stmt->next = NULL; if (is_keyword(mpl, "set")) { if (spec) error(mpl, "set statement not allowed here"); stmt->type = A_SET; stmt->u.set = set_statement(mpl); } else if (is_keyword(mpl, "param")) { if (spec) error(mpl, "parameter statement not allowed here"); stmt->type = A_PARAMETER; stmt->u.par = parameter_statement(mpl); } else if (is_keyword(mpl, "var")) { if (spec) error(mpl, "variable statement not allowed here"); stmt->type = A_VARIABLE; stmt->u.var = variable_statement(mpl); } else if (is_keyword(mpl, "subject") || is_keyword(mpl, "subj") || mpl->token == T_SPTP) { if (spec) error(mpl, "constraint statement not allowed here"); stmt->type = A_CONSTRAINT; stmt->u.con = constraint_statement(mpl); } else if (is_keyword(mpl, "minimize") || is_keyword(mpl, "maximize")) { if (spec) error(mpl, "objective statement not allowed here"); stmt->type = A_CONSTRAINT; stmt->u.con = objective_statement(mpl); } #if 1 /* 11/II-2008 */ else if (is_keyword(mpl, "table")) { if (spec) error(mpl, "table statement not allowed here"); stmt->type = A_TABLE; stmt->u.tab = table_statement(mpl); } #endif else if (is_keyword(mpl, "solve")) { if (spec) error(mpl, "solve statement not allowed here"); stmt->type = A_SOLVE; stmt->u.slv = solve_statement(mpl); } else if (is_keyword(mpl, "check")) { stmt->type = A_CHECK; stmt->u.chk = check_statement(mpl); } else if (is_keyword(mpl, "display")) { stmt->type = A_DISPLAY; stmt->u.dpy = display_statement(mpl); } else if (is_keyword(mpl, "printf")) { stmt->type = A_PRINTF; stmt->u.prt = printf_statement(mpl); } else if (is_keyword(mpl, "for")) { stmt->type = A_FOR; stmt->u.fur = for_statement(mpl); } else if (mpl->token == T_NAME) { if (spec) error(mpl, "constraint statement not allowed here"); stmt->type = A_CONSTRAINT; stmt->u.con = constraint_statement(mpl); } else if (is_reserved(mpl)) error(mpl, "invalid use of reserved keyword %s", mpl->image); else error(mpl, "syntax error in model section"); return stmt; } /*---------------------------------------------------------------------- -- model_section - parse model section. -- -- This routine parses model section using the syntax: -- -- ::= -- ::= -- -- Parsing model section is terminated by either the keyword 'data', or -- the keyword 'end', or the end of file. */ void model_section(MPL *mpl) { STATEMENT *stmt, *last_stmt; xassert(mpl->model == NULL); last_stmt = NULL; while (!(mpl->token == T_EOF || is_keyword(mpl, "data") || is_keyword(mpl, "end"))) { /* parse statement */ stmt = simple_statement(mpl, 0); /* and append it to the end of the statement list */ if (last_stmt == NULL) mpl->model = stmt; else last_stmt->next = stmt; last_stmt = stmt; } return; } /* eof */ igraph/src/glpenv07.c0000644000176000001440000003735112325527073014170 0ustar ripleyusers/* glpenv07.c (stream input/output) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #ifdef HAVE_CONFIG_H #include #endif #include "glpenv.h" /*********************************************************************** * NAME * * lib_err_msg - save error message string * * SYNOPSIS * * #include "glpenv.h" * void lib_err_msg(const char *msg); * * DESCRIPTION * * The routine lib_err_msg saves an error message string specified by * the parameter msg. The message is obtained by some library routines * with a call to strerror(errno). */ void lib_err_msg(const char *msg) { ENV *env = get_env_ptr(); int len = strlen(msg); if (len >= IOERR_MSG_SIZE) len = IOERR_MSG_SIZE - 1; memcpy(env->ioerr_msg, msg, len); if (len > 0 && env->ioerr_msg[len-1] == '\n') len--; env->ioerr_msg[len] = '\0'; return; } /*********************************************************************** * NAME * * xerrmsg - retrieve error message string * * SYNOPSIS * * #include "glpenv.h" * const char *xerrmsg(void); * * RETURNS * * The routine xerrmsg returns a pointer to an error message string * previously set by some library routine to indicate an error. */ const char *xerrmsg(void) { ENV *env = get_env_ptr(); return env->ioerr_msg; } /*********************************************************************** * NAME * * xfopen - open a stream * * SYNOPSIS * * #include "glpenv.h" * XFILE *xfopen(const char *fname, const char *mode); * * DESCRIPTION * * The routine xfopen opens the file whose name is a string pointed to * by fname and associates a stream with it. * * The parameter mode points to a string, which indicates the open mode * and should be one of the following: * * "r" open text file for reading; * "w" truncate to zero length or create text file for writing; * "rb" open binary file for reading; * "wb" truncate to zero length or create binary file for writing. * * RETURNS * * The routine xfopen returns a pointer to the object controlling the * stream. If the open operation fails, xfopen returns NULL. */ static void *c_fopen(const char *fname, const char *mode); static void *z_fopen(const char *fname, const char *mode); static int is_gz_file(const char *fname) { char *ext = strrchr(fname, '.'); return ext != NULL && strcmp(ext, ".gz") == 0; } XFILE *xfopen(const char *fname, const char *mode) { ENV *env = get_env_ptr(); XFILE *fp; int type; void *fh; if (!is_gz_file(fname)) { type = FH_FILE; fh = c_fopen(fname, mode); } else { type = FH_ZLIB; fh = z_fopen(fname, mode); } if (fh == NULL) { fp = NULL; goto done; } fp = xmalloc(sizeof(XFILE)); fp->type = type; fp->fh = fh; fp->prev = NULL; fp->next = env->file_ptr; if (fp->next != NULL) fp->next->prev = fp; env->file_ptr = fp; done: return fp; } /*********************************************************************** * NAME * * xfgetc - read character from the stream * * SYNOPSIS * * #include "glpenv.h" * int xfgetc(XFILE *fp); * * DESCRIPTION * * If the end-of-file indicator for the input stream pointed to by fp * is not set and a next character is present, the routine xfgetc * obtains that character as an unsigned char converted to an int and * advances the associated file position indicator for the stream (if * defined). * * RETURNS * * If the end-of-file indicator for the stream is set, or if the * stream is at end-of-file, the end-of-file indicator for the stream * is set and the routine xfgetc returns XEOF. Otherwise, the routine * xfgetc returns the next character from the input stream pointed to * by fp. If a read error occurs, the error indicator for the stream is * set and the xfgetc routine returns XEOF. * * Note: An end-of-file and a read error can be distinguished by use of * the routines xfeof and xferror. */ static int c_fgetc(void *fh); static int z_fgetc(void *fh); int xfgetc(XFILE *fp) { int c; switch (fp->type) { case FH_FILE: c = c_fgetc(fp->fh); break; case FH_ZLIB: c = z_fgetc(fp->fh); break; default: xassert(fp != fp); } return c; } /*********************************************************************** * NAME * * xfputc - write character to the stream * * SYNOPSIS * * #include "glpenv.h" * int xfputc(int c, XFILE *fp); * * DESCRIPTION * * The routine xfputc writes the character specified by c (converted * to an unsigned char) to the output stream pointed to by fp, at the * position indicated by the associated file position indicator (if * defined), and advances the indicator appropriately. * * RETURNS * * The routine xfputc returns the character written. If a write error * occurs, the error indicator for the stream is set and xfputc returns * XEOF. */ static int c_fputc(int c, void *fh); static int z_fputc(int c, void *fh); int xfputc(int c, XFILE *fp) { switch (fp->type) { case FH_FILE: c = c_fputc(c, fp->fh); break; case FH_ZLIB: c = z_fputc(c, fp->fh); break; default: xassert(fp != fp); } return c; } /*********************************************************************** * NAME * * xferror - test error indicator for the stream * * SYNOPSIS * * #include "glpenv.h" * int xferror(XFILE *fp); * * DESCRIPTION * * The routine xferror tests the error indicator for the stream * pointed to by fp. * * RETURNS * * The routine xferror returns non-zero if and only if the error * indicator is set for the stream. */ static int c_ferror(void *fh); static int z_ferror(void *fh); int xferror(XFILE *fp) { int ret; switch (fp->type) { case FH_FILE: ret = c_ferror(fp->fh); break; case FH_ZLIB: ret = z_ferror(fp->fh); break; default: xassert(fp != fp); } return ret; } /*********************************************************************** * NAME * * xfeof - test end-of-file indicator for the stream * * SYNOPSIS * * #include "glpenv.h" * int xfeof(XFILE *fp); * * DESCRIPTION * * The routine xfeof tests the end-of-file indicator for the stream * pointed to by fp. * * RETURNS * * The routine xfeof returns non-zero if and only if the end-of-file * indicator is set for the stream. */ static int c_feof(void *fh); static int z_feof(void *fh); int xfeof(XFILE *fp) { int ret; switch (fp->type) { case FH_FILE: ret = c_feof(fp->fh); break; case FH_ZLIB: ret = z_feof(fp->fh); break; default: xassert(fp != fp); } return ret; } int xfprintf(XFILE *file, const char *fmt, ...) { ENV *env = get_env_ptr(); int cnt, j; va_list arg; va_start(arg, fmt); cnt = vsprintf(env->term_buf, fmt, arg); va_end(arg); for (j = 0; j < cnt; j++) { if (xfputc(env->term_buf[j], file) < 0) { cnt = -1; break; } } return cnt; } /*********************************************************************** * NAME * * xfflush - flush the stream * * SYNOPSIS * * #include "glpenv.h" * int xfflush(XFILE *fp); * * DESCRIPTION * * The routine xfflush causes any unwritten data for the output stream * pointed to by fp to be written to the associated file. * * RETURNS * * The routine xfflush returns zero if the stream was successfully * flushed. Otherwise, xfflush sets the error indicator for the stream * and returns XEOF. */ static int c_fflush(void *fh); static int z_fflush(void *fh); int xfflush(XFILE *fp) { int ret; switch (fp->type) { case FH_FILE: ret = c_fflush(fp->fh); break; case FH_ZLIB: ret = z_fflush(fp->fh); break; default: xassert(fp != fp); } return ret; } /*********************************************************************** * NAME * * xfclose - close the stream * * SYNOPSIS * * #include "glpenv.h" * int xfclose(XFILE *fp); * * DESCRIPTION * * A successful call to the routine xfclose causes the stream pointed * to by fp to be flushed and the associated file to be closed. Whether * or not the call succeeds, the stream is disassociated from the file. * * RETURNS * * The routine xfclose returns zero if the stream was successfully * closed, or XEOF if any errors were detected. */ static int c_fclose(void *fh); static int z_fclose(void *fh); int xfclose(XFILE *fp) { ENV *env = get_env_ptr(); int ret; switch (fp->type) { case FH_FILE: ret = c_fclose(fp->fh); break; case FH_ZLIB: ret = z_fclose(fp->fh); break; default: xassert(fp != fp); } fp->type = 0xF00BAD; if (fp->prev == NULL) env->file_ptr = fp->next; else fp->prev->next = fp->next; if (fp->next == NULL) ; else fp->next->prev = fp->prev; xfree(fp); return ret; } /*********************************************************************** * The following routines implement stream input/output based on the * standard C streams. */ static void *c_fopen(const char *fname, const char *mode) { FILE *fh; /* if (strcmp(fname, "/dev/stdin") == 0) */ /* fh = stdin; */ /* else if (strcmp(fname, "/dev/stdout") == 0) */ /* fh = stdout; */ /* else if (strcmp(fname, "/dev/stderr") == 0) */ /* fh = stderr; */ /* else */ fh = fopen(fname, mode); if (fh == NULL) lib_err_msg(strerror(errno)); return fh; } static int c_fgetc(void *_fh) { FILE *fh = _fh; int c; if (ferror(fh) || feof(fh)) { c = XEOF; goto done; } c = fgetc(fh); if (ferror(fh)) { lib_err_msg(strerror(errno)); c = XEOF; } else if (feof(fh)) c = XEOF; else xassert(0x00 <= c && c <= 0xFF); done: return c; } static int c_fputc(int c, void *_fh) { FILE *fh = _fh; if (ferror(fh)) { c = XEOF; goto done; } c = (unsigned char)c; fputc(c, fh); if (ferror(fh)) { lib_err_msg(strerror(errno)); c = XEOF; } done: return c; } static int c_ferror(void *_fh) { FILE *fh = _fh; return ferror(fh); } static int c_feof(void *_fh) { FILE *fh = _fh; return feof(fh); } static int c_fflush(void *_fh) { FILE *fh = _fh; int ret; ret = fflush(fh); if (ret != 0) { lib_err_msg(strerror(errno)); ret = XEOF; } return ret; } static int c_fclose(void *_fh) { FILE *fh = _fh; int ret; /* if (fh == stdin) */ /* ret = 0; */ /* else if (fh == stdout || fh == stderr) */ /* fflush(fh), ret = 0; */ /* else */ ret = fclose(fh); if (ret != 0) { lib_err_msg(strerror(errno)); ret = XEOF; } return ret; } /*********************************************************************** * The following routines implement stream input/output based on the * zlib library, which provides processing .gz files "on the fly". */ #ifndef HAVE_ZLIB static void *z_fopen(const char *fname, const char *mode) { xassert(fname == fname); xassert(mode == mode); lib_err_msg("Compressed files not supported"); return NULL; } static int z_fgetc(void *fh) { xassert(fh != fh); return 0; } static int z_fputc(int c, void *fh) { xassert(c != c); xassert(fh != fh); return 0; } static int z_ferror(void *fh) { xassert(fh != fh); return 0; } static int z_feof(void *fh) { xassert(fh != fh); return 0; } static int z_fflush(void *fh) { xassert(fh != fh); return 0; } static int z_fclose(void *fh) { xassert(fh != fh); return 0; } #else #include struct z_file { /* .gz file handle */ gzFile file; /* pointer to .gz stream */ int err; /* i/o error indicator */ int eof; /* end-of-file indicator */ }; static void *z_fopen(const char *fname, const char *mode) { struct z_file *fh; gzFile file; if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) mode = "rb"; else if (strcmp(mode, "w") == 0 || strcmp(mode, "wb") == 0) mode = "wb"; else { lib_err_msg("Invalid open mode"); fh = NULL; goto done; } file = gzopen(fname, mode); if (file == NULL) { lib_err_msg(strerror(errno)); fh = NULL; goto done; } fh = xmalloc(sizeof(struct z_file)); fh->file = file; fh->err = fh->eof = 0; done: return fh; } static int z_fgetc(void *_fh) { struct z_file *fh = _fh; int c; if (fh->err || fh->eof) { c = XEOF; goto done; } c = gzgetc(fh->file); if (c < 0) { int errnum; const char *msg; msg = gzerror(fh->file, &errnum); if (errnum == Z_STREAM_END) fh->eof = 1; else if (errnum == Z_ERRNO) { fh->err = 1; lib_err_msg(strerror(errno)); } else { fh->err = 1; lib_err_msg(msg); } c = XEOF; } else xassert(0x00 <= c && c <= 0xFF); done: return c; } static int z_fputc(int c, void *_fh) { struct z_file *fh = _fh; if (fh->err) { c = XEOF; goto done; } c = (unsigned char)c; if (gzputc(fh->file, c) < 0) { int errnum; const char *msg; fh->err = 1; msg = gzerror(fh->file, &errnum); if (errnum == Z_ERRNO) lib_err_msg(strerror(errno)); else lib_err_msg(msg); c = XEOF; } done: return c; } static int z_ferror(void *_fh) { struct z_file *fh = _fh; return fh->err; } static int z_feof(void *_fh) { struct z_file *fh = _fh; return fh->eof; } static int z_fflush(void *_fh) { struct z_file *fh = _fh; int ret; ret = gzflush(fh->file, Z_FINISH); if (ret == Z_OK) ret = 0; else { int errnum; const char *msg; fh->err = 1; msg = gzerror(fh->file, &errnum); if (errnum == Z_ERRNO) lib_err_msg(strerror(errno)); else lib_err_msg(msg); ret = XEOF; } return ret; } static int z_fclose(void *_fh) { struct z_file *fh = _fh; gzclose(fh->file); xfree(fh); return 0; } #endif /* eof */ igraph/src/interrupt.c0000644000176000001440000000276412325527073014562 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_interrupt.h" #include "config.h" #include #include #include IGRAPH_THREAD_LOCAL igraph_interruption_handler_t *igraph_i_interruption_handler=0; int igraph_allow_interruption(void* data) { if (igraph_i_interruption_handler) { return igraph_i_interruption_handler(data); } return IGRAPH_SUCCESS; } igraph_interruption_handler_t * igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler) { igraph_interruption_handler_t * previous_handler = igraph_i_interruption_handler; igraph_i_interruption_handler = new_handler; return previous_handler; } igraph/src/igraph_interface.h0000644000176000001440000000746212325527073016025 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_INTERFACE_H #define IGRAPH_INTERFACE_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_iterators.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Interface */ /* -------------------------------------------------- */ int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr); int igraph_destroy(igraph_t *graph); int igraph_copy(igraph_t *to, const igraph_t *from); int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, void *attr); int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr); int igraph_delete_edges(igraph_t *graph, igraph_es_t edges); int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, igraph_vector_t *idx, igraph_vector_t *invidx); igraph_integer_t igraph_vcount(const igraph_t *graph); igraph_integer_t igraph_ecount(const igraph_t *graph); int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t vid, igraph_neimode_t mode); igraph_bool_t igraph_is_directed(const igraph_t *graph); int igraph_degree(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); int igraph_edge(const igraph_t *graph, igraph_integer_t eid, igraph_integer_t *from, igraph_integer_t *to); int igraph_edges(const igraph_t *graph, igraph_es_t eids, igraph_vector_t *edges); int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed, igraph_bool_t error); int igraph_get_eids(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, const igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t error); int igraph_get_eids_multi(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, const igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t error); int igraph_adjacent(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid, igraph_neimode_t mode); /* deprecated */ int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid, igraph_neimode_t mode); #define IGRAPH_FROM(g,e) ((igraph_integer_t)(VECTOR((g)->from)[(long int)(e)])) #define IGRAPH_TO(g,e) ((igraph_integer_t)(VECTOR((g)->to) [(long int)(e)])) #define IGRAPH_OTHER(g,e,v) \ ((igraph_integer_t)(IGRAPH_TO(g,(e))==(v) ? IGRAPH_FROM((g),(e)) : IGRAPH_TO((g),(e)))) __END_DECLS #endif igraph/src/hrg_dendro.h0000644000176000001440000002603212325527073014640 0ustar ripleyusers/* -*- mode: C++ -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // **************************************************************************************************** // *** COPYRIGHT NOTICE ******************************************************************************* // dendro_eq.h - hierarchical random graph (hrg) data structure // Copyright (C) 2006-2008 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // **************************************************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark E.J. Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute // Created : 19 April 2006 // Modified : 19 May 2007 // : 19 May 2008 (cleaned up for public consumption) // // **************************************************************************************************** // // Maximum likelihood dendrogram data structure. This is the heart of the HRG algorithm: all // manipulations are done here and all data is stored here. The data structure uses the separate // graph data structure to store the basic adjacency information (in a dangerously mutable way). // // Note: This version (dendro_eq.h) differs from other versions because it includes methods for // doing the consensus dendrogram calculation. // // **************************************************************************************************** #ifndef IGRAPH_HRG_DENDRO #define IGRAPH_HRG_DENDRO #include #include #include #include #include "hrg_graph.h" #include "hrg_rbtree.h" #include "hrg_splittree_eq.h" #include "igraph_hrg.h" using namespace std; using namespace fitHRG; namespace fitHRG { // *********************************************************************** // ******** Basic Structures ********************************************* #ifndef IGRAPH_HRG_LIST #define IGRAPH_HRG_LIST class list { public: int x; // stored elementd in linked-list list* next; // pointer to next elementd list::list(): x(-1), next(0) { } list::~list() { } }; #endif enum {DENDRO, GRAPH, LEFT, RIGHT}; struct block { double x; int y; }; struct ipair { int x; int y; short int t; string sp; }; struct child { int index; short int type; child* next; }; // *********************************************************************** // ******** Cnode Class ************************************************** #ifndef IGRAPH_HRG_CNODE #define IGRAPH_HRG_CNODE class cnode { public: int index; // array index of this node int degree; // number of children in list int parent; // index of parent node double weight; // sampled posterior weight child* children; // list of children (and their types) child* lastChild; // pointer to last child in list cnode(): index(-1), degree(0), parent(-1), weight(0.0), children(0), lastChild(0) { } ~cnode() { child *curr, *prev; curr = children; while (curr != NULL) { prev = curr; curr = curr->next; delete prev; prev = NULL; } lastChild = NULL; } }; #endif // *********************************************************************** // ******** Split Class ************************************************** class split { public: string s; // partition assignment of leaf vertices split(): s("") { } ~split() { } void initializeSplit(const int n) { s = ""; for (int i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_MOTIFS_H #define IGRAPH_MOTIFS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_iterators.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Graph motifs */ /* -------------------------------------------------- */ /** * \typedef igraph_motifs_handler_t * Callback type for \c igraph_motifs_randesu_callback * * \ref igraph_motifs_randesu_callback() calls a specified callback * function whenever a new motif is found during a motif search. This * callback function must be of type \c igraph_motifs_handler_t. It has * the following arguments: * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vids The IDs of the vertices in the motif that has just been * found. This vector is owned by the motif search algorithm, so do not * modify or destroy it; make a copy of it if you need it later. * \param isoclass The isomorphism class of the motif that has just been * found. Use \ref igraph_isoclass or \ref igraph_isoclass_subgraph to find * out which isomorphism class belongs to a given motif. * \param extra The extra argument that was passed to \ref * igraph_motifs_randesu_callback(). * \return A logical value, if TRUE (=non-zero), that is interpreted * as a request to stop the motif search and return to the caller. * * \sa \ref igraph_motifs_randesu_callback() */ typedef igraph_bool_t igraph_motifs_handler_t(const igraph_t *graph, igraph_vector_t *vids, int isoclass, void* extra); int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, int size, const igraph_vector_t *cut_prob); int igraph_motifs_randesu_callback(const igraph_t *graph, int size, const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, void* extra); int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, int size, const igraph_vector_t *cut_prob, igraph_integer_t sample_size, const igraph_vector_t *sample); int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, int size, const igraph_vector_t *cut_prob); int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, igraph_integer_t *asym, igraph_integer_t *null); int igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); int igraph_triad_census_24(const igraph_t *graph, igraph_integer_t *res2, igraph_integer_t *res4); int igraph_adjacent_triangles(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids); __END_DECLS #endif igraph/src/glpnet02.c0000644000176000001440000002443612325527073014161 0ustar ripleyusers/* glpnet02.c (permutations to block triangular form) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * This code is the result of translation of the Fortran subroutines * MC13D and MC13E associated with the following paper: * * I.S.Duff, J.K.Reid, Algorithm 529: Permutations to block triangular * form, ACM Trans. on Math. Softw. 4 (1978), 189-192. * * Use of ACM Algorithms is subject to the ACM Software Copyright and * License Agreement. See . * * The translation was made by Andrew Makhorin . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpnet.h" /*********************************************************************** * NAME * * mc13d - permutations to block triangular form * * SYNOPSIS * * #include "glpnet.h" * int mc13d(int n, const int icn[], const int ip[], const int lenr[], * int ior[], int ib[], int lowl[], int numb[], int prev[]); * * DESCRIPTION * * Given the column numbers of the nonzeros in each row of the sparse * matrix, the routine mc13d finds a symmetric permutation that makes * the matrix block lower triangular. * * INPUT PARAMETERS * * n order of the matrix. * * icn array containing the column indices of the non-zeros. Those * belonging to a single row must be contiguous but the ordering * of column indices within each row is unimportant and wasted * space between rows is permitted. * * ip ip[i], i = 1,2,...,n, is the position in array icn of the * first column index of a non-zero in row i. * * lenr lenr[i], i = 1,2,...,n, is the number of non-zeros in row i. * * OUTPUT PARAMETERS * * ior ior[i], i = 1,2,...,n, gives the position on the original * ordering of the row or column which is in position i in the * permuted form. * * ib ib[i], i = 1,2,...,num, is the row number in the permuted * matrix of the beginning of block i, 1 <= num <= n. * * WORKING ARRAYS * * arp working array of length [1+n], where arp[0] is not used. * arp[i] is one less than the number of unsearched edges leaving * node i. At the end of the algorithm it is set to a permutation * which puts the matrix in block lower triangular form. * * ib working array of length [1+n], where ib[0] is not used. * ib[i] is the position in the ordering of the start of the ith * block. ib[n+1-i] holds the node number of the ith node on the * stack. * * lowl working array of length [1+n], where lowl[0] is not used. * lowl[i] is the smallest stack position of any node to which a * path from node i has been found. It is set to n+1 when node i * is removed from the stack. * * numb working array of length [1+n], where numb[0] is not used. * numb[i] is the position of node i in the stack if it is on it, * is the permuted order of node i for those nodes whose final * position has been found and is otherwise zero. * * prev working array of length [1+n], where prev[0] is not used. * prev[i] is the node at the end of the path when node i was * placed on the stack. * * RETURNS * * The routine mc13d returns num, the number of blocks found. */ int mc13d(int n, const int icn[], const int ip[], const int lenr[], int ior[], int ib[], int lowl[], int numb[], int prev[]) { int *arp = ior; int dummy, i, i1, i2, icnt, ii, isn, ist, ist1, iv, iw, j, lcnt, nnm1, num, stp; /* icnt is the number of nodes whose positions in final ordering have been found. */ icnt = 0; /* num is the number of blocks that have been found. */ num = 0; nnm1 = n + n - 1; /* Initialization of arrays. */ for (j = 1; j <= n; j++) { numb[j] = 0; arp[j] = lenr[j] - 1; } for (isn = 1; isn <= n; isn++) { /* Look for a starting node. */ if (numb[isn] != 0) continue; iv = isn; /* ist is the number of nodes on the stack ... it is the stack pointer. */ ist = 1; /* Put node iv at beginning of stack. */ lowl[iv] = numb[iv] = 1; ib[n] = iv; /* The body of this loop puts a new node on the stack or backtracks. */ for (dummy = 1; dummy <= nnm1; dummy++) { i1 = arp[iv]; /* Have all edges leaving node iv been searched? */ if (i1 >= 0) { i2 = ip[iv] + lenr[iv] - 1; i1 = i2 - i1; /* Look at edges leaving node iv until one enters a new node or all edges are exhausted. */ for (ii = i1; ii <= i2; ii++) { iw = icn[ii]; /* Has node iw been on stack already? */ if (numb[iw] == 0) goto L70; /* Update value of lowl[iv] if necessary. */ if (lowl[iw] < lowl[iv]) lowl[iv] = lowl[iw]; } /* There are no more edges leaving node iv. */ arp[iv] = -1; } /* Is node iv the root of a block? */ if (lowl[iv] < numb[iv]) goto L60; /* Order nodes in a block. */ num++; ist1 = n + 1 - ist; lcnt = icnt + 1; /* Peel block off the top of the stack starting at the top and working down to the root of the block. */ for (stp = ist1; stp <= n; stp++) { iw = ib[stp]; lowl[iw] = n + 1; numb[iw] = ++icnt; if (iw == iv) break; } ist = n - stp; ib[num] = lcnt; /* Are there any nodes left on the stack? */ if (ist != 0) goto L60; /* Have all the nodes been ordered? */ if (icnt < n) break; goto L100; L60: /* Backtrack to previous node on path. */ iw = iv; iv = prev[iv]; /* Update value of lowl[iv] if necessary. */ if (lowl[iw] < lowl[iv]) lowl[iv] = lowl[iw]; continue; L70: /* Put new node on the stack. */ arp[iv] = i2 - ii - 1; prev[iw] = iv; iv = iw; lowl[iv] = numb[iv] = ++ist; ib[n+1-ist] = iv; } } L100: /* Put permutation in the required form. */ for (i = 1; i <= n; i++) arp[numb[i]] = i; return num; } /**********************************************************************/ #if 0 #include "glplib.h" void test(int n, int ipp); int main(void) { /* test program for routine mc13d */ test( 1, 0); test( 2, 1); test( 2, 2); test( 3, 3); test( 4, 4); test( 5, 10); test(10, 10); test(10, 20); test(20, 20); test(20, 50); test(50, 50); test(50, 200); return 0; } void fa01bs(int max, int *nrand); void setup(int n, char a[1+50][1+50], int ip[], int icn[], int lenr[]); void test(int n, int ipp) { int ip[1+50], icn[1+1000], ior[1+50], ib[1+51], iw[1+150], lenr[1+50]; char a[1+50][1+50], hold[1+100]; int i, ii, iblock, ij, index, j, jblock, jj, k9, num; xprintf("\n\n\nMatrix is of order %d and has %d off-diagonal non-" "zeros\n", n, ipp); for (j = 1; j <= n; j++) { for (i = 1; i <= n; i++) a[i][j] = 0; a[j][j] = 1; } for (k9 = 1; k9 <= ipp; k9++) { /* these statements should be replaced by calls to your favorite random number generator to place two pseudo-random numbers between 1 and n in the variables i and j */ for (;;) { fa01bs(n, &i); fa01bs(n, &j); if (!a[i][j]) break; } a[i][j] = 1; } /* setup converts matrix a[i,j] to required sparsity-oriented storage format */ setup(n, a, ip, icn, lenr); num = mc13d(n, icn, ip, lenr, ior, ib, &iw[0], &iw[n], &iw[n+n]); /* output reordered matrix with blocking to improve clarity */ xprintf("\nThe reordered matrix which has %d block%s is of the fo" "rm\n", num, num == 1 ? "" : "s"); ib[num+1] = n + 1; index = 100; iblock = 1; for (i = 1; i <= n; i++) { for (ij = 1; ij <= index; ij++) hold[ij] = ' '; if (i == ib[iblock]) { xprintf("\n"); iblock++; } jblock = 1; index = 0; for (j = 1; j <= n; j++) { if (j == ib[jblock]) { hold[++index] = ' '; jblock++; } ii = ior[i]; jj = ior[j]; hold[++index] = (char)(a[ii][jj] ? 'X' : '0'); } xprintf("%.*s\n", index, &hold[1]); } xprintf("\nThe starting point for each block is given by\n"); for (i = 1; i <= num; i++) { if ((i - 1) % 12 == 0) xprintf("\n"); xprintf(" %4d", ib[i]); } xprintf("\n"); return; } void setup(int n, char a[1+50][1+50], int ip[], int icn[], int lenr[]) { int i, j, ind; for (i = 1; i <= n; i++) lenr[i] = 0; ind = 1; for (i = 1; i <= n; i++) { ip[i] = ind; for (j = 1; j <= n; j++) { if (a[i][j]) { lenr[i]++; icn[ind++] = j; } } } return; } double g = 1431655765.0; double fa01as(int i) { /* random number generator */ g = fmod(g * 9228907.0, 4294967296.0); if (i >= 0) return g / 4294967296.0; else return 2.0 * g / 4294967296.0 - 1.0; } void fa01bs(int max, int *nrand) { *nrand = (int)(fa01as(1) * (double)max) + 1; return; } #endif /* eof */ igraph/src/cs_cholsol.c0000644000176000001440000000334312325527073014650 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* x=A\b where A is symmetric positive definite; b overwritten with solution */ CS_INT cs_cholsol (CS_INT order, const cs *A, CS_ENTRY *b) { CS_ENTRY *x ; css *S ; csn *N ; CS_INT n, ok ; if (!CS_CSC (A) || !b) return (0) ; /* check inputs */ n = A->n ; S = cs_schol (order, A) ; /* ordering and symbolic analysis */ N = cs_chol (A, S) ; /* numeric Cholesky factorization */ x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get workspace */ ok = (S && N && x) ; if (ok) { cs_ipvec (S->pinv, b, x, n) ; /* x = P*b */ cs_lsolve (N->L, x) ; /* x = L\x */ cs_ltsolve (N->L, x) ; /* x = L'\x */ cs_pvec (S->pinv, x, b, n) ; /* b = P'*x */ } cs_free (x) ; cs_sfree (S) ; cs_nfree (N) ; return (ok) ; } igraph/src/Sphere.cpp0000755000176000001440000000250112325527072014303 0ustar ripleyusers#include "Sphere.h" #include namespace igraph { Sphere::Sphere() {} Sphere::Sphere(Point vCenter, double vRadius) { Type("Sphere"); mCenter = vCenter; mRadius = vRadius; } Sphere::~Sphere() { } bool Sphere::Intersect(const Ray& vRay, Point& vIntersectPoint) const { double c; Vector V; Vector EO(vRay.Origin(), mCenter); double v; double disc; double d; Vector E(Point(0,0,0), vRay.Origin()); // E = vector from origin to ray origin Vector P; c = mCenter.Distance(vRay.Origin()); //c = distance from eye to center of sphere V = vRay.Direction(); V.NormalizeThis(); v = EO.Dot(V); double v2 = V.Dot(EO.Normalize()); if (v2 >= 0.0) { disc = mRadius*mRadius - (EO.Dot(EO) - v*v); if (disc <= 0) return false; else { d = sqrt(disc); P = E + V*(v-d); vIntersectPoint = P.ToPoint(); return true; } } else return false; } Vector Sphere::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const { // currently does not take rOffSurface point into account, // it should check if this point is inside the sphere, if it is // return a normal facing the center. Vector radius_vector (mCenter, rSurfacePoint); return (radius_vector.Normalize()); } double Sphere::Radius() const { return mRadius; } const Point& Sphere::Center() const { return mCenter; } } // namespace igraph igraph/src/glpapi07.c0000644000176000001440000003504212325527073014144 0ustar ripleyusers/* glpapi07.c (exact simplex solver) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" #include "glpssx.h" /*********************************************************************** * NAME * * glp_exact - solve LP problem in exact arithmetic * * SYNOPSIS * * int glp_exact(glp_prob *lp, const glp_smcp *parm); * * DESCRIPTION * * The routine glp_exact is a tentative implementation of the primal * two-phase simplex method based on exact (rational) arithmetic. It is * similar to the routine glp_simplex, however, for all internal * computations it uses arithmetic of rational numbers, which is exact * in mathematical sense, i.e. free of round-off errors unlike floating * point arithmetic. * * Note that the routine glp_exact uses inly two control parameters * passed in the structure glp_smcp, namely, it_lim and tm_lim. * * RETURNS * * 0 The LP problem instance has been successfully solved. This code * does not necessarily mean that the solver has found optimal * solution. It only means that the solution process was successful. * * GLP_EBADB * Unable to start the search, because the initial basis specified * in the problem object is invalid--the number of basic (auxiliary * and structural) variables is not the same as the number of rows in * the problem object. * * GLP_ESING * Unable to start the search, because the basis matrix correspodning * to the initial basis is exactly singular. * * GLP_EBOUND * Unable to start the search, because some double-bounded variables * have incorrect bounds. * * GLP_EFAIL * The problem has no rows/columns. * * GLP_EITLIM * The search was prematurely terminated, because the simplex * iteration limit has been exceeded. * * GLP_ETMLIM * The search was prematurely terminated, because the time limit has * been exceeded. */ static void set_d_eps(mpq_t x, double val) { /* convert double val to rational x obtaining a more adequate fraction than provided by mpq_set_d due to allowing a small approximation error specified by a given relative tolerance; for example, mpq_set_d would give the following 1/3 ~= 0.333333333333333314829616256247391... -> -> 6004799503160661/18014398509481984 while this routine gives exactly 1/3 */ int s, n, j; double f, p, q, eps = 1e-9; mpq_t temp; xassert(-DBL_MAX <= val && val <= +DBL_MAX); #if 1 /* 30/VII-2008 */ if (val == floor(val)) { /* if val is integral, do not approximate */ mpq_set_d(x, val); goto done; } #endif if (val > 0.0) s = +1; else if (val < 0.0) s = -1; else { mpq_set_si(x, 0, 1); goto done; } f = frexp(fabs(val), &n); /* |val| = f * 2^n, where 0.5 <= f < 1.0 */ fp2rat(f, 0.1 * eps, &p, &q); /* f ~= p / q, where p and q are integers */ mpq_init(temp); mpq_set_d(x, p); mpq_set_d(temp, q); mpq_div(x, x, temp); mpq_set_si(temp, 1, 1); for (j = 1; j <= abs(n); j++) mpq_add(temp, temp, temp); if (n > 0) mpq_mul(x, x, temp); else if (n < 0) mpq_div(x, x, temp); mpq_clear(temp); if (s < 0) mpq_neg(x, x); /* check that the desired tolerance has been attained */ xassert(fabs(val - mpq_get_d(x)) <= eps * (1.0 + fabs(val))); done: return; } static void load_data(SSX *ssx, LPX *lp) { /* load LP problem data into simplex solver workspace */ int m = ssx->m; int n = ssx->n; int nnz = ssx->A_ptr[n+1]-1; int j, k, type, loc, len, *ind; double lb, ub, coef, *val; xassert(lpx_get_num_rows(lp) == m); xassert(lpx_get_num_cols(lp) == n); xassert(lpx_get_num_nz(lp) == nnz); /* types and bounds of rows and columns */ for (k = 1; k <= m+n; k++) { if (k <= m) { type = lpx_get_row_type(lp, k); lb = lpx_get_row_lb(lp, k); ub = lpx_get_row_ub(lp, k); } else { type = lpx_get_col_type(lp, k-m); lb = lpx_get_col_lb(lp, k-m); ub = lpx_get_col_ub(lp, k-m); } switch (type) { case LPX_FR: type = SSX_FR; break; case LPX_LO: type = SSX_LO; break; case LPX_UP: type = SSX_UP; break; case LPX_DB: type = SSX_DB; break; case LPX_FX: type = SSX_FX; break; default: xassert(type != type); } ssx->type[k] = type; set_d_eps(ssx->lb[k], lb); set_d_eps(ssx->ub[k], ub); } /* optimization direction */ switch (lpx_get_obj_dir(lp)) { case LPX_MIN: ssx->dir = SSX_MIN; break; case LPX_MAX: ssx->dir = SSX_MAX; break; default: xassert(lp != lp); } /* objective coefficients */ for (k = 0; k <= m+n; k++) { if (k == 0) coef = lpx_get_obj_coef(lp, 0); else if (k <= m) coef = 0.0; else coef = lpx_get_obj_coef(lp, k-m); set_d_eps(ssx->coef[k], coef); } /* constraint coefficients */ ind = xcalloc(1+m, sizeof(int)); val = xcalloc(1+m, sizeof(double)); loc = 0; for (j = 1; j <= n; j++) { ssx->A_ptr[j] = loc+1; len = lpx_get_mat_col(lp, j, ind, val); for (k = 1; k <= len; k++) { loc++; ssx->A_ind[loc] = ind[k]; set_d_eps(ssx->A_val[loc], val[k]); } } xassert(loc == nnz); xfree(ind); xfree(val); return; } static int load_basis(SSX *ssx, LPX *lp) { /* load current LP basis into simplex solver workspace */ int m = ssx->m; int n = ssx->n; int *type = ssx->type; int *stat = ssx->stat; int *Q_row = ssx->Q_row; int *Q_col = ssx->Q_col; int i, j, k; xassert(lpx_get_num_rows(lp) == m); xassert(lpx_get_num_cols(lp) == n); /* statuses of rows and columns */ for (k = 1; k <= m+n; k++) { if (k <= m) stat[k] = lpx_get_row_stat(lp, k); else stat[k] = lpx_get_col_stat(lp, k-m); switch (stat[k]) { case LPX_BS: stat[k] = SSX_BS; break; case LPX_NL: stat[k] = SSX_NL; xassert(type[k] == SSX_LO || type[k] == SSX_DB); break; case LPX_NU: stat[k] = SSX_NU; xassert(type[k] == SSX_UP || type[k] == SSX_DB); break; case LPX_NF: stat[k] = SSX_NF; xassert(type[k] == SSX_FR); break; case LPX_NS: stat[k] = SSX_NS; xassert(type[k] == SSX_FX); break; default: xassert(stat != stat); } } /* build permutation matix Q */ i = j = 0; for (k = 1; k <= m+n; k++) { if (stat[k] == SSX_BS) { i++; if (i > m) return 1; Q_row[k] = i, Q_col[i] = k; } else { j++; if (j > n) return 1; Q_row[k] = m+j, Q_col[m+j] = k; } } xassert(i == m && j == n); return 0; } int glp_exact(glp_prob *lp, const glp_smcp *parm) { glp_smcp _parm; SSX *ssx; int m = lpx_get_num_rows(lp); int n = lpx_get_num_cols(lp); int nnz = lpx_get_num_nz(lp); int i, j, k, type, pst, dst, ret, *stat; double lb, ub, *prim, *dual, sum; if (parm == NULL) parm = &_parm, glp_init_smcp((glp_smcp *)parm); /* check control parameters */ if (parm->it_lim < 0) xerror("glp_exact: it_lim = %d; invalid parameter\n", parm->it_lim); if (parm->tm_lim < 0) xerror("glp_exact: tm_lim = %d; invalid parameter\n", parm->tm_lim); /* the problem must have at least one row and one column */ if (!(m > 0 && n > 0)) { xprintf("glp_exact: problem has no rows/columns\n"); return GLP_EFAIL; } #if 1 /* basic solution is currently undefined */ lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; lp->obj_val = 0.0; lp->some = 0; #endif /* check that all double-bounded variables have correct bounds */ for (k = 1; k <= m+n; k++) { if (k <= m) { type = lpx_get_row_type(lp, k); lb = lpx_get_row_lb(lp, k); ub = lpx_get_row_ub(lp, k); } else { type = lpx_get_col_type(lp, k-m); lb = lpx_get_col_lb(lp, k-m); ub = lpx_get_col_ub(lp, k-m); } if (type == LPX_DB && lb >= ub) { xprintf("glp_exact: %s %d has invalid bounds\n", k <= m ? "row" : "column", k <= m ? k : k-m); return GLP_EBOUND; } } /* create the simplex solver workspace */ xprintf("glp_exact: %d rows, %d columns, %d non-zeros\n", m, n, nnz); #ifdef HAVE_GMP xprintf("GNU MP bignum library is being used\n"); #else xprintf("GLPK bignum module is being used\n"); xprintf("(Consider installing GNU MP to attain a much better perf" "ormance.)\n"); #endif ssx = ssx_create(m, n, nnz); /* load LP problem data into the workspace */ load_data(ssx, lp); /* load current LP basis into the workspace */ if (load_basis(ssx, lp)) { xprintf("glp_exact: initial LP basis is invalid\n"); ret = GLP_EBADB; goto done; } /* inherit some control parameters from the LP object */ #if 0 ssx->it_lim = lpx_get_int_parm(lp, LPX_K_ITLIM); ssx->it_cnt = lpx_get_int_parm(lp, LPX_K_ITCNT); ssx->tm_lim = lpx_get_real_parm(lp, LPX_K_TMLIM); #else ssx->it_lim = parm->it_lim; ssx->it_cnt = lp->it_cnt; ssx->tm_lim = (double)parm->tm_lim / 1000.0; #endif ssx->out_frq = 5.0; ssx->tm_beg = xtime(); ssx->tm_lag = xlset(0); /* solve LP */ ret = ssx_driver(ssx); /* copy back some statistics to the LP object */ #if 0 lpx_set_int_parm(lp, LPX_K_ITLIM, ssx->it_lim); lpx_set_int_parm(lp, LPX_K_ITCNT, ssx->it_cnt); lpx_set_real_parm(lp, LPX_K_TMLIM, ssx->tm_lim); #else lp->it_cnt = ssx->it_cnt; #endif /* analyze the return code */ switch (ret) { case 0: /* optimal solution found */ ret = 0; pst = LPX_P_FEAS, dst = LPX_D_FEAS; break; case 1: /* problem has no feasible solution */ ret = 0; pst = LPX_P_NOFEAS, dst = LPX_D_INFEAS; break; case 2: /* problem has unbounded solution */ ret = 0; pst = LPX_P_FEAS, dst = LPX_D_NOFEAS; #if 1 xassert(1 <= ssx->q && ssx->q <= n); lp->some = ssx->Q_col[m + ssx->q]; xassert(1 <= lp->some && lp->some <= m+n); #endif break; case 3: /* iteration limit exceeded (phase I) */ ret = GLP_EITLIM; pst = LPX_P_INFEAS, dst = LPX_D_INFEAS; break; case 4: /* iteration limit exceeded (phase II) */ ret = GLP_EITLIM; pst = LPX_P_FEAS, dst = LPX_D_INFEAS; break; case 5: /* time limit exceeded (phase I) */ ret = GLP_ETMLIM; pst = LPX_P_INFEAS, dst = LPX_D_INFEAS; break; case 6: /* time limit exceeded (phase II) */ ret = GLP_ETMLIM; pst = LPX_P_FEAS, dst = LPX_D_INFEAS; break; case 7: /* initial basis matrix is singular */ ret = GLP_ESING; goto done; default: xassert(ret != ret); } /* obtain final basic solution components */ stat = xcalloc(1+m+n, sizeof(int)); prim = xcalloc(1+m+n, sizeof(double)); dual = xcalloc(1+m+n, sizeof(double)); for (k = 1; k <= m+n; k++) { if (ssx->stat[k] == SSX_BS) { i = ssx->Q_row[k]; /* x[k] = xB[i] */ xassert(1 <= i && i <= m); stat[k] = LPX_BS; prim[k] = mpq_get_d(ssx->bbar[i]); dual[k] = 0.0; } else { j = ssx->Q_row[k] - m; /* x[k] = xN[j] */ xassert(1 <= j && j <= n); switch (ssx->stat[k]) { case SSX_NF: stat[k] = LPX_NF; prim[k] = 0.0; break; case SSX_NL: stat[k] = LPX_NL; prim[k] = mpq_get_d(ssx->lb[k]); break; case SSX_NU: stat[k] = LPX_NU; prim[k] = mpq_get_d(ssx->ub[k]); break; case SSX_NS: stat[k] = LPX_NS; prim[k] = mpq_get_d(ssx->lb[k]); break; default: xassert(ssx != ssx); } dual[k] = mpq_get_d(ssx->cbar[j]); } } /* and store them into the LP object */ pst = pst - LPX_P_UNDEF + GLP_UNDEF; dst = dst - LPX_D_UNDEF + GLP_UNDEF; for (k = 1; k <= m+n; k++) stat[k] = stat[k] - LPX_BS + GLP_BS; sum = lpx_get_obj_coef(lp, 0); for (j = 1; j <= n; j++) sum += lpx_get_obj_coef(lp, j) * prim[m+j]; lpx_put_solution(lp, 1, &pst, &dst, &sum, &stat[0], &prim[0], &dual[0], &stat[m], &prim[m], &dual[m]); xfree(stat); xfree(prim); xfree(dual); done: /* delete the simplex solver workspace */ ssx_delete(ssx); /* return to the application program */ return ret; } /* eof */ igraph/src/igraph_error.c0000644000176000001440000002325612325527073015210 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "igraph_error.h" #include "igraph_types.h" #include #include #include #include static IGRAPH_THREAD_LOCAL igraph_error_handler_t *igraph_i_error_handler=0; static IGRAPH_THREAD_LOCAL char igraph_i_errormsg_buffer[500]; static IGRAPH_THREAD_LOCAL char igraph_i_warningmsg_buffer[500]; static const char *igraph_i_error_strings[]= { /* 0 */ "No error", /* 1 */ "Failed", /* 2 */ "Out of memory", /* 3 */ "Parse error", /* 4 */ "Invalid value", /* 5 */ "Already exists", /* 6 */ "Invalid edge vector", /* 7 */ "Invalid vertex id", /* 8 */ "Non-square matrix", /* 9 */ "Invalid mode", /* 10 */ "File operation error", /* 11 */ "Unfold infinite iterator", /* 12 */ "Unimplemented function call", /* 13 */ "Interrupted", /* 14 */ "Numeric procedure did not converge", /* 15 */ "Matrix-vector product failed", /* 16 */ "N must be positive", /* 17 */ "NEV must be positive", /* 18 */ "NCV must be greater than NEV and less than or equal to N " "(and for the non-symmetric solver NCV-NEV >=2 must also hold)", /* 19 */ "Maximum number of iterations should be positive", /* 20 */ "Invalid WHICH parameter", /* 21 */ "Invalid BMAT parameter", /* 22 */ "WORKL is too small", /* 23 */ "LAPACK error in tridiagonal eigenvalue calculation", /* 24 */ "Starting vector is zero", /* 25 */ "MODE is invalid", /* 26 */ "MODE and BMAT are not compatible", /* 27 */ "ISHIFT must be 0 or 1", /* 28 */ "NEV and WHICH='BE' are incompatible", /* 29 */ "Could not build an Arnoldi factorization", /* 30 */ "No eigenvalues to sufficient accuracy", /* 31 */ "HOWMNY is invalid", /* 32 */ "HOWMNY='S' is not implemented", /* 33 */ "Different number of converged Ritz values", /* 34 */ "Error from calculation of a real Schur form", /* 35 */ "LAPACK (dtrevc) error for calculating eigenvectors", /* 36 */ "Unknown ARPACK error", /* 37 */ "Negative loop detected while calculating shortest paths", /* 38 */ "Internal error, likely a bug in igraph", /* 39 */ "Maximum number of iterations reached", /* 40 */ "No shifts could be applied during a cycle of the " "Implicitly restarted Arnoldi iteration. One possibility " "is to increase the size of NCV relative to NEV", /* 41 */ "The Schur form computed by LAPACK routine dlahqr " "could not be reordered by LAPACK routine dtrsen.", /* 42 */ "Big integer division by zero", /* 43 */ "GLPK Error, GLP_EBOUND", /* 44 */ "GLPK Error, GLP_EROOT", /* 45 */ "GLPK Error, GLP_ENOPFS", /* 46 */ "GLPK Error, GLP_ENODFS", /* 47 */ "GLPK Error, GLP_EFAIL", /* 48 */ "GLPK Error, GLP_EMIPGAP", /* 49 */ "GLPK Error, GLP_ETMLIM", /* 50 */ "GLPK Error, GLP_STOP", /* 51 */ "Internal attribute handler error", /* 52 */ "Unimplemented attribute combination for this type", /* 53 */ "LAPACK call resulted an error", /* 54 */ "Internal DrL error", /* 55 */ "Integer or double overflow", /* 56 */ "Internal GPLK error", /* 57 */ "CPU time exceeded", /* 58 */ "Integer or double underflow" }; const char* igraph_strerror(const int igraph_errno) { return igraph_i_error_strings[igraph_errno]; } int igraph_error(const char *reason, const char *file, int line, int igraph_errno) { if (igraph_i_error_handler) { igraph_i_error_handler(reason, file, line, igraph_errno); #ifndef USING_R } else { igraph_error_handler_abort(reason, file, line, igraph_errno); #endif } return igraph_errno; } int igraph_errorf(const char *reason, const char *file, int line, int igraph_errno, ...) { va_list ap; va_start(ap, igraph_errno); vsnprintf(igraph_i_errormsg_buffer, sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap); return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno); } int igraph_errorvf(const char *reason, const char *file, int line, int igraph_errno, va_list ap) { vsnprintf(igraph_i_errormsg_buffer, sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap); return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno); } #ifndef USING_R void igraph_error_handler_abort (const char *reason, const char *file, int line, int igraph_errno) { fprintf(stderr, "Error at %s:%i :%s, %s\n", file, line, reason, igraph_strerror(igraph_errno)); abort(); } #endif void igraph_error_handler_ignore (const char *reason, const char *file, int line, int igraph_errno) { IGRAPH_UNUSED(reason); IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); IGRAPH_UNUSED(igraph_errno); IGRAPH_FINALLY_FREE(); } #ifndef USING_R void igraph_error_handler_printignore (const char *reason, const char *file, int line, int igraph_errno) { IGRAPH_FINALLY_FREE(); fprintf(stderr, "Error at %s:%i :%s, %s\n", file, line, reason, igraph_strerror(igraph_errno)); } #endif igraph_error_handler_t * igraph_set_error_handler (igraph_error_handler_t * new_handler) { igraph_error_handler_t * previous_handler = igraph_i_error_handler; igraph_i_error_handler = new_handler; return previous_handler; } IGRAPH_THREAD_LOCAL struct igraph_i_protectedPtr igraph_i_finally_stack[100]; /* * Adds another element to the free list */ void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr) { int no=igraph_i_finally_stack[0].all; assert (no<100); assert (no>=0); igraph_i_finally_stack[no].ptr=ptr; igraph_i_finally_stack[no].func=func; igraph_i_finally_stack[0].all ++; /* printf("--> Finally stack contains now %d elements\n", igraph_i_finally_stack[0].all); */ } void IGRAPH_FINALLY_CLEAN(int minus) { igraph_i_finally_stack[0].all -= minus; if (igraph_i_finally_stack[0].all < 0) { /* fprintf(stderr, "corrupt finally stack, popping %d elements when only %d left\n", minus, igraph_i_finally_stack[0].all+minus); */ igraph_i_finally_stack[0].all = 0; } /* printf("<-- Finally stack contains now %d elements\n", igraph_i_finally_stack[0].all); */ } void IGRAPH_FINALLY_FREE(void) { int p; /* printf("[X] Finally stack will be cleaned (contained %d elements)\n", igraph_i_finally_stack[0].all); */ for (p=igraph_i_finally_stack[0].all-1; p>=0; p--) { igraph_i_finally_stack[p].func(igraph_i_finally_stack[p].ptr); } igraph_i_finally_stack[0].all=0; } int IGRAPH_FINALLY_STACK_SIZE(void) { return igraph_i_finally_stack[0].all; } static IGRAPH_THREAD_LOCAL igraph_warning_handler_t *igraph_i_warning_handler=0; /** * \function igraph_warning_handler_ignore * Ignore all warnings * * This warning handler function simply ignores all warnings. * \param reason Textual description of the warning. * \param file The source file in which the warning was noticed. * \param line The number of line in the source file which triggered the * warning.. * \param igraph_errno Warnings could have potentially error codes as well, * but this is currently not used in igraph. */ void igraph_warning_handler_ignore (const char *reason, const char *file, int line, int igraph_errno) { IGRAPH_UNUSED(reason); IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); IGRAPH_UNUSED(igraph_errno); } #ifndef USING_R /** * \function igraph_warning_handler_print * Print all warning to the standard error * * This warning handler function simply prints all warnings to the * standard error. * \param reason Textual description of the warning. * \param file The source file in which the warning was noticed. * \param line The number of line in the source file which triggered the * warning.. * \param igraph_errno Warnings could have potentially error codes as well, * but this is currently not used in igraph. */ void igraph_warning_handler_print (const char *reason, const char *file, int line, int igraph_errno) { IGRAPH_UNUSED(igraph_errno); fprintf(stderr, "Warning: %s in file %s, line %i\n", reason, file, line); } #endif int igraph_warning(const char *reason, const char *file, int line, int igraph_errno) { if (igraph_i_warning_handler) { igraph_i_warning_handler(reason, file, line, igraph_errno); #ifndef USING_R } else { igraph_warning_handler_print(reason, file, line, igraph_errno); #endif } return igraph_errno; } int igraph_warningf(const char *reason, const char *file, int line, int igraph_errno, ...) { va_list ap; va_start(ap, igraph_errno); vsnprintf(igraph_i_warningmsg_buffer, sizeof(igraph_i_warningmsg_buffer) / sizeof(char), reason, ap); return igraph_warning(igraph_i_warningmsg_buffer, file, line, igraph_errno); } igraph_warning_handler_t * igraph_set_warning_handler (igraph_warning_handler_t * new_handler) { igraph_warning_handler_t * previous_handler = igraph_i_warning_handler; igraph_i_warning_handler = new_handler; return previous_handler; } igraph/src/foreign-lgl-header.h0000644000176000001440000000215712325527073016162 0ustar ripleyusers/* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge MA, 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_vector.h" #include "igraph_types_internal.h" typedef struct { void *scanner; int eof; char errmsg[300]; int has_weights; igraph_vector_t *vector; igraph_vector_t *weights; igraph_trie_t *trie; int actvertex; } igraph_i_lgl_parsedata_t; igraph/src/spanning_trees.c0000644000176000001440000003056412325527074015545 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2011 Gabor Csardi Rue de l'Industrie 5, Lausanne 1005, Switzerland This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_structural.h" #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_interrupt_internal.h" #include "igraph_memory.h" #include "igraph_progress.h" #include "igraph_types_internal.h" int igraph_i_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_vector_t *result); int igraph_i_minimum_spanning_tree_prim(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights); /** * \ingroup structural * \function igraph_minimum_spanning_tree * \brief Calculates one minimum spanning tree of a graph. * * * If the graph has more minimum spanning trees (this is always the * case, except if it is a forest) this implementation returns only * the same one. * * * Directed graphs are considered as undirected for this computation. * * * If the graph is not connected then its minimum spanning forest is * returned. This is the set of the minimum spanning trees of each * component. * * \param graph The graph object. * \param res An initialized vector, the IDs of the edges that constitute * a spanning tree will be returned here. Use * \ref igraph_subgraph_edges() to extract the spanning tree as * a separate graph object. * \param weights A vector containing the weights of the edges * in the same order as the simple edge iterator visits them * (i.e. in increasing order of edge IDs). * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * * Time complexity: O(|V|+|E|) for the unweighted case, O(|E| log |V|) * for the weighted case. |V| is the number of vertices, |E| the * number of edges in the graph. * * \sa \ref igraph_minimum_spanning_tree_unweighted() and * \ref igraph_minimum_spanning_tree_prim() if you only need the * tree as a separate graph object. * * \example examples/simple/igraph_minimum_spanning_tree.c */ int igraph_minimum_spanning_tree(const igraph_t* graph, igraph_vector_t* res, const igraph_vector_t* weights) { if (weights == 0) IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, res)); else IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, res, weights)); return IGRAPH_SUCCESS; } /** * \ingroup structural * \function igraph_minimum_spanning_tree_unweighted * \brief Calculates one minimum spanning tree of an unweighted graph. * * * If the graph has more minimum spanning trees (this is always the * case, except if it is a forest) this implementation returns only * the same one. * * * Directed graphs are considered as undirected for this computation. * * * If the graph is not connected then its minimum spanning forest is * returned. This is the set of the minimum spanning trees of each * component. * \param graph The graph object. * \param mst The minimum spanning tree, another graph object. Do * \em not initialize this object before passing it to * this function, but be sure to call \ref igraph_destroy() on it if * you don't need it any more. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * * Time complexity: O(|V|+|E|), * |V| is the * number of vertices, |E| the number * of edges in the graph. * * \sa \ref igraph_minimum_spanning_tree_prim() for weighted graphs, * \ref igraph_minimum_spanning_tree() if you need the IDs of the * edges that constitute the spanning tree. */ int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_t *mst) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph)-1); IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, &edges)); IGRAPH_CHECK(igraph_subgraph_edges(graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ 0)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup structural * \function igraph_minimum_spanning_tree_prim * \brief Calculates one minimum spanning tree of a weighted graph. * * * This function uses Prim's method for carrying out the computation, * see Prim, R.C.: Shortest connection networks and some * generalizations, Bell System Technical * Journal, Vol. 36, * 1957, 1389--1401. * * * If the graph has more than one minimum spanning tree, the current * implementation returns always the same one. * * * Directed graphs are considered as undirected for this computation. * * * If the graph is not connected then its minimum spanning forest is * returned. This is the set of the minimum spanning trees of each * component. * * \param graph The graph object. * \param mst The result of the computation, a graph object containing * the minimum spanning tree of the graph. * Do \em not initialize this object before passing it to * this function, but be sure to call \ref igraph_destroy() on it if * you don't need it any more. * \param weights A vector containing the weights of the edges * in the same order as the simple edge iterator visits them * (i.e. in increasing order of edge IDs). * \return Error code: * \c IGRAPH_ENOMEM, not enough memory. * \c IGRAPH_EINVAL, length of weight vector does not * match number of edges. * * Time complexity: O(|E| log |V|), * |V| is the number of vertices, * |E| the number of edges in the * graph. * * \sa \ref igraph_minimum_spanning_tree_unweighted() for unweighted graphs, * \ref igraph_minimum_spanning_tree() if you need the IDs of the * edges that constitute the spanning tree. * * \example examples/simple/igraph_minimum_spanning_tree.c */ int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, const igraph_vector_t *weights) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph)-1); IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, &edges, weights)); IGRAPH_CHECK(igraph_subgraph_edges(graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ 0)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph, igraph_vector_t* res) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); char *already_added; char *added_edges; igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; igraph_vector_t tmp=IGRAPH_VECTOR_NULL; long int i, j; igraph_vector_clear(res); added_edges=igraph_Calloc(no_of_edges, char); if (added_edges==0) { IGRAPH_ERROR("unweighted spanning tree failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added_edges); already_added=igraph_Calloc(no_of_nodes, char); if (already_added==0) { IGRAPH_ERROR("unweighted spanning tree failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, already_added); IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); for (i=0; i0) { continue; } IGRAPH_ALLOW_INTERRUPTION(); already_added[i]=1; IGRAPH_CHECK(igraph_dqueue_push(&q, i)); while (! igraph_dqueue_empty(&q)) { long int act_node=(long int) igraph_dqueue_pop(&q); IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act_node, IGRAPH_ALL)); for (j=0; j0) { continue; } IGRAPH_ALLOW_INTERRUPTION(); already_added[i]=1; /* add all edges of the first vertex */ igraph_incident(graph, &adj, (igraph_integer_t) i, (igraph_neimode_t) mode); for (j=0; jx) return (-1) ; /* check inputs */ n = A->n ; Ap = A->p ; Ax = A->x ; for (j = 0 ; j < n ; j++) { for (s = 0, p = Ap [j] ; p < Ap [j+1] ; p++) s += CS_ABS (Ax [p]) ; norm = CS_MAX (norm, s) ; } return (norm) ; } igraph/src/NetRoutines.h0000644000176000001440000000465012325527072015005 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Jörg Reichardt The original copyright notice follows here */ /*************************************************************************** NetRoutines.h - description ------------------- begin : Tue Oct 28 2003 copyright : (C) 2003 by Joerg Reichardt email : reichardt@mitte ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef NETROUTINES_H #define NETROUTINES_H #include "NetDataTypes.h" #include "igraph_types.h" #include "igraph_datatype.h" int igraph_i_read_network(const igraph_t *graph, const igraph_vector_t *weights, network *net, igraph_bool_t use_weights, unsigned int states); void reduce_cliques(DLList*>*, FILE *file); void reduce_cliques2(network*, bool, long ); void clear_all_markers(network *net); #endif igraph/src/math.c0000644000176000001440000001461112325527073013451 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "config.h" #include "igraph_math.h" #include "igraph_types.h" #ifdef _MSC_VER # define isinf(x) (!_finite(x) && !_isnan(x)) #endif int igraph_finite(double x) { #ifdef isfinite return isfinite(x); #elif HAVE_ISFINITE == 1 return isfinite(x); #elif HAVE_FINITE == 1 return finite(x); #else /* neither finite nor isfinite work. Do we really need the AIX exception? */ # ifdef _AIX # include return FINITE(x); # else return (!isnan(x) & (x != IGRAPH_POSINFINITY) & (x != IGRAPH_NEGINFINITY)); # endif #endif } double igraph_log2(const double a) { return log(a)/log(2.0); } int igraph_chebyshev_init(const double *dos, int nos, double eta) { int i, ii; double err; if (nos < 1) return 0; err = 0.0; i = 0; /* just to avoid compiler warnings */ for (ii=1; ii<=nos; ii++) { i = nos - ii; err += fabs(dos[i]); if (err > eta) { return i; } } return i; } double igraph_chebyshev_eval(double x, const double *a, const int n) { double b0, b1, b2, twox; int i; if (n < 1 || n > 1000) IGRAPH_NAN; if (x < -1.1 || x > 1.1) IGRAPH_NAN; twox = x * 2; b2 = b1 = 0; b0 = 0; for (i = 1; i <= n; i++) { b2 = b1; b1 = b0; b0 = twox * b1 - b2 + a[n - i]; } return (b0 - b2) * 0.5; } double igraph_log1p(double x) { /* series for log1p on the interval -.375 to .375 * with weighted error 6.35e-32 * log weighted error 31.20 * significant figures required 30.93 * decimal places required 32.01 */ static const double alnrcs[43] = { +.10378693562743769800686267719098e+1, -.13364301504908918098766041553133e+0, +.19408249135520563357926199374750e-1, -.30107551127535777690376537776592e-2, +.48694614797154850090456366509137e-3, -.81054881893175356066809943008622e-4, +.13778847799559524782938251496059e-4, -.23802210894358970251369992914935e-5, +.41640416213865183476391859901989e-6, -.73595828378075994984266837031998e-7, +.13117611876241674949152294345011e-7, -.23546709317742425136696092330175e-8, +.42522773276034997775638052962567e-9, -.77190894134840796826108107493300e-10, +.14075746481359069909215356472191e-10, -.25769072058024680627537078627584e-11, +.47342406666294421849154395005938e-12, -.87249012674742641745301263292675e-13, +.16124614902740551465739833119115e-13, -.29875652015665773006710792416815e-14, +.55480701209082887983041321697279e-15, -.10324619158271569595141333961932e-15, +.19250239203049851177878503244868e-16, -.35955073465265150011189707844266e-17, +.67264542537876857892194574226773e-18, -.12602624168735219252082425637546e-18, +.23644884408606210044916158955519e-19, -.44419377050807936898878389179733e-20, +.83546594464034259016241293994666e-21, -.15731559416479562574899253521066e-21, +.29653128740247422686154369706666e-22, -.55949583481815947292156013226666e-23, +.10566354268835681048187284138666e-23, -.19972483680670204548314999466666e-24, +.37782977818839361421049855999999e-25, -.71531586889081740345038165333333e-26, +.13552488463674213646502024533333e-26, -.25694673048487567430079829333333e-27, +.48747756066216949076459519999999e-28, -.92542112530849715321132373333333e-29, +.17578597841760239233269760000000e-29, -.33410026677731010351377066666666e-30, +.63533936180236187354180266666666e-31, }; static IGRAPH_THREAD_LOCAL int nlnrel = 0; static IGRAPH_THREAD_LOCAL double xmin = 0.0; if (xmin == 0.0) xmin = -1 + sqrt(DBL_EPSILON);/*was sqrt(d1mach(4)); */ if (nlnrel == 0) /* initialize chebychev coefficients */ nlnrel = igraph_chebyshev_init(alnrcs, 43, DBL_EPSILON/20);/*was .1*d1mach(3)*/ if (x == 0.) return 0.;/* speed */ if (x == -1) return(IGRAPH_NEGINFINITY); if (x < -1) return(IGRAPH_NAN); if (fabs(x) <= .375) { /* Improve on speed (only); again give result accurate to IEEE double precision: */ if(fabs(x) < .5 * DBL_EPSILON) return x; if( (0 < x && x < 1e-8) || (-1e-9 < x && x < 0)) return x * (1 - .5 * x); /* else */ return x * (1 - x * igraph_chebyshev_eval(x / .375, alnrcs, nlnrel)); } /* else */ /* if (x < xmin) { */ /* /\* answer less than half precision because x too near -1 *\/ */ /* ML_ERROR(ME_PRECISION, "log1p"); */ /* } */ return log(1 + x); } long double igraph_fabsl(long double a) { if (a<0) { return -a; } else { return a; } } double igraph_fmin(double a, double b) { if (b 0) { va_start(args, format); n = _vsnprintf(buffer, count, format, args); buffer[count-1] = 0; va_end(args); } else n=0; return n; } #endif int igraph_is_nan(double x) { return isnan(x); } int igraph_is_inf(double x) { return isinf(x) != 0; } int igraph_is_posinf(double x) { return isinf(x) == 1; } int igraph_is_neginf(double x) { return isinf(x) == -1; } igraph/src/cs_print.c0000644000176000001440000000534112325527073014341 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* print a sparse matrix */ /* CS_INT cs_print (const cs *A, CS_INT brief) */ /* { */ /* CS_INT p, j, m, n, nzmax, nz, *Ap, *Ai ; */ /* CS_ENTRY *Ax ; */ /* if (!A) { printf ("(null)\n") ; return (0) ; } */ /* m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; */ /* nzmax = A->nzmax ; nz = A->nz ; */ /* printf ("CXSparse Version %d.%d.%d, %s. %s\n", CS_VER, CS_SUBVER, */ /* CS_SUBSUB, CS_DATE, CS_COPYRIGHT) ; */ /* if (nz < 0) */ /* { */ /* printf (""CS_ID"-by-"CS_ID", nzmax: "CS_ID" nnz: "CS_ID", 1-norm: %g\n", m, n, nzmax, */ /* Ap [n], cs_norm (A)) ; */ /* for (j = 0 ; j < n ; j++) */ /* { */ /* printf (" col "CS_ID" : locations "CS_ID" to "CS_ID"\n", j, Ap [j], Ap [j+1]-1); */ /* for (p = Ap [j] ; p < Ap [j+1] ; p++) */ /* { */ /* #ifdef CS_COMPLEX */ /* printf (" "CS_ID" : (%g, %g)\n", Ai [p], */ /* Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; */ /* #else */ /* printf (" "CS_ID" : %g\n", Ai [p], Ax ? Ax [p] : 1) ; */ /* #endif */ /* if (brief && p > 20) { printf (" ...\n") ; return (1) ; } */ /* } */ /* } */ /* } */ /* else */ /* { */ /* printf ("triplet: "CS_ID"-by-"CS_ID", nzmax: "CS_ID" nnz: "CS_ID"\n", m, n, nzmax, nz) ; */ /* for (p = 0 ; p < nz ; p++) */ /* { */ /* #ifdef CS_COMPLEX */ /* printf (" "CS_ID" "CS_ID" : (%g, %g)\n", Ai [p], Ap [p], */ /* Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; */ /* #else */ /* printf (" "CS_ID" "CS_ID" : %g\n", Ai [p], Ap [p], Ax ? Ax [p] : 1) ; */ /* #endif */ /* if (brief && p > 20) { printf (" ...\n") ; return (1) ; } */ /* } */ /* } */ /* return (1) ; */ /* } */ igraph/src/plfit/0000755000176000001440000000000012325555115013465 5ustar ripleyusersigraph/src/plfit/arithmetic_sse_double.h0000644000176000001440000002113612325527074020201 0ustar ripleyusers/* * SSE2 implementation of vector oprations (64bit double). * * Copyright (c) 2007-2010 Naoaki Okazaki * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* $Id: arithmetic_sse_double.h 65 2010-01-29 12:19:16Z naoaki $ */ #include #if !defined(__APPLE__) #include #endif #include #if 1400 <= _MSC_VER #include #endif/*1400 <= _MSC_VER*/ #if HAVE_EMMINTRIN_H #include #endif/*HAVE_EMMINTRIN_H*/ inline static void* vecalloc(size_t size) { #ifdef _MSC_VER void *memblock = _aligned_malloc(size, 16); #elif defined(__APPLE__) /* Memory on Mac OS X is already aligned to 16 bytes */ void *memblock = malloc(size); #else void *memblock = memalign(16, size); #endif if (memblock != NULL) { memset(memblock, 0, size); } return memblock; } inline static void vecfree(void *memblock) { #ifdef _MSC_VER _aligned_free(memblock); #else free(memblock); #endif } #define fsigndiff(x, y) \ ((_mm_movemask_pd(_mm_set_pd(*(x), *(y))) + 1) & 0x002) #define vecset(x, c, n) \ { \ int i; \ __m128d XMM0 = _mm_set1_pd(c); \ for (i = 0;i < (n);i += 8) { \ _mm_store_pd((x)+i , XMM0); \ _mm_store_pd((x)+i+2, XMM0); \ _mm_store_pd((x)+i+4, XMM0); \ _mm_store_pd((x)+i+6, XMM0); \ } \ } #define veccpy(y, x, n) \ { \ int i; \ for (i = 0;i < (n);i += 8) { \ __m128d XMM0 = _mm_load_pd((x)+i ); \ __m128d XMM1 = _mm_load_pd((x)+i+2); \ __m128d XMM2 = _mm_load_pd((x)+i+4); \ __m128d XMM3 = _mm_load_pd((x)+i+6); \ _mm_store_pd((y)+i , XMM0); \ _mm_store_pd((y)+i+2, XMM1); \ _mm_store_pd((y)+i+4, XMM2); \ _mm_store_pd((y)+i+6, XMM3); \ } \ } #define vecncpy(y, x, n) \ { \ int i; \ for (i = 0;i < (n);i += 8) { \ __m128d XMM0 = _mm_setzero_pd(); \ __m128d XMM1 = _mm_setzero_pd(); \ __m128d XMM2 = _mm_setzero_pd(); \ __m128d XMM3 = _mm_setzero_pd(); \ __m128d XMM4 = _mm_load_pd((x)+i ); \ __m128d XMM5 = _mm_load_pd((x)+i+2); \ __m128d XMM6 = _mm_load_pd((x)+i+4); \ __m128d XMM7 = _mm_load_pd((x)+i+6); \ XMM0 = _mm_sub_pd(XMM0, XMM4); \ XMM1 = _mm_sub_pd(XMM1, XMM5); \ XMM2 = _mm_sub_pd(XMM2, XMM6); \ XMM3 = _mm_sub_pd(XMM3, XMM7); \ _mm_store_pd((y)+i , XMM0); \ _mm_store_pd((y)+i+2, XMM1); \ _mm_store_pd((y)+i+4, XMM2); \ _mm_store_pd((y)+i+6, XMM3); \ } \ } #define vecadd(y, x, c, n) \ { \ int i; \ __m128d XMM7 = _mm_set1_pd(c); \ for (i = 0;i < (n);i += 4) { \ __m128d XMM0 = _mm_load_pd((x)+i ); \ __m128d XMM1 = _mm_load_pd((x)+i+2); \ __m128d XMM2 = _mm_load_pd((y)+i ); \ __m128d XMM3 = _mm_load_pd((y)+i+2); \ XMM0 = _mm_mul_pd(XMM0, XMM7); \ XMM1 = _mm_mul_pd(XMM1, XMM7); \ XMM2 = _mm_add_pd(XMM2, XMM0); \ XMM3 = _mm_add_pd(XMM3, XMM1); \ _mm_store_pd((y)+i , XMM2); \ _mm_store_pd((y)+i+2, XMM3); \ } \ } #define vecdiff(z, x, y, n) \ { \ int i; \ for (i = 0;i < (n);i += 8) { \ __m128d XMM0 = _mm_load_pd((x)+i ); \ __m128d XMM1 = _mm_load_pd((x)+i+2); \ __m128d XMM2 = _mm_load_pd((x)+i+4); \ __m128d XMM3 = _mm_load_pd((x)+i+6); \ __m128d XMM4 = _mm_load_pd((y)+i ); \ __m128d XMM5 = _mm_load_pd((y)+i+2); \ __m128d XMM6 = _mm_load_pd((y)+i+4); \ __m128d XMM7 = _mm_load_pd((y)+i+6); \ XMM0 = _mm_sub_pd(XMM0, XMM4); \ XMM1 = _mm_sub_pd(XMM1, XMM5); \ XMM2 = _mm_sub_pd(XMM2, XMM6); \ XMM3 = _mm_sub_pd(XMM3, XMM7); \ _mm_store_pd((z)+i , XMM0); \ _mm_store_pd((z)+i+2, XMM1); \ _mm_store_pd((z)+i+4, XMM2); \ _mm_store_pd((z)+i+6, XMM3); \ } \ } #define vecscale(y, c, n) \ { \ int i; \ __m128d XMM7 = _mm_set1_pd(c); \ for (i = 0;i < (n);i += 4) { \ __m128d XMM0 = _mm_load_pd((y)+i ); \ __m128d XMM1 = _mm_load_pd((y)+i+2); \ XMM0 = _mm_mul_pd(XMM0, XMM7); \ XMM1 = _mm_mul_pd(XMM1, XMM7); \ _mm_store_pd((y)+i , XMM0); \ _mm_store_pd((y)+i+2, XMM1); \ } \ } #define vecmul(y, x, n) \ { \ int i; \ for (i = 0;i < (n);i += 8) { \ __m128d XMM0 = _mm_load_pd((x)+i ); \ __m128d XMM1 = _mm_load_pd((x)+i+2); \ __m128d XMM2 = _mm_load_pd((x)+i+4); \ __m128d XMM3 = _mm_load_pd((x)+i+6); \ __m128d XMM4 = _mm_load_pd((y)+i ); \ __m128d XMM5 = _mm_load_pd((y)+i+2); \ __m128d XMM6 = _mm_load_pd((y)+i+4); \ __m128d XMM7 = _mm_load_pd((y)+i+6); \ XMM4 = _mm_mul_pd(XMM4, XMM0); \ XMM5 = _mm_mul_pd(XMM5, XMM1); \ XMM6 = _mm_mul_pd(XMM6, XMM2); \ XMM7 = _mm_mul_pd(XMM7, XMM3); \ _mm_store_pd((y)+i , XMM4); \ _mm_store_pd((y)+i+2, XMM5); \ _mm_store_pd((y)+i+4, XMM6); \ _mm_store_pd((y)+i+6, XMM7); \ } \ } #if 3 <= __SSE__ /* Horizontal add with haddps SSE3 instruction. The work register (rw) is unused. */ #define __horizontal_sum(r, rw) \ r = _mm_hadd_ps(r, r); \ r = _mm_hadd_ps(r, r); #else /* Horizontal add with SSE instruction. The work register (rw) is used. */ #define __horizontal_sum(r, rw) \ rw = r; \ r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \ r = _mm_add_ps(r, rw); \ rw = r; \ r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \ r = _mm_add_ps(r, rw); #endif #define vecdot(s, x, y, n) \ { \ int i; \ __m128d XMM0 = _mm_setzero_pd(); \ __m128d XMM1 = _mm_setzero_pd(); \ __m128d XMM2, XMM3, XMM4, XMM5; \ for (i = 0;i < (n);i += 4) { \ XMM2 = _mm_load_pd((x)+i ); \ XMM3 = _mm_load_pd((x)+i+2); \ XMM4 = _mm_load_pd((y)+i ); \ XMM5 = _mm_load_pd((y)+i+2); \ XMM2 = _mm_mul_pd(XMM2, XMM4); \ XMM3 = _mm_mul_pd(XMM3, XMM5); \ XMM0 = _mm_add_pd(XMM0, XMM2); \ XMM1 = _mm_add_pd(XMM1, XMM3); \ } \ XMM0 = _mm_add_pd(XMM0, XMM1); \ XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \ XMM0 = _mm_add_pd(XMM0, XMM1); \ _mm_store_sd((s), XMM0); \ } #define vec2norm(s, x, n) \ { \ int i; \ __m128d XMM0 = _mm_setzero_pd(); \ __m128d XMM1 = _mm_setzero_pd(); \ __m128d XMM2, XMM3, XMM4, XMM5; \ for (i = 0;i < (n);i += 4) { \ XMM2 = _mm_load_pd((x)+i ); \ XMM3 = _mm_load_pd((x)+i+2); \ XMM4 = XMM2; \ XMM5 = XMM3; \ XMM2 = _mm_mul_pd(XMM2, XMM4); \ XMM3 = _mm_mul_pd(XMM3, XMM5); \ XMM0 = _mm_add_pd(XMM0, XMM2); \ XMM1 = _mm_add_pd(XMM1, XMM3); \ } \ XMM0 = _mm_add_pd(XMM0, XMM1); \ XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \ XMM0 = _mm_add_pd(XMM0, XMM1); \ XMM0 = _mm_sqrt_pd(XMM0); \ _mm_store_sd((s), XMM0); \ } #define vec2norminv(s, x, n) \ { \ int i; \ __m128d XMM0 = _mm_setzero_pd(); \ __m128d XMM1 = _mm_setzero_pd(); \ __m128d XMM2, XMM3, XMM4, XMM5; \ for (i = 0;i < (n);i += 4) { \ XMM2 = _mm_load_pd((x)+i ); \ XMM3 = _mm_load_pd((x)+i+2); \ XMM4 = XMM2; \ XMM5 = XMM3; \ XMM2 = _mm_mul_pd(XMM2, XMM4); \ XMM3 = _mm_mul_pd(XMM3, XMM5); \ XMM0 = _mm_add_pd(XMM0, XMM2); \ XMM1 = _mm_add_pd(XMM1, XMM3); \ } \ XMM2 = _mm_set1_pd(1.0); \ XMM0 = _mm_add_pd(XMM0, XMM1); \ XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \ XMM0 = _mm_add_pd(XMM0, XMM1); \ XMM0 = _mm_sqrt_pd(XMM0); \ XMM2 = _mm_div_pd(XMM2, XMM0); \ _mm_store_sd((s), XMM2); \ } igraph/src/plfit/kolmogorov.h0000644000176000001440000000234612325527074016044 0ustar ripleyusers/* kolmogorov.h * * Copyright (C) 2010-2011 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KOLMOGOROV_H__ #define __KOLMOGOROV_H__ #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include __BEGIN_DECLS double plfit_kolmogorov(double z); double plfit_ks_test_one_sample_p(double d, size_t n); double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2); __END_DECLS #endif igraph/src/plfit/arithmetic_ansi.h0000644000176000001440000000654712325527074017020 0ustar ripleyusers/* * ANSI C implementation of vector operations. * * Copyright (c) 2007-2010 Naoaki Okazaki * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* $Id: arithmetic_ansi.h 65 2010-01-29 12:19:16Z naoaki $ */ #include #include #if LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT #define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U) #else #define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.) #endif/*LBFGS_IEEE_FLOAT*/ inline static void* vecalloc(size_t size) { void *memblock = malloc(size); if (memblock) { memset(memblock, 0, size); } return memblock; } inline static void vecfree(void *memblock) { free(memblock); } inline static void vecset(lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n) { int i; for (i = 0;i < n;++i) { x[i] = c; } } inline static void veccpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n) { int i; for (i = 0;i < n;++i) { y[i] = x[i]; } } inline static void vecncpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n) { int i; for (i = 0;i < n;++i) { y[i] = -x[i]; } } inline static void vecadd(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n) { int i; for (i = 0;i < n;++i) { y[i] += c * x[i]; } } inline static void vecdiff(lbfgsfloatval_t *z, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n) { int i; for (i = 0;i < n;++i) { z[i] = x[i] - y[i]; } } inline static void vecscale(lbfgsfloatval_t *y, const lbfgsfloatval_t c, const int n) { int i; for (i = 0;i < n;++i) { y[i] *= c; } } inline static void vecmul(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n) { int i; for (i = 0;i < n;++i) { y[i] *= x[i]; } } inline static void vecdot(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n) { int i; *s = 0.; for (i = 0;i < n;++i) { *s += x[i] * y[i]; } } inline static void vec2norm(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n) { vecdot(s, x, x, n); *s = (lbfgsfloatval_t)sqrt(*s); } inline static void vec2norminv(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n) { vec2norm(s, x, n); *s = (lbfgsfloatval_t)(1.0 / *s); } igraph/src/plfit/gss.h0000644000176000001440000001366012325527074014443 0ustar ripleyusers/* gss.h * * Copyright (C) 2012 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __GSS_H__ #define __GSS_H__ #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /** * Enum specifying what the search should do when the function is not U-shaped. */ typedef enum { GSS_ERROR_STOP, /**< Stop and return an error code */ GSS_ERROR_WARN /**< Continue and set the warning flag */ } gss_error_handling_t; /** * Parameter settings for a golden section search. */ typedef struct { double epsilon; gss_error_handling_t on_error; } gss_parameter_t; /** * Callback interface to provide objective function evaluations for the golden * section search. * * The gss() function calls this function to obtain the values of the objective * function when needed. A client program must implement this function to evaluate * the value of the objective function, given the location. * * @param instance The user data sent for the gss() function by the client. * @param x The current value of the variable. * @retval double The value of the objective function for the current * variable. */ typedef double (*gss_evaluate_t)(void *instance, double x); /** * Callback interface to receive the progress of the optimization process for * the golden section search. * * The gss() function calls this function for each iteration. Implementing * this function, a client program can store or display the current progress * of the optimization process. * * @param instance The user data sent for the gss() function by the client. * @param x The current value of the variable. * @param fx The value of the objective function at x. * @param min The location of the minimum value of the objective * function found so far. * @param fmin The minimum value of the objective function found so far. * @param left The left side of the current bracket. * @param right The right side of the current bracket. * @param k The index of the current iteration. * @retval int Zero to continue the optimization process. Returning a * non-zero value will cancel the optimization process. */ typedef int (*gss_progress_t)(void *instance, double x, double fx, double min, double fmin, double left, double right, int k); /** * Start a golden section search optimization. * * @param a The left side of the bracket to start from * @param b The right side of the bracket to start from * @param min The pointer to the variable that receives the location of the * final value of the objective function. This argument can be set to * \c NULL if the location of the final value of the objective * function is unnecessary. * @param fmin The pointer to the variable that receives the final value of * the objective function. This argument can be st to \c NULL if the * final value of the objective function is unnecessary. * @param proc_evaluate The callback function to evaluate the objective * function at a given location. * @param proc_progress The callback function to receive the progress (the * last evaluated location, the value of the objective * function at that location, the width of the current * bracket, the minimum found so far and the step * count). This argument can be set to \c NULL if * a progress report is unnecessary. * @param instance A user data for the client program. The callback * functions will receive the value of this argument. * @param param The pointer to a structure representing parameters for * GSS algorithm. A client program can set this parameter * to \c NULL to use the default parameters. * Call the \ref gss_parameter_init() function to fill a * structure with the default values. * @retval int The status code. This function returns zero if the * minimization process terminates without an error. A * non-zero value indicates an error; in particular, * \c PLFIT_FAILURE means that the function is not * U-shaped. */ int gss(double a, double b, double *min, double *fmin, gss_evaluate_t proc_evaluate, gss_progress_t proc_progress, void* instance, const gss_parameter_t *_param); /** * Return the state of the warning flag. * * The warning flag is 1 if the last optimization was run on a function that * was not U-shaped. */ unsigned short int gss_get_warning_flag(); /** * Initialize GSS parameters to the default values. * * Call this function to fill a parameter structure with the default values * and overwrite parameter values if necessary. * * @param param The pointer to the parameter structure. */ void gss_parameter_init(gss_parameter_t *param); __END_DECLS #endif /* __GSS_H__ */ igraph/src/plfit/error.h0000644000176000001440000000473012325527074014776 0ustar ripleyusers/* error.h * * Copyright (C) 2010-2011 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __ERROR_H__ #define __ERROR_H__ #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS enum { PLFIT_SUCCESS = 0, PLFIT_FAILURE = 1, PLFIT_EINVAL = 2, PLFIT_UNDRFLOW = 3, PLFIT_OVERFLOW = 4, PLFIT_ENOMEM = 5 }; #if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3) # define PLFIT_UNLIKELY(a) __builtin_expect((a), 0) # define PLFIT_LIKELY(a) __builtin_expect((a), 1) #else # define PLFIT_UNLIKELY(a) a # define PLFIT_LIKELY(a) a #endif #define PLFIT_CHECK(a) \ do {\ int plfit_i_ret=(a); \ if (PLFIT_UNLIKELY(plfit_i_ret != PLFIT_SUCCESS)) {\ return plfit_i_ret; \ } \ } while(0) #define PLFIT_ERROR(reason,plfit_errno) \ do {\ plfit_error (reason, __FILE__, __LINE__, plfit_errno) ; \ return plfit_errno ; \ } while (0) typedef void plfit_error_handler_t(const char*, const char*, int, int); extern plfit_error_handler_t plfit_error_handler_abort; extern plfit_error_handler_t plfit_error_handler_ignore; extern plfit_error_handler_t plfit_error_handler_printignore; plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler); void plfit_error(const char *reason, const char *file, int line, int plfit_errno); const char* plfit_strerror(const int plfit_errno); void plfit_error_handler_abort(const char *reason, const char *file, int line, int plfit_errno); void plfit_error_handler_ignore(const char *reason, const char *file, int line, int plfit_errno); void plfit_error_handler_printignore(const char *reason, const char *file, int line, int plfit_errno); __END_DECLS #endif /* __ERROR_H__ */ igraph/src/plfit/zeta.h0000644000176000001440000000265012325527074014607 0ustar ripleyusers/* specfunc/gsl_sf_zeta.h * * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004 Gerard Jungman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Author: G. Jungman */ /* This file was taken from the GNU Scientific Library. Some modifications * were done in order to make it independent from the rest of GSL */ #ifndef __ZETA_H__ #define __ZETA_H__ #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* Hurwitz Zeta Function * zeta(s,q) = Sum[ (k+q)^(-s), {k,0,Infinity} ] * * s > 1.0, q > 0.0 */ double gsl_sf_hzeta(const double s, const double q); __END_DECLS #endif /* __ZETA_H__ */ igraph/src/plfit/plfit.h0000644000176000001440000000676612325527074014776 0ustar ripleyusers/* plfit.h * * Copyright (C) 2010-2011 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __PLFIT_H__ #define __PLFIT_H__ #include #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS #define PLFIT_VERSION_MAJOR 0 #define PLFIT_VERSION_MINOR 6 #define PLFIT_VERSION_STRING "0.6" typedef unsigned short int plfit_bool_t; typedef enum { PLFIT_GSS_OR_LINEAR, PLFIT_LINEAR_ONLY, PLFIT_DEFAULT_CONTINUOUS_METHOD = PLFIT_GSS_OR_LINEAR } plfit_continuous_method_t; typedef enum { PLFIT_LBFGS, PLFIT_LINEAR_SCAN, PLFIT_PRETEND_CONTINUOUS, PLFIT_DEFAULT_DISCRETE_METHOD = PLFIT_LBFGS } plfit_discrete_method_t; typedef struct _plfit_result_t { double alpha; /* fitted power-law exponent */ double xmin; /* cutoff where the power-law behaviour kicks in */ double L; /* log-likelihood of the sample */ double D; /* test statistic for the KS test */ double p; /* p-value of the KS test */ } plfit_result_t; /********** structure that holds the options of plfit **********/ typedef struct _plfit_continuous_options_t { plfit_bool_t finite_size_correction; plfit_continuous_method_t xmin_method; } plfit_continuous_options_t; typedef struct _plfit_discrete_options_t { plfit_bool_t finite_size_correction; plfit_discrete_method_t alpha_method; struct { double min; double max; double step; } alpha; } plfit_discrete_options_t; int plfit_continuous_options_init(plfit_continuous_options_t* options); int plfit_discrete_options_init(plfit_discrete_options_t* options); extern const plfit_continuous_options_t plfit_continuous_default_options; extern const plfit_discrete_options_t plfit_discrete_default_options; /********** continuous power law distribution fitting **********/ int plfit_log_likelihood_continuous(double* xs, size_t n, double alpha, double xmin, double* l); int plfit_estimate_alpha_continuous(double* xs, size_t n, double xmin, const plfit_continuous_options_t* options, plfit_result_t* result); int plfit_estimate_alpha_continuous_sorted(double* xs, size_t n, double xmin, const plfit_continuous_options_t* options, plfit_result_t* result); int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* options, plfit_result_t* result); /********** discrete power law distribution fitting **********/ int plfit_estimate_alpha_discrete(double* xs, size_t n, double xmin, const plfit_discrete_options_t* options, plfit_result_t *result); int plfit_log_likelihood_discrete(double* xs, size_t n, double alpha, double xmin, double* l); int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options, plfit_result_t* result); __END_DECLS #endif /* __PLFIT_H__ */ igraph/src/plfit/arithmetic_sse_float.h0000644000176000001440000002122512325527074020033 0ustar ripleyusers/* * SSE/SSE3 implementation of vector oprations (32bit float). * * Copyright (c) 2007-2010 Naoaki Okazaki * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* $Id: arithmetic_sse_float.h 65 2010-01-29 12:19:16Z naoaki $ */ #include #if !defined(__APPLE__) #include #endif #include #if 1400 <= _MSC_VER #include #endif/*_MSC_VER*/ #if HAVE_XMMINTRIN_H #include #endif/*HAVE_XMMINTRIN_H*/ #if LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT #define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U) #else #define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.) #endif/*LBFGS_IEEE_FLOAT*/ inline static void* vecalloc(size_t size) { void *memblock = _aligned_malloc(size, 16); if (memblock != NULL) { memset(memblock, 0, size); } return memblock; } inline static void vecfree(void *memblock) { _aligned_free(memblock); } #define vecset(x, c, n) \ { \ int i; \ __m128 XMM0 = _mm_set_ps1(c); \ for (i = 0;i < (n);i += 16) { \ _mm_store_ps((x)+i , XMM0); \ _mm_store_ps((x)+i+ 4, XMM0); \ _mm_store_ps((x)+i+ 8, XMM0); \ _mm_store_ps((x)+i+12, XMM0); \ } \ } #define veccpy(y, x, n) \ { \ int i; \ for (i = 0;i < (n);i += 16) { \ __m128 XMM0 = _mm_load_ps((x)+i ); \ __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ __m128 XMM3 = _mm_load_ps((x)+i+12); \ _mm_store_ps((y)+i , XMM0); \ _mm_store_ps((y)+i+ 4, XMM1); \ _mm_store_ps((y)+i+ 8, XMM2); \ _mm_store_ps((y)+i+12, XMM3); \ } \ } #define vecncpy(y, x, n) \ { \ int i; \ const uint32_t mask = 0x80000000; \ __m128 XMM4 = _mm_load_ps1((float*)&mask); \ for (i = 0;i < (n);i += 16) { \ __m128 XMM0 = _mm_load_ps((x)+i ); \ __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ __m128 XMM3 = _mm_load_ps((x)+i+12); \ XMM0 = _mm_xor_ps(XMM0, XMM4); \ XMM1 = _mm_xor_ps(XMM1, XMM4); \ XMM2 = _mm_xor_ps(XMM2, XMM4); \ XMM3 = _mm_xor_ps(XMM3, XMM4); \ _mm_store_ps((y)+i , XMM0); \ _mm_store_ps((y)+i+ 4, XMM1); \ _mm_store_ps((y)+i+ 8, XMM2); \ _mm_store_ps((y)+i+12, XMM3); \ } \ } #define vecadd(y, x, c, n) \ { \ int i; \ __m128 XMM7 = _mm_set_ps1(c); \ for (i = 0;i < (n);i += 8) { \ __m128 XMM0 = _mm_load_ps((x)+i ); \ __m128 XMM1 = _mm_load_ps((x)+i+4); \ __m128 XMM2 = _mm_load_ps((y)+i ); \ __m128 XMM3 = _mm_load_ps((y)+i+4); \ XMM0 = _mm_mul_ps(XMM0, XMM7); \ XMM1 = _mm_mul_ps(XMM1, XMM7); \ XMM2 = _mm_add_ps(XMM2, XMM0); \ XMM3 = _mm_add_ps(XMM3, XMM1); \ _mm_store_ps((y)+i , XMM2); \ _mm_store_ps((y)+i+4, XMM3); \ } \ } #define vecdiff(z, x, y, n) \ { \ int i; \ for (i = 0;i < (n);i += 16) { \ __m128 XMM0 = _mm_load_ps((x)+i ); \ __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ __m128 XMM3 = _mm_load_ps((x)+i+12); \ __m128 XMM4 = _mm_load_ps((y)+i ); \ __m128 XMM5 = _mm_load_ps((y)+i+ 4); \ __m128 XMM6 = _mm_load_ps((y)+i+ 8); \ __m128 XMM7 = _mm_load_ps((y)+i+12); \ XMM0 = _mm_sub_ps(XMM0, XMM4); \ XMM1 = _mm_sub_ps(XMM1, XMM5); \ XMM2 = _mm_sub_ps(XMM2, XMM6); \ XMM3 = _mm_sub_ps(XMM3, XMM7); \ _mm_store_ps((z)+i , XMM0); \ _mm_store_ps((z)+i+ 4, XMM1); \ _mm_store_ps((z)+i+ 8, XMM2); \ _mm_store_ps((z)+i+12, XMM3); \ } \ } #define vecscale(y, c, n) \ { \ int i; \ __m128 XMM7 = _mm_set_ps1(c); \ for (i = 0;i < (n);i += 8) { \ __m128 XMM0 = _mm_load_ps((y)+i ); \ __m128 XMM1 = _mm_load_ps((y)+i+4); \ XMM0 = _mm_mul_ps(XMM0, XMM7); \ XMM1 = _mm_mul_ps(XMM1, XMM7); \ _mm_store_ps((y)+i , XMM0); \ _mm_store_ps((y)+i+4, XMM1); \ } \ } #define vecmul(y, x, n) \ { \ int i; \ for (i = 0;i < (n);i += 16) { \ __m128 XMM0 = _mm_load_ps((x)+i ); \ __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ __m128 XMM3 = _mm_load_ps((x)+i+12); \ __m128 XMM4 = _mm_load_ps((y)+i ); \ __m128 XMM5 = _mm_load_ps((y)+i+ 4); \ __m128 XMM6 = _mm_load_ps((y)+i+ 8); \ __m128 XMM7 = _mm_load_ps((y)+i+12); \ XMM4 = _mm_mul_ps(XMM4, XMM0); \ XMM5 = _mm_mul_ps(XMM5, XMM1); \ XMM6 = _mm_mul_ps(XMM6, XMM2); \ XMM7 = _mm_mul_ps(XMM7, XMM3); \ _mm_store_ps((y)+i , XMM4); \ _mm_store_ps((y)+i+ 4, XMM5); \ _mm_store_ps((y)+i+ 8, XMM6); \ _mm_store_ps((y)+i+12, XMM7); \ } \ } #if 3 <= __SSE__ /* Horizontal add with haddps SSE3 instruction. The work register (rw) is unused. */ #define __horizontal_sum(r, rw) \ r = _mm_hadd_ps(r, r); \ r = _mm_hadd_ps(r, r); #else /* Horizontal add with SSE instruction. The work register (rw) is used. */ #define __horizontal_sum(r, rw) \ rw = r; \ r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \ r = _mm_add_ps(r, rw); \ rw = r; \ r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \ r = _mm_add_ps(r, rw); #endif #define vecdot(s, x, y, n) \ { \ int i; \ __m128 XMM0 = _mm_setzero_ps(); \ __m128 XMM1 = _mm_setzero_ps(); \ __m128 XMM2, XMM3, XMM4, XMM5; \ for (i = 0;i < (n);i += 8) { \ XMM2 = _mm_load_ps((x)+i ); \ XMM3 = _mm_load_ps((x)+i+4); \ XMM4 = _mm_load_ps((y)+i ); \ XMM5 = _mm_load_ps((y)+i+4); \ XMM2 = _mm_mul_ps(XMM2, XMM4); \ XMM3 = _mm_mul_ps(XMM3, XMM5); \ XMM0 = _mm_add_ps(XMM0, XMM2); \ XMM1 = _mm_add_ps(XMM1, XMM3); \ } \ XMM0 = _mm_add_ps(XMM0, XMM1); \ __horizontal_sum(XMM0, XMM1); \ _mm_store_ss((s), XMM0); \ } #define vec2norm(s, x, n) \ { \ int i; \ __m128 XMM0 = _mm_setzero_ps(); \ __m128 XMM1 = _mm_setzero_ps(); \ __m128 XMM2, XMM3; \ for (i = 0;i < (n);i += 8) { \ XMM2 = _mm_load_ps((x)+i ); \ XMM3 = _mm_load_ps((x)+i+4); \ XMM2 = _mm_mul_ps(XMM2, XMM2); \ XMM3 = _mm_mul_ps(XMM3, XMM3); \ XMM0 = _mm_add_ps(XMM0, XMM2); \ XMM1 = _mm_add_ps(XMM1, XMM3); \ } \ XMM0 = _mm_add_ps(XMM0, XMM1); \ __horizontal_sum(XMM0, XMM1); \ XMM2 = XMM0; \ XMM1 = _mm_rsqrt_ss(XMM0); \ XMM3 = XMM1; \ XMM1 = _mm_mul_ss(XMM1, XMM1); \ XMM1 = _mm_mul_ss(XMM1, XMM3); \ XMM1 = _mm_mul_ss(XMM1, XMM0); \ XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \ XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \ XMM3 = _mm_add_ss(XMM3, XMM1); \ XMM3 = _mm_mul_ss(XMM3, XMM2); \ _mm_store_ss((s), XMM3); \ } #define vec2norminv(s, x, n) \ { \ int i; \ __m128 XMM0 = _mm_setzero_ps(); \ __m128 XMM1 = _mm_setzero_ps(); \ __m128 XMM2, XMM3; \ for (i = 0;i < (n);i += 16) { \ XMM2 = _mm_load_ps((x)+i ); \ XMM3 = _mm_load_ps((x)+i+4); \ XMM2 = _mm_mul_ps(XMM2, XMM2); \ XMM3 = _mm_mul_ps(XMM3, XMM3); \ XMM0 = _mm_add_ps(XMM0, XMM2); \ XMM1 = _mm_add_ps(XMM1, XMM3); \ } \ XMM0 = _mm_add_ps(XMM0, XMM1); \ __horizontal_sum(XMM0, XMM1); \ XMM2 = XMM0; \ XMM1 = _mm_rsqrt_ss(XMM0); \ XMM3 = XMM1; \ XMM1 = _mm_mul_ss(XMM1, XMM1); \ XMM1 = _mm_mul_ss(XMM1, XMM3); \ XMM1 = _mm_mul_ss(XMM1, XMM0); \ XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \ XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \ XMM3 = _mm_add_ss(XMM3, XMM1); \ _mm_store_ss((s), XMM3); \ } igraph/src/plfit/platform.h0000644000176000001440000000250612325527074015470 0ustar ripleyusers/* platform.h * * Copyright (C) 2010-2011 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __PLATFORM_H__ #define __PLATFORM_H__ #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include __BEGIN_DECLS #ifdef _MSC_VER #define snprintf sprintf_s #define inline __inline #define isnan(x) _isnan(x) #define isfinite(x) _finite(x) #endif #ifndef INFINITY # define INFINITY (1.0/0.0) #endif #ifndef NAN # define NAN (INFINITY-INFINITY) #endif __END_DECLS #endif /* __PLATFORM_H__ */ igraph/src/plfit/lbfgs.h0000644000176000001440000007627612325527074014760 0ustar ripleyusers/* * C library of Limited memory BFGS (L-BFGS). * * Copyright (c) 1990, Jorge Nocedal * Copyright (c) 2007-2010 Naoaki Okazaki * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* $Id: lbfgs.h 65 2010-01-29 12:19:16Z naoaki $ */ #ifndef __LBFGS_H__ #define __LBFGS_H__ #ifdef __cplusplus extern "C" { #endif/*__cplusplus*/ /* * The default precision of floating point values is 64bit (double). */ #ifndef LBFGS_FLOAT #define LBFGS_FLOAT 64 #endif/*LBFGS_FLOAT*/ /* * Activate optimization routines for IEEE754 floating point values. */ #ifndef LBFGS_IEEE_FLOAT #define LBFGS_IEEE_FLOAT 1 #endif/*LBFGS_IEEE_FLOAT*/ #if LBFGS_FLOAT == 32 typedef float lbfgsfloatval_t; #elif LBFGS_FLOAT == 64 typedef double lbfgsfloatval_t; #else #error "libLBFGS supports single (float; LBFGS_FLOAT = 32) or double (double; LBFGS_FLOAT=64) precision only." #endif /** * \addtogroup liblbfgs_api libLBFGS API * @{ * * The libLBFGS API. */ /** * Return values of lbfgs(). * * Roughly speaking, a negative value indicates an error. */ enum { /** L-BFGS reaches convergence. */ LBFGS_SUCCESS = 0, LBFGS_CONVERGENCE = 0, LBFGS_STOP, /** The initial variables already minimize the objective function. */ LBFGS_ALREADY_MINIMIZED, /** Unknown error. */ LBFGSERR_UNKNOWNERROR = -1024, /** Logic error. */ LBFGSERR_LOGICERROR, /** Insufficient memory. */ LBFGSERR_OUTOFMEMORY, /** The minimization process has been canceled. */ LBFGSERR_CANCELED, /** Invalid number of variables specified. */ LBFGSERR_INVALID_N, /** Invalid number of variables (for SSE) specified. */ LBFGSERR_INVALID_N_SSE, /** The array x must be aligned to 16 (for SSE). */ LBFGSERR_INVALID_X_SSE, /** Invalid parameter lbfgs_parameter_t::epsilon specified. */ LBFGSERR_INVALID_EPSILON, /** Invalid parameter lbfgs_parameter_t::past specified. */ LBFGSERR_INVALID_TESTPERIOD, /** Invalid parameter lbfgs_parameter_t::delta specified. */ LBFGSERR_INVALID_DELTA, /** Invalid parameter lbfgs_parameter_t::linesearch specified. */ LBFGSERR_INVALID_LINESEARCH, /** Invalid parameter lbfgs_parameter_t::max_step specified. */ LBFGSERR_INVALID_MINSTEP, /** Invalid parameter lbfgs_parameter_t::max_step specified. */ LBFGSERR_INVALID_MAXSTEP, /** Invalid parameter lbfgs_parameter_t::ftol specified. */ LBFGSERR_INVALID_FTOL, /** Invalid parameter lbfgs_parameter_t::wolfe specified. */ LBFGSERR_INVALID_WOLFE, /** Invalid parameter lbfgs_parameter_t::gtol specified. */ LBFGSERR_INVALID_GTOL, /** Invalid parameter lbfgs_parameter_t::xtol specified. */ LBFGSERR_INVALID_XTOL, /** Invalid parameter lbfgs_parameter_t::max_linesearch specified. */ LBFGSERR_INVALID_MAXLINESEARCH, /** Invalid parameter lbfgs_parameter_t::orthantwise_c specified. */ LBFGSERR_INVALID_ORTHANTWISE, /** Invalid parameter lbfgs_parameter_t::orthantwise_start specified. */ LBFGSERR_INVALID_ORTHANTWISE_START, /** Invalid parameter lbfgs_parameter_t::orthantwise_end specified. */ LBFGSERR_INVALID_ORTHANTWISE_END, /** The line-search step went out of the interval of uncertainty. */ LBFGSERR_OUTOFINTERVAL, /** A logic error occurred; alternatively, the interval of uncertainty became too small. */ LBFGSERR_INCORRECT_TMINMAX, /** A rounding error occurred; alternatively, no line-search step satisfies the sufficient decrease and curvature conditions. */ LBFGSERR_ROUNDING_ERROR, /** The line-search step became smaller than lbfgs_parameter_t::min_step. */ LBFGSERR_MINIMUMSTEP, /** The line-search step became larger than lbfgs_parameter_t::max_step. */ LBFGSERR_MAXIMUMSTEP, /** The line-search routine reaches the maximum number of evaluations. */ LBFGSERR_MAXIMUMLINESEARCH, /** The algorithm routine reaches the maximum number of iterations. */ LBFGSERR_MAXIMUMITERATION, /** Relative width of the interval of uncertainty is at most lbfgs_parameter_t::xtol. */ LBFGSERR_WIDTHTOOSMALL, /** A logic error (negative line-search step) occurred. */ LBFGSERR_INVALIDPARAMETERS, /** The current search direction increases the objective function value. */ LBFGSERR_INCREASEGRADIENT, }; /** * Line search algorithms. */ enum { /** The default algorithm (MoreThuente method). */ LBFGS_LINESEARCH_DEFAULT = 0, /** MoreThuente method proposd by More and Thuente. */ LBFGS_LINESEARCH_MORETHUENTE = 0, /** * Backtracking method with the Armijo condition. * The backtracking method finds the step length such that it satisfies * the sufficient decrease (Armijo) condition, * - f(x + a * d) <= f(x) + lbfgs_parameter_t::ftol * a * g(x)^T d, * * where x is the current point, d is the current search direction, and * a is the step length. */ LBFGS_LINESEARCH_BACKTRACKING_ARMIJO = 1, /** The backtracking method with the defualt (regular Wolfe) condition. */ LBFGS_LINESEARCH_BACKTRACKING = 2, /** * Backtracking method with regular Wolfe condition. * The backtracking method finds the step length such that it satisfies * both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) * and the curvature condition, * - g(x + a * d)^T d >= lbfgs_parameter_t::wolfe * g(x)^T d, * * where x is the current point, d is the current search direction, and * a is the step length. */ LBFGS_LINESEARCH_BACKTRACKING_WOLFE = 2, /** * Backtracking method with strong Wolfe condition. * The backtracking method finds the step length such that it satisfies * both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) * and the following condition, * - |g(x + a * d)^T d| <= lbfgs_parameter_t::wolfe * |g(x)^T d|, * * where x is the current point, d is the current search direction, and * a is the step length. */ LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE = 3, }; /** * L-BFGS optimization parameters. * Call lbfgs_parameter_init() function to initialize parameters to the * default values. */ typedef struct { /** * The number of corrections to approximate the inverse hessian matrix. * The L-BFGS routine stores the computation results of previous \ref m * iterations to approximate the inverse hessian matrix of the current * iteration. This parameter controls the size of the limited memories * (corrections). The default value is \c 6. Values less than \c 3 are * not recommended. Large values will result in excessive computing time. */ int m; /** * Epsilon for convergence test. * This parameter determines the accuracy with which the solution is to * be found. A minimization terminates when * ||g|| < \ref epsilon * max(1, ||x||), * where ||.|| denotes the Euclidean (L2) norm. The default value is * \c 1e-5. */ lbfgsfloatval_t epsilon; /** * Distance for delta-based convergence test. * This parameter determines the distance, in iterations, to compute * the rate of decrease of the objective function. If the value of this * parameter is zero, the library does not perform the delta-based * convergence test. The default value is \c 0. */ int past; /** * Delta for convergence test. * This parameter determines the minimum rate of decrease of the * objective function. The library stops iterations when the * following condition is met: * (f' - f) / f < \ref delta, * where f' is the objective value of \ref past iterations ago, and f is * the objective value of the current iteration. * The default value is \c 0. */ lbfgsfloatval_t delta; /** * The maximum number of iterations. * The lbfgs() function terminates an optimization process with * ::LBFGSERR_MAXIMUMITERATION status code when the iteration count * exceedes this parameter. Setting this parameter to zero continues an * optimization process until a convergence or error. The default value * is \c 0. */ int max_iterations; /** * The line search algorithm. * This parameter specifies a line search algorithm to be used by the * L-BFGS routine. */ int linesearch; /** * The maximum number of trials for the line search. * This parameter controls the number of function and gradients evaluations * per iteration for the line search routine. The default value is \c 20. */ int max_linesearch; /** * The minimum step of the line search routine. * The default value is \c 1e-20. This value need not be modified unless * the exponents are too large for the machine being used, or unless the * problem is extremely badly scaled (in which case the exponents should * be increased). */ lbfgsfloatval_t min_step; /** * The maximum step of the line search. * The default value is \c 1e+20. This value need not be modified unless * the exponents are too large for the machine being used, or unless the * problem is extremely badly scaled (in which case the exponents should * be increased). */ lbfgsfloatval_t max_step; /** * A parameter to control the accuracy of the line search routine. * The default value is \c 1e-4. This parameter should be greater * than zero and smaller than \c 0.5. */ lbfgsfloatval_t ftol; /** * A coefficient for the Wolfe condition. * This parameter is valid only when the backtracking line-search * algorithm is used with the Wolfe condition, * ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE or * ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE . * The default value is \c 0.9. This parameter should be greater * the \ref ftol parameter and smaller than \c 1.0. */ lbfgsfloatval_t wolfe; /** * A parameter to control the accuracy of the line search routine. * The default value is \c 0.9. If the function and gradient * evaluations are inexpensive with respect to the cost of the * iteration (which is sometimes the case when solving very large * problems) it may be advantageous to set this parameter to a small * value. A typical small value is \c 0.1. This parameter shuold be * greater than the \ref ftol parameter (\c 1e-4) and smaller than * \c 1.0. */ lbfgsfloatval_t gtol; /** * The machine precision for floating-point values. * This parameter must be a positive value set by a client program to * estimate the machine precision. The line search routine will terminate * with the status code (::LBFGSERR_ROUNDING_ERROR) if the relative width * of the interval of uncertainty is less than this parameter. */ lbfgsfloatval_t xtol; /** * Coeefficient for the L1 norm of variables. * This parameter should be set to zero for standard minimization * problems. Setting this parameter to a positive value activates * Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) method, which * minimizes the objective function F(x) combined with the L1 norm |x| * of the variables, {F(x) + C |x|}. This parameter is the coeefficient * for the |x|, i.e., C. As the L1 norm |x| is not differentiable at * zero, the library modifies function and gradient evaluations from * a client program suitably; a client program thus have only to return * the function value F(x) and gradients G(x) as usual. The default value * is zero. */ lbfgsfloatval_t orthantwise_c; /** * Start index for computing L1 norm of the variables. * This parameter is valid only for OWL-QN method * (i.e., \ref orthantwise_c != 0). This parameter b (0 <= b < N) * specifies the index number from which the library computes the * L1 norm of the variables x, * |x| := |x_{b}| + |x_{b+1}| + ... + |x_{N}| . * In other words, variables x_1, ..., x_{b-1} are not used for * computing the L1 norm. Setting b (0 < b < N), one can protect * variables, x_1, ..., x_{b-1} (e.g., a bias term of logistic * regression) from being regularized. The default value is zero. */ int orthantwise_start; /** * End index for computing L1 norm of the variables. * This parameter is valid only for OWL-QN method * (i.e., \ref orthantwise_c != 0). This parameter e (0 < e <= N) * specifies the index number at which the library stops computing the * L1 norm of the variables x, */ int orthantwise_end; } lbfgs_parameter_t; /** * Callback interface to provide objective function and gradient evaluations. * * The lbfgs() function call this function to obtain the values of objective * function and its gradients when needed. A client program must implement * this function to evaluate the values of the objective function and its * gradients, given current values of variables. * * @param instance The user data sent for lbfgs() function by the client. * @param x The current values of variables. * @param g The gradient vector. The callback function must compute * the gradient values for the current variables. * @param n The number of variables. * @param step The current step of the line search routine. * @retval lbfgsfloatval_t The value of the objective function for the current * variables. */ typedef lbfgsfloatval_t (*lbfgs_evaluate_t)( void *instance, const lbfgsfloatval_t *x, lbfgsfloatval_t *g, const int n, const lbfgsfloatval_t step ); /** * Callback interface to receive the progress of the optimization process. * * The lbfgs() function call this function for each iteration. Implementing * this function, a client program can store or display the current progress * of the optimization process. * * @param instance The user data sent for lbfgs() function by the client. * @param x The current values of variables. * @param g The current gradient values of variables. * @param fx The current value of the objective function. * @param xnorm The Euclidean norm of the variables. * @param gnorm The Euclidean norm of the gradients. * @param step The line-search step used for this iteration. * @param n The number of variables. * @param k The iteration count. * @param ls The number of evaluations called for this iteration. * @retval int Zero to continue the optimization process. Returning a * non-zero value will cancel the optimization process. */ typedef int (*lbfgs_progress_t)( void *instance, const lbfgsfloatval_t *x, const lbfgsfloatval_t *g, const lbfgsfloatval_t fx, const lbfgsfloatval_t xnorm, const lbfgsfloatval_t gnorm, const lbfgsfloatval_t step, int n, int k, int ls ); /* A user must implement a function compatible with ::lbfgs_evaluate_t (evaluation callback) and pass the pointer to the callback function to lbfgs() arguments. Similarly, a user can implement a function compatible with ::lbfgs_progress_t (progress callback) to obtain the current progress (e.g., variables, function value, ||G||, etc) and to cancel the iteration process if necessary. Implementation of a progress callback is optional: a user can pass \c NULL if progress notification is not necessary. In addition, a user must preserve two requirements: - The number of variables must be multiples of 16 (this is not 4). - The memory block of variable array ::x must be aligned to 16. This algorithm terminates an optimization when: ||G|| < \epsilon \cdot \max(1, ||x||) . In this formula, ||.|| denotes the Euclidean norm. */ /** * Start a L-BFGS optimization. * * @param n The number of variables. * @param x The array of variables. A client program can set * default values for the optimization and receive the * optimization result through this array. This array * must be allocated by ::lbfgs_malloc function * for libLBFGS built with SSE/SSE2 optimization routine * enabled. The library built without SSE/SSE2 * optimization does not have such a requirement. * @param ptr_fx The pointer to the variable that receives the final * value of the objective function for the variables. * This argument can be set to \c NULL if the final * value of the objective function is unnecessary. * @param proc_evaluate The callback function to provide function and * gradient evaluations given a current values of * variables. A client program must implement a * callback function compatible with \ref * lbfgs_evaluate_t and pass the pointer to the * callback function. * @param proc_progress The callback function to receive the progress * (the number of iterations, the current value of * the objective function) of the minimization * process. This argument can be set to \c NULL if * a progress report is unnecessary. * @param instance A user data for the client program. The callback * functions will receive the value of this argument. * @param param The pointer to a structure representing parameters for * L-BFGS optimization. A client program can set this * parameter to \c NULL to use the default parameters. * Call lbfgs_parameter_init() function to fill a * structure with the default values. * @retval int The status code. This function returns zero if the * minimization process terminates without an error. A * non-zero value indicates an error. */ int lbfgs( int n, lbfgsfloatval_t *x, lbfgsfloatval_t *ptr_fx, lbfgs_evaluate_t proc_evaluate, lbfgs_progress_t proc_progress, void *instance, lbfgs_parameter_t *param ); /** * Initialize L-BFGS parameters to the default values. * * Call this function to fill a parameter structure with the default values * and overwrite parameter values if necessary. * * @param param The pointer to the parameter structure. */ void lbfgs_parameter_init(lbfgs_parameter_t *param); /** * Allocate an array for variables. * * This function allocates an array of variables for the convenience of * ::lbfgs function; the function has a requreiemt for a variable array * when libLBFGS is built with SSE/SSE2 optimization routines. A user does * not have to use this function for libLBFGS built without SSE/SSE2 * optimization. * * @param n The number of variables. */ lbfgsfloatval_t* lbfgs_malloc(int n); /** * Free an array of variables. * * @param x The array of variables allocated by ::lbfgs_malloc * function. */ void lbfgs_free(lbfgsfloatval_t *x); /** @} */ #ifdef __cplusplus } #endif/*__cplusplus*/ /** @mainpage libLBFGS: a library of Limited-memory Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) @section intro Introduction This library is a C port of the implementation of Limited-memory Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) method written by Jorge Nocedal. The original FORTRAN source code is available at: http://www.ece.northwestern.edu/~nocedal/lbfgs.html The L-BFGS method solves the unconstrainted minimization problem,
    minimize F(x), x = (x1, x2, ..., xN),
only if the objective function F(x) and its gradient G(x) are computable. The well-known Newton's method requires computation of the inverse of the hessian matrix of the objective function. However, the computational cost for the inverse hessian matrix is expensive especially when the objective function takes a large number of variables. The L-BFGS method iteratively finds a minimizer by approximating the inverse hessian matrix by information from last m iterations. This innovation saves the memory storage and computational time drastically for large-scaled problems. Among the various ports of L-BFGS, this library provides several features: - Optimization with L1-norm (Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) method): In addition to standard minimization problems, the library can minimize a function F(x) combined with L1-norm |x| of the variables, {F(x) + C |x|}, where C is a constant scalar parameter. This feature is useful for estimating parameters of sparse log-linear models (e.g., logistic regression and maximum entropy) with L1-regularization (or Laplacian prior). - Clean C code: Unlike C codes generated automatically by f2c (Fortran 77 into C converter), this port includes changes based on my interpretations, improvements, optimizations, and clean-ups so that the ported code would be well-suited for a C code. In addition to comments inherited from the original code, a number of comments were added through my interpretations. - Callback interface: The library receives function and gradient values via a callback interface. The library also notifies the progress of the optimization by invoking a callback function. In the original implementation, a user had to set function and gradient values every time the function returns for obtaining updated values. - Thread safe: The library is thread-safe, which is the secondary gain from the callback interface. - Cross platform. The source code can be compiled on Microsoft Visual Studio 2005, GNU C Compiler (gcc), etc. - Configurable precision: A user can choose single-precision (float) or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro. - SSE/SSE2 optimization: This library includes SSE/SSE2 optimization (written in compiler intrinsics) for vector arithmetic operations on Intel/AMD processors. The library uses SSE for float values and SSE2 for double values. The SSE/SSE2 optimization routine is disabled by default. This library is used by: - CRFsuite: A fast implementation of Conditional Random Fields (CRFs) - Classias: A collection of machine-learning algorithms for classification - mlegp: an R package for maximum likelihood estimates for Gaussian processes - imaging2: the imaging2 class library - Algorithm::LBFGS - Perl extension for L-BFGS - YAP-LBFGS (an interface to call libLBFGS from YAP Prolog) @section download Download - Source code libLBFGS is distributed under the term of the MIT license. @section changelog History - Version 1.9 (2010-01-29): - Fixed a mistake in checking the validity of the parameters "ftol" and "wolfe"; this was discovered by Kevin S. Van Horn. - Version 1.8 (2009-07-13): - Accepted the patch submitted by Takashi Imamichi; the backtracking method now has three criteria for choosing the step length: - ::LBFGS_LINESEARCH_BACKTRACKING_ARMIJO: sufficient decrease (Armijo) condition only - ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE: regular Wolfe condition (sufficient decrease condition + curvature condition) - ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE: strong Wolfe condition - Updated the documentation to explain the above three criteria. - Version 1.7 (2009-02-28): - Improved OWL-QN routines for stability. - Removed the support of OWL-QN method in MoreThuente algorithm because it accidentally fails in early stages of iterations for some objectives. Because of this change, the OW-LQN method must be used with the backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING), or the library returns ::LBFGSERR_INVALID_LINESEARCH. - Renamed line search algorithms as follows: - ::LBFGS_LINESEARCH_BACKTRACKING: regular Wolfe condition. - ::LBFGS_LINESEARCH_BACKTRACKING_LOOSE: regular Wolfe condition. - ::LBFGS_LINESEARCH_BACKTRACKING_STRONG: strong Wolfe condition. - Source code clean-up. - Version 1.6 (2008-11-02): - Improved line-search algorithm with strong Wolfe condition, which was contributed by Takashi Imamichi. This routine is now default for ::LBFGS_LINESEARCH_BACKTRACKING. The previous line search algorithm with regular Wolfe condition is still available as ::LBFGS_LINESEARCH_BACKTRACKING_LOOSE. - Configurable stop index for L1-norm computation. A member variable ::lbfgs_parameter_t::orthantwise_end was added to specify the index number at which the library stops computing the L1 norm of the variables. This is useful to prevent some variables from being regularized by the OW-LQN method. - A sample program written in C++ (sample/sample.cpp). - Version 1.5 (2008-07-10): - Configurable starting index for L1-norm computation. A member variable ::lbfgs_parameter_t::orthantwise_start was added to specify the index number from which the library computes the L1 norm of the variables. This is useful to prevent some variables from being regularized by the OWL-QN method. - Fixed a zero-division error when the initial variables have already been a minimizer (reported by Takashi Imamichi). In this case, the library returns ::LBFGS_ALREADY_MINIMIZED status code. - Defined ::LBFGS_SUCCESS status code as zero; removed unused constants, LBFGSFALSE and LBFGSTRUE. - Fixed a compile error in an implicit down-cast. - Version 1.4 (2008-04-25): - Configurable line search algorithms. A member variable ::lbfgs_parameter_t::linesearch was added to choose either MoreThuente method (::LBFGS_LINESEARCH_MORETHUENTE) or backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING). - Fixed a bug: the previous version did not compute psuedo-gradients properly in the line search routines for OWL-QN. This bug might quit an iteration process too early when the OWL-QN routine was activated (0 < ::lbfgs_parameter_t::orthantwise_c). - Configure script for POSIX environments. - SSE/SSE2 optimizations with GCC. - New functions ::lbfgs_malloc and ::lbfgs_free to use SSE/SSE2 routines transparently. It is uncessary to use these functions for libLBFGS built without SSE/SSE2 routines; you can still use any memory allocators if SSE/SSE2 routines are disabled in libLBFGS. - Version 1.3 (2007-12-16): - An API change. An argument was added to lbfgs() function to receive the final value of the objective function. This argument can be set to \c NULL if the final value is unnecessary. - Fixed a null-pointer bug in the sample code (reported by Takashi Imamichi). - Added build scripts for Microsoft Visual Studio 2005 and GCC. - Added README file. - Version 1.2 (2007-12-13): - Fixed a serious bug in orthant-wise L-BFGS. An important variable was used without initialization. - Version 1.1 (2007-12-01): - Implemented orthant-wise L-BFGS. - Implemented lbfgs_parameter_init() function. - Fixed several bugs. - API documentation. - Version 1.0 (2007-09-20): - Initial release. @section api Documentation - @ref liblbfgs_api "libLBFGS API" @section sample Sample code @include sample.c @section ack Acknowledgements The L-BFGS algorithm is described in: - Jorge Nocedal. Updating Quasi-Newton Matrices with Limited Storage. Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. - Dong C. Liu and Jorge Nocedal. On the limited memory BFGS method for large scale optimization. Mathematical Programming B, Vol. 45, No. 3, pp. 503-528, 1989. The line search algorithms used in this implementation are described in: - John E. Dennis and Robert B. Schnabel. Numerical Methods for Unconstrained Optimization and Nonlinear Equations, Englewood Cliffs, 1983. - Jorge J. More and David J. Thuente. Line search algorithm with guaranteed sufficient decrease. ACM Transactions on Mathematical Software (TOMS), Vol. 20, No. 3, pp. 286-307, 1994. This library also implements Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) method presented in: - Galen Andrew and Jianfeng Gao. Scalable training of L1-regularized log-linear models. In Proceedings of the 24th International Conference on Machine Learning (ICML 2007), pp. 33-40, 2007. Special thanks go to: - Yoshimasa Tsuruoka and Daisuke Okanohara for technical information about OWL-QN - Takashi Imamichi for the useful enhancements of the backtracking method Finally I would like to thank the original author, Jorge Nocedal, who has been distributing the effieicnt and explanatory implementation in an open source licence. @section reference Reference - L-BFGS by Jorge Nocedal. - Orthant-Wise Limited-memory Quasi-Newton Optimizer for L1-regularized Objectives by Galen Andrew. - C port (via f2c) by Taku Kudo. - C#/C++/Delphi/VisualBasic6 port in ALGLIB. - Computational Crystallography Toolbox includes scitbx::lbfgs. */ #endif/*__LBFGS_H__*/ igraph/src/igraph_estack.h0000644000176000001440000000302712325527073015330 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_ESTACK_H #define IGRAPH_ESTACK_H #include "igraph_stack.h" #include "igraph_vector.h" typedef struct igraph_estack_t { igraph_stack_long_t stack; igraph_vector_bool_t isin; } igraph_estack_t; int igraph_estack_init(igraph_estack_t *s, long int setsize, long int stacksize); void igraph_estack_destroy(igraph_estack_t *s); int igraph_estack_push(igraph_estack_t *s, long int elem); long int igraph_estack_pop(igraph_estack_t *s); igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, long int elem); long int igraph_estack_size(const igraph_estack_t *s); int igraph_estack_print(const igraph_estack_t *s); #endif igraph/src/evolver_cit.c0000644000176000001440000001243712325527073015045 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_revolver.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_constructors.h" #include "igraph_psumtree.h" #include "config.h" #include /* This file contains the method for creating citation networks */ int igraph_i_create_outseq(igraph_vector_t *real_outseq, igraph_integer_t nodes, const igraph_vector_t *outseq, const igraph_vector_t *outdist, igraph_integer_t m, igraph_integer_t *edges) { long int no_of_edges=0; if (outseq && nodes != igraph_vector_size(outseq)) { IGRAPH_ERROR("Invalid out-degree sequence length", IGRAPH_EINVAL); } if (!outseq && outdist && igraph_vector_size(outdist)==0) { IGRAPH_ERROR("Invalid out-degree distribution length", IGRAPH_EINVAL); } if (!outseq && !outdist && m<0) { IGRAPH_ERROR("Invalid constant out-degree", IGRAPH_EINVAL); } if (outseq) { igraph_vector_clear(real_outseq); igraph_vector_append(real_outseq, outseq); no_of_edges=(long int) (igraph_vector_sum(real_outseq) - VECTOR(*real_outseq)[0]); } else if (outdist) { igraph_vector_t cumsum; long int i, n=igraph_vector_size(outdist); IGRAPH_VECTOR_INIT_FINALLY(&cumsum, n+1); IGRAPH_CHECK(igraph_vector_resize(real_outseq, nodes)); VECTOR(cumsum)[0]=0; for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_constructors.h" #include "igraph_memory.h" #include "igraph_interface.h" #include "igraph_attributes.h" #include "igraph_adjlist.h" #include "igraph_interrupt_internal.h" #include "igraph_dqueue.h" #include "config.h" #include #include #include /** * \section about_generators * * Graph generators create graphs. * * Almost all functions which create graph objects are documented * here. The exceptions are \ref igraph_subgraph() and alike, these * create graphs based on another graph. */ /** * \ingroup generators * \function igraph_create * \brief Creates a graph with the specified edges. * * \param graph An uninitialized graph object. * \param edges The edges to add, the first two elements are the first * edge, etc. * \param n The number of vertices in the graph, if smaller or equal * to the highest vertex id in the \p edges vector it * will be increased automatically. So it is safe to give 0 * here. * \param directed Boolean, whether to create a directed graph or * not. If yes, then the first edge points from the first * vertex id in \p edges to the second, etc. * \return Error code: * \c IGRAPH_EINVEVECTOR: invalid edges * vector (odd number of vertices). * \c IGRAPH_EINVVID: invalid (negative) * vertex id. * * Time complexity: O(|V|+|E|), * |V| is the number of vertices, * |E| the number of edges in the * graph. * * \example examples/simple/igraph_create.c */ int igraph_create(igraph_t *graph, const igraph_vector_t *edges, igraph_integer_t n, igraph_bool_t directed) { igraph_real_t max=igraph_vector_max(edges)+1; if (igraph_vector_size(edges) % 2 != 0) { IGRAPH_ERROR("Invalid (odd) edges vector", IGRAPH_EINVEVECTOR); } if (!igraph_vector_isininterval(edges, 0, max-1)) { IGRAPH_ERROR("Invalid (negative) vertex id", IGRAPH_EINVVID); } IGRAPH_CHECK(igraph_empty(graph, n, directed)); IGRAPH_FINALLY(igraph_destroy, graph); if (igraph_vector_size(edges)>0) { igraph_integer_t vc=igraph_vcount(graph); if (vc < max) { IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) (max-vc), 0)); } IGRAPH_CHECK(igraph_add_edges(graph, edges, 0)); } IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_adjacency_directed(igraph_matrix_t *adjmatrix, igraph_vector_t *edges); int igraph_i_adjacency_max(igraph_matrix_t *adjmatrix, igraph_vector_t *edges); int igraph_i_adjacency_upper(igraph_matrix_t *adjmatrix, igraph_vector_t *edges); int igraph_i_adjacency_lower(igraph_matrix_t *adjmatrix, igraph_vector_t *edges); int igraph_i_adjacency_min(igraph_matrix_t *adjmatrix, igraph_vector_t *edges); int igraph_i_adjacency_directed(igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { long int no_of_nodes=igraph_matrix_nrow(adjmatrix); long int i, j, k; for (i=0; iM2) { M1=M2; } for (k=0; kM2) { M1=M2; } if (M1 == 0.0) continue; if (i==j && !loops) { continue; } IGRAPH_CHECK(igraph_vector_push_back(edges, i)); IGRAPH_CHECK(igraph_vector_push_back(edges, j)); IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); } } return 0; } /** * \ingroup generators * \function igraph_weighted_adjacency * \brief Creates a graph object from a weighted adjacency matrix. * * The order of the vertices in the matrix is preserved, i.e. the vertex * corresponding to the first row/column will be vertex with id 0, the * next row is for vertex 1, etc. * \param graph Pointer to an uninitialized graph object. * \param adjmatrix The weighted adjacency matrix. How it is interpreted * depends on the \p mode argument. The common feature is that * edges with zero weights are considered nonexistent (however, * negative weights are permitted). * \param mode Constant to specify how the given matrix is interpreted * as an adjacency matrix. Possible values * (A(i,j) * is the element in row i and column * j in the adjacency matrix * \p adjmatrix): * \clist * \cli IGRAPH_ADJ_DIRECTED * the graph will be directed and * an element gives the weight of the edge between two vertices. * \cli IGRAPH_ADJ_UNDIRECTED * this is the same as \c IGRAPH_ADJ_MAX, * for convenience. * \cli IGRAPH_ADJ_MAX * undirected graph will be created * and the weight of the edge between vertices * i and * j is * max(A(i,j), A(j,i)). * \cli IGRAPH_ADJ_MIN * undirected graph will be created * with edge weight min(A(i,j), A(j,i)) * between vertices * i and * j. * \cli IGRAPH_ADJ_PLUS * undirected graph will be created * with edge weight A(i,j)+A(j,i) * between vertices * i and * j. * \cli IGRAPH_ADJ_UPPER * undirected graph will be created, * only the upper right triangle (including the diagonal) is * used for the edge weights. * \cli IGRAPH_ADJ_LOWER * undirected graph will be created, * only the lower left triangle (including the diagonal) is * used for the edge weights. * \endclist * \param attr the name of the attribute that will store the edge weights. * If \c NULL , it will use \c weight as the attribute name. * \param loops Logical scalar, whether to ignore the diagonal elements * in the adjacency matrix. * \return Error code, * \c IGRAPH_NONSQUARE: non-square matrix. * * Time complexity: O(|V||V|), * |V| is the number of vertices in the graph. * * \example examples/simple/igraph_weighted_adjacency.c */ int igraph_weighted_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, const char* attr, igraph_bool_t loops) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; igraph_vector_t weights=IGRAPH_VECTOR_NULL; const char* default_attr = "weight"; igraph_vector_ptr_t attr_vec; igraph_attribute_record_t attr_rec; long int no_of_nodes; /* Some checks */ if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&weights, 0); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1); /* Collect the edges */ no_of_nodes=igraph_matrix_nrow(adjmatrix); switch (mode) { case IGRAPH_ADJ_DIRECTED: IGRAPH_CHECK(igraph_i_weighted_adjacency_directed(adjmatrix, &edges, &weights, loops)); break; case IGRAPH_ADJ_MAX: IGRAPH_CHECK(igraph_i_weighted_adjacency_max(adjmatrix, &edges, &weights, loops)); break; case IGRAPH_ADJ_UPPER: IGRAPH_CHECK(igraph_i_weighted_adjacency_upper(adjmatrix, &edges, &weights, loops)); break; case IGRAPH_ADJ_LOWER: IGRAPH_CHECK(igraph_i_weighted_adjacency_lower(adjmatrix, &edges, &weights, loops)); break; case IGRAPH_ADJ_MIN: IGRAPH_CHECK(igraph_i_weighted_adjacency_min(adjmatrix, &edges, &weights, loops)); break; case IGRAPH_ADJ_PLUS: IGRAPH_CHECK(igraph_i_weighted_adjacency_plus(adjmatrix, &edges, &weights, loops)); break; } /* Prepare attribute record */ attr_rec.name = attr ? attr : default_attr; attr_rec.type = IGRAPH_ATTRIBUTE_NUMERIC; attr_rec.value = &weights; VECTOR(attr_vec)[0] = &attr_rec; /* Create graph */ IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); IGRAPH_FINALLY(igraph_destroy, graph); if (igraph_vector_size(&edges)>0) { IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); } IGRAPH_FINALLY_CLEAN(1); /* Cleanup */ igraph_vector_destroy(&edges); igraph_vector_destroy(&weights); igraph_vector_ptr_destroy(&attr_vec); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \ingroup generators * \function igraph_star * \brief Creates a \em star graph, every vertex connects only to the center. * * \param graph Pointer to an uninitialized graph object, this will * be the result. * \param n Integer constant, the number of vertices in the graph. * \param mode Constant, gives the type of the star graph to * create. Possible values: * \clist * \cli IGRAPH_STAR_OUT * directed star graph, edges point * \em from the center to the other vertices. * \cli IGRAPH_STAR_IN * directed star graph, edges point * \em to the center from the other vertices. * \cli IGRAPH_STAR_MUTUAL * directed star graph with mutual edges. * \cli IGRAPH_STAR_UNDIRECTED * an undirected star graph is * created. * \endclist * \param center Id of the vertex which will be the center of the * graph. * \return Error code: * \clist * \cli IGRAPH_EINVVID * invalid number of vertices. * \cli IGRAPH_EINVAL * invalid center vertex. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(|V|), the * number of vertices in the graph. * * \sa \ref igraph_lattice(), \ref igraph_ring(), \ref igraph_tree() * for creating other regular structures. * * \example examples/simple/igraph_star.c */ int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, igraph_integer_t center) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; long int i; if (n<0) { IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVVID); } if (center<0 || center >n-1) { IGRAPH_ERROR("Invalid center vertex", IGRAPH_EINVAL); } if (mode != IGRAPH_STAR_OUT && mode != IGRAPH_STAR_IN && mode != IGRAPH_STAR_MUTUAL && mode != IGRAPH_STAR_UNDIRECTED) { IGRAPH_ERROR("invalid mode", IGRAPH_EINVMODE); } if (mode != IGRAPH_STAR_MUTUAL) { IGRAPH_VECTOR_INIT_FINALLY(&edges, (n-1)*2); } else { IGRAPH_VECTOR_INIT_FINALLY(&edges, (n-1)*2*2); } if (mode == IGRAPH_STAR_OUT) { for (i=0; i 0) { weights[0]=1; for (i=1; i= 2) { IGRAPH_CHECK(igraph_connect_neighborhood(graph, nei, IGRAPH_ALL)); } /* clean up */ igraph_Free(coords); igraph_Free(weights); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \ingroup generators * \function igraph_ring * \brief Creates a \em ring graph, a one dimensional lattice. * * An undirected (circular) ring on n vertices is commonly known in graph * theory as the cycle graph C_n. * * \param graph Pointer to an uninitialized graph object. * \param n The number of vertices in the ring. * \param directed Logical, whether to create a directed ring. * \param mutual Logical, whether to create mutual edges in a directed * ring. It is ignored for undirected graphs. * \param circular Logical, if false, the ring will be open (this is * not a real \em ring actually). * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * * Time complexity: O(|V|), the * number of vertices in the graph. * * \sa \ref igraph_lattice() for generating more general lattices. * * \example examples/simple/igraph_ring.c */ int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular) { igraph_vector_t v=IGRAPH_VECTOR_NULL; if (n<0) { IGRAPH_ERROR("negative number of vertices", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&v, 1); VECTOR(v)[0]=n; IGRAPH_CHECK(igraph_lattice(graph, &v, 1, directed, mutual, circular)); igraph_vector_destroy(&v); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup generators * \function igraph_tree * \brief Creates a tree in which almost all vertices have the same number of children. * * \param graph Pointer to an uninitialized graph object. * \param n Integer, the number of vertices in the graph. * \param children Integer, the number of children of a vertex in the * tree. * \param type Constant, gives whether to create a directed tree, and * if this is the case, also its orientation. Possible values: * \clist * \cli IGRAPH_TREE_OUT * directed tree, the edges point * from the parents to their children, * \cli IGRAPH_TREE_IN * directed tree, the edges point from * the children to their parents. * \cli IGRAPH_TREE_UNDIRECTED * undirected tree. * \endclist * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * \c IGRAPH_INVMODE: invalid mode argument. * * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges in the graph. * * \sa \ref igraph_lattice(), \ref igraph_star() for creating other regular * structures. * * \example examples/simple/igraph_tree.c */ int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, igraph_tree_mode_t type) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; long int i, j; long int idx=0; long int to=1; if (n<0 || children<=0) { IGRAPH_ERROR("Invalid number of vertices or children", IGRAPH_EINVAL); } if (type != IGRAPH_TREE_OUT && type != IGRAPH_TREE_IN && type != IGRAPH_TREE_UNDIRECTED) { IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 2*(n-1)); i=0; if (type == IGRAPH_TREE_OUT) { while (idx<2*(n-1)) { for (j=0; j * In a full graph every possible edge is present, every vertex is * connected to every other vertex. A full graph in \c igraph should be * distinguished from the concept of complete graphs as used in graph theory. * If n is a positive integer, then the complete graph K_n on n vertices is * the undirected simple graph with the following property. For any distinct * pair (u,v) of vertices in K_n, uv (or equivalently vu) is an edge of K_n. * In \c igraph, a full graph on n vertices can be K_n, a directed version of * K_n, or K_n with at least one loop edge. In any case, if F is a full graph * on n vertices as generated by \c igraph, then K_n is a subgraph of the * undirected version of F. * * \param graph Pointer to an uninitialized graph object. * \param n Integer, the number of vertices in the graph. * \param directed Logical, whether to create a directed graph. * \param loops Logical, whether to include self-edges (loops). * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * * Time complexity: O(|V|+|E|), * |V| is the number of vertices, * |E| the number of edges in the * graph. Of course this is the same as * O(|E|)=O(|V||V|) * here. * * \sa \ref igraph_lattice(), \ref igraph_star(), \ref igraph_tree() * for creating other regular structures. * * \example examples/simple/igraph_full.c */ int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; long int i, j; if (n<0) { IGRAPH_ERROR("invalid number of vertices", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (directed && loops) { IGRAPH_CHECK(igraph_vector_reserve(&edges, n*n)); for (i=0; ii->j edge is * present if and only if j<i. * If the \c directed argument is zero then an undirected graph is * created, and it is just a full graph. * \param graph Pointer to an uninitialized graph object, the result * is stored here. * \param n The number of vertices. * \param directed Whether to created a directed graph. If zero an * undirected graph is created. * \return Error code. * * Time complexity: O(|V|^2), as we have many edges. */ int igraph_full_citation(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { igraph_vector_t edges; long int i, j, ptr=0; IGRAPH_VECTOR_INIT_FINALLY(&edges, n*(n-1)); for (i=1; i * This function is handy when a relatively small graph needs to be created. * Instead of giving the edges as a vector, they are given simply as * arguments and a '-1' needs to be given after the last meaningful * edge argument. * * Note that only graphs which have vertices less than * the highest value of the 'int' type can be created this way. If you * give larger values then the result is undefined. * * \param graph Pointer to an uninitialized graph object. The result * will be stored here. * \param n The number of vertices in the graph; a nonnegative integer. * \param directed Logical constant; gives whether the graph should be * directed. Supported values are: * \clist * \cli IGRAPH_DIRECTED * The graph to be created will be \em directed. * \cli IGRAPH_UNDIRECTED * The graph to be created will be \em undirected. * \endclist * \param ... The additional arguments giving the edges of the * graph. Don't forget to supply an additional '-1' after the last * (meaningful) argument. * \return Error code. * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges in the graph to create. * * \example examples/simple/igraph_small.c */ int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, ...) { igraph_vector_t edges; va_list ap; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); va_start(ap, directed); while (1) { int num = va_arg(ap, int); if (num == -1) { break; } igraph_vector_push_back(&edges, num); } IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_extended_chordal_ring * Create an extended chordal ring * * An extended chordal ring is a regular graph, each node has the same * degree. It can be obtained from a simple ring by adding some extra * edges specified by a matrix. Let p denote the number of columns in * the W matrix. The extra edges of vertex i * are added according to column (i mod p) in * W. The number of extra edges is the number * of rows in W: for each row j an edge * i->i+w[ij] is added if i+w[ij] is less than the number of total * nodes. * * * See also Kotsis, G: Interconnection Topologies for Parallel Processing * Systems, PARS Mitteilungen 11, 1-6, 1993. * * \param graph Pointer to an uninitialized graph object, the result * will be stored here. The result is always an undirected graph. * \param nodes Integer constant, the number of vertices in the * graph. It must be at least 3. * \param W The matrix specifying the extra edges. The number of * columns should divide the number of total vertices. * \return Error code. * * \sa \ref igraph_ring(). * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. */ int igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_t *W) { igraph_vector_t edges; long int period=igraph_matrix_ncol(W); long int degree=igraph_matrix_nrow(W)+2; long int i, j, mpos=0, epos=0; if (nodes<3) { IGRAPH_ERROR("An extended chordal ring has at least 3 nodes", IGRAPH_EINVAL); } if ((long int)nodes % period != 0) { IGRAPH_ERROR("The period (number of columns in W) should divide the " "number of nodes", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, nodes*degree); for (i=0; i 2) { for (i=0; i Note that the input graph is modified in place, no * new graph is created, call \ref igraph_copy() if you want to keep * the original graph as well. * * For undirected graphs reachability is always * symmetric: if vertex A can be reached from vertex B in at * most \p order steps, then the opposite is also true. Only one * undirected (A,B) edge will be added in this case. * \param graph The input graph, this is the output graph as well. * \param order Integer constant, it gives the distance within which * the vertices will be connected to the source vertex. * \param mode Constant, it specifies how the neighborhood search is * performed for directed graphs. If \c IGRAPH_OUT then vertices * reachable from the source vertex will be connected, \c IGRAPH_IN * is the opposite. If \c IGRAPH_ALL then the directed graph is * considered as an undirected one. * \return Error code. * * \sa \ref igraph_lattice() uses this function to connect the * neighborhood of the vertices. * * Time complexity: O(|V|*d^o), |V| is the number of vertices in the * graph, d is the average degree and o is the \p order argument. */ int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q; igraph_vector_t edges; long int i, j, in; long int *added; igraph_vector_t neis; if (order<0) { IGRAPH_ERROR("Negative order, cannot connect neighborhood", IGRAPH_EINVAL); } if (order<2) { IGRAPH_WARNING("Order smaller than two, graph will be unchanged"); } if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); added=igraph_Calloc(no_of_nodes, long int); if (added==0) { IGRAPH_ERROR("Cannot connect neighborhood", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); for (i=0; i 1) { for (j=0; j * Please note that the graph will have \c m to the power \c n vertices and * even more edges, so probably you don't want to supply too big numbers for * \c m and \c n. * * * De Bruijn graphs have some interesting properties, please see another source, * eg. Wikipedia for details. * * \param graph Pointer to an uninitialized graph object, the result will be * stored here. * \param m Integer, the number of letters in the alphabet. * \param n Integer, the length of the strings. * \return Error code. * * \sa \ref igraph_kautz(). * * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. */ int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { /* m - number of symbols */ /* n - length of strings */ long int no_of_nodes, no_of_edges; igraph_vector_t edges; long int i, j; long int mm=m; if (m<0 || n<0) { IGRAPH_ERROR("`m' and `n' should be non-negative in a de Bruijn graph", IGRAPH_EINVAL); } if (n==0) { return igraph_empty(graph, 1, IGRAPH_DIRECTED); } if (m==0) { return igraph_empty(graph, 0, IGRAPH_DIRECTED); } no_of_nodes=(long int) pow(m, n); no_of_edges=no_of_nodes*m; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*2)); for (i=0; i * Kautz graphs have some interesting properties, see eg. Wikipedia * for details. * * * Vincent Matossian wrote the first version of this function in R, * thanks. * \param graph Pointer to an uninitialized graph object, the result * will be stored here. * \param m Integer, \c m+1 is the number of letters in the alphabet. * \param n Integer, \c n+1 is the length of the strings. * \return Error code. * * \sa \ref igraph_de_bruijn(). * * Time complexity: O(|V|* [(m+1)/m]^n +|E|), in practice it is more * like O(|V|+|E|). |V| is the number of vertices, |E| is the number * of edges and \c m and \c n are the corresponding arguments. */ int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { /* m+1 - number of symbols */ /* n+1 - length of strings */ long int mm=m; long int no_of_nodes, no_of_edges; long int allstrings; long int i, j, idx=0; igraph_vector_t edges; igraph_vector_long_t digits, table; igraph_vector_long_t index1, index2; long int actb=0; long int actvalue=0; if (m<0 || n<0) { IGRAPH_ERROR("`m' and `n' should be non-negative in a Kautz graph", IGRAPH_EINVAL); } if (n==0) { return igraph_full(graph, m+1, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); } if (m==0) { return igraph_empty(graph, 0, IGRAPH_DIRECTED); } no_of_nodes=(long int) ((m+1)*pow(m, n)); no_of_edges=no_of_nodes*m; allstrings=(long int) pow(m+1, n+1); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_long_init(&table, n+1)); IGRAPH_FINALLY(igraph_vector_long_destroy, &table); j=1; for (i=n; i>=0; i--) { VECTOR(table)[i]=j; j *= (m+1); } IGRAPH_CHECK(igraph_vector_long_init(&digits, n+1)); IGRAPH_FINALLY(igraph_vector_long_destroy, &digits); IGRAPH_CHECK(igraph_vector_long_init(&index1, (long int) pow(m+1, n+1))); IGRAPH_FINALLY(igraph_vector_long_destroy, &index1); IGRAPH_CHECK(igraph_vector_long_init(&index2, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &index2); /* Fill the index tables*/ while (1) { /* at the beginning of the loop, 0:actb contain the valid prefix */ /* we might need to fill it to get a valid string */ long int z=0; if (VECTOR(digits)[actb]==0) { z=1; } for (actb++; actb<=n; actb++) { VECTOR(digits)[actb]=z; actvalue += z*VECTOR(table)[actb]; z=1-z; } actb=n; /* ok, we have a valid string now */ VECTOR(index1)[actvalue]=idx+1; VECTOR(index2)[idx]=actvalue; idx++; /* finished? */ if (idx >= no_of_nodes) { break; } /* not yet, we need a valid prefix now */ while (1) { /* try to increase digits at position actb */ long int next=VECTOR(digits)[actb]+1; if (actb != 0 && VECTOR(digits)[actb-1]==next) { next++; } if (next <= m) { /* ok, no problem */ actvalue += (next-VECTOR(digits)[actb])*VECTOR(table)[actb]; VECTOR(digits)[actb]=next; break; } else { /* bad luck, try the previous digit */ actvalue -= VECTOR(digits)[actb]*VECTOR(table)[actb]; actb--; } } } IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*2)); /* Now come the edges at last */ for (i=0; i * LCF is short for Lederberg-Coxeter-Frucht, it is a concise notation for * 3-regular Hamiltonian graphs. It consists of three parameters: the * number of vertices in the graph, a list of shifts giving additional * edges to a cycle backbone, and another integer giving how many times * the shifts should be performed. See * http://mathworld.wolfram.com/LCFNotation.html for details. * * \param graph Pointer to an uninitialized graph object. * \param n Integer, the number of vertices in the graph. * \param ... The shifts and the number of repeats for the shifts, * plus an additional 0 to mark the end of the arguments. * \return Error code. * * \sa See \ref igraph_lcf_vector() for a similar function using a * vector_t instead of the variable length argument list. * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. * * \example examples/simple/igraph_lcf.c */ int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) { igraph_vector_t shifts; igraph_integer_t repeats; va_list ap; IGRAPH_VECTOR_INIT_FINALLY(&shifts, 0); va_start(ap, n); while (1) { int num=va_arg(ap, int); if (num==0) { break; } IGRAPH_CHECK(igraph_vector_push_back(&shifts, num)); } if (igraph_vector_size(&shifts)==0) { repeats=0; } else { repeats=(igraph_integer_t) igraph_vector_pop_back(&shifts); } IGRAPH_CHECK(igraph_lcf_vector(graph, n, &shifts, repeats)); igraph_vector_destroy(&shifts); IGRAPH_FINALLY_CLEAN(1); return 0; } const igraph_real_t igraph_i_famous_bull[] = { 5, 5, 0, 0,1,0,2,1,2,1,3,2,4 }; const igraph_real_t igraph_i_famous_chvatal[] = { 12, 24, 0, 5, 6, 6, 7, 7, 8, 8, 9, 5, 9, 4, 5, 4, 8, 2, 8, 2, 6, 0, 6, 0, 9, 3, 9, 3, 7, 1, 7, 1, 5, 1, 10, 4, 10, 4, 11, 2, 11, 0, 10, 0, 11, 3, 11, 3, 10, 1, 2 }; const igraph_real_t igraph_i_famous_coxeter[] = { 28, 42, 0, 0, 1, 0, 2, 0, 7, 1, 4, 1, 13, 2, 3, 2, 8, 3, 6, 3, 9, 4, 5, 4, 12, 5, 6, 5, 11, 6, 10, 7, 19, 7, 24, 8, 20, 8, 23, 9, 14, 9, 22, 10, 15, 10, 21, 11, 16, 11, 27, 12, 17, 12, 26, 13, 18, 13, 25, 14, 17, 14, 18, 15, 18, 15, 19, 16, 19, 16, 20, 17, 20, 21, 23, 21, 26, 22, 24, 22, 27, 23, 25, 24, 26, 25, 27 }; const igraph_real_t igraph_i_famous_cubical[] = { 8, 12, 0, 0, 1, 1, 2, 2, 3, 0, 3, 4, 5, 5, 6, 6, 7, 4, 7, 0, 4, 1, 5, 2, 6, 3, 7 }; const igraph_real_t igraph_i_famous_diamond[] = { 4, 5, 0, 0,1,0,2,1,2,1,3,2,3 }; const igraph_real_t igraph_i_famous_dodecahedron[] = { 20, 30, 0, 0, 1, 0, 4, 0, 5, 1, 2, 1, 6, 2, 3, 2, 7, 3, 4, 3, 8, 4, 9, 5, 10, 5, 11, 6, 10, 6, 14, 7, 13, 7, 14, 8, 12, 8, 13, 9, 11, 9, 12, 10, 15, 11, 16, 12, 17, 13, 18, 14, 19, 15, 16, 15, 19, 16, 17, 17, 18, 18, 19 }; const igraph_real_t igraph_i_famous_folkman[] = { 20, 40, 0, 0, 5, 0, 8, 0, 10, 0, 13, 1, 7, 1, 9, 1, 12, 1, 14, 2, 6, 2, 8, 2, 11, 2, 13, 3, 5, 3, 7, 3, 10, 3, 12, 4, 6, 4, 9, 4, 11, 4, 14, 5, 15, 5, 19, 6, 15, 6, 16, 7, 16, 7, 17, 8, 17, 8, 18, 9, 18, 9, 19, 10, 15, 10, 19, 11, 15, 11, 16, 12, 16, 12, 17, 13, 17, 13, 18, 14, 18, 14, 19 }; const igraph_real_t igraph_i_famous_franklin[] = { 12, 18, 0, 0, 1, 0, 2, 0, 6, 1, 3, 1, 7, 2, 4, 2, 10, 3, 5, 3, 11, 4, 5, 4, 6, 5, 7, 6, 8, 7, 9, 8, 9, 8, 11, 9, 10, 10, 11 }; const igraph_real_t igraph_i_famous_frucht[] = { 12, 18, 0, 0, 1, 0, 2, 0, 11, 1, 3, 1, 6, 2, 5, 2, 10, 3, 4, 3, 6, 4, 8, 4, 11, 5, 9, 5, 10, 6, 7, 7, 8, 7, 9, 8, 9, 10, 11 }; const igraph_real_t igraph_i_famous_grotzsch[] = { 11, 20, 0, 0, 1, 0, 2, 0, 7, 0, 10, 1, 3, 1, 6, 1, 9, 2, 4, 2, 6, 2, 8, 3, 4, 3, 8, 3, 10, 4, 7, 4, 9, 5, 6, 5, 7, 5, 8, 5, 9, 5, 10 }; const igraph_real_t igraph_i_famous_heawood[] = { 14, 21, 0, 0, 1, 0, 5, 0, 13, 1, 2, 1, 10, 2, 3, 2, 7, 3, 4, 3, 12, 4, 5, 4, 9, 5, 6, 6, 7, 6, 11, 7, 8, 8, 9, 8, 13, 9, 10, 10, 11, 11, 12, 12, 13 }; const igraph_real_t igraph_i_famous_herschel[] = { 11, 18, 0, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 6, 1, 7, 2, 10, 3, 9, 4, 8, 4, 9, 5, 8, 5, 10, 6, 8, 6, 9, 7, 8, 7, 10 }; const igraph_real_t igraph_i_famous_house[] = { 5, 6, 0, 0,1,0,2,1,3,2,3,2,4,3,4 }; const igraph_real_t igraph_i_famous_housex[] = { 5, 8, 0, 0,1,0,2,0,3,1,2,1,3,2,3,2,4,3,4 }; const igraph_real_t igraph_i_famous_icosahedron[] = { 12, 30, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 8, 1, 2, 1, 6, 1, 7, 1, 8, 2, 4, 2, 5, 2, 6, 3, 4, 3, 8, 3, 9, 3, 11, 4, 5, 4, 11, 5, 6, 5, 10, 5, 11, 6, 7, 6, 10, 7, 8, 7, 9, 7, 10, 8, 9, 9, 10, 9, 11, 10, 11 }; const igraph_real_t igraph_i_famous_krackhardt_kite[] = { 10,18,0, 0,1,0,2,0,3,0,5, 1,3,1,4,1,6, 2,3,2,5, 3,4,3,5,3,6, 4,6, 5,6,5,7, 6,7, 7,8, 8,9 }; const igraph_real_t igraph_i_famous_levi[] = { 30, 45, 0, 0, 1, 0, 7, 0, 29, 1, 2, 1, 24, 2, 3, 2, 11, 3, 4, 3, 16, 4, 5, 4, 21, 5, 6, 5, 26, 6, 7, 6, 13, 7, 8, 8, 9, 8, 17, 9, 10, 9, 22, 10, 11, 10, 27, 11, 12, 12, 13, 12, 19, 13, 14, 14, 15, 14, 23, 15, 16, 15, 28, 16, 17, 17, 18, 18, 19, 18, 25, 19, 20, 20, 21, 20, 29, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29 }; const igraph_real_t igraph_i_famous_mcgee[] = { 24, 36, 0, 0, 1, 0, 7, 0, 23, 1, 2, 1, 18, 2, 3, 2, 14, 3, 4, 3, 10, 4, 5, 4, 21, 5, 6, 5, 17, 6, 7, 6, 13, 7, 8, 8, 9, 8, 20, 9, 10, 9, 16, 10, 11, 11, 12, 11, 23, 12, 13, 12, 19, 13, 14, 14, 15, 15, 16, 15, 22, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23 }; const igraph_real_t igraph_i_famous_meredith[] = { 70, 140, 0, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 7, 11, 7, 12, 7, 13, 8, 11, 8, 12, 8, 13, 9, 11, 9, 12, 9, 13, 10, 11, 10, 12, 10, 13, 14, 18, 14, 19, 14, 20, 15, 18, 15, 19, 15, 20, 16, 18, 16, 19, 16, 20, 17, 18, 17, 19, 17, 20, 21, 25, 21, 26, 21, 27, 22, 25, 22, 26, 22, 27, 23, 25, 23, 26, 23, 27, 24, 25, 24, 26, 24, 27, 28, 32, 28, 33, 28, 34, 29, 32, 29, 33, 29, 34, 30, 32, 30, 33, 30, 34, 31, 32, 31, 33, 31, 34, 35, 39, 35, 40, 35, 41, 36, 39, 36, 40, 36, 41, 37, 39, 37, 40, 37, 41, 38, 39, 38, 40, 38, 41, 42, 46, 42, 47, 42, 48, 43, 46, 43, 47, 43, 48, 44, 46, 44, 47, 44, 48, 45, 46, 45, 47, 45, 48, 49, 53, 49, 54, 49, 55, 50, 53, 50, 54, 50, 55, 51, 53, 51, 54, 51, 55, 52, 53, 52, 54, 52, 55, 56, 60, 56, 61, 56, 62, 57, 60, 57, 61, 57, 62, 58, 60, 58, 61, 58, 62, 59, 60, 59, 61, 59, 62, 63, 67, 63, 68, 63, 69, 64, 67, 64, 68, 64, 69, 65, 67, 65, 68, 65, 69, 66, 67, 66, 68, 66, 69, 2, 50, 1, 51, 9, 57, 8, 58, 16, 64, 15, 65, 23, 36, 22, 37, 30, 43, 29, 44, 3, 21, 7, 24, 14, 31, 0, 17, 10, 28, 38, 42, 35, 66, 59, 63, 52, 56, 45, 49 }; const igraph_real_t igraph_i_famous_noperfectmatching[] = { 16, 27, 0, 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, 2, 4, 3, 4, 4, 5, 5, 6, 5, 7, 6, 12, 6, 13, 7, 8, 7, 9, 8, 9, 8, 10, 8, 11, 9, 10, 9, 11, 10, 11, 12, 13, 12, 14, 12, 15, 13, 14, 13, 15, 14, 15 }; const igraph_real_t igraph_i_famous_nonline[] = { 50, 72, 0, 0, 1, 0, 2, 0, 3, 4, 6, 4, 7, 5, 6, 5, 7, 6, 7, 7, 8, 9, 11, 9, 12, 9, 13, 10, 11, 10, 12, 10, 13, 11, 12, 11, 13, 12, 13, 14, 15, 15, 16, 15, 17, 16, 17, 16, 18, 17, 18, 18, 19, 20, 21, 20, 22, 20, 23, 21, 22, 21, 23, 21, 24, 22, 23, 22, 24, 24, 25, 26, 27, 26, 28, 26, 29, 27, 28, 27, 29, 27, 30, 27, 31, 28, 29, 28, 30, 28, 31, 30, 31, 32, 34, 32, 35, 32, 36, 33, 34, 33, 35, 33, 37, 34, 35, 36, 37, 38, 39, 38, 40, 38, 43, 39, 40, 39, 41, 39, 42, 39, 43, 40, 41, 41, 42, 42, 43, 44, 45, 44, 46, 45, 46, 45, 47, 46, 47, 46, 48, 47, 48, 47, 49, 48, 49 }; const igraph_real_t igraph_i_famous_octahedron[] = { 6, 12, 0, 0, 1, 0, 2, 1, 2, 3, 4, 3, 5, 4, 5, 0, 3, 0, 5, 1, 3, 1, 4, 2, 4, 2, 5 }; const igraph_real_t igraph_i_famous_petersen[] = { 10, 15, 0, 0,1,0,4,0,5, 1,2,1,6, 2,3,2,7, 3,4,3,8, 4,9, 5,7,5,8, 6,8,6,9, 7,9 }; const igraph_real_t igraph_i_famous_robertson[] = { 19, 38, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 0, 18, 0, 4, 4, 9, 9, 13, 13, 17, 2, 17, 2, 6, 6, 10, 10, 15, 0, 15, 1, 8, 8, 16, 5, 16, 5, 12, 1, 12, 7, 18, 7, 14, 3, 14, 3, 11, 11, 18 }; const igraph_real_t igraph_i_famous_smallestcyclicgroup[] = { 9, 15, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 7, 1, 8, 2, 5, 2, 6, 2, 7, 3, 8, 4, 5, 6, 7 }; const igraph_real_t igraph_i_famous_tetrahedron[] = { 4, 6, 0, 0, 3, 1, 3, 2, 3, 0, 1, 1, 2, 0, 2 }; const igraph_real_t igraph_i_famous_thomassen[] = { 34, 52, 0, 0, 2, 0, 3, 1, 3, 1, 4, 2, 4, 5, 7, 5, 8, 6, 8, 6, 9, 7, 9, 10, 12, 10, 13, 11, 13, 11, 14, 12, 14, 15, 17, 15, 18, 16, 18, 16, 19, 17, 19, 9, 19, 4, 14, 24, 25, 25, 26, 20, 26, 20, 21, 21, 22, 22, 23, 23, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 24, 33, 5, 24, 6, 25, 7, 26, 8, 20, 0, 20, 1, 21, 2, 22, 3, 23, 10, 27, 11, 28, 12, 29, 13, 30, 15, 30, 16, 31, 17, 32, 18, 33 }; const igraph_real_t igraph_i_famous_tutte[] = { 46, 69, 0, 0, 10, 0, 11, 0, 12, 1, 2, 1, 7, 1, 19, 2, 3, 2, 41, 3, 4, 3, 27, 4, 5, 4, 33, 5, 6, 5, 45, 6, 9, 6, 29, 7, 8, 7, 21, 8, 9, 8, 22, 9, 24, 10, 13, 10, 14, 11, 26, 11, 28, 12, 30, 12, 31, 13, 15, 13, 21, 14, 15, 14, 18, 15, 16, 16, 17, 16, 20, 17, 18, 17, 23, 18, 24, 19, 25, 19, 40, 20, 21, 20, 22, 22, 23, 23, 24, 25, 26, 25, 38, 26, 34, 27, 28, 27, 39, 28, 34, 29, 30, 29, 44, 30, 35, 31, 32, 31, 35, 32, 33, 32, 42, 33, 43, 34, 36, 35, 37, 36, 38, 36, 39, 37, 42, 37, 44, 38, 40, 39, 41, 40, 41, 42, 43, 43, 45, 44, 45 }; const igraph_real_t igraph_i_famous_uniquely3colorable[] = { 12, 22, 0, 0, 1, 0, 3, 0, 6, 0, 8, 1, 4, 1, 7, 1, 9, 2, 3, 2, 6, 2, 7, 2, 9, 2, 11, 3, 4, 3, 10, 4, 5, 4, 11, 5, 6, 5, 7, 5, 8, 5, 10, 8, 11, 9, 10 }; const igraph_real_t igraph_i_famous_walther[] = { 25, 31, 0, 0, 1, 1, 2, 1, 8, 2, 3, 2, 13, 3, 4, 3, 16, 4, 5, 5, 6, 5, 19, 6, 7, 6, 20, 7, 21, 8, 9, 8, 13, 9, 10, 9, 22, 10, 11, 10, 20, 11, 12, 13, 14, 14, 15, 14, 23, 15, 16, 15, 17, 17, 18, 18, 19, 18, 24, 20, 24, 22, 23, 23, 24 }; const igraph_real_t igraph_i_famous_zachary[] = { 34, 78, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,10, 0,11, 0,12, 0,13, 0,17, 0,19, 0,21, 0,31, 1, 2, 1, 3, 1, 7, 1,13, 1,17, 1,19, 1,21, 1,30, 2, 3, 2, 7, 2,27, 2,28, 2,32, 2, 9, 2, 8, 2,13, 3, 7, 3,12, 3,13, 4, 6, 4,10, 5, 6, 5,10, 5,16, 6,16, 8,30, 8,32, 8,33, 9,33,13,33,14,32,14,33, 15,32,15,33,18,32,18,33,19,33,20,32,20,33, 22,32,22,33,23,25,23,27,23,32,23,33,23,29, 24,25,24,27,24,31,25,31,26,29,26,33,27,33, 28,31,28,33,29,32,29,33,30,32,30,33,31,32,31,33, 32,33 }; int igraph_i_famous(igraph_t *graph, const igraph_real_t *data); int igraph_i_famous(igraph_t *graph, const igraph_real_t *data) { long int no_of_nodes=(long int) data[0]; long int no_of_edges=(long int) data[1]; igraph_bool_t directed=(igraph_bool_t) data[2]; igraph_vector_t edges; igraph_vector_view(&edges, data+3, 2*no_of_edges); IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, directed)); return 0; } /** * \function igraph_famous * \brief Create a famous graph by simply providing its name * * * The name of the graph can be simply supplied as a string. * Note that this function creates graphs which don't take any parameters, * there are separate functions for graphs with parameters, eg. \ref * igraph_full() for creating a full graph. * * * The following graphs are supported: * \clist * \cli Bull * The bull graph, 5 vertices, 5 edges, resembles the * head of a bull if drawn properly. * \cli Chvatal * This is the smallest triangle-free graph that is * both 4-chromatic and 4-regular. According to the Grunbaum * conjecture there exists an m-regular, m-chromatic graph * with n vertices for every m>1 and n>2. The Chvatal graph * is an example for m=4 and n=12. It has 24 edges. * \cli Coxeter * A non-Hamiltonian cubic symmetric graph with 28 * vertices and 42 edges. * \cli Cubical * The Platonic graph of the cube. A convex regular * polyhedron with 8 vertices and 12 edges. * \cli Diamond * A graph with 4 vertices and 5 edges, resembles a * schematic diamond if drawn properly. * \cli Dodecahedral, Dodecahedron * Another Platonic solid * with 20 vertices and 30 edges. * \cli Folkman * The semisymmetric graph with minimum number of * vertices, 20 and 40 edges. A semisymmetric graph is * regular, edge transitive and not vertex transitive. * \cli Franklin * This is a graph whose embedding to the Klein * bottle can be colored with six colors, it is a * counterexample to the necessity of the Heawood * conjecture on a Klein bottle. It has 12 vertices and 18 * edges. * \cli Frucht * The Frucht Graph is the smallest cubical graph * whose automorphism group consists only of the identity * element. It has 12 vertices and 18 edges. * \cli Grotzsch * The Grötzsch graph is a triangle-free graph with * 11 vertices, 20 edges, and chromatic number 4. It is named after * German mathematician Herbert Grötzsch, and its existence * demonstrates that the assumption of planarity is necessary in * Grötzsch's theorem that every triangle-free planar * graph is 3-colorable. * \cli Heawood * The Heawood graph is an undirected graph with 14 * vertices and 21 edges. The graph is cubic, and all cycles in the * graph have six or more edges. Every smaller cubic graph has shorter * cycles, so this graph is the 6-cage, the smallest cubic graph of * girth 6. * \cli Herschel * The Herschel graph is the smallest * nonhamiltonian polyhedral graph. It is the * unique such graph on 11 nodes, and has 18 edges. * \cli House * The house graph is a 5-vertex, 6-edge graph, the * schematic draw of a house if drawn properly, basically a * triangle on top of a square. * \cli HouseX * The same as the house graph with an X in the square. 5 * vertices and 8 edges. * \cli Icosahedral, Icosahedron * A Platonic solid with 12 * vertices and 30 edges. * \cli Krackhardt_Kite * A social network with 10 vertices and 18 edges. * Krackhardt, D. Assessing the Political Landscape: * Structure, Cognition, and Power in Organizations. * Admin. Sci. Quart. 35, 342-369, 1990. * \cli Levi * The graph is a 4-arc transitive cubic graph, it has * 30 vertices and 45 edges. * \cli McGee * The McGee graph is the unique 3-regular 7-cage * graph, it has 24 vertices and 36 edges. * \cli Meredith * The Meredith graph is a quartic graph on 70 * nodes and 140 edges that is a counterexample to the conjecture that * every 4-regular 4-connected graph is Hamiltonian. * \cli Noperfectmatching * A connected graph with 16 vertices and * 27 edges containing no perfect matching. A matching in a graph * is a set of pairwise non-incident edges; that is, no two edges * share a common vertex. A perfect matching is a matching * which covers all vertices of the graph. * \cli Nonline * A graph whose connected components are the 9 * graphs whose presence as a vertex-induced subgraph in a * graph makes a nonline graph. It has 50 vertices and 72 edges. * \cli Octahedral, Octahedron * Platonic solid with 6 * vertices and 12 edges. * \cli Petersen * A 3-regular graph with 10 vertices and 15 edges. It is * the smallest hypohamiltonian graph, ie. it is * non-hamiltonian but removing any single vertex from it makes it * Hamiltonian. * \cli Robertson * The unique (4,5)-cage graph, ie. a 4-regular * graph of girth 5. It has 19 vertices and 38 edges. * \cli Smallestcyclicgroup * A smallest nontrivial graph * whose automorphism group is cyclic. It has 9 vertices and * 15 edges. * \cli Tetrahedral, Tetrahedron * Platonic solid with 4 * vertices and 6 edges. * \cli Thomassen * The smallest hypotraceable graph, * on 34 vertices and 52 edges. A hypotracable graph does * not contain a Hamiltonian path but after removing any * single vertex from it the remainder always contains a * Hamiltonian path. A graph containing a Hamiltonian path * is called traceable. * \cli Tutte * Tait's Hamiltonian graph conjecture states that * every 3-connected 3-regular planar graph is Hamiltonian. * This graph is a counterexample. It has 46 vertices and 69 * edges. * \cli Uniquely3colorable * Returns a 12-vertex, triangle-free * graph with chromatic number 3 that is uniquely * 3-colorable. * \cli Walther * An identity graph with 25 vertices and 31 * edges. An identity graph has a single graph automorphism, * the trivial one. * \cli Zachary * Social network of friendships between 34 members of a * karate club at a US university in the 1970s. See * W. W. Zachary, An information flow model for conflict and * fission in small groups, Journal of Anthropological * Research 33, 452-473 (1977). * \endclist * * \param graph Pointer to an uninitialized graph object. * \param name Character constant, the name of the graph to be * created, it is case insensitive. * \return Error code, IGRAPH_EINVAL if there is no graph with the * given name. * * \sa Other functions for creating graph structures: * \ref igraph_ring(), \ref igraph_tree(), \ref igraph_lattice(), \ref * igraph_full(). * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges in the graph. */ int igraph_famous(igraph_t *graph, const char *name) { if (!strcasecmp(name, "bull")) { return igraph_i_famous(graph, igraph_i_famous_bull); } else if (!strcasecmp(name, "chvatal")) { return igraph_i_famous(graph, igraph_i_famous_chvatal); } else if (!strcasecmp(name, "coxeter")) { return igraph_i_famous(graph, igraph_i_famous_coxeter); } else if (!strcasecmp(name, "cubical")) { return igraph_i_famous(graph, igraph_i_famous_cubical); } else if (!strcasecmp(name, "diamond")) { return igraph_i_famous(graph, igraph_i_famous_diamond); } else if (!strcasecmp(name, "dodecahedral") || !strcasecmp(name, "dodecahedron")) { return igraph_i_famous(graph, igraph_i_famous_dodecahedron); } else if (!strcasecmp(name, "folkman")) { return igraph_i_famous(graph, igraph_i_famous_folkman); } else if (!strcasecmp(name, "franklin")) { return igraph_i_famous(graph, igraph_i_famous_franklin); } else if (!strcasecmp(name, "frucht")) { return igraph_i_famous(graph, igraph_i_famous_frucht); } else if (!strcasecmp(name, "grotzsch")) { return igraph_i_famous(graph, igraph_i_famous_grotzsch); } else if (!strcasecmp(name, "heawood")) { return igraph_i_famous(graph, igraph_i_famous_heawood); } else if (!strcasecmp(name, "herschel")) { return igraph_i_famous(graph, igraph_i_famous_herschel); } else if (!strcasecmp(name, "house")) { return igraph_i_famous(graph, igraph_i_famous_house); } else if (!strcasecmp(name, "housex")) { return igraph_i_famous(graph, igraph_i_famous_housex); } else if (!strcasecmp(name, "icosahedral") || !strcasecmp(name, "icosahedron")) { return igraph_i_famous(graph, igraph_i_famous_icosahedron); } else if (!strcasecmp(name, "krackhardt_kite")) { return igraph_i_famous(graph, igraph_i_famous_krackhardt_kite); } else if (!strcasecmp(name, "levi")) { return igraph_i_famous(graph, igraph_i_famous_levi); } else if (!strcasecmp(name, "mcgee")) { return igraph_i_famous(graph, igraph_i_famous_mcgee); } else if (!strcasecmp(name, "meredith")) { return igraph_i_famous(graph, igraph_i_famous_meredith); } else if (!strcasecmp(name, "noperfectmatching")) { return igraph_i_famous(graph, igraph_i_famous_noperfectmatching); } else if (!strcasecmp(name, "nonline")) { return igraph_i_famous(graph, igraph_i_famous_nonline); } else if (!strcasecmp(name, "octahedral") || !strcasecmp(name, "octahedron")) { return igraph_i_famous(graph, igraph_i_famous_octahedron); } else if (!strcasecmp(name, "petersen")) { return igraph_i_famous(graph, igraph_i_famous_petersen); } else if (!strcasecmp(name, "robertson")) { return igraph_i_famous(graph, igraph_i_famous_robertson); } else if (!strcasecmp(name, "smallestcyclicgroup")) { return igraph_i_famous(graph, igraph_i_famous_smallestcyclicgroup); } else if (!strcasecmp(name, "tetrahedral") || !strcasecmp(name, "tetrahedron")) { return igraph_i_famous(graph, igraph_i_famous_tetrahedron); } else if (!strcasecmp(name, "thomassen")) { return igraph_i_famous(graph, igraph_i_famous_thomassen); } else if (!strcasecmp(name, "tutte")) { return igraph_i_famous(graph, igraph_i_famous_tutte); } else if (!strcasecmp(name, "uniquely3colorable")) { return igraph_i_famous(graph, igraph_i_famous_uniquely3colorable); } else if (!strcasecmp(name, "walther")) { return igraph_i_famous(graph, igraph_i_famous_walther); } else if (!strcasecmp(name, "zachary")) { return igraph_i_famous(graph, igraph_i_famous_zachary); } else { IGRAPH_ERROR("Unknown graph, see documentation", IGRAPH_EINVAL); } return 0; } /** * \function igraph_adjlist * Create a graph from an adjacency list * * An adjacency list is a list of vectors, containing the neighbors * of all vertices. For operations that involve many changes to the * graph structure, it is recommended that you convert the graph into * an adjacency list via \ref igraph_adjlist_init(), perform the * modifications (these are cheap for an adjacency list) and then * recreate the igraph graph via this function. * * \param graph Pointer to an uninitialized graph object. * \param adjlist The adjacency list. * \param mode Whether or not to create a directed graph. \c IGRAPH_ALL * means an undirected graph, \c IGRAPH_OUT means a * directed graph from an out-adjacency list (i.e. each * list contains the successors of the corresponding * vertices), \c IGRAPH_IN means a directed graph from an * in-adjacency list * \param duplicate Logical, for undirected graphs this specified * whether each edge is included twice, in the vectors of * both adjacent vertices. If this is false (0), then it is * assumed that every edge is included only once. This argument * is ignored for directed graphs. * \return Error code. * * \sa \ref igraph_adjlist_init() for the opposite operation. * * Time complexity: O(|V|+|E|). * */ int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, igraph_neimode_t mode, igraph_bool_t duplicate) { long int no_of_nodes=igraph_adjlist_size(adjlist); long int no_of_edges=0; long int i; igraph_vector_t edges; long int edgeptr=0; duplicate = duplicate && (mode == IGRAPH_ALL); /* only duplicate if undirected */ for (i=0; i i) { if (edgeptr+2 > 2*no_of_edges) { IGRAPH_ERROR("Invalid adjacency list, most probably not correctly" " duplicated edges for an undirected graph", IGRAPH_EINVAL); } if (mode == IGRAPH_IN) { VECTOR(edges)[edgeptr++] = nei; VECTOR(edges)[edgeptr++] = i; } else { VECTOR(edges)[edgeptr++] = i; VECTOR(edges)[edgeptr++] = nei; } } } } /* loops */ if (duplicate) { loops=loops/2; } if (edgeptr+2*loops > 2*no_of_edges) { IGRAPH_ERROR("Invalid adjacency list, most probably not correctly" " duplicated edges for an undirected graph", IGRAPH_EINVAL); } for (j=0; j 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * SCGlib : A C library for the spectral coarse graining of matrices * as described in the paper: Shrinking Matrices while preserving their * eigenpairs with Application to the Spectral Coarse Graining of Graphs. * Preprint available at * * Copyright (C) 2008 David Morton de Lachapelle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * DESCRIPTION * ----------- * This file contains the headers of the library SCGlib. * For use with R software define * the constant R_COMPIL and refer to the R documentation to compile * a dynamic library. The scg_r_wrapper function should be useful. */ #ifndef SCG_HEADERS_H #define SCG_HEADERS_H #include #include #include "igraph_types.h" #include "igraph_vector.h" typedef struct ind_val { int ind; igraph_real_t val; } igraph_i_scg_indval_t; int igraph_i_compare_ind_val(const void *a, const void *b); typedef struct groups{ int ind; int n; int* gr; } igraph_i_scg_groups_t; /*------------------------------------------------- ------------DEFINED IN scg_approximate_methods.c--- ---------------------------------------------------*/ int igraph_i_breaks_computation(const igraph_vector_t *v, igraph_vector_t *breaks, int nb, int method); int igraph_i_intervals_plus_kmeans(const igraph_vector_t *v, int *gr, int n, int n_interv, int maxiter); int igraph_i_intervals_method(const igraph_vector_t *v, int *gr, int n, int n_interv); /*------------------------------------------------- ------------DEFINED IN scg_optimal_method.c-------- ---------------------------------------------------*/ int igraph_i_cost_matrix(igraph_real_t *Cv, const igraph_i_scg_indval_t *vs, int n, int matrix, const igraph_vector_t *ps); int igraph_i_optimal_partition(const igraph_real_t *v, int *gr, int n, int nt, int matrix, const igraph_real_t *p, igraph_real_t *value); /*------------------------------------------------- ------------DEFINED IN scg_kmeans.c---------------- ---------------------------------------------------*/ int igraph_i_kmeans_Lloyd(const igraph_vector_t *x, int n, int p, igraph_vector_t *centers, int k, int *cl, int maxiter); /*------------------------------------------------- ------------DEFINED IN scg_exact_scg.c------------- ---------------------------------------------------*/ int igraph_i_exact_coarse_graining(const igraph_real_t *v, int *gr, int n); /*------------------------------------------------- ------------DEFINED IN scg_utils.c----------------- ---------------------------------------------------*/ int igraph_i_compare_groups(const void *a,const void *b); int igraph_i_compare_real(const void *a, const void *b); int igraph_i_compare_int(const void *a, const void *b); igraph_real_t *igraph_i_real_sym_matrix(int size); #define igraph_i_real_sym_mat_get(S,i,j) S[i+j*(j+1)/2] #define igraph_i_real_sym_mat_set(S,i,j,val) S[i+j*(j+1)/2] = val #define igraph_i_free_real_sym_matrix(S) igraph_Free(S) #endif igraph/src/kolmogorov.c0000644000176000001440000000355212325527073014720 0ustar ripleyusers/* kolmogorov.c * * Copyright (C) 2010-2011 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "kolmogorov.h" double plfit_kolmogorov(double z) { const double fj[4] = { -2, -8, -18, -32 }; const double w = 2.50662827; const double c1 = -1.2337005501361697; /* -pi^2 / 8 */ const double c2 = -11.103304951225528; /* 9*c1 */ const double c3 = -30.842513753404244; /* 25*c1 */ double u = fabs(z); double v; if (u < 0.2) return 1; if (u < 0.755) { v = 1.0 / (u*u); return 1 - w * (exp(c1*v) + exp(c2*v) + exp(c3*v)) / u; } if (u < 6.8116) { double r[4] = { 0, 0, 0, 0 }; long int maxj = (long int)(3.0 / u + 0.5); long int j; if (maxj < 1) maxj = 1; v = u*u; for (j = 0; j < maxj; j++) { r[j] = exp(fj[j] * v); } return 2*(r[0] - r[1] + r[2] - r[3]); } return 0; } double plfit_ks_test_one_sample_p(double d, size_t n) { return plfit_kolmogorov(d * sqrt(n)); } double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2) { return plfit_kolmogorov(d * sqrt(n1*n2 / ((double)(n1+n2)))); } igraph/src/igraph_flow.h0000644000176000001440000001410112325527073015020 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_FLOW_H #define IGRAPH_FLOW_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* MAximum flows, minimum cuts & such */ /* -------------------------------------------------- */ /** * \typedef igraph_maxflow_stats_t * A simple data type to return some statistics from the * push-relabel maximum flow solver. * * \param nopush The number of push operations performed. * \param norelabel The number of relabel operarions performed. * \param nogap The number of times the gap heuristics was used. * \param nogapnodes The total number of vertices that were * omitted form further calculations because of the gap * heuristics. * \param nobfs The number of times the reverse BFS was run to * assign good values to the height function. This includes * an initial run before the whole algorithm, so it is always * at least one. */ typedef struct { int nopush, norelabel, nogap, nogapnodes, nobfs; } igraph_maxflow_stats_t; int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *flow, igraph_vector_t *cut, igraph_vector_t *partition, igraph_vector_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity, igraph_maxflow_stats_t *stats); int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity, igraph_maxflow_stats_t *stats); int igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *cut, igraph_vector_t *partition, igraph_vector_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *res, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *capacity); int igraph_mincut(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *partition, igraph_vector_t *partition2, igraph_vector_t *cut, const igraph_vector_t *capacity); int igraph_st_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target, igraph_vconn_nei_t neighbors); int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); /* s-t cut listing related stuff */ int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, igraph_vector_t *capacity); int igraph_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, igraph_vector_t *residual_capacity, const igraph_vector_t *flow); int igraph_i_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, igraph_vector_t *residual_capacity, const igraph_vector_t *flow, igraph_vector_t *tmp); int igraph_i_reverse_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, const igraph_vector_t *flow, igraph_vector_t *tmp); int igraph_reverse_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, const igraph_vector_t *flow); int igraph_dominator_tree(const igraph_t *graph, igraph_integer_t root, igraph_vector_t *dom, igraph_t *domtree, igraph_vector_t *leftout, igraph_neimode_t mode); int igraph_all_st_cuts(const igraph_t *graph, igraph_vector_ptr_t *cuts, igraph_vector_ptr_t *partition1s, igraph_integer_t source, igraph_integer_t target); int igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, igraph_vector_ptr_t *cuts, igraph_vector_ptr_t *partition1s, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, igraph_vector_t *flows, const igraph_vector_t *capacity); __END_DECLS #endif igraph/src/glprgr.c0000644000176000001440000001415412325527073014017 0ustar ripleyusers/* glprgr.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #define _GLPSTD_ERRNO #define _GLPSTD_STDIO #include "glpenv.h" #include "glprgr.h" #define xfault xerror /*********************************************************************** * NAME * * rgr_write_bmp16 - write 16-color raster image in BMP file format * * SYNOPSIS * * #include "glprgr.h" * int rgr_write_bmp16(const char *fname, int m, int n, const char * map[]); * * DESCRIPTION * * The routine rgr_write_bmp16 writes 16-color raster image in * uncompressed BMP file format (Windows bitmap) to a binary file whose * name is specified by the character string fname. * * The parameters m and n specify, respectively, the number of rows and * the numbers of columns (i.e. height and width) of the raster image. * * The character array map has m*n elements. Elements map[0, ..., n-1] * correspond to the first (top) scanline, elements map[n, ..., 2*n-1] * correspond to the second scanline, etc. * * Each element of the array map specifies a color of the corresponding * pixel as 8-bit binary number XXXXIRGB, where four high-order bits (X) * are ignored, I is high intensity bit, R is red color bit, G is green * color bit, and B is blue color bit. Thus, all 16 possible colors are * coded as following hexadecimal numbers: * * 0x00 = black 0x08 = dark gray * 0x01 = blue 0x09 = bright blue * 0x02 = green 0x0A = bright green * 0x03 = cyan 0x0B = bright cyan * 0x04 = red 0x0C = bright red * 0x05 = magenta 0x0D = bright magenta * 0x06 = brown 0x0E = yellow * 0x07 = light gray 0x0F = white * * RETURNS * * If no error occured, the routine returns zero; otherwise, it prints * an appropriate error message and returns non-zero. */ static void put_byte(FILE *fp, int c) { fputc(c, fp); return; } static void put_word(FILE *fp, int w) { /* big endian */ put_byte(fp, w); put_byte(fp, w >> 8); return; } static void put_dword(FILE *fp, int d) { /* big endian */ put_word(fp, d); put_word(fp, d >> 16); return; } int rgr_write_bmp16(const char *fname, int m, int n, const char map[]) { FILE *fp; int offset, bmsize, i, j, b, ret = 0; if (!(1 <= m && m <= 32767)) xfault("rgr_write_bmp16: m = %d; invalid height\n", m); if (!(1 <= n && n <= 32767)) xfault("rgr_write_bmp16: n = %d; invalid width\n", n); fp = fopen(fname, "wb"); if (fp == NULL) { xprintf("rgr_write_bmp16: unable to create `%s' - %s\n", fname, strerror(errno)); ret = 1; goto fini; } offset = 14 + 40 + 16 * 4; bmsize = (4 * n + 31) / 32; /* struct BMPFILEHEADER (14 bytes) */ /* UINT bfType */ put_byte(fp, 'B'), put_byte(fp, 'M'); /* DWORD bfSize */ put_dword(fp, offset + bmsize * 4); /* UINT bfReserved1 */ put_word(fp, 0); /* UNIT bfReserved2 */ put_word(fp, 0); /* DWORD bfOffBits */ put_dword(fp, offset); /* struct BMPINFOHEADER (40 bytes) */ /* DWORD biSize */ put_dword(fp, 40); /* LONG biWidth */ put_dword(fp, n); /* LONG biHeight */ put_dword(fp, m); /* WORD biPlanes */ put_word(fp, 1); /* WORD biBitCount */ put_word(fp, 4); /* DWORD biCompression */ put_dword(fp, 0 /* BI_RGB */); /* DWORD biSizeImage */ put_dword(fp, 0); /* LONG biXPelsPerMeter */ put_dword(fp, 2953 /* 75 dpi */); /* LONG biYPelsPerMeter */ put_dword(fp, 2953 /* 75 dpi */); /* DWORD biClrUsed */ put_dword(fp, 0); /* DWORD biClrImportant */ put_dword(fp, 0); /* struct RGBQUAD (16 * 4 = 64 bytes) */ /* CGA-compatible colors: */ /* 0x00 = black */ put_dword(fp, 0x000000); /* 0x01 = blue */ put_dword(fp, 0x000080); /* 0x02 = green */ put_dword(fp, 0x008000); /* 0x03 = cyan */ put_dword(fp, 0x008080); /* 0x04 = red */ put_dword(fp, 0x800000); /* 0x05 = magenta */ put_dword(fp, 0x800080); /* 0x06 = brown */ put_dword(fp, 0x808000); /* 0x07 = light gray */ put_dword(fp, 0xC0C0C0); /* 0x08 = dark gray */ put_dword(fp, 0x808080); /* 0x09 = bright blue */ put_dword(fp, 0x0000FF); /* 0x0A = bright green */ put_dword(fp, 0x00FF00); /* 0x0B = bright cyan */ put_dword(fp, 0x00FFFF); /* 0x0C = bright red */ put_dword(fp, 0xFF0000); /* 0x0D = bright magenta */ put_dword(fp, 0xFF00FF); /* 0x0E = yellow */ put_dword(fp, 0xFFFF00); /* 0x0F = white */ put_dword(fp, 0xFFFFFF); /* pixel data bits */ b = 0; for (i = m - 1; i >= 0; i--) { for (j = 0; j < ((n + 7) / 8) * 8; j++) { b <<= 4; b |= (j < n ? map[i * n + j] & 15 : 0); if (j & 1) put_byte(fp, b); } } fflush(fp); if (ferror(fp)) { xprintf("rgr_write_bmp16: write error on `%s' - %s\n", fname, strerror(errno)); ret = 1; } fini: if (fp != NULL) fclose(fp); return ret; } /* eof */ igraph/src/glpsql.c0000644000176000001440000013165312325527073014030 0ustar ripleyusers/* glpsql.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Author: Heinrich Schuchardt . * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wunused-function" #endif #ifdef HAVE_CONFIG_H #include #endif #include "glpmpl.h" #include "glpsql.h" #ifdef ODBC_DLNAME #define HAVE_ODBC #define libodbc ODBC_DLNAME #define h_odbc (get_env_ptr()->h_odbc) #endif #ifdef MYSQL_DLNAME #define HAVE_MYSQL #define libmysql MYSQL_DLNAME #define h_mysql (get_env_ptr()->h_mysql) #endif static void *db_iodbc_open_int(TABDCA *dca, int mode, const char **sqllines); static void *db_mysql_open_int(TABDCA *dca, int mode, const char **sqllines); /**********************************************************************/ #if defined(HAVE_ODBC) || defined(HAVE_MYSQL) #define SQL_FIELD_MAX 100 /* maximal field count */ #define SQL_FDLEN_MAX 255 /* maximal field length */ /*********************************************************************** * NAME * * args_concat - concatenate arguments * * SYNOPSIS * * static char **args_concat(TABDCA *dca); * * DESCRIPTION * * The arguments passed in dca are SQL statements. A SQL statement may * be split over multiple arguments. The last argument of a SQL * statement will be terminated with a semilocon. Each SQL statement is * merged into a single zero terminated string. Boundaries between * arguments are replaced by space. * * RETURNS * * Buffer with SQL statements */ static char **args_concat(TABDCA *dca) { const char *arg; int i; int j; int j0; int j1; int len; int lentot; int narg; int nline = 0; void *ret; char **sqllines = NULL; narg = mpl_tab_num_args(dca); /* The SQL statements start with argument 3. */ if (narg < 3) return NULL; /* Count the SQL statements */ for (j = 3; j <= narg; j++) { arg = mpl_tab_get_arg(dca, j); len = strlen(arg); if (arg[len-1] == ';' || j == narg) nline ++; } /* Allocate string buffer. */ sqllines = (char **) xmalloc((nline+1) * sizeof(char **)); /* Join arguments */ sqllines[0] = NULL; j0 = 3; i = 0; lentot = 0; for (j = 3; j <= narg; j++) { arg = mpl_tab_get_arg(dca, j); len = strlen(arg); lentot += len; if (arg[len-1] == ';' || j == narg) { /* Join arguments for a single SQL statement */ sqllines[i] = xmalloc(lentot+1); sqllines[i+1] = NULL; sqllines[i][0] = 0x00; for (j1 = j0; j1 <= j; j1++) { if(j1>j0) strcat(sqllines[i], " "); strcat(sqllines[i], mpl_tab_get_arg(dca, j1)); } len = strlen(sqllines[i]); if (sqllines[i][len-1] == ';') sqllines[i][len-1] = 0x00; j0 = j+1; i++; lentot = 0; } } return sqllines; } /*********************************************************************** * NAME * * free_buffer - free multiline string buffer * * SYNOPSIS * * static void free_buffer(char **buf); * * DESCRIPTION * * buf is a list of strings terminated by NULL. * The memory for the strings and for the list is released. */ static void free_buffer(char **buf) { int i; for(i = 0; buf[i] != NULL; i++) xfree(buf[i]); xfree(buf); } static int db_escaped_string_length(const char* from) /* length of escaped string */ { int count; const char *pointer; for (pointer = from, count = 0; *pointer != (char) '\0'; pointer++, count++) { switch (*pointer) { case '\'': count++; break; } } return count; } static int db_escape_string (char *to, const char *from) /* escape string*/ { const char *source = from; char *target = to; unsigned int remaining; remaining = strlen(from); if (to == NULL) to = (char *) (from + remaining); while (remaining > 0) { switch (*source) { case '\'': *target = '\''; target++; *target = '\''; break; default: *target = *source; } source++; target++; remaining--; } /* Write the terminating NUL character. */ *target = '\0'; return target - to; } static char *db_generate_select_stmt(TABDCA *dca) /* generate select statement */ { char *arg; char const *field; char *query; int j; int narg; int nf; int total; total = 50; nf = mpl_tab_num_flds(dca); narg = mpl_tab_num_args(dca); for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) { field = mpl_tab_get_name(dca, j); total += strlen(field); total += 2; } arg = (char *) mpl_tab_get_arg(dca, narg); total += strlen(arg); query = xmalloc( total * sizeof(char)); strcpy (query, "SELECT "); for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) { field = mpl_tab_get_name(dca, j); strcat(query, field); if ( j < nf ) strcat(query, ", "); } strcat(query, " FROM "); strcat(query, arg); return query; } static char *db_generate_insert_stmt(TABDCA *dca) /* generate insert statement */ { char *arg; char const *field; char *query; int j; int narg; int nf; int total; total = 50; nf = mpl_tab_num_flds(dca); narg = mpl_tab_num_args(dca); for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) { field = mpl_tab_get_name(dca, j); total += strlen(field); total += 5; } arg = (char *) mpl_tab_get_arg(dca, narg); total += strlen(arg); query = xmalloc( (total+1) * sizeof(char)); strcpy (query, "INSERT INTO "); strcat(query, arg); strcat(query, " ( "); for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) { field = mpl_tab_get_name(dca, j); strcat(query, field); if ( j < nf ) strcat(query, ", "); } strcat(query, " ) VALUES ( "); for (j=1; j <= nf && j <= SQL_FIELD_MAX; j++) { strcat(query, "?"); if ( j < nf ) strcat(query, ", "); } strcat(query, " )"); return query; } #endif /**********************************************************************/ #ifndef HAVE_ODBC void *db_iodbc_open(TABDCA *dca, int mode) { xassert(dca == dca); xassert(mode == mode); xprintf("iODBC table driver not supported\n"); return NULL; } int db_iodbc_read(TABDCA *dca, void *link) { xassert(dca != dca); xassert(link != link); return 0; } int db_iodbc_write(TABDCA *dca, void *link) { xassert(dca != dca); xassert(link != link); return 0; } int db_iodbc_close(TABDCA *dca, void *link) { xassert(dca != dca); xassert(link != link); return 0; } #else #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__WOE__) #include #endif #include #include struct db_odbc { int mode; /*'R' = Read, 'W' = Write*/ SQLHDBC hdbc; /*connection handle*/ SQLHENV henv; /*environment handle*/ SQLHSTMT hstmt; /*statement handle*/ SQLSMALLINT nresultcols; /* columns in result*/ SQLULEN collen[SQL_FIELD_MAX+1]; SQLLEN outlen[SQL_FIELD_MAX+1]; SQLSMALLINT coltype[SQL_FIELD_MAX+1]; SQLCHAR data[SQL_FIELD_MAX+1][SQL_FDLEN_MAX+1]; SQLCHAR colname[SQL_FIELD_MAX+1][SQL_FDLEN_MAX+1]; int isnumeric[SQL_FIELD_MAX+1]; int nf; /* number of fields in the csv file */ int ref[1+SQL_FIELD_MAX]; /* ref[k] = k', if k-th field of the csv file corresponds to k'-th field in the table statement; if ref[k] = 0, k-th field of the csv file is ignored */ SQLCHAR *query; /* query generated by db_iodbc_open */ }; SQLRETURN SQL_API dl_SQLAllocHandle ( SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) { typedef SQLRETURN SQL_API ep_SQLAllocHandle( SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle); ep_SQLAllocHandle *fn; fn = (ep_SQLAllocHandle *) xdlsym(h_odbc, "SQLAllocHandle"); xassert(fn != NULL); return (*fn)(HandleType, InputHandle, OutputHandle); } SQLRETURN SQL_API dl_SQLBindCol ( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind) { typedef SQLRETURN SQL_API ep_SQLBindCol( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind); ep_SQLBindCol *fn; fn = (ep_SQLBindCol *) xdlsym(h_odbc, "SQLBindCol"); xassert(fn != NULL); return (*fn)(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); } SQLRETURN SQL_API dl_SQLCloseCursor ( SQLHSTMT StatementHandle) { typedef SQLRETURN SQL_API ep_SQLCloseCursor ( SQLHSTMT StatementHandle); ep_SQLCloseCursor *fn; fn = (ep_SQLCloseCursor *) xdlsym(h_odbc, "SQLCloseCursor"); xassert(fn != NULL); return (*fn)(StatementHandle); } SQLRETURN SQL_API dl_SQLDisconnect ( SQLHDBC ConnectionHandle) { typedef SQLRETURN SQL_API ep_SQLDisconnect( SQLHDBC ConnectionHandle); ep_SQLDisconnect *fn; fn = (ep_SQLDisconnect *) xdlsym(h_odbc, "SQLDisconnect"); xassert(fn != NULL); return (*fn)(ConnectionHandle); } SQLRETURN SQL_API dl_SQLDriverConnect ( SQLHDBC hdbc, SQLHWND hwnd, SQLCHAR *szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR *szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT *pcbConnStrOut, SQLUSMALLINT fDriverCompletion) { typedef SQLRETURN SQL_API ep_SQLDriverConnect( SQLHDBC hdbc, SQLHWND hwnd, SQLCHAR * szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR * szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT * pcbConnStrOut, SQLUSMALLINT fDriverCompletion); ep_SQLDriverConnect *fn; fn = (ep_SQLDriverConnect *) xdlsym(h_odbc, "SQLDriverConnect"); xassert(fn != NULL); return (*fn)(hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); } SQLRETURN SQL_API dl_SQLEndTran ( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { typedef SQLRETURN SQL_API ep_SQLEndTran ( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType); ep_SQLEndTran *fn; fn = (ep_SQLEndTran *) xdlsym(h_odbc, "SQLEndTran"); xassert(fn != NULL); return (*fn)(HandleType, Handle, CompletionType); } SQLRETURN SQL_API dl_SQLExecDirect ( SQLHSTMT StatementHandle, SQLCHAR * StatementText, SQLINTEGER TextLength) { typedef SQLRETURN SQL_API ep_SQLExecDirect ( SQLHSTMT StatementHandle, SQLCHAR * StatementText, SQLINTEGER TextLength); ep_SQLExecDirect *fn; fn = (ep_SQLExecDirect *) xdlsym(h_odbc, "SQLExecDirect"); xassert(fn != NULL); return (*fn)(StatementHandle, StatementText, TextLength); } SQLRETURN SQL_API dl_SQLFetch ( SQLHSTMT StatementHandle) { typedef SQLRETURN SQL_API ep_SQLFetch ( SQLHSTMT StatementHandle); ep_SQLFetch *fn; fn = (ep_SQLFetch*) xdlsym(h_odbc, "SQLFetch"); xassert(fn != NULL); return (*fn)(StatementHandle); } SQLRETURN SQL_API dl_SQLFreeHandle ( SQLSMALLINT HandleType, SQLHANDLE Handle) { typedef SQLRETURN SQL_API ep_SQLFreeHandle ( SQLSMALLINT HandleType, SQLHANDLE Handle); ep_SQLFreeHandle *fn; fn = (ep_SQLFreeHandle *) xdlsym(h_odbc, "SQLFreeHandle"); xassert(fn != NULL); return (*fn)(HandleType, Handle); } SQLRETURN SQL_API dl_SQLDescribeCol ( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLCHAR * ColumnName, SQLSMALLINT BufferLength, SQLSMALLINT * NameLength, SQLSMALLINT * DataType, SQLULEN * ColumnSize, SQLSMALLINT * DecimalDigits, SQLSMALLINT * Nullable) { typedef SQLRETURN SQL_API ep_SQLDescribeCol ( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, SQLSMALLINT *DataType, SQLULEN *ColumnSize, SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable); ep_SQLDescribeCol *fn; fn = (ep_SQLDescribeCol *) xdlsym(h_odbc, "SQLDescribeCol"); xassert(fn != NULL); return (*fn)(StatementHandle, ColumnNumber, ColumnName, BufferLength, NameLength, DataType, ColumnSize, DecimalDigits, Nullable); } SQLRETURN SQL_API dl_SQLGetDiagRec ( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) { typedef SQLRETURN SQL_API ep_SQLGetDiagRec ( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength); ep_SQLGetDiagRec *fn; fn = (ep_SQLGetDiagRec *) xdlsym(h_odbc, "SQLGetDiagRec"); xassert(fn != NULL); return (*fn)(HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); } SQLRETURN SQL_API dl_SQLGetInfo ( SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, SQLPOINTER InfoValue, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength) { typedef SQLRETURN SQL_API ep_SQLGetInfo ( SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, SQLPOINTER InfoValue, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength); ep_SQLGetInfo *fn; fn = (ep_SQLGetInfo *) xdlsym(h_odbc, "SQLGetInfo"); xassert(fn != NULL); return (*fn)(ConnectionHandle, InfoType, InfoValue, BufferLength, StringLength); } SQLRETURN SQL_API dl_SQLNumResultCols ( SQLHSTMT StatementHandle, SQLSMALLINT *ColumnCount) { typedef SQLRETURN SQL_API ep_SQLNumResultCols ( SQLHSTMT StatementHandle, SQLSMALLINT *ColumnCount); ep_SQLNumResultCols *fn; fn = (ep_SQLNumResultCols *) xdlsym(h_odbc, "SQLNumResultCols"); xassert(fn != NULL); return (*fn)(StatementHandle, ColumnCount); } SQLRETURN SQL_API dl_SQLSetConnectAttr ( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) { typedef SQLRETURN SQL_API ep_SQLSetConnectAttr ( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength); ep_SQLSetConnectAttr *fn; fn = (ep_SQLSetConnectAttr *) xdlsym(h_odbc, "SQLSetConnectAttr"); xassert(fn != NULL); return (*fn)(ConnectionHandle, Attribute, Value, StringLength); } SQLRETURN SQL_API dl_SQLSetEnvAttr ( SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) { typedef SQLRETURN SQL_API ep_SQLSetEnvAttr ( SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength); ep_SQLSetEnvAttr *fn; fn = (ep_SQLSetEnvAttr *) xdlsym(h_odbc, "SQLSetEnvAttr"); xassert(fn != NULL); return (*fn)(EnvironmentHandle, Attribute, Value, StringLength); } static void extract_error( char *fn, SQLHANDLE handle, SQLSMALLINT type); static int is_numeric( SQLSMALLINT coltype); /*********************************************************************** * NAME * * db_iodbc_open - open connection to ODBC data base * * SYNOPSIS * * #include "glpsql.h" * void *db_iodbc_open(TABDCA *dca, int mode); * * DESCRIPTION * * The routine db_iodbc_open opens a connection to an ODBC data base. * It then executes the sql statements passed. * * In the case of table read the SELECT statement is executed. * * In the case of table write the INSERT statement is prepared. * RETURNS * * The routine returns a pointer to data storage area created. */ void *db_iodbc_open(TABDCA *dca, int mode) { void *ret; char **sqllines; sqllines = args_concat(dca); if (sqllines == NULL) { xprintf("Missing arguments in table statement.\n" "Please, supply table driver, dsn, and query.\n"); return NULL; } ret = db_iodbc_open_int(dca, mode, (const char **) sqllines); free_buffer(sqllines); return ret; } static void *db_iodbc_open_int(TABDCA *dca, int mode, const char **sqllines) { struct db_odbc *sql; SQLRETURN ret; SQLCHAR FAR *dsn; SQLCHAR info[256]; SQLSMALLINT colnamelen; SQLSMALLINT nullable; SQLSMALLINT scale; const char *arg; int narg; int i, j; int total; if (libodbc == NULL) { xprintf("No loader for shared ODBC library available\n"); return NULL; } if (h_odbc == NULL) { h_odbc = xdlopen(libodbc); if (h_odbc == NULL) { xprintf("unable to open library %s\n", libodbc); xprintf("%s\n", xerrmsg()); return NULL; } } sql = (struct db_odbc *) xmalloc(sizeof(struct db_odbc)); if (sql == NULL) return NULL; sql->mode = mode; sql->hdbc = NULL; sql->henv = NULL; sql->hstmt = NULL; sql->query = NULL; narg = mpl_tab_num_args(dca); dsn = (SQLCHAR FAR *) mpl_tab_get_arg(dca, 2); /* allocate an environment handle */ ret = dl_SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(sql->henv)); /* set attribute to enable application to run as ODBC 3.0 application */ ret = dl_SQLSetEnvAttr(sql->henv, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); /* allocate a connection handle */ ret = dl_SQLAllocHandle(SQL_HANDLE_DBC, sql->henv, &(sql->hdbc)); /* connect */ ret = dl_SQLDriverConnect(sql->hdbc, NULL, dsn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE); if (SQL_SUCCEEDED(ret)) { /* output information about data base connection */ xprintf("Connected to "); dl_SQLGetInfo(sql->hdbc, SQL_DBMS_NAME, (SQLPOINTER)info, sizeof(info), NULL); xprintf("%s ", info); dl_SQLGetInfo(sql->hdbc, SQL_DBMS_VER, (SQLPOINTER)info, sizeof(info), NULL); xprintf("%s - ", info); dl_SQLGetInfo(sql->hdbc, SQL_DATABASE_NAME, (SQLPOINTER)info, sizeof(info), NULL); xprintf("%s\n", info); } else { /* describe error */ xprintf("Failed to connect\n"); extract_error("SQLDriverConnect", sql->hdbc, SQL_HANDLE_DBC); dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); xfree(sql); return NULL; } /* set AUTOCOMMIT on*/ ret = dl_SQLSetConnectAttr(sql->hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0); /* allocate a statement handle */ ret = dl_SQLAllocHandle(SQL_HANDLE_STMT, sql->hdbc, &(sql->hstmt)); /* initialization queries */ for(j = 0; sqllines[j+1] != NULL; j++) { sql->query = (SQLCHAR *) sqllines[j]; xprintf("%s\n", sql->query); ret = dl_SQLExecDirect(sql->hstmt, sql->query, SQL_NTS); switch (ret) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: case SQL_NO_DATA_FOUND: break; default: xprintf("db_iodbc_open: Query\n\"%s\"\nfailed.\n", sql->query); extract_error("SQLExecDirect", sql->hstmt, SQL_HANDLE_STMT); dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); dl_SQLDisconnect(sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); xfree(sql); return NULL; } /* commit statement */ dl_SQLEndTran(SQL_HANDLE_ENV, sql->henv, SQL_COMMIT); } if ( sql->mode == 'R' ) { sql->nf = mpl_tab_num_flds(dca); for(j = 0; sqllines[j] != NULL; j++) arg = sqllines[j]; total = strlen(arg); if (total > 7 && 0 == strncmp(arg, "SELECT ", 7)) { total = strlen(arg); sql->query = xmalloc( (total+1) * sizeof(char)); strcpy (sql->query, arg); } else { sql->query = db_generate_select_stmt(dca); } xprintf("%s\n", sql->query); if (dl_SQLExecDirect(sql->hstmt, sql->query, SQL_NTS) != SQL_SUCCESS) { xprintf("db_iodbc_open: Query\n\"%s\"\nfailed.\n", sql->query); extract_error("SQLExecDirect", sql->hstmt, SQL_HANDLE_STMT); dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); dl_SQLDisconnect(sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); xfree(sql->query); xfree(sql); return NULL; } xfree(sql->query); /* determine number of result columns */ ret = dl_SQLNumResultCols(sql->hstmt, &sql->nresultcols); total = sql->nresultcols; if (total > SQL_FIELD_MAX) { xprintf("db_iodbc_open: Too many fields (> %d) in query.\n" "\"%s\"\n", SQL_FIELD_MAX, sql->query); dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); dl_SQLDisconnect(sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); xfree(sql->query); return NULL; } for (i = 1; i <= total; i++) { /* return a set of attributes for a column */ ret = dl_SQLDescribeCol(sql->hstmt, (SQLSMALLINT) i, sql->colname[i], SQL_FDLEN_MAX, &colnamelen, &(sql->coltype[i]), &(sql->collen[i]), &scale, &nullable); sql->isnumeric[i] = is_numeric(sql->coltype[i]); /* bind columns to program vars, converting all types to CHAR*/ dl_SQLBindCol(sql->hstmt, i, SQL_CHAR, sql->data[i], SQL_FDLEN_MAX, &(sql->outlen[i])); for (j = sql->nf; j >= 1; j--) { if (strcmp(mpl_tab_get_name(dca, j), sql->colname[i]) == 0) break; } sql->ref[i] = j; } } else if ( sql->mode == 'W' ) { for(j = 0; sqllines[j] != NULL; j++) arg = sqllines[j]; if ( NULL != strchr(arg, '?') ) { total = strlen(arg); sql->query = xmalloc( (total+1) * sizeof(char)); strcpy (sql->query, arg); } else { sql->query = db_generate_insert_stmt(dca); } xprintf("%s\n", sql->query); } return sql; } int db_iodbc_read(TABDCA *dca, void *link) { struct db_odbc *sql; SQLRETURN ret; char buf[SQL_FDLEN_MAX+1]; int i; int len; double num; sql = (struct db_odbc *) link; xassert(sql != NULL); xassert(sql->mode == 'R'); ret=dl_SQLFetch(sql->hstmt); if (ret== SQL_ERROR) return -1; if (ret== SQL_NO_DATA_FOUND) return -1; /*EOF*/ for (i=1; i <= sql->nresultcols; i++) { if (sql->ref[i] > 0) { len = sql->outlen[i]; if (len != SQL_NULL_DATA) { if (len > SQL_FDLEN_MAX) len = SQL_FDLEN_MAX; else if (len < 0) len = 0; strncpy(buf, (const char *) sql->data[i], len); buf[len] = 0x00; if (0 != (sql->isnumeric[i])) { strspx(buf); /* remove spaces*/ if (str2num(buf, &num) != 0) { xprintf("'%s' cannot be converted to a number.\n", buf); return 1; } mpl_tab_set_num(dca, sql->ref[i], num); } else { mpl_tab_set_str(dca, sql->ref[i], strtrim(buf)); } } } } return 0; } int db_iodbc_write(TABDCA *dca, void *link) { struct db_odbc *sql; char *part; char *query; char *template; char num[50]; int k; int len; int nf; sql = (struct db_odbc *) link; xassert(sql != NULL); xassert(sql->mode == 'W'); len = strlen(sql->query); template = (char *) xmalloc( (len + 1) * sizeof(char) ); strcpy(template, sql->query); nf = mpl_tab_num_flds(dca); for (k = 1; k <= nf; k++) { switch (mpl_tab_get_type(dca, k)) { case 'N': len += 20; break; case 'S': len += db_escaped_string_length(mpl_tab_get_str(dca, k)); len += 2; break; default: xassert(dca != dca); } } query = xmalloc( (len + 1 ) * sizeof(char) ); query[0] = 0x00; for (k = 1, part = strtok (template, "?"); (part != NULL); part = strtok (NULL, "?"), k++) { if (k > nf) break; strcat( query, part ); switch (mpl_tab_get_type(dca, k)) { case 'N': #if 0 /* 02/XI-2010 by xypron */ sprintf(num, "%-18g",mpl_tab_get_num(dca, k)); #else sprintf(num, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); #endif strcat( query, num ); break; case 'S': strcat( query, "'"); db_escape_string( query + strlen(query), mpl_tab_get_str(dca, k) ); strcat( query, "'"); break; default: xassert(dca != dca); } } if (part != NULL) strcat(query, part); if (dl_SQLExecDirect(sql->hstmt, (SQLCHAR *) query, SQL_NTS) != SQL_SUCCESS) { xprintf("db_iodbc_write: Query\n\"%s\"\nfailed.\n", query); extract_error("SQLExecDirect", sql->hdbc, SQL_HANDLE_DBC); xfree(query); xfree(template); return 1; } xfree(query); xfree(template); return 0; } int db_iodbc_close(TABDCA *dca, void *link) { struct db_odbc *sql; sql = (struct db_odbc *) link; xassert(sql != NULL); /* Commit */ if ( sql->mode == 'W' ) dl_SQLEndTran(SQL_HANDLE_ENV, sql->henv, SQL_COMMIT); if ( sql->mode == 'R' ) dl_SQLCloseCursor(sql->hstmt); dl_SQLFreeHandle(SQL_HANDLE_STMT, sql->hstmt); dl_SQLDisconnect(sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_DBC, sql->hdbc); dl_SQLFreeHandle(SQL_HANDLE_ENV, sql->henv); if ( sql->mode == 'W' ) xfree(sql->query); xfree(sql); dca->link = NULL; return 0; } static void extract_error( char *fn, SQLHANDLE handle, SQLSMALLINT type) { SQLINTEGER i = 0; SQLINTEGER native; SQLCHAR state[ 7 ]; SQLCHAR text[256]; SQLSMALLINT len; SQLRETURN ret; xprintf("\nThe driver reported the following diagnostics whilst " "running %s\n", fn); do { ret = dl_SQLGetDiagRec(type, handle, ++i, state, &native, text, sizeof(text), &len ); if (SQL_SUCCEEDED(ret)) xprintf("%s:%ld:%ld:%s\n", state, i, native, text); } while( ret == SQL_SUCCESS ); } static int is_numeric(SQLSMALLINT coltype) { int ret = 0; switch (coltype) { case SQL_DECIMAL: case SQL_NUMERIC: case SQL_SMALLINT: case SQL_INTEGER: case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: case SQL_TINYINT: case SQL_BIGINT: ret = 1; break; } return ret; } #endif /**********************************************************************/ #ifndef HAVE_MYSQL void *db_mysql_open(TABDCA *dca, int mode) { xassert(dca == dca); xassert(mode == mode); xprintf("MySQL table driver not supported\n"); return NULL; } int db_mysql_read(TABDCA *dca, void *link) { xassert(dca != dca); xassert(link != link); return 0; } int db_mysql_write(TABDCA *dca, void *link) { xassert(dca != dca); xassert(link != link); return 0; } int db_mysql_close(TABDCA *dca, void *link) { xassert(dca != dca); xassert(link != link); return 0; } #else #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__WOE__) #include #endif #ifdef __CYGWIN__ #define byte_defined 1 #endif #include #include #include struct db_mysql { int mode; /*'R' = Read, 'W' = Write*/ MYSQL *con; /*connection*/ MYSQL_RES *res; /*result*/ int nf; /* number of fields in the csv file */ int ref[1+SQL_FIELD_MAX]; /* ref[k] = k', if k-th field of the csv file corresponds to k'-th field in the table statement; if ref[k] = 0, k-th field of the csv file is ignored */ char *query; /* query generated by db_mysql_open */ }; void STDCALL dl_mysql_close(MYSQL *sock) { typedef void STDCALL ep_mysql_close(MYSQL *sock); ep_mysql_close *fn; fn = (ep_mysql_close *) xdlsym(h_mysql, "mysql_close"); xassert(fn != NULL); return (*fn)(sock); } const char * STDCALL dl_mysql_error(MYSQL *mysql) { typedef const char * STDCALL ep_mysql_error(MYSQL *mysql); ep_mysql_error *fn; fn = (ep_mysql_error *) xdlsym(h_mysql, "mysql_error"); xassert(fn != NULL); return (*fn)(mysql); } MYSQL_FIELD * STDCALL dl_mysql_fetch_fields(MYSQL_RES *res) { typedef MYSQL_FIELD * STDCALL ep_mysql_fetch_fields(MYSQL_RES *res); ep_mysql_fetch_fields *fn; fn = (ep_mysql_fetch_fields *) xdlsym(h_mysql, "mysql_fetch_fields"); xassert(fn != NULL); return (*fn)(res); } unsigned long * STDCALL dl_mysql_fetch_lengths(MYSQL_RES *result) { typedef unsigned long * STDCALL ep_mysql_fetch_lengths(MYSQL_RES *result); ep_mysql_fetch_lengths *fn; fn = (ep_mysql_fetch_lengths *) xdlsym(h_mysql, "mysql_fetch_lengths"); xassert(fn != NULL); return (*fn)(result); } MYSQL_ROW STDCALL dl_mysql_fetch_row(MYSQL_RES *result) { typedef MYSQL_ROW STDCALL ep_mysql_fetch_row(MYSQL_RES *result); ep_mysql_fetch_row *fn; fn = (ep_mysql_fetch_row *) xdlsym(h_mysql, "mysql_fetch_row"); xassert(fn != NULL); return (*fn)(result); } unsigned int STDCALL dl_mysql_field_count(MYSQL *mysql) { typedef unsigned int STDCALL ep_mysql_field_count(MYSQL *mysql); ep_mysql_field_count *fn; fn = (ep_mysql_field_count *) xdlsym(h_mysql, "mysql_field_count"); xassert(fn != NULL); return (*fn)(mysql); } MYSQL * STDCALL dl_mysql_init(MYSQL *mysql) { typedef MYSQL * STDCALL ep_mysql_init(MYSQL *mysql); ep_mysql_init *fn; fn = (ep_mysql_init *) xdlsym(h_mysql, "mysql_init"); xassert(fn != NULL); return (*fn)(mysql); } unsigned int STDCALL dl_mysql_num_fields(MYSQL_RES *res) { typedef unsigned int STDCALL ep_mysql_num_fields(MYSQL_RES *res); ep_mysql_num_fields *fn; fn = (ep_mysql_num_fields *) xdlsym(h_mysql, "mysql_num_fields"); xassert(fn != NULL); return (*fn)(res); } int STDCALL dl_mysql_query(MYSQL *mysql, const char *q) { typedef int STDCALL ep_mysql_query(MYSQL *mysql, const char *q); ep_mysql_query *fn; fn = (ep_mysql_query *) xdlsym(h_mysql, "mysql_query"); xassert(fn != NULL); return (*fn)(mysql, q); } MYSQL * STDCALL dl_mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag) { typedef MYSQL * STDCALL ep_mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag); ep_mysql_real_connect *fn; fn = (ep_mysql_real_connect *) xdlsym(h_mysql, "mysql_real_connect"); xassert(fn != NULL); return (*fn)(mysql, host, user, passwd, db, port, unix_socket, clientflag); } MYSQL_RES * STDCALL dl_mysql_use_result(MYSQL *mysql) { typedef MYSQL_RES * STDCALL ep_mysql_use_result(MYSQL *mysql); ep_mysql_use_result *fn; fn = (ep_mysql_use_result *) xdlsym(h_mysql, "mysql_use_result"); xassert(fn != NULL); return (*fn)(mysql); } /*********************************************************************** * NAME * * db_mysql_open - open connection to ODBC data base * * SYNOPSIS * * #include "glpsql.h" * void *db_mysql_open(TABDCA *dca, int mode); * * DESCRIPTION * * The routine db_mysql_open opens a connection to a MySQL data base. * It then executes the sql statements passed. * * In the case of table read the SELECT statement is executed. * * In the case of table write the INSERT statement is prepared. * RETURNS * * The routine returns a pointer to data storage area created. */ void *db_mysql_open(TABDCA *dca, int mode) { void *ret; char **sqllines; sqllines = args_concat(dca); if (sqllines == NULL) { xprintf("Missing arguments in table statement.\n" "Please, supply table driver, dsn, and query.\n"); return NULL; } ret = db_mysql_open_int(dca, mode, (const char **) sqllines); free_buffer(sqllines); return ret; } static void *db_mysql_open_int(TABDCA *dca, int mode, const char **sqllines) { struct db_mysql *sql = NULL; char *arg = NULL; const char *field; MYSQL_FIELD *fields; char *keyword; char *value; char *query; char *dsn; /* "Server=[server_name];Database=[database_name];UID=[username];*/ /* PWD=[password];Port=[port]"*/ char *server = NULL; /* Server */ char *user = NULL; /* UID */ char *password = NULL; /* PWD */ char *database = NULL; /* Database */ unsigned int port = 0; /* Port */ int narg; int i, j, total; if (libmysql == NULL) { xprintf("No loader for shared MySQL library available\n"); return NULL; } if (h_mysql == NULL) { h_mysql = xdlopen(libmysql); if (h_mysql == NULL) { xprintf("unable to open library %s\n", libmysql); xprintf("%s\n", xerrmsg()); return NULL; } } sql = (struct db_mysql *) xmalloc(sizeof(struct db_mysql)); if (sql == NULL) return NULL; sql->mode = mode; sql->res = NULL; sql->query = NULL; sql->nf = mpl_tab_num_flds(dca); narg = mpl_tab_num_args(dca); if (narg < 3 ) xprintf("MySQL driver: string list too short \n"); /* get connection string*/ dsn = (char *) mpl_tab_get_arg(dca, 2); /* copy connection string*/ i = strlen(dsn); i++; arg = xmalloc(i * sizeof(char)); strcpy(arg, dsn); /*tokenize connection string*/ for (i = 1, keyword = strtok (arg, "="); (keyword != NULL); keyword = strtok (NULL, "="), i++) { value = strtok (NULL, ";"); if (value==NULL) { xprintf("db_mysql_open: Missing value for keyword %s\n", keyword); xfree(arg); xfree(sql); return NULL; } if (0 == strcmp(keyword, "Server")) server = value; else if (0 == strcmp(keyword, "Database")) database = value; else if (0 == strcmp(keyword, "UID")) user = value; else if (0 == strcmp(keyword, "PWD")) password = value; else if (0 == strcmp(keyword, "Port")) port = (unsigned int) atol(value); } /* Connect to database */ sql->con = dl_mysql_init(NULL); if (!dl_mysql_real_connect(sql->con, server, user, password, database, port, NULL, 0)) { xprintf("db_mysql_open: Connect failed\n"); xprintf("%s\n", dl_mysql_error(sql->con)); xfree(arg); xfree(sql); return NULL; } xfree(arg); for(j = 0; sqllines[j+1] != NULL; j++) { query = (char *) sqllines[j]; xprintf("%s\n", query); if (dl_mysql_query(sql->con, query)) { xprintf("db_mysql_open: Query\n\"%s\"\nfailed.\n", query); xprintf("%s\n",dl_mysql_error(sql->con)); dl_mysql_close(sql->con); xfree(sql); return NULL; } } if ( sql->mode == 'R' ) { sql->nf = mpl_tab_num_flds(dca); for(j = 0; sqllines[j] != NULL; j++) arg = (char *) sqllines[j]; total = strlen(arg); if (total > 7 && 0 == strncmp(arg, "SELECT ", 7)) { total = strlen(arg); query = xmalloc( (total+1) * sizeof(char)); strcpy (query, arg); } else { query = db_generate_select_stmt(dca); } xprintf("%s\n", query); if (dl_mysql_query(sql->con, query)) { xprintf("db_mysql_open: Query\n\"%s\"\nfailed.\n", query); xprintf("%s\n",dl_mysql_error(sql->con)); dl_mysql_close(sql->con); xfree(query); xfree(sql); return NULL; } xfree(query); sql->res = dl_mysql_use_result(sql->con); if (sql->res) { /* create references between query results and table fields*/ total = dl_mysql_num_fields(sql->res); if (total > SQL_FIELD_MAX) { xprintf("db_mysql_open: Too many fields (> %d) in query.\n" "\"%s\"\n", SQL_FIELD_MAX, query); xprintf("%s\n",dl_mysql_error(sql->con)); dl_mysql_close(sql->con); xfree(query); xfree(sql); return NULL; } fields = dl_mysql_fetch_fields(sql->res); for (i = 1; i <= total; i++) { for (j = sql->nf; j >= 1; j--) { if (strcmp(mpl_tab_get_name(dca, j), fields[i-1].name) == 0) break; } sql->ref[i] = j; } } else { if(dl_mysql_field_count(sql->con) == 0) { xprintf("db_mysql_open: Query was not a SELECT\n\"%s\"\n", query); xprintf("%s\n",dl_mysql_error(sql->con)); xfree(query); xfree(sql); return NULL; } else { xprintf("db_mysql_open: Query\n\"%s\"\nfailed.\n", query); xprintf("%s\n",dl_mysql_error(sql->con)); xfree(query); xfree(sql); return NULL; } } } else if ( sql->mode == 'W' ) { for(j = 0; sqllines[j] != NULL; j++) arg = (char *) sqllines[j]; if ( NULL != strchr(arg, '?') ) { total = strlen(arg); query = xmalloc( (total+1) * sizeof(char)); strcpy (query, arg); } else query = db_generate_insert_stmt(dca); sql->query = query; xprintf("%s\n", query); } return sql; } int db_mysql_read(TABDCA *dca, void *link) { struct db_mysql *sql; char buf[255+1]; char **row; unsigned long *lengths; MYSQL_FIELD *fields; double num; int len; unsigned long num_fields; int i; sql = (struct db_mysql *) link; xassert(sql != NULL); xassert(sql->mode == 'R'); if (NULL == sql->res) { xprintf("db_mysql_read: no result set available"); return 1; } if (NULL==(row = (char **)dl_mysql_fetch_row(sql->res))) { return -1; /*EOF*/ } lengths = dl_mysql_fetch_lengths(sql->res); fields = dl_mysql_fetch_fields(sql->res); num_fields = dl_mysql_num_fields(sql->res); for (i=1; i <= num_fields; i++) { if (row[i-1] != NULL) { len = (size_t) lengths[i-1]; if (len > 255) len = 255; strncpy(buf, (const char *) row[i-1], len); buf[len] = 0x00; if (0 != (fields[i-1].flags & NUM_FLAG)) { strspx(buf); /* remove spaces*/ if (str2num(buf, &num) != 0) { xprintf("'%s' cannot be converted to a number.\n", buf); return 1; } if (sql->ref[i] > 0) mpl_tab_set_num(dca, sql->ref[i], num); } else { if (sql->ref[i] > 0) mpl_tab_set_str(dca, sql->ref[i], strtrim(buf)); } } } return 0; } int db_mysql_write(TABDCA *dca, void *link) { struct db_mysql *sql; char *part; char *query; char *template; char num[50]; int k; int len; int nf; sql = (struct db_mysql *) link; xassert(sql != NULL); xassert(sql->mode == 'W'); len = strlen(sql->query); template = (char *) xmalloc( (len + 1) * sizeof(char) ); strcpy(template, sql->query); nf = mpl_tab_num_flds(dca); for (k = 1; k <= nf; k++) { switch (mpl_tab_get_type(dca, k)) { case 'N': len += 20; break; case 'S': len += db_escaped_string_length(mpl_tab_get_str(dca, k)); len += 2; break; default: xassert(dca != dca); } } query = xmalloc( (len + 1 ) * sizeof(char) ); query[0] = 0x00; for (k = 1, part = strtok (template, "?"); (part != NULL); part = strtok (NULL, "?"), k++) { if (k > nf) break; strcat( query, part ); switch (mpl_tab_get_type(dca, k)) { case 'N': #if 0 /* 02/XI-2010 by xypron */ sprintf(num, "%-18g",mpl_tab_get_num(dca, k)); #else sprintf(num, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); #endif strcat( query, num ); break; case 'S': strcat( query, "'"); db_escape_string( query + strlen(query), mpl_tab_get_str(dca, k) ); strcat( query, "'"); break; default: xassert(dca != dca); } } if (part != NULL) strcat(query, part); if (dl_mysql_query(sql->con, query)) { xprintf("db_mysql_write: Query\n\"%s\"\nfailed.\n", query); xprintf("%s\n",dl_mysql_error(sql->con)); xfree(query); xfree(template); return 1; } xfree(query); xfree(template); return 0; } int db_mysql_close(TABDCA *dca, void *link) { struct db_mysql *sql; sql = (struct db_mysql *) link; xassert(sql != NULL); dl_mysql_close(sql->con); if ( sql->mode == 'W' ) xfree(sql->query); xfree(sql); dca->link = NULL; return 0; } #endif /* eof */ igraph/src/drl_layout_3d.cpp0000644000176000001440000001071712325527073015627 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Layout // // This program implements a parallel force directed graph drawing // algorithm. The algorithm used is based upon a random decomposition // of the graph and simulated shared memory of node position and density. // In this version, the simulated shared memory is spread among all processors // // The structure of the inputs and outputs of this code will be displayed // if the program is called without parameters, or if an erroneous // parameter is passed to the program. // // S. Martin // 5/6/2005 // C++ library routines #include #include #include #include #include #include #include using namespace std; // layout routines and constants #include "drl_layout_3d.h" #include "drl_parse.h" #include "drl_graph_3d.h" // MPI #ifdef MUSE_MPI #include #endif using namespace drl3d; #include "igraph_layout.h" #include "igraph_random.h" #include "igraph_interface.h" /** * \function igraph_layout_drl_3d * The DrL layout generator, 3d version. * * This function implements the force-directed DrL layout generator. * Please see more in the technical report: Martin, S., Brown, W.M., * Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph) * Layout. SAND Reports, 2008. 2936: p. 1-10. * * This function uses a modified DrL generator that does * the layout in three dimensions. * \param graph The input graph. * \param use_seed Logical scalar, if true, then the coordinates * supplied in the \p res argument are used as starting points. * \param res Pointer to a matrix, the result layout is stored * here. It will be resized as needed. * \param options The parameters to pass to the layout generator. * \param weights Edge weights, pointer to a vector. If this is a null * pointer then every edge will have the same weight. * \param fixed Pointer to a logical vector, or a null pointer. This * can be used to fix the position of some vertices. Vertices for * which it is true will not be moved, but stay at the coordinates * given in the \p res matrix. This argument is ignored if it is a * null pointer or if use_seed is false. * \return Error code. * * Time complexity: ???. * * \sa \ref igraph_layout_drl() for the standard 2d version. */ int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t use_seed, igraph_layout_drl_options_t *options, const igraph_vector_t *weights, const igraph_vector_bool_t *fixed) { RNG_BEGIN(); drl3d::graph neighbors(graph, options, weights); neighbors.init_parms(options); if (use_seed) { IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3)); neighbors.read_real(res, fixed); } neighbors.draw_graph(res); RNG_END(); return 0; } igraph/src/bliss_graph.hh0000644000176000001440000001542012325372072015166 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_GRAPH_HH #define BLISS_GRAPH_HH namespace igraph { class AbstractGraph; } #include #include #include "bliss_kstack.hh" #include "bliss_kqueue.hh" #include "bliss_heap.hh" #include "bliss_orbit.hh" #include "bliss_partition.hh" #include "bliss_bignum.hh" #include "bliss_eqrefhash.hh" #include "igraph_datatype.h" namespace igraph { typedef struct t_Stats { BigNum group_size; long unsigned int nof_nodes; long unsigned int nof_leaf_nodes; long unsigned int nof_bad_nodes; long unsigned int nof_canupdates; long unsigned int nof_generators; unsigned long int max_level; } Stats; class AbstractGraph { friend class Partition; protected: AbstractGraph(); virtual ~AbstractGraph(); Partition p; bool in_search; bool refine_compare_certificate; bool refine_equal_to_first; unsigned int refine_first_path_subcertificate_end; int refine_cmp_to_best; unsigned int refine_best_path_subcertificate_end; /* Max mem used by long prune in megabytes */ static const unsigned int long_prune_options_max_mem = 20; static const unsigned int long_prune_options_max_stored_auts = 50; unsigned int long_prune_max_stored_autss; std::vector *> long_prune_fixed; std::vector *> long_prune_mcrs; std::vector long_prune_temp; unsigned int long_prune_begin; unsigned int long_prune_end; void long_prune_init(); void long_prune_add_automorphism(const unsigned int *aut); std::vector &long_prune_get_fixed(const unsigned int index); std::vector &long_prune_get_mcrs(const unsigned int index); void long_prune_swap(const unsigned int i, const unsigned int j); /* * Data structures and routines for refining the partition p into equitable */ Heap neighbour_heap; virtual bool split_neighbourhood_of_unit_cell(Cell *) = 0; virtual void split_neighbourhood_of_cell(Cell * const) = 0; void refine_to_equitable(); void refine_to_equitable(Cell *cell); void refine_to_equitable(Cell *cell1, Cell *cell2); void do_refine_to_equitable(); unsigned int eqref_max_certificate_index; bool eqref_worse_than_certificate; //void eqref_update_hash(unsigned int i); EqrefHash eqref_hash; /* For debugging purposes only */ virtual bool is_equitable() {assert(0); return false; } void print_permutation(FILE *, const unsigned int *perm); unsigned int *first_path_labeling; unsigned int *first_path_labeling_inv; Orbit first_path_orbits; unsigned int *first_path_automorphism; unsigned int *best_path_labeling; unsigned int *best_path_labeling_inv; Orbit best_path_orbits; unsigned int *best_path_automorphism; void update_labeling(unsigned int * const lab); void update_labeling_and_its_inverse(unsigned int * const lab, unsigned int * const lab_inv); void update_orbit_information(Orbit &o, const unsigned int *perm); void reset_permutation(unsigned int *perm); /* Mainly for debugging purposes */ virtual bool is_automorphism(unsigned int * const perm); std::vector certificate_current_path; std::vector certificate_first_path; std::vector certificate_best_path; //unsigned int *certificate; unsigned int certificate_size; unsigned int certificate_index; virtual void initialize_certificate() = 0; virtual void remove_duplicate_edges() = 0; virtual void make_initial_equitable_partition() = 0; virtual Cell *find_next_cell_to_be_splitted(Cell *cell) = 0; void search(const bool canonical, Stats &stats); #ifdef PRINT_SEARCH_TREE_DOT FILE *dotty_output; #endif public: virtual unsigned int get_nof_vertices() = 0; void find_automorphisms(Stats &stats); const unsigned int *canonical_form(Stats &stats); }; /* * Undirected, vertex labeled graph * Multiple edges between vertices are not allowed (i.e., will be ignored) */ class Graph : public AbstractGraph { class Vertex { public: Vertex(); ~Vertex(); void add_edge(const unsigned int other_vertex); void remove_duplicate_edges(bool * const); unsigned int label; unsigned int nof_edges; std::vector edges; }; std::vector vertices; void remove_duplicate_edges(); /* * Partition independent invariants for this graph class */ static unsigned int label_invariant(Graph *g, unsigned int v); static unsigned int degree_invariant(Graph *g, unsigned int v); bool refine_according_to_invariant(unsigned int (*inv)(Graph * g, unsigned int v)); /* * Routines needed when refining the partition p into equitable */ bool split_neighbourhood_of_unit_cell(Cell *); void split_neighbourhood_of_cell(Cell * const); /* For debugging purposes only */ bool is_equitable(); Cell *find_next_cell_to_be_splitted(Cell *cell); /* Splitting heuristics */ unsigned int sh; Cell *sh_first(Cell *cell); Cell *sh_first_smallest(Cell *cell); Cell *sh_first_largest(Cell *cell); Cell *sh_first_max_neighbours(Cell *cell); Cell *sh_first_smallest_max_neighbours(Cell *cell); Cell *sh_first_largest_max_neighbours(Cell *cell); void make_initial_equitable_partition(); void initialize_certificate(); bool is_automorphism(unsigned int * const perm); public: Graph(const unsigned int nof_vertices = 0); ~Graph(); static const unsigned int sh_f = 0; static const unsigned int sh_fs = 1; static const unsigned int sh_fl = 2; static const unsigned int sh_fm = 3; static const unsigned int sh_fsm = 4; static const unsigned int sh_flm = 5; static Graph *read_dimacs(FILE *); static Graph *from_igraph(const igraph_t *graph); void print_dimacs(FILE *); void to_dot(FILE *fp); void to_dot(const char *file_name); unsigned int get_nof_vertices() {return vertices.size(); } Graph *permute(const unsigned int *perm); unsigned int add_vertex(const unsigned int label = 1); void add_edge(const unsigned int vertex1, const unsigned int vertex2); void change_label(const unsigned int vertex, const unsigned int new_label); void set_splitting_heuristics(unsigned int shs) {sh = shs; } }; } #endif igraph/src/igraph_vector.h0000644000176000001440000001137712325527073015367 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_VECTOR_H #define IGRAPH_VECTOR_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_complex.h" #ifdef HAVE_STDINT_H # include #else # if HAVE_SYS_INT_TYPES_H # include /* for Solaris */ # endif #endif __BEGIN_DECLS /* -------------------------------------------------- */ /* Flexible vector */ /* -------------------------------------------------- */ #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "igraph_vector_type.h" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "igraph_vector_type.h" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_vector_type.h" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "igraph_vector_type.h" #include "igraph_pmt_off.h" #undef BASE_BOOL #define BASE_INT #include "igraph_pmt.h" #include "igraph_vector_type.h" #include "igraph_pmt_off.h" #undef BASE_INT #define BASE_COMPLEX #include "igraph_pmt.h" #include "igraph_vector_type.h" #include "igraph_pmt_off.h" #undef BASE_COMPLEX #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "igraph_vector_pmt.h" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "igraph_vector_pmt.h" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_vector_pmt.h" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "igraph_vector_pmt.h" #include "igraph_pmt_off.h" #undef BASE_BOOL #define BASE_INT #include "igraph_pmt.h" #include "igraph_vector_pmt.h" #include "igraph_pmt_off.h" #undef BASE_INT #define BASE_COMPLEX #include "igraph_pmt.h" #include "igraph_vector_pmt.h" #include "igraph_pmt_off.h" #undef BASE_COMPLEX /* -------------------------------------------------- */ /* Helper macros */ /* -------------------------------------------------- */ #ifndef IGRAPH_VECTOR_NULL #define IGRAPH_VECTOR_NULL { 0,0,0 } #endif #ifndef IGRAPH_VECTOR_INIT_FINALLY #define IGRAPH_VECTOR_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_vector_init(v, size)); \ IGRAPH_FINALLY(igraph_vector_destroy, v); } while (0) #endif #ifndef IGRAPH_VECTOR_BOOL_INIT_FINALLY #define IGRAPH_VECTOR_BOOL_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_vector_bool_init(v, size)); \ IGRAPH_FINALLY(igraph_vector_bool_destroy, v); } while (0) #endif #ifndef IGRAPH_VECTOR_LONG_INIT_FINALLY #define IGRAPH_VECTOR_LONG_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_vector_long_init(v, size)); \ IGRAPH_FINALLY(igraph_vector_long_destroy, v); } while (0) #endif /* -------------------------------------------------- */ /* Type-specific vector functions */ /* -------------------------------------------------- */ int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to); int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to); igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, const igraph_vector_t *rhs, igraph_real_t tol); /* These are for internal use only */ int igraph_vector_order(const igraph_vector_t* v, const igraph_vector_t *v2, igraph_vector_t* res, igraph_real_t maxval); int igraph_vector_order1(const igraph_vector_t* v, igraph_vector_t* res, igraph_real_t maxval); int igraph_vector_order1_int(const igraph_vector_t* v, igraph_vector_int_t* res, igraph_real_t maxval); int igraph_vector_order2(igraph_vector_t *v); int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, long int nodes); __END_DECLS #endif igraph/src/foreign-dl-parser.h0000644000176000001440000000571212325527073016047 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton interface for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { NUM = 258, NEWLINE = 259, DL = 260, NEQ = 261, DATA = 262, LABELS = 263, LABELSEMBEDDED = 264, FORMATFULLMATRIX = 265, FORMATEDGELIST1 = 266, FORMATNODELIST1 = 267, DIGIT = 268, LABEL = 269, EOFF = 270 }; #endif /* Tokens. */ #define NUM 258 #define NEWLINE 259 #define DL 260 #define NEQ 261 #define DATA 262 #define LABELS 263 #define LABELSEMBEDDED 264 #define FORMATFULLMATRIX 265 #define FORMATEDGELIST1 266 #define FORMATNODELIST1 267 #define DIGIT 268 #define LABEL 269 #define EOFF 270 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 91 "igraph/src/foreign-dl-parser.y" { long int integer; igraph_real_t real; } /* Line 1529 of yacc.c. */ #line 84 "y.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif igraph/src/igraph_mixing.h0000644000176000001440000000323012325527073015345 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_MIXING_H #define IGRAPH_MIXING_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_vector.h" __BEGIN_DECLS int igraph_assortativity_nominal(const igraph_t *graph, const igraph_vector_t *types, igraph_real_t *res, igraph_bool_t directed); int igraph_assortativity(const igraph_t *graph, const igraph_vector_t *types1, const igraph_vector_t *types2, igraph_real_t *res, igraph_bool_t directed); int igraph_assortativity_degree(const igraph_t *graph, igraph_real_t *res, igraph_bool_t directed); __END_DECLS #endif igraph/src/gml_tree.c0000644000176000001440000001572412325527073014324 0ustar ripleyusers/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_gml_tree.h" #include "igraph_memory.h" #include "igraph_error.h" #include "config.h" #include #include int igraph_gml_tree_init_integer(igraph_gml_tree_t *t, const char *name, int namelen, igraph_integer_t value) { igraph_integer_t *p; IGRAPH_UNUSED(namelen); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); /* names */ VECTOR(t->names)[0] = (void*)name; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_INTEGER; /* children */ p=igraph_Calloc(1, igraph_integer_t); if (!p) { IGRAPH_ERROR("Cannot create integer GML tree node", IGRAPH_ENOMEM); } *p=value; VECTOR(t->children)[0]=p; IGRAPH_FINALLY_CLEAN(3); return 0; } int igraph_gml_tree_init_real(igraph_gml_tree_t *t, const char *name, int namelen, igraph_real_t value) { igraph_real_t *p; IGRAPH_UNUSED(namelen); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); /* names */ VECTOR(t->names)[0] = (void*) name; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_REAL; /* children */ p=igraph_Calloc(1, igraph_real_t); if (!p) { IGRAPH_ERROR("Cannot create real GML tree node", IGRAPH_ENOMEM); } *p=value; VECTOR(t->children)[0]=p; IGRAPH_FINALLY_CLEAN(3); return 0; } int igraph_gml_tree_init_string(igraph_gml_tree_t *t, const char *name, int namelen, const char *value, int valuelen) { IGRAPH_UNUSED(namelen); IGRAPH_UNUSED(valuelen); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); /* names */ VECTOR(t->names)[0] = (void*) name; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_STRING; /* children */ VECTOR(t->children)[0]=(void*)value; IGRAPH_FINALLY_CLEAN(3); return 0; } int igraph_gml_tree_init_tree(igraph_gml_tree_t *t, const char *name, int namelen, igraph_gml_tree_t *value) { IGRAPH_UNUSED(namelen); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); /* names */ VECTOR(t->names)[0] = (void*)name; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_TREE; /* children */ VECTOR(t->children)[0]=value; IGRAPH_FINALLY_CLEAN(3); return 0; } /* merge is destructive, the _second_ tree is destroyed */ int igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2) { long int i, n=igraph_vector_ptr_size(&t2->children); for (i=0; inames, VECTOR(t2->names)[i])); IGRAPH_CHECK(igraph_vector_char_push_back(&t1->types, VECTOR(t2->types)[i])); IGRAPH_CHECK(igraph_vector_ptr_push_back(&t1->children, VECTOR(t2->children)[i])); } igraph_vector_ptr_destroy(&t2->names); igraph_vector_char_destroy(&t2->types); igraph_vector_ptr_destroy(&t2->children); return 0; } void igraph_gml_tree_destroy(igraph_gml_tree_t *t) { long int i, n=igraph_vector_ptr_size(&t->children); for (i=0; itypes)[i]; switch (type) { case IGRAPH_I_GML_TREE_TREE: igraph_gml_tree_destroy(VECTOR(t->children)[i]); igraph_Free(VECTOR(t->names)[i]); break; case IGRAPH_I_GML_TREE_INTEGER: igraph_Free(VECTOR(t->children)[i]); igraph_Free(VECTOR(t->names)[i]); break; case IGRAPH_I_GML_TREE_REAL: igraph_Free(VECTOR(t->children)[i]); igraph_Free(VECTOR(t->names)[i]); break; case IGRAPH_I_GML_TREE_STRING: igraph_Free(VECTOR(t->children)[i]); igraph_Free(VECTOR(t->names)[i]); break; case IGRAPH_I_GML_TREE_DELETED: break; } } igraph_vector_ptr_destroy(&t->names); igraph_vector_char_destroy(&t->types); igraph_vector_ptr_destroy(&t->children); igraph_Free(t); } long int igraph_gml_tree_length(const igraph_gml_tree_t *t) { return igraph_vector_ptr_size(&t->names); } long int igraph_gml_tree_find(const igraph_gml_tree_t *t, const char *name, long int from) { long int size=igraph_vector_ptr_size(&t->names); while ( from < size && (! VECTOR(t->names)[from] || strcmp(VECTOR(t->names)[from], name)) ) { from++; } if (from==size) { from=-1; } return from; } long int igraph_gml_tree_findback(const igraph_gml_tree_t *t, const char *name, long int from) { while ( from >= 0 && (! VECTOR(t->names)[from] || strcmp(VECTOR(t->names)[from], name)) ) { from--; } return from; } int igraph_gml_tree_type(const igraph_gml_tree_t *t, long int pos) { return VECTOR(t->types)[pos]; } const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, long int pos) { return VECTOR(t->names)[pos]; } igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, long int pos) { igraph_integer_t *i=VECTOR(t->children)[pos]; return *i; } igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, long int pos) { igraph_real_t *d=VECTOR(t->children)[pos]; return *d; } const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, long int pos) { const char *s=VECTOR(t->children)[pos]; return s; } igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, long int pos) { igraph_gml_tree_t *tree=VECTOR(t->children)[pos]; return tree; } void igraph_gml_tree_delete(igraph_gml_tree_t *t, long int pos) { if (VECTOR(t->types)[pos] == IGRAPH_I_GML_TREE_TREE) { igraph_gml_tree_destroy(VECTOR(t->children)[pos]); } igraph_Free(VECTOR(t->names)[pos]); igraph_Free(VECTOR(t->children)[pos]); VECTOR(t->children)[pos]=0; VECTOR(t->names)[pos]=0; VECTOR(t->types)[pos]=IGRAPH_I_GML_TREE_DELETED; } igraph/src/glpscl.c0000644000176000001440000003723212325527073014010 0ustar ripleyusers/* glpscl.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" /*********************************************************************** * min_row_aij - determine minimal |a[i,j]| in i-th row * * This routine returns minimal magnitude of (non-zero) constraint * coefficients in i-th row of the constraint matrix. * * If the parameter scaled is zero, the original constraint matrix A is * assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. * * If i-th row of the matrix is empty, the routine returns 1. */ static double min_row_aij(glp_prob *lp, int i, int scaled) { GLPAIJ *aij; double min_aij, temp; xassert(1 <= i && i <= lp->m); min_aij = 1.0; for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) { temp = fabs(aij->val); if (scaled) temp *= (aij->row->rii * aij->col->sjj); if (aij->r_prev == NULL || min_aij > temp) min_aij = temp; } return min_aij; } /*********************************************************************** * max_row_aij - determine maximal |a[i,j]| in i-th row * * This routine returns maximal magnitude of (non-zero) constraint * coefficients in i-th row of the constraint matrix. * * If the parameter scaled is zero, the original constraint matrix A is * assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. * * If i-th row of the matrix is empty, the routine returns 1. */ static double max_row_aij(glp_prob *lp, int i, int scaled) { GLPAIJ *aij; double max_aij, temp; xassert(1 <= i && i <= lp->m); max_aij = 1.0; for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) { temp = fabs(aij->val); if (scaled) temp *= (aij->row->rii * aij->col->sjj); if (aij->r_prev == NULL || max_aij < temp) max_aij = temp; } return max_aij; } /*********************************************************************** * min_col_aij - determine minimal |a[i,j]| in j-th column * * This routine returns minimal magnitude of (non-zero) constraint * coefficients in j-th column of the constraint matrix. * * If the parameter scaled is zero, the original constraint matrix A is * assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. * * If j-th column of the matrix is empty, the routine returns 1. */ static double min_col_aij(glp_prob *lp, int j, int scaled) { GLPAIJ *aij; double min_aij, temp; xassert(1 <= j && j <= lp->n); min_aij = 1.0; for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) { temp = fabs(aij->val); if (scaled) temp *= (aij->row->rii * aij->col->sjj); if (aij->c_prev == NULL || min_aij > temp) min_aij = temp; } return min_aij; } /*********************************************************************** * max_col_aij - determine maximal |a[i,j]| in j-th column * * This routine returns maximal magnitude of (non-zero) constraint * coefficients in j-th column of the constraint matrix. * * If the parameter scaled is zero, the original constraint matrix A is * assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. * * If j-th column of the matrix is empty, the routine returns 1. */ static double max_col_aij(glp_prob *lp, int j, int scaled) { GLPAIJ *aij; double max_aij, temp; xassert(1 <= j && j <= lp->n); max_aij = 1.0; for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) { temp = fabs(aij->val); if (scaled) temp *= (aij->row->rii * aij->col->sjj); if (aij->c_prev == NULL || max_aij < temp) max_aij = temp; } return max_aij; } /*********************************************************************** * min_mat_aij - determine minimal |a[i,j]| in constraint matrix * * This routine returns minimal magnitude of (non-zero) constraint * coefficients in the constraint matrix. * * If the parameter scaled is zero, the original constraint matrix A is * assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. * * If the matrix is empty, the routine returns 1. */ static double min_mat_aij(glp_prob *lp, int scaled) { int i; double min_aij, temp; min_aij = 1.0; for (i = 1; i <= lp->m; i++) { temp = min_row_aij(lp, i, scaled); if (i == 1 || min_aij > temp) min_aij = temp; } return min_aij; } /*********************************************************************** * max_mat_aij - determine maximal |a[i,j]| in constraint matrix * * This routine returns maximal magnitude of (non-zero) constraint * coefficients in the constraint matrix. * * If the parameter scaled is zero, the original constraint matrix A is * assumed. Otherwise, the scaled constraint matrix R*A*S is assumed. * * If the matrix is empty, the routine returns 1. */ static double max_mat_aij(glp_prob *lp, int scaled) { int i; double max_aij, temp; max_aij = 1.0; for (i = 1; i <= lp->m; i++) { temp = max_row_aij(lp, i, scaled); if (i == 1 || max_aij < temp) max_aij = temp; } return max_aij; } /*********************************************************************** * eq_scaling - perform equilibration scaling * * This routine performs equilibration scaling of rows and columns of * the constraint matrix. * * If the parameter flag is zero, the routine scales rows at first and * then columns. Otherwise, the routine scales columns and then rows. * * Rows are scaled as follows: * * n * a'[i,j] = a[i,j] / max |a[i,j]|, i = 1,...,m. * j=1 * * This makes the infinity (maximum) norm of each row of the matrix * equal to 1. * * Columns are scaled as follows: * * n * a'[i,j] = a[i,j] / max |a[i,j]|, j = 1,...,n. * i=1 * * This makes the infinity (maximum) norm of each column of the matrix * equal to 1. */ static void eq_scaling(glp_prob *lp, int flag) { int i, j, pass; double temp; xassert(flag == 0 || flag == 1); for (pass = 0; pass <= 1; pass++) { if (pass == flag) { /* scale rows */ for (i = 1; i <= lp->m; i++) { temp = max_row_aij(lp, i, 1); glp_set_rii(lp, i, glp_get_rii(lp, i) / temp); } } else { /* scale columns */ for (j = 1; j <= lp->n; j++) { temp = max_col_aij(lp, j, 1); glp_set_sjj(lp, j, glp_get_sjj(lp, j) / temp); } } } return; } /*********************************************************************** * gm_scaling - perform geometric mean scaling * * This routine performs geometric mean scaling of rows and columns of * the constraint matrix. * * If the parameter flag is zero, the routine scales rows at first and * then columns. Otherwise, the routine scales columns and then rows. * * Rows are scaled as follows: * * a'[i,j] = a[i,j] / sqrt(alfa[i] * beta[i]), i = 1,...,m, * * where: * n n * alfa[i] = min |a[i,j]|, beta[i] = max |a[i,j]|. * j=1 j=1 * * This allows decreasing the ratio beta[i] / alfa[i] for each row of * the matrix. * * Columns are scaled as follows: * * a'[i,j] = a[i,j] / sqrt(alfa[j] * beta[j]), j = 1,...,n, * * where: * m m * alfa[j] = min |a[i,j]|, beta[j] = max |a[i,j]|. * i=1 i=1 * * This allows decreasing the ratio beta[j] / alfa[j] for each column * of the matrix. */ static void gm_scaling(glp_prob *lp, int flag) { int i, j, pass; double temp; xassert(flag == 0 || flag == 1); for (pass = 0; pass <= 1; pass++) { if (pass == flag) { /* scale rows */ for (i = 1; i <= lp->m; i++) { temp = min_row_aij(lp, i, 1) * max_row_aij(lp, i, 1); glp_set_rii(lp, i, glp_get_rii(lp, i) / sqrt(temp)); } } else { /* scale columns */ for (j = 1; j <= lp->n; j++) { temp = min_col_aij(lp, j, 1) * max_col_aij(lp, j, 1); glp_set_sjj(lp, j, glp_get_sjj(lp, j) / sqrt(temp)); } } } return; } /*********************************************************************** * max_row_ratio - determine worst scaling "quality" for rows * * This routine returns the worst scaling "quality" for rows of the * currently scaled constraint matrix: * * m * ratio = max ratio[i], * i=1 * where: * n n * ratio[i] = max |a[i,j]| / min |a[i,j]|, 1 <= i <= m, * j=1 j=1 * * is the scaling "quality" of i-th row. */ static double max_row_ratio(glp_prob *lp) { int i; double ratio, temp; ratio = 1.0; for (i = 1; i <= lp->m; i++) { temp = max_row_aij(lp, i, 1) / min_row_aij(lp, i, 1); if (i == 1 || ratio < temp) ratio = temp; } return ratio; } /*********************************************************************** * max_col_ratio - determine worst scaling "quality" for columns * * This routine returns the worst scaling "quality" for columns of the * currently scaled constraint matrix: * * n * ratio = max ratio[j], * j=1 * where: * m m * ratio[j] = max |a[i,j]| / min |a[i,j]|, 1 <= j <= n, * i=1 i=1 * * is the scaling "quality" of j-th column. */ static double max_col_ratio(glp_prob *lp) { int j; double ratio, temp; ratio = 1.0; for (j = 1; j <= lp->n; j++) { temp = max_col_aij(lp, j, 1) / min_col_aij(lp, j, 1); if (j == 1 || ratio < temp) ratio = temp; } return ratio; } /*********************************************************************** * gm_iterate - perform iterative geometric mean scaling * * This routine performs iterative geometric mean scaling of rows and * columns of the constraint matrix. * * The parameter it_max specifies the maximal number of iterations. * Recommended value of it_max is 15. * * The parameter tau specifies a minimal improvement of the scaling * "quality" on each iteration, 0 < tau < 1. It means than the scaling * process continues while the following condition is satisfied: * * ratio[k] <= tau * ratio[k-1], * * where ratio = max |a[i,j]| / min |a[i,j]| is the scaling "quality" * to be minimized, k is the iteration number. Recommended value of tau * is 0.90. */ static void gm_iterate(glp_prob *lp, int it_max, double tau) { int k, flag; double ratio = 0.0, r_old; /* if the scaling "quality" for rows is better than for columns, the rows are scaled first; otherwise, the columns are scaled first */ flag = (max_row_ratio(lp) > max_col_ratio(lp)); for (k = 1; k <= it_max; k++) { /* save the scaling "quality" from previous iteration */ r_old = ratio; /* determine the current scaling "quality" */ ratio = max_mat_aij(lp, 1) / min_mat_aij(lp, 1); #if 0 xprintf("k = %d; ratio = %g\n", k, ratio); #endif /* if improvement is not enough, terminate scaling */ if (k > 1 && ratio > tau * r_old) break; /* otherwise, perform another iteration */ gm_scaling(lp, flag); } return; } /*********************************************************************** * NAME * * scale_prob - scale problem data * * SYNOPSIS * * #include "glpscl.h" * void scale_prob(glp_prob *lp, int flags); * * DESCRIPTION * * The routine scale_prob performs automatic scaling of problem data * for the specified problem object. */ static void scale_prob(glp_prob *lp, int flags) { static const char *fmt = "%s: min|aij| = %10.3e max|aij| = %10.3e ratio = %10.3e\n"; double min_aij, max_aij, ratio; xprintf("Scaling...\n"); /* cancel the current scaling effect */ glp_unscale_prob(lp); /* report original scaling "quality" */ min_aij = min_mat_aij(lp, 1); max_aij = max_mat_aij(lp, 1); ratio = max_aij / min_aij; xprintf(fmt, " A", min_aij, max_aij, ratio); /* check if the problem is well scaled */ if (min_aij >= 0.10 && max_aij <= 10.0) { xprintf("Problem data seem to be well scaled\n"); /* skip scaling, if required */ if (flags & GLP_SF_SKIP) goto done; } /* perform iterative geometric mean scaling, if required */ if (flags & GLP_SF_GM) { gm_iterate(lp, 15, 0.90); min_aij = min_mat_aij(lp, 1); max_aij = max_mat_aij(lp, 1); ratio = max_aij / min_aij; xprintf(fmt, "GM", min_aij, max_aij, ratio); } /* perform equilibration scaling, if required */ if (flags & GLP_SF_EQ) { eq_scaling(lp, max_row_ratio(lp) > max_col_ratio(lp)); min_aij = min_mat_aij(lp, 1); max_aij = max_mat_aij(lp, 1); ratio = max_aij / min_aij; xprintf(fmt, "EQ", min_aij, max_aij, ratio); } /* round scale factors to nearest power of two, if required */ if (flags & GLP_SF_2N) { int i, j; for (i = 1; i <= lp->m; i++) glp_set_rii(lp, i, round2n(glp_get_rii(lp, i))); for (j = 1; j <= lp->n; j++) glp_set_sjj(lp, j, round2n(glp_get_sjj(lp, j))); min_aij = min_mat_aij(lp, 1); max_aij = max_mat_aij(lp, 1); ratio = max_aij / min_aij; xprintf(fmt, "2N", min_aij, max_aij, ratio); } done: return; } /*********************************************************************** * NAME * * glp_scale_prob - scale problem data * * SYNOPSIS * * void glp_scale_prob(glp_prob *lp, int flags); * * DESCRIPTION * * The routine glp_scale_prob performs automatic scaling of problem * data for the specified problem object. * * The parameter flags specifies scaling options used by the routine. * Options can be combined with the bitwise OR operator and may be the * following: * * GLP_SF_GM perform geometric mean scaling; * GLP_SF_EQ perform equilibration scaling; * GLP_SF_2N round scale factors to nearest power of two; * GLP_SF_SKIP skip scaling, if the problem is well scaled. * * The parameter flags may be specified as GLP_SF_AUTO, in which case * the routine chooses scaling options automatically. */ void glp_scale_prob(glp_prob *lp, int flags) { if (flags & ~(GLP_SF_GM | GLP_SF_EQ | GLP_SF_2N | GLP_SF_SKIP | GLP_SF_AUTO)) xerror("glp_scale_prob: flags = 0x%02X; invalid scaling option" "s\n", flags); if (flags & GLP_SF_AUTO) flags = (GLP_SF_GM | GLP_SF_EQ | GLP_SF_SKIP); scale_prob(lp, flags); return; } /* eof */ igraph/src/igraph_estack.c0000644000176000001440000000402112325527073015316 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_estack.h" int igraph_estack_init(igraph_estack_t *s, long int setsize, long int stacksize) { IGRAPH_CHECK(igraph_vector_bool_init(&s->isin, setsize)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &s->isin); IGRAPH_CHECK(igraph_stack_long_init(&s->stack, stacksize)); IGRAPH_FINALLY_CLEAN(1); return 0; } void igraph_estack_destroy(igraph_estack_t *s) { igraph_stack_long_destroy(&s->stack); igraph_vector_bool_destroy(&s->isin); } int igraph_estack_push(igraph_estack_t *s, long int elem) { if ( !VECTOR(s->isin)[elem] ) { IGRAPH_CHECK(igraph_stack_long_push(&s->stack, elem)); VECTOR(s->isin)[elem] = 1; } return 0; } long int igraph_estack_pop(igraph_estack_t *s) { long int elem=igraph_stack_long_pop(&s->stack); VECTOR(s->isin)[elem] = 0; return elem; } igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, long int elem) { return VECTOR(s->isin)[elem]; } long int igraph_estack_size(const igraph_estack_t *s) { return igraph_stack_long_size(&s->stack); } #ifndef USING_R int igraph_estack_print(const igraph_estack_t *s) { return igraph_stack_long_print(&s->stack); } #endif igraph/src/motifs.c0000644000176000001440000010113312325527073014015 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_motifs.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_adjlist.h" #include "igraph_interrupt_internal.h" #include "igraph_interface.h" #include "igraph_nongraph.h" #include "igraph_structural.h" #include "igraph_stack.h" #include "config.h" #include extern unsigned int igraph_i_isoclass_3[]; extern unsigned int igraph_i_isoclass_4[]; extern unsigned int igraph_i_isoclass_3u[]; extern unsigned int igraph_i_isoclass_4u[]; extern unsigned int igraph_i_isoclass2_3[]; extern unsigned int igraph_i_isoclass2_4[]; extern unsigned int igraph_i_isoclass2_3u[]; extern unsigned int igraph_i_isoclass2_4u[]; extern unsigned int igraph_i_isoclass_3_idx[]; extern unsigned int igraph_i_isoclass_4_idx[]; extern unsigned int igraph_i_isoclass_3u_idx[]; extern unsigned int igraph_i_isoclass_4u_idx[]; /** * Callback function for igraph_motifs_randesu that counts the motifs by * isomorphism class in a histogram. */ igraph_bool_t igraph_i_motifs_randesu_update_hist(const igraph_t *graph, igraph_vector_t *vids, int isoclass, void* extra) { igraph_vector_t *hist = (igraph_vector_t*)extra; IGRAPH_UNUSED(graph); IGRAPH_UNUSED(vids); VECTOR(*hist)[isoclass]++; return 0; } /** * \function igraph_motifs_randesu * \brief Count the number of motifs in a graph * * * Motifs are small connected subgraphs of a given structure in a * graph. It is argued that the motif profile (ie. the number of * different motifs in the graph) is characteristic for different * types of networks and network function is related to the motifs in * the graph. * * * This function is able to find the different motifs of size three * and four (ie. the number of different subgraphs with three and four * vertices) in the network. * * * In a big network the total number of motifs can be very large, so * it takes a lot of time to find all of them, a sampling method can * be used. This function is capable of doing sampling via the * \c cut_prob argument. This argument gives the probability that * a branch of the motif search tree will not be explored. See * S. Wernicke and F. Rasche: FANMOD: a tool for fast network motif * detection, Bioinformatics 22(9), 1152--1153, 2006 for details. * * * Set the \c cut_prob argument to a zero vector for finding all * motifs. * * * Directed motifs will be counted in directed graphs and undirected * motifs in undirected graphs. * * \param graph The graph to find the motifs in. * \param hist The result of the computation, it gives the number of * motifs found for each isomorphism class. See * \ref igraph_isoclass() for help about isomorphism classes. * Note that this function does \em not count isomorphism * classes that are not connected and will report NaN (more * precisely \c IGRAPH_NAN) for them. * \param size The size of the motifs to search for. Only three and * four are implemented currently. The limitation is not in the * motif finding code, but the graph isomorphism code. * \param cut_prob Vector of probabilities for cutting the search tree * at a given level. The first element is the first level, etc. * Supply all zeros here (of length \c size) to find all motifs * in a graph. * \return Error code. * \sa \ref igraph_motifs_randesu_estimate() for estimating the number * of motifs in a graph, this can help to set the \c cut_prob * parameter; \ref igraph_motifs_randesu_no() to calculate the total * number of motifs of a given size in a graph; * \ref igraph_motifs_randesu_callback() for calling a callback function * for every motif found. * * Time complexity: TODO. * * \example examples/simple/igraph_motifs_randesu.c */ int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, int size, const igraph_vector_t *cut_prob) { int histlen; if (size != 3 && size != 4) { IGRAPH_ERROR("Only 3 and 4 vertex motifs are implemented", IGRAPH_EINVAL); } if (size==3) { histlen = igraph_is_directed(graph) ? 16 : 4; } else { histlen = igraph_is_directed(graph) ? 218 : 11; } IGRAPH_CHECK(igraph_vector_resize(hist, histlen)); igraph_vector_null(hist); IGRAPH_CHECK(igraph_motifs_randesu_callback(graph, size, cut_prob, &igraph_i_motifs_randesu_update_hist, hist)); if (size == 3) { if (igraph_is_directed(graph)) { VECTOR(*hist)[0] = VECTOR(*hist)[1] = VECTOR(*hist)[3] = IGRAPH_NAN; } else { VECTOR(*hist)[0] = VECTOR(*hist)[1] = IGRAPH_NAN; } } else if (size == 4) { if (igraph_is_directed(graph)) { int not_connected[] = { 0, 1, 2, 4, 5, 6, 9, 10, 11, 15, 22, 23, 27, 28, 33, 34, 39, 62, 120 }; int i, n=sizeof(not_connected) / sizeof(int); for (i=0; i * Similarly to \ref igraph_motifs_randesu(), this function is able to find the * different motifs of size three and four (ie. the number of different * subgraphs with three and four vertices) in the network. However, instead of * counting them, the function will call a callback function for each motif * found to allow further tests or post-processing. * * * The \c cut_prob argument also allows sampling the motifs, just like for * \ref igraph_motifs_randesu(). Set the \c cut_prob argument to a zero vector * for finding all motifs. * * \param graph The graph to find the motifs in. * \param size The size of the motifs to search for. Only three and * four are implemented currently. The limitation is not in the * motif finding code, but the graph isomorphism code. * \param cut_prob Vector of probabilities for cutting the search tree * at a given level. The first element is the first level, etc. * Supply all zeros here (of length \c size) to find all motifs * in a graph. * \param callback A pointer to a function of type \ref igraph_motifs_handler_t. * This function will be called whenever a new motif is found. * \param extra Extra argument to pass to the callback function. * \return Error code. * * Time complexity: TODO. * * \example examples/simple/igraph_motifs_randesu.c */ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, void* extra) { long int no_of_nodes=igraph_vcount(graph); igraph_adjlist_t allneis, alloutneis; igraph_vector_int_t *neis; long int father; long int i, j, s; long int motifs=0; igraph_vector_t vids; /* this is G */ igraph_vector_t adjverts; /* this is V_E */ igraph_stack_t stack; /* this is S */ long int *added; char *subg; unsigned int *arr_idx, *arr_code; int code=0; unsigned char mul, idx; igraph_bool_t terminate = 0; if (size != 3 && size != 4) { IGRAPH_ERROR("Only 3 and 4 vertex motifs are implemented", IGRAPH_EINVAL); } if (size==3) { mul=3; if (igraph_is_directed(graph)) { arr_idx=igraph_i_isoclass_3_idx; arr_code=igraph_i_isoclass2_3; } else { arr_idx=igraph_i_isoclass_3u_idx; arr_code=igraph_i_isoclass2_3u; } } else { mul=4; if (igraph_is_directed(graph)) { arr_idx=igraph_i_isoclass_4_idx; arr_code=igraph_i_isoclass2_4; } else { arr_idx=igraph_i_isoclass_4u_idx; arr_code=igraph_i_isoclass2_4u; } } added=igraph_Calloc(no_of_nodes, long int); if (added==0) { IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); subg=igraph_Calloc(no_of_nodes, char); if (subg==0) { IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, subg); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); IGRAPH_CHECK(igraph_adjlist_init(graph, &alloutneis, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &alloutneis); IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0); IGRAPH_CHECK(igraph_stack_init(&stack, 0)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); RNG_BEGIN(); for (father=0; father father) { IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ igraph_stack_clear(&stack); while (level > 1 || !igraph_vector_empty(&adjverts)) { igraph_real_t cp=VECTOR(*cut_prob)[level]; if (level==size-1) { s=igraph_vector_size(&adjverts)/2; for (i=0; i cp) { /* yes, step down */ IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); subg[nei] = (char) level+1; added[nei] += 1; level += 1; IGRAPH_CHECK(igraph_stack_push(&stack, neifather)); IGRAPH_CHECK(igraph_stack_push(&stack, nei)); IGRAPH_CHECK(igraph_stack_push(&stack, level)); neis=igraph_adjlist_get(&allneis, nei); s=igraph_vector_int_size(neis); for (i=0; i father) { IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); } added[nei2] += 1; } } } else { /* no, step back */ long int nei, neifather; while (!igraph_stack_empty(&stack) && level==igraph_stack_top(&stack)-1) { igraph_stack_pop(&stack); nei=(long int) igraph_stack_pop(&stack); neifather=(long int) igraph_stack_pop(&stack); igraph_vector_push_back(&adjverts, nei); igraph_vector_push_back(&adjverts, neifather); } nei=(long int) igraph_vector_pop_back(&vids); subg[nei]=0; added[nei] -= 1; level -= 1; neis=igraph_adjlist_get(&allneis, nei); s=igraph_vector_int_size(neis); for (i=0; i * This function is useful for large graphs for which it is not * feasible to count all the different motifs, because there is very * many of them. * * * The total number of motifs is estimated by taking a sample of * vertices and counts all motifs in which these vertices are * included. (There is also a \c cut_prob parameter which gives the * probabilities to cut a branch of the search tree.) * * * Directed motifs will be counted in directed graphs and undirected * motifs in undirected graphs. * * \param graph The graph object to study. * \param est Pointer to an integer type, the result will be stored * here. * \param size The size of the motif to look for. * \param cut_prob Vector giving the probabilities to cut a branch of * the search tree and omit counting the motifs in that branch. * It contains a probability for each level. Supply \c size * zeros here to count all the motifs in the sample. * \param sample_size The number of vertices to use as the * sample. This parameter is only used if the \c parsample * argument is a null pointer. * \param parsample Either pointer to an initialized vector or a null * pointer. If a vector then the vertex ids in the vector are * used as a sample. If a null pointer then the \c sample_size * argument is used to create a sample of vertices drawn with * uniform probability. * \return Error code. * \sa \ref igraph_motifs_randesu(), \ref igraph_motifs_randesu_no(). * * Time complexity: TODO. */ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, int size, const igraph_vector_t *cut_prob, igraph_integer_t sample_size, const igraph_vector_t *parsample) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t neis; igraph_vector_t vids; /* this is G */ igraph_vector_t adjverts; /* this is V_E */ igraph_stack_t stack; /* this is S */ long int *added; igraph_vector_t *sample; long int sam; long int i; added=igraph_Calloc(no_of_nodes, long int); if (added==0) { IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0); IGRAPH_CHECK(igraph_stack_init(&stack, 0)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (parsample==0) { sample=igraph_Calloc(1, igraph_vector_t); if (sample==0) { IGRAPH_ERROR("Cannot estimate motifs", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, sample); IGRAPH_VECTOR_INIT_FINALLY(sample, 0); IGRAPH_CHECK(igraph_random_sample(sample, 0, no_of_nodes-1, sample_size)); } else { sample=(igraph_vector_t*)parsample; sample_size=(igraph_integer_t) igraph_vector_size(sample); } *est=0; RNG_BEGIN(); for (sam=0; sam father) { IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ igraph_stack_clear(&stack); while (level > 1 || !igraph_vector_empty(&adjverts)) { igraph_real_t cp=VECTOR(*cut_prob)[level]; if (level==size-1) { s=igraph_vector_size(&adjverts)/2; for (i=0; i cp) { /* Yes, step down */ IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); added[nei] += 1; level += 1; IGRAPH_CHECK(igraph_stack_push(&stack, neifather)); IGRAPH_CHECK(igraph_stack_push(&stack, nei)); IGRAPH_CHECK(igraph_stack_push(&stack, level)); IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, IGRAPH_ALL)); s=igraph_vector_size(&neis); for (i=0; i father) { IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); } added[nei2] += 1; } } } else { /* no, step back */ long int nei, neifather; while (!igraph_stack_empty(&stack) && level==igraph_stack_top(&stack)-1) { igraph_stack_pop(&stack); nei=(long int) igraph_stack_pop(&stack); neifather=(long int) igraph_stack_pop(&stack); igraph_vector_push_back(&adjverts, nei); igraph_vector_push_back(&adjverts, neifather); } nei=(long int) igraph_vector_pop_back(&vids); added[nei] -= 1; level -= 1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, IGRAPH_ALL)); s=igraph_vector_size(&neis); for (i=0; i * This function counts the total number of motifs in a graph without * assigning isomorphism classes to them. * * * Directed motifs will be counted in directed graphs and undirected * motifs in undirected graphs. * * \param graph The graph object to study. * \param no Pointer to an integer type, the result will be stored * here. * \param size The size of the motifs to count. * \param cut_prob Vector giving the probabilities that a branch of * the search tree will be cut at a given level. * \return Error code. * \sa \ref igraph_motifs_randesu(), \ref * igraph_motifs_randesu_estimate(). * * Time complexity: TODO. */ int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, int size, const igraph_vector_t *cut_prob) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t neis; igraph_vector_t vids; /* this is G */ igraph_vector_t adjverts; /* this is V_E */ igraph_stack_t stack; /* this is S */ long int *added; long int father; long int i; added=igraph_Calloc(no_of_nodes, long int); if (added==0) { IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0); IGRAPH_CHECK(igraph_stack_init(&stack, 0)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); *no=0; RNG_BEGIN(); for (father=0; father father) { IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ igraph_stack_clear(&stack); while (level > 1 || !igraph_vector_empty(&adjverts)) { igraph_real_t cp=VECTOR(*cut_prob)[level]; if (level==size-1) { s=igraph_vector_size(&adjverts)/2; for (i=0; i cp) { /* Yes, step down */ IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); added[nei] += 1; level += 1; IGRAPH_CHECK(igraph_stack_push(&stack, neifather)); IGRAPH_CHECK(igraph_stack_push(&stack, nei)); IGRAPH_CHECK(igraph_stack_push(&stack, level)); IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, IGRAPH_ALL)); s=igraph_vector_size(&neis); for (i=0; i father) { IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); } added[nei2] += 1; } } } else { /* no, step back */ long int nei, neifather; while (!igraph_stack_empty(&stack) && level==igraph_stack_top(&stack)-1) { igraph_stack_pop(&stack); nei=(long int) igraph_stack_pop(&stack); neifather=(long int) igraph_stack_pop(&stack); igraph_vector_push_back(&adjverts, nei); igraph_vector_push_back(&adjverts, neifather); } nei=(long int) igraph_vector_pop_back(&vids); added[nei] -= 1; level -= 1; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, IGRAPH_ALL)); s=igraph_vector_size(&neis); for (i=0; i * Dyad census means classifying each pair of vertices of a directed * graph into three categories: mutual, there is an edge from \c a to * \c b and also from \c b to \c a; asymmetric, there is an edge * either from \c a to \c b or from \c b to \c a but not the other way * and null, no edges between \c a and \c b. * * * Holland, P.W. and Leinhardt, S. (1970). A Method for Detecting * Structure in Sociometric Data. American Journal of Sociology, * 70, 492-513. * \param graph The input graph, a warning is given if undirected as * the results are undefined for undirected graphs. * \param mut Pointer to an integer, the number of mutual dyads is * stored here. * \param asym Pointer to an integer, the number of asymmetric dyads * is stored here. * \param null Pointer to an integer, the number of null dyads is * stored here. * \return Error code. * * \sa \ref igraph_reciprocity(), \ref igraph_triad_census(). * * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. */ int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, igraph_integer_t *asym, igraph_integer_t *null) { igraph_integer_t nonrec=0, rec=0; igraph_vector_t inneis, outneis; igraph_integer_t vc=igraph_vcount(graph); long int i; if (!igraph_is_directed(graph)) { IGRAPH_WARNING("Dyad census called on undirected graph"); } IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); for (i=0; i VECTOR(outneis)[op]) { nonrec += 1; op++; } else { rec += 1; ip++; op++; } } nonrec += (igraph_vector_size(&inneis)-ip) + (igraph_vector_size(&outneis)-op); } igraph_vector_destroy(&inneis); igraph_vector_destroy(&outneis); IGRAPH_FINALLY_CLEAN(2); *mut = rec / 2; *asym = nonrec / 2; if (vc % 2) { *null = vc * ((vc-1)/2); } else { *null = (vc/2) * (vc-1); } if (*null < vc) { IGRAPH_WARNING("Integer overflow, returning zero"); *null = IGRAPH_NAN; } else { *null = *null-(*mut)-(*asym); } return 0; } /** * \function igraph_triad_census_24 * TODO */ int igraph_triad_census_24(const igraph_t *graph, igraph_integer_t *res2, igraph_integer_t *res4) { long int vc=igraph_vcount(graph); igraph_vector_long_t seen; igraph_vector_int_t *neis, *neis2; long int i, j, k, s, neilen, neilen2, ign; igraph_adjlist_t adjlist; IGRAPH_CHECK(igraph_vector_long_init(&seen, vc)); IGRAPH_FINALLY(igraph_vector_long_destroy, &seen); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); *res2=*res4=0; for (i=0; i0 && nei==VECTOR(*neis)[j-1])) { continue; } neis2=igraph_adjlist_get(&adjlist, nei); neilen2=igraph_vector_int_size(neis2); s=0; for (k=0; k0 && nei2==VECTOR(*neis2)[k-1]) { continue; } if (VECTOR(seen)[nei2] != i+1 && VECTOR(seen)[nei2] != -(i+1)) { s++; } } if (VECTOR(seen)[nei] > 0) { *res2 += vc-s-neilen+ign-1; } else { *res4 += vc-s-neilen+ign-1; } } } igraph_adjlist_destroy(&adjlist); igraph_vector_long_destroy(&seen); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_triad_census * \brief Triad census, as defined by Davis and Leinhardt * * * Calculating the triad census means classifying every triple of * vertices in a directed graph. A triple can be in one of 16 states: * \clist * \cli 003 * A, B, C, the empty graph. * \cli 012 * A->B, C, a graph with a single directed edge. * \cli 102 * A<->B, C, a graph with a mutual connection between two vertices. * \cli 021D * A<-B->C, the binary out-tree. * \cli 021U * A->B<-C, the binary in-tree. * \cli 021C * A->B->C, the directed line. * \cli 111D * A<->B<-C. * \cli 111U * A<->B->C. * \cli 030T * A->B<-C, A->C. * \cli 030C * A<-B<-C, A->C. * \cli 201 * A<->B<->C. * \cli 120D * A<-B->C, A<->C. * \cli 120U * A->B<-C, A<->C. * \cli 120C * A->B->C, A<->C. * \cli 210 * A->B<->C, A<->C. * \cli 300 * A<->B<->C, A<->C, the complete graph. * \endclist * * * See also Davis, J.A. and Leinhardt, S. (1972). The Structure of * Positive Interpersonal Relations in Small Groups. In J. Berger * (Ed.), Sociological Theories in Progress, Volume 2, 218-251. * Boston: Houghton Mifflin. * * * This function calls \ref igraph_motifs_randesu() which is an * implementation of the FANMOD motif finder tool, see \ref * igraph_motifs_randesu() for details. Note that the order of the * triads is not the same for \ref igraph_triad_census() and \ref * igraph_motifs_randesu(). * * \param graph The input graph. A warning is given for undirected * graphs, as the result is undefined for those. * \param res Pointer to an initialized vector, the result is stored * here in the same order as given in the list above. Note that this * order is different than the one used by \ref igraph_motifs_randesu(). * \return Error code. * * \sa \ref igraph_motifs_randesu(), \ref igraph_dyad_census(). * * Time complexity: TODO. */ int igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { igraph_vector_t cut_prob; igraph_integer_t m2, m4; igraph_vector_t tmp; igraph_integer_t vc=igraph_vcount(graph); if (!igraph_is_directed(graph)) { IGRAPH_WARNING("Triad census called on an undirected graph"); } IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_VECTOR_INIT_FINALLY(&cut_prob, 3); /* all zeros */ IGRAPH_CHECK(igraph_vector_resize(res, 16)); IGRAPH_CHECK(igraph_motifs_randesu(graph, &tmp, 3, &cut_prob)); IGRAPH_CHECK(igraph_triad_census_24(graph, &m2, &m4)); VECTOR(tmp)[0]=0; VECTOR(tmp)[1]=m2; VECTOR(tmp)[3]=m4; VECTOR(tmp)[0]=vc*(vc-1)*(vc-2)/6 - igraph_vector_sum(&tmp); /* Reorder */ VECTOR(*res)[0] = VECTOR(tmp)[0]; VECTOR(*res)[1] = VECTOR(tmp)[1]; VECTOR(*res)[2] = VECTOR(tmp)[3]; VECTOR(*res)[3] = VECTOR(tmp)[6]; VECTOR(*res)[4] = VECTOR(tmp)[2]; VECTOR(*res)[5] = VECTOR(tmp)[4]; VECTOR(*res)[6] = VECTOR(tmp)[5]; VECTOR(*res)[7] = VECTOR(tmp)[9]; VECTOR(*res)[8] = VECTOR(tmp)[7]; VECTOR(*res)[9] = VECTOR(tmp)[11]; VECTOR(*res)[10] = VECTOR(tmp)[10]; VECTOR(*res)[11] = VECTOR(tmp)[8]; VECTOR(*res)[12] = VECTOR(tmp)[13]; VECTOR(*res)[13] = VECTOR(tmp)[12]; VECTOR(*res)[14] = VECTOR(tmp)[14]; VECTOR(*res)[15] = VECTOR(tmp)[15]; igraph_vector_destroy(&cut_prob); igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(2); return 0; } igraph/src/spmatrix.c0000644000176000001440000007235012325527074014374 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_spmatrix.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include /** * \section igraph_spmatrix_constructor_and_destructor Sparse matrix constructors * and destructors. */ /** * \ingroup matrix * \function igraph_spmatrix_init * \brief Initializes a sparse matrix. * * * Every sparse matrix needs to be initialized before using it, this is done * by calling this function. A matrix has to be destroyed if it is not * needed any more, see \ref igraph_spmatrix_destroy(). * \param m Pointer to a not yet initialized sparse matrix object to be * initialized. * \param nrow The number of rows in the matrix. * \param ncol The number of columns in the matrix. * \return Error code. * * Time complexity: operating system dependent. */ int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol) { assert(m != NULL); IGRAPH_VECTOR_INIT_FINALLY(&m->ridx, 0); IGRAPH_VECTOR_INIT_FINALLY(&m->cidx, ncol+1); IGRAPH_VECTOR_INIT_FINALLY(&m->data, 0); IGRAPH_FINALLY_CLEAN(3); m->nrow=nrow; m->ncol=ncol; return 0; } /** * \ingroup matrix * \function igraph_spmatrix_destroy * \brief Destroys a sparse matrix object. * * * This function frees all the memory allocated for a sparse matrix * object. The destroyed object needs to be reinitialized before using * it again. * \param m The matrix to destroy. * * Time complexity: operating system dependent. */ void igraph_spmatrix_destroy(igraph_spmatrix_t *m) { assert(m != NULL); igraph_vector_destroy(&m->ridx); igraph_vector_destroy(&m->cidx); igraph_vector_destroy(&m->data); } /** * \ingroup matrix * \function igraph_spmatrix_copy * \brief Copies a sparse matrix. * * * Creates a sparse matrix object by copying another one. * \param to Pointer to an uninitialized sparse matrix object. * \param from The initialized sparse matrix object to copy. * \return Error code, \c IGRAPH_ENOMEM if there * isn't enough memory to allocate the new sparse matrix. * * Time complexity: O(n), the number * of elements in the matrix. */ int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from) { assert(from != NULL); assert(to != NULL); to->nrow = from->nrow; to->ncol = from->ncol; IGRAPH_CHECK(igraph_vector_copy(&to->ridx, &from->ridx)); IGRAPH_CHECK(igraph_vector_copy(&to->cidx, &from->cidx)); IGRAPH_CHECK(igraph_vector_copy(&to->data, &from->data)); return 0; } /** * \section igraph_spmatrix_accessing_elements Accessing elements of a sparse matrix */ /** * \ingroup matrix * \function igraph_spmatrix_e * \brief Accessing an element of a sparse matrix. * * Note that there are no range checks right now. * \param m The matrix object. * \param row The index of the row, starting with zero. * \param col The index of the column, starting with zero. * * Time complexity: O(log n), where n is the number of nonzero elements in * the requested column. */ igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col) { long int start, end; assert(m != NULL); start = (long) VECTOR(m->cidx)[col]; end = (long) VECTOR(m->cidx)[col+1]-1; if (enddata[start] and * m->data[end], inclusive, ordered by row index */ while (start < end-1) { long int mid=(start+end)/2; if (VECTOR(m->ridx)[mid] > row) { end=mid; } else if (VECTOR(m->ridx)[mid] < row) { start=mid; } else { start=mid; break; } } if (VECTOR(m->ridx)[start] == row) return VECTOR(m->data)[start]; if (VECTOR(m->ridx)[start] != row && VECTOR(m->ridx)[end] == row) return VECTOR(m->data)[end]; return 0; } /** * \ingroup matrix * \function igraph_spmatrix_set * \brief Setting an element of a sparse matrix. * * Note that there are no range checks right now. * \param m The matrix object. * \param row The index of the row, starting with zero. * \param col The index of the column, starting with zero. * \param value The new value. * * Time complexity: O(log n), where n is the number of nonzero elements in * the requested column. */ int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, igraph_real_t value) { long int start, end; assert(m != NULL); start = (long) VECTOR(m->cidx)[col]; end = (long) VECTOR(m->cidx)[col+1]-1; if (endridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]++; return 0; } /* Elements residing in column col are between m->data[start] and * m->data[end], inclusive, ordered by row index */ while (start < end-1) { long int mid=(start+end)/2; if (VECTOR(m->ridx)[mid] > row) { end=mid; } else if (VECTOR(m->ridx)[mid] < row) { start=mid; } else { start=mid; break; } } if (VECTOR(m->ridx)[start] == row) { /* Overwriting a value - or deleting it if it has been overwritten by zero */ if (value == 0) { igraph_vector_remove(&m->ridx, start); igraph_vector_remove(&m->data, start); for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]--; } else { VECTOR(m->data)[start] = value; } return 0; } else if (VECTOR(m->ridx)[end] == row) { /* Overwriting a value - or deleting it if it has been overwritten by zero */ if (value == 0) { igraph_vector_remove(&m->ridx, end); igraph_vector_remove(&m->data, end); for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]--; } else { VECTOR(m->data)[end] = value; } return 0; } /* New element has to be inserted, but only if not a zero is * being written into the matrix */ if (value != 0.0) { if (VECTOR(m->ridx)[end] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end+1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, end+1, value)); } else if (VECTOR(m->ridx)[start] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start+1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start+1, value)); } else { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); } for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]++; } return 0; } /** * \ingroup matrix * \function igraph_spmatrix_add_e * \brief Adding a real value to an element of a sparse matrix. * * Note that there are no range checks right now. This is implemented to avoid * double lookup of a given element in the matrix by using \ref igraph_spmatrix_e() * and \ref igraph_spmatrix_set() consecutively. * * \param m The matrix object. * \param row The index of the row, starting with zero. * \param col The index of the column, starting with zero. * \param value The value to add. * * Time complexity: O(log n), where n is the number of nonzero elements in * the requested column. */ int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, igraph_real_t value) { long int start, end; assert(m != NULL); start = (long) VECTOR(m->cidx)[col]; end = (long) VECTOR(m->cidx)[col+1]-1; if (endridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]++; return 0; } /* Elements residing in column col are between m->data[start] and * m->data[end], inclusive, ordered by row index */ while (start < end-1) { long int mid=(start+end)/2; if (VECTOR(m->ridx)[mid] > row) { end=mid; } else if (VECTOR(m->ridx)[mid] < row) { start=mid; } else { start=mid; break; } } if (VECTOR(m->ridx)[start] == row) { /* Overwriting a value */ if (VECTOR(m->data)[start] == -1) { igraph_vector_remove(&m->ridx, start); igraph_vector_remove(&m->data, start); for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]--; } else { VECTOR(m->data)[start] += value; } return 0; } else if (VECTOR(m->ridx)[end] == row) { /* Overwriting a value */ if (VECTOR(m->data)[end] == -1) { igraph_vector_remove(&m->ridx, end); igraph_vector_remove(&m->data, end); for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]--; } else { VECTOR(m->data)[end] += value; } return 0; } /* New element has to be inserted, but only if not a zero is * being added to a zero element of the matrix */ if (value != 0.0) { if (VECTOR(m->ridx)[end] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end+1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, end+1, value)); } else if (VECTOR(m->ridx)[start] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start+1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start+1, value)); } else { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); } for (start=col+1; start < m->ncol+1; start++) VECTOR(m->cidx)[start]++; } return 0; } /** * \function igraph_spmatrix_add_col_values * \brief Adds the values of a column to another column. * * \param to The index of the column to be added to * \param from The index of the column to be added * \return Error code. */ int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from) { long int i; /* TODO: I think this implementation could be speeded up if I don't use * igraph_spmatrix_add_e directly -- but maybe it's not worth the fuss */ for (i=(long int) VECTOR(m->cidx)[from]; icidx)[from+1]; i++) { IGRAPH_CHECK(igraph_spmatrix_add_e(m, (long int) VECTOR(m->ridx)[i], to, VECTOR(m->data)[i])); } return 0; } /** * \ingroup matrix * \function igraph_spmatrix_resize * \brief Resizes a sparse matrix. * * * This function resizes a sparse matrix by adding more elements to it. * The matrix retains its data even after resizing it, except for the data * which lies outside the new boundaries (if the new size is smaller). * \param m Pointer to an already initialized sparse matrix object. * \param nrow The number of rows in the resized matrix. * \param ncol The number of columns in the resized matrix. * \return Error code. * * Time complexity: O(n). * n is the number of elements in the old matrix. */ int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol) { long int i, j, ci, ei, mincol; assert(m != NULL); /* Iterating through the matrix data and deleting unnecessary data. */ /* At the same time, we create the new indices as well */ if (nrow < m->nrow) { ei = j = 0; mincol = (m->ncol < ncol) ? m->ncol : ncol; for (ci=0; ci < mincol; ci++) { for (; eicidx)[ci+1]; ei++) { if (VECTOR(m->ridx)[ei] < nrow) { VECTOR(m->ridx)[j]=VECTOR(m->ridx)[ei]; VECTOR(m->data)[j]=VECTOR(m->data)[ei]; j++; } } VECTOR(m->cidx)[ci]=j; } /* Contract the row index and the data vector */ IGRAPH_CHECK(igraph_vector_resize(&m->ridx, j)); IGRAPH_CHECK(igraph_vector_resize(&m->cidx, j)); } /* Updating cidx */ IGRAPH_CHECK(igraph_vector_resize(&m->cidx, ncol+1)); for (i=m->ncol+1; icidx)[i] = VECTOR(m->cidx)[m->ncol]; m->nrow=nrow; m->ncol=ncol; return 0; } /** * \ingroup matrix * \function igraph_spmatrix_count_nonzero * \brief The number of non-zero elements in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The size of the matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m) { assert(m != NULL); return igraph_vector_size(&m->data); } /** * \ingroup matrix * \function igraph_spmatrix_size * \brief The number of elements in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The size of the matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_size(const igraph_spmatrix_t *m) { assert(m != NULL); return (m->nrow) * (m->ncol); } /** * \ingroup matrix * \function igraph_spmatrix_nrow * \brief The number of rows in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The number of rows in the matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m) { assert(m != NULL); return m->nrow; } /** * \ingroup matrix * \function igraph_spmatrix_ncol * \brief The number of columns in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The number of columns in the sparse matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m) { assert(m != NULL); return m->ncol; } /** * \ingroup matrix * \brief Copies a sparse matrix to a regular C array. * * * The matrix is copied columnwise, as this is the format most * programs and languages use. * The C array should be of sufficient size, there are (of course) no * range checks done. * \param m Pointer to an initialized sparse matrix object. * \param to Pointer to a C array, the place to copy the data to. * \return Error code. * * Time complexity: O(n), * n is the number of * elements in the matrix. */ int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to) { long int c, dest_idx, idx; memset(to, 0, sizeof(igraph_real_t) * (size_t) igraph_spmatrix_size(m)); for (c=0, dest_idx=0; c < m->ncol; c++, dest_idx+=m->nrow) { for (idx=(long int) VECTOR(m->cidx)[c]; idxcidx)[c+1]; idx++) { to[dest_idx+(long)VECTOR(m->ridx)[idx]]=VECTOR(m->data)[idx]; } } return 0; } /** * \ingroup matrix * \brief Sets all element in a sparse matrix to zero. * * \param m Pointer to an initialized matrix object. * \return Error code, always returns with success. * * Time complexity: O(n), * n is the number of columns in the matrix */ int igraph_spmatrix_null(igraph_spmatrix_t *m) { assert(m != NULL); igraph_vector_clear(&m->data); igraph_vector_clear(&m->ridx); igraph_vector_null(&m->cidx); return 0; } /** * \ingroup matrix * \function igraph_spmatrix_add_cols * \brief Adds columns to a sparse matrix. * \param m The sparse matrix object. * \param n The number of columns to add. * \return Error code. * * Time complexity: O(1). */ int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n) { igraph_spmatrix_resize(m, m->nrow, m->ncol+n); return 0; } /** * \ingroup matrix * \function igraph_spmatrix_add_rows * \brief Adds rows to a sparse matrix. * \param m The sparse matrix object. * \param n The number of rows to add. * \return Error code. * * Time complexity: O(1). */ int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n) { igraph_spmatrix_resize(m, m->nrow+n, m->ncol); return 0; } /** * \function igraph_spmatrix_clear_row * \brief Clears a row in the matrix (sets all of its elements to zero) * \param m The matrix. * \param row The index of the row to be cleared. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row) { long int ci, ei, i, j, nremove=0, nremove_old=0; igraph_vector_t permvec; assert(m != NULL); IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data)); for (ci=0, i=0, j=1; ci < m->ncol; ci++) { for (ei=(long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci+1]; ei++) { if (VECTOR(m->ridx)[ei] == row) { /* this element will be deleted, so all elements in cidx from the * column index of this element will have to be decreased by one */ nremove++; } else { /* this element will be kept */ VECTOR(permvec)[i] = j; j++; } i++; } if (ci > 0) { VECTOR(m->cidx)[ci] -= nremove_old; } nremove_old = nremove; } VECTOR(m->cidx)[m->ncol] -= nremove; igraph_vector_permdelete(&m->ridx, &permvec, nremove); igraph_vector_permdelete(&m->data, &permvec, nremove); igraph_vector_destroy(&permvec); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row) { long int ei, n; assert(m != NULL); n = igraph_vector_size(&m->data); for (ei=0; eiridx)[ei] == row) VECTOR(m->data)[ei]=0.0; } return 0; } int igraph_i_spmatrix_cleanup(igraph_spmatrix_t *m) { long int ci, ei, i, j, nremove=0, nremove_old=0; igraph_vector_t permvec; assert(m != NULL); IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data)); for (ci=0, i=0, j=1; ci < m->ncol; ci++) { for (ei=(long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci+1]; ei++) { if (VECTOR(m->data)[ei] == 0.0) { /* this element will be deleted, so all elements in cidx from the * column index of this element will have to be decreased by one */ nremove++; } else { /* this element will be kept */ VECTOR(permvec)[i] = j; j++; } i++; } if (ci > 0) { VECTOR(m->cidx)[ci] -= nremove_old; } nremove_old = nremove; } VECTOR(m->cidx)[m->ncol] -= nremove; igraph_vector_permdelete(&m->ridx, &permvec, nremove); igraph_vector_permdelete(&m->data, &permvec, nremove); igraph_vector_destroy(&permvec); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_spmatrix_clear_col * \brief Clears a column in the matrix (sets all of its elements to zero) * \param m The matrix. * \param col The index of the column to be cleared. * \return Error code. The current implementation always succeeds. * * Time complexity: TODO */ int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col) { long int i, n; assert(m != NULL); n = (long)VECTOR(m->cidx)[col+1] - (long)VECTOR(m->cidx)[col]; if (n == 0) return 0; igraph_vector_remove_section(&m->ridx, (long int) VECTOR(m->cidx)[col], (long int) VECTOR(m->cidx)[col+1]); igraph_vector_remove_section(&m->data, (long int) VECTOR(m->cidx)[col], (long int) VECTOR(m->cidx)[col+1]); for (i=col+1; i <= m->ncol; i++) { VECTOR(m->cidx)[i] -= n; } return 0; } /** * \function igraph_spmatrix_scale * \brief Multiplies each element of the sparse matrix by a constant. * \param m The matrix. * \param by The constant. * * Time complexity: O(n), the number of elements in the matrix. */ void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by) { assert(m != NULL); igraph_vector_scale(&m->data, by); } /** * \function igraph_spmatrix_colsums * \brief Calculates the column sums of the matrix. * \param m The matrix. * \param res An initialized \c igraph_vector_t, the result will be stored here. * The vector will be resized as needed. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res) { long int i, c; assert(m != NULL); IGRAPH_CHECK(igraph_vector_resize(res, m->ncol)); igraph_vector_null(res); for (c=0; c < m->ncol; c++) { for (i=(long int) VECTOR(m->cidx)[c]; icidx)[c+1]; i++) { VECTOR(*res)[c] += VECTOR(m->data)[i]; } } return 0; } /** * \function igraph_spmatrix_rowsums * \brief Calculates the row sums of the matrix. * \param m The matrix. * \param res An initialized \c igraph_vector_t, the result will be stored here. * The vector will be resized as needed. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res) { long int i, n; assert(m != NULL); IGRAPH_CHECK(igraph_vector_resize(res, m->nrow)); n = igraph_vector_size(&m->data); igraph_vector_null(res); for (i=0; i < n; i++) { VECTOR(*res)[(long int)VECTOR(m->ridx)[i]] += VECTOR(m->data)[i]; } return 0; } /** * \function igraph_spmatrix_max_nonzero * \brief Returns the maximum nonzero element of a matrix. * If the matrix is empty, zero is returned. * * \param m the matrix object. * \param ridx the row index of the maximum element if not \c NULL. * \param cidx the column index of the maximum element if not \c NULL. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, igraph_real_t *ridx, igraph_real_t *cidx) { igraph_real_t res; long int i, n, maxidx; assert(m != NULL); n=igraph_vector_size(&m->data); if (n == 0) return 0.0; maxidx = -1; for (i=0; idata)[i] != 0.0 && (maxidx == -1 || VECTOR(m->data)[i] >= VECTOR(m->data)[maxidx])) maxidx = i; if (maxidx == -1) return 0.0; res=VECTOR(m->data)[maxidx]; if (ridx != 0) *ridx = VECTOR(m->ridx)[maxidx]; if (cidx != 0) { igraph_vector_binsearch(&m->cidx, maxidx, &i); while (VECTOR(m->cidx)[i+1] == VECTOR(m->cidx)[i]) i++; *cidx = (igraph_real_t)i; } return res; } /** * \function igraph_spmatrix_max * \brief Returns the maximum element of a matrix. * If the matrix is empty, zero is returned. * * \param m the matrix object. * \param ridx the row index of the maximum element if not \c NULL. * \param cidx the column index of the maximum element if not \c NULL. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, igraph_real_t *ridx, igraph_real_t *cidx) { igraph_real_t res; long int i, j, k, maxidx; assert(m != NULL); i=igraph_vector_size(&m->data); if (i == 0) return 0.0; maxidx=(long)igraph_vector_which_max(&m->data); res=VECTOR(m->data)[maxidx]; if (res>=0.0 || i == m->nrow * m->ncol) { if (ridx != 0) *ridx = VECTOR(m->ridx)[maxidx]; if (cidx != 0) { igraph_vector_binsearch(&m->cidx, maxidx, &i); i--; while (i < m->ncol-1 && VECTOR(m->cidx)[i+1] == VECTOR(m->cidx)[i]) i++; *cidx = (igraph_real_t)i; } return res; } /* the maximal nonzero element is negative and there is at least a * single zero */ res=0.0; if (cidx != 0 || ridx != 0) { for (i=0; i < m->ncol; i++) { if (VECTOR(m->cidx)[i+1] - VECTOR(m->cidx)[i] < m->nrow) { if (cidx != 0) *cidx = i; if (ridx != 0) { for (j=(long int) VECTOR(m->cidx)[i], k=0; j < VECTOR(m->cidx)[i+1]; j++, k++) { if (VECTOR(m->ridx)[j] != k) { *ridx = k; break; } } } break; } } } return res; } int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m, igraph_vector_t *res, long int col) { long int i, n; assert(m != NULL); n = (long int) (VECTOR(m->cidx)[col+1]-VECTOR(m->cidx)[col]); IGRAPH_CHECK(igraph_vector_resize(res, n)); for (i=(long int) VECTOR(m->cidx)[col], n=0; icidx)[col+1]; i++, n++) if (VECTOR(m->data)[i] != 0.0) VECTOR(*res)[n] = VECTOR(m->ridx)[i]; return 0; } /** * \section igraph_spmatrix_iterating Iterating over the non-zero elements of a sparse matrix * * The \type igraph_spmatrix_iter_t type represents an iterator that can * be used to step over the non-zero elements of a sparse matrix in columnwise * order efficiently. In general, you shouldn't modify the elements of the matrix * while iterating over it; doing so will probably invalidate the iterator, but * there are no checks to prevent you from doing this. * * To access the row index of the current element of the iterator, use its * \c ri field. Similarly, the \c ci field stores the column index of the current * element and the \c value field stores the value of the element. */ /** * \function igraph_spmatrix_iter_create * \brief Creates a sparse matrix iterator corresponding to the given matrix. * * \param mit pointer to the matrix iterator being initialized * \param m pointer to the matrix we will be iterating over * \return Error code. The current implementation is always successful. * * Time complexity: O(1). */ int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m) { mit->m = m; IGRAPH_CHECK(igraph_spmatrix_iter_reset(mit)); return 0; } /** * \function igraph_spmatrix_iter_reset * \brief Resets a sparse matrix iterator. * * * After resetting, the iterator will point to the first nonzero element (if any). * * \param mit pointer to the matrix iterator being reset * \return Error code. The current implementation is always successful. * * Time complexity: O(1). */ int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit) { assert(mit->m); if (igraph_spmatrix_count_nonzero(mit->m) == 0) { mit->pos = mit->ri = mit->ci = -1L; mit->value = -1; return 0; } mit->ci = 0; mit->pos = -1; IGRAPH_CHECK(igraph_spmatrix_iter_next(mit)); return 0; } /** * \function igraph_spmatrix_iter_next * \brief Moves a sparse matrix iterator to the next nonzero element. * * * You should call this function only if \ref igraph_spmatrix_iter_end() * returns FALSE (0). * * \param mit pointer to the matrix iterator being moved * \return Error code. The current implementation is always successful. * * Time complexity: O(1). */ int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit) { mit->pos++; if (igraph_spmatrix_iter_end(mit)) return 0; mit->ri = (long int)VECTOR(mit->m->ridx)[mit->pos]; mit->value = VECTOR(mit->m->data)[mit->pos]; while (VECTOR(mit->m->cidx)[mit->ci+1] <= mit->pos) { mit->ci++; } return 0; } /** * \function igraph_spmatrix_iter_end * \brief Checks whether there are more elements in the iterator. * * * You should call this function before calling \ref igraph_spmatrix_iter_next() * to make sure you have more elements in the iterator. * * \param mit pointer to the matrix iterator being checked * \return TRUE (1) if there are more elements in the iterator, * FALSE (0) otherwise. * * Time complexity: O(1). */ igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit) { return mit->pos >= igraph_spmatrix_count_nonzero(mit->m); } /** * \function igraph_spmatrix_iter_destroy * \brief Frees the memory used by the iterator. * * * The current implementation does not allocate any memory upon * creation, so this function does nothing. However, since there is * no guarantee that future implementations will not allocate any * memory in \ref igraph_spmatrix_iter_create(), you are still * required to call this function whenever you are done with the * iterator. * * \param mit pointer to the matrix iterator being destroyed * * Time complexity: O(1). */ void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit) { IGRAPH_UNUSED(mit); /* Nothing to do at the moment */ } #ifndef USING_R /** * \function igraph_spmatrix_print * \brief Prints a sparse matrix. * * Prints a sparse matrix to the standard output. Only the non-zero entries * are printed. * * \return Error code. * * Time complexity: O(n), the number of non-zero elements. */ int igraph_spmatrix_print(const igraph_spmatrix_t* matrix) { return igraph_spmatrix_fprint(matrix, stdout); } #endif /** * \function igraph_spmatrix_fprint * \brief Prints a sparse matrix to the given file. * * Prints a sparse matrix to the given file. Only the non-zero entries * are printed. * * \return Error code. * * Time complexity: O(n), the number of non-zero elements. */ int igraph_spmatrix_fprint(const igraph_spmatrix_t* matrix, FILE *file) { igraph_spmatrix_iter_t mit; IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, matrix)); IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); while (!igraph_spmatrix_iter_end(&mit)) { fprintf(file, "[%ld, %ld] = %.4f\n", (long int)mit.ri, (long int)mit.ci, mit.value); igraph_spmatrix_iter_next(&mit); } igraph_spmatrix_iter_destroy(&mit); IGRAPH_FINALLY_CLEAN(1); return 0; } igraph/src/igraph_games.h0000644000176000001440000001627012325527073015156 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_GAMES_H #define IGRAPH_GAMES_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_vector.h" #include "igraph_datatype.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Constructors, games (=stochastic) */ /* -------------------------------------------------- */ int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, igraph_barabasi_algorithm_t algo, const igraph_t *start_from); int igraph_nonlinear_barabasi_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t zeroappeal, igraph_bool_t directed); int igraph_erdos_renyi_game(igraph_t *graph, igraph_erdos_renyi_t type, igraph_integer_t n, igraph_real_t p, igraph_bool_t directed, igraph_bool_t loops); int igraph_erdos_renyi_game_gnp(igraph_t *graph, igraph_integer_t n, igraph_real_t p, igraph_bool_t directed, igraph_bool_t loops); int igraph_erdos_renyi_game_gnm(igraph_t *graph, igraph_integer_t n, igraph_real_t m, igraph_bool_t directed, igraph_bool_t loops); int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg, const igraph_vector_t *in_deg, igraph_degseq_t method); int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation); int igraph_barabasi_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, igraph_integer_t aging_bin, igraph_real_t zero_deg_appeal, igraph_real_t zero_age_appeal, igraph_real_t deg_coef, igraph_real_t age_coef, igraph_bool_t directed); int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t window, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t zero_appeal, igraph_bool_t directed); int igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, igraph_integer_t aging_bin, igraph_integer_t window, igraph_real_t zero_appeal, igraph_bool_t directed); int igraph_callaway_traits_game (igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, igraph_integer_t edges_per_step, igraph_vector_t *type_dist, igraph_matrix_t *pref_matrix, igraph_bool_t directed); int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, igraph_integer_t k, igraph_vector_t *type_dist, igraph_matrix_t *pref_matrix, igraph_bool_t directed); int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, igraph_real_t radius, igraph_bool_t torus, igraph_vector_t *x, igraph_vector_t *y); int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, const igraph_vector_t *type_dist, igraph_bool_t fixed_sizes, const igraph_matrix_t *pref_matrix, igraph_vector_t *node_type_vec, igraph_bool_t directed, igraph_bool_t loops); int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, igraph_matrix_t *type_dist_matrix, igraph_matrix_t *pref_matrix, igraph_vector_t *node_type_in_vec, igraph_vector_t *node_type_out_vec, igraph_bool_t loops); int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_bool_t multiple); int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, igraph_integer_t size, igraph_integer_t nei, igraph_real_t p, igraph_bool_t loops, igraph_bool_t multiple); int igraph_lastcit_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t edges_per_node, igraph_integer_t agebins, const igraph_vector_t *preference, igraph_bool_t directed); int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, const igraph_vector_t *types, const igraph_vector_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed); int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, const igraph_vector_t *types, const igraph_matrix_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed); int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, igraph_real_t fw_prob, igraph_real_t bw_factor, igraph_integer_t ambs, igraph_bool_t directed); int igraph_simple_interconnected_islands_game( igraph_t *graph, igraph_integer_t islands_n, igraph_integer_t islands_size, igraph_real_t islands_pin, igraph_integer_t n_inter); int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, igraph_vector_t* fitness_out, igraph_vector_t* fitness_in, igraph_bool_t loops, igraph_bool_t multiple); int igraph_static_power_law_game(igraph_t *graph, igraph_integer_t no_of_nodes, igraph_integer_t no_of_edges, igraph_real_t exponent_out, igraph_real_t exponent_in, igraph_bool_t loops, igraph_bool_t multiple, igraph_bool_t finite_size_correction); int igraph_k_regular_game(igraph_t *graph, igraph_integer_t no_of_nodes, igraph_integer_t k, igraph_bool_t directed, igraph_bool_t multiple); int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, const igraph_matrix_t *pref_matrix, const igraph_vector_int_t *block_sizes, igraph_bool_t directed, igraph_bool_t loops); __END_DECLS #endif igraph/src/igraph_glpk_support.h0000644000176000001440000000267412325527073016616 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_GLPK_SUPPORT_H #define IGRAPH_GLPK_SUPPORT_H #include "config.h" /* Note: only files calling the GLPK routines directly need to include this header. */ #ifdef HAVE_GLPK #include int igraph_i_glpk_check(int retval, const char* message); void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info); #define IGRAPH_GLPK_CHECK(func, message) do {\ int igraph_i_ret = igraph_i_glpk_check(func, message); \ if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ return igraph_i_ret; \ } } while (0) #endif #endif igraph/src/glprng01.c0000644000176000001440000001425112325527073014152 0ustar ripleyusers/* glprng01.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * This code is a modified version of the module GB_FLIP, a portable * pseudo-random number generator. The original version of GB_FLIP is * a part of The Stanford GraphBase developed by Donald E. Knuth (see * http://www-cs-staff.stanford.edu/~knuth/sgb.html). * * Note that all changes concern only external names, so this modified * version produces exactly the same results as the original version. * * Changes were made by Andrew Makhorin . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpenv.h" #include "glprng.h" #if 0 int A[56] = { -1 }; #else #define A (rand->A) #endif /* pseudo-random values */ #if 0 int *fptr = A; #else #define fptr (rand->fptr) #endif /* the next A value to be exported */ #define mod_diff(x, y) (((x) - (y)) & 0x7FFFFFFF) /* difference modulo 2^31 */ static int flip_cycle(RNG *rand) { /* this is an auxiliary routine to do 55 more steps of the basic recurrence, at high speed, and to reset fptr */ int *ii, *jj; for (ii = &A[1], jj = &A[32]; jj <= &A[55]; ii++, jj++) *ii = mod_diff(*ii, *jj); for (jj = &A[1]; ii <= &A[55]; ii++, jj++) *ii = mod_diff(*ii, *jj); fptr = &A[54]; return A[55]; } /*********************************************************************** * NAME * * rng_create_rand - create pseudo-random number generator * * SYNOPSIS * * #include "glprng.h" * RNG *rng_create_rand(void); * * DESCRIPTION * * The routine rng_create_rand creates and initializes a pseudo-random * number generator. * * RETURNS * * The routine returns a pointer to the generator created. */ RNG *rng_create_rand(void) { RNG *rand; int i; rand = xmalloc(sizeof(RNG)); A[0] = -1; for (i = 1; i <= 55; i++) A[i] = 0; fptr = A; rng_init_rand(rand, 1); return rand; } /*********************************************************************** * NAME * * rng_init_rand - initialize pseudo-random number generator * * SYNOPSIS * * #include "glprng.h" * void rng_init_rand(RNG *rand, int seed); * * DESCRIPTION * * The routine rng_init_rand initializes the pseudo-random number * generator. The parameter seed may be any integer number. Note that * on creating the generator this routine is called with the parameter * seed equal to 1. */ void rng_init_rand(RNG *rand, int seed) { int i; int prev = seed, next = 1; seed = prev = mod_diff(prev, 0); A[55] = prev; for (i = 21; i; i = (i + 21) % 55) { A[i] = next; next = mod_diff(prev, next); if (seed & 1) seed = 0x40000000 + (seed >> 1); else seed >>= 1; next = mod_diff(next, seed); prev = A[i]; } flip_cycle(rand); flip_cycle(rand); flip_cycle(rand); flip_cycle(rand); flip_cycle(rand); return; } /*********************************************************************** * NAME * * rng_next_rand - obtain pseudo-random integer in the range [0, 2^31-1] * * SYNOPSIS * * #include "glprng.h" * int rng_next_rand(RNG *rand); * * RETURNS * * The routine rng_next_rand returns a next pseudo-random integer which * is uniformly distributed between 0 and 2^31-1, inclusive. The period * length of the generated numbers is 2^85 - 2^30. The low order bits of * the generated numbers are just as random as the high-order bits. */ int rng_next_rand(RNG *rand) { return *fptr >= 0 ? *fptr-- : flip_cycle(rand); } /*********************************************************************** * NAME * * rng_unif_rand - obtain pseudo-random integer in the range [0, m-1] * * SYNOPSIS * * #include "glprng.h" * int rng_unif_rand(RNG *rand, int m); * * RETURNS * * The routine rng_unif_rand returns a next pseudo-random integer which * is uniformly distributed between 0 and m-1, inclusive, where m is any * positive integer less than 2^31. */ #define two_to_the_31 ((unsigned int)0x80000000) int rng_unif_rand(RNG *rand, int m) { unsigned int t = two_to_the_31 - (two_to_the_31 % m); int r; xassert(m > 0); do { r = rng_next_rand(rand); } while (t <= (unsigned int)r); return r % m; } /*********************************************************************** * NAME * * rng_delete_rand - delete pseudo-random number generator * * SYNOPSIS * * #include "glprng.h" * void rng_delete_rand(RNG *rand); * * DESCRIPTION * * The routine rng_delete_rand frees all the memory allocated to the * specified pseudo-random number generator. */ void rng_delete_rand(RNG *rand) { xfree(rand); return; } /**********************************************************************/ #if 0 /* To be sure that this modified version produces the same results as the original version, run this validation program. */ int main(void) { RNG *rand; int j; rand = rng_create_rand(); rng_init_rand(rand, -314159); if (rng_next_rand(rand) != 119318998) { fprintf(stderr, "Failure on the first try!\n"); return -1; } for (j = 1; j <= 133; j++) rng_next_rand(rand); if (rng_unif_rand(rand, 0x55555555) != 748103812) { fprintf(stderr, "Failure on the second try!\n"); return -2; } fprintf(stderr, "OK, the random-number generator routines seem to" " work!\n"); rng_delete_rand(rand); return 0; } #endif /* eof */ igraph/src/walktrap_heap.cpp0000644000176000001440000001410212325527074015676 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Pascal Pons The original copyright notice follows here. The FSF address was fixed by Tamas Nepusz */ // File: heap.cpp //----------------------------------------------------------------------------- // Walktrap v0.2 -- Finds community structure of networks using random walks // Copyright (C) 2004-2005 Pascal Pons // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA //----------------------------------------------------------------------------- // Author : Pascal Pons // Email : pascal.pons@gmail.com // Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html // Location : Paris, France // Time : June 2005 //----------------------------------------------------------------------------- // see readme.txt for more details #include "walktrap_heap.h" #include #include using namespace std; using namespace igraph::walktrap; void Neighbor_heap::move_up(int index) { while(H[index/2]->delta_sigma > H[index]->delta_sigma) { Neighbor* tmp = H[index/2]; H[index]->heap_index = index/2; H[index/2] = H[index]; tmp->heap_index = index; H[index] = tmp; index = index/2; } } void Neighbor_heap::move_down(int index) { while(true) { int min = index; if((2*index < size) && (H[2*index]->delta_sigma < H[min]->delta_sigma)) min = 2*index; if(2*index+1 < size && H[2*index+1]->delta_sigma < H[min]->delta_sigma) min = 2*index+1; if(min != index) { Neighbor* tmp = H[min]; H[index]->heap_index = min; H[min] = H[index]; tmp->heap_index = index; H[index] = tmp; index = min; } else break; } } Neighbor* Neighbor_heap::get_first() { if(size == 0) return 0; else return H[0]; } void Neighbor_heap::remove(Neighbor* N) { if(N->heap_index == -1 || size == 0) return; Neighbor* last_N = H[--size]; H[N->heap_index] = last_N; last_N->heap_index = N->heap_index; move_up(last_N->heap_index); move_down(last_N->heap_index); N->heap_index = -1; } void Neighbor_heap::add(Neighbor* N) { if(size >= max_size) return; N->heap_index = size++; H[N->heap_index] = N; move_up(N->heap_index); } void Neighbor_heap::update(Neighbor* N) { if(N->heap_index == -1) return; move_up(N->heap_index); move_down(N->heap_index); } long Neighbor_heap::memory() { return (sizeof(Neighbor_heap) + long(max_size)*sizeof(Neighbor*)); } Neighbor_heap::Neighbor_heap(int max_s) { max_size = max_s; size = 0; H = new Neighbor*[max_s]; } Neighbor_heap::~Neighbor_heap() { delete[] H; } bool Neighbor_heap::is_empty() { return (size == 0); } //################################################################# void Min_delta_sigma_heap::move_up(int index) { while(delta_sigma[H[index/2]] < delta_sigma[H[index]]) { int tmp = H[index/2]; I[H[index]] = index/2; H[index/2] = H[index]; I[tmp] = index; H[index] = tmp; index = index/2; } } void Min_delta_sigma_heap::move_down(int index) { while(true) { int max = index; if(2*index < size && delta_sigma[H[2*index]] > delta_sigma[H[max]]) max = 2*index; if(2*index+1 < size && delta_sigma[H[2*index+1]] > delta_sigma[H[max]]) max = 2*index+1; if(max != index) { int tmp = H[max]; I[H[index]] = max; H[max] = H[index]; I[tmp] = index; H[index] = tmp; index = max; } else break; } } int Min_delta_sigma_heap::get_max_community() { if(size == 0) return -1; else return H[0]; } void Min_delta_sigma_heap::remove_community(int community) { if(I[community] == -1 || size == 0) return; int last_community = H[--size]; H[I[community]] = last_community; I[last_community] = I[community]; move_up(I[last_community]); move_down(I[last_community]); I[community] = -1; } void Min_delta_sigma_heap::update(int community) { if(community < 0 || community >= max_size) return; if(I[community] == -1) { I[community] = size++; H[I[community]] = community; } move_up(I[community]); move_down(I[community]); } long Min_delta_sigma_heap::memory() { return (sizeof(Min_delta_sigma_heap) + long(max_size)*(2*sizeof(int) + sizeof(float))); } Min_delta_sigma_heap::Min_delta_sigma_heap(int max_s) { max_size = max_s; size = 0; H = new int[max_s]; I = new int[max_s]; delta_sigma = new float[max_s]; for(int i = 0; i < max_size; i++) { I[i] = -1; delta_sigma[i] = 1.; } } Min_delta_sigma_heap::~Min_delta_sigma_heap() { delete[] H; delete[] I; delete[] delta_sigma; } bool Min_delta_sigma_heap::is_empty() { return (size == 0); } igraph/src/bliss_orbit.hh0000644000176000001440000000266712325372072015215 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_ORBIT_HH #define BLISS_ORBIT_HH namespace igraph { class Orbit { protected: class OrbitEntry { public: unsigned int element; OrbitEntry *next; unsigned int size; }; OrbitEntry *orbits; OrbitEntry **in_orbit; unsigned int nof_elements; unsigned int _nof_orbits; void merge_orbits(OrbitEntry *o1, OrbitEntry *o2); public: Orbit(); ~Orbit(); void init(const unsigned int n); void reset(); void merge_orbits(unsigned int e1, unsigned int e2); bool is_minimal_representative(unsigned int e); unsigned int get_minimal_representative(unsigned int e); unsigned int orbit_size(unsigned int e); unsigned int nof_orbits() const {return _nof_orbits; } }; } #endif igraph/src/eigen.c0000644000176000001440000012117712325527073013615 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_eigen.h" #include "igraph_qsort.h" #include "igraph_blas.h" #include "igraph_interface.h" #include "igraph_adjlist.h" #include #include #include int igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun, int n, void *extra, igraph_matrix_t *res) { int i; igraph_vector_t v; IGRAPH_CHECK(igraph_matrix_init(res, n, n)); IGRAPH_FINALLY(igraph_matrix_destroy, res); IGRAPH_VECTOR_INIT_FINALLY(&v, n); VECTOR(v)[0]=1; IGRAPH_CHECK(fun(/*to=*/ &MATRIX(*res, 0, 0), /*from=*/ VECTOR(v), n, extra)); for (i=1; ihowmany-1, pr=0; IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); if (vectors) { IGRAPH_CHECK(igraph_matrix_init(&vec1, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); IGRAPH_CHECK(igraph_matrix_init(&vec2, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); } IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ 1, /*iu=*/ which->howmany, /*abstol=*/ 1e-14, &val1, vectors ? &vec1 : 0, /*support=*/ 0)); IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ n-which->howmany+1, /*iu=*/ n, /*abstol=*/ 1e-14, &val2, vectors ? &vec2 : 0, /*support=*/ 0)); if (values) { IGRAPH_CHECK(igraph_vector_resize(values, which->howmany)); } if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany)); } while (pr < which->howmany) { if (p2 < 0 || fabs(VECTOR(val1)[p1]) > fabs(VECTOR(val2)[p2])) { if (values) { VECTOR(*values)[pr]=VECTOR(val1)[p1]; } if (vectors) { memcpy(&MATRIX(*vectors,0,pr), &MATRIX(vec1,0,p1), sizeof(igraph_real_t) * (size_t) n); } p1++; pr++; } else { if (values) { VECTOR(*values)[pr]=VECTOR(val2)[p2]; } if (vectors) { memcpy(&MATRIX(*vectors,0,pr), &MATRIX(vec2,0,p2), sizeof(igraph_real_t) * (size_t) n); } p2--; pr++; } } if (vectors) { igraph_matrix_destroy(&vec2); igraph_matrix_destroy(&vec1); IGRAPH_FINALLY_CLEAN(2); } igraph_vector_destroy(&val2); igraph_vector_destroy(&val1); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_eigen_matrix_symmetric_lapack_sm(const igraph_matrix_t *A, const igraph_eigen_which_t *which, igraph_vector_t *values, igraph_matrix_t *vectors) { igraph_vector_t val; igraph_matrix_t vec; int i, w=0, n=(int) igraph_matrix_nrow(A); igraph_real_t small; int p1, p2, pr=0; IGRAPH_VECTOR_INIT_FINALLY(&val, 0); if (vectors) { IGRAPH_MATRIX_INIT_FINALLY(&vec, 0, 0); } IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_ALL, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ 0, /*iu=*/ 0, /*abstol=*/ 1e-14, &val, vectors ? &vec : 0, /*support=*/ 0)); /* Look for smallest value */ small=fabs(VECTOR(val)[0]); for (i=1; ihowmany)); } if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany)); } while (pr < which->howmany) { if (p2 == n-1 || fabs(VECTOR(val)[p1]) < fabs(VECTOR(val)[p2])) { if (values) { VECTOR(*values)[pr]=VECTOR(val)[p1]; } if (vectors) { memcpy(&MATRIX(*vectors,0,pr), &MATRIX(vec,0,p1), sizeof(igraph_real_t) * (size_t) n); } p1--; pr++; } else { if (values) { VECTOR(*values)[pr]=VECTOR(val)[p2]; } if (vectors) { memcpy(&MATRIX(*vectors,0,pr), &MATRIX(vec,0,p2), sizeof(igraph_real_t) * (size_t) n); } p2++; pr++; } } if (vectors) { igraph_matrix_destroy(&vec); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&val); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_eigen_matrix_symmetric_lapack_la(const igraph_matrix_t *A, const igraph_eigen_which_t *which, igraph_vector_t *values, igraph_matrix_t *vectors) { /* TODO: ordering? */ int n=(int) igraph_matrix_nrow(A); int il=n-which->howmany+1; IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ il, /*iu=*/ n, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); return 0; } int igraph_i_eigen_matrix_symmetric_lapack_sa(const igraph_matrix_t *A, const igraph_eigen_which_t *which, igraph_vector_t *values, igraph_matrix_t *vectors) { /* TODO: ordering? */ IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ 1, /*iu=*/ which->howmany, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); return 0; } int igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_matrix_t *A, const igraph_eigen_which_t *which, igraph_vector_t *values, igraph_matrix_t *vectors) { /* TODO: ordering? */ igraph_matrix_t vec1, vec2; igraph_vector_t val1, val2; int n=(int) igraph_matrix_nrow(A); int p1=0, p2=which->howmany/2, pr=0; IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); if (vectors) { IGRAPH_CHECK(igraph_matrix_init(&vec1, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); IGRAPH_CHECK(igraph_matrix_init(&vec2, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); } IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ 1, /*iu=*/ (which->howmany)/2, /*abstol=*/ 1e-14, &val1, vectors ? &vec1 : 0, /*support=*/ 0)); IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ n-(which->howmany)/2, /*iu=*/ n, /*abstol=*/ 1e-14, &val2, vectors ? &vec2 : 0, /*support=*/ 0)); if (values) { IGRAPH_CHECK(igraph_vector_resize(values, which->howmany)); } if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany)); } while (pr < which->howmany) { if (pr % 2) { if (values) { VECTOR(*values)[pr]=VECTOR(val1)[p1]; } if (vectors) { memcpy(&MATRIX(*vectors,0,pr), &MATRIX(vec1,0,p1), sizeof(igraph_real_t) * (size_t) n); } p1++; pr++; } else { if (values) { VECTOR(*values)[pr]=VECTOR(val2)[p2]; } if (vectors) { memcpy(&MATRIX(*vectors,0,pr), &MATRIX(vec2,0,p2), sizeof(igraph_real_t) * (size_t) n); } p2--; pr++; } } if (vectors) { igraph_matrix_destroy(&vec2); igraph_matrix_destroy(&vec1); IGRAPH_FINALLY_CLEAN(2); } igraph_vector_destroy(&val2); igraph_vector_destroy(&val1); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A, igraph_vector_t *values, igraph_matrix_t *vectors) { IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_ALL, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ 0, /*iu=*/ 0, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); return 0; } int igraph_i_eigen_matrix_symmetric_lapack_iv(const igraph_matrix_t *A, const igraph_eigen_which_t *which, igraph_vector_t *values, igraph_matrix_t *vectors) { IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_INTERVAL, /*vl=*/ which->vl, /*vu=*/ which->vu, /*vestimate=*/ which->vestimate, /*il=*/ 0, /*iu=*/ 0, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); return 0; } int igraph_i_eigen_matrix_symmetric_lapack_sel(const igraph_matrix_t *A, const igraph_eigen_which_t *which, igraph_vector_t *values, igraph_matrix_t *vectors) { IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, /*il=*/ which->il, /*iu=*/ which->iu, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); return 0; } int igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, const igraph_eigen_which_t *which, igraph_vector_t *values, igraph_matrix_t *vectors) { const igraph_matrix_t *myA=A; igraph_matrix_t mA; /* First we need to create a dense square matrix */ if (A) { n=(int) igraph_matrix_nrow(A); } else if (sA) { n=(int) igraph_sparsemat_nrow(sA); IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &mA); IGRAPH_CHECK(igraph_sparsemat_as_matrix(&mA, sA)); myA=&mA; } else if (fun) { IGRAPH_CHECK(igraph_i_eigen_arpackfun_to_mat(fun, n, extra, &mA)); IGRAPH_FINALLY(igraph_matrix_destroy, &mA); myA=&mA; } switch (which->pos) { case IGRAPH_EIGEN_LM: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_lm(myA, which, values, vectors)); break; case IGRAPH_EIGEN_SM: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sm(myA, which, values, vectors)); break; case IGRAPH_EIGEN_LA: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_la(myA, which, values, vectors)); break; case IGRAPH_EIGEN_SA: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sa(myA, which, values, vectors)); break; case IGRAPH_EIGEN_BE: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_be(myA, which, values, vectors)); break; case IGRAPH_EIGEN_ALL: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_all(myA, values, vectors)); break; case IGRAPH_EIGEN_INTERVAL: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_iv(myA, which, values, vectors)); break; case IGRAPH_EIGEN_SELECT: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sel(myA, which, values, vectors)); break; default: /* This cannot happen */ break; } if (!A) { igraph_matrix_destroy(&mA); IGRAPH_FINALLY_CLEAN(1); } return 0; } typedef struct igraph_i_eigen_matrix_sym_arpack_data_t { const igraph_matrix_t *A; const igraph_sparsemat_t *sA; } igraph_i_eigen_matrix_sym_arpack_data_t; int igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_eigen_matrix_sym_arpack_data_t *data= (igraph_i_eigen_matrix_sym_arpack_data_t *) extra; if (data->A) { igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, data->A, from, /*beta=*/ 0.0, to); } else { /* data->sA */ igraph_vector_t vto, vfrom; igraph_vector_view(&vto, to, n); igraph_vector_view(&vfrom, to, n); igraph_vector_null(&vto); igraph_sparsemat_gaxpy(data->sA, &vfrom, &vto); } return 0; } int igraph_i_eigen_matrix_symmetric_arpack_be(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors) { igraph_vector_t tmpvalues, tmpvalues2; igraph_matrix_t tmpvectors, tmpvectors2; igraph_i_eigen_matrix_sym_arpack_data_t myextra = { A, sA }; int low=(int) floor(which->howmany/2.0), high=(int) ceil(which->howmany/2.0); int l1, l2, w; if (low + high >= n) { IGRAPH_ERROR("Requested too many eigenvalues/vectors", IGRAPH_EINVAL); } if (!fun) { fun=igraph_i_eigen_matrix_sym_arpack_cb; extra=(void*) &myextra; } IGRAPH_VECTOR_INIT_FINALLY(&tmpvalues, high); IGRAPH_MATRIX_INIT_FINALLY(&tmpvectors, n, high); IGRAPH_VECTOR_INIT_FINALLY(&tmpvalues2, low); IGRAPH_MATRIX_INIT_FINALLY(&tmpvectors2, n, low); options->n=n; options->nev=high; options->ncv= 2*options->nev < n ? 2*options->nev : n; options->which[0]='L'; options->which[1]='A'; IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, &tmpvalues, &tmpvectors)); options->nev=low; options->ncv= 2*options->nev < n ? 2*options->nev : n; options->which[0]='S'; options->which[1]='A'; IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, &tmpvalues2, &tmpvectors2)); IGRAPH_CHECK(igraph_vector_resize(values, low+high)); IGRAPH_CHECK(igraph_matrix_resize(vectors, n, low+high)); l1=0; l2=0; w=0; while (w < which->howmany) { VECTOR(*values)[w] = VECTOR(tmpvalues)[l1]; memcpy(&MATRIX(*vectors, 0, w), &MATRIX(tmpvectors, 0, l1), (size_t) n * sizeof(igraph_real_t)); w++; l1++; if (w < which->howmany) { VECTOR(*values)[w] = VECTOR(tmpvalues2)[l2]; memcpy(&MATRIX(*vectors, 0, w), &MATRIX(tmpvectors2, 0, l2), (size_t) n * sizeof(igraph_real_t)); w++; l2++; } } igraph_matrix_destroy(&tmpvectors2); igraph_vector_destroy(&tmpvalues2); igraph_matrix_destroy(&tmpvectors); igraph_vector_destroy(&tmpvalues); IGRAPH_FINALLY_CLEAN(4); return 0; } int igraph_i_eigen_matrix_symmetric_arpack(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors) { /* For ARPACK we need a matrix multiplication operation. This can be done in any format, so everything is fine, we don't have to convert. */ igraph_i_eigen_matrix_sym_arpack_data_t myextra = { A, sA }; if (!options) { IGRAPH_ERROR("`options' must be given for ARPACK algorithm", IGRAPH_EINVAL); } if (which->pos == IGRAPH_EIGEN_BE) { return igraph_i_eigen_matrix_symmetric_arpack_be(A, sA, fun, n, extra, which, options, storage, values, vectors); } else { switch (which->pos) { case IGRAPH_EIGEN_LM: options->which[0]='L'; options->which[1]='M'; options->nev=which->howmany; break; case IGRAPH_EIGEN_SM: options->which[0]='S'; options->which[1]='M'; options->nev=which->howmany; break; case IGRAPH_EIGEN_LA: options->which[0]='L'; options->which[1]='A'; options->nev=which->howmany; break; case IGRAPH_EIGEN_SA: options->which[0]='S'; options->which[1]='A'; options->nev=which->howmany; break; case IGRAPH_EIGEN_ALL: options->which[0]='L'; options->which[1]='M'; options->nev=n; break; case IGRAPH_EIGEN_INTERVAL: IGRAPH_ERROR("Interval of eigenvectors with ARPACK", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_SELECT: IGRAPH_ERROR("Selected eigenvalues with ARPACK", IGRAPH_UNIMPLEMENTED); /* TODO */ break; default: /* This cannot happen */ break; } options->n=n; options->ncv= 2*options->nev < n ? 2*options->nev : n; if (!fun) { fun=igraph_i_eigen_matrix_sym_arpack_cb; extra=(void*) &myextra; } IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, values, vectors)); return 0; } } /* Get the eigenvalues and the eigenvectors from the compressed form. Order them according to the ordering criteria. Comparison functions for the reordering first */ typedef int (*igraph_i_eigen_matrix_lapack_cmp_t)(void*, const void*, const void *); typedef struct igraph_i_eml_cmp_t { const igraph_vector_t *mag, *real, *imag; } igraph_i_eml_cmp_t; /* TODO: these should be defined in some header */ #define EPS (DBL_EPSILON*100) #define LESS(a,b) ((a) < (b)-EPS) #define MORE(a,b) ((a) > (b)+EPS) #define ZERO(a) ((a) > -EPS && (a) < EPS) #define NONZERO(a) ((a) < -EPS || (a) > EPS) /* Largest magnitude. Ordering is according to 1 Larger magnitude 2 Real eigenvalues before complex ones 3 Larger real part 4 Larger imaginary part */ int igraph_i_eigen_matrix_lapack_cmp_lm(void *extra, const void *a, const void *b) { igraph_i_eml_cmp_t *myextra=(igraph_i_eml_cmp_t *) extra; int *aa=(int*) a, *bb=(int*) b; igraph_real_t a_m=VECTOR(*myextra->mag)[*aa]; igraph_real_t b_m=VECTOR(*myextra->mag)[*bb]; if (LESS(a_m, b_m)) { return 1; } else if (MORE(a_m, b_m)) { return -1; } else { igraph_real_t a_r=VECTOR(*myextra->real)[*aa]; igraph_real_t a_i=VECTOR(*myextra->imag)[*aa]; igraph_real_t b_r=VECTOR(*myextra->real)[*bb]; igraph_real_t b_i=VECTOR(*myextra->imag)[*bb]; if (ZERO(a_i) && NONZERO(b_i)) { return -1; } if (NONZERO(a_i) && ZERO(b_i)) { return 1; } if (MORE(a_r, b_r)) { return -1; } if (LESS(a_r, b_r)) { return 1; } if (MORE(a_i, b_i)) { return -1; } if (LESS(a_i, b_i)) { return 1; } } return 0; } /* Smallest marginude. Ordering is according to 1 Magnitude (smaller first) 2 Complex eigenvalues before real ones 3 Smaller real part 4 Smaller imaginary part This ensures that lm has exactly the opposite order to sm */ int igraph_i_eigen_matrix_lapack_cmp_sm(void *extra, const void *a, const void *b) { igraph_i_eml_cmp_t *myextra=(igraph_i_eml_cmp_t *) extra; int *aa=(int*) a, *bb=(int*) b; igraph_real_t a_m=VECTOR(*myextra->mag)[*aa]; igraph_real_t b_m=VECTOR(*myextra->mag)[*bb]; if (MORE(a_m, b_m)) { return 1; } else if (LESS(a_m, b_m)) { return -1; } else { igraph_real_t a_r=VECTOR(*myextra->real)[*aa]; igraph_real_t a_i=VECTOR(*myextra->imag)[*aa]; igraph_real_t b_r=VECTOR(*myextra->real)[*bb]; igraph_real_t b_i=VECTOR(*myextra->imag)[*bb]; if (NONZERO(a_i) && ZERO(b_i)) { return -1; } if (ZERO(a_i) && NONZERO(b_i)) { return 1; } if (LESS(a_r, b_r)) { return -1; } if (MORE(a_r, b_r)) { return 1; } if (LESS(a_i, b_i)) { return -1; } if (MORE(a_i, b_i)) { return 1; } } return 0; } /* Largest real part. Ordering is according to 1 Larger real part 2 Real eigenvalues come before complex ones 3 Larger complex part */ int igraph_i_eigen_matrix_lapack_cmp_lr(void *extra, const void *a, const void *b) { igraph_i_eml_cmp_t *myextra=(igraph_i_eml_cmp_t *) extra; int *aa=(int*) a, *bb=(int*) b; igraph_real_t a_r=VECTOR(*myextra->real)[*aa]; igraph_real_t b_r=VECTOR(*myextra->real)[*bb]; if (MORE(a_r, b_r)) { return -1; } else if (LESS(a_r, b_r)) { return 1; } else { igraph_real_t a_i=VECTOR(*myextra->imag)[*aa]; igraph_real_t b_i=VECTOR(*myextra->imag)[*bb]; if (ZERO(a_i) && NONZERO(b_i)) { return -1; } if (NONZERO(a_i) && ZERO(b_i)) { return 1; } if (MORE(a_i, b_i)) { return -1; } if (LESS(a_i, b_i)) { return 1; } } return 0; } /* Largest real part. Ordering is according to 1 Smaller real part 2 Complex eigenvalues come before real ones 3 Smaller complex part This is opposite to LR */ int igraph_i_eigen_matrix_lapack_cmp_sr(void *extra, const void *a, const void *b) { igraph_i_eml_cmp_t *myextra=(igraph_i_eml_cmp_t *) extra; int *aa=(int*) a, *bb=(int*) b; igraph_real_t a_r=VECTOR(*myextra->real)[*aa]; igraph_real_t b_r=VECTOR(*myextra->real)[*bb]; if (LESS(a_r, b_r)) { return -1; } else if (MORE(a_r, b_r)) { return 1; } else { igraph_real_t a_i=VECTOR(*myextra->imag)[*aa]; igraph_real_t b_i=VECTOR(*myextra->imag)[*bb]; if (NONZERO(a_i) && ZERO(b_i)) { return -1; } if (ZERO(a_i) && NONZERO(b_i)) { return 1; } if (LESS(a_i, b_i)) { return -1; } if (MORE(a_i, b_i)) { return 1; } } return 0; } /* Order: 1 Larger imaginary part 2 Real eigenvalues before complex ones 3 Larger real part */ int igraph_i_eigen_matrix_lapack_cmp_li(void *extra, const void *a, const void *b) { igraph_i_eml_cmp_t *myextra=(igraph_i_eml_cmp_t *) extra; int *aa=(int*) a, *bb=(int*) b; igraph_real_t a_i=VECTOR(*myextra->imag)[*aa]; igraph_real_t b_i=VECTOR(*myextra->imag)[*bb]; if (MORE(a_i, b_i)) { return -1; } else if (LESS(a_i, b_i)) { return 1; } else { igraph_real_t a_r=VECTOR(*myextra->real)[*aa]; igraph_real_t b_r=VECTOR(*myextra->real)[*bb]; if (ZERO(a_i) && NONZERO(b_i)) { return -1; } if (NONZERO(a_i) && ZERO(b_i)) { return 1; } if (MORE(a_r, b_r)) { return -1; } if (LESS(a_r, b_r)) { return 1; } } return 0; } /* Order: 1 Smaller imaginary part 2 Complex eigenvalues before real ones 3 Smaller real part Order is opposite to LI */ int igraph_i_eigen_matrix_lapack_cmp_si(void *extra, const void *a, const void *b) { igraph_i_eml_cmp_t *myextra=(igraph_i_eml_cmp_t *) extra; int *aa=(int*) a, *bb=(int*) b; igraph_real_t a_i=VECTOR(*myextra->imag)[*aa]; igraph_real_t b_i=VECTOR(*myextra->imag)[*bb]; if (LESS(a_i, b_i)) { return -1; } else if (MORE(a_i, b_i)) { return 1; } else { igraph_real_t a_r=VECTOR(*myextra->real)[*aa]; igraph_real_t b_r=VECTOR(*myextra->real)[*bb]; if (NONZERO(a_i) && ZERO(b_i)) { return -1; } if (ZERO(a_i) && NONZERO(b_i)) { return 1; } if (LESS(a_r, b_r)) { return -1; } if (MORE(a_r, b_r)) { return 1; } } return 0; } #undef EPS #undef LESS #undef MORE #undef ZERO #undef NONZERO #define INITMAG() \ do { \ int i; \ IGRAPH_VECTOR_INIT_FINALLY(&mag, nev); \ hasmag=1; \ for (i=0; ipos) { case IGRAPH_EIGEN_LM: INITMAG(); cmpfunc=igraph_i_eigen_matrix_lapack_cmp_lm; howmany=which->howmany; break; case IGRAPH_EIGEN_ALL: INITMAG(); cmpfunc=igraph_i_eigen_matrix_lapack_cmp_sm; howmany=nev; break; case IGRAPH_EIGEN_SM: INITMAG(); cmpfunc=igraph_i_eigen_matrix_lapack_cmp_sm; howmany=which->howmany; break; case IGRAPH_EIGEN_LR: cmpfunc=igraph_i_eigen_matrix_lapack_cmp_lr; howmany=which->howmany; break; case IGRAPH_EIGEN_SR: cmpfunc=igraph_i_eigen_matrix_lapack_cmp_sr; howmany=which->howmany; break; case IGRAPH_EIGEN_SELECT: INITMAG(); cmpfunc=igraph_i_eigen_matrix_lapack_cmp_sm; start=which->il-1; howmany=which->iu - which->il + 1; break; case IGRAPH_EIGEN_LI: cmpfunc=igraph_i_eigen_matrix_lapack_cmp_li; howmany=which->howmany; break; case IGRAPH_EIGEN_SI: cmpfunc=igraph_i_eigen_matrix_lapack_cmp_si; howmany=which->howmany; break; case IGRAPH_EIGEN_INTERVAL: case IGRAPH_EIGEN_BE: default: IGRAPH_ERROR("Unimplemented eigenvalue ordering", IGRAPH_UNIMPLEMENTED); break; } for (i=0; ipos) { case IGRAPH_EIGEN_LM: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_lm(myA, which, values, vectors)); break; case IGRAPH_EIGEN_SM: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_sm(myA, which, values, vectors)); break; case IGRAPH_EIGEN_LR: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_lr(myA, which, values, vectors)); break; case IGRAPH_EIGEN_SR: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_sr(myA, which, values, vectors)); break; case IGRAPH_EIGEN_LI: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_li(myA, which, values, vectors)); break; case IGRAPH_EIGEN_SI: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_si(myA, which, values, vectors)); break; case IGRAPH_EIGEN_SELECT: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_select(myA, which, values, vectors)); break; case IGRAPH_EIGEN_ALL: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_all(myA, which, values, vectors)); break; default: /* This cannot happen */ break; } if (!A) { igraph_matrix_destroy(&mA); IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_eigen_checks(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n) { if ( (A?1:0)+(sA?1:0)+(fun?1:0) != 1) { IGRAPH_ERROR("Exactly one of 'A', 'sA' and 'fun' must be given", IGRAPH_EINVAL); } if (A) { if (n != igraph_matrix_ncol(A) || n != igraph_matrix_nrow(A)) { IGRAPH_ERROR("Invalid matrix", IGRAPH_NONSQUARE); } } else if (sA) { if (n != igraph_sparsemat_ncol(sA) || n != igraph_sparsemat_nrow(sA)) { IGRAPH_ERROR("Invalid matrix", IGRAPH_NONSQUARE); } } return 0; } /** * \function igraph_eigen_matrix_symmetric * * \example examples/simple/igraph_eigen_matrix_symmetric.c */ int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors) { IGRAPH_CHECK(igraph_i_eigen_checks(A, sA, fun, n)); if (which->pos != IGRAPH_EIGEN_LM && which->pos != IGRAPH_EIGEN_SM && which->pos != IGRAPH_EIGEN_LA && which->pos != IGRAPH_EIGEN_SA && which->pos != IGRAPH_EIGEN_BE && which->pos != IGRAPH_EIGEN_ALL && which->pos != IGRAPH_EIGEN_INTERVAL && which->pos != IGRAPH_EIGEN_SELECT) { IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); } switch (algorithm) { case IGRAPH_EIGEN_AUTO: if (which->howmany==n || n < 100) { IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n, extra, which, values, vectors)); } else { IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, extra, which, options, storage, values, vectors)); } break; case IGRAPH_EIGEN_LAPACK: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n ,extra, which, values, vectors)); break; case IGRAPH_EIGEN_ARPACK: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, extra, which, options, storage, values, vectors)); break; default: IGRAPH_ERROR("Unknown 'algorithm'", IGRAPH_EINVAL); } return 0; } /** * \function igraph_eigen_matrix * */ int igraph_eigen_matrix(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_complex_t *values, igraph_matrix_complex_t *vectors) { IGRAPH_CHECK(igraph_i_eigen_checks(A, sA, fun, n)); if (which->pos != IGRAPH_EIGEN_LM && which->pos != IGRAPH_EIGEN_SM && which->pos != IGRAPH_EIGEN_LR && which->pos != IGRAPH_EIGEN_SR && which->pos != IGRAPH_EIGEN_LI && which->pos != IGRAPH_EIGEN_SI && which->pos != IGRAPH_EIGEN_SELECT && which->pos != IGRAPH_EIGEN_ALL) { IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); } switch (algorithm) { case IGRAPH_EIGEN_AUTO: IGRAPH_ERROR("'AUTO' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_LAPACK: IGRAPH_CHECK(igraph_i_eigen_matrix_lapack(A, sA, fun, n, extra, which, values, vectors)); /* TODO */ break; case IGRAPH_EIGEN_ARPACK: IGRAPH_ERROR("'ARPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_COMP_AUTO: IGRAPH_ERROR("'COMP_AUTO' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_COMP_LAPACK: IGRAPH_ERROR("'COMP_LAPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_COMP_ARPACK: IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; default: IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); } return 0; } int igraph_i_eigen_adjacency_arpack_sym_cb(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_adjlist_t *adjlist = (igraph_adjlist_t *) extra; igraph_vector_int_t *neis; int i, j, nlen; for (i=0; ipos == IGRAPH_EIGEN_INTERVAL) { IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " "`INTERNAL' eigenvalues", IGRAPH_UNIMPLEMENTED); } if (which->pos == IGRAPH_EIGEN_SELECT) { IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " "`SELECT' eigenvalues", IGRAPH_UNIMPLEMENTED); } if (which->pos == IGRAPH_EIGEN_ALL) { IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " "`ALL' eigenvalues", IGRAPH_UNIMPLEMENTED); } switch (which->pos) { case IGRAPH_EIGEN_LM: options->which[0]='L'; options->which[1]='M'; options->nev=which->howmany; break; case IGRAPH_EIGEN_SM: options->which[0]='S'; options->which[1]='M'; options->nev=which->howmany; break; case IGRAPH_EIGEN_LA: options->which[0]='L'; options->which[1]='A'; options->nev=which->howmany; break; case IGRAPH_EIGEN_SA: options->which[0]='S'; options->which[1]='A'; options->nev=which->howmany; break; case IGRAPH_EIGEN_ALL: options->which[0]='L'; options->which[1]='M'; options->nev=n; break; case IGRAPH_EIGEN_BE: IGRAPH_ERROR("Eigenvectors from both ends with ARPACK", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_INTERVAL: IGRAPH_ERROR("Interval of eigenvectors with ARPACK", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_SELECT: IGRAPH_ERROR("Selected eigenvalues with ARPACK", IGRAPH_UNIMPLEMENTED); /* TODO */ break; default: /* This cannot happen */ break; } options->n=n; options->ncv= 2*options->nev < n ? 2*options->nev : n; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigen_adjacency_arpack_sym_cb, extra, options, storage, values, vectors)); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_eigen_adjacency * */ int igraph_eigen_adjacency(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_complex_t *cmplxvalues, igraph_matrix_complex_t *cmplxvectors) { if (which->pos != IGRAPH_EIGEN_LM && which->pos != IGRAPH_EIGEN_SM && which->pos != IGRAPH_EIGEN_LA && which->pos != IGRAPH_EIGEN_SA && which->pos != IGRAPH_EIGEN_BE && which->pos != IGRAPH_EIGEN_SELECT && which->pos != IGRAPH_EIGEN_INTERVAL && which->pos != IGRAPH_EIGEN_ALL) { IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); } switch (algorithm) { case IGRAPH_EIGEN_AUTO: IGRAPH_ERROR("'AUTO' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_LAPACK: IGRAPH_ERROR("'LAPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_ARPACK: IGRAPH_CHECK(igraph_i_eigen_adjacency_arpack(graph, which, options, storage, values, vectors, cmplxvalues, cmplxvectors)); break; case IGRAPH_EIGEN_COMP_AUTO: IGRAPH_ERROR("'COMP_AUTO' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_COMP_LAPACK: IGRAPH_ERROR("'COMP_LAPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; case IGRAPH_EIGEN_COMP_ARPACK: IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ break; default: IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); } return 0; } /** * \function igraph_eigen_laplacian * */ int igraph_eigen_laplacian(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_complex_t *cmplxvalues, igraph_matrix_complex_t *cmplxvectors) { IGRAPH_ERROR("'igraph_eigen_laplacian'", IGRAPH_UNIMPLEMENTED); /* TODO */ return 0; } igraph/src/visitors.c0000644000176000001440000004407312325527074014410 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_visitor.h" #include "igraph_memory.h" #include "igraph_adjlist.h" #include "igraph_interface.h" #include "igraph_dqueue.h" #include "igraph_stack.h" #include "config.h" /** * \function igraph_bfs * Breadth-first search * * A simple breadth-first search, with a lot of different results and * the possibility to call a callback whenever a vertex is visited. * It is allowed to supply null pointers as the output arguments the * user is not interested in, in this case they will be ignored. * * * If not all vertices can be reached from the supplied root vertex, * then additional root vertices will be used, in the order of their * vertex ids. * \param graph The input graph. * \param root The id of the root vertex. It is ignored if the \c * roots argument is not a null pointer. * \param roots Pointer to an initialized vector, or a null * pointer. If not a null pointer, then it is a vector * containing root vertices to start the BFS from. The vertices * are considered in the order they appear. If a root vertex * was already found while searching from another one, then no * search is conducted from it. * \param mode For directed graphs, it defines which edges to follow. * \c IGRAPH_OUT means following the direction of the edges, * \c IGRAPH_IN means the opposite, and * \c IGRAPH_ALL ignores the direction of the edges. * This parameter is ignored for undirected graphs. * \param unreachable Logical scalar, whether the search should visit * the vertices that are unreachable from the given root * node(s). If true, then additional searches are performed * until all vertices are visited. * \param restricted If not a null pointer, then it must be a pointer * to a vector containing vertex ids. The BFS is carried out * only on these vertices. * \param order If not null pointer, then the vertex ids of the graph are * stored here, in the same order as they were visited. * \param rank If not a null pointer, then the rank of each vertex is * stored here. * \param father If not a null pointer, then the id of the father of * each vertex is stored here. * \param pred If not a null pointer, then the id of vertex that was * visited before the current one is stored here. If there is * no such vertex (the current vertex is the root of a search * tree), then -1 is stored. * \param succ If not a null pointer, then the id of the vertex that * was visited after the current one is stored here. If there * is no such vertex (the current one is the last in a search * tree), then -1 is stored. * \param dist If not a null pointer, then the distance from the root of * the current search tree is stored here. * \param callback If not null, then it should be a pointer to a * function of type \ref igraph_bfshandler_t. This function * will be called, whenever a new vertex is visited. * \param extra Extra argument to pass to the callback function. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. * * \example examples/simple/igraph_bfs.c * \example examples/simple/igraph_bfs2.c */ int igraph_bfs(const igraph_t *graph, igraph_integer_t root, const igraph_vector_t *roots, igraph_neimode_t mode, igraph_bool_t unreachable, const igraph_vector_t *restricted, igraph_vector_t *order, igraph_vector_t *rank, igraph_vector_t *father, igraph_vector_t *pred, igraph_vector_t *succ, igraph_vector_t *dist, igraph_bfshandler_t *callback, void *extra) { igraph_dqueue_t Q; long int no_of_nodes=igraph_vcount(graph); long int actroot=0; igraph_vector_char_t added; igraph_lazy_adjlist_t adjlist; long int act_rank=0; long int pred_vec=-1; long int rootpos=0; long int noroots= roots ? igraph_vector_size(roots) : 1; if (!roots && (root < 0 || root >= no_of_nodes)) { IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL); } if (roots) { igraph_real_t min, max; igraph_vector_minmax(roots, &min, &max); if (min < 0 || max >= no_of_nodes) { IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL); } } if (restricted) { igraph_real_t min, max; igraph_vector_minmax(restricted, &min, &max); if (min < 0 || max >= no_of_nodes) { IGRAPH_ERROR("Invalid vertex id in restricted set", IGRAPH_EINVAL); } } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } IGRAPH_CHECK(igraph_vector_char_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_char_destroy, &added); IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, /*simplify=*/ 0)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); /* Mark the vertices that are not in the restricted set, as already found. Special care must be taken for vertices that are not in the restricted set, but are to be used as 'root' vertices. */ if (restricted) { long int i, n=igraph_vector_size(restricted); igraph_vector_char_fill(&added, 1); for (i=0; i * If not all vertices can be reached from the supplied root vertex, * then additional root vertices will be used, in the order of their * vertex ids. * \param graph The input graph. * \param root The id of the root vertex. * \param mode For directed graphs, it defines which edges to follow. * \c IGRAPH_OUT means following the direction of the edges, * \c IGRAPH_IN means the opposite, and * \c IGRAPH_ALL ignores the direction of the edges. * This parameter is ignored for undirected graphs. * \param unreachable Logical scalar, whether the search should visit * the vertices that are unreachable from the given root * node(s). If true, then additional searches are performed * until all vertices are visited. * \param order If not null pointer, then the vertex ids of the graph are * stored here, in the same order as they were discovered. * \param order_out If not a null pointer, then the vertex ids of the * graphs are stored here, in the order of the completion of * their subtree. * \param father If not a null pointer, then the id of the father of * each vertex is stored here. * \param dist If not a null pointer, then the distance from the root of * the current search tree is stored here. * \param in_callback If not null, then it should be a pointer to a * function of type \ref igraph_dfshandler_t. This function * will be called, whenever a new vertex is discovered. * \param out_callback If not null, then it should be a pointer to a * function of type \ref igraph_dfshandler_t. This function * will be called, whenever the subtree of a vertex is completed. * \param extra Extra argument to pass to the callback function(s). * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. */ int igraph_dfs(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, igraph_bool_t unreachable, igraph_vector_t *order, igraph_vector_t *order_out, igraph_vector_t *father, igraph_vector_t *dist, igraph_dfshandler_t *in_callback, igraph_dfshandler_t *out_callback, void *extra) { long int no_of_nodes=igraph_vcount(graph); igraph_lazy_adjlist_t adjlist; igraph_stack_t stack; igraph_vector_char_t added; igraph_vector_long_t nptr; long int actroot; long int act_rank=0; long int rank_out=0; long int act_dist=0; if (root < 0 || root >= no_of_nodes) { IGRAPH_ERROR("Invalid root vertex for DFS", IGRAPH_EINVAL); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } IGRAPH_CHECK(igraph_vector_char_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_char_destroy, &added); IGRAPH_CHECK(igraph_stack_init(&stack, 100)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, /*simplify=*/ 0)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_vector_long_init(&nptr, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &nptr); # define FREE_ALL() do { \ igraph_vector_long_destroy(&nptr); \ igraph_lazy_adjlist_destroy(&adjlist); \ igraph_stack_destroy(&stack); \ igraph_vector_char_destroy(&added); \ IGRAPH_FINALLY_CLEAN(4); } while (0) /* Resize result vectors and fill them with IGRAPH_NAN */ # define VINIT(v) if (v) { \ igraph_vector_resize(v, no_of_nodes); \ igraph_vector_fill(v, IGRAPH_NAN); } VINIT(order); VINIT(order_out); VINIT(father); VINIT(dist); # undef VINIT IGRAPH_CHECK(igraph_stack_push(&stack, root)); VECTOR(added)[(long int)root] = 1; if (father) { VECTOR(*father)[(long int)root] = -1; } if (order) { VECTOR(*order)[act_rank++] = root; } if (dist) { VECTOR(*dist)[(long int)root] = 0; } if (in_callback) { igraph_bool_t terminate=in_callback(graph, root, 0, extra); if (terminate) { FREE_ALL(); return 0; } } for (actroot=0; actroot= 0. c c D Double precision array, dimension (N). (INPUT/OUTPUT) c On entry, D contains the diagonal elements of the c tridiagonal matrix. c On exit, D contains the eigenvalues, in ascending order. c If an error exit is made, the eigenvalues are correct c for indices 1,2,...,INFO-1, but they are unordered and c may not be the smallest eigenvalues of the matrix. c c E Double precision array, dimension (N-1). (INPUT/OUTPUT) c On entry, E contains the subdiagonal elements of the c tridiagonal matrix in positions 1 through N-1. c On exit, E has been destroyed. c c Z Double precision array, dimension (N). (OUTPUT) c On exit, Z contains the last row of the orthonormal c eigenvector matrix of the symmetric tridiagonal matrix. c If an error exit is made, Z contains the last row of the c eigenvector matrix associated with the stored eigenvalues. c c WORK Double precision array, dimension (max(1,2*N-2)). (WORKSPACE) c Workspace used in accumulating the transformation for c computing the last components of the eigenvectors. c c INFO Integer. (OUTPUT) c = 0: normal return. c < 0: if INFO = -i, the i-th argument had an illegal value. c > 0: if INFO = +i, the i-th eigenvalue has not converged c after a total of 30*N iterations. c c\Remarks c 1. None. c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c daxpy Level 1 BLAS that computes a vector triad. c dcopy Level 1 BLAS that copies one vector to another. c dswap Level 1 BLAS that swaps the contents of two vectors. c lsame LAPACK character comparison routine. c dlae2 LAPACK routine that computes the eigenvalues of a 2-by-2 c symmetric matrix. c dlaev2 LAPACK routine that eigendecomposition of a 2-by-2 symmetric c matrix. c dlamch LAPACK routine that determines machine constants. c dlanst LAPACK routine that computes the norm of a matrix. c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. c dlartg LAPACK Givens rotation construction routine. c dlascl LAPACK routine for careful scaling of a matrix. c dlaset LAPACK matrix initialization routine. c dlasr LAPACK routine that applies an orthogonal transformation to c a matrix. c dlasrt LAPACK sorting routine. c dsteqr LAPACK routine that computes eigenvalues and eigenvectors c of a symmetric tridiagonal matrix. c xerbla LAPACK error handler routine. c c\Authors c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\SCCS Information: @(#) c FILE: stqrb.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 c c\Remarks c 1. Starting with version 2.5, this routine is a modified version c of LAPACK version 2.0 subroutine SSTEQR. No lines are deleted, c only commeted out and new lines inserted. c All lines commented out have "c$$$" at the beginning. c Note that the LAPACK version 1.0 subroutine SSTEQR contained c bugs. c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdstqrb ( n, d, e, z, work, info ) c c %------------------% c | Scalar Arguments | c %------------------% c integer info, n c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & d( n ), e( n-1 ), z( n ), work( 2*n-2 ) c c .. parameters .. Double precision & zero, one, two, three parameter ( zero = 0.0D+0, one = 1.0D+0, & two = 2.0D+0, three = 3.0D+0 ) integer maxit parameter ( maxit = 30 ) c .. c .. local scalars .. integer i, icompz, ii, iscale, j, jtot, k, l, l1, lend, & lendm1, lendp1, lendsv, lm1, lsv, m, mm, mm1, & nm1, nmaxit Double precision & anorm, b, c, eps, eps2, f, g, p, r, rt1, rt2, & s, safmax, safmin, ssfmax, ssfmin, tst c .. c .. external functions .. logical lsame Double precision & dlamch, dlanst, dlapy2 external lsame, dlamch, dlanst, dlapy2 c .. c .. external subroutines .. external dlae2, dlaev2, dlartg, dlascl, dlaset, dlasr, & dlasrt, dswap, xerbla c .. c .. intrinsic functions .. intrinsic abs, max, sign, sqrt c .. c .. executable statements .. c c test the input parameters. c info = 0 c c$$$ IF( LSAME( COMPZ, 'N' ) ) THEN c$$$ ICOMPZ = 0 c$$$ ELSE IF( LSAME( COMPZ, 'V' ) ) THEN c$$$ ICOMPZ = 1 c$$$ ELSE IF( LSAME( COMPZ, 'I' ) ) THEN c$$$ ICOMPZ = 2 c$$$ ELSE c$$$ ICOMPZ = -1 c$$$ END IF c$$$ IF( ICOMPZ.LT.0 ) THEN c$$$ INFO = -1 c$$$ ELSE IF( N.LT.0 ) THEN c$$$ INFO = -2 c$$$ ELSE IF( ( LDZ.LT.1 ) .OR. ( ICOMPZ.GT.0 .AND. LDZ.LT.MAX( 1, c$$$ $ N ) ) ) THEN c$$$ INFO = -6 c$$$ END IF c$$$ IF( INFO.NE.0 ) THEN c$$$ CALL XERBLA( 'SSTEQR', -INFO ) c$$$ RETURN c$$$ END IF c c *** New starting with version 2.5 *** c icompz = 2 c ************************************* c c quick return if possible c if( n.eq.0 ) $ return c if( n.eq.1 ) then if( icompz.eq.2 ) z( 1 ) = one return end if c c determine the unit roundoff and over/underflow thresholds. c eps = dlamch( 'e' ) eps2 = eps**2 safmin = dlamch( 's' ) safmax = one / safmin ssfmax = sqrt( safmax ) / three ssfmin = sqrt( safmin ) / eps2 c c compute the eigenvalues and eigenvectors of the tridiagonal c matrix. c c$$ if( icompz.eq.2 ) c$$$ $ call dlaset( 'full', n, n, zero, one, z, ldz ) c c *** New starting with version 2.5 *** c if ( icompz .eq. 2 ) then do 5 j = 1, n-1 z(j) = zero 5 continue z( n ) = one end if c ************************************* c nmaxit = n*maxit jtot = 0 c c determine where the matrix splits and choose ql or qr iteration c for each block, according to whether top or bottom diagonal c element is smaller. c l1 = 1 nm1 = n - 1 c 10 continue if( l1.gt.n ) $ go to 160 if( l1.gt.1 ) $ e( l1-1 ) = zero if( l1.le.nm1 ) then do 20 m = l1, nm1 tst = abs( e( m ) ) if( tst.eq.zero ) $ go to 30 if( tst.le.( sqrt( abs( d( m ) ) )*sqrt( abs( d( m+ $ 1 ) ) ) )*eps ) then e( m ) = zero go to 30 end if 20 continue end if m = n c 30 continue l = l1 lsv = l lend = m lendsv = lend l1 = m + 1 if( lend.eq.l ) $ go to 10 c c scale submatrix in rows and columns l to lend c anorm = dlanst( 'i', lend-l+1, d( l ), e( l ) ) iscale = 0 if( anorm.eq.zero ) $ go to 10 if( anorm.gt.ssfmax ) then iscale = 1 call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l+1, 1, d( l ), n, $ info ) call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l, 1, e( l ), n, $ info ) else if( anorm.lt.ssfmin ) then iscale = 2 call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l+1, 1, d( l ), n, $ info ) call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l, 1, e( l ), n, $ info ) end if c c choose between ql and qr iteration c if( abs( d( lend ) ).lt.abs( d( l ) ) ) then lend = lsv l = lendsv end if c if( lend.gt.l ) then c c ql iteration c c look for small subdiagonal element. c 40 continue if( l.ne.lend ) then lendm1 = lend - 1 do 50 m = l, lendm1 tst = abs( e( m ) )**2 if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m+1 ) )+ $ safmin )go to 60 50 continue end if c m = lend c 60 continue if( m.lt.lend ) $ e( m ) = zero p = d( l ) if( m.eq.l ) $ go to 80 c c if remaining matrix is 2-by-2, use dlae2 or dlaev2 c to compute its eigensystem. c if( m.eq.l+1 ) then if( icompz.gt.0 ) then call dlaev2( d( l ), e( l ), d( l+1 ), rt1, rt2, c, s ) work( l ) = c work( n-1+l ) = s c$$$ call dlasr( 'r', 'v', 'b', n, 2, work( l ), c$$$ $ work( n-1+l ), z( 1, l ), ldz ) c c *** New starting with version 2.5 *** c tst = z(l+1) z(l+1) = c*tst - s*z(l) z(l) = s*tst + c*z(l) c ************************************* else call dlae2( d( l ), e( l ), d( l+1 ), rt1, rt2 ) end if d( l ) = rt1 d( l+1 ) = rt2 e( l ) = zero l = l + 2 if( l.le.lend ) $ go to 40 go to 140 end if c if( jtot.eq.nmaxit ) $ go to 140 jtot = jtot + 1 c c form shift. c g = ( d( l+1 )-p ) / ( two*e( l ) ) r = dlapy2( g, one ) g = d( m ) - p + ( e( l ) / ( g+sign( r, g ) ) ) c s = one c = one p = zero c c inner loop c mm1 = m - 1 do 70 i = mm1, l, -1 f = s*e( i ) b = c*e( i ) call dlartg( g, f, c, s, r ) if( i.ne.m-1 ) $ e( i+1 ) = r g = d( i+1 ) - p r = ( d( i )-g )*s + two*c*b p = s*r d( i+1 ) = g + p g = c*r - b c c if eigenvectors are desired, then save rotations. c if( icompz.gt.0 ) then work( i ) = c work( n-1+i ) = -s end if c 70 continue c c if eigenvectors are desired, then apply saved rotations. c if( icompz.gt.0 ) then mm = m - l + 1 c$$$ call dlasr( 'r', 'v', 'b', n, mm, work( l ), work( n-1+l ), c$$$ $ z( 1, l ), ldz ) c c *** New starting with version 2.5 *** c call dlasr( 'r', 'v', 'b', 1, mm, work( l ), & work( n-1+l ), z( l ), 1 ) c ************************************* end if c d( l ) = d( l ) - p e( l ) = g go to 40 c c eigenvalue found. c 80 continue d( l ) = p c l = l + 1 if( l.le.lend ) $ go to 40 go to 140 c else c c qr iteration c c look for small superdiagonal element. c 90 continue if( l.ne.lend ) then lendp1 = lend + 1 do 100 m = l, lendp1, -1 tst = abs( e( m-1 ) )**2 if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m-1 ) )+ $ safmin )go to 110 100 continue end if c m = lend c 110 continue if( m.gt.lend ) $ e( m-1 ) = zero p = d( l ) if( m.eq.l ) $ go to 130 c c if remaining matrix is 2-by-2, use dlae2 or dlaev2 c to compute its eigensystem. c if( m.eq.l-1 ) then if( icompz.gt.0 ) then call dlaev2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2, c, s ) c$$$ work( m ) = c c$$$ work( n-1+m ) = s c$$$ call dlasr( 'r', 'v', 'f', n, 2, work( m ), c$$$ $ work( n-1+m ), z( 1, l-1 ), ldz ) c c *** New starting with version 2.5 *** c tst = z(l) z(l) = c*tst - s*z(l-1) z(l-1) = s*tst + c*z(l-1) c ************************************* else call dlae2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2 ) end if d( l-1 ) = rt1 d( l ) = rt2 e( l-1 ) = zero l = l - 2 if( l.ge.lend ) $ go to 90 go to 140 end if c if( jtot.eq.nmaxit ) $ go to 140 jtot = jtot + 1 c c form shift. c g = ( d( l-1 )-p ) / ( two*e( l-1 ) ) r = dlapy2( g, one ) g = d( m ) - p + ( e( l-1 ) / ( g+sign( r, g ) ) ) c s = one c = one p = zero c c inner loop c lm1 = l - 1 do 120 i = m, lm1 f = s*e( i ) b = c*e( i ) call dlartg( g, f, c, s, r ) if( i.ne.m ) $ e( i-1 ) = r g = d( i ) - p r = ( d( i+1 )-g )*s + two*c*b p = s*r d( i ) = g + p g = c*r - b c c if eigenvectors are desired, then save rotations. c if( icompz.gt.0 ) then work( i ) = c work( n-1+i ) = s end if c 120 continue c c if eigenvectors are desired, then apply saved rotations. c if( icompz.gt.0 ) then mm = l - m + 1 c$$$ call dlasr( 'r', 'v', 'f', n, mm, work( m ), work( n-1+m ), c$$$ $ z( 1, m ), ldz ) c c *** New starting with version 2.5 *** c call dlasr( 'r', 'v', 'f', 1, mm, work( m ), work( n-1+m ), & z( m ), 1 ) c ************************************* end if c d( l ) = d( l ) - p e( lm1 ) = g go to 90 c c eigenvalue found. c 130 continue d( l ) = p c l = l - 1 if( l.ge.lend ) $ go to 90 go to 140 c end if c c undo scaling if necessary c 140 continue if( iscale.eq.1 ) then call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, $ d( lsv ), n, info ) call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv, 1, e( lsv ), $ n, info ) else if( iscale.eq.2 ) then call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, $ d( lsv ), n, info ) call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv, 1, e( lsv ), $ n, info ) end if c c check for no convergence to an eigenvalue after a total c of n*maxit iterations. c if( jtot.lt.nmaxit ) $ go to 10 do 150 i = 1, n - 1 if( e( i ).ne.zero ) $ info = info + 1 150 continue go to 190 c c order eigenvalues and eigenvectors. c 160 continue if( icompz.eq.0 ) then c c use quick sort c call dlasrt( 'i', n, d, info ) c else c c use selection sort to minimize swaps of eigenvectors c do 180 ii = 2, n i = ii - 1 k = i p = d( i ) do 170 j = ii, n if( d( j ).lt.p ) then k = j p = d( j ) end if 170 continue if( k.ne.i ) then d( k ) = d( i ) d( i ) = p c$$$ call dswap( n, z( 1, i ), 1, z( 1, k ), 1 ) c *** New starting with version 2.5 *** c p = z(k) z(k) = z(i) z(i) = p c ************************************* end if 180 continue end if c 190 continue return c c %---------------% c | End of igraphdstqrb | c %---------------% c end igraph/src/hrg_rbtree.h0000644000176000001440000001346712325527073014660 0ustar ripleyusers/* -*- mode: C++ -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // **************************************************************************************************** // *** COPYRIGHT NOTICE ******************************************************************************* // rbtree - red-black tree (self-balancing binary tree data structure) // Copyright (C) 2004 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // **************************************************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute // Created : Spring 2004 // Modified : many, many times // // **************************************************************************************************** #ifndef IGRAPH_HRG_RBTREE #define IGRAPH_HRG_RBTREE #include using namespace std; namespace fitHRG { // ******** Basic Structures ********************************************* #ifndef IGRAPH_HRG_LIST #define IGRAPH_HRG_LIST class list { public: int x; // stored elementd in linked-list list* next; // pointer to next elementd list(): x(-1), next(0) { } ~list() { } }; #endif class keyValuePair { public: int x; // elementrb key (int) int y; // stored value (int) keyValuePair* next; // linked-list pointer keyValuePair(): x(-1), y(-1), next(0) { } ~keyValuePair() { } }; // ******** Tree elementrb Class ***************************************** class elementrb { public: int key; // search key (int) int value; // stored value (int) bool color; // F: BLACK, T: RED short int mark; // marker elementrb *parent; // pointer to parent node elementrb *left; // pointer for left subtree elementrb *right; // pointer for right subtree elementrb(): key(-1), value(-1), color(false), mark(0), parent(0), left(0), right(0) { } ~elementrb() { } }; // ******** Red-Black Tree Class ***************************************** // This vector implementation is a red-black balanced binary tree data // structure. It provides find a stored elementrb in time O(log n), // find the maximum elementrb in time O(1), delete an elementrb in // time O(log n), and insert an elementrb in time O(log n). // // Note that the key=0 is assumed to be a special value, and thus you // cannot insert such an item. Beware of this limitation. class rbtree { private: elementrb* root; // binary tree root elementrb* leaf; // all leaf nodes int support; // number of nodes in the tree void rotateLeft(elementrb *x); // left-rotation operator void rotateRight(elementrb *y); // right-rotation operator void insertCleanup(elementrb *z); // house-keeping after insertion void deleteCleanup(elementrb *x); // house-keeping after deletion keyValuePair* returnSubtreeAsList(elementrb *z, keyValuePair *head); void deleteSubTree(elementrb *z); // delete subtree rooted at z elementrb* returnMinKey(elementrb *z); // returns minimum of subtree // rooted at z elementrb* returnSuccessor(elementrb *z); // returns successor of z's key public: rbtree(); ~rbtree(); // default constructor/destructor // returns value associated with searchKey int returnValue(const int searchKey); // returns T if searchKey found, and points foundNode at the // corresponding node elementrb* findItem(const int searchKey); // insert a new key with stored value void insertItem(int newKey, int newValue); // selete a node with given key void deleteItem(int killKey); // replace value of a node with given key void replaceItem(int key, int newValue); // increment the value of the given key void incrementValue(int key); // delete the entire tree void deleteTree(); // return array of keys in tree int* returnArrayOfKeys(); // return list of keys in tree list* returnListOfKeys(); // return the tree as a list of keyValuePairs keyValuePair* returnTreeAsList(); // returns the maximum key in the tree keyValuePair returnMaxKey(); // returns the minimum key in the tree keyValuePair returnMinKey(); // returns number of items in tree int returnNodecount(); }; } #endif igraph/src/RayVector.cpp0000755000176000001440000000433412325527072015001 0ustar ripleyusers#include "RayVector.h" #include namespace igraph { Vector::Vector() { mI = mJ = mK = 0.0; } Vector::Vector(const Point& vStartPoint, const Point& vEndPoint) { mI = vEndPoint.X() - vStartPoint.X(); mJ = vEndPoint.Y() - vStartPoint.Y(); mK = vEndPoint.Z() - vStartPoint.Z(); } Vector::Vector(double vI, double vJ, double vK) { mI = vI; mJ = vJ; mK = vK; } Vector::~Vector() {} // returns a unit vector of this vector Vector Vector::Normalize() const { double magnitude = Magnitude(); return Vector(mI/magnitude, mJ/magnitude, mK/magnitude); } void Vector::NormalizeThis() { *this = Normalize(); } void Vector::ReverseDirection() { *this = *this * -1.0; } bool Vector::IsSameDirection(const Vector& rVector) const { return ( this->Normalize().Dot(rVector.Normalize()) > 0.0 ); } void Vector::I(double vI) { mI = vI; } double Vector::I() const { return mI; } void Vector::J(double vJ) { mJ = vJ; } double Vector::J() const { return mJ; } void Vector::K(double vK) { mK = vK; } double Vector::K() const { return mK; } // returns the dot product of this and rVector double Vector::Dot(const Vector& rVector) const { return mI*rVector.I() + mJ*rVector.J() + mK*rVector.K(); } // returns the cross product of this and vVector Vector Vector::Cross(const Vector& rVector) const { return Vector(mJ*rVector.K() - rVector.J()*mK, -1.0*(mI*rVector.K() - rVector.I()*mK), mI*rVector.J() - rVector.I()*mJ); } // returns the sum of this vector with another vector Vector Vector::operator+ (Vector vRhs) const { return Vector(mI + vRhs.I(), mJ + vRhs.J(), mK + vRhs.K()); } // returns the sume of a vector and a Point Point Vector::operator+ (Point vRhs) const { return Point(mI + vRhs.X(), mJ + vRhs.Y(), mK + vRhs.Z()); } // returns the difference of two vectors Vector Vector::operator- (Vector vRhs) const { return Vector(mI-vRhs.I(), mJ-vRhs.J(), mK-vRhs.K()); } // returns multiplication of a scalar with this vector Vector Vector::operator* (double vRhs) const { return Vector(mI*vRhs, mJ*vRhs, mK*vRhs); } // converts this vector to a point Point Vector::ToPoint() const { return Point(mI,mJ,mK); } // returns the magnitude double Vector::Magnitude() const { return sqrt(mI*mI + mJ*mJ + mK*mK); } } // namespace igraph igraph/src/foreign-dl-header.h0000644000176000001440000000243112325527073015776 0ustar ripleyusers/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_types_internal.h" typedef enum { IGRAPH_DL_MATRIX, IGRAPH_DL_EDGELIST1, IGRAPH_DL_NODELIST1 } igraph_i_dl_type_t; typedef struct { void *scanner; int eof; int mode; long int n; long int from, to; igraph_vector_t edges; igraph_vector_t weights; igraph_strvector_t labels; igraph_trie_t trie; igraph_i_dl_type_t type; char errmsg[300]; } igraph_i_dl_parsedata_t; igraph/src/igraph_complex.h0000644000176000001440000000767612325527073015543 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_COMPLEX_H #define IGRAPH_COMPLEX_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" __BEGIN_DECLS typedef struct igraph_complex_t { igraph_real_t dat[2]; } igraph_complex_t; #define IGRAPH_REAL(x) ((x).dat[0]) #define IGRAPH_IMAG(x) ((x).dat[1]) #define IGRAPH_COMPLEX_EQ(x,y) ((x).dat[0]==(y).dat[0] && (x).dat[1]==(y).dat[1]) igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y); igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta); igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, igraph_complex_t z2, igraph_real_t tol); igraph_real_t igraph_complex_mod(igraph_complex_t z); igraph_real_t igraph_complex_arg(igraph_complex_t z); igraph_real_t igraph_complex_abs(igraph_complex_t z); igraph_real_t igraph_complex_logabs(igraph_complex_t z); igraph_complex_t igraph_complex_add(igraph_complex_t z1, igraph_complex_t z2); igraph_complex_t igraph_complex_sub(igraph_complex_t z1, igraph_complex_t z2); igraph_complex_t igraph_complex_mul(igraph_complex_t z1, igraph_complex_t z2); igraph_complex_t igraph_complex_div(igraph_complex_t z1, igraph_complex_t z2); igraph_complex_t igraph_complex_add_real(igraph_complex_t z, igraph_real_t x); igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, igraph_real_t y); igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, igraph_real_t x); igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, igraph_real_t y); igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, igraph_real_t x); igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, igraph_real_t y); igraph_complex_t igraph_complex_div_real(igraph_complex_t z, igraph_real_t x); igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, igraph_real_t y); igraph_complex_t igraph_complex_conj(igraph_complex_t z); igraph_complex_t igraph_complex_neg(igraph_complex_t z); igraph_complex_t igraph_complex_inv(igraph_complex_t z); igraph_complex_t igraph_complex_sqrt(igraph_complex_t z); igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x); igraph_complex_t igraph_complex_exp(igraph_complex_t z); igraph_complex_t igraph_complex_pow(igraph_complex_t z1, igraph_complex_t z2); igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, igraph_real_t x); igraph_complex_t igraph_complex_log(igraph_complex_t z); igraph_complex_t igraph_complex_log10(igraph_complex_t z); igraph_complex_t igraph_complex_log_b(igraph_complex_t z, igraph_complex_t b); igraph_complex_t igraph_complex_sin(igraph_complex_t z); igraph_complex_t igraph_complex_cos(igraph_complex_t z); igraph_complex_t igraph_complex_tan(igraph_complex_t z); igraph_complex_t igraph_complex_sec(igraph_complex_t z); igraph_complex_t igraph_complex_csc(igraph_complex_t z); igraph_complex_t igraph_complex_cot(igraph_complex_t z); __END_DECLS #endif igraph/src/hacks.c0000644000176000001440000000317512325527073013614 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "igraph_hacks_internal.h" /* These are implementations of common C functions that may be missing from some * compilers; for instance, icc does not provide stpcpy so we implement it * here. */ /** * Drop-in replacement for strdup. * Used only in compilers that do not have strdup or _strdup */ char* igraph_i_strdup(const char *s) { size_t n = strlen(s) + 1; char* result = (char*)malloc(sizeof(char) * n); if (result) memcpy(result, s, n); return result; } /** * Drop-in replacement for stpcpy. * Used only in compilers that do not have stpcpy */ char* igraph_i_stpcpy(char* s1, const char* s2) { char* result = strcpy(s1, s2); return result + strlen(s1); } igraph/src/cs_etree.c0000644000176000001440000000433512325527073014313 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* compute the etree of A (using triu(A), or A'A without forming A'A */ CS_INT *cs_etree (const cs *A, CS_INT ata) { CS_INT i, k, p, m, n, inext, *Ap, *Ai, *w, *parent, *ancestor, *prev ; if (!CS_CSC (A)) return (NULL) ; /* check inputs */ m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; parent = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ w = cs_malloc (n + (ata ? m : 0), sizeof (CS_INT)) ; /* get workspace */ if (!w || !parent) return (cs_idone (parent, NULL, w, 0)) ; ancestor = w ; prev = w + n ; if (ata) for (i = 0 ; i < m ; i++) prev [i] = -1 ; for (k = 0 ; k < n ; k++) { parent [k] = -1 ; /* node k has no parent yet */ ancestor [k] = -1 ; /* nor does k have an ancestor */ for (p = Ap [k] ; p < Ap [k+1] ; p++) { i = ata ? (prev [Ai [p]]) : (Ai [p]) ; for ( ; i != -1 && i < k ; i = inext) /* traverse from i to k */ { inext = ancestor [i] ; /* inext = ancestor of i */ ancestor [i] = k ; /* path compression */ if (inext == -1) parent [i] = k ; /* no anc., parent is k */ } if (ata) prev [Ai [p]] = k ; } } return (cs_idone (parent, NULL, w, 1)) ; } igraph/src/dnaup2.f0000644000176000001440000007606512325527073013727 0ustar ripleyusersc\BeginDoc c c\Name: igraphdnaup2 c c\Description: c Intermediate level interface called by igraphdnaupd. c c\Usage: c call igraphdnaup2 c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, c ISHIFT, MXITER, V, LDV, H, LDH, RITZR, RITZI, BOUNDS, c Q, LDQ, WORKL, IPNTR, WORKD, INFO ) c c\Arguments c c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdnaupd. c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdnaupd. c c NP Integer. (INPUT/OUTPUT) c Contains the number of implicit shifts to apply during c each Arnoldi iteration. c If ISHIFT=1, NP is adjusted dynamically at each iteration c to accelerate convergence and prevent stagnation. c This is also roughly equal to the number of matrix-vector c products (involving the operator OP) per Arnoldi iteration. c The logic for adjusting is contained within the current c subroutine. c If ISHIFT=0, NP is the number of shifts the user needs c to provide via reverse comunication. 0 < NP < NCV-NEV. c NP may be less than NCV-NEV for two reasons. The first, is c to keep complex conjugate pairs of "wanted" Ritz values c together. The igraphsecond, is that a leading block of the current c upper Hessenberg matrix has split off and contains "unwanted" c Ritz values. c Upon termination of the IRA iteration, NP contains the number c of "converged" wanted Ritz values. c c IUPD Integer. (INPUT) c IUPD .EQ. 0: use explicit restart instead implicit update. c IUPD .NE. 0: use implicit update. c c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) c The Arnoldi basis vectors are returned in the first NEV c columns of V. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c H Double precision (NEV+NP) by (NEV+NP) array. (OUTPUT) c H is used to store the generated upper Hessenberg matrix c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c RITZR, Double precision arrays of length NEV+NP. (OUTPUT) c RITZI RITZR(1:NEV) (resp. RITZI(1:NEV)) contains the real (resp. c imaginary) part of the computed Ritz values of OP. c c BOUNDS Double precision array of length NEV+NP. (OUTPUT) c BOUNDS(1:NEV) contain the error bounds corresponding to c the computed Ritz values. c c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) c Private (replicated) work array used to accumulate the c rotation in the shift application step. c c LDQ Integer. (INPUT) c Leading dimension of Q exactly as declared in the calling c program. c c WORKL Double precision work array of length at least c (NEV+NP)**2 + 3*(NEV+NP). (INPUT/WORKSPACE) c Private (replicated) array on each PE or array allocated on c the front end. It is used in shifts calculation, shifts c application and convergence checking. c c On exit, the last 3*(NEV+NP) locations of WORKL contain c the Ritz values (real,imaginary) and associated Ritz c estimates of the current Hessenberg matrix. They are c listed in the same order as returned from igraphdneigh. c c If ISHIFT .EQ. O and IDO .EQ. 3, the first 2*NP locations c of WORKL are used in reverse communication to hold the user c supplied shifts. c c IPNTR Integer array of length 3. (OUTPUT) c Pointer to mark the starting locations in the WORKD for c vectors used by the Arnoldi iteration. c ------------------------------------------------------------- c IPNTR(1): pointer to the current operand vector X. c IPNTR(2): pointer to the current result vector Y. c IPNTR(3): pointer to the vector B * X when used in the c shift-and-invert mode. X is the current operand. c ------------------------------------------------------------- c c WORKD Double precision work array of length 3*N. (WORKSPACE) c Distributed array to be used in the basic Arnoldi iteration c for reverse communication. The user should not use WORKD c as temporary workspace during the iteration !!!!!!!!!! c See Data Distribution Note in DNAUPD. c c INFO Integer. (INPUT/OUTPUT) c If INFO .EQ. 0, a randomly initial residual vector is used. c If INFO .NE. 0, RESID contains the initial residual vector, c possibly from a previous run. c Error flag on output. c = 0: Normal return. c = 1: Maximum number of iterations taken. c All possible eigenvalues of OP has been found. c NP returns the number of converged Ritz values. c = 2: No shifts could be applied. c = -8: Error return from LAPACK eigenvalue calculation; c This should never happen. c = -9: Starting vector is zero. c = -9999: Could not build an Arnoldi factorization. c Size that was built in returned in NP. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c c\Routines called: c igraphdgetv0 ARPACK initial vector generation routine. c igraphdnaitr ARPACK Arnoldi factorization routine. c igraphdnapps ARPACK application of implicit shifts routine. c igraphdnconv ARPACK convergence of Ritz values routine. c igraphdneigh ARPACK compute Ritz values and error bounds routine. c igraphdngets ARPACK reorder Ritz values and error bounds routine. c igraphdsortc ARPACK sorting routine. c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdmout ARPACK utility routine that prints matrices c igraphdvout ARPACK utility routine that prints vectors. c dlamch LAPACK routine that determines machine constants. c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. c dcopy Level 1 BLAS that copies one vector to another . c ddot Level 1 BLAS that computes the scalar product of two vectors. c dnrm2 Level 1 BLAS that computes the norm of a vector. c dswap Level 1 BLAS that swaps two vectors. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\SCCS Information: @(#) c FILE: naup2.F SID: 2.4 DATE OF SID: 7/30/96 RELEASE: 2 c c\Remarks c 1. None c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdnaup2 & ( ido, bmat, n, which, nev, np, tol, resid, mode, iupd, & ishift, mxiter, v, ldv, h, ldh, ritzr, ritzi, bounds, & q, ldq, workl, ipntr, workd, info ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat*1, which*2 integer ido, info, ishift, iupd, mode, ldh, ldq, ldv, mxiter, & n, nev, np Double precision & tol c c %-----------------% c | Array Arguments | c %-----------------% c integer ipntr(13) Double precision & bounds(nev+np), h(ldh,nev+np), q(ldq,nev+np), resid(n), & ritzi(nev+np), ritzr(nev+np), v(ldv,nev+np), & workd(3*n), workl( (nev+np)*(nev+np+3) ) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c character wprime*2 logical cnorm, getv0, initv, update, ushift integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, & np0, nptemp, numcnv Double precision & rnorm, temp, eps23 c c %-----------------------% c | Local array arguments | c %-----------------------% c integer kp(4) save c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, igraphdgetv0, igraphdnaitr, igraphdnconv, & igraphdneigh, igraphdngets, igraphdnapps, & igraphdvout, igraphivout, igraphsecond c c %--------------------% c | External Functions | c %--------------------% c Double precision & ddot, dnrm2, dlapy2, dlamch external ddot, dnrm2, dlapy2, dlamch c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic min, max, abs, sqrt c c %-----------------------% c | Executable Statements | c %-----------------------% c if (ido .eq. 0) then c call igraphsecond (t0) c msglvl = mnaup2 c c %-------------------------------------% c | Get the machine dependent constant. | c %-------------------------------------% c eps23 = dlamch('Epsilon-Machine') eps23 = eps23**(2.0D+0 / 3.0D+0) c nev0 = nev np0 = np c c %-------------------------------------% c | kplusp is the bound on the largest | c | Lanczos factorization built. | c | nconv is the current number of | c | "converged" eigenvlues. | c | iter is the counter on the current | c | iteration step. | c %-------------------------------------% c kplusp = nev + np nconv = 0 iter = 0 c c %---------------------------------------% c | Set flags for computing the first NEV | c | steps of the Arnoldi factorization. | c %---------------------------------------% c getv0 = .true. update = .false. ushift = .false. cnorm = .false. c if (info .ne. 0) then c c %--------------------------------------------% c | User provides the initial residual vector. | c %--------------------------------------------% c initv = .true. info = 0 else initv = .false. end if end if c c %---------------------------------------------% c | Get a possibly random starting vector and | c | force it into the range of the operator OP. | c %---------------------------------------------% c 10 continue c if (getv0) then call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, & rnorm, ipntr, workd, info) c if (ido .ne. 99) go to 9000 c if (rnorm .eq. zero) then c c %-----------------------------------------% c | The initial vector is zero. Error exit. | c %-----------------------------------------% c info = -9 go to 1100 end if getv0 = .false. ido = 0 end if c c %-----------------------------------% c | Back from reverse communication : | c | continue with update step | c %-----------------------------------% c if (update) go to 20 c c %-------------------------------------------% c | Back from computing user specified shifts | c %-------------------------------------------% c if (ushift) go to 50 c c %-------------------------------------% c | Back from computing residual norm | c | at the end of the current iteration | c %-------------------------------------% c if (cnorm) go to 100 c c %----------------------------------------------------------% c | Compute the first NEV steps of the Arnoldi factorization | c %----------------------------------------------------------% c call igraphdnaitr (ido, bmat, n, 0, nev, mode, resid, rnorm, v, & ldv, h, ldh, ipntr, workd, info) c c %---------------------------------------------------% c | ido .ne. 99 implies use of reverse communication | c | to compute operations involving OP and possibly B | c %---------------------------------------------------% c if (ido .ne. 99) go to 9000 c if (info .gt. 0) then np = info mxiter = iter info = -9999 go to 1200 end if c c %--------------------------------------------------------------% c | | c | M A I N ARNOLDI I T E R A T I O N L O O P | c | Each iteration implicitly restarts the Arnoldi | c | factorization in place. | c | | c %--------------------------------------------------------------% c 1000 continue c iter = iter + 1 c if (msglvl .gt. 0) then call igraphivout (logfil, 1, iter, ndigit, & '_naup2: **** Start of major iteration number ****') end if c c %-----------------------------------------------------------% c | Compute NP additional steps of the Arnoldi factorization. | c | Adjust NP since NEV might have been updated by last call | c | to the shift application routine igraphdnapps. | c %-----------------------------------------------------------% c np = kplusp - nev c if (msglvl .gt. 1) then call igraphivout (logfil, 1, nev, ndigit, & '_naup2: The length of the current Arnoldi factorization') call igraphivout (logfil, 1, np, ndigit, & '_naup2: Extend the Arnoldi factorization by') end if c c %-----------------------------------------------------------% c | Compute NP additional steps of the Arnoldi factorization. | c %-----------------------------------------------------------% c ido = 0 20 continue update = .true. c call igraphdnaitr (ido, bmat, n, nev, np, mode, resid, rnorm, & v, ldv, h, ldh, ipntr, workd, info) c c %---------------------------------------------------% c | ido .ne. 99 implies use of reverse communication | c | to compute operations involving OP and possibly B | c %---------------------------------------------------% c if (ido .ne. 99) go to 9000 c if (info .gt. 0) then np = info mxiter = iter info = -9999 go to 1200 end if update = .false. c if (msglvl .gt. 1) then call igraphdvout (logfil, 1, rnorm, ndigit, & '_naup2: Corresponding B-norm of the residual') end if c c %--------------------------------------------------------% c | Compute the eigenvalues and corresponding error bounds | c | of the current upper Hessenberg matrix. | c %--------------------------------------------------------% c call igraphdneigh (rnorm, kplusp, h, ldh, ritzr, ritzi, bounds, & q, ldq, workl, ierr) c if (ierr .ne. 0) then info = -8 go to 1200 end if c c %----------------------------------------------------% c | Make a copy of eigenvalues and corresponding error | c | bounds obtained from igraphdneigh. | c %----------------------------------------------------% c call dcopy(kplusp, ritzr, 1, workl(kplusp**2+1), 1) call dcopy(kplusp, ritzi, 1, workl(kplusp**2+kplusp+1), 1) call dcopy(kplusp, bounds, 1, workl(kplusp**2+2*kplusp+1), 1) c c %---------------------------------------------------% c | Select the wanted Ritz values and their bounds | c | to be used in the convergence test. | c | The wanted part of the spectrum and corresponding | c | error bounds are in the last NEV loc. of RITZR, | c | RITZI and BOUNDS respectively. The variables NEV | c | and NP may be updated if the NEV-th wanted Ritz | c | value has a non zero imaginary part. In this case | c | NEV is increased by one and NP decreased by one. | c | NOTE: The last two arguments of igraphdngets are no | c | longer used as of version 2.1. | c %---------------------------------------------------% c nev = nev0 np = np0 numcnv = nev call igraphdngets (ishift, which, nev, np, ritzr, ritzi, & bounds, workl, workl(np+1)) if (nev .eq. nev0+1) numcnv = nev0+1 c c %-------------------% c | Convergence test. | c %-------------------% c call dcopy (nev, bounds(np+1), 1, workl(2*np+1), 1) call igraphdnconv (nev, ritzr(np+1), ritzi(np+1), & workl(2*np+1), tol, nconv) c if (msglvl .gt. 2) then kp(1) = nev kp(2) = np kp(3) = numcnv kp(4) = nconv call igraphivout (logfil, 4, kp, ndigit, & '_naup2: NEV, NP, NUMCNV, NCONV are') call igraphdvout (logfil, kplusp, ritzr, ndigit, & '_naup2: Real part of the eigenvalues of H') call igraphdvout (logfil, kplusp, ritzi, ndigit, & '_naup2: Imaginary part of the eigenvalues of H') call igraphdvout (logfil, kplusp, bounds, ndigit, & '_naup2: Ritz estimates of the current NCV Ritz values') end if c c %---------------------------------------------------------% c | Count the number of unwanted Ritz values that have zero | c | Ritz estimates. If any Ritz estimates are equal to zero | c | then a leading block of H of order equal to at least | c | the number of Ritz values with zero Ritz estimates has | c | split off. None of these Ritz values may be removed by | c | shifting. Decrease NP the number of shifts to apply. If | c | no shifts may be applied, then prepare to exit | c %---------------------------------------------------------% c nptemp = np do 30 j=1, nptemp if (bounds(j) .eq. zero) then np = np - 1 nev = nev + 1 end if 30 continue c if ( (nconv .ge. numcnv) .or. & (iter .gt. mxiter) .or. & (np .eq. 0) ) then c if (msglvl .gt. 4) then call igraphdvout(logfil, kplusp, workl(kplusp**2+1), & ndigit, & '_naup2: Real part of the eig computed by _neigh:') call igraphdvout(logfil, kplusp, & workl(kplusp**2+kplusp+1), ndigit, & '_naup2: Imag part of the eig computed by _neigh:') call igraphdvout(logfil, kplusp, & workl(kplusp**2+kplusp*2+1), ndigit, & '_naup2: Ritz eistmates computed by _neigh:') end if c c %------------------------------------------------% c | Prepare to exit. Put the converged Ritz values | c | and corresponding bounds in RITZ(1:NCONV) and | c | BOUNDS(1:NCONV) respectively. Then sort. Be | c | careful when NCONV > NP | c %------------------------------------------------% c c %------------------------------------------% c | Use h( 3,1 ) as storage to communicate | c | rnorm to _neupd if needed | c %------------------------------------------% h(3,1) = rnorm c c %----------------------------------------------% c | To be consistent with igraphdngets, we first do a | c | pre-processing sort in order to keep complex | c | conjugate pairs together. This is similar | c | to the pre-processing sort used in igraphdngets | c | except that the sort is done in the opposite | c | order. | c %----------------------------------------------% c if (which .eq. 'LM') wprime = 'SR' if (which .eq. 'SM') wprime = 'LR' if (which .eq. 'LR') wprime = 'SM' if (which .eq. 'SR') wprime = 'LM' if (which .eq. 'LI') wprime = 'SM' if (which .eq. 'SI') wprime = 'LM' c call igraphdsortc (wprime, .true., kplusp, ritzr, ritzi, & bounds) c c %----------------------------------------------% c | Now sort Ritz values so that converged Ritz | c | values appear within the first NEV locations | c | of ritzr, ritzi and bounds, and the most | c | desired one appears at the front. | c %----------------------------------------------% c if (which .eq. 'LM') wprime = 'SM' if (which .eq. 'SM') wprime = 'LM' if (which .eq. 'LR') wprime = 'SR' if (which .eq. 'SR') wprime = 'LR' if (which .eq. 'LI') wprime = 'SI' if (which .eq. 'SI') wprime = 'LI' c call igraphdsortc(wprime, .true., kplusp, ritzr, ritzi, & bounds) c c %--------------------------------------------------% c | Scale the Ritz estimate of each Ritz value | c | by 1 / max(eps23,magnitude of the Ritz value). | c %--------------------------------------------------% c do 35 j = 1, nev0 temp = max(eps23,dlapy2(ritzr(j), & ritzi(j))) bounds(j) = bounds(j)/temp 35 continue c c %----------------------------------------------------% c | Sort the Ritz values according to the scaled Ritz | c | esitmates. This will push all the converged ones | c | towards the front of ritzr, ritzi, bounds | c | (in the case when NCONV < NEV.) | c %----------------------------------------------------% c wprime = 'LR' call igraphdsortc(wprime, .true., nev0, bounds, ritzr, & ritzi) c c %----------------------------------------------% c | Scale the Ritz estimate back to its original | c | value. | c %----------------------------------------------% c do 40 j = 1, nev0 temp = max(eps23, dlapy2(ritzr(j), & ritzi(j))) bounds(j) = bounds(j)*temp 40 continue c c %------------------------------------------------% c | Sort the converged Ritz values again so that | c | the "threshold" value appears at the front of | c | ritzr, ritzi and bound. | c %------------------------------------------------% c call igraphdsortc(which, .true., nconv, ritzr, ritzi, & bounds) c if (msglvl .gt. 1) then call igraphdvout (logfil, kplusp, ritzr, ndigit, & '_naup2: Sorted real part of the eigenvalues') call igraphdvout (logfil, kplusp, ritzi, ndigit, & '_naup2: Sorted imaginary part of the eigenvalues') call igraphdvout (logfil, kplusp, bounds, ndigit, & '_naup2: Sorted ritz estimates.') end if c c %------------------------------------% c | Max iterations have been exceeded. | c %------------------------------------% c if (iter .gt. mxiter .and. nconv .lt. numcnv) info = 1 c c %---------------------% c | No shifts to apply. | c %---------------------% c if (np .eq. 0 .and. nconv .lt. numcnv) info = 2 c np = nconv go to 1100 c else if ( (nconv .lt. numcnv) .and. (ishift .eq. 1) ) then c c %-------------------------------------------------% c | Do not have all the requested eigenvalues yet. | c | To prevent possible stagnation, adjust the size | c | of NEV. | c %-------------------------------------------------% c nevbef = nev nev = nev + min(nconv, np/2) if (nev .eq. 1 .and. kplusp .ge. 6) then nev = kplusp / 2 else if (nev .eq. 1 .and. kplusp .gt. 3) then nev = 2 end if np = kplusp - nev c c %---------------------------------------% c | If the size of NEV was just increased | c | resort the eigenvalues. | c %---------------------------------------% c if (nevbef .lt. nev) & call igraphdngets (ishift, which, nev, np, ritzr, ritzi, & bounds, workl, workl(np+1)) c end if c if (msglvl .gt. 0) then call igraphivout (logfil, 1, nconv, ndigit, & '_naup2: no. of "converged" Ritz values at this iter.') if (msglvl .gt. 1) then kp(1) = nev kp(2) = np call igraphivout (logfil, 2, kp, ndigit, & '_naup2: NEV and NP are') call igraphdvout (logfil, nev, ritzr(np+1), ndigit, & '_naup2: "wanted" Ritz values -- real part') call igraphdvout (logfil, nev, ritzi(np+1), ndigit, & '_naup2: "wanted" Ritz values -- imag part') call igraphdvout (logfil, nev, bounds(np+1), ndigit, & '_naup2: Ritz estimates of the "wanted" values ') end if end if c if (ishift .eq. 0) then c c %-------------------------------------------------------% c | User specified shifts: reverse comminucation to | c | compute the shifts. They are returned in the first | c | 2*NP locations of WORKL. | c %-------------------------------------------------------% c ushift = .true. ido = 3 go to 9000 end if c 50 continue c c %------------------------------------% c | Back from reverse communication; | c | User specified shifts are returned | c | in WORKL(1:2*NP) | c %------------------------------------% c ushift = .false. c if ( ishift .eq. 0 ) then c c %----------------------------------% c | Move the NP shifts from WORKL to | c | RITZR, RITZI to free up WORKL | c | for non-exact shift case. | c %----------------------------------% c call dcopy (np, workl, 1, ritzr, 1) call dcopy (np, workl(np+1), 1, ritzi, 1) end if c if (msglvl .gt. 2) then call igraphivout (logfil, 1, np, ndigit, & '_naup2: The number of shifts to apply ') call igraphdvout (logfil, np, ritzr, ndigit, & '_naup2: Real part of the shifts') call igraphdvout (logfil, np, ritzi, ndigit, & '_naup2: Imaginary part of the shifts') if ( ishift .eq. 1 ) & call igraphdvout (logfil, np, bounds, ndigit, & '_naup2: Ritz estimates of the shifts') end if c c %---------------------------------------------------------% c | Apply the NP implicit shifts by QR bulge chasing. | c | Each shift is applied to the whole upper Hessenberg | c | matrix H. | c | The first 2*N locations of WORKD are used as workspace. | c %---------------------------------------------------------% c call igraphdnapps (n, nev, np, ritzr, ritzi, v, ldv, & h, ldh, resid, q, ldq, workl, workd) c c %---------------------------------------------% c | Compute the B-norm of the updated residual. | c | Keep B*RESID in WORKD(1:N) to be used in | c | the first step of the next call to igraphdnaitr. | c %---------------------------------------------% c cnorm = .true. call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, resid, 1, workd(n+1), 1) ipntr(1) = n + 1 ipntr(2) = 1 ido = 2 c c %----------------------------------% c | Exit in order to compute B*RESID | c %----------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd, 1) end if c 100 continue c c %----------------------------------% c | Back from reverse communication; | c | WORKD(1:N) := B*RESID | c %----------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c if (bmat .eq. 'G') then rnorm = ddot (n, resid, 1, workd, 1) rnorm = sqrt(abs(rnorm)) else if (bmat .eq. 'I') then rnorm = dnrm2(n, resid, 1) end if cnorm = .false. c if (msglvl .gt. 2) then call igraphdvout (logfil, 1, rnorm, ndigit, & '_naup2: B-norm of residual for compressed factorization') call igraphdmout (logfil, nev, nev, h, ldh, ndigit, & '_naup2: Compressed upper Hessenberg matrix H') end if c go to 1000 c c %---------------------------------------------------------------% c | | c | E N D O F M A I N I T E R A T I O N L O O P | c | | c %---------------------------------------------------------------% c 1100 continue c mxiter = iter nev = numcnv c 1200 continue ido = 99 c c %------------% c | Error Exit | c %------------% c call igraphsecond (t1) tnaup2 = t1 - t0 c 9000 continue c c %---------------% c | End of igraphdnaup2 | c %---------------% c return end igraph/src/bliss_kstack.hh0000644000176000001440000000416412325372072015350 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_KSTACK_H #define BLISS_KSTACK_H #include "bliss_defs.hh" #include // malloc namespace igraph { /* * A stack with fixed capacity */ template class KStack { public: KStack(); KStack(int k); ~KStack(); void init(int k); bool is_empty() const {return(cursor == entries); } Type top() const {DEBUG_ASSERT(cursor > entries); return *cursor; } Type pop() { DEBUG_ASSERT(cursor > entries); Type obj = *cursor; cursor--; return obj; } void push(Type obj) { DEBUG_ASSERT(cursor < entries + kapacity); cursor++; *cursor = obj; } void clean() {cursor = entries; } unsigned int size() const {return(cursor - entries); } Type element_at(unsigned int i) { assert(i < size()); return entries[i+1]; } int capacity() {return kapacity; } private: int kapacity; Type *entries; Type *cursor; }; template KStack::KStack() { kapacity = 0; entries = 0; cursor = 0; } template KStack::KStack(int k) { assert(k > 0); kapacity = k; entries = (Type*)malloc((k+1) * sizeof(Type)); cursor = entries; } template void KStack::init(int k) { assert(k > 0); if(entries) free(entries); kapacity = k; entries = (Type*)malloc((k+1) * sizeof(Type)); cursor = entries; } template KStack::~KStack() { free(entries); } } #endif igraph/src/Color.cpp0000755000176000001440000000320512325527072014135 0ustar ripleyusers#include "Color.h" #include "unit_limiter.h" namespace igraph { Color::Color() { } Color::Color(double vRed, double vGreen, double vBlue, double vTransparent) { Red(vRed); Green(vGreen); Blue(vBlue); Transparent(vTransparent); } Color::~Color() { } // returns multiplication of a scalar with this vector Color Color::operator* (double vRhs) const { return Color(mRed*vRhs, mGreen*vRhs, mBlue*vRhs, mTransparent); } // returns the addition of this color with another color Color Color::operator+ (const Color& vRhs) const { double trans=Transparent() > vRhs.Transparent() ? Transparent() : vRhs.Transparent(); return Color(Red()+vRhs.Red(),Green()+vRhs.Green(),Blue()+vRhs.Blue(), trans); } void Color::Red(double vRed) { mRed = unit_limiter(vRed); } double Color::Red() const { return mRed; } void Color::Green(double vGreen) { mGreen = unit_limiter(vGreen); } double Color::Green() const { return mGreen; } void Color::Blue(double vBlue) { mBlue = unit_limiter(vBlue); } double Color::Blue() const { return mBlue; } void Color::Transparent(double vTransparent) { mTransparent = unit_limiter(vTransparent); } double Color::Transparent() const { return mTransparent; } unsigned char Color::RedByte() const { return ByteValue(mRed); } unsigned char Color::GreenByte() const { return ByteValue(mGreen); } unsigned char Color::BlueByte() const { return ByteValue(mBlue); } unsigned char Color::TransparentByte() const { return ByteValue(mTransparent); } unsigned char Color::ByteValue(double vZeroToOne) const { return (unsigned char)(vZeroToOne*255.0); } } // namespace igraph igraph/src/igraph.h0000644000176000001440000000536012325527073014000 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_H #define IGRAPH_H #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif #include "igraph_version.h" #include "igraph_memory.h" #include "igraph_error.h" #include "igraph_random.h" #include "igraph_progress.h" #include "igraph_statusbar.h" #include "igraph_types.h" #include "igraph_complex.h" #include "igraph_vector.h" #include "igraph_matrix.h" #include "igraph_array.h" #include "igraph_dqueue.h" #include "igraph_stack.h" #include "igraph_heap.h" #include "igraph_psumtree.h" #include "igraph_strvector.h" #include "igraph_vector_ptr.h" #include "igraph_spmatrix.h" #include "igraph_sparsemat.h" #include "igraph_qsort.h" #include "igraph_constants.h" #include "igraph_datatype.h" #include "igraph_iterators.h" #include "igraph_interface.h" #include "igraph_constructors.h" #include "igraph_games.h" #include "igraph_microscopic_update.h" #include "igraph_centrality.h" #include "igraph_paths.h" #include "igraph_components.h" #include "igraph_structural.h" #include "igraph_transitivity.h" #include "igraph_neighborhood.h" #include "igraph_topology.h" #include "igraph_bipartite.h" #include "igraph_cliques.h" #include "igraph_layout.h" #include "igraph_visitor.h" #include "igraph_community.h" #include "igraph_conversion.h" #include "igraph_foreign.h" #include "igraph_motifs.h" #include "igraph_operators.h" #include "igraph_flow.h" #include "igraph_revolver.h" #include "igraph_nongraph.h" #include "igraph_cocitation.h" #include "igraph_adjlist.h" #include "igraph_attributes.h" #include "igraph_blas.h" #include "igraph_lapack.h" #include "igraph_arpack.h" #include "igraph_mixing.h" #include "igraph_separators.h" #include "igraph_cohesive_blocks.h" #include "igraph_eigen.h" #include "igraph_hrg.h" #include "igraph_threading.h" #include "igraph_interrupt.h" #include "igraph_scg.h" #include "igraph_matching.h" #include "igraph_graphlets.h" #include "igraph_epidemics.h" #endif igraph/src/igraph_marked_queue.h0000644000176000001440000000463112325527073016527 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_MARKED_QUEUE_H #define IGRAPH_MARKED_QUEUE_H #include "igraph_vector.h" #include "igraph_dqueue.h" #include /* This is essentially a double ended queue, with some extra features: (1) The is-element? operation is fast, O(1). This requires that we know a limit for the number of elements in the queue. (2) We can insert elements in batches, and the whole batch can be removed at once. Currently only the top-end operations are implemented, so the queue is essentially a stack. */ typedef struct igraph_marked_queue_t { igraph_dqueue_t Q; igraph_vector_long_t set; long int mark; long int size; } igraph_marked_queue_t; int igraph_marked_queue_init(igraph_marked_queue_t *q, long int size); void igraph_marked_queue_destroy(igraph_marked_queue_t *q); void igraph_marked_queue_reset(igraph_marked_queue_t *q); igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q); long int igraph_marked_queue_size(const igraph_marked_queue_t *q); int igraph_marked_queue_print(const igraph_marked_queue_t *q); int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file); igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q, long int elem); int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem); int igraph_marked_queue_start_batch(igraph_marked_queue_t *q); void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q); int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q, igraph_vector_t *vec); #endif igraph/src/second.f0000644000176000001440000000137112325527074013776 0ustar ripleyusers SUBROUTINE IGRAPHSECOND( T ) * REAL T * * -- LAPACK auxiliary routine (preliminary version) -- * Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., * Courant Institute, Argonne National Lab, and Rice University * July 26, 1991 * * Purpose * ======= * * SECOND returns the user time for a process in igraphseconds. * This version gets the time from the system function ETIME. * * .. Local Scalars .. REAL T1 * .. * .. Local Arrays .. REAL TARRAY( 2 ) * .. * .. External Functions .. REAL ETIME * .. * .. Executable Statements .. * T1 = ETIME( TARRAY ) T = TARRAY( 1 ) RETURN * * End of SECOND * END igraph/src/scg_exact_scg.c0000644000176000001440000000410212325527074015307 0ustar ripleyusers/* * SCGlib : A C library for the spectral coarse graining of matrices * as described in the paper: Shrinking Matrices while preserving their * eigenpairs with Application to the Spectral Coarse Graining of Graphs. * Preprint available at * * Copyright (C) 2008 David Morton de Lachapelle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * DESCRIPTION * ----------- * The exact_coarse_graining function labels all the objects whose * components in 'v' are equal. The result is stored in 'gr'. Labels * are positive consecutive integers starting from 0. * See also Section 5.4.1 (last paragraph) of the above reference. */ #include "igraph_memory.h" #include "scg_headers.h" #include int igraph_i_exact_coarse_graining(const igraph_real_t *v, int *gr, const int n) { int i, gr_nb; igraph_i_scg_indval_t *w = igraph_Calloc(n, igraph_i_scg_indval_t); if (!w) { IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, w); for(i=0; i 1e-14 ) { gr_nb++; } gr[w[i].ind] = gr_nb; } igraph_Free(w); IGRAPH_FINALLY_CLEAN(1); return 0; } igraph/src/drl_layout_3d.h0000644000176000001440000000561212325527073015272 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file contains compile time parameters which affect the entire // DrL program. #define DRL_VERSION "3.2 5/5/2006" // compile time parameters for MPI message passing #define MAX_PROCS 256 // maximum number of processors #define MAX_FILE_NAME 250 // max length of filename #define MAX_INT_LENGTH 4 // max length of integer suffix of intermediate .coord file // Compile time adjustable parameters for the Density grid #define GRID_SIZE 100 // size of Density grid #define VIEW_SIZE 250.0 // actual physical size of layout plane // these values use more memory but have // little effect on performance or layout #define RADIUS 10 // radius for density fall-off: // larger values tends to slow down // the program and clump the data #define HALF_VIEW 125.0 // 1/2 of VIEW_SIZE #define VIEW_TO_GRID .4 // ratio of GRID_SIZE to VIEW_SIZE /* // original values for VxOrd #define GRID_SIZE 400 // size of VxOrd Density grid #define VIEW_SIZE 1600.0 // actual physical size of VxOrd plane #define RADIUS 10 // radius for density fall-off #define HALF_VIEW 800 // 1/2 of VIEW_SIZE #define VIEW_TO_GRID .25 // ratio of GRID_SIZE to VIEW_SIZE */ igraph/src/cs_entry.c0000644000176000001440000000251112325527073014342 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* add an entry to a triplet matrix; return 1 if ok, 0 otherwise */ CS_INT cs_entry (cs *T, CS_INT i, CS_INT j, CS_ENTRY x) { if (!CS_TRIPLET (T) || i < 0 || j < 0) return (0) ; /* check inputs */ if (T->nz >= T->nzmax && !cs_sprealloc (T,2*(T->nzmax))) return (0) ; if (T->x) T->x [T->nz] = x ; T->i [T->nz] = i ; T->p [T->nz++] = j ; T->m = CS_MAX (T->m, i+1) ; T->n = CS_MAX (T->n, j+1) ; return (1) ; } igraph/src/igraph_stack.h0000644000176000001440000000411512325527073015162 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_STACK_H #define IGRAPH_STACK_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Plain stack */ /* -------------------------------------------------- */ #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "igraph_stack_pmt.h" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "igraph_stack_pmt.h" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_stack_pmt.h" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "igraph_stack_pmt.h" #include "igraph_pmt_off.h" #undef BASE_BOOL #define BASE_PTR #include "igraph_pmt.h" #include "igraph_stack_pmt.h" #include "igraph_pmt_off.h" #undef BASE_PTR #define IGRAPH_STACK_NULL { 0,0,0 } void igraph_stack_ptr_free_all(igraph_stack_ptr_t* s); void igraph_stack_ptr_destroy_all(igraph_stack_ptr_t* s); __END_DECLS #endif igraph/src/drl_parse.h0000644000176000001440000000501712325527073014500 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // The parse class contains the methods necessary to parse // the command line, print help, and do error checking #ifdef MUSE_MPI #include #endif namespace drl { class parse { public: // Methods parse ( int argc, char **argv ); ~parse () {} // user parameters string sim_file; // .sim file string coord_file; // .coord file string parms_file; // .parms file string real_file; // .real file int rand_seed; // random seed int >= 0 float edge_cut; // edge cutting real [0,1] int int_out; // intermediate output, int >= 1 int edges_out; // true if .edges file is requested int parms_in; // true if .parms file is to be read float real_in; // true if .real file is to be read private: void print_syntax ( const char *error_string ); }; } // namespace drl igraph/src/f2c_dummy.c0000644000176000001440000000163212325527073014404 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ int MAIN__(void) { return 0; } igraph/src/cs_load.c0000644000176000001440000000304612325527073014124 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* load a triplet matrix from a file */ cs *cs_load (FILE *f) { CS_INT i, j ; double x ; #ifdef CS_COMPLEX double xi ; #endif cs *T ; if (!f) return (NULL) ; /* check inputs */ T = cs_spalloc (0, 0, 1, 1, 1) ; /* allocate result */ #ifdef CS_COMPLEX while (fscanf (f, ""CS_ID" "CS_ID" %lg %lg\n", &i, &j, &x, &xi) == 4) #else while (fscanf (f, ""CS_ID" "CS_ID" %lg\n", &i, &j, &x) == 3) #endif { #ifdef CS_COMPLEX if (!cs_entry (T, i, j, x + xi*I)) return (cs_spfree (T)) ; #else if (!cs_entry (T, i, j, x)) return (cs_spfree (T)) ; #endif } return (T) ; } igraph/src/cs_droptol.c0000644000176000001440000000217212325527073014667 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" static CS_INT cs_tol (CS_INT i, CS_INT j, CS_ENTRY aij, void *tol) { return (CS_ABS (aij) > *((double *) tol)) ; } CS_INT cs_droptol (cs *A, double tol) { return (cs_fkeep (A, &cs_tol, &tol)) ; /* keep all large entries */ } igraph/src/dnaitr.f0000644000176000001440000007425312325527073014014 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdnaitr c c\Description: c Reverse communication interface for applying NP additional steps to c a K step nonsymmetric Arnoldi factorization. c c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T c c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. c c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T c c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. c c where OP and B are as in igraphdnaupd. The B-norm of r_{k+p} is also c computed and returned. c c\Usage: c call igraphdnaitr c ( IDO, BMAT, N, K, NP, NB, RESID, RNORM, V, LDV, H, LDH, c IPNTR, WORKD, INFO ) c c\Arguments c IDO Integer. (INPUT/OUTPUT) c Reverse communication flag. c ------------------------------------------------------------- c IDO = 0: first call to the reverse communication interface c IDO = -1: compute Y = OP * X where c IPNTR(1) is the pointer into WORK for X, c IPNTR(2) is the pointer into WORK for Y. c This is for the restart phase to force the new c starting vector into the range of OP. c IDO = 1: compute Y = OP * X where c IPNTR(1) is the pointer into WORK for X, c IPNTR(2) is the pointer into WORK for Y, c IPNTR(3) is the pointer into WORK for B * X. c IDO = 2: compute Y = B * X where c IPNTR(1) is the pointer into WORK for X, c IPNTR(2) is the pointer into WORK for Y. c IDO = 99: done c ------------------------------------------------------------- c When the routine is used in the "shift-and-invert" mode, the c vector B * Q is already available and do not need to be c recompute in forming OP * Q. c c BMAT Character*1. (INPUT) c BMAT specifies the type of the matrix B that defines the c semi-inner product for the operator OP. See igraphdnaupd. c B = 'I' -> standard eigenvalue problem A*x = lambda*x c B = 'G' -> generalized eigenvalue problem A*x = lambda*M**x c c N Integer. (INPUT) c Dimension of the eigenproblem. c c K Integer. (INPUT) c Current size of V and H. c c NP Integer. (INPUT) c Number of additional Arnoldi steps to take. c c NB Integer. (INPUT) c Blocksize to be used in the recurrence. c Only work for NB = 1 right now. The goal is to have a c program that implement both the block and non-block method. c c RESID Double precision array of length N. (INPUT/OUTPUT) c On INPUT: RESID contains the residual vector r_{k}. c On OUTPUT: RESID contains the residual vector r_{k+p}. c c RNORM Double precision scalar. (INPUT/OUTPUT) c B-norm of the starting residual on input. c B-norm of the updated residual r_{k+p} on output. c c V Double precision N by K+NP array. (INPUT/OUTPUT) c On INPUT: V contains the Arnoldi vectors in the first K c columns. c On OUTPUT: V contains the new NP Arnoldi vectors in the next c NP columns. The first K columns are unchanged. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c H Double precision (K+NP) by (K+NP) array. (INPUT/OUTPUT) c H is used to store the generated upper Hessenberg matrix. c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c IPNTR Integer array of length 3. (OUTPUT) c Pointer to mark the starting locations in the WORK for c vectors used by the Arnoldi iteration. c ------------------------------------------------------------- c IPNTR(1): pointer to the current operand vector X. c IPNTR(2): pointer to the current result vector Y. c IPNTR(3): pointer to the vector B * X when used in the c shift-and-invert mode. X is the current operand. c ------------------------------------------------------------- c c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) c Distributed array to be used in the basic Arnoldi iteration c for reverse communication. The calling program should not c use WORKD as temporary workspace during the iteration !!!!!! c On input, WORKD(1:N) = B*RESID and is used to save some c computation at the first step. c c INFO Integer. (OUTPUT) c = 0: Normal exit. c > 0: Size of the spanning invariant subspace of OP found. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c c\Routines called: c igraphdgetv0 ARPACK routine to generate the initial vector. c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdmout ARPACK utility routine that prints matrices c igraphdvout ARPACK utility routine that prints vectors. c dlabad LAPACK routine that computes machine constants. c dlamch LAPACK routine that determines machine constants. c dlascl LAPACK routine for careful scaling of a matrix. c dlanhs LAPACK routine that computes various norms of a matrix. c dgemv Level 2 BLAS routine for matrix vector multiplication. c daxpy Level 1 BLAS that computes a vector triad. c dscal Level 1 BLAS that scales a vector. c dcopy Level 1 BLAS that copies one vector to another . c ddot Level 1 BLAS that computes the scalar product of two vectors. c dnrm2 Level 1 BLAS that computes the norm of a vector. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.4' c c\SCCS Information: @(#) c FILE: naitr.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 c c\Remarks c The algorithm implemented is: c c restart = .false. c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; c r_{k} contains the initial residual vector even for k = 0; c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already c computed by the calling program. c c betaj = rnorm ; p_{k+1} = B*r_{k} ; c For j = k+1, ..., k+np Do c 1) if ( betaj < tol ) stop or restart depending on j. c ( At present tol is zero ) c if ( restart ) generate a new starting vector. c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; c p_{j} = p_{j}/betaj c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdnaupd c For shift-invert mode p_{j} = B*v_{j} is already available. c wnorm = || OP*v_{j} || c 4) Compute the j-th step residual vector. c w_{j} = V_{j}^T * B * OP * v_{j} c r_{j} = OP*v_{j} - V_{j} * w_{j} c H(:,j) = w_{j}; c H(j,j-1) = rnorm c rnorm = || r_(j) || c If (rnorm > 0.717*wnorm) accept step and go back to 1) c 5) Re-orthogonalization step: c s = V_{j}'*B*r_{j} c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || c alphaj = alphaj + s_{j}; c 6) Iterative refinement step: c If (rnorm1 > 0.717*rnorm) then c rnorm = rnorm1 c accept step and go back to 1) c Else c rnorm = rnorm1 c If this is the first time in step 6), go to 5) c Else r_{j} lies in the span of V_{j} numerically. c Set r_{j} = 0 and rnorm = 0; go to 1) c EndIf c End Do c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdnaitr & (ido, bmat, n, k, np, nb, resid, rnorm, v, ldv, h, ldh, & ipntr, workd, info) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat*1 integer ido, info, k, ldh, ldv, n, nb, np Double precision & rnorm c c %-----------------% c | Array Arguments | c %-----------------% c integer ipntr(3) Double precision & h(ldh,k+np), resid(n), v(ldv,k+np), workd(3*n) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c logical first, orth1, orth2, rstart, step3, step4 integer ierr, i, infol, ipj, irj, ivj, iter, itry, j, msglvl, & jj Double precision & betaj, ovfl, temp1, rnorm1, smlnum, tst1, ulp, unfl, & wnorm save first, orth1, orth2, rstart, step3, step4, & ierr, ipj, irj, ivj, iter, itry, j, msglvl, ovfl, & betaj, rnorm1, smlnum, ulp, unfl, wnorm c c %-----------------------% c | Local Array Arguments | c %-----------------------% c Double precision & xtemp(2) c c %----------------------% c | External Subroutines | c %----------------------% c external daxpy, dcopy, dscal, dgemv, igraphdgetv0, dlabad, & igraphdvout, igraphdmout, igraphivout, igraphsecond c c %--------------------% c | External Functions | c %--------------------% c Double precision & ddot, dnrm2, dlanhs, dlamch external ddot, dnrm2, dlanhs, dlamch c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic abs, sqrt c c %-----------------% c | Data statements | c %-----------------% c data first / .true. / c c %-----------------------% c | Executable Statements | c %-----------------------% c if (first) then c c %-----------------------------------------% c | Set machine-dependent constants for the | c | the splitting and deflation criterion. | c | If norm(H) <= sqrt(OVFL), | c | overflow should not occur. | c | REFERENCE: LAPACK subroutine dlahqr | c %-----------------------------------------% c unfl = dlamch( 'safe minimum' ) ovfl = one / unfl call dlabad( unfl, ovfl ) ulp = dlamch( 'precision' ) smlnum = unfl*( n / ulp ) first = .false. end if c if (ido .eq. 0) then c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = mnaitr c c %------------------------------% c | Initial call to this routine | c %------------------------------% c info = 0 step3 = .false. step4 = .false. rstart = .false. orth1 = .false. orth2 = .false. j = k + 1 ipj = 1 irj = ipj + n ivj = irj + n end if c c %-------------------------------------------------% c | When in reverse communication mode one of: | c | STEP3, STEP4, ORTH1, ORTH2, RSTART | c | will be .true. when .... | c | STEP3: return from computing OP*v_{j}. | c | STEP4: return from computing B-norm of OP*v_{j} | c | ORTH1: return from computing B-norm of r_{j+1} | c | ORTH2: return from computing B-norm of | c | correction to the residual vector. | c | RSTART: return from OP computations needed by | c | igraphdgetv0. | c %-------------------------------------------------% c if (step3) go to 50 if (step4) go to 60 if (orth1) go to 70 if (orth2) go to 90 if (rstart) go to 30 c c %-----------------------------% c | Else this is the first step | c %-----------------------------% c c %--------------------------------------------------------------% c | | c | A R N O L D I I T E R A T I O N L O O P | c | | c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | c %--------------------------------------------------------------% 1000 continue c if (msglvl .gt. 1) then call igraphivout (logfil, 1, j, ndigit, & '_naitr: generating Arnoldi vector number') call igraphdvout (logfil, 1, rnorm, ndigit, & '_naitr: B-norm of the current residual is') end if c c %---------------------------------------------------% c | STEP 1: Check if the B norm of j-th residual | c | vector is zero. Equivalent to determing whether | c | an exact j-step Arnoldi factorization is present. | c %---------------------------------------------------% c betaj = rnorm if (rnorm .gt. zero) go to 40 c c %---------------------------------------------------% c | Invariant subspace found, generate a new starting | c | vector which is orthogonal to the current Arnoldi | c | basis and continue the iteration. | c %---------------------------------------------------% c if (msglvl .gt. 0) then call igraphivout (logfil, 1, j, ndigit, & '_naitr: ****** RESTART AT STEP ******') end if c c %---------------------------------------------% c | ITRY is the loop variable that controls the | c | maximum amount of times that a restart is | c | attempted. NRSTRT is used by stat.h | c %---------------------------------------------% c betaj = zero nrstrt = nrstrt + 1 itry = 1 20 continue rstart = .true. ido = 0 30 continue c c %--------------------------------------% c | If in reverse communication mode and | c | RSTART = .true. flow returns here. | c %--------------------------------------% c call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, & resid, rnorm, ipntr, workd, ierr) if (ido .ne. 99) go to 9000 if (ierr .lt. 0) then itry = itry + 1 if (itry .le. 3) go to 20 c c %------------------------------------------------% c | Give up after several restart attempts. | c | Set INFO to the size of the invariant subspace | c | which spans OP and exit. | c %------------------------------------------------% c info = j - 1 call igraphsecond (t1) tnaitr = tnaitr + (t1 - t0) ido = 99 go to 9000 end if c 40 continue c c %---------------------------------------------------------% c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | c | when reciprocating a small RNORM, test against lower | c | machine bound. | c %---------------------------------------------------------% c call dcopy (n, resid, 1, v(1,j), 1) if (rnorm .ge. unfl) then temp1 = one / rnorm call dscal (n, temp1, v(1,j), 1) call dscal (n, temp1, workd(ipj), 1) else c c %-----------------------------------------% c | To scale both v_{j} and p_{j} carefully | c | use LAPACK routine SLASCL | c %-----------------------------------------% c call dlascl ('General', i, i, rnorm, one, n, 1, & v(1,j), n, infol) call dlascl ('General', i, i, rnorm, one, n, 1, & workd(ipj), n, infol) end if c c %------------------------------------------------------% c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | c | Note that this is not quite yet r_{j}. See STEP 4 | c %------------------------------------------------------% c step3 = .true. nopx = nopx + 1 call igraphsecond (t2) call dcopy (n, v(1,j), 1, workd(ivj), 1) ipntr(1) = ivj ipntr(2) = irj ipntr(3) = ipj ido = 1 c c %-----------------------------------% c | Exit in order to compute OP*v_{j} | c %-----------------------------------% c go to 9000 50 continue c c %----------------------------------% c | Back from reverse communication; | c | WORKD(IRJ:IRJ+N-1) := OP*v_{j} | c | if step3 = .true. | c %----------------------------------% c call igraphsecond (t3) tmvopx = tmvopx + (t3 - t2) step3 = .false. c c %------------------------------------------% c | Put another copy of OP*v_{j} into RESID. | c %------------------------------------------% c call dcopy (n, workd(irj), 1, resid, 1) c c %---------------------------------------% c | STEP 4: Finish extending the Arnoldi | c | factorization to length j. | c %---------------------------------------% c call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 step4 = .true. ipntr(1) = irj ipntr(2) = ipj ido = 2 c c %-------------------------------------% c | Exit in order to compute B*OP*v_{j} | c %-------------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd(ipj), 1) end if 60 continue c c %----------------------------------% c | Back from reverse communication; | c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j} | c | if step4 = .true. | c %----------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c step4 = .false. c c %-------------------------------------% c | The following is needed for STEP 5. | c | Compute the B-norm of OP*v_{j}. | c %-------------------------------------% c if (bmat .eq. 'G') then wnorm = ddot (n, resid, 1, workd(ipj), 1) wnorm = sqrt(abs(wnorm)) else if (bmat .eq. 'I') then wnorm = dnrm2(n, resid, 1) end if c c %-----------------------------------------% c | Compute the j-th residual corresponding | c | to the j step factorization. | c | Use Classical Gram Schmidt and compute: | c | w_{j} <- V_{j}^T * B * OP * v_{j} | c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | c %-----------------------------------------% c c c %------------------------------------------% c | Compute the j Fourier coefficients w_{j} | c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | c %------------------------------------------% c call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, & zero, h(1,j), 1) c c %--------------------------------------% c | Orthogonalize r_{j} against V_{j}. | c | RESID contains OP*v_{j}. See STEP 3. | c %--------------------------------------% c call dgemv ('N', n, j, -one, v, ldv, h(1,j), 1, & one, resid, 1) c if (j .gt. 1) h(j,j-1) = betaj c call igraphsecond (t4) c orth1 = .true. c call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, resid, 1, workd(irj), 1) ipntr(1) = irj ipntr(2) = ipj ido = 2 c c %----------------------------------% c | Exit in order to compute B*r_{j} | c %----------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd(ipj), 1) end if 70 continue c c %---------------------------------------------------% c | Back from reverse communication if ORTH1 = .true. | c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | c %---------------------------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c orth1 = .false. c c %------------------------------% c | Compute the B-norm of r_{j}. | c %------------------------------% c if (bmat .eq. 'G') then rnorm = ddot (n, resid, 1, workd(ipj), 1) rnorm = sqrt(abs(rnorm)) else if (bmat .eq. 'I') then rnorm = dnrm2(n, resid, 1) end if c c %-----------------------------------------------------------% c | STEP 5: Re-orthogonalization / Iterative refinement phase | c | Maximum NITER_ITREF tries. | c | | c | s = V_{j}^T * B * r_{j} | c | r_{j} = r_{j} - V_{j}*s | c | alphaj = alphaj + s_{j} | c | | c | The stopping criteria used for iterative refinement is | c | discussed in Parlett's book SEP, page 107 and in Gragg & | c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | c | Determine if we need to correct the residual. The goal is | c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | c | The following test determines whether the sine of the | c | angle between OP*x and the computed residual is less | c | than or equal to 0.717. | c %-----------------------------------------------------------% c if (rnorm .gt. 0.717*wnorm) go to 100 iter = 0 nrorth = nrorth + 1 c c %---------------------------------------------------% c | Enter the Iterative refinement phase. If further | c | refinement is necessary, loop back here. The loop | c | variable is ITER. Perform a step of Classical | c | Gram-Schmidt using all the Arnoldi vectors V_{j} | c %---------------------------------------------------% c 80 continue c if (msglvl .gt. 2) then xtemp(1) = wnorm xtemp(2) = rnorm call igraphdvout (logfil, 2, xtemp, ndigit, & '_naitr: re-orthonalization; wnorm and rnorm are') call igraphdvout (logfil, j, h(1,j), ndigit, & '_naitr: j-th column of H') end if c c %----------------------------------------------------% c | Compute V_{j}^T * B * r_{j}. | c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | c %----------------------------------------------------% c call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, & zero, workd(irj), 1) c c %---------------------------------------------% c | Compute the correction to the residual: | c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | c | The correction to H is v(:,1:J)*H(1:J,1:J) | c | + v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j. | c %---------------------------------------------% c call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, & one, resid, 1) call daxpy (j, one, workd(irj), 1, h(1,j), 1) c orth2 = .true. call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, resid, 1, workd(irj), 1) ipntr(1) = irj ipntr(2) = ipj ido = 2 c c %-----------------------------------% c | Exit in order to compute B*r_{j}. | c | r_{j} is the corrected residual. | c %-----------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd(ipj), 1) end if 90 continue c c %---------------------------------------------------% c | Back from reverse communication if ORTH2 = .true. | c %---------------------------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c c %-----------------------------------------------------% c | Compute the B-norm of the corrected residual r_{j}. | c %-----------------------------------------------------% c if (bmat .eq. 'G') then rnorm1 = ddot (n, resid, 1, workd(ipj), 1) rnorm1 = sqrt(abs(rnorm1)) else if (bmat .eq. 'I') then rnorm1 = dnrm2(n, resid, 1) end if c if (msglvl .gt. 0 .and. iter .gt. 0) then call igraphivout (logfil, 1, j, ndigit, & '_naitr: Iterative refinement for Arnoldi residual') if (msglvl .gt. 2) then xtemp(1) = rnorm xtemp(2) = rnorm1 call igraphdvout (logfil, 2, xtemp, ndigit, & '_naitr: iterative refinement ; rnorm and rnorm1 are') end if end if c c %-----------------------------------------% c | Determine if we need to perform another | c | step of re-orthogonalization. | c %-----------------------------------------% c if (rnorm1 .gt. 0.717*rnorm) then c c %---------------------------------------% c | No need for further refinement. | c | The cosine of the angle between the | c | corrected residual vector and the old | c | residual vector is greater than 0.717 | c | In other words the corrected residual | c | and the old residual vector share an | c | angle of less than arcCOS(0.717) | c %---------------------------------------% c rnorm = rnorm1 c else c c %-------------------------------------------% c | Another step of iterative refinement step | c | is required. NITREF is used by stat.h | c %-------------------------------------------% c nitref = nitref + 1 rnorm = rnorm1 iter = iter + 1 if (iter .le. 1) go to 80 c c %-------------------------------------------------% c | Otherwise RESID is numerically in the span of V | c %-------------------------------------------------% c do 95 jj = 1, n resid(jj) = zero 95 continue rnorm = zero end if c c %----------------------------------------------% c | Branch here directly if iterative refinement | c | wasn't necessary or after at most NITER_REF | c | steps of iterative refinement. | c %----------------------------------------------% c 100 continue c rstart = .false. orth2 = .false. c call igraphsecond (t5) titref = titref + (t5 - t4) c c %------------------------------------% c | STEP 6: Update j = j+1; Continue | c %------------------------------------% c j = j + 1 if (j .gt. k+np) then call igraphsecond (t1) tnaitr = tnaitr + (t1 - t0) ido = 99 do 110 i = max(1,k), k+np-1 c c %--------------------------------------------% c | Check for splitting and deflation. | c | Use a standard test as in the QR algorithm | c | REFERENCE: LAPACK subroutine dlahqr | c %--------------------------------------------% c tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) if( tst1.eq.zero ) & tst1 = dlanhs( '1', k+np, h, ldh, workd(n+1) ) if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) & h(i+1,i) = zero 110 continue c if (msglvl .gt. 2) then call igraphdmout (logfil, k+np, k+np, h, ldh, ndigit, & '_naitr: Final upper Hessenberg matrix H of order K+NP') end if c go to 9000 end if c c %--------------------------------------------------------% c | Loop back to extend the factorization by another step. | c %--------------------------------------------------------% c go to 1000 c c %---------------------------------------------------------------% c | | c | E N D O F M A I N I T E R A T I O N L O O P | c | | c %---------------------------------------------------------------% c 9000 continue return c c %---------------% c | End of igraphdnaitr | c %---------------% c end igraph/src/glpapi05.c0000644000176000001440000001325612325527073014145 0ustar ripleyusers/* glpapi05.c (LP basis constructing routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_set_row_stat - set (change) row status * * SYNOPSIS * * void glp_set_row_stat(glp_prob *lp, int i, int stat); * * DESCRIPTION * * The routine glp_set_row_stat sets (changes) status of the auxiliary * variable associated with i-th row. * * The new status of the auxiliary variable should be specified by the * parameter stat as follows: * * GLP_BS - basic variable; * GLP_NL - non-basic variable; * GLP_NU - non-basic variable on its upper bound; if the variable is * not double-bounded, this means the same as GLP_NL (only in * case of this routine); * GLP_NF - the same as GLP_NL (only in case of this routine); * GLP_NS - the same as GLP_NL (only in case of this routine). */ void glp_set_row_stat(glp_prob *lp, int i, int stat) { GLPROW *row; if (!(1 <= i && i <= lp->m)) xerror("glp_set_row_stat: i = %d; row number out of range\n", i); if (!(stat == GLP_BS || stat == GLP_NL || stat == GLP_NU || stat == GLP_NF || stat == GLP_NS)) xerror("glp_set_row_stat: i = %d; stat = %d; invalid status\n", i, stat); row = lp->row[i]; if (stat != GLP_BS) { switch (row->type) { case GLP_FR: stat = GLP_NF; break; case GLP_LO: stat = GLP_NL; break; case GLP_UP: stat = GLP_NU; break; case GLP_DB: if (stat != GLP_NU) stat = GLP_NL; break; case GLP_FX: stat = GLP_NS; break; default: xassert(row != row); } } if (row->stat == GLP_BS && stat != GLP_BS || row->stat != GLP_BS && stat == GLP_BS) { /* invalidate the basis factorization */ lp->valid = 0; } row->stat = stat; return; } /*********************************************************************** * NAME * * glp_set_col_stat - set (change) column status * * SYNOPSIS * * void glp_set_col_stat(glp_prob *lp, int j, int stat); * * DESCRIPTION * * The routine glp_set_col_stat sets (changes) status of the structural * variable associated with j-th column. * * The new status of the structural variable should be specified by the * parameter stat as follows: * * GLP_BS - basic variable; * GLP_NL - non-basic variable; * GLP_NU - non-basic variable on its upper bound; if the variable is * not double-bounded, this means the same as GLP_NL (only in * case of this routine); * GLP_NF - the same as GLP_NL (only in case of this routine); * GLP_NS - the same as GLP_NL (only in case of this routine). */ void glp_set_col_stat(glp_prob *lp, int j, int stat) { GLPCOL *col; if (!(1 <= j && j <= lp->n)) xerror("glp_set_col_stat: j = %d; column number out of range\n" , j); if (!(stat == GLP_BS || stat == GLP_NL || stat == GLP_NU || stat == GLP_NF || stat == GLP_NS)) xerror("glp_set_col_stat: j = %d; stat = %d; invalid status\n", j, stat); col = lp->col[j]; if (stat != GLP_BS) { switch (col->type) { case GLP_FR: stat = GLP_NF; break; case GLP_LO: stat = GLP_NL; break; case GLP_UP: stat = GLP_NU; break; case GLP_DB: if (stat != GLP_NU) stat = GLP_NL; break; case GLP_FX: stat = GLP_NS; break; default: xassert(col != col); } } if (col->stat == GLP_BS && stat != GLP_BS || col->stat != GLP_BS && stat == GLP_BS) { /* invalidate the basis factorization */ lp->valid = 0; } col->stat = stat; return; } /*********************************************************************** * NAME * * glp_std_basis - construct standard initial LP basis * * SYNOPSIS * * void glp_std_basis(glp_prob *lp); * * DESCRIPTION * * The routine glp_std_basis builds the "standard" (trivial) initial * basis for the specified problem object. * * In the "standard" basis all auxiliary variables are basic, and all * structural variables are non-basic. */ void glp_std_basis(glp_prob *lp) { int i, j; /* make all auxiliary variables basic */ for (i = 1; i <= lp->m; i++) glp_set_row_stat(lp, i, GLP_BS); /* make all structural variables non-basic */ for (j = 1; j <= lp->n; j++) { GLPCOL *col = lp->col[j]; if (col->type == GLP_DB && fabs(col->lb) > fabs(col->ub)) glp_set_col_stat(lp, j, GLP_NU); else glp_set_col_stat(lp, j, GLP_NL); } return; } /* eof */ igraph/src/igraph_nongraph.h0000644000176000001440000000761012325527073015674 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_NONGRAPH_H #define IGRAPH_NONGRAPH_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_matrix.h" #include "igraph_types.h" #include "igraph_vector.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Other, not graph related */ /* -------------------------------------------------- */ /** * \struct igraph_plfit_result_t * \brief Result of fitting a power-law distribution to a vector * * This data structure contains the result of \ref igraph_power_law_fit(), * which tries to fit a power-law distribution to a vector of numbers. The * structure contains the following members: * * \member continuous Whether the fitted power-law distribution was continuous * or discrete. * \member alpha The exponent of the fitted power-law distribution. * \member xmin The minimum value from which the power-law distribution was * fitted. In other words, only the values larger than \c xmin * were used from the input vector. * \member L The log-likelihood of the fitted parameters; in other words, * the probability of observing the input vector given the * parameters. * \member D The test statistic of a Kolmogorov-Smirnov test that compares * the fitted distribution with the input vector. Smaller scores * denote better fit. * \member p The p-value of the Kolmogorov-Smirnov test. Small p-values * (less than 0.05) indicate that the test rejected the hypothesis * that the original data could have been drawn from the fitted * power-law distribution. */ typedef struct igraph_plfit_result_t { igraph_bool_t continuous; double alpha; double xmin; double L; double D; double p; } igraph_plfit_result_t; int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, igraph_integer_t binwidth); int igraph_fisher_yates_shuffle(igraph_vector_t *seq); int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, igraph_integer_t length); int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, igraph_matrix_t *rescoords); int igraph_zeroin(igraph_real_t *ax, igraph_real_t *bx, igraph_real_t (*f)(igraph_real_t x, void *info), void *info, igraph_real_t *Tol, int *Maxit, igraph_real_t *res); int igraph_bfgs(igraph_vector_t *b, igraph_real_t *Fmin, igraph_scalar_function_t fminfn, igraph_vector_function_t fmingr, int maxit, int trace, igraph_real_t abstol, igraph_real_t reltol, int nREPORT, void *ex, igraph_integer_t *fncount, igraph_integer_t *grcount); int igraph_power_law_fit(const igraph_vector_t* vector, igraph_plfit_result_t* result, igraph_real_t xmin, igraph_bool_t force_continuous); __END_DECLS #endif igraph/src/gengraph_qsort.h0000644000176000001440000002462212325527073015553 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef QSORT_H #define QSORT_H #include #include namespace gengraph { //___________________________________________________________________________ // check if every element is zero inline bool check_zero(int *mem, int n) { for(int *v = mem+n; v!=mem; ) if(*(--v)!=0) return false; return true; } //___________________________________________________________________________ // Sort simple integer arrays in ASCENDING order //___________________________________________________________________________ inline int med3(int a, int b, int c) { if(atmp) { *w = *(w-1); w--; } *w = tmp; } } inline int partitionne(int *v, int t, int p) { int i=0; int j=t-1; while(ip) j--; if(i>1], v[(t>>2)+2], v[t-(t>>1)-2])); qsort(v,x); qsort(v+x,t-x); } } inline int qsort_median(int *v, int t, int pos) { if(t<10) { isort(v,t); return v[pos]; } int x = partitionne(v, t, med3(v[t>>1], v[(t>>2)+2], v[t-(t>>1)-2])); if(postmp) { *w = *(w-1); w--; } *w = tmp; } } inline int partitionne(double *v, int t, double p) { int i=0; int j=t-1; while(ip) j--; if(i>1], v[(t>>2)+2], v[t-(t>>1)-2])); qsort(v,x); qsort(v+x,t-x); } } inline double qsort_median(double *v, int t, int pos) { if(t<10) { isort(v,t); return v[pos]; } int x = partitionne(v, t, med3(v[t>>1], v[(t>>2)+2], v[t-(t>>1)-2])); if(pos0 && tmp>1]], mem[v[(t>>2)+3]], mem[v[t-(t>>1)-3]]); int i=0; int j=t-1; while(ip) j--; if(imx) mx=x; if(x0) return b; else return (ca>0) ? c : a; } else { if(cb<0) return b; else return (ca<0) ? c : a; } } } // Lexicographic sort inline void lex_isort(int **l, int *v, int t, int s) { if(t<2) return; for(int i=1; i>1]], l[v[(t>>2)+2]], l[v[t-(t>>1)-2]], s); int i=0; int j=t-1; // printf("pivot = %d\n",p); while(i0) j--; if(ikey[b]) return 1; else { int cmp=lex_comp_indirect(key,neigh[a],neigh[b],qsort_min(degs[a],degs[b])); if(cmp==0) { if(degs[a]>degs[b]) return -1; if(degs[a]0) return b; else return (ca>0) ? c : a; } else { if(cb<0) return b; else return (ca<0) ? c : a; } } } // Sort integer arrays in ASCENDING order inline void mix_isort_indirect(int *key, int *v, int t, int **neigh, int *degs) { if(t<2) return; for(int i=1; i>1], v[(t>>2)+2], v[t-(t>>1)-2], neigh, degs); int i=0; int j=t-1; // printf("pivot = %d\n",p); while(i0) j--; if(i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_FOREIGN_H #define IGRAPH_FOREIGN_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_strvector.h" #include __BEGIN_DECLS /* -------------------------------------------------- */ /* Read and write foreign formats */ /* -------------------------------------------------- */ int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, igraph_integer_t n, igraph_bool_t directed); int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, igraph_strvector_t *predefnames, igraph_bool_t names, igraph_add_weights_t weights, igraph_bool_t directed); int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, igraph_bool_t names, igraph_add_weights_t weights, igraph_bool_t directed); int igraph_read_graph_pajek(igraph_t *graph, FILE *instream); int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index); int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, igraph_strvector_t *problem, igraph_vector_t *label, igraph_integer_t *source, igraph_integer_t *target, igraph_vector_t *capacity, igraph_bool_t directed); int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, igraph_bool_t directed); int igraph_read_graph_gml(igraph_t *graph, FILE *instream); int igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_bool_t directed); int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, const char *names, const char *weights); int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, const char *names, const char *weights, igraph_bool_t isolates); int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, igraph_bool_t prefixattr); int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, long int source, long int target, const igraph_vector_t *capacity); int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, const igraph_vector_t *id, const char *creator); int igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, const char* vertex_attr_name, const char* edge_attr_name); __END_DECLS #endif igraph/src/glpapi14.c0000644000176000001440000002210612325527073014137 0ustar ripleyusers/* glpapi14.c (processing models in GNU MathProg language) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #define GLP_TRAN_DEFINED typedef struct MPL glp_tran; #include "glpmpl.h" #include "glpapi.h" glp_tran *glp_mpl_alloc_wksp(void) { /* allocate the MathProg translator workspace */ glp_tran *tran; tran = mpl_initialize(); return tran; } #if 1 /* 08/XII-2009 */ void _glp_mpl_init_rand(glp_tran *tran, int seed) { if (tran->phase != 0) xerror("glp_mpl_init_rand: invalid call sequence\n"); rng_init_rand(tran->rand, seed); return; } #endif int glp_mpl_read_model(glp_tran *tran, const char *fname, int skip) { /* read and translate model section */ int ret; if (tran->phase != 0) xerror("glp_mpl_read_model: invalid call sequence\n"); ret = mpl_read_model(tran, (char *)fname, skip); if (ret == 1 || ret == 2) ret = 0; else if (ret == 4) ret = 1; else xassert(ret != ret); return ret; } int glp_mpl_read_data(glp_tran *tran, const char *fname) { /* read and translate data section */ int ret; if (!(tran->phase == 1 || tran->phase == 2)) xerror("glp_mpl_read_data: invalid call sequence\n"); ret = mpl_read_data(tran, (char *)fname); if (ret == 2) ret = 0; else if (ret == 4) ret = 1; else xassert(ret != ret); return ret; } int glp_mpl_generate(glp_tran *tran, const char *fname) { /* generate the model */ int ret; if (!(tran->phase == 1 || tran->phase == 2)) xerror("glp_mpl_generate: invalid call sequence\n"); ret = mpl_generate(tran, (char *)fname); if (ret == 3) ret = 0; else if (ret == 4) ret = 1; return ret; } void glp_mpl_build_prob(glp_tran *tran, glp_prob *prob) { /* build LP/MIP problem instance from the model */ int m, n, i, j, t, kind, type, len, *ind; double lb, ub, *val; if (tran->phase != 3) xerror("glp_mpl_build_prob: invalid call sequence\n"); /* erase the problem object */ glp_erase_prob(prob); /* set problem name */ glp_set_prob_name(prob, mpl_get_prob_name(tran)); /* build rows (constraints) */ m = mpl_get_num_rows(tran); if (m > 0) glp_add_rows(prob, m); for (i = 1; i <= m; i++) { /* set row name */ glp_set_row_name(prob, i, mpl_get_row_name(tran, i)); /* set row bounds */ type = mpl_get_row_bnds(tran, i, &lb, &ub); switch (type) { case MPL_FR: type = GLP_FR; break; case MPL_LO: type = GLP_LO; break; case MPL_UP: type = GLP_UP; break; case MPL_DB: type = GLP_DB; break; case MPL_FX: type = GLP_FX; break; default: xassert(type != type); } if (type == GLP_DB && fabs(lb - ub) < 1e-9 * (1.0 + fabs(lb))) { type = GLP_FX; if (fabs(lb) <= fabs(ub)) ub = lb; else lb = ub; } glp_set_row_bnds(prob, i, type, lb, ub); /* warn about non-zero constant term */ if (mpl_get_row_c0(tran, i) != 0.0) xprintf("glp_mpl_build_prob: row %s; constant term %.12g ig" "nored\n", mpl_get_row_name(tran, i), mpl_get_row_c0(tran, i)); } /* build columns (variables) */ n = mpl_get_num_cols(tran); if (n > 0) glp_add_cols(prob, n); for (j = 1; j <= n; j++) { /* set column name */ glp_set_col_name(prob, j, mpl_get_col_name(tran, j)); /* set column kind */ kind = mpl_get_col_kind(tran, j); switch (kind) { case MPL_NUM: break; case MPL_INT: case MPL_BIN: glp_set_col_kind(prob, j, GLP_IV); break; default: xassert(kind != kind); } /* set column bounds */ type = mpl_get_col_bnds(tran, j, &lb, &ub); switch (type) { case MPL_FR: type = GLP_FR; break; case MPL_LO: type = GLP_LO; break; case MPL_UP: type = GLP_UP; break; case MPL_DB: type = GLP_DB; break; case MPL_FX: type = GLP_FX; break; default: xassert(type != type); } if (kind == MPL_BIN) { if (type == GLP_FR || type == GLP_UP || lb < 0.0) lb = 0.0; if (type == GLP_FR || type == GLP_LO || ub > 1.0) ub = 1.0; type = GLP_DB; } if (type == GLP_DB && fabs(lb - ub) < 1e-9 * (1.0 + fabs(lb))) { type = GLP_FX; if (fabs(lb) <= fabs(ub)) ub = lb; else lb = ub; } glp_set_col_bnds(prob, j, type, lb, ub); } /* load the constraint matrix */ ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); for (i = 1; i <= m; i++) { len = mpl_get_mat_row(tran, i, ind, val); glp_set_mat_row(prob, i, len, ind, val); } /* build objective function (the first objective is used) */ for (i = 1; i <= m; i++) { kind = mpl_get_row_kind(tran, i); if (kind == MPL_MIN || kind == MPL_MAX) { /* set objective name */ glp_set_obj_name(prob, mpl_get_row_name(tran, i)); /* set optimization direction */ glp_set_obj_dir(prob, kind == MPL_MIN ? GLP_MIN : GLP_MAX); /* set constant term */ glp_set_obj_coef(prob, 0, mpl_get_row_c0(tran, i)); /* set objective coefficients */ len = mpl_get_mat_row(tran, i, ind, val); for (t = 1; t <= len; t++) glp_set_obj_coef(prob, ind[t], val[t]); break; } } /* free working arrays */ xfree(ind); xfree(val); return; } int glp_mpl_postsolve(glp_tran *tran, glp_prob *prob, int sol) { /* postsolve the model */ int i, j, m, n, stat, ret; double prim, dual; if (!(tran->phase == 3 && !tran->flag_p)) xerror("glp_mpl_postsolve: invalid call sequence\n"); if (!(sol == GLP_SOL || sol == GLP_IPT || sol == GLP_MIP)) xerror("glp_mpl_postsolve: sol = %d; invalid parameter\n", sol); m = mpl_get_num_rows(tran); n = mpl_get_num_cols(tran); if (!(m == glp_get_num_rows(prob) && n == glp_get_num_cols(prob))) xerror("glp_mpl_postsolve: wrong problem object\n"); if (!mpl_has_solve_stmt(tran)) { ret = 0; goto done; } for (i = 1; i <= m; i++) { if (sol == GLP_SOL) { stat = glp_get_row_stat(prob, i); prim = glp_get_row_prim(prob, i); dual = glp_get_row_dual(prob, i); } else if (sol == GLP_IPT) { stat = 0; prim = glp_ipt_row_prim(prob, i); dual = glp_ipt_row_dual(prob, i); } else if (sol == GLP_MIP) { stat = 0; prim = glp_mip_row_val(prob, i); dual = 0.0; } else xassert(sol != sol); if (fabs(prim) < 1e-9) prim = 0.0; if (fabs(dual) < 1e-9) dual = 0.0; mpl_put_row_soln(tran, i, stat, prim, dual); } for (j = 1; j <= n; j++) { if (sol == GLP_SOL) { stat = glp_get_col_stat(prob, j); prim = glp_get_col_prim(prob, j); dual = glp_get_col_dual(prob, j); } else if (sol == GLP_IPT) { stat = 0; prim = glp_ipt_col_prim(prob, j); dual = glp_ipt_col_dual(prob, j); } else if (sol == GLP_MIP) { stat = 0; prim = glp_mip_col_val(prob, j); dual = 0.0; } else xassert(sol != sol); if (fabs(prim) < 1e-9) prim = 0.0; if (fabs(dual) < 1e-9) dual = 0.0; mpl_put_col_soln(tran, j, stat, prim, dual); } ret = mpl_postsolve(tran); if (ret == 3) ret = 0; else if (ret == 4) ret = 1; done: return ret; } void glp_mpl_free_wksp(glp_tran *tran) { /* free the MathProg translator workspace */ mpl_terminate(tran); return; } /* eof */ igraph/src/glpios12.c0000644000176000001440000001340612325527073014161 0ustar ripleyusers/* glpios12.c (node selection heuristics) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpios.h" /*********************************************************************** * NAME * * ios_choose_node - select subproblem to continue the search * * SYNOPSIS * * #include "glpios.h" * int ios_choose_node(glp_tree *T); * * DESCRIPTION * * The routine ios_choose_node selects a subproblem from the active * list to continue the search. The choice depends on the backtracking * technique option. * * RETURNS * * The routine ios_choose_node return the reference number of the * subproblem selected. */ static int most_feas(glp_tree *T); static int best_proj(glp_tree *T); static int best_node(glp_tree *T); int ios_choose_node(glp_tree *T) { int p; if (T->parm->bt_tech == GLP_BT_DFS) { /* depth first search */ xassert(T->tail != NULL); p = T->tail->p; } else if (T->parm->bt_tech == GLP_BT_BFS) { /* breadth first search */ xassert(T->head != NULL); p = T->head->p; } else if (T->parm->bt_tech == GLP_BT_BLB) { /* select node with best local bound */ p = best_node(T); } else if (T->parm->bt_tech == GLP_BT_BPH) { if (T->mip->mip_stat == GLP_UNDEF) { /* "most integer feasible" subproblem */ p = most_feas(T); } else { /* best projection heuristic */ p = best_proj(T); } } else xassert(T != T); return p; } static int most_feas(glp_tree *T) { /* select subproblem whose parent has minimal sum of integer infeasibilities */ IOSNPD *node; int p; double best; p = 0, best = DBL_MAX; for (node = T->head; node != NULL; node = node->next) { xassert(node->up != NULL); if (best > node->up->ii_sum) p = node->p, best = node->up->ii_sum; } return p; } static int best_proj(glp_tree *T) { /* select subproblem using the best projection heuristic */ IOSNPD *root, *node; int p; double best, deg, obj; /* the global bound must exist */ xassert(T->mip->mip_stat == GLP_FEAS); /* obtain pointer to the root node, which must exist */ root = T->slot[1].node; xassert(root != NULL); /* deg estimates degradation of the objective function per unit of the sum of integer infeasibilities */ xassert(root->ii_sum > 0.0); deg = (T->mip->mip_obj - root->bound) / root->ii_sum; /* nothing has been selected so far */ p = 0, best = DBL_MAX; /* walk through the list of active subproblems */ for (node = T->head; node != NULL; node = node->next) { xassert(node->up != NULL); /* obj estimates optimal objective value if the sum of integer infeasibilities were zero */ obj = node->up->bound + deg * node->up->ii_sum; if (T->mip->dir == GLP_MAX) obj = - obj; /* select the subproblem which has the best estimated optimal objective value */ if (best > obj) p = node->p, best = obj; } return p; } static int best_node(glp_tree *T) { /* select subproblem with best local bound */ IOSNPD *node, *best = NULL; double bound, eps; switch (T->mip->dir) { case GLP_MIN: bound = +DBL_MAX; for (node = T->head; node != NULL; node = node->next) if (bound > node->bound) bound = node->bound; xassert(bound != +DBL_MAX); eps = 0.001 * (1.0 + fabs(bound)); for (node = T->head; node != NULL; node = node->next) { if (node->bound <= bound + eps) { xassert(node->up != NULL); if (best == NULL || #if 1 best->up->ii_sum > node->up->ii_sum) best = node; #else best->lp_obj > node->lp_obj) best = node; #endif } } break; case GLP_MAX: bound = -DBL_MAX; for (node = T->head; node != NULL; node = node->next) if (bound < node->bound) bound = node->bound; xassert(bound != -DBL_MAX); eps = 0.001 * (1.0 + fabs(bound)); for (node = T->head; node != NULL; node = node->next) { if (node->bound >= bound - eps) { xassert(node->up != NULL); if (best == NULL || #if 1 best->up->ii_sum > node->up->ii_sum) best = node; #else best->lp_obj < node->lp_obj) best = node; #endif } } break; default: xassert(T != T); } xassert(best != NULL); return best->p; } /* eof */ igraph/src/gengraph_graph_molloy_hash.cpp0000644000176000001440000007322512325527073020440 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gengraph_definitions.h" #include #include #include #include #include "gengraph_qsort.h" #include "gengraph_hash.h" #include "gengraph_degree_sequence.h" #include "gengraph_graph_molloy_hash.h" #include "config.h" #include "igraph_math.h" #include "igraph_constructors.h" #include "igraph_error.h" #include "igraph_statusbar.h" #include "igraph_progress.h" namespace gengraph { //_________________________________________________________________________ void graph_molloy_hash::compute_neigh() { int *p = links; for(int i=0; i=i) *(p++)=d; } assert(p==hc+2+n+a/2); return hc; } //_________________________________________________________________________ bool graph_molloy_hash::is_connected() { bool *visited = new bool[n]; int *buff = new int[n]; int comp_size = depth_search(visited, buff); delete[] visited; delete[] buff; return (comp_size==n); } //_________________________________________________________________________ int* graph_molloy_hash::backup() { int *b = new int[a/2]; int *c = b; int *p = links; for(int i=0; ii) *(c++)=*p; assert(c==b+(a/2)); return b; } //_________________________________________________________________________ void graph_molloy_hash::restore(int* b) { init(); int i; int *dd = new int[n]; memcpy(dd,deg,sizeof(int)*n); for(i=0; inb_swaps && maxtimes>all_swaps) { // Backup graph int *save = backup(); // Prepare counters, K, T unsigned long swaps = 0; int K_int = 0; if(type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS) K_int=int(K); unsigned long T_int = (unsigned long)(floor(T)); if(T_int<1) T_int=1; // compute cost cost += T_int; if(K_int>2) cost += (unsigned long)(K_int)*(unsigned long)(T_int); // Perform T edge swap attempts for(int i=T_int; i>0; i--) { // try one swap swaps += (unsigned long)(random_edge_swap(K_int, Kbuff, visited)); all_swaps++; // Verbose if(nb_swaps+swaps>next) { next = (nb_swaps+swaps)+max((unsigned long)(100),(unsigned long)(times/1000)); int progress = int(double(nb_swaps+swaps) / double(times)); igraph_progress("Shuffle", progress, 0); } } // test connectivity cost+=(unsigned long)(a/2); bool ok = is_connected(); // performance monitor { avg_T += double(T_int); avg_K += double(K_int); if(ok) successes++; else failures++; } // restore graph if needed, and count validated swaps if(ok) nb_swaps += swaps; else { restore(save); next=nb_swaps; } delete[] save; // Adjust K and T following the heuristics. switch(type) { int steps; case GKAN_HEURISTICS: if (ok) T+=1.0; else T*=0.5; break; case FAB_HEURISTICS: steps = 50 / (8+failures+successes); if(steps<1) steps=1; while(steps--) if(ok) T*=1.17182818; else T*=0.9; if(T>double(5*a)) T=double(5*a); break; case FINAL_HEURISTICS: if(ok) { if((K+10.0)*T>5.0*double(a)) K/=1.03; else T*=2; } else { K*=1.35; delete[] Kbuff; Kbuff = new int[int(K)+1]; } break; case OPTIMAL_HEURISTICS: if(ok) T=double(optimal_window()); break; case BRUTE_FORCE_HEURISTICS: K*=2; delete[] Kbuff; Kbuff = new int[int(K)+1]; break; default: IGRAPH_ERROR("Error in graph_molloy_hash::shuffle(): " "Unknown heuristics type", IGRAPH_EINVAL); return 0; } } delete[] Kbuff; delete[] visited; if (maxtimes <= all_swaps) { IGRAPH_WARNING("Cannot shuffle graph, maybe there is only a single one?"); } // Status report { igraph_status("*** Shuffle Monitor ***\n", 0); igraph_statusf(" - Average cost : %f / validated edge swap\n", 0, double(cost)/double(nb_swaps)); igraph_statusf(" - Connectivity tests : %d (%d successes, %d failures)\n", 0, successes + failures, successes, failures); igraph_statusf(" - Average window : %d\n", 0, int(avg_T/double(successes+failures))); if(type==FINAL_HEURISTICS || type==BRUTE_FORCE_HEURISTICS) igraph_statusf(" - Average isolation test width : %f\n", 0, avg_K/double(successes+failures)); } return nb_swaps; } //_________________________________________________________________________ void graph_molloy_hash::print(FILE *f) { int i,j; for(i=0; i i) { VECTOR(edges)[ptr++] = i; VECTOR(edges)[ptr++] = neigh[i][j]; } } } } IGRAPH_CHECK(igraph_create(graph, &edges, n, /*undirected=*/ 0)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } //_________________________________________________________________________ bool graph_molloy_hash::try_shuffle(int T, int K, int *backup_graph) { // init all int *Kbuff = NULL; bool *visited = NULL; if(K>2) { Kbuff = new int[K]; visited = new bool[n]; for(int i=0; i=double(trials)*param) return false; double comb = 1.0; double fact = 1.0; for(int i=0; i %s\n",success, trials, param, (sum < _TRUST_BERNOULLI_LOWER) ? "lower" : "can't say"); return (sum < _TRUST_BERNOULLI_LOWER); } //_________________________________________________________________________ #define _MIN_SUCCESS_FOR_BERNOULLI_TRUST 100 double graph_molloy_hash::average_cost(int T, int *backup, double min_cost) { if(T<1) return 1e+99; int successes = 0; int trials = 0; while(successes < _MIN_SUCCESS_FOR_BERNOULLI_TRUST && !bernoulli_param_is_lower(successes, trials, 1.0/min_cost)) { if(try_shuffle(T,0,backup)) successes++; trials++; } if(successes >= _MIN_SUCCESS_FOR_BERNOULLI_TRUST) return double(trials)/double(successes)*(1.0+double(a/2)/double(T)); else return 2.0*min_cost; } //_________________________________________________________________________ int graph_molloy_hash::optimal_window() { int Tmax; int optimal_T=1; double min_cost=1e+99; int *back=backup(); // on cherche une borne sup pour Tmax int been_greater = 0; for(Tmax=1; Tmax<=5*a ;Tmax*=2) { double c = average_cost(Tmax, back, min_cost); if(c > 1.5 * min_cost) break; if(c > 1.2 * min_cost && ++been_greater >= 3) break; if(c < min_cost) { min_cost = c; optimal_T = Tmax; } igraph_statusf("Tmax = %d [%f]", 0, Tmax, min_cost); } // on cree Tmin int Tmin = int(0.5*double(a)/(min_cost-1.0)); igraph_statusf("Optimal T is in [%d, %d]\n", 0, Tmin, Tmax); // on cherche autour double span = 2.0; int try_again = 4; while(span>1.05 && optimal_T <= 5*a) { igraph_statusf("Best T [cost]: %d [%f]", 0, optimal_T, min_cost); int T_low = int(double(optimal_T)/span); int T_high = int(double(optimal_T)*span); double c_low = average_cost(T_low , back, min_cost); double c_high = average_cost(T_high, back, min_cost); if(c_lowdeg[t2] ? f1 : t2, K, Kbuff, visited); // assert(verify()); sum_K += effective_isolated(deg[f2]>deg[t1] ? f2 : t1, K, Kbuff, visited); // assert(verify()); // undo swap swap_edges(f1,t2,f2,t1); // assert(verify()); } delete[] Kbuff; delete[] visited; return double(sum_K)/double(2*quality); } //_________________________________________________________________________ long graph_molloy_hash::effective_isolated(int v, int K, int *Kbuff, bool *visited) { int i; for(i=0; i=dmax) { left_to_explore = 0; return; } *(Kbuff++) = v; visited[v] = true; // print(); // fflush(stdout); calls++; int *copy = NULL; int *w = neigh[v]; if(IS_HASH(deg[v])) { copy = new int[deg[v]]; H_copy(copy,w,deg[v]); w = copy; } qsort(deg, w, deg[v]); w+=deg[v]; for(int i=deg[v]; i--; ) { if(visited[*--w]) calls++; else depth_isolated(*w, calls, left_to_explore, dmax, Kbuff, visited); if(left_to_explore==0) break; } if(copy!=NULL) delete[] copy; } //_________________________________________________________________________ int graph_molloy_hash::depth_search(bool *visited, int *buff, int v0) { for(int i=0; in) n=i; n++; // degrees ? if(VERBOSE()) fprintf(stderr,"%d, #edges=",n); int *degs = new int[n]; rewind(f); while(fgets(buff,FBUFF_SIZE,f)) { int d = 0; if(sscanf(buff,"%d",&i)==1) { char *b = buff; while(skip_int(b)) d++; degs[i]=d; } } // allocate memory degree_sequence dd(n,degs); if(VERBOSE()) fprintf(stderr,"%d\nAllocating memory...",dd.sum()); alloc(dd); // add edges if(VERBOSE()) fprintf(stderr,"done\nCreating edges..."); rewind(f); for(i=0; im) m=deg[k]; return m; } bool graph_molloy_hash::havelhakimi() { int i; int dmax = max_degree()+1; // Sort vertices using basket-sort, in descending degrees int *nb = new int[dmax]; int *sorted = new int[n]; // init basket for(i=0; i=0; i--) { int t=nb[i]; nb[i]=c; c+=t; } // sort for(i=0; i0; ) { // pick a vertex. we could pick any, but here we pick the one with biggest degree int v = sorted[first]; // look for current degree of v while(nb[d]<=first) d--; // store it in dv int dv = d; // bind it ! c -= dv; int dc = d; // residual degree of vertices we bind to int fc = ++first; // position of the first vertex with degree dc while(dv>0 && dc>0) { int lc = nb[dc]; if(lc!=fc) { while(dv>0 && lc>fc) { // binds v with sorted[--lc] dv--; int w = sorted[--lc]; add_edge(v,w); } fc = nb[dc]; nb[dc] = lc; } dc--; } if(dv != 0) { // We couldn't bind entirely v if(VERBOSE()) { fprintf(stderr,"Error in graph_molloy_hash::havelhakimi() :\n"); fprintf(stderr,"Couldn't bind vertex %d entirely (%d edges remaining)\n",v,dv); } delete[] nb; delete[] sorted; return false; } } assert(c==0); delete[] nb; delete[] sorted; return true; } bool graph_molloy_hash::make_connected() { assert(verify()); if(a/2 < n-1) { // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n"); return false; } int i; // Data struct for the visit : // - buff[] contains vertices to visit // - dist[V] is V's distance modulo 4 to the root of its comp, or -1 if it hasn't been visited yet #define MC_BUFF_SIZE (n+2) int *buff = new int[MC_BUFF_SIZE]; unsigned char * dist = new unsigned char[n]; #define NOT_VISITED 255 #define FORBIDDEN 254 for(i=n; i>0; dist[--i]=NOT_VISITED); // Data struct to store components : either surplus trees or surplus edges are stored at buff[]'s end // - A Tree is coded by one of its vertices // - An edge (a,b) is coded by the TWO ints a and b int *ffub = buff+MC_BUFF_SIZE; edge *edges = (edge *) ffub; int *trees = ffub; int *min_ffub = buff+1+(MC_BUFF_SIZE%2 ? 0 : 1); // There will be only one "fatty" component, and trees. edge fatty_edge; fatty_edge.from = -1; bool enough_edges = false; // start main loop for(int v0=0; v0min_ffub) min_ffub+=2; // update limit of ffub's storage //assert(verify()); } else if(dist[w]==next_dist || (w!=HASH_NONE && w>v && dist[w]==current_dist)) { // we found a removable edge if(is_a_tree) { // we must first merge with the fatty component is_a_tree = false; if(fatty_edge.from < 0) { // we ARE the first component! fatty is us fatty_edge.from = v; fatty_edge.to = w; } else { // we connect to fatty swap_edges(fatty_edge.from, fatty_edge.to, v, w); //assert(verify()); } } else { // we have removable edges to give! if(trees!=ffub) { // some trees still.. Let's merge with them! assert(trees>=min_ffub); assert(edges==(edge *)ffub); swap_edges(v,w,*trees,neigh[*trees][0]); trees++; //assert(verify()); } else if(!enough_edges) { // Store the removable edge for future use if(edges<=(edge *)min_ffub+1) enough_edges = true; else { edges--; edges->from = v; edges->to = w; } } } } } } // Mark component while(to_visit!=buff) dist[*(--to_visit)] = FORBIDDEN; // Check if it is a tree if(is_a_tree ) { assert(deg[v0]!=0); if(edges!=(edge *)ffub) { // let's bind the tree we found with a removable edge in stock assert(trees == ffub); if(edges<(edge *)min_ffub) edges=(edge *)min_ffub; swap_edges(v0,neigh[v0][0],edges->from,edges->to); edges++; assert(verify()); } else { // add the tree to the list of trees assert(trees>min_ffub); *(--trees) = v0; assert(verify()); } } } delete[] buff; delete[] dist; return(trees == ffub); } long long graph_molloy_hash::slow_connected_shuffle(long long times) { assert(verify()); long long nb_swaps = 0; int T = 1; while(times>nb_swaps) { // Backup graph int *save = backup(); // Swaps int swaps = 0; for(int i=T; i>0; i--) { // Pick two random vertices a and c int f1 = pick_random_vertex(); int f2 = pick_random_vertex(); // Check that f1 != f2 if(f1==f2) continue; // Get two random edges (f1,*f1t1) and (f2,*f2t2) int *f1t1 = random_neighbour(f1); int t1 = *f1t1; int *f2t2 = random_neighbour(f2); int t2 = *f2t2; // Check simplicity if(t1==t2 || f1==t2 || f2==t1) continue; if(is_edge(f1,t2) || is_edge(f2,t1)) continue; // Swap H_rpl(neigh[f1],deg[f1],f1t1,t2); H_rpl(neigh[f2],deg[f2],f2t2,t1); H_rpl(neigh[t1],deg[t1],f1,f2); H_rpl(neigh[t2],deg[t2],f2,f1); swaps++; } // test connectivity bool ok = is_connected(); if(ok) { nb_swaps += swaps; } else { restore(save); } delete[] save; } return nb_swaps; } int graph_molloy_hash::width_search(unsigned char *dist, int *buff, int v0) { for(int i=0; i. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpenv.h" #include "glpnet.h" /*********************************************************************** * NAME * * ffalg - Ford-Fulkerson algorithm * * SYNOPSIS * * #include "glpnet.h" * void ffalg(int nv, int na, const int tail[], const int head[], * int s, int t, const int cap[], int x[], char cut[]); * * DESCRIPTION * * The routine ffalg implements the Ford-Fulkerson algorithm to find a * maximal flow in the specified flow network. * * INPUT PARAMETERS * * nv is the number of nodes, nv >= 2. * * na is the number of arcs, na >= 0. * * tail[a], a = 1,...,na, is the index of tail node of arc a. * * head[a], a = 1,...,na, is the index of head node of arc a. * * s is the source node index, 1 <= s <= nv. * * t is the sink node index, 1 <= t <= nv, t != s. * * cap[a], a = 1,...,na, is the capacity of arc a, cap[a] >= 0. * * NOTE: Multiple arcs are allowed, but self-loops are not allowed. * * OUTPUT PARAMETERS * * x[a], a = 1,...,na, is optimal value of the flow through arc a. * * cut[i], i = 1,...,nv, is 1 if node i is labelled, and 0 otherwise. * The set of arcs, whose one endpoint is labelled and other is not, * defines the minimal cut corresponding to the maximal flow found. * If the parameter cut is NULL, the cut information are not stored. * * REFERENCES * * L.R.Ford, Jr., and D.R.Fulkerson, "Flows in Networks," The RAND * Corp., Report R-375-PR (August 1962), Chap. I "Static Maximal Flow," * pp.30-33. */ void ffalg(int nv, int na, const int tail[], const int head[], int s, int t, const int cap[], int x[], char cut[]) { int a, delta, i, j, k, pos1, pos2, temp, *ptr, *arc, *link, *list; /* sanity checks */ xassert(nv >= 2); xassert(na >= 0); xassert(1 <= s && s <= nv); xassert(1 <= t && t <= nv); xassert(s != t); for (a = 1; a <= na; a++) { i = tail[a], j = head[a]; xassert(1 <= i && i <= nv); xassert(1 <= j && j <= nv); xassert(i != j); xassert(cap[a] >= 0); } /* allocate working arrays */ ptr = xcalloc(1+nv+1, sizeof(int)); arc = xcalloc(1+na+na, sizeof(int)); link = xcalloc(1+nv, sizeof(int)); list = xcalloc(1+nv, sizeof(int)); /* ptr[i] := (degree of node i) */ for (i = 1; i <= nv; i++) ptr[i] = 0; for (a = 1; a <= na; a++) { ptr[tail[a]]++; ptr[head[a]]++; } /* initialize arc pointers */ ptr[1]++; for (i = 1; i < nv; i++) ptr[i+1] += ptr[i]; ptr[nv+1] = ptr[nv]; /* build arc lists */ for (a = 1; a <= na; a++) { arc[--ptr[tail[a]]] = a; arc[--ptr[head[a]]] = a; } xassert(ptr[1] == 1); xassert(ptr[nv+1] == na+na+1); /* now the indices of arcs incident to node i are stored in locations arc[ptr[i]], arc[ptr[i]+1], ..., arc[ptr[i+1]-1] */ /* initialize arc flows */ for (a = 1; a <= na; a++) x[a] = 0; loop: /* main loop starts here */ /* build augmenting tree rooted at s */ /* link[i] = 0 means that node i is not labelled yet; link[i] = a means that arc a immediately precedes node i */ /* initially node s is labelled as the root */ for (i = 1; i <= nv; i++) link[i] = 0; link[s] = -1, list[1] = s, pos1 = pos2 = 1; /* breadth first search */ while (pos1 <= pos2) { /* dequeue node i */ i = list[pos1++]; /* consider all arcs incident to node i */ for (k = ptr[i]; k < ptr[i+1]; k++) { a = arc[k]; if (tail[a] == i) { /* a = i->j is a forward arc from s to t */ j = head[a]; /* if node j has been labelled, skip the arc */ if (link[j] != 0) continue; /* if the arc does not allow increasing the flow through it, skip the arc */ if (x[a] == cap[a]) continue; } else if (head[a] == i) { /* a = i<-j is a backward arc from s to t */ j = tail[a]; /* if node j has been labelled, skip the arc */ if (link[j] != 0) continue; /* if the arc does not allow decreasing the flow through it, skip the arc */ if (x[a] == 0) continue; } else xassert(a != a); /* label node j and enqueue it */ link[j] = a, list[++pos2] = j; /* check for breakthrough */ if (j == t) goto brkt; } } /* NONBREAKTHROUGH */ /* no augmenting path exists; current flow is maximal */ /* store minimal cut information, if necessary */ if (cut != NULL) { for (i = 1; i <= nv; i++) cut[i] = (char)(link[i] != 0); } goto done; brkt: /* BREAKTHROUGH */ /* walk through arcs of the augmenting path (s, ..., t) found in the reverse order and determine maximal change of the flow */ delta = 0; for (j = t; j != s; j = i) { /* arc a immediately precedes node j in the path */ a = link[j]; if (head[a] == j) { /* a = i->j is a forward arc of the cycle */ i = tail[a]; /* x[a] may be increased until its upper bound */ temp = cap[a] - x[a]; } else if (tail[a] == j) { /* a = i<-j is a backward arc of the cycle */ i = head[a]; /* x[a] may be decreased until its lower bound */ temp = x[a]; } else xassert(a != a); if (delta == 0 || delta > temp) delta = temp; } xassert(delta > 0); /* increase the flow along the path */ for (j = t; j != s; j = i) { /* arc a immediately precedes node j in the path */ a = link[j]; if (head[a] == j) { /* a = i->j is a forward arc of the cycle */ i = tail[a]; x[a] += delta; } else if (tail[a] == j) { /* a = i<-j is a backward arc of the cycle */ i = head[a]; x[a] -= delta; } else xassert(a != a); } goto loop; done: /* free working arrays */ xfree(ptr); xfree(arc); xfree(link); xfree(list); return; } /* eof */ igraph/src/f2c.h0000644000176000001440000001126112325527073013175 0ustar ripleyusers/* f2c.h -- Standard Fortran to C header file */ /** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ #ifndef F2C_INCLUDE #define F2C_INCLUDE #include "igraph_blas_internal.h" #include "igraph_lapack_internal.h" #include "igraph_arpack_internal.h" typedef int integer; typedef unsigned int uinteger; typedef char *address; typedef short int shortint; typedef float real; typedef double doublereal; typedef struct { real r, i; } f2c_complex; typedef struct { doublereal r, i; } doublecomplex; typedef int logical; typedef short int shortlogical; typedef char logical1; typedef char integer1; #ifdef INTEGER_STAR_8 /* Adjust for integer*8. */ typedef long longint; /* system-dependent */ typedef unsigned long ulongint; /* system-dependent */ #define qbit_clear(a,b) ((a) & ~((ulongint)1 << (b))) #define qbit_set(a,b) ((a) | ((ulongint)1 << (b))) #endif #define TRUE_ (1) #define FALSE_ (0) /* Extern is for use with -E */ #ifndef Extern #define Extern extern #endif /* I/O stuff */ #ifdef f2c_i2 /* for -i2 */ typedef short flag; typedef short ftnlen; typedef short ftnint; #else typedef int flag; typedef int ftnlen; typedef int ftnint; #endif /*external read, write*/ typedef struct { flag cierr; ftnint ciunit; flag ciend; char *cifmt; ftnint cirec; } cilist; /*internal read, write*/ typedef struct { flag icierr; char *iciunit; flag iciend; char *icifmt; ftnint icirlen; ftnint icirnum; } icilist; /*open*/ typedef struct { flag oerr; ftnint ounit; char *ofnm; ftnlen ofnmlen; char *osta; char *oacc; char *ofm; ftnint orl; char *oblnk; } olist; /*close*/ typedef struct { flag cerr; ftnint cunit; char *csta; } cllist; /*rewind, backspace, endfile*/ typedef struct { flag aerr; ftnint aunit; } alist; /* inquire */ typedef struct { flag inerr; ftnint inunit; char *infile; ftnlen infilen; ftnint *inex; /*parameters in standard's order*/ ftnint *inopen; ftnint *innum; ftnint *innamed; char *inname; ftnlen innamlen; char *inacc; ftnlen inacclen; char *inseq; ftnlen inseqlen; char *indir; ftnlen indirlen; char *infmt; ftnlen infmtlen; char *inform; ftnint informlen; char *inunf; ftnlen inunflen; ftnint *inrecl; ftnint *innrec; char *inblank; ftnlen inblanklen; } inlist; #define VOID void union Multitype { /* for multiple entry points */ integer1 g; shortint h; integer i; /* longint j; */ real r; doublereal d; f2c_complex c; doublecomplex z; }; typedef union Multitype Multitype; /*typedef long int Long;*/ /* No longer used; formerly in Namelist */ struct Vardesc { /* for Namelist */ char *name; char *addr; ftnlen *dims; int type; }; typedef struct Vardesc Vardesc; struct Namelist { char *name; Vardesc **vars; int nvars; }; typedef struct Namelist Namelist; #define abs(x) ((x) >= 0 ? (x) : -(x)) #define dabs(x) (doublereal)abs(x) #define min(a,b) ((a) <= (b) ? (a) : (b)) #define max(a,b) ((a) >= (b) ? (a) : (b)) #define dmin(a,b) (doublereal)min(a,b) #define dmax(a,b) (doublereal)max(a,b) #define bit_test(a,b) ((a) >> (b) & 1) #define bit_clear(a,b) ((a) & ~((uinteger)1 << (b))) #define bit_set(a,b) ((a) | ((uinteger)1 << (b))) /* procedure parameter types for -A and -C++ */ #define F2C_proc_par_types 1 #ifdef __cplusplus typedef int /* Unknown procedure type */ (*U_fp)(...); typedef shortint (*J_fp)(...); typedef integer (*I_fp)(...); typedef real (*R_fp)(...); typedef doublereal (*D_fp)(...), (*E_fp)(...); typedef /* Complex */ VOID (*C_fp)(...); typedef /* Double Complex */ VOID (*Z_fp)(...); typedef logical (*L_fp)(...); typedef shortlogical (*K_fp)(...); typedef /* Character */ VOID (*H_fp)(...); typedef /* Subroutine */ int (*S_fp)(...); #else typedef int /* Unknown procedure type */ (*U_fp)(); typedef shortint (*J_fp)(); typedef integer (*I_fp)(); typedef real (*R_fp)(); typedef doublereal (*D_fp)(), (*E_fp)(); typedef /* Complex */ VOID (*C_fp)(); typedef /* Double Complex */ VOID (*Z_fp)(); typedef logical (*L_fp)(); typedef shortlogical (*K_fp)(); typedef /* Character */ VOID (*H_fp)(); typedef /* Subroutine */ int (*S_fp)(); #endif /* E_fp is for real functions when -R is not specified */ typedef VOID C_f; /* complex function */ typedef VOID H_f; /* character function */ typedef VOID Z_f; /* double complex function */ typedef doublereal E_f; /* real function with -R not specified */ /* undef any lower-case symbols that your C compiler predefines, e.g.: */ #ifndef Skip_f2c_Undefs #undef cray #undef gcos #undef mc68010 #undef mc68020 #undef mips #undef pdp11 #undef sgi #undef sparc #undef sun #undef sun2 #undef sun3 #undef sun4 #undef u370 #undef u3b #undef u3b2 #undef u3b5 #undef unix #undef vax #endif #include "config.h" #endif igraph/src/glpapi17.c0000644000176000001440000010376712325527073014157 0ustar ripleyusers/* glpapi17.c (flow network problems) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" #include "glpnet.h" /*********************************************************************** * NAME * * glp_mincost_lp - convert minimum cost flow problem to LP * * SYNOPSIS * * void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, * int v_rhs, int a_low, int a_cap, int a_cost); * * DESCRIPTION * * The routine glp_mincost_lp builds an LP problem, which corresponds * to the minimum cost flow problem on the specified network G. */ void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, int v_rhs, int a_low, int a_cap, int a_cost) { glp_vertex *v; glp_arc *a; int i, j, type, ind[1+2]; double rhs, low, cap, cost, val[1+2]; if (!(names == GLP_ON || names == GLP_OFF)) xerror("glp_mincost_lp: names = %d; invalid parameter\n", names); if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) xerror("glp_mincost_lp: v_rhs = %d; invalid offset\n", v_rhs); if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) xerror("glp_mincost_lp: a_low = %d; invalid offset\n", a_low); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_mincost_lp: a_cap = %d; invalid offset\n", a_cap); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_mincost_lp: a_cost = %d; invalid offset\n", a_cost) ; glp_erase_prob(lp); if (names) glp_set_prob_name(lp, G->name); if (G->nv > 0) glp_add_rows(lp, G->nv); for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (names) glp_set_row_name(lp, i, v->name); if (v_rhs >= 0) memcpy(&rhs, (char *)v->data + v_rhs, sizeof(double)); else rhs = 0.0; glp_set_row_bnds(lp, i, GLP_FX, rhs, rhs); } if (G->na > 0) glp_add_cols(lp, G->na); for (i = 1, j = 0; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { j++; if (names) { char name[50+1]; sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); xassert(strlen(name) < sizeof(name)); glp_set_col_name(lp, j, name); } if (a->tail->i != a->head->i) { ind[1] = a->tail->i, val[1] = +1.0; ind[2] = a->head->i, val[2] = -1.0; glp_set_mat_col(lp, j, 2, ind, val); } if (a_low >= 0) memcpy(&low, (char *)a->data + a_low, sizeof(double)); else low = 0.0; if (a_cap >= 0) memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); else cap = 1.0; if (cap == DBL_MAX) type = GLP_LO; else if (low != cap) type = GLP_DB; else type = GLP_FX; glp_set_col_bnds(lp, j, type, low, cap); if (a_cost >= 0) memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); else cost = 0.0; glp_set_obj_coef(lp, j, cost); } } xassert(j == G->na); return; } /**********************************************************************/ int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, int a_cap, int a_cost, double *sol, int a_x, int v_pi) { /* find minimum-cost flow with out-of-kilter algorithm */ glp_vertex *v; glp_arc *a; int nv, na, i, k, s, t, *tail, *head, *low, *cap, *cost, *x, *pi, ret; double sum, temp; if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) xerror("glp_mincost_okalg: v_rhs = %d; invalid offset\n", v_rhs); if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) xerror("glp_mincost_okalg: a_low = %d; invalid offset\n", a_low); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_mincost_okalg: a_cap = %d; invalid offset\n", a_cap); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_mincost_okalg: a_cost = %d; invalid offset\n", a_cost); if (a_x >= 0 && a_x > G->a_size - (int)sizeof(double)) xerror("glp_mincost_okalg: a_x = %d; invalid offset\n", a_x); if (v_pi >= 0 && v_pi > G->v_size - (int)sizeof(double)) xerror("glp_mincost_okalg: v_pi = %d; invalid offset\n", v_pi); /* s is artificial source node */ s = G->nv + 1; /* t is artificial sink node */ t = s + 1; /* nv is the total number of nodes in the resulting network */ nv = t; /* na is the total number of arcs in the resulting network */ na = G->na + 1; for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (v_rhs >= 0) memcpy(&temp, (char *)v->data + v_rhs, sizeof(double)); else temp = 0.0; if (temp != 0.0) na++; } /* allocate working arrays */ tail = xcalloc(1+na, sizeof(int)); head = xcalloc(1+na, sizeof(int)); low = xcalloc(1+na, sizeof(int)); cap = xcalloc(1+na, sizeof(int)); cost = xcalloc(1+na, sizeof(int)); x = xcalloc(1+na, sizeof(int)); pi = xcalloc(1+nv, sizeof(int)); /* construct the resulting network */ k = 0; /* (original arcs) */ for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { k++; tail[k] = a->tail->i; head[k] = a->head->i; if (tail[k] == head[k]) { ret = GLP_EDATA; goto done; } if (a_low >= 0) memcpy(&temp, (char *)a->data + a_low, sizeof(double)); else temp = 0.0; if (!(0.0 <= temp && temp <= (double)INT_MAX && temp == floor(temp))) { ret = GLP_EDATA; goto done; } low[k] = (int)temp; if (a_cap >= 0) memcpy(&temp, (char *)a->data + a_cap, sizeof(double)); else temp = 1.0; if (!((double)low[k] <= temp && temp <= (double)INT_MAX && temp == floor(temp))) { ret = GLP_EDATA; goto done; } cap[k] = (int)temp; if (a_cost >= 0) memcpy(&temp, (char *)a->data + a_cost, sizeof(double)); else temp = 0.0; if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) { ret = GLP_EDATA; goto done; } cost[k] = (int)temp; } } /* (artificial arcs) */ sum = 0.0; for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (v_rhs >= 0) memcpy(&temp, (char *)v->data + v_rhs, sizeof(double)); else temp = 0.0; if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) { ret = GLP_EDATA; goto done; } if (temp > 0.0) { /* artificial arc from s to original source i */ k++; tail[k] = s; head[k] = i; low[k] = cap[k] = (int)(+temp); /* supply */ cost[k] = 0; sum += (double)temp; } else if (temp < 0.0) { /* artificial arc from original sink i to t */ k++; tail[k] = i; head[k] = t; low[k] = cap[k] = (int)(-temp); /* demand */ cost[k] = 0; } } /* (feedback arc from t to s) */ k++; xassert(k == na); tail[k] = t; head[k] = s; if (sum > (double)INT_MAX) { ret = GLP_EDATA; goto done; } low[k] = cap[k] = (int)sum; /* total supply/demand */ cost[k] = 0; /* find minimal-cost circulation in the resulting network */ ret = okalg(nv, na, tail, head, low, cap, cost, x, pi); switch (ret) { case 0: /* optimal circulation found */ ret = 0; break; case 1: /* no feasible circulation exists */ ret = GLP_ENOPFS; break; case 2: /* integer overflow occured */ ret = GLP_ERANGE; goto done; case 3: /* optimality test failed (logic error) */ ret = GLP_EFAIL; goto done; default: xassert(ret != ret); } /* store solution components */ /* (objective function = the total cost) */ if (sol != NULL) { temp = 0.0; for (k = 1; k <= na; k++) temp += (double)cost[k] * (double)x[k]; *sol = temp; } /* (arc flows) */ if (a_x >= 0) { k = 0; for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { temp = (double)x[++k]; memcpy((char *)a->data + a_x, &temp, sizeof(double)); } } } /* (node potentials = Lagrange multipliers) */ if (v_pi >= 0) { for (i = 1; i <= G->nv; i++) { v = G->v[i]; temp = - (double)pi[i]; memcpy((char *)v->data + v_pi, &temp, sizeof(double)); } } done: /* free working arrays */ xfree(tail); xfree(head); xfree(low); xfree(cap); xfree(cost); xfree(x); xfree(pi); return ret; } /*********************************************************************** * NAME * * glp_maxflow_lp - convert maximum flow problem to LP * * SYNOPSIS * * void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s, * int t, int a_cap); * * DESCRIPTION * * The routine glp_maxflow_lp builds an LP problem, which corresponds * to the maximum flow problem on the specified network G. */ void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s, int t, int a_cap) { glp_vertex *v; glp_arc *a; int i, j, type, ind[1+2]; double cap, val[1+2]; if (!(names == GLP_ON || names == GLP_OFF)) xerror("glp_maxflow_lp: names = %d; invalid parameter\n", names); if (!(1 <= s && s <= G->nv)) xerror("glp_maxflow_lp: s = %d; source node number out of rang" "e\n", s); if (!(1 <= t && t <= G->nv)) xerror("glp_maxflow_lp: t = %d: sink node number out of range " "\n", t); if (s == t) xerror("glp_maxflow_lp: s = t = %d; source and sink nodes must" " be distinct\n", s); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_maxflow_lp: a_cap = %d; invalid offset\n", a_cap); glp_erase_prob(lp); if (names) glp_set_prob_name(lp, G->name); glp_set_obj_dir(lp, GLP_MAX); glp_add_rows(lp, G->nv); for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (names) glp_set_row_name(lp, i, v->name); if (i == s) type = GLP_LO; else if (i == t) type = GLP_UP; else type = GLP_FX; glp_set_row_bnds(lp, i, type, 0.0, 0.0); } if (G->na > 0) glp_add_cols(lp, G->na); for (i = 1, j = 0; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { j++; if (names) { char name[50+1]; sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); xassert(strlen(name) < sizeof(name)); glp_set_col_name(lp, j, name); } if (a->tail->i != a->head->i) { ind[1] = a->tail->i, val[1] = +1.0; ind[2] = a->head->i, val[2] = -1.0; glp_set_mat_col(lp, j, 2, ind, val); } if (a_cap >= 0) memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); else cap = 1.0; if (cap == DBL_MAX) type = GLP_LO; else if (cap != 0.0) type = GLP_DB; else type = GLP_FX; glp_set_col_bnds(lp, j, type, 0.0, cap); if (a->tail->i == s) glp_set_obj_coef(lp, j, +1.0); else if (a->head->i == s) glp_set_obj_coef(lp, j, -1.0); } } xassert(j == G->na); return; } int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap, double *sol, int a_x, int v_cut) { /* find maximal flow with Ford-Fulkerson algorithm */ glp_vertex *v; glp_arc *a; int nv, na, i, k, flag, *tail, *head, *cap, *x, ret; char *cut; double temp; if (!(1 <= s && s <= G->nv)) xerror("glp_maxflow_ffalg: s = %d; source node number out of r" "ange\n", s); if (!(1 <= t && t <= G->nv)) xerror("glp_maxflow_ffalg: t = %d: sink node number out of ran" "ge\n", t); if (s == t) xerror("glp_maxflow_ffalg: s = t = %d; source and sink nodes m" "ust be distinct\n", s); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_maxflow_ffalg: a_cap = %d; invalid offset\n", a_cap); if (v_cut >= 0 && v_cut > G->v_size - (int)sizeof(int)) xerror("glp_maxflow_ffalg: v_cut = %d; invalid offset\n", v_cut); /* allocate working arrays */ nv = G->nv; na = G->na; tail = xcalloc(1+na, sizeof(int)); head = xcalloc(1+na, sizeof(int)); cap = xcalloc(1+na, sizeof(int)); x = xcalloc(1+na, sizeof(int)); if (v_cut < 0) cut = NULL; else cut = xcalloc(1+nv, sizeof(char)); /* copy the flow network */ k = 0; for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { k++; tail[k] = a->tail->i; head[k] = a->head->i; if (tail[k] == head[k]) { ret = GLP_EDATA; goto done; } if (a_cap >= 0) memcpy(&temp, (char *)a->data + a_cap, sizeof(double)); else temp = 1.0; if (!(0.0 <= temp && temp <= (double)INT_MAX && temp == floor(temp))) { ret = GLP_EDATA; goto done; } cap[k] = (int)temp; } } xassert(k == na); /* find maximal flow in the flow network */ ffalg(nv, na, tail, head, s, t, cap, x, cut); ret = 0; /* store solution components */ /* (objective function = total flow through the network) */ if (sol != NULL) { temp = 0.0; for (k = 1; k <= na; k++) { if (tail[k] == s) temp += (double)x[k]; else if (head[k] == s) temp -= (double)x[k]; } *sol = temp; } /* (arc flows) */ if (a_x >= 0) { k = 0; for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { temp = (double)x[++k]; memcpy((char *)a->data + a_x, &temp, sizeof(double)); } } } /* (node flags) */ if (v_cut >= 0) { for (i = 1; i <= G->nv; i++) { v = G->v[i]; flag = cut[i]; memcpy((char *)v->data + v_cut, &flag, sizeof(int)); } } done: /* free working arrays */ xfree(tail); xfree(head); xfree(cap); xfree(x); if (cut != NULL) xfree(cut); return ret; } /*********************************************************************** * NAME * * glp_check_asnprob - check correctness of assignment problem data * * SYNOPSIS * * int glp_check_asnprob(glp_graph *G, int v_set); * * RETURNS * * If the specified assignment problem data are correct, the routine * glp_check_asnprob returns zero, otherwise, non-zero. */ int glp_check_asnprob(glp_graph *G, int v_set) { glp_vertex *v; int i, k, ret = 0; if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) xerror("glp_check_asnprob: v_set = %d; invalid offset\n", v_set); for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (v_set >= 0) { memcpy(&k, (char *)v->data + v_set, sizeof(int)); if (k == 0) { if (v->in != NULL) { ret = 1; break; } } else if (k == 1) { if (v->out != NULL) { ret = 2; break; } } else { ret = 3; break; } } else { if (v->in != NULL && v->out != NULL) { ret = 4; break; } } } return ret; } /*********************************************************************** * NAME * * glp_asnprob_lp - convert assignment problem to LP * * SYNOPSIS * * int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, * int v_set, int a_cost); * * DESCRIPTION * * The routine glp_asnprob_lp builds an LP problem, which corresponds * to the assignment problem on the specified graph G. * * RETURNS * * If the LP problem has been successfully built, the routine returns * zero, otherwise, non-zero. */ int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, int v_set, int a_cost) { glp_vertex *v; glp_arc *a; int i, j, ret, ind[1+2]; double cost, val[1+2]; if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX || form == GLP_ASN_MMP)) xerror("glp_asnprob_lp: form = %d; invalid parameter\n", form); if (!(names == GLP_ON || names == GLP_OFF)) xerror("glp_asnprob_lp: names = %d; invalid parameter\n", names); if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) xerror("glp_asnprob_lp: v_set = %d; invalid offset\n", v_set); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_asnprob_lp: a_cost = %d; invalid offset\n", a_cost); ret = glp_check_asnprob(G, v_set); if (ret != 0) goto done; glp_erase_prob(P); if (names) glp_set_prob_name(P, G->name); glp_set_obj_dir(P, form == GLP_ASN_MIN ? GLP_MIN : GLP_MAX); if (G->nv > 0) glp_add_rows(P, G->nv); for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (names) glp_set_row_name(P, i, v->name); glp_set_row_bnds(P, i, form == GLP_ASN_MMP ? GLP_UP : GLP_FX, 1.0, 1.0); } if (G->na > 0) glp_add_cols(P, G->na); for (i = 1, j = 0; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { j++; if (names) { char name[50+1]; sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); xassert(strlen(name) < sizeof(name)); glp_set_col_name(P, j, name); } ind[1] = a->tail->i, val[1] = +1.0; ind[2] = a->head->i, val[2] = +1.0; glp_set_mat_col(P, j, 2, ind, val); glp_set_col_bnds(P, j, GLP_DB, 0.0, 1.0); if (a_cost >= 0) memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); else cost = 1.0; glp_set_obj_coef(P, j, cost); } } xassert(j == G->na); done: return ret; } /**********************************************************************/ int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost, double *sol, int a_x) { /* solve assignment problem with out-of-kilter algorithm */ glp_vertex *v; glp_arc *a; int nv, na, i, k, *tail, *head, *low, *cap, *cost, *x, *pi, ret; double temp; if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX || form == GLP_ASN_MMP)) xerror("glp_asnprob_okalg: form = %d; invalid parameter\n", form); if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) xerror("glp_asnprob_okalg: v_set = %d; invalid offset\n", v_set); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_asnprob_okalg: a_cost = %d; invalid offset\n", a_cost); if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int)) xerror("glp_asnprob_okalg: a_x = %d; invalid offset\n", a_x); if (glp_check_asnprob(G, v_set)) return GLP_EDATA; /* nv is the total number of nodes in the resulting network */ nv = G->nv + 1; /* na is the total number of arcs in the resulting network */ na = G->na + G->nv; /* allocate working arrays */ tail = xcalloc(1+na, sizeof(int)); head = xcalloc(1+na, sizeof(int)); low = xcalloc(1+na, sizeof(int)); cap = xcalloc(1+na, sizeof(int)); cost = xcalloc(1+na, sizeof(int)); x = xcalloc(1+na, sizeof(int)); pi = xcalloc(1+nv, sizeof(int)); /* construct the resulting network */ k = 0; /* (original arcs) */ for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { k++; tail[k] = a->tail->i; head[k] = a->head->i; low[k] = 0; cap[k] = 1; if (a_cost >= 0) memcpy(&temp, (char *)a->data + a_cost, sizeof(double)); else temp = 1.0; if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) { ret = GLP_EDATA; goto done; } cost[k] = (int)temp; if (form != GLP_ASN_MIN) cost[k] = - cost[k]; } } /* (artificial arcs) */ for (i = 1; i <= G->nv; i++) { v = G->v[i]; k++; if (v->out == NULL) tail[k] = i, head[k] = nv; else if (v->in == NULL) tail[k] = nv, head[k] = i; else xassert(v != v); low[k] = (form == GLP_ASN_MMP ? 0 : 1); cap[k] = 1; cost[k] = 0; } xassert(k == na); /* find minimal-cost circulation in the resulting network */ ret = okalg(nv, na, tail, head, low, cap, cost, x, pi); switch (ret) { case 0: /* optimal circulation found */ ret = 0; break; case 1: /* no feasible circulation exists */ ret = GLP_ENOPFS; break; case 2: /* integer overflow occured */ ret = GLP_ERANGE; goto done; case 3: /* optimality test failed (logic error) */ ret = GLP_EFAIL; goto done; default: xassert(ret != ret); } /* store solution components */ /* (objective function = the total cost) */ if (sol != NULL) { temp = 0.0; for (k = 1; k <= na; k++) temp += (double)cost[k] * (double)x[k]; if (form != GLP_ASN_MIN) temp = - temp; *sol = temp; } /* (arc flows) */ if (a_x >= 0) { k = 0; for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { k++; if (ret == 0) xassert(x[k] == 0 || x[k] == 1); memcpy((char *)a->data + a_x, &x[k], sizeof(int)); } } } done: /* free working arrays */ xfree(tail); xfree(head); xfree(low); xfree(cap); xfree(cost); xfree(x); xfree(pi); return ret; } /*********************************************************************** * NAME * * glp_asnprob_hall - find bipartite matching of maximum cardinality * * SYNOPSIS * * int glp_asnprob_hall(glp_graph *G, int v_set, int a_x); * * DESCRIPTION * * The routine glp_asnprob_hall finds a matching of maximal cardinality * in the specified bipartite graph G. It uses a version of the Fortran * routine MC21A developed by I.S.Duff [1], which implements Hall's * algorithm [2]. * * RETURNS * * The routine glp_asnprob_hall returns the cardinality of the matching * found. However, if the specified graph is incorrect (as detected by * the routine glp_check_asnprob), the routine returns negative value. * * REFERENCES * * 1. I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM * Trans. on Math. Softw. 7 (1981), 387-390. * * 2. M.Hall, "An Algorithm for distinct representatives," Amer. Math. * Monthly 63 (1956), 716-717. */ int glp_asnprob_hall(glp_graph *G, int v_set, int a_x) { glp_vertex *v; glp_arc *a; int card, i, k, loc, n, n1, n2, xij; int *num, *icn, *ip, *lenr, *iperm, *pr, *arp, *cv, *out; if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) xerror("glp_asnprob_hall: v_set = %d; invalid offset\n", v_set); if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int)) xerror("glp_asnprob_hall: a_x = %d; invalid offset\n", a_x); if (glp_check_asnprob(G, v_set)) return -1; /* determine the number of vertices in sets R and S and renumber vertices in S which correspond to columns of the matrix; skip all isolated vertices */ num = xcalloc(1+G->nv, sizeof(int)); n1 = n2 = 0; for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (v->in == NULL && v->out != NULL) n1++, num[i] = 0; /* vertex in R */ else if (v->in != NULL && v->out == NULL) n2++, num[i] = n2; /* vertex in S */ else { xassert(v->in == NULL && v->out == NULL); num[i] = -1; /* isolated vertex */ } } /* the matrix must be square, thus, if it has more columns than rows, extra rows will be just empty, and vice versa */ n = (n1 >= n2 ? n1 : n2); /* allocate working arrays */ icn = xcalloc(1+G->na, sizeof(int)); ip = xcalloc(1+n, sizeof(int)); lenr = xcalloc(1+n, sizeof(int)); iperm = xcalloc(1+n, sizeof(int)); pr = xcalloc(1+n, sizeof(int)); arp = xcalloc(1+n, sizeof(int)); cv = xcalloc(1+n, sizeof(int)); out = xcalloc(1+n, sizeof(int)); /* build the adjacency matrix of the bipartite graph in row-wise format (rows are vertices in R, columns are vertices in S) */ k = 0, loc = 1; for (i = 1; i <= G->nv; i++) { if (num[i] != 0) continue; /* vertex i in R */ ip[++k] = loc; v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { xassert(num[a->head->i] != 0); icn[loc++] = num[a->head->i]; } lenr[k] = loc - ip[k]; } xassert(loc-1 == G->na); /* make all extra rows empty (all extra columns are empty due to the row-wise format used) */ for (k++; k <= n; k++) ip[k] = loc, lenr[k] = 0; /* find a row permutation that maximizes the number of non-zeros on the main diagonal */ card = mc21a(n, icn, ip, lenr, iperm, pr, arp, cv, out); #if 1 /* 18/II-2010 */ /* FIXED: if card = n, arp remains clobbered on exit */ for (i = 1; i <= n; i++) arp[i] = 0; for (i = 1; i <= card; i++) { k = iperm[i]; xassert(1 <= k && k <= n); xassert(arp[k] == 0); arp[k] = i; } #endif /* store solution, if necessary */ if (a_x < 0) goto skip; k = 0; for (i = 1; i <= G->nv; i++) { if (num[i] != 0) continue; /* vertex i in R */ k++; v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { /* arp[k] is the number of matched column or zero */ if (arp[k] == num[a->head->i]) { xassert(arp[k] != 0); xij = 1; } else xij = 0; memcpy((char *)a->data + a_x, &xij, sizeof(int)); } } skip: /* free working arrays */ xfree(num); xfree(icn); xfree(ip); xfree(lenr); xfree(iperm); xfree(pr); xfree(arp); xfree(cv); xfree(out); return card; } /*********************************************************************** * NAME * * glp_cpp - solve critical path problem * * SYNOPSIS * * double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls); * * DESCRIPTION * * The routine glp_cpp solves the critical path problem represented in * the form of the project network. * * The parameter G is a pointer to the graph object, which specifies * the project network. This graph must be acyclic. Multiple arcs are * allowed being considered as single arcs. * * The parameter v_t specifies an offset of the field of type double * in the vertex data block, which contains time t[i] >= 0 needed to * perform corresponding job j. If v_t < 0, it is assumed that t[i] = 1 * for all jobs. * * The parameter v_es specifies an offset of the field of type double * in the vertex data block, to which the routine stores earliest start * time for corresponding job. If v_es < 0, this time is not stored. * * The parameter v_ls specifies an offset of the field of type double * in the vertex data block, to which the routine stores latest start * time for corresponding job. If v_ls < 0, this time is not stored. * * RETURNS * * The routine glp_cpp returns the minimal project duration, that is, * minimal time needed to perform all jobs in the project. */ static void sorting(glp_graph *G, int list[]); double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls) { glp_vertex *v; glp_arc *a; int i, j, k, nv, *list; double temp, total, *t, *es, *ls; if (v_t >= 0 && v_t > G->v_size - (int)sizeof(double)) xerror("glp_cpp: v_t = %d; invalid offset\n", v_t); if (v_es >= 0 && v_es > G->v_size - (int)sizeof(double)) xerror("glp_cpp: v_es = %d; invalid offset\n", v_es); if (v_ls >= 0 && v_ls > G->v_size - (int)sizeof(double)) xerror("glp_cpp: v_ls = %d; invalid offset\n", v_ls); nv = G->nv; if (nv == 0) { total = 0.0; goto done; } /* allocate working arrays */ t = xcalloc(1+nv, sizeof(double)); es = xcalloc(1+nv, sizeof(double)); ls = xcalloc(1+nv, sizeof(double)); list = xcalloc(1+nv, sizeof(int)); /* retrieve job times */ for (i = 1; i <= nv; i++) { v = G->v[i]; if (v_t >= 0) { memcpy(&t[i], (char *)v->data + v_t, sizeof(double)); if (t[i] < 0.0) xerror("glp_cpp: t[%d] = %g; invalid time\n", i, t[i]); } else t[i] = 1.0; } /* perform topological sorting to determine the list of nodes (jobs) such that if list[k] = i and list[kk] = j and there exists arc (i->j), then k < kk */ sorting(G, list); /* FORWARD PASS */ /* determine earliest start times */ for (k = 1; k <= nv; k++) { j = list[k]; es[j] = 0.0; for (a = G->v[j]->in; a != NULL; a = a->h_next) { i = a->tail->i; /* there exists arc (i->j) in the project network */ temp = es[i] + t[i]; if (es[j] < temp) es[j] = temp; } } /* determine the minimal project duration */ total = 0.0; for (i = 1; i <= nv; i++) { temp = es[i] + t[i]; if (total < temp) total = temp; } /* BACKWARD PASS */ /* determine latest start times */ for (k = nv; k >= 1; k--) { i = list[k]; ls[i] = total - t[i]; for (a = G->v[i]->out; a != NULL; a = a->t_next) { j = a->head->i; /* there exists arc (i->j) in the project network */ temp = ls[j] - t[i]; if (ls[i] > temp) ls[i] = temp; } /* avoid possible round-off errors */ if (ls[i] < es[i]) ls[i] = es[i]; } /* store results, if necessary */ if (v_es >= 0) { for (i = 1; i <= nv; i++) { v = G->v[i]; memcpy((char *)v->data + v_es, &es[i], sizeof(double)); } } if (v_ls >= 0) { for (i = 1; i <= nv; i++) { v = G->v[i]; memcpy((char *)v->data + v_ls, &ls[i], sizeof(double)); } } /* free working arrays */ xfree(t); xfree(es); xfree(ls); xfree(list); done: return total; } static void sorting(glp_graph *G, int list[]) { /* perform topological sorting to determine the list of nodes (jobs) such that if list[k] = i and list[kk] = j and there exists arc (i->j), then k < kk */ int i, k, nv, v_size, *num; void **save; nv = G->nv; v_size = G->v_size; save = xcalloc(1+nv, sizeof(void *)); num = xcalloc(1+nv, sizeof(int)); G->v_size = sizeof(int); for (i = 1; i <= nv; i++) { save[i] = G->v[i]->data; G->v[i]->data = &num[i]; list[i] = 0; } if (glp_top_sort(G, 0) != 0) xerror("glp_cpp: project network is not acyclic\n"); G->v_size = v_size; for (i = 1; i <= nv; i++) { G->v[i]->data = save[i]; k = num[i]; xassert(1 <= k && k <= nv); xassert(list[k] == 0); list[k] = i; } xfree(save); xfree(num); return; } /* eof */ igraph/src/arpack.c0000644000176000001440000012450112325527072013760 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set sw=2 ts=2 sts=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_arpack.h" #include "igraph_arpack_internal.h" #include "igraph_memory.h" #include #include #include /* The ARPACK example file dssimp.f is used as a template */ int igraph_i_arpack_err_dsaupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_MAXIT; case 3: return IGRAPH_ARPACK_NOSHIFT; case -1: return IGRAPH_ARPACK_NPOS; case -2: return IGRAPH_ARPACK_NEVNPOS; case -3: return IGRAPH_ARPACK_NCVSMALL; case -4: return IGRAPH_ARPACK_NONPOSI; case -5: return IGRAPH_ARPACK_WHICHINV; case -6: return IGRAPH_ARPACK_BMATINV; case -7: return IGRAPH_ARPACK_WORKLSMALL; case -8: return IGRAPH_ARPACK_TRIDERR; case -9: return IGRAPH_ARPACK_ZEROSTART; case -10: return IGRAPH_ARPACK_MODEINV; case -11: return IGRAPH_ARPACK_MODEBMAT; case -12: return IGRAPH_ARPACK_ISHIFT; case -13: return IGRAPH_ARPACK_NEVBE; case -9999: return IGRAPH_ARPACK_NOFACT; default: return IGRAPH_ARPACK_UNKNOWN; } } int igraph_i_arpack_err_dseupd(int error) { switch (error) { case -1: return IGRAPH_ARPACK_NPOS; case -2: return IGRAPH_ARPACK_NEVNPOS; case -3: return IGRAPH_ARPACK_NCVSMALL; case -5: return IGRAPH_ARPACK_WHICHINV; case -6: return IGRAPH_ARPACK_BMATINV; case -7: return IGRAPH_ARPACK_WORKLSMALL; case -8: return IGRAPH_ARPACK_TRIDERR; case -9: return IGRAPH_ARPACK_ZEROSTART; case -10: return IGRAPH_ARPACK_MODEINV; case -11: return IGRAPH_ARPACK_MODEBMAT; case -12: return IGRAPH_ARPACK_NEVBE; case -14: return IGRAPH_ARPACK_FAILED; case -15: return IGRAPH_ARPACK_HOWMNY; case -16: return IGRAPH_ARPACK_HOWMNYS; case -17: return IGRAPH_ARPACK_EVDIFF; default: return IGRAPH_ARPACK_UNKNOWN; } } int igraph_i_arpack_err_dnaupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_MAXIT; case 3: return IGRAPH_ARPACK_NOSHIFT; case -1: return IGRAPH_ARPACK_NPOS; case -2: return IGRAPH_ARPACK_NEVNPOS; case -3: return IGRAPH_ARPACK_NCVSMALL; case -4: return IGRAPH_ARPACK_NONPOSI; case -5: return IGRAPH_ARPACK_WHICHINV; case -6: return IGRAPH_ARPACK_BMATINV; case -7: return IGRAPH_ARPACK_WORKLSMALL; case -8: return IGRAPH_ARPACK_TRIDERR; case -9: return IGRAPH_ARPACK_ZEROSTART; case -10: return IGRAPH_ARPACK_MODEINV; case -11: return IGRAPH_ARPACK_MODEBMAT; case -12: return IGRAPH_ARPACK_ISHIFT; case -9999: return IGRAPH_ARPACK_NOFACT; default: return IGRAPH_ARPACK_UNKNOWN; } } int igraph_i_arpack_err_dneupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_REORDER; case -1: return IGRAPH_ARPACK_NPOS; case -2: return IGRAPH_ARPACK_NEVNPOS; case -3: return IGRAPH_ARPACK_NCVSMALL; case -5: return IGRAPH_ARPACK_WHICHINV; case -6: return IGRAPH_ARPACK_BMATINV; case -7: return IGRAPH_ARPACK_WORKLSMALL; case -8: return IGRAPH_ARPACK_SHUR; case -9: return IGRAPH_ARPACK_LAPACK; case -10: return IGRAPH_ARPACK_MODEINV; case -11: return IGRAPH_ARPACK_MODEBMAT; case -12: return IGRAPH_ARPACK_HOWMNYS; case -13: return IGRAPH_ARPACK_HOWMNY; case -14: return IGRAPH_ARPACK_FAILED; case -15: return IGRAPH_ARPACK_EVDIFF; default: return IGRAPH_ARPACK_UNKNOWN; } } /** * \function igraph_arpack_options_init * Initialize ARPACK options * * Initializes ARPACK options, set them to default values. * You can always pass the initialized \ref igraph_arpack_options_t * object to built-in igraph functions without any modification. The * built-in igraph functions modify the options to perform their * calculation, e.g. \ref igraph_pagerank() always searches for the * eigenvalue with the largest magnitude, regardless of the supplied * value. * * If you want to implement your own function involving eigenvalue * calculation using ARPACK, however, you will likely need to set up * the fields for yourself. * \param o The \ref igraph_arpack_options_t object to initialize. * * Time complexity: O(1). */ void igraph_arpack_options_init(igraph_arpack_options_t *o) { o->bmat[0]='I'; o->n=0; /* needs to be updated! */ o->which[0]='X'; o->which[1]='X'; o->nev=1; o->tol=0; o->ncv=0; /* 0 means "automatic" */ o->ldv=o->n; /* will be updated to (real) n */ o->ishift=1; o->mxiter=3000; o->nb=1; o->mode=1; o->start=0; o->lworkl=0; o->sigma=0; o->sigmai=0; o->info=o->start; o->iparam[0]=o->ishift; o->iparam[1]=0; o->iparam[2]=o->mxiter; o->iparam[3]=o->nb; o->iparam[4]=0; o->iparam[5]=0; o->iparam[6]=o->mode; o->iparam[7]=0; o->iparam[8]=0; o->iparam[9]=0; o->iparam[10]=0; } /** * \function igraph_arpack_storage_init * Initialize ARPACK storage * * You only need this function if you want to run multiple eigenvalue * calculations using ARPACK, and want to spare the memory * allocation/deallocation between each two runs. Otherwise it is safe * to supply a null pointer as the \c storage argument of both \ref * igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() to make * memory allocated and deallocated automatically. * * Don't forget to call the \ref * igraph_arpack_storage_destroy() function on the storage object if * you don't need it any more. * \param s The \ref igraph_arpack_storage_t object to initialize. * \param maxn The maximum order of the matrices. * \param maxncv The maximum NCV parameter intended to use. * \param maxldv The maximum LDV parameter intended to use. * \param symm Whether symmetric or non-symmetric problems will be * solved using this \ref igraph_arpack_storage_t. (You cannot use * the same storage both with symmetric and non-symmetric solvers.) * \return Error code. * * Time complexity: O(maxncv*(maxldv+maxn)). */ int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, long int maxncv, long int maxldv, igraph_bool_t symm) { /* TODO: check arguments */ s->maxn=(int) maxn; s->maxncv=(int) maxncv; s->maxldv=(int) maxldv; #define CHECKMEM(x) \ if (!x) { \ IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, x); s->v=igraph_Calloc(maxldv * maxncv, igraph_real_t); CHECKMEM(s->v); s->workd=igraph_Calloc(3*maxn, igraph_real_t); CHECKMEM(s->workd); s->d=igraph_Calloc(2*maxncv, igraph_real_t); CHECKMEM(s->d); s->resid=igraph_Calloc(maxn, igraph_real_t); CHECKMEM(s->resid); s->ax=igraph_Calloc(maxn, igraph_real_t); CHECKMEM(s->ax); s->select=igraph_Calloc(maxncv, int); CHECKMEM(s->select); if (symm) { s->workl=igraph_Calloc(maxncv*(maxncv+8), igraph_real_t); CHECKMEM(s->workl); s->di=0; s->workev=0; } else { s->workl=igraph_Calloc(3*maxncv*(maxncv+2), igraph_real_t); CHECKMEM(s->workl); s->di=igraph_Calloc(2*maxncv, igraph_real_t); CHECKMEM(s->di); s->workev=igraph_Calloc(3*maxncv, igraph_real_t); CHECKMEM(s->workev); IGRAPH_FINALLY_CLEAN(2); } #undef CHECKMEM IGRAPH_FINALLY_CLEAN(7); return 0; } /** * \function igraph_arpack_storage_destroy * Deallocate ARPACK storage * * \param s The \ref igraph_arpack_storage_t object for which the * memory will be deallocated. * * Time complexity: operating system dependent. */ void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s) { if (s->di) { igraph_Free(s->di); } if (s->workev) { igraph_Free(s->workev); } igraph_Free(s->workl); igraph_Free(s->select); igraph_Free(s->ax); igraph_Free(s->resid); igraph_Free(s->d); igraph_Free(s->workd); igraph_Free(s->v); } /** * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with * these. */ int igraph_i_arpack_rssolve_1x1(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t* options, igraph_vector_t* values, igraph_matrix_t* vectors) { igraph_real_t a, b; int nev = options->nev; if (nev <= 0) { IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); } /* Probe the value in the matrix */ a = 1; if (fun(&b, &a, 1, extra)) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } options->nconv=nev; if (values != 0) { IGRAPH_CHECK(igraph_vector_resize(values, 1)); VECTOR(*values)[0] = b; } if (vectors != 0) { IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1)); MATRIX(*vectors, 0, 0) = 1; } return IGRAPH_SUCCESS; } /** * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with * these. */ int igraph_i_arpack_rnsolve_1x1(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t* options, igraph_matrix_t* values, igraph_matrix_t* vectors) { igraph_real_t a, b; int nev = options->nev; if (nev <= 0) { IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); } /* Probe the value in the matrix */ a = 1; if (fun(&b, &a, 1, extra)) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } options->nconv=nev; if (values != 0) { IGRAPH_CHECK(igraph_matrix_resize(values, 1, 2)); MATRIX(*values, 0, 0) = b; MATRIX(*values, 0, 1) = 0; } if (vectors != 0) { IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1)); MATRIX(*vectors, 0, 0) = 1; } return IGRAPH_SUCCESS; } /** * "Solver" for 2x2 nonsymmetric eigenvalue problems since ARPACK sometimes * blows up with these. */ int igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t* options, igraph_matrix_t* values, igraph_matrix_t* vectors) { igraph_real_t vec[2], mat[4]; igraph_real_t a, b, c, d; igraph_real_t trace, det, tsq4_minus_d; igraph_complex_t eval1, eval2; igraph_complex_t evec1[2], evec2[2]; igraph_bool_t swap_evals = 0; igraph_bool_t complex_evals = 0; int nev = options->nev; if (nev <= 0) { IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); } if (nev > 2) nev = 2; /* Probe the values in the matrix */ vec[0] = 1; vec[1] = 0; if (fun(mat, vec, 2, extra)) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } vec[0] = 0; vec[1] = 1; if (fun(mat+2, vec, 2, extra)) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3]; /* Get the trace and the determinant */ trace = a+d; det = a*d - b*c; tsq4_minus_d = trace*trace / 4 - det; /* Calculate the eigenvalues */ complex_evals = tsq4_minus_d < 0; eval1 = igraph_complex_sqrt_real(tsq4_minus_d); if (complex_evals) { eval2 = igraph_complex_mul_real(eval1, -1); } else { /* to avoid having -0 in the imaginary part */ eval2 = igraph_complex(-IGRAPH_REAL(eval1), 0); } eval1 = igraph_complex_add_real(eval1, trace/2); eval2 = igraph_complex_add_real(eval2, trace/2); if (c != 0) { evec1[0] = igraph_complex_sub_real(eval1, d); evec1[1] = igraph_complex(c, 0); evec2[0] = igraph_complex_sub_real(eval2, d); evec2[1] = igraph_complex(c, 0); } else if (b != 0) { evec1[0] = igraph_complex(b, 0); evec1[1] = igraph_complex_sub_real(eval1, a); evec2[0] = igraph_complex(b, 0); evec2[1] = igraph_complex_sub_real(eval2, a); } else { evec1[0] = igraph_complex(1, 0); evec1[1] = igraph_complex(0, 0); evec2[0] = igraph_complex(0, 0); evec2[1] = igraph_complex(1, 0); } /* Sometimes we have to swap eval1 with eval2 and evec1 with eval2; * determine whether we have to do it now */ if (options->which[0] == 'S') { if (options->which[1] == 'M') { /* eval1 must be the one with the smallest magnitude */ swap_evals = (igraph_complex_mod(eval1) > igraph_complex_mod(eval2)); } else if (options->which[1] == 'R') { /* eval1 must be the one with the smallest real part */ swap_evals = (IGRAPH_REAL(eval1) > IGRAPH_REAL(eval2)); } else if (options->which[1] == 'I') { /* eval1 must be the one with the smallest imaginary part */ swap_evals = (IGRAPH_IMAG(eval1) > IGRAPH_IMAG(eval2)); } else { IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); } } else if (options->which[0] == 'L') { if (options->which[1] == 'M') { /* eval1 must be the one with the largest magnitude */ swap_evals = (igraph_complex_mod(eval1) < igraph_complex_mod(eval2)); } else if (options->which[1] == 'R') { /* eval1 must be the one with the largest real part */ swap_evals = (IGRAPH_REAL(eval1) < IGRAPH_REAL(eval2)); } else if (options->which[1] == 'I') { /* eval1 must be the one with the largest imaginary part */ swap_evals = (IGRAPH_IMAG(eval1) < IGRAPH_IMAG(eval2)); } else { IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); } } else if (options->which[0] == 'X' && options->which[1] == 'X') { /* No preference on the ordering of eigenvectors */ } else { /* fprintf(stderr, "%c%c\n", options->which[0], options->which[1]); */ IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); } options->nconv=nev; if (swap_evals) { igraph_complex_t dummy; dummy = eval1; eval1 = eval2; eval2 = dummy; dummy = evec1[0]; evec1[0] = evec2[0]; evec2[0] = dummy; dummy = evec1[1]; evec1[1] = evec2[1]; evec2[1] = dummy; } if (complex_evals) { /* The eigenvalues are conjugate pairs, so we store only the * one with positive imaginary part */ if (IGRAPH_IMAG(eval1) < 0) { eval1 = eval2; evec1[0] = evec2[0]; evec1[1] = evec2[1]; } } if (values != 0) { IGRAPH_CHECK(igraph_matrix_resize(values, nev, 2)); MATRIX(*values, 0, 0) = IGRAPH_REAL(eval1); MATRIX(*values, 0, 1) = IGRAPH_IMAG(eval1); if (nev > 1) { MATRIX(*values, 1, 0) = IGRAPH_REAL(eval2); MATRIX(*values, 1, 1) = IGRAPH_IMAG(eval2); } } if (vectors != 0) { if (complex_evals) { IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, 2)); MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]); MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]); MATRIX(*vectors, 0, 1) = IGRAPH_IMAG(evec1[0]); MATRIX(*vectors, 1, 1) = IGRAPH_IMAG(evec1[1]); } else { IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev)); MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]); MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]); if (nev > 1) { MATRIX(*vectors, 0, 1) = IGRAPH_REAL(evec2[0]); MATRIX(*vectors, 1, 1) = IGRAPH_REAL(evec2[1]); } } } return IGRAPH_SUCCESS; } /** * "Solver" for symmetric 2x2 eigenvalue problems since ARPACK sometimes blows * up with these. */ int igraph_i_arpack_rssolve_2x2(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t* options, igraph_vector_t* values, igraph_matrix_t* vectors) { igraph_real_t vec[2], mat[4]; igraph_real_t a, b, c, d; igraph_real_t trace, det, tsq4_minus_d; igraph_real_t eval1, eval2; int nev = options->nev; if (nev <= 0) { IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); } if (nev > 2) nev = 2; /* Probe the values in the matrix */ vec[0] = 1; vec[1] = 0; if (fun(mat, vec, 2, extra)) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } vec[0] = 0; vec[1] = 1; if (fun(mat+2, vec, 2, extra)) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3]; /* Get the trace and the determinant */ trace = a+d; det = a*d - b*c; tsq4_minus_d = trace*trace / 4 - det; if (tsq4_minus_d >= 0) { /* Both eigenvalues are real */ eval1 = trace/2 + sqrt(tsq4_minus_d); eval2 = trace/2 - sqrt(tsq4_minus_d); if (c != 0) { mat[0] = eval1-d; mat[2] = eval2-d; mat[1] = c; mat[3] = c; } else if (b != 0) { mat[0] = b; mat[2] = b; mat[1] = eval1-a; mat[3] = eval2-a; } else { mat[0] = 1; mat[2] = 0; mat[1] = 0; mat[3] = 1; } } else { /* Both eigenvalues are complex. Should not happen with symmetric * matrices. */ IGRAPH_ERROR("ARPACK error, 2x2 matrix is not symmetric", IGRAPH_EINVAL); } /* eval1 is always the larger eigenvalue. If we want the smaller * one, we have to swap eval1 with eval2 and also the columns of mat */ if (options->which[0] == 'S') { trace = eval1; eval1 = eval2; eval2 = trace; trace = mat[0]; mat[0] = mat[2]; mat[2] = trace; trace = mat[1]; mat[1] = mat[3]; mat[3] = trace; } else if (options->which[0] == 'L' || options->which[0] == 'B') { /* Nothing to do here */ } else if (options->which[0] == 'X' && options->which[1] == 'X') { /* No preference on the ordering of eigenvectors */ } else { IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); } options->nconv=nev; if (values != 0) { IGRAPH_CHECK(igraph_vector_resize(values, nev)); VECTOR(*values)[0] = eval1; if (nev > 1) { VECTOR(*values)[1] = eval2; } } if (vectors != 0) { IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev)); MATRIX(*vectors, 0, 0) = mat[0]; MATRIX(*vectors, 1, 0) = mat[1]; if (nev > 1) { MATRIX(*vectors, 0, 1) = mat[2]; MATRIX(*vectors, 1, 1) = mat[3]; } } return IGRAPH_SUCCESS; } int igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *vectors, const igraph_arpack_options_t *options, igraph_real_t *d, const igraph_real_t *v) { igraph_vector_t order; char sort[2]; int apply=1; unsigned int n=(unsigned int) options->n; int nconv=options->nconv; int nev=options->nev; unsigned int nans= (unsigned int) (nconv < nev ? nconv : nev); #define which(a,b) (options->which[0]==a && options->which[1]==b) if (which('L','A')) { sort[0]='S'; sort[1]='A'; } else if (which('S','A')) { sort[0]='L'; sort[1]='A'; } else if (which('L','M')) { sort[0]='S'; sort[1]='M'; } else if (which('S','M')) { sort[0]='L'; sort[1]='M'; } else if (which('B','E')) { sort[0]='L'; sort[1]='A'; } IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv-1)); IGRAPH_FINALLY(igraph_vector_destroy, &order); #ifdef HAVE_GFORTRAN igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order), /*which_len=*/ 2); #else igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order)); #endif /* BE is special */ if (which('B','E')) { int w=0, l1=0, l2=nev-1; igraph_vector_t order2, d2; IGRAPH_VECTOR_INIT_FINALLY(&order2, nev); IGRAPH_VECTOR_INIT_FINALLY(&d2, nev); while (l1 <= l2) { VECTOR(order2)[w] = VECTOR(order)[l1]; VECTOR(d2)[w]=d[l1]; w++; l1++; if (l1 <= l2) { VECTOR(order2)[w] = VECTOR(order)[l2]; VECTOR(d2)[w]=d[l2]; w++; l2--; } } igraph_vector_update(&order, &order2); igraph_vector_copy_to(&d2, d); igraph_vector_destroy(&order2); igraph_vector_destroy(&d2); IGRAPH_FINALLY_CLEAN(2); } #undef which /* Copy values */ if (values) { IGRAPH_CHECK(igraph_vector_resize(values, nans)); memcpy(VECTOR(*values), d, sizeof(igraph_real_t) * nans); } /* Reorder vectors */ if (vectors) { int i; IGRAPH_CHECK(igraph_matrix_resize(vectors, n, nans)); for (i=0; in; int nconv=options->nconv; int nev=options->nev; unsigned int nans=(unsigned int) (nconv < nev ? nconv : nev); #define which(a,b) (options->which[0]==a && options->which[1]==b) if (which('L','M')) { sort[0]='S'; sort[1]='M'; } else if (which('S', 'M')) { sort[0]='L'; sort[1]='M'; } else if (which('L', 'R')) { sort[0]='S'; sort[1]='R'; } else if (which('S', 'R')) { sort[0]='L'; sort[1]='R'; } else if (which('L', 'I')) { sort[0]='S'; sort[1]='I'; } else if (which('S', 'I')) { sort[0]='L'; sort[1]='I'; } #undef which IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv-1)); IGRAPH_FINALLY(igraph_vector_destroy, &order); #ifdef HAVE_GFORTRAN igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order), /*which_len=*/ 2); #else igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order)); #endif if (values) { IGRAPH_CHECK(igraph_matrix_resize(values, nans, 2)); memcpy(&MATRIX(*values, 0, 0), dr, sizeof(igraph_real_t) * nans); memcpy(&MATRIX(*values, 0, 1), di, sizeof(igraph_real_t) * nans); } if (vectors) { int i, nc=0, nr=0, ncol, wh=0, vx=0; for (i=0; inev * 2 + 1; /* Use twice the number of desired eigenvectors plus one by default */ options->ncv = min_ncv; /* ...but use at least 20 Lanczos vectors... */ if (options->ncv < 20) { options->ncv = 20; } /* ...but having ncv close to n leads to some problems with small graphs * (example: PageRank of "A <--> C, D <--> E, B"), so we don't let it * to be larger than n / 2... */ if (options->ncv > options->n / 2) { options->ncv = options->n / 2; } /* ...but we need at least min_ncv. */ if (options->ncv < min_ncv) { options->ncv = min_ncv; } /* ...but at most n-1 */ if (options->ncv > options->n) { options->ncv = options->n; } } /** * \function igraph_i_arpack_report_no_convergence * \brief Prints a warning that informs the user that the ARPACK solver * did not converge. */ void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* options) { char buf[1024]; snprintf(buf, sizeof(buf), "ARPACK solver failed to converge (%d iterations, " "%d/%d eigenvectors converged)", options->iparam[2], options->iparam[4], options->nev); IGRAPH_WARNING(buf); } /** * \function igraph_arpack_rssolve * \brief ARPACK solver for symmetric matrices * * This is the ARPACK solver for symmetric matrices. Please use * \ref igraph_arpack_rnsolve() for non-symmetric matrices. * \param fun Pointer to an \ref igraph_arpack_function_t object, * the function that performs the matrix-vector multiplication. * \param extra An extra argument to be passed to \c fun. * \param options An \ref igraph_arpack_options_t object. * \param storage An \ref igraph_arpack_storage_t object, or a null * pointer. In the latter case memory allocation and deallocation * is performed automatically. Either this or the \p vectors argument * must be non-null if the ARPACK iteration is started from a * given starting vector. If both are given \p vectors take * precedence. * \param values If not a null pointer, then it should be a pointer to an * initialized vector. The eigenvalues will be stored here. The * vector will be resized as needed. * \param vectors If not a null pointer, then it must be a pointer to * an initialized matrix. The eigenvectors will be stored in the * columns of the matrix. The matrix will be resized as needed. * Either this or the \p vectors argument must be non-null if the * ARPACK iteration is started from a given starting vector. If * both are given \p vectors take precedence. * \return Error code. * * Time complexity: depends on the matrix-vector * multiplication. Usually a small number of iterations is enough, so * if the matrix is sparse and the matrix-vector multiplication can be * done in O(n) time (the number of vertices), then the eigenvalues * are found in O(n) time as well. */ int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors) { igraph_real_t *v, *workl, *workd, *d, *resid, *ax; igraph_bool_t free_them=0; int *select, i; int ido=0; int rvec= vectors || storage ? 1 : 0; /* calculate eigenvectors? */ char *all="All"; int origldv=options->ldv, origlworkl=options->lworkl, orignev=options->nev, origncv=options->ncv; char origwhich[2]={ options->which[0], options->which[1] }; igraph_real_t origtol=options->tol; /* Special case for 1x1 and 2x2 matrices */ if (options->n == 1) { return igraph_i_arpack_rssolve_1x1(fun, extra, options, values, vectors); } else if (options->n == 2) { return igraph_i_arpack_rssolve_2x2(fun, extra, options, values, vectors); } /* Brush up options if needed */ if (options->ldv == 0) { options->ldv=options->n; } if (options->ncv == 0) { igraph_i_arpack_auto_ncv(options); } if (options->lworkl == 0) { options->lworkl=options->ncv*(options->ncv+8); } if (options->which[0] == 'X') { options->which[0]='L'; options->which[1]='M'; } if (storage) { /* Storage provided */ if (storage->maxn < options->n) { IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL); } if (storage->maxncv < options->ncv) { IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL); } if (storage->maxldv < options->ldv) { IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL); } v = storage->v; workl = storage->workl; workd = storage->workd; d = storage->d; resid = storage->resid; ax = storage->ax; select = storage->select; } else { /* Storage not provided */ free_them=1; #define CHECKMEM(x) \ if (!x) { \ IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, x); v=igraph_Calloc(options->ldv * options->ncv, igraph_real_t); CHECKMEM(v); workl=igraph_Calloc(options->lworkl, igraph_real_t); CHECKMEM(workl); workd=igraph_Calloc(3*options->n, igraph_real_t); CHECKMEM(workd); d=igraph_Calloc(2*options->ncv, igraph_real_t); CHECKMEM(d); resid=igraph_Calloc(options->n, igraph_real_t); CHECKMEM(resid); ax=igraph_Calloc(options->n, igraph_real_t); CHECKMEM(ax); select=igraph_Calloc(options->ncv, int); CHECKMEM(select); #undef CHECKMEM } /* Set final bits */ options->iparam[0]=options->ishift; options->iparam[2]=options->mxiter; options->iparam[3]=options->nb; options->iparam[4]=0; options->iparam[6]=options->mode; options->info=options->start; if (options->start) { if (!storage && !vectors) { IGRAPH_ERROR("Starting vector not given", IGRAPH_EINVAL); } if (vectors && (igraph_matrix_nrow(vectors) != options->n || igraph_matrix_ncol(vectors) != 1)) { IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL); } if (vectors) { for (i=0; in; i++) { resid[i]=MATRIX(*vectors, i, 0); } } } /* Ok, we have everything */ while (1) { #ifdef HAVE_GFORTRAN igraphdsaupd_(&ido, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->info, /*bmat_len=*/ 1, /*which_len=*/ 2); #else igraphdsaupd_(&ido, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->info); #endif if (ido==-1 || ido==1) { igraph_real_t *from=workd+options->ipntr[0]-1; igraph_real_t *to=workd+options->ipntr[1]-1; if (fun(to, from, options->n, extra) != 0) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } } else { break; } } if (options->info == 1) { igraph_i_arpack_report_no_convergence(options); } if (options->info != 0) { IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dsaupd(options->info)); } options->ierr=0; #ifdef HAVE_GFORTRAN igraphdseupd_(&rvec, all, select, d, v, &options->ldv, &options->sigma, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1, /*which_len=*/ 2); #else igraphdseupd_(&rvec, all, select, d, v, &options->ldv, &options->sigma, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->ierr); #endif if (options->ierr != 0) { IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dseupd(options->ierr)); } /* Save the result */ options->noiter=options->iparam[2]; options->nconv=options->iparam[4]; options->numop=options->iparam[8]; options->numopb=options->iparam[9]; options->numreo=options->iparam[10]; if (options->nconv < options->nev) { IGRAPH_WARNING("Not enough eigenvalues/vectors in symmetric ARPACK " "solver"); } if (values || vectors) { IGRAPH_CHECK(igraph_arpack_rssort(values, vectors, options, d, v)); } options->ldv=origldv; options->ncv=origncv; options->lworkl=origlworkl; options->which[0] = origwhich[0]; options->which[1] = origwhich[1]; options->tol=origtol; options->nev=orignev; /* Clean up if needed */ if (free_them) { igraph_Free(select); igraph_Free(ax); igraph_Free(resid); igraph_Free(d); igraph_Free(workd); igraph_Free(workl); igraph_Free(v); IGRAPH_FINALLY_CLEAN(7); } return 0; } /** * \function igraph_arpack_rnsolve * \brief ARPACK solver for non-symmetric matrices * * Please always consider calling \ref igraph_arpack_rssolve() if your * matrix is symmetric, it is much faster. * \ref igraph_arpack_rnsolve() for non-symmetric matrices. * * Note that ARPACK is not called for 2x2 matrices as an exact algebraic * solution exists in these cases. * * \param fun Pointer to an \ref igraph_arpack_function_t object, * the function that performs the matrix-vector multiplication. * \param extra An extra argument to be passed to \c fun. * \param options An \ref igraph_arpack_options_t object. * \param storage An \ref igraph_arpack_storage_t object, or a null * pointer. In the latter case memory allocation and deallocation * is performed automatically. * \param values If not a null pointer, then it should be a pointer to an * initialized matrix. The (possibly complex) eigenvalues will be * stored here. The matrix will have two columns, the first column * contains the real, the second the imaginary parts of the * eigenvalues. * The matrix will be resized as needed. * \param vectors If not a null pointer, then it must be a pointer to * an initialized matrix. The eigenvectors will be stored in the * columns of the matrix. The matrix will be resized as needed. * \return Error code. * * Time complexity: depends on the matrix-vector * multiplication. Usually a small number of iterations is enough, so * if the matrix is sparse and the matrix-vector multiplication can be * done in O(n) time (the number of vertices), then the eigenvalues * are found in O(n) time as well. */ int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_matrix_t *values, igraph_matrix_t *vectors) { igraph_real_t *v, *workl, *workd, *dr, *di, *resid, *workev; igraph_bool_t free_them=0; int *select, i; int ido=0; int rvec= vectors || storage ? 1 : 0; char *all="All"; int origldv=options->ldv, origlworkl=options->lworkl, orignev=options->nev, origncv=options->ncv; char origwhich[2]={ options->which[0], options->which[1] }; igraph_real_t origtol=options->tol; int d_size; /* Special case for 1x1 and 2x2 matrices */ if (options->n == 1) { return igraph_i_arpack_rnsolve_1x1(fun, extra, options, values, vectors); } else if (options->n == 2) { return igraph_i_arpack_rnsolve_2x2(fun, extra, options, values, vectors); } /* Brush up options if needed */ if (options->ldv == 0) { options->ldv=options->n; } if (options->ncv == 0) { igraph_i_arpack_auto_ncv(options); } if (options->lworkl == 0) { options->lworkl=3*options->ncv*(options->ncv+2); } if (options->which[0] == 'X') { options->which[0]='L'; options->which[1]='M'; } if (storage) { /* Storage provided */ if (storage->maxn < options->n) { IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL); } if (storage->maxncv < options->ncv) { IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL); } if (storage->maxldv < options->ldv) { IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL); } v = storage->v; workl = storage->workl; workd = storage->workd; workev = storage->workev; dr = storage->d; di = storage->di; d_size = options->n; resid = storage->resid; select = storage->select; } else { /* Storage not provided */ free_them=1; #define CHECKMEM(x) \ if (!x) { \ IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, x); v=igraph_Calloc(options->n * options->ncv, igraph_real_t); CHECKMEM(v); workl=igraph_Calloc(options->lworkl, igraph_real_t); CHECKMEM(workl); workd=igraph_Calloc(3*options->n, igraph_real_t); CHECKMEM(workd); d_size = 2*options->nev+1 > options->ncv ? 2*options->nev+1 : options->ncv; dr=igraph_Calloc(d_size, igraph_real_t); CHECKMEM(dr); di=igraph_Calloc(d_size, igraph_real_t); CHECKMEM(di); resid=igraph_Calloc(options->n, igraph_real_t); CHECKMEM(resid); select=igraph_Calloc(options->ncv, int); CHECKMEM(select); workev=igraph_Calloc(3*options->ncv, igraph_real_t); CHECKMEM(workev); #undef CHECKMEM } /* Set final bits */ options->iparam[0]=options->ishift; options->iparam[2]=options->mxiter; options->iparam[3]=options->nb; options->iparam[4]=0; options->iparam[6]=options->mode; options->info=options->start; if (options->start) { if (igraph_matrix_nrow(vectors) != options->n || igraph_matrix_ncol(vectors) != 1) { IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL); } for (i=0; in; i++) { resid[i]=MATRIX(*vectors, i, 0); } } /* Ok, we have everything */ while (1) { #ifdef HAVE_GFORTRAN igraphdnaupd_(&ido, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->info, /*bmat_len=*/ 1, /*which_len=*/ 2); #else igraphdnaupd_(&ido, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->info); #endif if (ido==-1 || ido==1) { igraph_real_t *from=workd+options->ipntr[0]-1; igraph_real_t *to=workd+options->ipntr[1]-1; if (fun(to, from, options->n, extra) != 0) { IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", IGRAPH_ARPACK_PROD); } } else { break; } } if (options->info == 1) { igraph_i_arpack_report_no_convergence(options); } if (options->info != 0) { IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dnaupd(options->info)); } options->ierr=0; #ifdef HAVE_GFORTRAN igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv, &options->sigma, &options->sigmai, workev, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1, /*which_len=*/ 2); #else igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv, &options->sigma, &options->sigmai, workev, options->bmat, &options->n, options->which, &options->nev, &options->tol, resid, &options->ncv, v, &options->ldv, options->iparam, options->ipntr, workd, workl, &options->lworkl, &options->ierr); #endif if (options->ierr != 0) { IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dneupd(options->info)); } /* Save the result */ options->noiter=options->iparam[2]; options->nconv=options->iparam[4]; options->numop=options->iparam[8]; options->numopb=options->iparam[9]; options->numreo=options->iparam[10]; if (options->nconv < options->nev) { IGRAPH_WARNING("Not enough eigenvalues/vectors in ARPACK " "solver"); } if (values || vectors) { IGRAPH_CHECK(igraph_arpack_rnsort(values, vectors, options, dr, di, v)); } options->ldv=origldv; options->ncv=origncv; options->lworkl=origlworkl; options->which[0] = origwhich[0]; options->which[1] = origwhich[1]; options->tol=origtol; options->nev=orignev; /* Clean up if needed */ if (free_them) { igraph_Free(workev); igraph_Free(select); igraph_Free(resid); igraph_Free(di); igraph_Free(dr); igraph_Free(workd); igraph_Free(workl); igraph_Free(v); IGRAPH_FINALLY_CLEAN(8); } return 0; } /** * \function igraph_arpack_unpack_complex * \brief Make the result of the non-symmetric ARPACK solver more readable * * This function works on the output of \ref igraph_arpack_rnsolve and * brushes it up a bit: it only keeps \p nev eigenvalues/vectors and * every eigenvector is stored in two columns of the \p vectors * matrix. * * * The output of the non-symmetric ARPACK solver is somewhat hard to * parse, as real eigenvectors occupy only one column in the matrix, * and the complex conjugate eigenvectors are not stored at all * (usually). The other problem is that the solver might return more * eigenvalues than requested. The common use of this function is to * call it directly after \ref igraph_arpack_rnsolve with its \p * vectors and \p values argument and \c options->nev as \p nev. * \param vectors The eigenvector matrix, as returned by \ref * igraph_arpack_rnsolve. It will be resized, typically it will be * larger. * \param values The eigenvalue matrix, as returned by \ref * igraph_arpack_rnsolve. It will be resized, typically extra, * unneeded rows (=eigenvalues) will be removed. * \param nev The number of eigenvalues/vectors to keep. Can be less * or equal than the number originally requested from ARPACK. * \return Error code. * * Time complexity: linear in the number of elements in the \p vectors * matrix. */ int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, long int nev) { long int nodes=igraph_matrix_nrow(vectors); long int no_evs=igraph_matrix_nrow(values); long int i, j, k, wh; size_t colsize=(unsigned) nodes * sizeof(igraph_real_t); /* Error checks */ if (nev < 0) { IGRAPH_ERROR("`nev' cannot be negative", IGRAPH_EINVAL); } if (nev > no_evs) { IGRAPH_ERROR("`nev' too large, we don't have that many in `values'", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(vectors, nodes, nev * 2)); for (i=nev; i=origcol) { */ /* IGRAPH_WARNING("Too few columns in `vectors', ARPACK results are likely wrong"); */ /* } */ /* We copy the j-th eigenvector to the (k-1)-th and k-th column */ k=nev*2-1; for (i=nev-1; i>=0; i--) { if (MATRIX(*values,i,1)==0) { /* real */ memset( &MATRIX(*vectors,0,k), 0, colsize); if (k-1 != j) { memcpy( &MATRIX(*vectors,0,k-1), &MATRIX(*vectors,0,j), colsize); } k-=2; j-=1; } else { /* complex */ if (k!=j) { /* Separate copy required, otherwise 'from' and 'to' might overlap */ memcpy( &MATRIX(*vectors,0,k), &MATRIX(*vectors,0,j), colsize); memcpy( &MATRIX(*vectors,0,k-1), &MATRIX(*vectors,0,j-1), colsize); } if (i>1 && MATRIX(*values,i,1) != -MATRIX(*values,i-1,1)) { /* The next one is not a conjugate of this one */ j-=2; } else { /* Conjugate */ int l; for (l=0; l 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // **************************************************************************************************** // *** COPYRIGHT NOTICE ******************************************************************************* // graph_simp.h - graph data structure // Copyright (C) 2006-2008 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // **************************************************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark E.J. Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute // Created : 21 June 2006 // Modified : 23 December 2007 (cleaned up for public consumption) // // ************************************************************************ // // Simple graph data structure. The basic structure is an adjacency // list of edges, along with degree information for the vertices. // // ************************************************************************ #ifndef IGRAPH_HRG_SIMPLEGRAPH #define IGRAPH_HRG_SIMPLEGRAPH #include #include #include #include "hrg_rbtree.h" using namespace std; namespace fitHRG { // ******** Basic Structures ********************************************* #ifndef IGRAPH_HRG_SIMPLEEDGE #define IGRAPH_HRG_SIMPLEEDGE class simpleEdge { public: int x; // index of edge terminator simpleEdge* next; // pointer to next elementd simpleEdge(): x(-1), next(0) { } ~simpleEdge() { } }; #endif #ifndef IGRAPH_HRG_SIMPLEVERT #define IGRAPH_HRG_SIMPLEVERT class simpleVert { public: string name; // (external) name of vertex int degree; // degree of this vertex int group_true; // index of vertex's true group simpleVert(): name(""), degree(0), group_true(-1) { } ~simpleVert() { } }; #endif #ifndef IGRAPH_HRG_TWOEDGE #define IGRAPH_HRG_TWOEDGE class twoEdge { public: int o; // index of edge originator int x; // index of edge terminator twoEdge(): o(-1), x(-1) { } ~twoEdge() { } }; #endif // ******** Graph Class with Edge Statistics ***************************** class simpleGraph { public: simpleGraph(const int); ~simpleGraph(); // add group label to vertex i bool addGroup(const int, const int); // add (i,j) to graph bool addLink(const int, const int); // true if (i,j) is already in graph bool doesLinkExist(const int, const int); // returns A(i,j) double getAdjacency(const int, const int); // returns degree of vertex i int getDegree(const int); // returns group label of vertex i int getGroupLabel(const int); // returns name of vertex i string getName(const int); // returns edge list of vertex i simpleEdge* getNeighborList(const int); // return pointer to a node simpleVert* getNode(const int); // returns num_groups int getNumGroups(); // returns m int getNumLinks(); // returns n int getNumNodes(); // set name of vertex i bool setName(const int, const string); private: simpleVert* nodes; // list of nodes simpleEdge** nodeLink; // linked list of neighbors to vertex simpleEdge** nodeLinkTail; // pointers to tail of neighbor list double** A; // adjacency matrix for this graph twoEdge* E; // list of all edges (array) int n; // number of vertices int m; // number of directed edges int num_groups; // number of bins in node histograms // quicksort functions void QsortMain(block*, int, int); int QsortPartition(block*, int, int, int); }; } // namespace fitHRG #endif igraph/src/Sphere.h0000755000176000001440000000074212325527072013755 0ustar ripleyusers/** Sphere.h */ #ifndef SPHERE_H #define SPHERE_H #include "Shape.h" namespace igraph { class Sphere : public Shape { public: Sphere(); Sphere(Point vCenter, double vRadius); ~Sphere(); virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; double Radius() const; const Point& Center() const; private: Point mCenter; double mRadius; }; } // namespace igraph #endif igraph/src/cs_qrsol.c0000644000176000001440000000533112325527073014344 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* x=A\b where A can be rectangular; b overwritten with solution */ CS_INT cs_qrsol (CS_INT order, const cs *A, CS_ENTRY *b) { CS_ENTRY *x ; css *S ; csn *N ; cs *AT = NULL ; CS_INT k, m, n, ok ; if (!CS_CSC (A) || !b) return (0) ; /* check inputs */ n = A->n ; m = A->m ; if (m >= n) { S = cs_sqr (order, A, 1) ; /* ordering and symbolic analysis */ N = cs_qr (A, S) ; /* numeric QR factorization */ x = cs_calloc (S ? S->m2 : 1, sizeof (CS_ENTRY)) ; /* get workspace */ ok = (S && N && x) ; if (ok) { cs_ipvec (S->pinv, b, x, m) ; /* x(0:m-1) = b(p(0:m-1) */ for (k = 0 ; k < n ; k++) /* apply Householder refl. to x */ { cs_happly (N->L, k, N->B [k], x) ; } cs_usolve (N->U, x) ; /* x = R\x */ cs_ipvec (S->q, x, b, n) ; /* b(q(0:n-1)) = x(0:n-1) */ } } else { AT = cs_transpose (A, 1) ; /* Ax=b is underdetermined */ S = cs_sqr (order, AT, 1) ; /* ordering and symbolic analysis */ N = cs_qr (AT, S) ; /* numeric QR factorization of A' */ x = cs_calloc (S ? S->m2 : 1, sizeof (CS_ENTRY)) ; /* get workspace */ ok = (AT && S && N && x) ; if (ok) { cs_pvec (S->q, b, x, m) ; /* x(q(0:m-1)) = b(0:m-1) */ cs_utsolve (N->U, x) ; /* x = R'\x */ for (k = m-1 ; k >= 0 ; k--) /* apply Householder refl. to x */ { cs_happly (N->L, k, N->B [k], x) ; } cs_pvec (S->pinv, x, b, n) ; /* b(0:n-1) = x(p(0:n-1)) */ } } cs_free (x) ; cs_sfree (S) ; cs_nfree (N) ; cs_spfree (AT) ; return (ok) ; } igraph/src/glpnpp03.c0000644000176000001440000030107412325527073014165 0ustar ripleyusers/* glpnpp03.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpnpp.h" /*********************************************************************** * NAME * * npp_empty_row - process empty row * * SYNOPSIS * * #include "glpnpp.h" * int npp_empty_row(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_empty_row processes row p, which is empty, i.e. * coefficients at all columns in this row are zero: * * L[p] <= sum 0 x[j] <= U[p], (1) * * where L[p] <= U[p]. * * RETURNS * * 0 - success; * * 1 - problem has no primal feasible solution. * * PROBLEM TRANSFORMATION * * If the following conditions hold: * * L[p] <= +eps, U[p] >= -eps, (2) * * where eps is an absolute tolerance for row value, the row p is * redundant. In this case it can be replaced by equivalent redundant * row, which is free (unbounded), and then removed from the problem. * Otherwise, the row p is infeasible and, thus, the problem has no * primal feasible solution. * * RECOVERING BASIC SOLUTION * * See the routine npp_free_row. * * RECOVERING INTERIOR-POINT SOLUTION * * See the routine npp_free_row. * * RECOVERING MIP SOLUTION * * None needed. */ int npp_empty_row(NPP *npp, NPPROW *p) { /* process empty row */ double eps = 1e-3; /* the row must be empty */ xassert(p->ptr == NULL); /* check primal feasibility */ if (p->lb > +eps || p->ub < -eps) return 1; /* replace the row by equivalent free (unbounded) row */ p->lb = -DBL_MAX, p->ub = +DBL_MAX; /* and process it */ npp_free_row(npp, p); return 0; } /*********************************************************************** * NAME * * npp_empty_col - process empty column * * SYNOPSIS * * #include "glpnpp.h" * int npp_empty_col(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_empty_col processes column q: * * l[q] <= x[q] <= u[q], (1) * * where l[q] <= u[q], which is empty, i.e. has zero coefficients in * all constraint rows. * * RETURNS * * 0 - success; * * 1 - problem has no dual feasible solution. * * PROBLEM TRANSFORMATION * * The row of the dual system corresponding to the empty column is the * following: * * sum 0 pi[i] + lambda[q] = c[q], (2) * i * * from which it follows that: * * lambda[q] = c[q]. (3) * * If the following condition holds: * * c[q] < - eps, (4) * * where eps is an absolute tolerance for column multiplier, the lower * column bound l[q] must be active to provide dual feasibility (note * that being preprocessed the problem is always minimization). In this * case the column can be fixed on its lower bound and removed from the * problem (if the column is integral, its bounds are also assumed to * be integral). And if the column has no lower bound (l[q] = -oo), the * problem has no dual feasible solution. * * If the following condition holds: * * c[q] > + eps, (5) * * the upper column bound u[q] must be active to provide dual * feasibility. In this case the column can be fixed on its upper bound * and removed from the problem. And if the column has no upper bound * (u[q] = +oo), the problem has no dual feasible solution. * * Finally, if the following condition holds: * * - eps <= c[q] <= +eps, (6) * * dual feasibility does not depend on a particular value of column q. * In this case the column can be fixed either on its lower bound (if * l[q] > -oo) or on its upper bound (if u[q] < +oo) or at zero (if the * column is unbounded) and then removed from the problem. * * RECOVERING BASIC SOLUTION * * See the routine npp_fixed_col. Having been recovered the column * is assigned status GLP_NS. However, if actually it is not fixed * (l[q] < u[q]), its status should be changed to GLP_NL, GLP_NU, or * GLP_NF depending on which bound it was fixed on transformation stage. * * RECOVERING INTERIOR-POINT SOLUTION * * See the routine npp_fixed_col. * * RECOVERING MIP SOLUTION * * See the routine npp_fixed_col. */ struct empty_col { /* empty column */ int q; /* column reference number */ char stat; /* status in basic solution */ }; static int rcv_empty_col(NPP *npp, void *info); int npp_empty_col(NPP *npp, NPPCOL *q) { /* process empty column */ struct empty_col *info; double eps = 1e-3; /* the column must be empty */ xassert(q->ptr == NULL); /* check dual feasibility */ if (q->coef > +eps && q->lb == -DBL_MAX) return 1; if (q->coef < -eps && q->ub == +DBL_MAX) return 1; /* create transformation stack entry */ info = npp_push_tse(npp, rcv_empty_col, sizeof(struct empty_col)); info->q = q->j; /* fix the column */ if (q->lb == -DBL_MAX && q->ub == +DBL_MAX) { /* free column */ info->stat = GLP_NF; q->lb = q->ub = 0.0; } else if (q->ub == +DBL_MAX) lo: { /* column with lower bound */ info->stat = GLP_NL; q->ub = q->lb; } else if (q->lb == -DBL_MAX) up: { /* column with upper bound */ info->stat = GLP_NU; q->lb = q->ub; } else if (q->lb != q->ub) { /* double-bounded column */ if (q->coef >= +DBL_EPSILON) goto lo; if (q->coef <= -DBL_EPSILON) goto up; if (fabs(q->lb) <= fabs(q->ub)) goto lo; else goto up; } else { /* fixed column */ info->stat = GLP_NS; } /* process fixed column */ npp_fixed_col(npp, q); return 0; } static int rcv_empty_col(NPP *npp, void *_info) { /* recover empty column */ struct empty_col *info = _info; if (npp->sol == GLP_SOL) npp->c_stat[info->q] = info->stat; return 0; } /*********************************************************************** * NAME * * npp_implied_value - process implied column value * * SYNOPSIS * * #include "glpnpp.h" * int npp_implied_value(NPP *npp, NPPCOL *q, double s); * * DESCRIPTION * * For column q: * * l[q] <= x[q] <= u[q], (1) * * where l[q] < u[q], the routine npp_implied_value processes its * implied value s[q]. If this implied value satisfies to the current * column bounds and integrality condition, the routine fixes column q * at the given point. Note that the column is kept in the problem in * any case. * * RETURNS * * 0 - column has been fixed; * * 1 - implied value violates to current column bounds; * * 2 - implied value violates integrality condition. * * ALGORITHM * * Implied column value s[q] satisfies to the current column bounds if * the following condition holds: * * l[q] - eps <= s[q] <= u[q] + eps, (2) * * where eps is an absolute tolerance for column value. If the column * is integral, the following condition also must hold: * * |s[q] - floor(s[q]+0.5)| <= eps, (3) * * where floor(s[q]+0.5) is the nearest integer to s[q]. * * If both condition (2) and (3) are satisfied, the column can be fixed * at the value s[q], or, if it is integral, at floor(s[q]+0.5). * Otherwise, if s[q] violates (2) or (3), the problem has no feasible * solution. * * Note: If s[q] is close to l[q] or u[q], it seems to be reasonable to * fix the column at its lower or upper bound, resp. rather than at the * implied value. */ int npp_implied_value(NPP *npp, NPPCOL *q, double s) { /* process implied column value */ double eps, nint; xassert(npp == npp); /* column must not be fixed */ xassert(q->lb < q->ub); /* check integrality */ if (q->is_int) { nint = floor(s + 0.5); if (fabs(s - nint) <= 1e-5) s = nint; else return 2; } /* check current column lower bound */ if (q->lb != -DBL_MAX) { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->lb)); if (s < q->lb - eps) return 1; /* if s[q] is close to l[q], fix column at its lower bound rather than at the implied value */ if (s < q->lb + 1e-3 * eps) { q->ub = q->lb; return 0; } } /* check current column upper bound */ if (q->ub != +DBL_MAX) { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->ub)); if (s > q->ub + eps) return 1; /* if s[q] is close to u[q], fix column at its upper bound rather than at the implied value */ if (s > q->ub - 1e-3 * eps) { q->lb = q->ub; return 0; } } /* fix column at the implied value */ q->lb = q->ub = s; return 0; } /*********************************************************************** * NAME * * npp_eq_singlet - process row singleton (equality constraint) * * SYNOPSIS * * #include "glpnpp.h" * int npp_eq_singlet(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_eq_singlet processes row p, which is equiality * constraint having the only non-zero coefficient: * * a[p,q] x[q] = b. (1) * * RETURNS * * 0 - success; * * 1 - problem has no primal feasible solution; * * 2 - problem has no integer feasible solution. * * PROBLEM TRANSFORMATION * * The equality constraint defines implied value of column q: * * x[q] = s[q] = b / a[p,q]. (2) * * If the implied value s[q] satisfies to the column bounds (see the * routine npp_implied_value), the column can be fixed at s[q] and * removed from the problem. In this case row p becomes redundant, so * it can be replaced by equivalent free row and also removed from the * problem. * * Note that the routine removes from the problem only row p. Column q * becomes fixed, however, it is kept in the problem. * * RECOVERING BASIC SOLUTION * * In solution to the original problem row p is assigned status GLP_NS * (active equality constraint), and column q is assigned status GLP_BS * (basic column). * * Multiplier for row p can be computed as follows. In the dual system * of the original problem column q corresponds to the following row: * * sum a[i,q] pi[i] + lambda[q] = c[q] ==> * i * * sum a[i,q] pi[i] + a[p,q] pi[p] + lambda[q] = c[q]. * i!=p * * Therefore: * * 1 * pi[p] = ------ (c[q] - lambda[q] - sum a[i,q] pi[i]), (3) * a[p,q] i!=q * * where lambda[q] = 0 (since column[q] is basic), and pi[i] for all * i != p are known in solution to the transformed problem. * * Value of column q in solution to the original problem is assigned * its implied value s[q]. * * RECOVERING INTERIOR-POINT SOLUTION * * Multiplier for row p is computed with formula (3). Value of column * q is assigned its implied value s[q]. * * RECOVERING MIP SOLUTION * * Value of column q is assigned its implied value s[q]. */ struct eq_singlet { /* row singleton (equality constraint) */ int p; /* row reference number */ int q; /* column reference number */ double apq; /* constraint coefficient a[p,q] */ double c; /* objective coefficient at x[q] */ NPPLFE *ptr; /* list of non-zero coefficients a[i,q], i != p */ }; static int rcv_eq_singlet(NPP *npp, void *info); int npp_eq_singlet(NPP *npp, NPPROW *p) { /* process row singleton (equality constraint) */ struct eq_singlet *info; NPPCOL *q; NPPAIJ *aij; NPPLFE *lfe; int ret; double s; /* the row must be singleton equality constraint */ xassert(p->lb == p->ub); xassert(p->ptr != NULL && p->ptr->r_next == NULL); /* compute and process implied column value */ aij = p->ptr; q = aij->col; s = p->lb / aij->val; ret = npp_implied_value(npp, q, s); xassert(0 <= ret && ret <= 2); if (ret != 0) return ret; /* create transformation stack entry */ info = npp_push_tse(npp, rcv_eq_singlet, sizeof(struct eq_singlet)); info->p = p->i; info->q = q->j; info->apq = aij->val; info->c = q->coef; info->ptr = NULL; /* save column coefficients a[i,q], i != p (not needed for MIP solution) */ if (npp->sol != GLP_MIP) { for (aij = q->ptr; aij != NULL; aij = aij->c_next) { if (aij->row == p) continue; /* skip a[p,q] */ lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE)); lfe->ref = aij->row->i; lfe->val = aij->val; lfe->next = info->ptr; info->ptr = lfe; } } /* remove the row from the problem */ npp_del_row(npp, p); return 0; } static int rcv_eq_singlet(NPP *npp, void *_info) { /* recover row singleton (equality constraint) */ struct eq_singlet *info = _info; NPPLFE *lfe; double temp; if (npp->sol == GLP_SOL) { /* column q must be already recovered as GLP_NS */ if (npp->c_stat[info->q] != GLP_NS) { npp_error(); return 1; } npp->r_stat[info->p] = GLP_NS; npp->c_stat[info->q] = GLP_BS; } if (npp->sol != GLP_MIP) { /* compute multiplier for row p with formula (3) */ temp = info->c; for (lfe = info->ptr; lfe != NULL; lfe = lfe->next) temp -= lfe->val * npp->r_pi[lfe->ref]; npp->r_pi[info->p] = temp / info->apq; } return 0; } /*********************************************************************** * NAME * * npp_implied_lower - process implied column lower bound * * SYNOPSIS * * #include "glpnpp.h" * int npp_implied_lower(NPP *npp, NPPCOL *q, double l); * * DESCRIPTION * * For column q: * * l[q] <= x[q] <= u[q], (1) * * where l[q] < u[q], the routine npp_implied_lower processes its * implied lower bound l'[q]. As the result the current column lower * bound may increase. Note that the column is kept in the problem in * any case. * * RETURNS * * 0 - current column lower bound has not changed; * * 1 - current column lower bound has changed, but not significantly; * * 2 - current column lower bound has significantly changed; * * 3 - column has been fixed on its upper bound; * * 4 - implied lower bound violates current column upper bound. * * ALGORITHM * * If column q is integral, before processing its implied lower bound * should be rounded up: * * ( floor(l'[q]+0.5), if |l'[q] - floor(l'[q]+0.5)| <= eps * l'[q] := < (2) * ( ceil(l'[q]), otherwise * * where floor(l'[q]+0.5) is the nearest integer to l'[q], ceil(l'[q]) * is smallest integer not less than l'[q], and eps is an absolute * tolerance for column value. * * Processing implied column lower bound l'[q] includes the following * cases: * * 1) if l'[q] < l[q] + eps, implied lower bound is redundant; * * 2) if l[q] + eps <= l[q] <= u[q] + eps, current column lower bound * l[q] can be strengthened by replacing it with l'[q]. If in this * case new column lower bound becomes close to current column upper * bound u[q], the column can be fixed on its upper bound; * * 3) if l'[q] > u[q] + eps, implied lower bound violates current * column upper bound u[q], in which case the problem has no primal * feasible solution. */ int npp_implied_lower(NPP *npp, NPPCOL *q, double l) { /* process implied column lower bound */ int ret; double eps, nint; xassert(npp == npp); /* column must not be fixed */ xassert(q->lb < q->ub); /* implied lower bound must be finite */ xassert(l != -DBL_MAX); /* if column is integral, round up l'[q] */ if (q->is_int) { nint = floor(l + 0.5); if (fabs(l - nint) <= 1e-5) l = nint; else l = ceil(l); } /* check current column lower bound */ if (q->lb != -DBL_MAX) { eps = (q->is_int ? 1e-3 : 1e-3 + 1e-6 * fabs(q->lb)); if (l < q->lb + eps) { ret = 0; /* redundant */ goto done; } } /* check current column upper bound */ if (q->ub != +DBL_MAX) { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->ub)); if (l > q->ub + eps) { ret = 4; /* infeasible */ goto done; } /* if l'[q] is close to u[q], fix column at its upper bound */ if (l > q->ub - 1e-3 * eps) { q->lb = q->ub; ret = 3; /* fixed */ goto done; } } /* check if column lower bound changes significantly */ if (q->lb == -DBL_MAX) ret = 2; /* significantly */ else if (q->is_int && l > q->lb + 0.5) ret = 2; /* significantly */ else if (l > q->lb + 0.30 * (1.0 + fabs(q->lb))) ret = 2; /* significantly */ else ret = 1; /* not significantly */ /* set new column lower bound */ q->lb = l; done: return ret; } /*********************************************************************** * NAME * * npp_implied_upper - process implied column upper bound * * SYNOPSIS * * #include "glpnpp.h" * int npp_implied_upper(NPP *npp, NPPCOL *q, double u); * * DESCRIPTION * * For column q: * * l[q] <= x[q] <= u[q], (1) * * where l[q] < u[q], the routine npp_implied_upper processes its * implied upper bound u'[q]. As the result the current column upper * bound may decrease. Note that the column is kept in the problem in * any case. * * RETURNS * * 0 - current column upper bound has not changed; * * 1 - current column upper bound has changed, but not significantly; * * 2 - current column upper bound has significantly changed; * * 3 - column has been fixed on its lower bound; * * 4 - implied upper bound violates current column lower bound. * * ALGORITHM * * If column q is integral, before processing its implied upper bound * should be rounded down: * * ( floor(u'[q]+0.5), if |u'[q] - floor(l'[q]+0.5)| <= eps * u'[q] := < (2) * ( floor(l'[q]), otherwise * * where floor(u'[q]+0.5) is the nearest integer to u'[q], * floor(u'[q]) is largest integer not greater than u'[q], and eps is * an absolute tolerance for column value. * * Processing implied column upper bound u'[q] includes the following * cases: * * 1) if u'[q] > u[q] - eps, implied upper bound is redundant; * * 2) if l[q] - eps <= u[q] <= u[q] - eps, current column upper bound * u[q] can be strengthened by replacing it with u'[q]. If in this * case new column upper bound becomes close to current column lower * bound, the column can be fixed on its lower bound; * * 3) if u'[q] < l[q] - eps, implied upper bound violates current * column lower bound l[q], in which case the problem has no primal * feasible solution. */ int npp_implied_upper(NPP *npp, NPPCOL *q, double u) { int ret; double eps, nint; xassert(npp == npp); /* column must not be fixed */ xassert(q->lb < q->ub); /* implied upper bound must be finite */ xassert(u != +DBL_MAX); /* if column is integral, round down u'[q] */ if (q->is_int) { nint = floor(u + 0.5); if (fabs(u - nint) <= 1e-5) u = nint; else u = floor(u); } /* check current column upper bound */ if (q->ub != +DBL_MAX) { eps = (q->is_int ? 1e-3 : 1e-3 + 1e-6 * fabs(q->ub)); if (u > q->ub - eps) { ret = 0; /* redundant */ goto done; } } /* check current column lower bound */ if (q->lb != -DBL_MAX) { eps = (q->is_int ? 1e-5 : 1e-5 + 1e-8 * fabs(q->lb)); if (u < q->lb - eps) { ret = 4; /* infeasible */ goto done; } /* if u'[q] is close to l[q], fix column at its lower bound */ if (u < q->lb + 1e-3 * eps) { q->ub = q->lb; ret = 3; /* fixed */ goto done; } } /* check if column upper bound changes significantly */ if (q->ub == +DBL_MAX) ret = 2; /* significantly */ else if (q->is_int && u < q->ub - 0.5) ret = 2; /* significantly */ else if (u < q->ub - 0.30 * (1.0 + fabs(q->ub))) ret = 2; /* significantly */ else ret = 1; /* not significantly */ /* set new column upper bound */ q->ub = u; done: return ret; } /*********************************************************************** * NAME * * npp_ineq_singlet - process row singleton (inequality constraint) * * SYNOPSIS * * #include "glpnpp.h" * int npp_ineq_singlet(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_ineq_singlet processes row p, which is inequality * constraint having the only non-zero coefficient: * * L[p] <= a[p,q] * x[q] <= U[p], (1) * * where L[p] < U[p], L[p] > -oo and/or U[p] < +oo. * * RETURNS * * 0 - current column bounds have not changed; * * 1 - current column bounds have changed, but not significantly; * * 2 - current column bounds have significantly changed; * * 3 - column has been fixed on its lower or upper bound; * * 4 - problem has no primal feasible solution. * * PROBLEM TRANSFORMATION * * Inequality constraint (1) defines implied bounds of column q: * * ( L[p] / a[p,q], if a[p,q] > 0 * l'[q] = < (2) * ( U[p] / a[p,q], if a[p,q] < 0 * * ( U[p] / a[p,q], if a[p,q] > 0 * u'[q] = < (3) * ( L[p] / a[p,q], if a[p,q] < 0 * * If these implied bounds do not violate current bounds of column q: * * l[q] <= x[q] <= u[q], (4) * * they can be used to strengthen the current column bounds: * * l[q] := max(l[q], l'[q]), (5) * * u[q] := min(u[q], u'[q]). (6) * * (See the routines npp_implied_lower and npp_implied_upper.) * * Once bounds of row p (1) have been carried over column q, the row * becomes redundant, so it can be replaced by equivalent free row and * removed from the problem. * * Note that the routine removes from the problem only row p. Column q, * even it has been fixed, is kept in the problem. * * RECOVERING BASIC SOLUTION * * Note that the row in the dual system corresponding to column q is * the following: * * sum a[i,q] pi[i] + lambda[q] = c[q] ==> * i * (7) * sum a[i,q] pi[i] + a[p,q] pi[p] + lambda[q] = c[q], * i!=p * * where pi[i] for all i != p are known in solution to the transformed * problem. Row p does not exist in the transformed problem, so it has * zero multiplier there. This allows computing multiplier for column q * in solution to the transformed problem: * * lambda~[q] = c[q] - sum a[i,q] pi[i]. (8) * i!=p * * Let in solution to the transformed problem column q be non-basic * with lower bound active (GLP_NL, lambda~[q] >= 0), and this lower * bound be implied one l'[q]. From the original problem's standpoint * this then means that actually the original column lower bound l[q] * is inactive, and active is that row bound L[p] or U[p] that defines * the implied bound l'[q] (2). In this case in solution to the * original problem column q is assigned status GLP_BS while row p is * assigned status GLP_NL (if a[p,q] > 0) or GLP_NU (if a[p,q] < 0). * Since now column q is basic, its multiplier lambda[q] is zero. This * allows using (7) and (8) to find multiplier for row p in solution to * the original problem: * * 1 * pi[p] = ------ (c[q] - sum a[i,q] pi[i]) = lambda~[q] / a[p,q] (9) * a[p,q] i!=p * * Now let in solution to the transformed problem column q be non-basic * with upper bound active (GLP_NU, lambda~[q] <= 0), and this upper * bound be implied one u'[q]. As in the previous case this then means * that from the original problem's standpoint actually the original * column upper bound u[q] is inactive, and active is that row bound * L[p] or U[p] that defines the implied bound u'[q] (3). In this case * in solution to the original problem column q is assigned status * GLP_BS, row p is assigned status GLP_NU (if a[p,q] > 0) or GLP_NL * (if a[p,q] < 0), and its multiplier is computed with formula (9). * * Strengthening bounds of column q according to (5) and (6) may make * it fixed. Thus, if in solution to the transformed problem column q is * non-basic and fixed (GLP_NS), we can suppose that if lambda~[q] > 0, * column q has active lower bound (GLP_NL), and if lambda~[q] < 0, * column q has active upper bound (GLP_NU), reducing this case to two * previous ones. If, however, lambda~[q] is close to zero or * corresponding bound of row p does not exist (this may happen if * lambda~[q] has wrong sign due to round-off errors, in which case it * is expected to be close to zero, since solution is assumed to be dual * feasible), column q can be assigned status GLP_BS (basic), and row p * can be made active on its existing bound. In the latter case row * multiplier pi[p] computed with formula (9) will be also close to * zero, and dual feasibility will be kept. * * In all other cases, namely, if in solution to the transformed * problem column q is basic (GLP_BS), or non-basic with original lower * bound l[q] active (GLP_NL), or non-basic with original upper bound * u[q] active (GLP_NU), constraint (1) is inactive. So in solution to * the original problem status of column q remains unchanged, row p is * assigned status GLP_BS, and its multiplier pi[p] is assigned zero * value. * * RECOVERING INTERIOR-POINT SOLUTION * * First, value of multiplier for column q in solution to the original * problem is computed with formula (8). If lambda~[q] > 0 and column q * has implied lower bound, or if lambda~[q] < 0 and column q has * implied upper bound, this means that from the original problem's * standpoint actually row p has corresponding active bound, in which * case its multiplier pi[p] is computed with formula (9). In other * cases, when the sign of lambda~[q] corresponds to original bound of * column q, or when lambda~[q] =~ 0, value of row multiplier pi[p] is * assigned zero value. * * RECOVERING MIP SOLUTION * * None needed. */ struct ineq_singlet { /* row singleton (inequality constraint) */ int p; /* row reference number */ int q; /* column reference number */ double apq; /* constraint coefficient a[p,q] */ double c; /* objective coefficient at x[q] */ double lb; /* row lower bound */ double ub; /* row upper bound */ char lb_changed; /* this flag is set if column lower bound was changed */ char ub_changed; /* this flag is set if column upper bound was changed */ NPPLFE *ptr; /* list of non-zero coefficients a[i,q], i != p */ }; static int rcv_ineq_singlet(NPP *npp, void *info); int npp_ineq_singlet(NPP *npp, NPPROW *p) { /* process row singleton (inequality constraint) */ struct ineq_singlet *info; NPPCOL *q; NPPAIJ *apq, *aij; NPPLFE *lfe; int lb_changed, ub_changed; double ll, uu; /* the row must be singleton inequality constraint */ xassert(p->lb != -DBL_MAX || p->ub != +DBL_MAX); xassert(p->lb < p->ub); xassert(p->ptr != NULL && p->ptr->r_next == NULL); /* compute implied column bounds */ apq = p->ptr; q = apq->col; xassert(q->lb < q->ub); if (apq->val > 0.0) { ll = (p->lb == -DBL_MAX ? -DBL_MAX : p->lb / apq->val); uu = (p->ub == +DBL_MAX ? +DBL_MAX : p->ub / apq->val); } else { ll = (p->ub == +DBL_MAX ? -DBL_MAX : p->ub / apq->val); uu = (p->lb == -DBL_MAX ? +DBL_MAX : p->lb / apq->val); } /* process implied column lower bound */ if (ll == -DBL_MAX) lb_changed = 0; else { lb_changed = npp_implied_lower(npp, q, ll); xassert(0 <= lb_changed && lb_changed <= 4); if (lb_changed == 4) return 4; /* infeasible */ } /* process implied column upper bound */ if (uu == +DBL_MAX) ub_changed = 0; else if (lb_changed == 3) { /* column was fixed on its upper bound due to l'[q] = u[q] */ /* note that L[p] < U[p], so l'[q] = u[q] < u'[q] */ ub_changed = 0; } else { ub_changed = npp_implied_upper(npp, q, uu); xassert(0 <= ub_changed && ub_changed <= 4); if (ub_changed == 4) return 4; /* infeasible */ } /* if neither lower nor upper column bound was changed, the row is originally redundant and can be replaced by free row */ if (!lb_changed && !ub_changed) { p->lb = -DBL_MAX, p->ub = +DBL_MAX; npp_free_row(npp, p); return 0; } /* create transformation stack entry */ info = npp_push_tse(npp, rcv_ineq_singlet, sizeof(struct ineq_singlet)); info->p = p->i; info->q = q->j; info->apq = apq->val; info->c = q->coef; info->lb = p->lb; info->ub = p->ub; info->lb_changed = (char)lb_changed; info->ub_changed = (char)ub_changed; info->ptr = NULL; /* save column coefficients a[i,q], i != p (not needed for MIP solution) */ if (npp->sol != GLP_MIP) { for (aij = q->ptr; aij != NULL; aij = aij->c_next) { if (aij == apq) continue; /* skip a[p,q] */ lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE)); lfe->ref = aij->row->i; lfe->val = aij->val; lfe->next = info->ptr; info->ptr = lfe; } } /* remove the row from the problem */ npp_del_row(npp, p); return lb_changed >= ub_changed ? lb_changed : ub_changed; } static int rcv_ineq_singlet(NPP *npp, void *_info) { /* recover row singleton (inequality constraint) */ struct ineq_singlet *info = _info; NPPLFE *lfe; double lambda; if (npp->sol == GLP_MIP) goto done; /* compute lambda~[q] in solution to the transformed problem with formula (8) */ lambda = info->c; for (lfe = info->ptr; lfe != NULL; lfe = lfe->next) lambda -= lfe->val * npp->r_pi[lfe->ref]; if (npp->sol == GLP_SOL) { /* recover basic solution */ if (npp->c_stat[info->q] == GLP_BS) { /* column q is basic, so row p is inactive */ npp->r_stat[info->p] = GLP_BS; npp->r_pi[info->p] = 0.0; } else if (npp->c_stat[info->q] == GLP_NL) nl: { /* column q is non-basic with lower bound active */ if (info->lb_changed) { /* it is implied bound, so actually row p is active while column q is basic */ npp->r_stat[info->p] = (char)(info->apq > 0.0 ? GLP_NL : GLP_NU); npp->c_stat[info->q] = GLP_BS; npp->r_pi[info->p] = lambda / info->apq; } else { /* it is original bound, so row p is inactive */ npp->r_stat[info->p] = GLP_BS; npp->r_pi[info->p] = 0.0; } } else if (npp->c_stat[info->q] == GLP_NU) nu: { /* column q is non-basic with upper bound active */ if (info->ub_changed) { /* it is implied bound, so actually row p is active while column q is basic */ npp->r_stat[info->p] = (char)(info->apq > 0.0 ? GLP_NU : GLP_NL); npp->c_stat[info->q] = GLP_BS; npp->r_pi[info->p] = lambda / info->apq; } else { /* it is original bound, so row p is inactive */ npp->r_stat[info->p] = GLP_BS; npp->r_pi[info->p] = 0.0; } } else if (npp->c_stat[info->q] == GLP_NS) { /* column q is non-basic and fixed; note, however, that in in the original problem it is non-fixed */ if (lambda > +1e-7) { if (info->apq > 0.0 && info->lb != -DBL_MAX || info->apq < 0.0 && info->ub != +DBL_MAX || !info->lb_changed) { /* either corresponding bound of row p exists or column q remains non-basic with its original lower bound active */ npp->c_stat[info->q] = GLP_NL; goto nl; } } if (lambda < -1e-7) { if (info->apq > 0.0 && info->ub != +DBL_MAX || info->apq < 0.0 && info->lb != -DBL_MAX || !info->ub_changed) { /* either corresponding bound of row p exists or column q remains non-basic with its original upper bound active */ npp->c_stat[info->q] = GLP_NU; goto nu; } } /* either lambda~[q] is close to zero, or corresponding bound of row p does not exist, because lambda~[q] has wrong sign due to round-off errors; in the latter case lambda~[q] is also assumed to be close to zero; so, we can make row p active on its existing bound and column q basic; pi[p] will have wrong sign, but it also will be close to zero (rarus casus of dual degeneracy) */ if (info->lb != -DBL_MAX && info->ub == +DBL_MAX) { /* row lower bound exists, but upper bound doesn't */ npp->r_stat[info->p] = GLP_NL; } else if (info->lb == -DBL_MAX && info->ub != +DBL_MAX) { /* row upper bound exists, but lower bound doesn't */ npp->r_stat[info->p] = GLP_NU; } else if (info->lb != -DBL_MAX && info->ub != +DBL_MAX) { /* both row lower and upper bounds exist */ /* to choose proper active row bound we should not use lambda~[q], because its value being close to zero is unreliable; so we choose that bound which provides primal feasibility for original constraint (1) */ if (info->apq * npp->c_value[info->q] <= 0.5 * (info->lb + info->ub)) npp->r_stat[info->p] = GLP_NL; else npp->r_stat[info->p] = GLP_NU; } else { npp_error(); return 1; } npp->c_stat[info->q] = GLP_BS; npp->r_pi[info->p] = lambda / info->apq; } else { npp_error(); return 1; } } if (npp->sol == GLP_IPT) { /* recover interior-point solution */ if (lambda > +DBL_EPSILON && info->lb_changed || lambda < -DBL_EPSILON && info->ub_changed) { /* actually row p has corresponding active bound */ npp->r_pi[info->p] = lambda / info->apq; } else { /* either bounds of column q are both inactive or its original bound is active */ npp->r_pi[info->p] = 0.0; } } done: return 0; } /*********************************************************************** * NAME * * npp_implied_slack - process column singleton (implied slack variable) * * SYNOPSIS * * #include "glpnpp.h" * void npp_implied_slack(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_implied_slack processes column q: * * l[q] <= x[q] <= u[q], (1) * * where l[q] < u[q], having the only non-zero coefficient in row p, * which is equality constraint: * * sum a[p,j] x[j] + a[p,q] x[q] = b. (2) * j!=q * * PROBLEM TRANSFORMATION * * (If x[q] is integral, this transformation must not be used.) * * The term a[p,q] x[q] in constraint (2) can be considered as a slack * variable that allows to carry bounds of column q over row p and then * remove column q from the problem. * * Constraint (2) can be written as follows: * * sum a[p,j] x[j] = b - a[p,q] x[q]. (3) * j!=q * * According to (1) constraint (3) is equivalent to the following * inequality constraint: * * L[p] <= sum a[p,j] x[j] <= U[p], (4) * j!=q * * where * * ( b - a[p,q] u[q], if a[p,q] > 0 * L[p] = < (5) * ( b - a[p,q] l[q], if a[p,q] < 0 * * ( b - a[p,q] l[q], if a[p,q] > 0 * U[p] = < (6) * ( b - a[p,q] u[q], if a[p,q] < 0 * * From (2) it follows that: * * 1 * x[q] = ------ (b - sum a[p,j] x[j]). (7) * a[p,q] j!=q * * In order to eliminate x[q] from the objective row we substitute it * from (6) to that row: * * z = sum c[j] x[j] + c[q] x[q] + c[0] = * j!=q * 1 * = sum c[j] x[j] + c[q] [------ (b - sum a[p,j] x[j])] + c0 = * j!=q a[p,q] j!=q * * = sum c~[j] x[j] + c~[0], * j!=q * a[p,j] b * c~[j] = c[j] - c[q] ------, c~0 = c0 - c[q] ------ (8) * a[p,q] a[p,q] * * are values of objective coefficients and constant term, resp., in * the transformed problem. * * Note that column q is column singleton, so in the dual system of the * original problem it corresponds to the following row singleton: * * a[p,q] pi[p] + lambda[q] = c[q]. (9) * * In the transformed problem row (9) would be the following: * * a[p,q] pi~[p] + lambda[q] = c~[q] = 0. (10) * * Subtracting (10) from (9) we have: * * a[p,q] (pi[p] - pi~[p]) = c[q] * * that gives the following formula to compute multiplier for row p in * solution to the original problem using its value in solution to the * transformed problem: * * pi[p] = pi~[p] + c[q] / a[p,q]. (11) * * RECOVERING BASIC SOLUTION * * Status of column q in solution to the original problem is defined * by status of row p in solution to the transformed problem and the * sign of coefficient a[p,q] in the original inequality constraint (2) * as follows: * * +-----------------------+---------+--------------------+ * | Status of row p | Sign of | Status of column q | * | (transformed problem) | a[p,q] | (original problem) | * +-----------------------+---------+--------------------+ * | GLP_BS | + / - | GLP_BS | * | GLP_NL | + | GLP_NU | * | GLP_NL | - | GLP_NL | * | GLP_NU | + | GLP_NL | * | GLP_NU | - | GLP_NU | * | GLP_NF | + / - | GLP_NF | * +-----------------------+---------+--------------------+ * * Value of column q is computed with formula (7). Since originally row * p is equality constraint, its status is assigned GLP_NS, and value of * its multiplier pi[p] is computed with formula (11). * * RECOVERING INTERIOR-POINT SOLUTION * * Value of column q is computed with formula (7). Row multiplier value * pi[p] is computed with formula (11). * * RECOVERING MIP SOLUTION * * Value of column q is computed with formula (7). */ struct implied_slack { /* column singleton (implied slack variable) */ int p; /* row reference number */ int q; /* column reference number */ double apq; /* constraint coefficient a[p,q] */ double b; /* right-hand side of original equality constraint */ double c; /* original objective coefficient at x[q] */ NPPLFE *ptr; /* list of non-zero coefficients a[p,j], j != q */ }; static int rcv_implied_slack(NPP *npp, void *info); void npp_implied_slack(NPP *npp, NPPCOL *q) { /* process column singleton (implied slack variable) */ struct implied_slack *info; NPPROW *p; NPPAIJ *aij; NPPLFE *lfe; /* the column must be non-integral non-fixed singleton */ xassert(!q->is_int); xassert(q->lb < q->ub); xassert(q->ptr != NULL && q->ptr->c_next == NULL); /* corresponding row must be equality constraint */ aij = q->ptr; p = aij->row; xassert(p->lb == p->ub); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_implied_slack, sizeof(struct implied_slack)); info->p = p->i; info->q = q->j; info->apq = aij->val; info->b = p->lb; info->c = q->coef; info->ptr = NULL; /* save row coefficients a[p,j], j != q, and substitute x[q] into the objective row */ for (aij = p->ptr; aij != NULL; aij = aij->r_next) { if (aij->col == q) continue; /* skip a[p,q] */ lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE)); lfe->ref = aij->col->j; lfe->val = aij->val; lfe->next = info->ptr; info->ptr = lfe; aij->col->coef -= info->c * (aij->val / info->apq); } npp->c0 += info->c * (info->b / info->apq); /* compute new row bounds */ if (info->apq > 0.0) { p->lb = (q->ub == +DBL_MAX ? -DBL_MAX : info->b - info->apq * q->ub); p->ub = (q->lb == -DBL_MAX ? +DBL_MAX : info->b - info->apq * q->lb); } else { p->lb = (q->lb == -DBL_MAX ? -DBL_MAX : info->b - info->apq * q->lb); p->ub = (q->ub == +DBL_MAX ? +DBL_MAX : info->b - info->apq * q->ub); } /* remove the column from the problem */ npp_del_col(npp, q); return; } static int rcv_implied_slack(NPP *npp, void *_info) { /* recover column singleton (implied slack variable) */ struct implied_slack *info = _info; NPPLFE *lfe; double temp; if (npp->sol == GLP_SOL) { /* assign statuses to row p and column q */ if (npp->r_stat[info->p] == GLP_BS || npp->r_stat[info->p] == GLP_NF) npp->c_stat[info->q] = npp->r_stat[info->p]; else if (npp->r_stat[info->p] == GLP_NL) npp->c_stat[info->q] = (char)(info->apq > 0.0 ? GLP_NU : GLP_NL); else if (npp->r_stat[info->p] == GLP_NU) npp->c_stat[info->q] = (char)(info->apq > 0.0 ? GLP_NL : GLP_NU); else { npp_error(); return 1; } npp->r_stat[info->p] = GLP_NS; } if (npp->sol != GLP_MIP) { /* compute multiplier for row p */ npp->r_pi[info->p] += info->c / info->apq; } /* compute value of column q */ temp = info->b; for (lfe = info->ptr; lfe != NULL; lfe = lfe->next) temp -= lfe->val * npp->c_value[lfe->ref]; npp->c_value[info->q] = temp / info->apq; return 0; } /*********************************************************************** * NAME * * npp_implied_free - process column singleton (implied free variable) * * SYNOPSIS * * #include "glpnpp.h" * int npp_implied_free(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_implied_free processes column q: * * l[q] <= x[q] <= u[q], (1) * * having non-zero coefficient in the only row p, which is inequality * constraint: * * L[p] <= sum a[p,j] x[j] + a[p,q] x[q] <= U[p], (2) * j!=q * * where l[q] < u[q], L[p] < U[p], L[p] > -oo and/or U[p] < +oo. * * RETURNS * * 0 - success; * * 1 - column lower and/or upper bound(s) can be active; * * 2 - problem has no dual feasible solution. * * PROBLEM TRANSFORMATION * * Constraint (2) can be written as follows: * * L[p] - sum a[p,j] x[j] <= a[p,q] x[q] <= U[p] - sum a[p,j] x[j], * j!=q j!=q * * from which it follows that: * * alfa <= a[p,q] x[q] <= beta, (3) * * where * * alfa = inf(L[p] - sum a[p,j] x[j]) = * j!=q * * = L[p] - sup sum a[p,j] x[j] = (4) * j!=q * * = L[p] - sum a[p,j] u[j] - sum a[p,j] l[j], * j in Jp j in Jn * * beta = sup(L[p] - sum a[p,j] x[j]) = * j!=q * * = L[p] - inf sum a[p,j] x[j] = (5) * j!=q * * = L[p] - sum a[p,j] l[j] - sum a[p,j] u[j], * j in Jp j in Jn * * Jp = {j != q: a[p,j] > 0}, Jn = {j != q: a[p,j] < 0}. (6) * * Inequality (3) defines implied bounds of variable x[q]: * * l'[q] <= x[q] <= u'[q], (7) * * where * * ( alfa / a[p,q], if a[p,q] > 0 * l'[q] = < (8a) * ( beta / a[p,q], if a[p,q] < 0 * * ( beta / a[p,q], if a[p,q] > 0 * u'[q] = < (8b) * ( alfa / a[p,q], if a[p,q] < 0 * * Thus, if l'[q] > l[q] - eps and u'[q] < u[q] + eps, where eps is * an absolute tolerance for column value, column bounds (1) cannot be * active, in which case column q can be replaced by equivalent free * (unbounded) column. * * Note that column q is column singleton, so in the dual system of the * original problem it corresponds to the following row singleton: * * a[p,q] pi[p] + lambda[q] = c[q], (9) * * from which it follows that: * * pi[p] = (c[q] - lambda[q]) / a[p,q]. (10) * * Let x[q] be implied free (unbounded) variable. Then column q can be * only basic, so its multiplier lambda[q] is equal to zero, and from * (10) we have: * * pi[p] = c[q] / a[p,q]. (11) * * There are possible three cases: * * 1) pi[p] < -eps, where eps is an absolute tolerance for row * multiplier. In this case, to provide dual feasibility of the * original problem, row p must be active on its lower bound, and * if its lower bound does not exist (L[p] = -oo), the problem has * no dual feasible solution; * * 2) pi[p] > +eps. In this case row p must be active on its upper * bound, and if its upper bound does not exist (U[p] = +oo), the * problem has no dual feasible solution; * * 3) -eps <= pi[p] <= +eps. In this case any (either lower or upper) * bound of row p can be active, because this does not affect dual * feasibility. * * Thus, in all three cases original inequality constraint (2) can be * replaced by equality constraint, where the right-hand side is either * lower or upper bound of row p, and bounds of column q can be removed * that makes it free (unbounded). (May note that this transformation * can be followed by transformation "Column singleton (implied slack * variable)" performed by the routine npp_implied_slack.) * * RECOVERING BASIC SOLUTION * * Status of row p in solution to the original problem is determined * by its status in solution to the transformed problem and its bound, * which was choosen to be active: * * +-----------------------+--------+--------------------+ * | Status of row p | Active | Status of row p | * | (transformed problem) | bound | (original problem) | * +-----------------------+--------+--------------------+ * | GLP_BS | L[p] | GLP_BS | * | GLP_BS | U[p] | GLP_BS | * | GLP_NS | L[p] | GLP_NL | * | GLP_NS | U[p] | GLP_NU | * +-----------------------+--------+--------------------+ * * Value of row multiplier pi[p] (as well as value of column q) in * solution to the original problem is the same as in solution to the * transformed problem. * * RECOVERING INTERIOR-POINT SOLUTION * * Value of row multiplier pi[p] in solution to the original problem is * the same as in solution to the transformed problem. * * RECOVERING MIP SOLUTION * * None needed. */ struct implied_free { /* column singleton (implied free variable) */ int p; /* row reference number */ char stat; /* row status: GLP_NL - active constraint on lower bound GLP_NU - active constraint on upper bound */ }; static int rcv_implied_free(NPP *npp, void *info); int npp_implied_free(NPP *npp, NPPCOL *q) { /* process column singleton (implied free variable) */ struct implied_free *info; NPPROW *p; NPPAIJ *apq, *aij; double alfa, beta, l, u, pi, eps; /* the column must be non-fixed singleton */ xassert(q->lb < q->ub); xassert(q->ptr != NULL && q->ptr->c_next == NULL); /* corresponding row must be inequality constraint */ apq = q->ptr; p = apq->row; xassert(p->lb != -DBL_MAX || p->ub != +DBL_MAX); xassert(p->lb < p->ub); /* compute alfa */ alfa = p->lb; if (alfa != -DBL_MAX) { for (aij = p->ptr; aij != NULL; aij = aij->r_next) { if (aij == apq) continue; /* skip a[p,q] */ if (aij->val > 0.0) { if (aij->col->ub == +DBL_MAX) { alfa = -DBL_MAX; break; } alfa -= aij->val * aij->col->ub; } else /* < 0.0 */ { if (aij->col->lb == -DBL_MAX) { alfa = -DBL_MAX; break; } alfa -= aij->val * aij->col->lb; } } } /* compute beta */ beta = p->ub; if (beta != +DBL_MAX) { for (aij = p->ptr; aij != NULL; aij = aij->r_next) { if (aij == apq) continue; /* skip a[p,q] */ if (aij->val > 0.0) { if (aij->col->lb == -DBL_MAX) { beta = +DBL_MAX; break; } beta -= aij->val * aij->col->lb; } else /* < 0.0 */ { if (aij->col->ub == +DBL_MAX) { beta = +DBL_MAX; break; } beta -= aij->val * aij->col->ub; } } } /* compute implied column lower bound l'[q] */ if (apq->val > 0.0) l = (alfa == -DBL_MAX ? -DBL_MAX : alfa / apq->val); else /* < 0.0 */ l = (beta == +DBL_MAX ? -DBL_MAX : beta / apq->val); /* compute implied column upper bound u'[q] */ if (apq->val > 0.0) u = (beta == +DBL_MAX ? +DBL_MAX : beta / apq->val); else u = (alfa == -DBL_MAX ? +DBL_MAX : alfa / apq->val); /* check if column lower bound l[q] can be active */ if (q->lb != -DBL_MAX) { eps = 1e-9 + 1e-12 * fabs(q->lb); if (l < q->lb - eps) return 1; /* yes, it can */ } /* check if column upper bound u[q] can be active */ if (q->ub != +DBL_MAX) { eps = 1e-9 + 1e-12 * fabs(q->ub); if (u > q->ub + eps) return 1; /* yes, it can */ } /* okay; make column q free (unbounded) */ q->lb = -DBL_MAX, q->ub = +DBL_MAX; /* create transformation stack entry */ info = npp_push_tse(npp, rcv_implied_free, sizeof(struct implied_free)); info->p = p->i; info->stat = -1; /* compute row multiplier pi[p] */ pi = q->coef / apq->val; /* check dual feasibility for row p */ if (pi > +DBL_EPSILON) { /* lower bound L[p] must be active */ if (p->lb != -DBL_MAX) nl: { info->stat = GLP_NL; p->ub = p->lb; } else { if (pi > +1e-5) return 2; /* dual infeasibility */ /* take a chance on U[p] */ xassert(p->ub != +DBL_MAX); goto nu; } } else if (pi < -DBL_EPSILON) { /* upper bound U[p] must be active */ if (p->ub != +DBL_MAX) nu: { info->stat = GLP_NU; p->lb = p->ub; } else { if (pi < -1e-5) return 2; /* dual infeasibility */ /* take a chance on L[p] */ xassert(p->lb != -DBL_MAX); goto nl; } } else { /* any bound (either L[p] or U[p]) can be made active */ if (p->ub == +DBL_MAX) { xassert(p->lb != -DBL_MAX); goto nl; } if (p->lb == -DBL_MAX) { xassert(p->ub != +DBL_MAX); goto nu; } if (fabs(p->lb) <= fabs(p->ub)) goto nl; else goto nu; } return 0; } static int rcv_implied_free(NPP *npp, void *_info) { /* recover column singleton (implied free variable) */ struct implied_free *info = _info; if (npp->sol == GLP_SOL) { if (npp->r_stat[info->p] == GLP_BS) npp->r_stat[info->p] = GLP_BS; else if (npp->r_stat[info->p] == GLP_NS) { xassert(info->stat == GLP_NL || info->stat == GLP_NU); npp->r_stat[info->p] = info->stat; } else { npp_error(); return 1; } } return 0; } /*********************************************************************** * NAME * * npp_eq_doublet - process row doubleton (equality constraint) * * SYNOPSIS * * #include "glpnpp.h" * NPPCOL *npp_eq_doublet(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_eq_doublet processes row p, which is equality * constraint having exactly two non-zero coefficients: * * a[p,q] x[q] + a[p,r] x[r] = b. (1) * * As the result of processing one of columns q or r is eliminated from * all other rows and, thus, becomes column singleton of type "implied * slack variable". Row p is not changed and along with column q and r * remains in the problem. * * RETURNS * * The routine npp_eq_doublet returns pointer to the descriptor of that * column q or r which has been eliminated. If, due to some reason, the * elimination was not performed, the routine returns NULL. * * PROBLEM TRANSFORMATION * * First, we decide which column q or r will be eliminated. Let it be * column q. Consider i-th constraint row, where column q has non-zero * coefficient a[i,q] != 0: * * L[i] <= sum a[i,j] x[j] <= U[i]. (2) * j * * In order to eliminate column q from row (2) we subtract from it row * (1) multiplied by gamma[i] = a[i,q] / a[p,q], i.e. we replace in the * transformed problem row (2) by its linear combination with row (1). * This transformation changes only coefficients in columns q and r, * and bounds of row i as follows: * * a~[i,q] = a[i,q] - gamma[i] a[p,q] = 0, (3) * * a~[i,r] = a[i,r] - gamma[i] a[p,r], (4) * * L~[i] = L[i] - gamma[i] b, (5) * * U~[i] = U[i] - gamma[i] b. (6) * * RECOVERING BASIC SOLUTION * * The transformation of the primal system of the original problem: * * L <= A x <= U (7) * * is equivalent to multiplying from the left a transformation matrix F * by components of this primal system, which in the transformed problem * becomes the following: * * F L <= F A x <= F U ==> L~ <= A~x <= U~. (8) * * The matrix F has the following structure: * * ( 1 -gamma[1] ) * ( ) * ( 1 -gamma[2] ) * ( ) * ( ... ... ) * ( ) * F = ( 1 -gamma[p-1] ) (9) * ( ) * ( 1 ) * ( ) * ( -gamma[p+1] 1 ) * ( ) * ( ... ... ) * * where its column containing elements -gamma[i] corresponds to row p * of the primal system. * * From (8) it follows that the dual system of the original problem: * * A'pi + lambda = c, (10) * * in the transformed problem becomes the following: * * A'F'inv(F')pi + lambda = c ==> (A~)'pi~ + lambda = c, (11) * * where: * * pi~ = inv(F')pi (12) * * is the vector of row multipliers in the transformed problem. Thus: * * pi = F'pi~. (13) * * Therefore, as it follows from (13), value of multiplier for row p in * solution to the original problem can be computed as follows: * * pi[p] = pi~[p] - sum gamma[i] pi~[i], (14) * i * * where pi~[i] = pi[i] is multiplier for row i (i != p). * * Note that the statuses of all rows and columns are not changed. * * RECOVERING INTERIOR-POINT SOLUTION * * Multiplier for row p in solution to the original problem is computed * with formula (14). * * RECOVERING MIP SOLUTION * * None needed. */ struct eq_doublet { /* row doubleton (equality constraint) */ int p; /* row reference number */ double apq; /* constraint coefficient a[p,q] */ NPPLFE *ptr; /* list of non-zero coefficients a[i,q], i != p */ }; static int rcv_eq_doublet(NPP *npp, void *info); NPPCOL *npp_eq_doublet(NPP *npp, NPPROW *p) { /* process row doubleton (equality constraint) */ struct eq_doublet *info; NPPROW *i; NPPCOL *q, *r; NPPAIJ *apq, *apr, *aiq, *air, *next; NPPLFE *lfe; double gamma; /* the row must be doubleton equality constraint */ xassert(p->lb == p->ub); xassert(p->ptr != NULL && p->ptr->r_next != NULL && p->ptr->r_next->r_next == NULL); /* choose column to be eliminated */ { NPPAIJ *a1, *a2; a1 = p->ptr, a2 = a1->r_next; if (fabs(a2->val) < 0.001 * fabs(a1->val)) { /* only first column can be eliminated, because second one has too small constraint coefficient */ apq = a1, apr = a2; } else if (fabs(a1->val) < 0.001 * fabs(a2->val)) { /* only second column can be eliminated, because first one has too small constraint coefficient */ apq = a2, apr = a1; } else { /* both columns are appropriate; choose that one which is shorter to minimize fill-in */ if (npp_col_nnz(npp, a1->col) <= npp_col_nnz(npp, a2->col)) { /* first column is shorter */ apq = a1, apr = a2; } else { /* second column is shorter */ apq = a2, apr = a1; } } } /* now columns q and r have been chosen */ q = apq->col, r = apr->col; /* create transformation stack entry */ info = npp_push_tse(npp, rcv_eq_doublet, sizeof(struct eq_doublet)); info->p = p->i; info->apq = apq->val; info->ptr = NULL; /* transform each row i (i != p), where a[i,q] != 0, to eliminate column q */ for (aiq = q->ptr; aiq != NULL; aiq = next) { next = aiq->c_next; if (aiq == apq) continue; /* skip row p */ i = aiq->row; /* row i to be transformed */ /* save constraint coefficient a[i,q] */ if (npp->sol != GLP_MIP) { lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE)); lfe->ref = i->i; lfe->val = aiq->val; lfe->next = info->ptr; info->ptr = lfe; } /* find coefficient a[i,r] in row i */ for (air = i->ptr; air != NULL; air = air->r_next) if (air->col == r) break; /* if a[i,r] does not exist, create a[i,r] = 0 */ if (air == NULL) air = npp_add_aij(npp, i, r, 0.0); /* compute gamma[i] = a[i,q] / a[p,q] */ gamma = aiq->val / apq->val; /* (row i) := (row i) - gamma[i] * (row p); see (3)-(6) */ /* new a[i,q] is exact zero due to elimnation; remove it from row i */ npp_del_aij(npp, aiq); /* compute new a[i,r] */ air->val -= gamma * apr->val; /* if new a[i,r] is close to zero due to numeric cancelation, remove it from row i */ if (fabs(air->val) <= 1e-10) npp_del_aij(npp, air); /* compute new lower and upper bounds of row i */ if (i->lb == i->ub) i->lb = i->ub = (i->lb - gamma * p->lb); else { if (i->lb != -DBL_MAX) i->lb -= gamma * p->lb; if (i->ub != +DBL_MAX) i->ub -= gamma * p->lb; } } return q; } static int rcv_eq_doublet(NPP *npp, void *_info) { /* recover row doubleton (equality constraint) */ struct eq_doublet *info = _info; NPPLFE *lfe; double gamma, temp; /* we assume that processing row p is followed by processing column q as singleton of type "implied slack variable", in which case row p must always be active equality constraint */ if (npp->sol == GLP_SOL) { if (npp->r_stat[info->p] != GLP_NS) { npp_error(); return 1; } } if (npp->sol != GLP_MIP) { /* compute value of multiplier for row p; see (14) */ temp = npp->r_pi[info->p]; for (lfe = info->ptr; lfe != NULL; lfe = lfe->next) { gamma = lfe->val / info->apq; /* a[i,q] / a[p,q] */ temp -= gamma * npp->r_pi[lfe->ref]; } npp->r_pi[info->p] = temp; } return 0; } /*********************************************************************** * NAME * * npp_forcing_row - process forcing row * * SYNOPSIS * * #include "glpnpp.h" * int npp_forcing_row(NPP *npp, NPPROW *p, int at); * * DESCRIPTION * * The routine npp_forcing row processes row p of general format: * * L[p] <= sum a[p,j] x[j] <= U[p], (1) * j * * l[j] <= x[j] <= u[j], (2) * * where L[p] <= U[p] and l[j] < u[j] for all a[p,j] != 0. It is also * assumed that: * * 1) if at = 0 then |L[p] - U'[p]| <= eps, where U'[p] is implied * row upper bound (see below), eps is an absolute tolerance for row * value; * * 2) if at = 1 then |U[p] - L'[p]| <= eps, where L'[p] is implied * row lower bound (see below). * * RETURNS * * 0 - success; * * 1 - cannot fix columns due to too small constraint coefficients. * * PROBLEM TRANSFORMATION * * Implied lower and upper bounds of row (1) are determined by bounds * of corresponding columns (variables) as follows: * * L'[p] = inf sum a[p,j] x[j] = * j * (3) * = sum a[p,j] l[j] + sum a[p,j] u[j], * j in Jp j in Jn * * U'[p] = sup sum a[p,j] x[j] = * (4) * = sum a[p,j] u[j] + sum a[p,j] l[j], * j in Jp j in Jn * * Jp = {j: a[p,j] > 0}, Jn = {j: a[p,j] < 0}. (5) * * If L[p] =~ U'[p] (at = 0), solution can be primal feasible only when * all variables take their boundary values as defined by (4): * * ( u[j], if j in Jp * x[j] = < (6) * ( l[j], if j in Jn * * Similarly, if U[p] =~ L'[p] (at = 1), solution can be primal feasible * only when all variables take their boundary values as defined by (3): * * ( l[j], if j in Jp * x[j] = < (7) * ( u[j], if j in Jn * * Condition (6) or (7) allows fixing all columns (variables x[j]) * in row (1) on their bounds and then removing them from the problem * (see the routine npp_fixed_col). Due to this row p becomes redundant, * so it can be replaced by equivalent free (unbounded) row and also * removed from the problem (see the routine npp_free_row). * * 1. To apply this transformation row (1) should not have coefficients * whose magnitude is too small, i.e. all a[p,j] should satisfy to * the following condition: * * |a[p,j]| >= eps * max(1, |a[p,k]|), (8) * k * where eps is a relative tolerance for constraint coefficients. * Otherwise, fixing columns may be numerically unreliable and may * lead to wrong solution. * * 2. The routine fixes columns and remove bounds of row p, however, * it does not remove the row and columns from the problem. * * RECOVERING BASIC SOLUTION * * In the transformed problem row p being inactive constraint is * assigned status GLP_BS (as the result of transformation of free * row), and all columns in this row are assigned status GLP_NS (as the * result of transformation of fixed columns). * * Note that in the dual system of the transformed (as well as original) * problem every column j in row p corresponds to the following row: * * sum a[i,j] pi[i] + a[p,j] pi[p] + lambda[j] = c[j], (9) * i!=p * * from which it follows that: * * lambda[j] = c[j] - sum a[i,j] pi[i] - a[p,j] pi[p]. (10) * i!=p * * In the transformed problem values of all multipliers pi[i] are known * (including pi[i], whose value is zero, since row p is inactive). * Thus, using formula (10) it is possible to compute values of * multipliers lambda[j] for all columns in row p. * * Note also that in the original problem all columns in row p are * bounded, not fixed. So status GLP_NS assigned to every such column * must be changed to GLP_NL or GLP_NU depending on which bound the * corresponding column has been fixed. This status change may lead to * dual feasibility violation for solution of the original problem, * because now column multipliers must satisfy to the following * condition: * * ( >= 0, if status of column j is GLP_NL, * lambda[j] < (11) * ( <= 0, if status of column j is GLP_NU. * * If this condition holds, solution to the original problem is the * same as to the transformed problem. Otherwise, we have to perform * one degenerate pivoting step of the primal simplex method to obtain * dual feasible (hence, optimal) solution to the original problem as * follows. If, on problem transformation, row p was made active on its * lower bound (case at = 0), we change its status to GLP_NL (or GLP_NS) * and start increasing its multiplier pi[p]. Otherwise, if row p was * made active on its upper bound (case at = 1), we change its status * to GLP_NU (or GLP_NS) and start decreasing pi[p]. From (10) it * follows that: * * delta lambda[j] = - a[p,j] * delta pi[p] = - a[p,j] pi[p]. (12) * * Simple analysis of formulae (3)-(5) shows that changing pi[p] in the * specified direction causes increasing lambda[j] for every column j * assigned status GLP_NL (delta lambda[j] > 0) and decreasing lambda[j] * for every column j assigned status GLP_NU (delta lambda[j] < 0). It * is understood that once the last lambda[q], which violates condition * (11), has reached zero, multipliers lambda[j] for all columns get * valid signs. Such column q can be determined as follows. Let d[j] be * initial value of lambda[j] (i.e. reduced cost of column j) in the * transformed problem computed with formula (10) when pi[p] = 0. Then * lambda[j] = d[j] + delta lambda[j], and from (12) it follows that * lambda[j] becomes zero if: * * delta lambda[j] = - a[p,j] pi[p] = - d[j] ==> * (13) * pi[p] = d[j] / a[p,j]. * * Therefore, the last column q, for which lambda[q] becomes zero, can * be determined from the following condition: * * |d[q] / a[p,q]| = max |pi[p]| = max |d[j] / a[p,j]|, (14) * j in D j in D * * where D is a set of columns j whose, reduced costs d[j] have invalid * signs, i.e. violate condition (11). (Thus, if D is empty, solution * to the original problem is the same as solution to the transformed * problem, and no correction is needed as was noticed above.) In * solution to the original problem column q is assigned status GLP_BS, * since it replaces column of auxiliary variable of row p (becoming * active) in the basis, and multiplier for row p is assigned its new * value, which is pi[p] = d[q] / a[p,q]. Note that due to primal * degeneracy values of all columns having non-zero coefficients in row * p remain unchanged. * * RECOVERING INTERIOR-POINT SOLUTION * * Value of multiplier pi[p] in solution to the original problem is * corrected in the same way as for basic solution. Values of all * columns having non-zero coefficients in row p remain unchanged. * * RECOVERING MIP SOLUTION * * None needed. */ struct forcing_col { /* column fixed on its bound by forcing row */ int j; /* column reference number */ char stat; /* original column status: GLP_NL - fixed on lower bound GLP_NU - fixed on upper bound */ double a; /* constraint coefficient a[p,j] */ double c; /* objective coefficient c[j] */ NPPLFE *ptr; /* list of non-zero coefficients a[i,j], i != p */ struct forcing_col *next; /* pointer to another column fixed by forcing row */ }; struct forcing_row { /* forcing row */ int p; /* row reference number */ char stat; /* status assigned to the row if it becomes active: GLP_NS - active equality constraint GLP_NL - inequality constraint with lower bound active GLP_NU - inequality constraint with upper bound active */ struct forcing_col *ptr; /* list of all columns having non-zero constraint coefficient a[p,j] in the forcing row */ }; static int rcv_forcing_row(NPP *npp, void *info); int npp_forcing_row(NPP *npp, NPPROW *p, int at) { /* process forcing row */ struct forcing_row *info; struct forcing_col *col = NULL; NPPCOL *j; NPPAIJ *apj, *aij; NPPLFE *lfe; double big; xassert(at == 0 || at == 1); /* determine maximal magnitude of the row coefficients */ big = 1.0; for (apj = p->ptr; apj != NULL; apj = apj->r_next) if (big < fabs(apj->val)) big = fabs(apj->val); /* if there are too small coefficients in the row, transformation should not be applied */ for (apj = p->ptr; apj != NULL; apj = apj->r_next) if (fabs(apj->val) < 1e-7 * big) return 1; /* create transformation stack entry */ info = npp_push_tse(npp, rcv_forcing_row, sizeof(struct forcing_row)); info->p = p->i; if (p->lb == p->ub) { /* equality constraint */ info->stat = GLP_NS; } else if (at == 0) { /* inequality constraint; case L[p] = U'[p] */ info->stat = GLP_NL; xassert(p->lb != -DBL_MAX); } else /* at == 1 */ { /* inequality constraint; case U[p] = L'[p] */ info->stat = GLP_NU; xassert(p->ub != +DBL_MAX); } info->ptr = NULL; /* scan the forcing row, fix columns at corresponding bounds, and save column information (the latter is not needed for MIP) */ for (apj = p->ptr; apj != NULL; apj = apj->r_next) { /* column j has non-zero coefficient in the forcing row */ j = apj->col; /* it must be non-fixed */ xassert(j->lb < j->ub); /* allocate stack entry to save column information */ if (npp->sol != GLP_MIP) { col = dmp_get_atom(npp->stack, sizeof(struct forcing_col)); col->j = j->j; col->stat = -1; /* will be set below */ col->a = apj->val; col->c = j->coef; col->ptr = NULL; col->next = info->ptr; info->ptr = col; } /* fix column j */ if (at == 0 && apj->val < 0.0 || at != 0 && apj->val > 0.0) { /* at its lower bound */ if (npp->sol != GLP_MIP) col->stat = GLP_NL; xassert(j->lb != -DBL_MAX); j->ub = j->lb; } else { /* at its upper bound */ if (npp->sol != GLP_MIP) col->stat = GLP_NU; xassert(j->ub != +DBL_MAX); j->lb = j->ub; } /* save column coefficients a[i,j], i != p */ if (npp->sol != GLP_MIP) { for (aij = j->ptr; aij != NULL; aij = aij->c_next) { if (aij == apj) continue; /* skip a[p,j] */ lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE)); lfe->ref = aij->row->i; lfe->val = aij->val; lfe->next = col->ptr; col->ptr = lfe; } } } /* make the row free (unbounded) */ p->lb = -DBL_MAX, p->ub = +DBL_MAX; return 0; } static int rcv_forcing_row(NPP *npp, void *_info) { /* recover forcing row */ struct forcing_row *info = _info; struct forcing_col *col, *piv; NPPLFE *lfe; double d, big, temp; if (npp->sol == GLP_MIP) goto done; /* initially solution to the original problem is the same as to the transformed problem, where row p is inactive constraint with pi[p] = 0, and all columns are non-basic */ if (npp->sol == GLP_SOL) { if (npp->r_stat[info->p] != GLP_BS) { npp_error(); return 1; } for (col = info->ptr; col != NULL; col = col->next) { if (npp->c_stat[col->j] != GLP_NS) { npp_error(); return 1; } npp->c_stat[col->j] = col->stat; /* original status */ } } /* compute reduced costs d[j] for all columns with formula (10) and store them in col.c instead objective coefficients */ for (col = info->ptr; col != NULL; col = col->next) { d = col->c; for (lfe = col->ptr; lfe != NULL; lfe = lfe->next) d -= lfe->val * npp->r_pi[lfe->ref]; col->c = d; } /* consider columns j, whose multipliers lambda[j] has wrong sign in solution to the transformed problem (where lambda[j] = d[j]), and choose column q, whose multipler lambda[q] reaches zero last on changing row multiplier pi[p]; see (14) */ piv = NULL, big = 0.0; for (col = info->ptr; col != NULL; col = col->next) { d = col->c; /* d[j] */ temp = fabs(d / col->a); if (col->stat == GLP_NL) { /* column j has active lower bound */ if (d < 0.0 && big < temp) piv = col, big = temp; } else if (col->stat == GLP_NU) { /* column j has active upper bound */ if (d > 0.0 && big < temp) piv = col, big = temp; } else { npp_error(); return 1; } } /* if column q does not exist, no correction is needed */ if (piv != NULL) { /* correct solution; row p becomes active constraint while column q becomes basic */ if (npp->sol == GLP_SOL) { npp->r_stat[info->p] = info->stat; npp->c_stat[piv->j] = GLP_BS; } /* assign new value to row multiplier pi[p] = d[p] / a[p,q] */ npp->r_pi[info->p] = piv->c / piv->a; } done: return 0; } /*********************************************************************** * NAME * * npp_analyze_row - perform general row analysis * * SYNOPSIS * * #include "glpnpp.h" * int npp_analyze_row(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_analyze_row performs analysis of row p of general * format: * * L[p] <= sum a[p,j] x[j] <= U[p], (1) * j * * l[j] <= x[j] <= u[j], (2) * * where L[p] <= U[p] and l[j] <= u[j] for all a[p,j] != 0. * * RETURNS * * 0x?0 - row lower bound does not exist or is redundant; * * 0x?1 - row lower bound can be active; * * 0x?2 - row lower bound is a forcing bound; * * 0x0? - row upper bound does not exist or is redundant; * * 0x1? - row upper bound can be active; * * 0x2? - row upper bound is a forcing bound; * * 0x33 - row bounds are inconsistent with column bounds. * * ALGORITHM * * Analysis of row (1) is based on analysis of its implied lower and * upper bounds, which are determined by bounds of corresponding columns * (variables) as follows: * * L'[p] = inf sum a[p,j] x[j] = * j * (3) * = sum a[p,j] l[j] + sum a[p,j] u[j], * j in Jp j in Jn * * U'[p] = sup sum a[p,j] x[j] = * (4) * = sum a[p,j] u[j] + sum a[p,j] l[j], * j in Jp j in Jn * * Jp = {j: a[p,j] > 0}, Jn = {j: a[p,j] < 0}. (5) * * (Note that bounds of all columns in row p are assumed to be correct, * so L'[p] <= U'[p].) * * Analysis of row lower bound L[p] includes the following cases: * * 1) if L[p] > U'[p] + eps, where eps is an absolute tolerance for row * value, row lower bound L[p] and implied row upper bound U'[p] are * inconsistent, ergo, the problem has no primal feasible solution; * * 2) if U'[p] - eps <= L[p] <= U'[p] + eps, i.e. if L[p] =~ U'[p], * the row is a forcing row on its lower bound (see description of * the routine npp_forcing_row); * * 3) if L[p] > L'[p] + eps, row lower bound L[p] can be active (this * conclusion does not account other rows in the problem); * * 4) if L[p] <= L'[p] + eps, row lower bound L[p] cannot be active, so * it is redundant and can be removed (replaced by -oo). * * Analysis of row upper bound U[p] is performed in a similar way and * includes the following cases: * * 1) if U[p] < L'[p] - eps, row upper bound U[p] and implied row lower * bound L'[p] are inconsistent, ergo the problem has no primal * feasible solution; * * 2) if L'[p] - eps <= U[p] <= L'[p] + eps, i.e. if U[p] =~ L'[p], * the row is a forcing row on its upper bound (see description of * the routine npp_forcing_row); * * 3) if U[p] < U'[p] - eps, row upper bound U[p] can be active (this * conclusion does not account other rows in the problem); * * 4) if U[p] >= U'[p] - eps, row upper bound U[p] cannot be active, so * it is redundant and can be removed (replaced by +oo). */ int npp_analyze_row(NPP *npp, NPPROW *p) { /* perform general row analysis */ NPPAIJ *aij; int ret = 0x00; double l, u, eps; xassert(npp == npp); /* compute implied lower bound L'[p]; see (3) */ l = 0.0; for (aij = p->ptr; aij != NULL; aij = aij->r_next) { if (aij->val > 0.0) { if (aij->col->lb == -DBL_MAX) { l = -DBL_MAX; break; } l += aij->val * aij->col->lb; } else /* aij->val < 0.0 */ { if (aij->col->ub == +DBL_MAX) { l = -DBL_MAX; break; } l += aij->val * aij->col->ub; } } /* compute implied upper bound U'[p]; see (4) */ u = 0.0; for (aij = p->ptr; aij != NULL; aij = aij->r_next) { if (aij->val > 0.0) { if (aij->col->ub == +DBL_MAX) { u = +DBL_MAX; break; } u += aij->val * aij->col->ub; } else /* aij->val < 0.0 */ { if (aij->col->lb == -DBL_MAX) { u = +DBL_MAX; break; } u += aij->val * aij->col->lb; } } /* column bounds are assumed correct, so L'[p] <= U'[p] */ /* check if row lower bound is consistent */ if (p->lb != -DBL_MAX) { eps = 1e-3 + 1e-6 * fabs(p->lb); if (p->lb - eps > u) { ret = 0x33; goto done; } } /* check if row upper bound is consistent */ if (p->ub != +DBL_MAX) { eps = 1e-3 + 1e-6 * fabs(p->ub); if (p->ub + eps < l) { ret = 0x33; goto done; } } /* check if row lower bound can be active/forcing */ if (p->lb != -DBL_MAX) { eps = 1e-9 + 1e-12 * fabs(p->lb); if (p->lb - eps > l) { if (p->lb + eps <= u) ret |= 0x01; else ret |= 0x02; } } /* check if row upper bound can be active/forcing */ if (p->ub != +DBL_MAX) { eps = 1e-9 + 1e-12 * fabs(p->ub); if (p->ub + eps < u) { /* check if the upper bound is forcing */ if (p->ub - eps >= l) ret |= 0x10; else ret |= 0x20; } } done: return ret; } /*********************************************************************** * NAME * * npp_inactive_bound - remove row lower/upper inactive bound * * SYNOPSIS * * #include "glpnpp.h" * void npp_inactive_bound(NPP *npp, NPPROW *p, int which); * * DESCRIPTION * * The routine npp_inactive_bound removes lower (if which = 0) or upper * (if which = 1) bound of row p: * * L[p] <= sum a[p,j] x[j] <= U[p], * * which (bound) is assumed to be redundant. * * PROBLEM TRANSFORMATION * * If which = 0, current lower bound L[p] of row p is assigned -oo. * If which = 1, current upper bound U[p] of row p is assigned +oo. * * RECOVERING BASIC SOLUTION * * If in solution to the transformed problem row p is inactive * constraint (GLP_BS), its status is not changed in solution to the * original problem. Otherwise, status of row p in solution to the * original problem is defined by its type before transformation and * its status in solution to the transformed problem as follows: * * +---------------------+-------+---------------+---------------+ * | Row | Flag | Row status in | Row status in | * | type | which | transfmd soln | original soln | * +---------------------+-------+---------------+---------------+ * | sum >= L[p] | 0 | GLP_NF | GLP_NL | * | sum <= U[p] | 1 | GLP_NF | GLP_NU | * | L[p] <= sum <= U[p] | 0 | GLP_NU | GLP_NU | * | L[p] <= sum <= U[p] | 1 | GLP_NL | GLP_NL | * | sum = L[p] = U[p] | 0 | GLP_NU | GLP_NS | * | sum = L[p] = U[p] | 1 | GLP_NL | GLP_NS | * +---------------------+-------+---------------+---------------+ * * RECOVERING INTERIOR-POINT SOLUTION * * None needed. * * RECOVERING MIP SOLUTION * * None needed. */ struct inactive_bound { /* row inactive bound */ int p; /* row reference number */ char stat; /* row status (if active constraint) */ }; static int rcv_inactive_bound(NPP *npp, void *info); void npp_inactive_bound(NPP *npp, NPPROW *p, int which) { /* remove row lower/upper inactive bound */ struct inactive_bound *info; if (npp->sol == GLP_SOL) { /* create transformation stack entry */ info = npp_push_tse(npp, rcv_inactive_bound, sizeof(struct inactive_bound)); info->p = p->i; if (p->ub == +DBL_MAX) info->stat = GLP_NL; else if (p->lb == -DBL_MAX) info->stat = GLP_NU; else if (p->lb != p->ub) info->stat = (char)(which == 0 ? GLP_NU : GLP_NL); else info->stat = GLP_NS; } /* remove row inactive bound */ if (which == 0) { xassert(p->lb != -DBL_MAX); p->lb = -DBL_MAX; } else if (which == 1) { xassert(p->ub != +DBL_MAX); p->ub = +DBL_MAX; } else xassert(which != which); return; } static int rcv_inactive_bound(NPP *npp, void *_info) { /* recover row status */ struct inactive_bound *info = _info; if (npp->sol != GLP_SOL) { npp_error(); return 1; } if (npp->r_stat[info->p] == GLP_BS) npp->r_stat[info->p] = GLP_BS; else npp->r_stat[info->p] = info->stat; return 0; } /*********************************************************************** * NAME * * npp_implied_bounds - determine implied column bounds * * SYNOPSIS * * #include "glpnpp.h" * void npp_implied_bounds(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_implied_bounds inspects general row (constraint) p: * * L[p] <= sum a[p,j] x[j] <= U[p], (1) * * l[j] <= x[j] <= u[j], (2) * * where L[p] <= U[p] and l[j] <= u[j] for all a[p,j] != 0, to compute * implied bounds of columns (variables x[j]) in this row. * * The routine stores implied column bounds l'[j] and u'[j] in column * descriptors (NPPCOL); it does not change current column bounds l[j] * and u[j]. (Implied column bounds can be then used to strengthen the * current column bounds; see the routines npp_implied_lower and * npp_implied_upper). * * ALGORITHM * * Current column bounds (2) define implied lower and upper bounds of * row (1) as follows: * * L'[p] = inf sum a[p,j] x[j] = * j * (3) * = sum a[p,j] l[j] + sum a[p,j] u[j], * j in Jp j in Jn * * U'[p] = sup sum a[p,j] x[j] = * (4) * = sum a[p,j] u[j] + sum a[p,j] l[j], * j in Jp j in Jn * * Jp = {j: a[p,j] > 0}, Jn = {j: a[p,j] < 0}. (5) * * (Note that bounds of all columns in row p are assumed to be correct, * so L'[p] <= U'[p].) * * If L[p] > L'[p] and/or U[p] < U'[p], the lower and/or upper bound of * row (1) can be active, in which case such row defines implied bounds * of its variables. * * Let x[k] be some variable having in row (1) coefficient a[p,k] != 0. * Consider a case when row lower bound can be active (L[p] > L'[p]): * * sum a[p,j] x[j] >= L[p] ==> * j * * sum a[p,j] x[j] + a[p,k] x[k] >= L[p] ==> * j!=k * (6) * a[p,k] x[k] >= L[p] - sum a[p,j] x[j] ==> * j!=k * * a[p,k] x[k] >= L[p,k], * * where * * L[p,k] = inf(L[p] - sum a[p,j] x[j]) = * j!=k * * = L[p] - sup sum a[p,j] x[j] = (7) * j!=k * * = L[p] - sum a[p,j] u[j] - sum a[p,j] l[j]. * j in Jp\{k} j in Jn\{k} * * Thus: * * x[k] >= l'[k] = L[p,k] / a[p,k], if a[p,k] > 0, (8) * * x[k] <= u'[k] = L[p,k] / a[p,k], if a[p,k] < 0. (9) * * where l'[k] and u'[k] are implied lower and upper bounds of variable * x[k], resp. * * Now consider a similar case when row upper bound can be active * (U[p] < U'[p]): * * sum a[p,j] x[j] <= U[p] ==> * j * * sum a[p,j] x[j] + a[p,k] x[k] <= U[p] ==> * j!=k * (10) * a[p,k] x[k] <= U[p] - sum a[p,j] x[j] ==> * j!=k * * a[p,k] x[k] <= U[p,k], * * where: * * U[p,k] = sup(U[p] - sum a[p,j] x[j]) = * j!=k * * = U[p] - inf sum a[p,j] x[j] = (11) * j!=k * * = U[p] - sum a[p,j] l[j] - sum a[p,j] u[j]. * j in Jp\{k} j in Jn\{k} * * Thus: * * x[k] <= u'[k] = U[p,k] / a[p,k], if a[p,k] > 0, (12) * * x[k] >= l'[k] = U[p,k] / a[p,k], if a[p,k] < 0. (13) * * Note that in formulae (8), (9), (12), and (13) coefficient a[p,k] * must not be too small in magnitude relatively to other non-zero * coefficients in row (1), i.e. the following condition must hold: * * |a[p,k]| >= eps * max(1, |a[p,j]|), (14) * j * * where eps is a relative tolerance for constraint coefficients. * Otherwise the implied column bounds can be numerical inreliable. For * example, using formula (8) for the following inequality constraint: * * 1e-12 x1 - x2 - x3 >= 0, * * where x1 >= -1, x2, x3, >= 0, may lead to numerically unreliable * conclusion that x1 >= 0. * * Using formulae (8), (9), (12), and (13) to compute implied bounds * for one variable requires |J| operations, where J = {j: a[p,j] != 0}, * because this needs computing L[p,k] and U[p,k]. Thus, computing * implied bounds for all variables in row (1) would require |J|^2 * operations, that is not a good technique. However, the total number * of operations can be reduced to |J| as follows. * * Let a[p,k] > 0. Then from (7) and (11) we have: * * L[p,k] = L[p] - (U'[p] - a[p,k] u[k]) = * * = L[p] - U'[p] + a[p,k] u[k], * * U[p,k] = U[p] - (L'[p] - a[p,k] l[k]) = * * = U[p] - L'[p] + a[p,k] l[k], * * where L'[p] and U'[p] are implied row lower and upper bounds defined * by formulae (3) and (4). Substituting these expressions into (8) and * (12) gives: * * l'[k] = L[p,k] / a[p,k] = u[k] + (L[p] - U'[p]) / a[p,k], (15) * * u'[k] = U[p,k] / a[p,k] = l[k] + (U[p] - L'[p]) / a[p,k]. (16) * * Similarly, if a[p,k] < 0, according to (7) and (11) we have: * * L[p,k] = L[p] - (U'[p] - a[p,k] l[k]) = * * = L[p] - U'[p] + a[p,k] l[k], * * U[p,k] = U[p] - (L'[p] - a[p,k] u[k]) = * * = U[p] - L'[p] + a[p,k] u[k], * * and substituting these expressions into (8) and (12) gives: * * l'[k] = U[p,k] / a[p,k] = u[k] + (U[p] - L'[p]) / a[p,k], (17) * * u'[k] = L[p,k] / a[p,k] = l[k] + (L[p] - U'[p]) / a[p,k]. (18) * * Note that formulae (15)-(18) can be used only if L'[p] and U'[p] * exist. However, if for some variable x[j] it happens that l[j] = -oo * and/or u[j] = +oo, values of L'[p] (if a[p,j] > 0) and/or U'[p] (if * a[p,j] < 0) are undefined. Consider, therefore, the most general * situation, when some column bounds (2) may not exist. * * Let: * * J' = {j : (a[p,j] > 0 and l[j] = -oo) or * (19) * (a[p,j] < 0 and u[j] = +oo)}. * * Then (assuming that row upper bound U[p] can be active) the following * three cases are possible: * * 1) |J'| = 0. In this case L'[p] exists, thus, for all variables x[j] * in row (1) we can use formulae (16) and (17); * * 2) J' = {k}. In this case L'[p] = -oo, however, U[p,k] (11) exists, * so for variable x[k] we can use formulae (12) and (13). Note that * for all other variables x[j] (j != k) l'[j] = -oo (if a[p,j] < 0) * or u'[j] = +oo (if a[p,j] > 0); * * 3) |J'| > 1. In this case for all variables x[j] in row [1] we have * l'[j] = -oo (if a[p,j] < 0) or u'[j] = +oo (if a[p,j] > 0). * * Similarly, let: * * J'' = {j : (a[p,j] > 0 and u[j] = +oo) or * (20) * (a[p,j] < 0 and l[j] = -oo)}. * * Then (assuming that row lower bound L[p] can be active) the following * three cases are possible: * * 1) |J''| = 0. In this case U'[p] exists, thus, for all variables x[j] * in row (1) we can use formulae (15) and (18); * * 2) J'' = {k}. In this case U'[p] = +oo, however, L[p,k] (7) exists, * so for variable x[k] we can use formulae (8) and (9). Note that * for all other variables x[j] (j != k) l'[j] = -oo (if a[p,j] > 0) * or u'[j] = +oo (if a[p,j] < 0); * * 3) |J''| > 1. In this case for all variables x[j] in row (1) we have * l'[j] = -oo (if a[p,j] > 0) or u'[j] = +oo (if a[p,j] < 0). */ void npp_implied_bounds(NPP *npp, NPPROW *p) { NPPAIJ *apj, *apk; double big, eps, temp; xassert(npp == npp); /* initialize implied bounds for all variables and determine maximal magnitude of row coefficients a[p,j] */ big = 1.0; for (apj = p->ptr; apj != NULL; apj = apj->r_next) { apj->col->ll.ll = -DBL_MAX, apj->col->uu.uu = +DBL_MAX; if (big < fabs(apj->val)) big = fabs(apj->val); } eps = 1e-6 * big; /* process row lower bound (assuming that it can be active) */ if (p->lb != -DBL_MAX) { apk = NULL; for (apj = p->ptr; apj != NULL; apj = apj->r_next) { if (apj->val > 0.0 && apj->col->ub == +DBL_MAX || apj->val < 0.0 && apj->col->lb == -DBL_MAX) { if (apk == NULL) apk = apj; else goto skip1; } } /* if a[p,k] = NULL then |J'| = 0 else J' = { k } */ temp = p->lb; for (apj = p->ptr; apj != NULL; apj = apj->r_next) { if (apj == apk) /* skip a[p,k] */; else if (apj->val > 0.0) temp -= apj->val * apj->col->ub; else /* apj->val < 0.0 */ temp -= apj->val * apj->col->lb; } /* compute column implied bounds */ if (apk == NULL) { /* temp = L[p] - U'[p] */ for (apj = p->ptr; apj != NULL; apj = apj->r_next) { if (apj->val >= +eps) { /* l'[j] := u[j] + (L[p] - U'[p]) / a[p,j] */ apj->col->ll.ll = apj->col->ub + temp / apj->val; } else if (apj->val <= -eps) { /* u'[j] := l[j] + (L[p] - U'[p]) / a[p,j] */ apj->col->uu.uu = apj->col->lb + temp / apj->val; } } } else { /* temp = L[p,k] */ if (apk->val >= +eps) { /* l'[k] := L[p,k] / a[p,k] */ apk->col->ll.ll = temp / apk->val; } else if (apk->val <= -eps) { /* u'[k] := L[p,k] / a[p,k] */ apk->col->uu.uu = temp / apk->val; } } skip1: ; } /* process row upper bound (assuming that it can be active) */ if (p->ub != +DBL_MAX) { apk = NULL; for (apj = p->ptr; apj != NULL; apj = apj->r_next) { if (apj->val > 0.0 && apj->col->lb == -DBL_MAX || apj->val < 0.0 && apj->col->ub == +DBL_MAX) { if (apk == NULL) apk = apj; else goto skip2; } } /* if a[p,k] = NULL then |J''| = 0 else J'' = { k } */ temp = p->ub; for (apj = p->ptr; apj != NULL; apj = apj->r_next) { if (apj == apk) /* skip a[p,k] */; else if (apj->val > 0.0) temp -= apj->val * apj->col->lb; else /* apj->val < 0.0 */ temp -= apj->val * apj->col->ub; } /* compute column implied bounds */ if (apk == NULL) { /* temp = U[p] - L'[p] */ for (apj = p->ptr; apj != NULL; apj = apj->r_next) { if (apj->val >= +eps) { /* u'[j] := l[j] + (U[p] - L'[p]) / a[p,j] */ apj->col->uu.uu = apj->col->lb + temp / apj->val; } else if (apj->val <= -eps) { /* l'[j] := u[j] + (U[p] - L'[p]) / a[p,j] */ apj->col->ll.ll = apj->col->ub + temp / apj->val; } } } else { /* temp = U[p,k] */ if (apk->val >= +eps) { /* u'[k] := U[p,k] / a[p,k] */ apk->col->uu.uu = temp / apk->val; } else if (apk->val <= -eps) { /* l'[k] := U[p,k] / a[p,k] */ apk->col->ll.ll = temp / apk->val; } } skip2: ; } return; } /* eof */ igraph/src/glpini01.c0000644000176000001440000005426312325527073014152 0ustar ripleyusers/* glpini01.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wself-assign" #endif #include "glpapi.h" /*---------------------------------------------------------------------- -- triang - find maximal triangular part of a rectangular matrix. -- -- *Synopsis* -- -- int triang(int m, int n, -- void *info, int (*mat)(void *info, int k, int ndx[]), -- int rn[], int cn[]); -- -- *Description* -- -- For a given rectangular (sparse) matrix A with m rows and n columns -- the routine triang tries to find such permutation matrices P and Q -- that the first rows and columns of the matrix B = P*A*Q form a lower -- triangular submatrix of as greatest size as possible: -- -- 1 n -- 1 * . . . . . . x x x x x x -- * * . . . . . x x x x x x -- * * * . . . . x x x x x x -- * * * * . . . x x x x x x -- B = P*A*Q = * * * * * . . x x x x x x -- * * * * * * . x x x x x x -- * * * * * * * x x x x x x -- x x x x x x x x x x x x x -- x x x x x x x x x x x x x -- m x x x x x x x x x x x x x -- -- where: '*' - elements of the lower triangular part, '.' - structural -- zeros, 'x' - other (either non-zero or zero) elements. -- -- The parameter info is a transit pointer passed to the formal routine -- mat (see below). -- -- The formal routine mat specifies the given matrix A in both row- and -- column-wise formats. In order to obtain an i-th row of the matrix A -- the routine triang calls the routine mat with the parameter k = +i, -- 1 <= i <= m. In response the routine mat should store column indices -- of (non-zero) elements of the i-th row to the locations ndx[1], ..., -- ndx[len], where len is number of non-zeros in the i-th row returned -- on exit. Analogously, in order to obtain a j-th column of the matrix -- A, the routine mat is called with the parameter k = -j, 1 <= j <= n, -- and should return pattern of the j-th column in the same way as for -- row patterns. Note that the routine mat may be called more than once -- for the same rows and columns. -- -- On exit the routine computes two resultant arrays rn and cn, which -- define the permutation matrices P and Q, respectively. The array rn -- should have at least 1+m locations, where rn[i] = i' (1 <= i <= m) -- means that i-th row of the original matrix A corresponds to i'-th row -- of the matrix B = P*A*Q. Similarly, the array cn should have at least -- 1+n locations, where cn[j] = j' (1 <= j <= n) means that j-th column -- of the matrix A corresponds to j'-th column of the matrix B. -- -- *Returns* -- -- The routine triang returns the size of the lower tringular part of -- the matrix B = P*A*Q (see the figure above). -- -- *Complexity* -- -- The time complexity of the routine triang is O(nnz), where nnz is -- number of non-zeros in the given matrix A. -- -- *Algorithm* -- -- The routine triang starts from the matrix B = P*Q*A, where P and Q -- are unity matrices, so initially B = A. -- -- Before the next iteration B = (B1 | B2 | B3), where B1 is partially -- built a lower triangular submatrix, B2 is the active submatrix, and -- B3 is a submatrix that contains rejected columns. Thus, the current -- matrix B looks like follows (initially k1 = 1 and k2 = n): -- -- 1 k1 k2 n -- 1 x . . . . . . . . . . . . . # # # -- x x . . . . . . . . . . . . # # # -- x x x . . . . . . . . . . # # # # -- x x x x . . . . . . . . . # # # # -- x x x x x . . . . . . . # # # # # -- k1 x x x x x * * * * * * * # # # # # -- x x x x x * * * * * * * # # # # # -- x x x x x * * * * * * * # # # # # -- x x x x x * * * * * * * # # # # # -- m x x x x x * * * * * * * # # # # # -- <--B1---> <----B2-----> <---B3--> -- -- On each iteartion the routine looks for a singleton row, i.e. some -- row that has the only non-zero in the active submatrix B2. If such -- row exists and the corresponding non-zero is b[i,j], where (by the -- definition) k1 <= i <= m and k1 <= j <= k2, the routine permutes -- k1-th and i-th rows and k1-th and j-th columns of the matrix B (in -- order to place the element in the position b[k1,k1]), removes the -- k1-th column from the active submatrix B2, and adds this column to -- the submatrix B1. If no row singletons exist, but B2 is not empty -- yet, the routine chooses a j-th column, which has maximal number of -- non-zeros among other columns of B2, removes this column from B2 and -- adds it to the submatrix B3 in the hope that new row singletons will -- appear in the active submatrix. */ static int triang(int m, int n, void *info, int (*mat)(void *info, int k, int ndx[]), int rn[], int cn[]) { int *ndx; /* int ndx[1+max(m,n)]; */ /* this array is used for querying row and column patterns of the given matrix A (the third parameter to the routine mat) */ int *rs_len; /* int rs_len[1+m]; */ /* rs_len[0] is not used; rs_len[i], 1 <= i <= m, is number of non-zeros in the i-th row of the matrix A, which (non-zeros) belong to the current active submatrix */ int *rs_head; /* int rs_head[1+n]; */ /* rs_head[len], 0 <= len <= n, is the number i of the first row of the matrix A, for which rs_len[i] = len */ int *rs_prev; /* int rs_prev[1+m]; */ /* rs_prev[0] is not used; rs_prev[i], 1 <= i <= m, is a number i' of the previous row of the matrix A, for which rs_len[i] = rs_len[i'] (zero marks the end of this linked list) */ int *rs_next; /* int rs_next[1+m]; */ /* rs_next[0] is not used; rs_next[i], 1 <= i <= m, is a number i' of the next row of the matrix A, for which rs_len[i] = rs_len[i'] (zero marks the end this linked list) */ int cs_head; /* is a number j of the first column of the matrix A, which has maximal number of non-zeros among other columns */ int *cs_prev; /* cs_prev[1+n]; */ /* cs_prev[0] is not used; cs_prev[j], 1 <= j <= n, is a number of the previous column of the matrix A with the same or greater number of non-zeros than in the j-th column (zero marks the end of this linked list) */ int *cs_next; /* cs_next[1+n]; */ /* cs_next[0] is not used; cs_next[j], 1 <= j <= n, is a number of the next column of the matrix A with the same or lesser number of non-zeros than in the j-th column (zero marks the end of this linked list) */ int i, j, ii, jj, k1, k2, len, t, size = 0; int *head, *rn_inv, *cn_inv; if (!(m > 0 && n > 0)) xerror("triang: m = %d; n = %d; invalid dimension\n", m, n); /* allocate working arrays */ ndx = xcalloc(1+(m >= n ? m : n), sizeof(int)); rs_len = xcalloc(1+m, sizeof(int)); rs_head = xcalloc(1+n, sizeof(int)); rs_prev = xcalloc(1+m, sizeof(int)); rs_next = xcalloc(1+m, sizeof(int)); cs_prev = xcalloc(1+n, sizeof(int)); cs_next = xcalloc(1+n, sizeof(int)); /* build linked lists of columns of the matrix A with the same number of non-zeros */ head = rs_len; /* currently rs_len is used as working array */ for (len = 0; len <= m; len ++) head[len] = 0; for (j = 1; j <= n; j++) { /* obtain length of the j-th column */ len = mat(info, -j, ndx); xassert(0 <= len && len <= m); /* include the j-th column in the corresponding linked list */ cs_prev[j] = head[len]; head[len] = j; } /* merge all linked lists of columns in one linked list, where columns are ordered by descending of their lengths */ cs_head = 0; for (len = 0; len <= m; len++) { for (j = head[len]; j != 0; j = cs_prev[j]) { cs_next[j] = cs_head; cs_head = j; } } jj = 0; for (j = cs_head; j != 0; j = cs_next[j]) { cs_prev[j] = jj; jj = j; } /* build initial doubly linked lists of rows of the matrix A with the same number of non-zeros */ for (len = 0; len <= n; len++) rs_head[len] = 0; for (i = 1; i <= m; i++) { /* obtain length of the i-th row */ rs_len[i] = len = mat(info, +i, ndx); xassert(0 <= len && len <= n); /* include the i-th row in the correspondng linked list */ rs_prev[i] = 0; rs_next[i] = rs_head[len]; if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; rs_head[len] = i; } /* initially all rows and columns of the matrix A are active */ for (i = 1; i <= m; i++) rn[i] = 0; for (j = 1; j <= n; j++) cn[j] = 0; /* set initial bounds of the active submatrix */ k1 = 1, k2 = n; /* main loop starts here */ while (k1 <= k2) { i = rs_head[1]; if (i != 0) { /* the i-th row of the matrix A is a row singleton, since it has the only non-zero in the active submatrix */ xassert(rs_len[i] == 1); /* determine the number j of an active column of the matrix A, in which this non-zero is placed */ j = 0; t = mat(info, +i, ndx); xassert(0 <= t && t <= n); for (t = t; t >= 1; t--) { jj = ndx[t]; xassert(1 <= jj && jj <= n); if (cn[jj] == 0) { xassert(j == 0); j = jj; } } xassert(j != 0); /* the singleton is a[i,j]; move a[i,j] to the position b[k1,k1] of the matrix B */ rn[i] = cn[j] = k1; /* shift the left bound of the active submatrix */ k1++; /* increase the size of the lower triangular part */ size++; } else { /* the current active submatrix has no row singletons */ /* remove an active column with maximal number of non-zeros from the active submatrix */ j = cs_head; xassert(j != 0); cn[j] = k2; /* shift the right bound of the active submatrix */ k2--; } /* the j-th column of the matrix A has been removed from the active submatrix */ /* remove the j-th column from the linked list */ if (cs_prev[j] == 0) cs_head = cs_next[j]; else cs_next[cs_prev[j]] = cs_next[j]; if (cs_next[j] == 0) /* nop */; else cs_prev[cs_next[j]] = cs_prev[j]; /* go through non-zeros of the j-th columns and update active lengths of the corresponding rows */ t = mat(info, -j, ndx); xassert(0 <= t && t <= m); for (t = t; t >= 1; t--) { i = ndx[t]; xassert(1 <= i && i <= m); /* the non-zero a[i,j] has left the active submatrix */ len = rs_len[i]; xassert(len >= 1); /* remove the i-th row from the linked list of rows with active length len */ if (rs_prev[i] == 0) rs_head[len] = rs_next[i]; else rs_next[rs_prev[i]] = rs_next[i]; if (rs_next[i] == 0) /* nop */; else rs_prev[rs_next[i]] = rs_prev[i]; /* decrease the active length of the i-th row */ rs_len[i] = --len; /* return the i-th row to the corresponding linked list */ rs_prev[i] = 0; rs_next[i] = rs_head[len]; if (rs_next[i] != 0) rs_prev[rs_next[i]] = i; rs_head[len] = i; } } /* other rows of the matrix A, which are still active, correspond to rows k1, ..., m of the matrix B (in arbitrary order) */ for (i = 1; i <= m; i++) if (rn[i] == 0) rn[i] = k1++; /* but for columns this is not needed, because now the submatrix B2 has no columns */ for (j = 1; j <= n; j++) xassert(cn[j] != 0); /* perform some optional checks */ /* make sure that rn is a permutation of {1, ..., m} and cn is a permutation of {1, ..., n} */ rn_inv = rs_len; /* used as working array */ for (ii = 1; ii <= m; ii++) rn_inv[ii] = 0; for (i = 1; i <= m; i++) { ii = rn[i]; xassert(1 <= ii && ii <= m); xassert(rn_inv[ii] == 0); rn_inv[ii] = i; } cn_inv = rs_head; /* used as working array */ for (jj = 1; jj <= n; jj++) cn_inv[jj] = 0; for (j = 1; j <= n; j++) { jj = cn[j]; xassert(1 <= jj && jj <= n); xassert(cn_inv[jj] == 0); cn_inv[jj] = j; } /* make sure that the matrix B = P*A*Q really has the form, which was declared */ for (ii = 1; ii <= size; ii++) { int diag = 0; i = rn_inv[ii]; t = mat(info, +i, ndx); xassert(0 <= t && t <= n); for (t = t; t >= 1; t--) { j = ndx[t]; xassert(1 <= j && j <= n); jj = cn[j]; if (jj <= size) xassert(jj <= ii); if (jj == ii) { xassert(!diag); diag = 1; } } xassert(diag); } /* free working arrays */ xfree(ndx); xfree(rs_len); xfree(rs_head); xfree(rs_prev); xfree(rs_next); xfree(cs_prev); xfree(cs_next); /* return to the calling program */ return size; } /*---------------------------------------------------------------------- -- adv_basis - construct advanced initial LP basis. -- -- *Synopsis* -- -- #include "glpini.h" -- void adv_basis(glp_prob *lp); -- -- *Description* -- -- The routine adv_basis constructs an advanced initial basis for an LP -- problem object, which the parameter lp points to. -- -- In order to build the initial basis the routine does the following: -- -- 1) includes in the basis all non-fixed auxiliary variables; -- -- 2) includes in the basis as many as possible non-fixed structural -- variables preserving triangular form of the basis matrix; -- -- 3) includes in the basis appropriate (fixed) auxiliary variables -- in order to complete the basis. -- -- As a result the initial basis has minimum of fixed variables and the -- corresponding basis matrix is triangular. */ static int mat(void *info, int k, int ndx[]) { /* this auxiliary routine returns the pattern of a given row or a given column of the augmented constraint matrix A~ = (I|-A), in which columns of fixed variables are implicitly cleared */ LPX *lp = info; int m = lpx_get_num_rows(lp); int n = lpx_get_num_cols(lp); int typx, i, j, lll, len = 0; if (k > 0) { /* the pattern of the i-th row is required */ i = +k; xassert(1 <= i && i <= m); #if 0 /* 22/XII-2003 */ /* if the auxiliary variable x[i] is non-fixed, include its element (placed in the i-th column) in the pattern */ lpx_get_row_bnds(lp, i, &typx, NULL, NULL); if (typx != LPX_FX) ndx[++len] = i; /* include in the pattern elements placed in columns, which correspond to non-fixed structural varables */ i_beg = aa_ptr[i]; i_end = i_beg + aa_len[i] - 1; for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++) { j = m + sv_ndx[i_ptr]; lpx_get_col_bnds(lp, j-m, &typx, NULL, NULL); if (typx != LPX_FX) ndx[++len] = j; } #else lll = lpx_get_mat_row(lp, i, ndx, NULL); for (k = 1; k <= lll; k++) { lpx_get_col_bnds(lp, ndx[k], &typx, NULL, NULL); if (typx != LPX_FX) ndx[++len] = m + ndx[k]; } lpx_get_row_bnds(lp, i, &typx, NULL, NULL); if (typx != LPX_FX) ndx[++len] = i; #endif } else { /* the pattern of the j-th column is required */ j = -k; xassert(1 <= j && j <= m+n); /* if the (auxiliary or structural) variable x[j] is fixed, the pattern of its column is empty */ if (j <= m) lpx_get_row_bnds(lp, j, &typx, NULL, NULL); else lpx_get_col_bnds(lp, j-m, &typx, NULL, NULL); if (typx != LPX_FX) { if (j <= m) { /* x[j] is non-fixed auxiliary variable */ ndx[++len] = j; } else { /* x[j] is non-fixed structural variables */ #if 0 /* 22/XII-2003 */ j_beg = aa_ptr[j]; j_end = j_beg + aa_len[j] - 1; for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++) ndx[++len] = sv_ndx[j_ptr]; #else len = lpx_get_mat_col(lp, j-m, ndx, NULL); #endif } } } /* return the length of the row/column pattern */ return len; } static void adv_basis(glp_prob *lp) { int m = lpx_get_num_rows(lp); int n = lpx_get_num_cols(lp); int i, j, jj, k, size; int *rn, *cn, *rn_inv, *cn_inv; int typx, *tagx = xcalloc(1+m+n, sizeof(int)); double lb, ub; xprintf("Constructing initial basis...\n"); #if 0 /* 13/V-2009 */ if (m == 0) xerror("glp_adv_basis: problem has no rows\n"); if (n == 0) xerror("glp_adv_basis: problem has no columns\n"); #else if (m == 0 || n == 0) { glp_std_basis(lp); return; } #endif /* use the routine triang (see above) to find maximal triangular part of the augmented constraint matrix A~ = (I|-A); in order to prevent columns of fixed variables to be included in the triangular part, such columns are implictly removed from the matrix A~ by the routine adv_mat */ rn = xcalloc(1+m, sizeof(int)); cn = xcalloc(1+m+n, sizeof(int)); size = triang(m, m+n, lp, mat, rn, cn); if (lpx_get_int_parm(lp, LPX_K_MSGLEV) >= 3) xprintf("Size of triangular part = %d\n", size); /* the first size rows and columns of the matrix P*A~*Q (where P and Q are permutation matrices defined by the arrays rn and cn) form a lower triangular matrix; build the arrays (rn_inv and cn_inv), which define the matrices inv(P) and inv(Q) */ rn_inv = xcalloc(1+m, sizeof(int)); cn_inv = xcalloc(1+m+n, sizeof(int)); for (i = 1; i <= m; i++) rn_inv[rn[i]] = i; for (j = 1; j <= m+n; j++) cn_inv[cn[j]] = j; /* include the columns of the matrix A~, which correspond to the first size columns of the matrix P*A~*Q, in the basis */ for (k = 1; k <= m+n; k++) tagx[k] = -1; for (jj = 1; jj <= size; jj++) { j = cn_inv[jj]; /* the j-th column of A~ is the jj-th column of P*A~*Q */ tagx[j] = LPX_BS; } /* if size < m, we need to add appropriate columns of auxiliary variables to the basis */ for (jj = size + 1; jj <= m; jj++) { /* the jj-th column of P*A~*Q should be replaced by the column of the auxiliary variable, for which the only unity element is placed in the position [jj,jj] */ i = rn_inv[jj]; /* the jj-th row of P*A~*Q is the i-th row of A~, but in the i-th row of A~ the unity element belongs to the i-th column of A~; therefore the disired column corresponds to the i-th auxiliary variable (note that this column doesn't belong to the triangular part found by the routine triang) */ xassert(1 <= i && i <= m); xassert(cn[i] > size); tagx[i] = LPX_BS; } /* free working arrays */ xfree(rn); xfree(cn); xfree(rn_inv); xfree(cn_inv); /* build tags of non-basic variables */ for (k = 1; k <= m+n; k++) { if (tagx[k] != LPX_BS) { if (k <= m) lpx_get_row_bnds(lp, k, &typx, &lb, &ub); else lpx_get_col_bnds(lp, k-m, &typx, &lb, &ub); switch (typx) { case LPX_FR: tagx[k] = LPX_NF; break; case LPX_LO: tagx[k] = LPX_NL; break; case LPX_UP: tagx[k] = LPX_NU; break; case LPX_DB: tagx[k] = (fabs(lb) <= fabs(ub) ? LPX_NL : LPX_NU); break; case LPX_FX: tagx[k] = LPX_NS; break; default: xassert(typx != typx); } } } for (k = 1; k <= m+n; k++) { if (k <= m) lpx_set_row_stat(lp, k, tagx[k]); else lpx_set_col_stat(lp, k-m, tagx[k]); } xfree(tagx); return; } /*********************************************************************** * NAME * * glp_adv_basis - construct advanced initial LP basis * * SYNOPSIS * * void glp_adv_basis(glp_prob *lp, int flags); * * DESCRIPTION * * The routine glp_adv_basis constructs an advanced initial basis for * the specified problem object. * * The parameter flags is reserved for use in the future and must be * specified as zero. */ void glp_adv_basis(glp_prob *lp, int flags) { if (flags != 0) xerror("glp_adv_basis: flags = %d; invalid flags\n", flags); if (lp->m == 0 || lp->n == 0) glp_std_basis(lp); else adv_basis(lp); return; } /* eof */ igraph/src/igraph_flow_internal.h0000644000176000001440000000240712325527073016722 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_FLOW_INTERNAL_H #define IGRAPH_FLOW_INTERNAL_H #include "igraph_types.h" #include "igraph_marked_queue.h" #include "igraph_estack.h" typedef int igraph_provan_shier_pivot_t(const igraph_t *graph, const igraph_marked_queue_t *S, const igraph_estack_t *T, long int source, long int target, long int *v, igraph_vector_t *Isv, void *arg); #endif igraph/src/cs_add.c0000644000176000001440000000441112325527073013732 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* C = alpha*A + beta*B */ cs *cs_add (const cs *A, const cs *B, CS_ENTRY alpha, CS_ENTRY beta) { CS_INT p, j, nz = 0, anz, *Cp, *Ci, *Bp, m, n, bnz, *w, values ; CS_ENTRY *x, *Bx, *Cx ; cs *C ; if (!CS_CSC (A) || !CS_CSC (B)) return (NULL) ; /* check inputs */ if (A->m != B->m || A->n != B->n) return (NULL) ; m = A->m ; anz = A->p [A->n] ; n = B->n ; Bp = B->p ; Bx = B->x ; bnz = Bp [n] ; w = cs_calloc (m, sizeof (CS_INT)) ; /* get workspace */ values = (A->x != NULL) && (Bx != NULL) ; x = values ? cs_malloc (m, sizeof (CS_ENTRY)) : NULL ; /* get workspace */ C = cs_spalloc (m, n, anz + bnz, values, 0) ; /* allocate result*/ if (!C || !w || (values && !x)) return (cs_done (C, w, x, 0)) ; Cp = C->p ; Ci = C->i ; Cx = C->x ; for (j = 0 ; j < n ; j++) { Cp [j] = nz ; /* column j of C starts here */ nz = cs_scatter (A, j, alpha, w, x, j+1, C, nz) ; /* alpha*A(:,j)*/ nz = cs_scatter (B, j, beta, w, x, j+1, C, nz) ; /* beta*B(:,j) */ if (values) for (p = Cp [j] ; p < nz ; p++) Cx [p] = x [Ci [p]] ; } Cp [n] = nz ; /* finalize the last column of C */ cs_sprealloc (C, 0) ; /* remove extra space from C */ return (cs_done (C, w, x, 1)) ; /* success; free workspace, return C */ } igraph/src/bipartite.c0000644000176000001440000010450412325527072014503 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2008-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_bipartite.h" #include "igraph_attributes.h" #include "igraph_adjlist.h" #include "igraph_interface.h" #include "igraph_constructors.h" #include "igraph_dqueue.h" #include "igraph_random.h" #include "igraph_nongraph.h" /** * \section about_bipartite Bipartite networks in igraph * * * A bipartite network contains two kinds of vertices and connections * are only possible between two vertices of different kind. There are * many natural examples, e.g. movies and actors as vertices and a * movie is connected to all participating actors, etc. * * * igraph does not have direct support for bipartite networks, at * least not at the C language level. In other words the igraph_t * structure does not contain information about the vertex types. * The C functions for bipartite networks usually have an additional * input argument to graph, called \c types, a boolean vector giving * the vertex types. * * * Most functions creating bipartite networks are able to create this * extra vector, you just need to supply an initialized boolean vector * to them. */ /** * \function igraph_bipartite_projection_size * Calculate the number of vertices and edges in the bipartite projections * * This function calculates the number of vertices and edges in the * two projections of a bipartite network. This is useful if you have * a big bipartite network and you want to estimate the amount of * memory you would need to calculate the projections themselves. * * \param graph The input graph. * \param types Boolean vector giving the vertex types of the graph. * \param vcount1 Pointer to an \c igraph_integer_t, the number of * vertices in the first projection is stored here. * \param ecount1 Pointer to an \c igraph_integer_t, the number of * edges in the first projection is stored here. * \param vcount2 Pointer to an \c igraph_integer_t, the number of * vertices in the second projection is stored here. * \param ecount2 Pointer to an \c igraph_integer_t, the number of * edges in the second projection is stored here. * \return Error code. * * \sa \ref igraph_bipartite_projection() to calculate the actual * projection. * * Time complexity: O(|V|*d^2+|E|), |V| is the number of vertices, |E| * is the number of edges, d is the average (total) degree of the * graphs. * * \example examples/simple/igraph_bipartite_projection.c */ int igraph_bipartite_projection_size(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_integer_t *vcount1, igraph_integer_t *ecount1, igraph_integer_t *vcount2, igraph_integer_t *ecount2) { long int no_of_nodes=igraph_vcount(graph); long int vc1=0, ec1=0, vc2=0, ec2=0; igraph_adjlist_t adjlist; igraph_vector_long_t added; long int i; IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &added); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); for (i=0; i= no_of_nodes) { IGRAPH_ERROR("No such vertex to probe", IGRAPH_EINVAL); } if (probe1 >= 0 && !proj1) { IGRAPH_ERROR("`probe1' given, but `proj1' is a null pointer", IGRAPH_EINVAL); } if (probe1 >=0) { t1=VECTOR(*types)[(long int)probe1]; if (proj2) { t2=1-t1; } else { t2=-1; } } else { t1 = proj1 ? 0 : -1; t2 = proj2 ? 1 : -1; } IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj1, t1, multiplicity1)); IGRAPH_FINALLY(igraph_destroy, proj1); IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj2, t2, multiplicity2)); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_full_bipartite * Create a full bipartite network * * A bipartite network contains two kinds of vertices and connections * are only possible between two vertices of different kind. There are * many natural examples, e.g. movies and actors as vertices and a * movie is connected to all participating actors, etc. * * * igraph does not have direct support for bipartite networks, at * least not at the C language level. In other words the igraph_t * structure does not contain information about the vertex types. * The C functions for bipartite networks usually have an additional * input argument to graph, called \c types, a boolean vector giving * the vertex types. * * * Most functions creating bipartite networks are able to create this * extra vector, you just need to supply an initialized boolean vector * to them. * * \param graph Pointer to an igraph_t object, the graph will be * created here. * \param types Pointer to a boolean vector. If not a null pointer, * then the vertex types will be stored here. * \param n1 Integer, the number of vertices of the first kind. * \param n2 Integer, the number of vertices of the second kind. * \param directed Boolean, whether to create a directed graph. * \param mode A constant that gives the type of connections for * directed graphs. If \c IGRAPH_OUT, then edges point from vertices * of the first kind to vertices of the second kind; if \c * IGRAPH_IN, then the opposite direction is realized; if \c * IGRAPH_ALL, then mutual edges will be created. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. * * \sa \ref igraph_full() for non-bipartite full graphs. */ int igraph_full_bipartite(igraph_t *graph, igraph_vector_bool_t *types, igraph_integer_t n1, igraph_integer_t n2, igraph_bool_t directed, igraph_neimode_t mode) { igraph_integer_t nn1=n1, nn2=n2; igraph_integer_t no_of_nodes=nn1+nn2; igraph_vector_t edges; long int no_of_edges; long int ptr=0; long int i, j; if (!directed) { no_of_edges=nn1 * nn2; } else if (mode==IGRAPH_OUT || mode==IGRAPH_IN) { no_of_edges=nn1 * nn2; } else { /* mode==IGRAPH_ALL */ no_of_edges=nn1 * nn2 * 2; } IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2); if (!directed || mode==IGRAPH_OUT) { for (i=0; i= no_of_nodes) { IGRAPH_ERROR("Invalid (negative) vertex id", IGRAPH_EINVVID); } /* Check types vector */ if (no_of_nodes != 0) { igraph_vector_bool_minmax(types, &min_type, &max_type); if (min_type < 0 || max_type > 1) { IGRAPH_WARNING("Non-binary type vector when creating a bipartite graph"); } } /* Check bipartiteness */ for (i=0; i * Note that this function can operate in two modes, depending on the * \p multiple argument. If it is FALSE (i.e. 0), then a single edge is * created for every non-zero element in the incidence matrix. If \p * multiple is TRUE (i.e. 1), then the matrix elements are rounded up * to the closest non-negative integer to get the number of edges to * create between a pair of vertices. * * * This function does not create multiple edges if \p multiple is * FALSE, but might create some if it is TRUE. * * \param graph Pointer to an uninitialized graph object. * \param types Pointer to an initialized boolean vector, or a null * pointer. If not a null pointer, then the vertex types are stored * here. It is resized as needed. * \param incidence The incidence matrix. * \param directed Gives whether to create an undirected or a directed * graph. * \param mode Specifies the direction of the edges in a directed * graph. If \c IGRAPH_OUT, then edges point from vertices * of the first kind (corresponding to rows) to vertices of the * second kind (corresponding to columns); if \c * IGRAPH_IN, then the opposite direction is realized; if \c * IGRAPH_ALL, then mutual edges will be created. * \param multiple How to interpret the incidence matrix elements. See * details below. * \return Error code. * * Time complexity: O(n*m), the size of the incidence matrix. */ int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, const igraph_matrix_t *incidence, igraph_bool_t directed, igraph_neimode_t mode, igraph_bool_t multiple) { igraph_integer_t n1=(igraph_integer_t) igraph_matrix_nrow(incidence); igraph_integer_t n2=(igraph_integer_t) igraph_matrix_ncol(incidence); igraph_integer_t no_of_nodes=n1+n2; igraph_vector_t edges; long int i, j, k; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (multiple) { for (i=0; i * This function simply checks whether a graph \emph{could} be * bipartite. It tries to find a mapping that gives a possible division * of the vertices into two classes, such that no two vertices of the * same class are connected by an edge. * * * The existence of such a mapping is equivalent of having no circuits of * odd length in the graph. A graph with loop edges cannot bipartite. * * * Note that the mapping is not necessarily unique, e.g. if the graph has * at least two components, then the vertices in the separate components * can be mapped independently. * * \param graph The input graph. * \param res Pointer to a boolean, the result is stored here. * \param type Pointer to an initialized boolean vector, or a null * pointer. If not a null pointer and a mapping was found, then it * is stored here. If not a null pointer, but no mapping was found, * the contents of this vector is invalid. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. */ int igraph_is_bipartite(const igraph_t *graph, igraph_bool_t *res, igraph_vector_bool_t *type) { /* We basically do a breadth first search and label the vertices along the way. We stop as soon as we can find a contradiction. In the 'seen' vector 0 means 'not seen yet', 1 means type 1, 2 means type 2. */ long int no_of_nodes=igraph_vcount(graph); igraph_vector_char_t seen; igraph_dqueue_t Q; igraph_vector_t neis; igraph_bool_t bi=1; long int i; IGRAPH_CHECK(igraph_vector_char_init(&seen, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_char_destroy, &seen); IGRAPH_DQUEUE_INIT_FINALLY(&Q, 100); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); for (i=0; bi && i 1.0) { IGRAPH_ERROR("Invalid connection probability", IGRAPH_EINVAL); } if (types) { IGRAPH_CHECK(igraph_vector_bool_resize(types, n1 + n2)); igraph_vector_bool_null(types); for (i=n1; i maxedges) { IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); } if (maxedges == m) { IGRAPH_CHECK(retval=igraph_full_bipartite(graph, types, n1, n2, directed, mode)); } else { long int to, from; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&s, 0); IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges-1, m)); IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s)*2)); for (i=0; i. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpios.h" #define _MIR_DEBUG 0 #define MAXAGGR 5 /* maximal number of rows which can be aggregated */ struct MIR { /* MIR cut generator working area */ /*--------------------------------------------------------------*/ /* global information valid for the root subproblem */ int m; /* number of rows (in the root subproblem) */ int n; /* number of columns */ char *skip; /* char skip[1+m]; */ /* skip[i], 1 <= i <= m, is a flag that means that row i should not be used because (1) it is not suitable, or (2) because it has been used in the aggregated constraint */ char *isint; /* char isint[1+m+n]; */ /* isint[k], 1 <= k <= m+n, is a flag that means that variable x[k] is integer (otherwise, continuous) */ double *lb; /* double lb[1+m+n]; */ /* lb[k], 1 <= k <= m+n, is lower bound of x[k]; -DBL_MAX means that x[k] has no lower bound */ int *vlb; /* int vlb[1+m+n]; */ /* vlb[k] = k', 1 <= k <= m+n, is the number of integer variable, which defines variable lower bound x[k] >= lb[k] * x[k']; zero means that x[k] has simple lower bound */ double *ub; /* double ub[1+m+n]; */ /* ub[k], 1 <= k <= m+n, is upper bound of x[k]; +DBL_MAX means that x[k] has no upper bound */ int *vub; /* int vub[1+m+n]; */ /* vub[k] = k', 1 <= k <= m+n, is the number of integer variable, which defines variable upper bound x[k] <= ub[k] * x[k']; zero means that x[k] has simple upper bound */ /*--------------------------------------------------------------*/ /* current (fractional) point to be separated */ double *x; /* double x[1+m+n]; */ /* x[k] is current value of auxiliary (1 <= k <= m) or structural (m+1 <= k <= m+n) variable */ /*--------------------------------------------------------------*/ /* aggregated constraint sum a[k] * x[k] = b, which is a linear combination of original constraints transformed to equalities by introducing auxiliary variables */ int agg_cnt; /* number of rows (original constraints) used to build aggregated constraint, 1 <= agg_cnt <= MAXAGGR */ int *agg_row; /* int agg_row[1+MAXAGGR]; */ /* agg_row[k], 1 <= k <= agg_cnt, is the row number used to build aggregated constraint */ IOSVEC *agg_vec; /* IOSVEC agg_vec[1:m+n]; */ /* sparse vector of aggregated constraint coefficients, a[k] */ double agg_rhs; /* right-hand side of the aggregated constraint, b */ /*--------------------------------------------------------------*/ /* bound substitution flags for modified constraint */ char *subst; /* char subst[1+m+n]; */ /* subst[k], 1 <= k <= m+n, is a bound substitution flag used for variable x[k]: '?' - x[k] is missing in modified constraint 'L' - x[k] = (lower bound) + x'[k] 'U' - x[k] = (upper bound) - x'[k] */ /*--------------------------------------------------------------*/ /* modified constraint sum a'[k] * x'[k] = b', where x'[k] >= 0, derived from aggregated constraint by substituting bounds; note that due to substitution of variable bounds there may be additional terms in the modified constraint */ IOSVEC *mod_vec; /* IOSVEC mod_vec[1:m+n]; */ /* sparse vector of modified constraint coefficients, a'[k] */ double mod_rhs; /* right-hand side of the modified constraint, b' */ /*--------------------------------------------------------------*/ /* cutting plane sum alpha[k] * x[k] <= beta */ IOSVEC *cut_vec; /* IOSVEC cut_vec[1:m+n]; */ /* sparse vector of cutting plane coefficients, alpha[k] */ double cut_rhs; /* right-hand size of the cutting plane, beta */ }; /*********************************************************************** * NAME * * ios_mir_init - initialize MIR cut generator * * SYNOPSIS * * #include "glpios.h" * void *ios_mir_init(glp_tree *tree); * * DESCRIPTION * * The routine ios_mir_init initializes the MIR cut generator assuming * that the current subproblem is the root subproblem. * * RETURNS * * The routine ios_mir_init returns a pointer to the MIR cut generator * working area. */ static void set_row_attrib(glp_tree *tree, struct MIR *mir) { /* set global row attributes */ glp_prob *mip = tree->mip; int m = mir->m; int k; for (k = 1; k <= m; k++) { GLPROW *row = mip->row[k]; mir->skip[k] = 0; mir->isint[k] = 0; switch (row->type) { case GLP_FR: mir->lb[k] = -DBL_MAX, mir->ub[k] = +DBL_MAX; break; case GLP_LO: mir->lb[k] = row->lb, mir->ub[k] = +DBL_MAX; break; case GLP_UP: mir->lb[k] = -DBL_MAX, mir->ub[k] = row->ub; break; case GLP_DB: mir->lb[k] = row->lb, mir->ub[k] = row->ub; break; case GLP_FX: mir->lb[k] = mir->ub[k] = row->lb; break; default: xassert(row != row); } mir->vlb[k] = mir->vub[k] = 0; } return; } static void set_col_attrib(glp_tree *tree, struct MIR *mir) { /* set global column attributes */ glp_prob *mip = tree->mip; int m = mir->m; int n = mir->n; int k; for (k = m+1; k <= m+n; k++) { GLPCOL *col = mip->col[k-m]; switch (col->kind) { case GLP_CV: mir->isint[k] = 0; break; case GLP_IV: mir->isint[k] = 1; break; default: xassert(col != col); } switch (col->type) { case GLP_FR: mir->lb[k] = -DBL_MAX, mir->ub[k] = +DBL_MAX; break; case GLP_LO: mir->lb[k] = col->lb, mir->ub[k] = +DBL_MAX; break; case GLP_UP: mir->lb[k] = -DBL_MAX, mir->ub[k] = col->ub; break; case GLP_DB: mir->lb[k] = col->lb, mir->ub[k] = col->ub; break; case GLP_FX: mir->lb[k] = mir->ub[k] = col->lb; break; default: xassert(col != col); } mir->vlb[k] = mir->vub[k] = 0; } return; } static void set_var_bounds(glp_tree *tree, struct MIR *mir) { /* set variable bounds */ glp_prob *mip = tree->mip; int m = mir->m; GLPAIJ *aij; int i, k1, k2; double a1, a2; for (i = 1; i <= m; i++) { /* we need the row to be '>= 0' or '<= 0' */ if (!(mir->lb[i] == 0.0 && mir->ub[i] == +DBL_MAX || mir->lb[i] == -DBL_MAX && mir->ub[i] == 0.0)) continue; /* take first term */ aij = mip->row[i]->ptr; if (aij == NULL) continue; k1 = m + aij->col->j, a1 = aij->val; /* take second term */ aij = aij->r_next; if (aij == NULL) continue; k2 = m + aij->col->j, a2 = aij->val; /* there must be only two terms */ if (aij->r_next != NULL) continue; /* interchange terms, if needed */ if (!mir->isint[k1] && mir->isint[k2]) ; else if (mir->isint[k1] && !mir->isint[k2]) { k2 = k1, a2 = a1; k1 = m + aij->col->j, a1 = aij->val; } else { /* both terms are either continuous or integer */ continue; } /* x[k2] should be double-bounded */ if (mir->lb[k2] == -DBL_MAX || mir->ub[k2] == +DBL_MAX || mir->lb[k2] == mir->ub[k2]) continue; /* change signs, if necessary */ if (mir->ub[i] == 0.0) a1 = - a1, a2 = - a2; /* now the row has the form a1 * x1 + a2 * x2 >= 0, where x1 is continuous, x2 is integer */ if (a1 > 0.0) { /* x1 >= - (a2 / a1) * x2 */ if (mir->vlb[k1] == 0) { /* set variable lower bound for x1 */ mir->lb[k1] = - a2 / a1; mir->vlb[k1] = k2; /* the row should not be used */ mir->skip[i] = 1; } } else /* a1 < 0.0 */ { /* x1 <= - (a2 / a1) * x2 */ if (mir->vub[k1] == 0) { /* set variable upper bound for x1 */ mir->ub[k1] = - a2 / a1; mir->vub[k1] = k2; /* the row should not be used */ mir->skip[i] = 1; } } } return; } static void mark_useless_rows(glp_tree *tree, struct MIR *mir) { /* mark rows which should not be used */ glp_prob *mip = tree->mip; int m = mir->m; GLPAIJ *aij; int i, k, nv; for (i = 1; i <= m; i++) { /* free rows should not be used */ if (mir->lb[i] == -DBL_MAX && mir->ub[i] == +DBL_MAX) { mir->skip[i] = 1; continue; } nv = 0; for (aij = mip->row[i]->ptr; aij != NULL; aij = aij->r_next) { k = m + aij->col->j; /* rows with free variables should not be used */ if (mir->lb[k] == -DBL_MAX && mir->ub[k] == +DBL_MAX) { mir->skip[i] = 1; break; } /* rows with integer variables having infinite (lower or upper) bound should not be used */ if (mir->isint[k] && mir->lb[k] == -DBL_MAX || mir->isint[k] && mir->ub[k] == +DBL_MAX) { mir->skip[i] = 1; break; } /* count non-fixed variables */ if (!(mir->vlb[k] == 0 && mir->vub[k] == 0 && mir->lb[k] == mir->ub[k])) nv++; } /* rows with all variables fixed should not be used */ if (nv == 0) { mir->skip[i] = 1; continue; } } return; } void *ios_mir_init(glp_tree *tree) { /* initialize MIR cut generator */ glp_prob *mip = tree->mip; int m = mip->m; int n = mip->n; struct MIR *mir; #if _MIR_DEBUG xprintf("ios_mir_init: warning: debug mode enabled\n"); #endif /* allocate working area */ mir = xmalloc(sizeof(struct MIR)); mir->m = m; mir->n = n; mir->skip = xcalloc(1+m, sizeof(char)); mir->isint = xcalloc(1+m+n, sizeof(char)); mir->lb = xcalloc(1+m+n, sizeof(double)); mir->vlb = xcalloc(1+m+n, sizeof(int)); mir->ub = xcalloc(1+m+n, sizeof(double)); mir->vub = xcalloc(1+m+n, sizeof(int)); mir->x = xcalloc(1+m+n, sizeof(double)); mir->agg_row = xcalloc(1+MAXAGGR, sizeof(int)); mir->agg_vec = ios_create_vec(m+n); mir->subst = xcalloc(1+m+n, sizeof(char)); mir->mod_vec = ios_create_vec(m+n); mir->cut_vec = ios_create_vec(m+n); /* set global row attributes */ set_row_attrib(tree, mir); /* set global column attributes */ set_col_attrib(tree, mir); /* set variable bounds */ set_var_bounds(tree, mir); /* mark rows which should not be used */ mark_useless_rows(tree, mir); return mir; } /*********************************************************************** * NAME * * ios_mir_gen - generate MIR cuts * * SYNOPSIS * * #include "glpios.h" * void ios_mir_gen(glp_tree *tree, void *gen, IOSPOOL *pool); * * DESCRIPTION * * The routine ios_mir_gen generates MIR cuts for the current point and * adds them to the cut pool. */ static void get_current_point(glp_tree *tree, struct MIR *mir) { /* obtain current point */ glp_prob *mip = tree->mip; int m = mir->m; int n = mir->n; int k; for (k = 1; k <= m; k++) mir->x[k] = mip->row[k]->prim; for (k = m+1; k <= m+n; k++) mir->x[k] = mip->col[k-m]->prim; return; } #if _MIR_DEBUG static void check_current_point(struct MIR *mir) { /* check current point */ int m = mir->m; int n = mir->n; int k, kk; double lb, ub, eps; for (k = 1; k <= m+n; k++) { /* determine lower bound */ lb = mir->lb[k]; kk = mir->vlb[k]; if (kk != 0) { xassert(lb != -DBL_MAX); xassert(!mir->isint[k]); xassert(mir->isint[kk]); lb *= mir->x[kk]; } /* check lower bound */ if (lb != -DBL_MAX) { eps = 1e-6 * (1.0 + fabs(lb)); xassert(mir->x[k] >= lb - eps); } /* determine upper bound */ ub = mir->ub[k]; kk = mir->vub[k]; if (kk != 0) { xassert(ub != +DBL_MAX); xassert(!mir->isint[k]); xassert(mir->isint[kk]); ub *= mir->x[kk]; } /* check upper bound */ if (ub != +DBL_MAX) { eps = 1e-6 * (1.0 + fabs(ub)); xassert(mir->x[k] <= ub + eps); } } return; } #endif static void initial_agg_row(glp_tree *tree, struct MIR *mir, int i) { /* use original i-th row as initial aggregated constraint */ glp_prob *mip = tree->mip; int m = mir->m; GLPAIJ *aij; xassert(1 <= i && i <= m); xassert(!mir->skip[i]); /* mark i-th row in order not to use it in the same aggregated constraint */ mir->skip[i] = 2; mir->agg_cnt = 1; mir->agg_row[1] = i; /* use x[i] - sum a[i,j] * x[m+j] = 0, where x[i] is auxiliary variable of row i, x[m+j] are structural variables */ ios_clear_vec(mir->agg_vec); ios_set_vj(mir->agg_vec, i, 1.0); for (aij = mip->row[i]->ptr; aij != NULL; aij = aij->r_next) ios_set_vj(mir->agg_vec, m + aij->col->j, - aij->val); mir->agg_rhs = 0.0; #if _MIR_DEBUG ios_check_vec(mir->agg_vec); #endif return; } #if _MIR_DEBUG static void check_agg_row(struct MIR *mir) { /* check aggregated constraint */ int m = mir->m; int n = mir->n; int j, k; double r, big; /* compute the residual r = sum a[k] * x[k] - b and determine big = max(1, |a[k]|, |b|) */ r = 0.0, big = 1.0; for (j = 1; j <= mir->agg_vec->nnz; j++) { k = mir->agg_vec->ind[j]; xassert(1 <= k && k <= m+n); r += mir->agg_vec->val[j] * mir->x[k]; if (big < fabs(mir->agg_vec->val[j])) big = fabs(mir->agg_vec->val[j]); } r -= mir->agg_rhs; if (big < fabs(mir->agg_rhs)) big = fabs(mir->agg_rhs); /* the residual must be close to zero */ xassert(fabs(r) <= 1e-6 * big); return; } #endif static void subst_fixed_vars(struct MIR *mir) { /* substitute fixed variables into aggregated constraint */ int m = mir->m; int n = mir->n; int j, k; for (j = 1; j <= mir->agg_vec->nnz; j++) { k = mir->agg_vec->ind[j]; xassert(1 <= k && k <= m+n); if (mir->vlb[k] == 0 && mir->vub[k] == 0 && mir->lb[k] == mir->ub[k]) { /* x[k] is fixed */ mir->agg_rhs -= mir->agg_vec->val[j] * mir->lb[k]; mir->agg_vec->val[j] = 0.0; } } /* remove terms corresponding to fixed variables */ ios_clean_vec(mir->agg_vec, DBL_EPSILON); #if _MIR_DEBUG ios_check_vec(mir->agg_vec); #endif return; } static void bound_subst_heur(struct MIR *mir) { /* bound substitution heuristic */ int m = mir->m; int n = mir->n; int j, k, kk; double d1, d2; for (j = 1; j <= mir->agg_vec->nnz; j++) { k = mir->agg_vec->ind[j]; xassert(1 <= k && k <= m+n); if (mir->isint[k]) continue; /* skip integer variable */ /* compute distance from x[k] to its lower bound */ kk = mir->vlb[k]; if (kk == 0) { if (mir->lb[k] == -DBL_MAX) d1 = DBL_MAX; else d1 = mir->x[k] - mir->lb[k]; } else { xassert(1 <= kk && kk <= m+n); xassert(mir->isint[kk]); xassert(mir->lb[k] != -DBL_MAX); d1 = mir->x[k] - mir->lb[k] * mir->x[kk]; } /* compute distance from x[k] to its upper bound */ kk = mir->vub[k]; if (kk == 0) { if (mir->vub[k] == +DBL_MAX) d2 = DBL_MAX; else d2 = mir->ub[k] - mir->x[k]; } else { xassert(1 <= kk && kk <= m+n); xassert(mir->isint[kk]); xassert(mir->ub[k] != +DBL_MAX); d2 = mir->ub[k] * mir->x[kk] - mir->x[k]; } /* x[k] cannot be free */ xassert(d1 != DBL_MAX || d2 != DBL_MAX); /* choose the bound which is closer to x[k] */ xassert(mir->subst[k] == '?'); if (d1 <= d2) mir->subst[k] = 'L'; else mir->subst[k] = 'U'; } return; } static void build_mod_row(struct MIR *mir) { /* substitute bounds and build modified constraint */ int m = mir->m; int n = mir->n; int j, jj, k, kk; /* initially modified constraint is aggregated constraint */ ios_copy_vec(mir->mod_vec, mir->agg_vec); mir->mod_rhs = mir->agg_rhs; #if _MIR_DEBUG ios_check_vec(mir->mod_vec); #endif /* substitute bounds for continuous variables; note that due to substitution of variable bounds additional terms may appear in modified constraint */ for (j = mir->mod_vec->nnz; j >= 1; j--) { k = mir->mod_vec->ind[j]; xassert(1 <= k && k <= m+n); if (mir->isint[k]) continue; /* skip integer variable */ if (mir->subst[k] == 'L') { /* x[k] = (lower bound) + x'[k] */ xassert(mir->lb[k] != -DBL_MAX); kk = mir->vlb[k]; if (kk == 0) { /* x[k] = lb[k] + x'[k] */ mir->mod_rhs -= mir->mod_vec->val[j] * mir->lb[k]; } else { /* x[k] = lb[k] * x[kk] + x'[k] */ xassert(mir->isint[kk]); jj = mir->mod_vec->pos[kk]; if (jj == 0) { ios_set_vj(mir->mod_vec, kk, 1.0); jj = mir->mod_vec->pos[kk]; mir->mod_vec->val[jj] = 0.0; } mir->mod_vec->val[jj] += mir->mod_vec->val[j] * mir->lb[k]; } } else if (mir->subst[k] == 'U') { /* x[k] = (upper bound) - x'[k] */ xassert(mir->ub[k] != +DBL_MAX); kk = mir->vub[k]; if (kk == 0) { /* x[k] = ub[k] - x'[k] */ mir->mod_rhs -= mir->mod_vec->val[j] * mir->ub[k]; } else { /* x[k] = ub[k] * x[kk] - x'[k] */ xassert(mir->isint[kk]); jj = mir->mod_vec->pos[kk]; if (jj == 0) { ios_set_vj(mir->mod_vec, kk, 1.0); jj = mir->mod_vec->pos[kk]; mir->mod_vec->val[jj] = 0.0; } mir->mod_vec->val[jj] += mir->mod_vec->val[j] * mir->ub[k]; } mir->mod_vec->val[j] = - mir->mod_vec->val[j]; } else xassert(k != k); } #if _MIR_DEBUG ios_check_vec(mir->mod_vec); #endif /* substitute bounds for integer variables */ for (j = 1; j <= mir->mod_vec->nnz; j++) { k = mir->mod_vec->ind[j]; xassert(1 <= k && k <= m+n); if (!mir->isint[k]) continue; /* skip continuous variable */ xassert(mir->subst[k] == '?'); xassert(mir->vlb[k] == 0 && mir->vub[k] == 0); xassert(mir->lb[k] != -DBL_MAX && mir->ub[k] != +DBL_MAX); if (fabs(mir->lb[k]) <= fabs(mir->ub[k])) { /* x[k] = lb[k] + x'[k] */ mir->subst[k] = 'L'; mir->mod_rhs -= mir->mod_vec->val[j] * mir->lb[k]; } else { /* x[k] = ub[k] - x'[k] */ mir->subst[k] = 'U'; mir->mod_rhs -= mir->mod_vec->val[j] * mir->ub[k]; mir->mod_vec->val[j] = - mir->mod_vec->val[j]; } } #if _MIR_DEBUG ios_check_vec(mir->mod_vec); #endif return; } #if _MIR_DEBUG static void check_mod_row(struct MIR *mir) { /* check modified constraint */ int m = mir->m; int n = mir->n; int j, k, kk; double r, big, x; /* compute the residual r = sum a'[k] * x'[k] - b' and determine big = max(1, |a[k]|, |b|) */ r = 0.0, big = 1.0; for (j = 1; j <= mir->mod_vec->nnz; j++) { k = mir->mod_vec->ind[j]; xassert(1 <= k && k <= m+n); if (mir->subst[k] == 'L') { /* x'[k] = x[k] - (lower bound) */ xassert(mir->lb[k] != -DBL_MAX); kk = mir->vlb[k]; if (kk == 0) x = mir->x[k] - mir->lb[k]; else x = mir->x[k] - mir->lb[k] * mir->x[kk]; } else if (mir->subst[k] == 'U') { /* x'[k] = (upper bound) - x[k] */ xassert(mir->ub[k] != +DBL_MAX); kk = mir->vub[k]; if (kk == 0) x = mir->ub[k] - mir->x[k]; else x = mir->ub[k] * mir->x[kk] - mir->x[k]; } else xassert(k != k); r += mir->mod_vec->val[j] * x; if (big < fabs(mir->mod_vec->val[j])) big = fabs(mir->mod_vec->val[j]); } r -= mir->mod_rhs; if (big < fabs(mir->mod_rhs)) big = fabs(mir->mod_rhs); /* the residual must be close to zero */ xassert(fabs(r) <= 1e-6 * big); return; } #endif /*********************************************************************** * mir_ineq - construct MIR inequality * * Given the single constraint mixed integer set * * |N| * X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s}, * + + j in N * * this routine constructs the mixed integer rounding (MIR) inequality * * sum alpha[j] * x[j] <= beta + gamma * s, * j in N * * which is valid for X. * * If the MIR inequality has been successfully constructed, the routine * returns zero. Otherwise, if b is close to nearest integer, there may * be numeric difficulties due to big coefficients; so in this case the * routine returns non-zero. */ static int mir_ineq(const int n, const double a[], const double b, double alpha[], double *beta, double *gamma) { int j; double f, t; if (fabs(b - floor(b + .5)) < 0.01) return 1; f = b - floor(b); for (j = 1; j <= n; j++) { t = (a[j] - floor(a[j])) - f; if (t <= 0.0) alpha[j] = floor(a[j]); else alpha[j] = floor(a[j]) + t / (1.0 - f); } *beta = floor(b); *gamma = 1.0 / (1.0 - f); return 0; } /*********************************************************************** * cmir_ineq - construct c-MIR inequality * * Given the mixed knapsack set * * MK |N| * X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s, * + + j in N * * x[j] <= u[j]}, * * a subset C of variables to be complemented, and a divisor delta > 0, * this routine constructs the complemented MIR (c-MIR) inequality * * sum alpha[j] * x[j] <= beta + gamma * s, * j in N * MK * which is valid for X . * * If the c-MIR inequality has been successfully constructed, the * routine returns zero. Otherwise, if there is a risk of numerical * difficulties due to big coefficients (see comments to the routine * mir_ineq), the routine cmir_ineq returns non-zero. */ static int cmir_ineq(const int n, const double a[], const double b, const double u[], const char cset[], const double delta, double alpha[], double *beta, double *gamma) { int j; double *aa, bb; aa = alpha, bb = b; for (j = 1; j <= n; j++) { aa[j] = a[j] / delta; if (cset[j]) aa[j] = - aa[j], bb -= a[j] * u[j]; } bb /= delta; if (mir_ineq(n, aa, bb, alpha, beta, gamma)) return 1; for (j = 1; j <= n; j++) { if (cset[j]) alpha[j] = - alpha[j], *beta += alpha[j] * u[j]; } *gamma /= delta; return 0; } /*********************************************************************** * cmir_sep - c-MIR separation heuristic * * Given the mixed knapsack set * * MK |N| * X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s, * + + j in N * * x[j] <= u[j]} * * * * * and a fractional point (x , s ), this routine tries to construct * c-MIR inequality * * sum alpha[j] * x[j] <= beta + gamma * s, * j in N * MK * which is valid for X and has (desirably maximal) violation at the * fractional point given. This is attained by choosing an appropriate * set C of variables to be complemented and a divisor delta > 0, which * together define corresponding c-MIR inequality. * * If a violated c-MIR inequality has been successfully constructed, * the routine returns its violation: * * * * * sum alpha[j] * x [j] - beta - gamma * s , * j in N * * which is positive. In case of failure the routine returns zero. */ struct vset { int j; double v; }; static int cmir_cmp(const void *p1, const void *p2) { const struct vset *v1 = p1, *v2 = p2; if (v1->v < v2->v) return -1; if (v1->v > v2->v) return +1; return 0; } static double cmir_sep(const int n, const double a[], const double b, const double u[], const double x[], const double s, double alpha[], double *beta, double *gamma) { int fail, j, k, nv, v; double delta, eps, d_try[1+3], r, r_best; char *cset; struct vset *vset; /* allocate working arrays */ cset = xcalloc(1+n, sizeof(char)); vset = xcalloc(1+n, sizeof(struct vset)); /* choose initial C */ for (j = 1; j <= n; j++) cset[j] = (char)(x[j] >= 0.5 * u[j]); /* choose initial delta */ r_best = delta = 0.0; for (j = 1; j <= n; j++) { xassert(a[j] != 0.0); /* if x[j] is close to its bounds, skip it */ eps = 1e-9 * (1.0 + fabs(u[j])); if (x[j] < eps || x[j] > u[j] - eps) continue; /* try delta = |a[j]| to construct c-MIR inequality */ fail = cmir_ineq(n, a, b, u, cset, fabs(a[j]), alpha, beta, gamma); if (fail) continue; /* compute violation */ r = - (*beta) - (*gamma) * s; for (k = 1; k <= n; k++) r += alpha[k] * x[k]; if (r_best < r) r_best = r, delta = fabs(a[j]); } if (r_best < 0.001) r_best = 0.0; if (r_best == 0.0) goto done; xassert(delta > 0.0); /* try to increase violation by dividing delta by 2, 4, and 8, respectively */ d_try[1] = delta / 2.0; d_try[2] = delta / 4.0; d_try[3] = delta / 8.0; for (j = 1; j <= 3; j++) { /* construct c-MIR inequality */ fail = cmir_ineq(n, a, b, u, cset, d_try[j], alpha, beta, gamma); if (fail) continue; /* compute violation */ r = - (*beta) - (*gamma) * s; for (k = 1; k <= n; k++) r += alpha[k] * x[k]; if (r_best < r) r_best = r, delta = d_try[j]; } /* build subset of variables lying strictly between their bounds and order it by nondecreasing values of |x[j] - u[j]/2| */ nv = 0; for (j = 1; j <= n; j++) { /* if x[j] is close to its bounds, skip it */ eps = 1e-9 * (1.0 + fabs(u[j])); if (x[j] < eps || x[j] > u[j] - eps) continue; /* add x[j] to the subset */ nv++; vset[nv].j = j; vset[nv].v = fabs(x[j] - 0.5 * u[j]); } qsort(&vset[1], nv, sizeof(struct vset), cmir_cmp); /* try to increase violation by successively complementing each variable in the subset */ for (v = 1; v <= nv; v++) { j = vset[v].j; /* replace x[j] by its complement or vice versa */ cset[j] = (char)!cset[j]; /* construct c-MIR inequality */ fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); /* restore the variable */ cset[j] = (char)!cset[j]; /* do not replace the variable in case of failure */ if (fail) continue; /* compute violation */ r = - (*beta) - (*gamma) * s; for (k = 1; k <= n; k++) r += alpha[k] * x[k]; if (r_best < r) r_best = r, cset[j] = (char)!cset[j]; } /* construct the best c-MIR inequality chosen */ fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); xassert(!fail); done: /* free working arrays */ xfree(cset); xfree(vset); /* return to the calling routine */ return r_best; } static double generate(struct MIR *mir) { /* try to generate violated c-MIR cut for modified constraint */ int m = mir->m; int n = mir->n; int j, k, kk, nint; double s, *u, *x, *alpha, r_best = 0.0, b, beta, gamma; ios_copy_vec(mir->cut_vec, mir->mod_vec); mir->cut_rhs = mir->mod_rhs; /* remove small terms, which can appear due to substitution of variable bounds */ ios_clean_vec(mir->cut_vec, DBL_EPSILON); #if _MIR_DEBUG ios_check_vec(mir->cut_vec); #endif /* remove positive continuous terms to obtain MK relaxation */ for (j = 1; j <= mir->cut_vec->nnz; j++) { k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); if (!mir->isint[k] && mir->cut_vec->val[j] > 0.0) mir->cut_vec->val[j] = 0.0; } ios_clean_vec(mir->cut_vec, 0.0); #if _MIR_DEBUG ios_check_vec(mir->cut_vec); #endif /* move integer terms to the beginning of the sparse vector and determine the number of integer variables */ nint = 0; for (j = 1; j <= mir->cut_vec->nnz; j++) { k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); if (mir->isint[k]) { double temp; nint++; /* interchange elements [nint] and [j] */ kk = mir->cut_vec->ind[nint]; mir->cut_vec->pos[k] = nint; mir->cut_vec->pos[kk] = j; mir->cut_vec->ind[nint] = k; mir->cut_vec->ind[j] = kk; temp = mir->cut_vec->val[nint]; mir->cut_vec->val[nint] = mir->cut_vec->val[j]; mir->cut_vec->val[j] = temp; } } #if _MIR_DEBUG ios_check_vec(mir->cut_vec); #endif /* if there is no integer variable, nothing to generate */ if (nint == 0) goto done; /* allocate working arrays */ u = xcalloc(1+nint, sizeof(double)); x = xcalloc(1+nint, sizeof(double)); alpha = xcalloc(1+nint, sizeof(double)); /* determine u and x */ for (j = 1; j <= nint; j++) { k = mir->cut_vec->ind[j]; xassert(m+1 <= k && k <= m+n); xassert(mir->isint[k]); u[j] = mir->ub[k] - mir->lb[k]; xassert(u[j] >= 1.0); if (mir->subst[k] == 'L') x[j] = mir->x[k] - mir->lb[k]; else if (mir->subst[k] == 'U') x[j] = mir->ub[k] - mir->x[k]; else xassert(k != k); xassert(x[j] >= -0.001); if (x[j] < 0.0) x[j] = 0.0; } /* compute s = - sum of continuous terms */ s = 0.0; for (j = nint+1; j <= mir->cut_vec->nnz; j++) { double x; k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); /* must be continuous */ xassert(!mir->isint[k]); if (mir->subst[k] == 'L') { xassert(mir->lb[k] != -DBL_MAX); kk = mir->vlb[k]; if (kk == 0) x = mir->x[k] - mir->lb[k]; else x = mir->x[k] - mir->lb[k] * mir->x[kk]; } else if (mir->subst[k] == 'U') { xassert(mir->ub[k] != +DBL_MAX); kk = mir->vub[k]; if (kk == 0) x = mir->ub[k] - mir->x[k]; else x = mir->ub[k] * mir->x[kk] - mir->x[k]; } else xassert(k != k); xassert(x >= -0.001); if (x < 0.0) x = 0.0; s -= mir->cut_vec->val[j] * x; } xassert(s >= 0.0); /* apply heuristic to obtain most violated c-MIR inequality */ b = mir->cut_rhs; r_best = cmir_sep(nint, mir->cut_vec->val, b, u, x, s, alpha, &beta, &gamma); if (r_best == 0.0) goto skip; xassert(r_best > 0.0); /* convert to raw cut */ /* sum alpha[j] * x[j] <= beta + gamma * s */ for (j = 1; j <= nint; j++) mir->cut_vec->val[j] = alpha[j]; for (j = nint+1; j <= mir->cut_vec->nnz; j++) { k = mir->cut_vec->ind[j]; if (k <= m+n) mir->cut_vec->val[j] *= gamma; } mir->cut_rhs = beta; #if _MIR_DEBUG ios_check_vec(mir->cut_vec); #endif skip: /* free working arrays */ xfree(u); xfree(x); xfree(alpha); done: return r_best; } #if _MIR_DEBUG static void check_raw_cut(struct MIR *mir, double r_best) { /* check raw cut before back bound substitution */ int m = mir->m; int n = mir->n; int j, k, kk; double r, big, x; /* compute the residual r = sum a[k] * x[k] - b and determine big = max(1, |a[k]|, |b|) */ r = 0.0, big = 1.0; for (j = 1; j <= mir->cut_vec->nnz; j++) { k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); if (mir->subst[k] == 'L') { xassert(mir->lb[k] != -DBL_MAX); kk = mir->vlb[k]; if (kk == 0) x = mir->x[k] - mir->lb[k]; else x = mir->x[k] - mir->lb[k] * mir->x[kk]; } else if (mir->subst[k] == 'U') { xassert(mir->ub[k] != +DBL_MAX); kk = mir->vub[k]; if (kk == 0) x = mir->ub[k] - mir->x[k]; else x = mir->ub[k] * mir->x[kk] - mir->x[k]; } else xassert(k != k); r += mir->cut_vec->val[j] * x; if (big < fabs(mir->cut_vec->val[j])) big = fabs(mir->cut_vec->val[j]); } r -= mir->cut_rhs; if (big < fabs(mir->cut_rhs)) big = fabs(mir->cut_rhs); /* the residual must be close to r_best */ xassert(fabs(r - r_best) <= 1e-6 * big); return; } #endif static void back_subst(struct MIR *mir) { /* back substitution of original bounds */ int m = mir->m; int n = mir->n; int j, jj, k, kk; /* at first, restore bounds of integer variables (because on restoring variable bounds of continuous variables we need original, not shifted, bounds of integer variables) */ for (j = 1; j <= mir->cut_vec->nnz; j++) { k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); if (!mir->isint[k]) continue; /* skip continuous */ if (mir->subst[k] == 'L') { /* x'[k] = x[k] - lb[k] */ xassert(mir->lb[k] != -DBL_MAX); xassert(mir->vlb[k] == 0); mir->cut_rhs += mir->cut_vec->val[j] * mir->lb[k]; } else if (mir->subst[k] == 'U') { /* x'[k] = ub[k] - x[k] */ xassert(mir->ub[k] != +DBL_MAX); xassert(mir->vub[k] == 0); mir->cut_rhs -= mir->cut_vec->val[j] * mir->ub[k]; mir->cut_vec->val[j] = - mir->cut_vec->val[j]; } else xassert(k != k); } /* now restore bounds of continuous variables */ for (j = 1; j <= mir->cut_vec->nnz; j++) { k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); if (mir->isint[k]) continue; /* skip integer */ if (mir->subst[k] == 'L') { /* x'[k] = x[k] - (lower bound) */ xassert(mir->lb[k] != -DBL_MAX); kk = mir->vlb[k]; if (kk == 0) { /* x'[k] = x[k] - lb[k] */ mir->cut_rhs += mir->cut_vec->val[j] * mir->lb[k]; } else { /* x'[k] = x[k] - lb[k] * x[kk] */ jj = mir->cut_vec->pos[kk]; #if 0 xassert(jj != 0); #else if (jj == 0) { ios_set_vj(mir->cut_vec, kk, 1.0); jj = mir->cut_vec->pos[kk]; xassert(jj != 0); mir->cut_vec->val[jj] = 0.0; } #endif mir->cut_vec->val[jj] -= mir->cut_vec->val[j] * mir->lb[k]; } } else if (mir->subst[k] == 'U') { /* x'[k] = (upper bound) - x[k] */ xassert(mir->ub[k] != +DBL_MAX); kk = mir->vub[k]; if (kk == 0) { /* x'[k] = ub[k] - x[k] */ mir->cut_rhs -= mir->cut_vec->val[j] * mir->ub[k]; } else { /* x'[k] = ub[k] * x[kk] - x[k] */ jj = mir->cut_vec->pos[kk]; if (jj == 0) { ios_set_vj(mir->cut_vec, kk, 1.0); jj = mir->cut_vec->pos[kk]; xassert(jj != 0); mir->cut_vec->val[jj] = 0.0; } mir->cut_vec->val[jj] += mir->cut_vec->val[j] * mir->ub[k]; } mir->cut_vec->val[j] = - mir->cut_vec->val[j]; } else xassert(k != k); } #if _MIR_DEBUG ios_check_vec(mir->cut_vec); #endif return; } #if _MIR_DEBUG static void check_cut_row(struct MIR *mir, double r_best) { /* check the cut after back bound substitution or elimination of auxiliary variables */ int m = mir->m; int n = mir->n; int j, k; double r, big; /* compute the residual r = sum a[k] * x[k] - b and determine big = max(1, |a[k]|, |b|) */ r = 0.0, big = 1.0; for (j = 1; j <= mir->cut_vec->nnz; j++) { k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); r += mir->cut_vec->val[j] * mir->x[k]; if (big < fabs(mir->cut_vec->val[j])) big = fabs(mir->cut_vec->val[j]); } r -= mir->cut_rhs; if (big < fabs(mir->cut_rhs)) big = fabs(mir->cut_rhs); /* the residual must be close to r_best */ xassert(fabs(r - r_best) <= 1e-6 * big); return; } #endif static void subst_aux_vars(glp_tree *tree, struct MIR *mir) { /* final substitution to eliminate auxiliary variables */ glp_prob *mip = tree->mip; int m = mir->m; int n = mir->n; GLPAIJ *aij; int j, k, kk, jj; for (j = mir->cut_vec->nnz; j >= 1; j--) { k = mir->cut_vec->ind[j]; xassert(1 <= k && k <= m+n); if (k > m) continue; /* skip structurals */ for (aij = mip->row[k]->ptr; aij != NULL; aij = aij->r_next) { kk = m + aij->col->j; /* structural */ jj = mir->cut_vec->pos[kk]; if (jj == 0) { ios_set_vj(mir->cut_vec, kk, 1.0); jj = mir->cut_vec->pos[kk]; mir->cut_vec->val[jj] = 0.0; } mir->cut_vec->val[jj] += mir->cut_vec->val[j] * aij->val; } mir->cut_vec->val[j] = 0.0; } ios_clean_vec(mir->cut_vec, 0.0); return; } static void add_cut(glp_tree *tree, struct MIR *mir) { /* add constructed cut inequality to the cut pool */ int m = mir->m; int n = mir->n; int j, k, len; int *ind = xcalloc(1+n, sizeof(int)); double *val = xcalloc(1+n, sizeof(double)); len = 0; for (j = mir->cut_vec->nnz; j >= 1; j--) { k = mir->cut_vec->ind[j]; xassert(m+1 <= k && k <= m+n); len++, ind[len] = k - m, val[len] = mir->cut_vec->val[j]; } #if 0 ios_add_cut_row(tree, pool, GLP_RF_MIR, len, ind, val, GLP_UP, mir->cut_rhs); #else glp_ios_add_row(tree, NULL, GLP_RF_MIR, 0, len, ind, val, GLP_UP, mir->cut_rhs); #endif xfree(ind); xfree(val); return; } static int aggregate_row(glp_tree *tree, struct MIR *mir) { /* try to aggregate another row */ glp_prob *mip = tree->mip; int m = mir->m; int n = mir->n; GLPAIJ *aij; IOSVEC *v; int ii, j, jj, k, kk, kappa = 0, ret = 0; double d1, d2, d, d_max = 0.0; /* choose appropriate structural variable in the aggregated row to be substituted */ for (j = 1; j <= mir->agg_vec->nnz; j++) { k = mir->agg_vec->ind[j]; xassert(1 <= k && k <= m+n); if (k <= m) continue; /* skip auxiliary var */ if (mir->isint[k]) continue; /* skip integer var */ if (fabs(mir->agg_vec->val[j]) < 0.001) continue; /* compute distance from x[k] to its lower bound */ kk = mir->vlb[k]; if (kk == 0) { if (mir->lb[k] == -DBL_MAX) d1 = DBL_MAX; else d1 = mir->x[k] - mir->lb[k]; } else { xassert(1 <= kk && kk <= m+n); xassert(mir->isint[kk]); xassert(mir->lb[k] != -DBL_MAX); d1 = mir->x[k] - mir->lb[k] * mir->x[kk]; } /* compute distance from x[k] to its upper bound */ kk = mir->vub[k]; if (kk == 0) { if (mir->vub[k] == +DBL_MAX) d2 = DBL_MAX; else d2 = mir->ub[k] - mir->x[k]; } else { xassert(1 <= kk && kk <= m+n); xassert(mir->isint[kk]); xassert(mir->ub[k] != +DBL_MAX); d2 = mir->ub[k] * mir->x[kk] - mir->x[k]; } /* x[k] cannot be free */ xassert(d1 != DBL_MAX || d2 != DBL_MAX); /* d = min(d1, d2) */ d = (d1 <= d2 ? d1 : d2); xassert(d != DBL_MAX); /* should not be close to corresponding bound */ if (d < 0.001) continue; if (d_max < d) d_max = d, kappa = k; } if (kappa == 0) { /* nothing chosen */ ret = 1; goto done; } /* x[kappa] has been chosen */ xassert(m+1 <= kappa && kappa <= m+n); xassert(!mir->isint[kappa]); /* find another row, which have not been used yet, to eliminate x[kappa] from the aggregated row */ for (ii = 1; ii <= m; ii++) { if (mir->skip[ii]) continue; for (aij = mip->row[ii]->ptr; aij != NULL; aij = aij->r_next) if (aij->col->j == kappa - m) break; if (aij != NULL && fabs(aij->val) >= 0.001) break; } if (ii > m) { /* nothing found */ ret = 2; goto done; } /* row ii has been found; include it in the aggregated list */ mir->agg_cnt++; xassert(mir->agg_cnt <= MAXAGGR); mir->agg_row[mir->agg_cnt] = ii; mir->skip[ii] = 2; /* v := new row */ v = ios_create_vec(m+n); ios_set_vj(v, ii, 1.0); for (aij = mip->row[ii]->ptr; aij != NULL; aij = aij->r_next) ios_set_vj(v, m + aij->col->j, - aij->val); #if _MIR_DEBUG ios_check_vec(v); #endif /* perform gaussian elimination to remove x[kappa] */ j = mir->agg_vec->pos[kappa]; xassert(j != 0); jj = v->pos[kappa]; xassert(jj != 0); ios_linear_comb(mir->agg_vec, - mir->agg_vec->val[j] / v->val[jj], v); ios_delete_vec(v); ios_set_vj(mir->agg_vec, kappa, 0.0); #if _MIR_DEBUG ios_check_vec(mir->agg_vec); #endif done: return ret; } void ios_mir_gen(glp_tree *tree, void *gen) { /* main routine to generate MIR cuts */ glp_prob *mip = tree->mip; struct MIR *mir = gen; int m = mir->m; int n = mir->n; int i; double r_best; xassert(mip->m >= m); xassert(mip->n == n); /* obtain current point */ get_current_point(tree, mir); #if _MIR_DEBUG /* check current point */ check_current_point(mir); #endif /* reset bound substitution flags */ memset(&mir->subst[1], '?', m+n); /* try to generate a set of violated MIR cuts */ for (i = 1; i <= m; i++) { if (mir->skip[i]) continue; /* use original i-th row as initial aggregated constraint */ initial_agg_row(tree, mir, i); loop: ; #if _MIR_DEBUG /* check aggregated row */ check_agg_row(mir); #endif /* substitute fixed variables into aggregated constraint */ subst_fixed_vars(mir); #if _MIR_DEBUG /* check aggregated row */ check_agg_row(mir); #endif #if _MIR_DEBUG /* check bound substitution flags */ { int k; for (k = 1; k <= m+n; k++) xassert(mir->subst[k] == '?'); } #endif /* apply bound substitution heuristic */ bound_subst_heur(mir); /* substitute bounds and build modified constraint */ build_mod_row(mir); #if _MIR_DEBUG /* check modified row */ check_mod_row(mir); #endif /* try to generate violated c-MIR cut for modified row */ r_best = generate(mir); if (r_best > 0.0) { /* success */ #if _MIR_DEBUG /* check raw cut before back bound substitution */ check_raw_cut(mir, r_best); #endif /* back substitution of original bounds */ back_subst(mir); #if _MIR_DEBUG /* check the cut after back bound substitution */ check_cut_row(mir, r_best); #endif /* final substitution to eliminate auxiliary variables */ subst_aux_vars(tree, mir); #if _MIR_DEBUG /* check the cut after elimination of auxiliaries */ check_cut_row(mir, r_best); #endif /* add constructed cut inequality to the cut pool */ add_cut(tree, mir); } /* reset bound substitution flags */ { int j, k; for (j = 1; j <= mir->mod_vec->nnz; j++) { k = mir->mod_vec->ind[j]; xassert(1 <= k && k <= m+n); xassert(mir->subst[k] != '?'); mir->subst[k] = '?'; } } if (r_best == 0.0) { /* failure */ if (mir->agg_cnt < MAXAGGR) { /* try to aggregate another row */ if (aggregate_row(tree, mir) == 0) goto loop; } } /* unmark rows used in the aggregated constraint */ { int k, ii; for (k = 1; k <= mir->agg_cnt; k++) { ii = mir->agg_row[k]; xassert(1 <= ii && ii <= m); xassert(mir->skip[ii] == 2); mir->skip[ii] = 0; } } } return; } /*********************************************************************** * NAME * * ios_mir_term - terminate MIR cut generator * * SYNOPSIS * * #include "glpios.h" * void ios_mir_term(void *gen); * * DESCRIPTION * * The routine ios_mir_term deletes the MIR cut generator working area * freeing all the memory allocated to it. */ void ios_mir_term(void *gen) { struct MIR *mir = gen; xfree(mir->skip); xfree(mir->isint); xfree(mir->lb); xfree(mir->vlb); xfree(mir->ub); xfree(mir->vub); xfree(mir->x); xfree(mir->agg_row); ios_delete_vec(mir->agg_vec); xfree(mir->subst); ios_delete_vec(mir->mod_vec); ios_delete_vec(mir->cut_vec); xfree(mir); return; } /* eof */ igraph/src/amd_order.c0000644000176000001440000001467212325527072014462 0ustar ripleyusers/* ========================================================================= */ /* === AMD_order =========================================================== */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* User-callable AMD minimum degree ordering routine. See amd.h for * documentation. */ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #include "amd_internal.h" /* ========================================================================= */ /* === AMD_order =========================================================== */ /* ========================================================================= */ GLOBAL Int AMD_order ( Int n, const Int Ap [ ], const Int Ai [ ], Int P [ ], double Control [ ], double Info [ ] ) { Int *Len, *S, nz, i, *Pinv, info, status, *Rp, *Ri, *Cp, *Ci, ok ; size_t nzaat, slen ; double mem = 0 ; #ifndef NDEBUG AMD_debug_init ("amd") ; #endif /* clear the Info array, if it exists */ info = Info != (double *) NULL ; if (info) { for (i = 0 ; i < AMD_INFO ; i++) { Info [i] = EMPTY ; } Info [AMD_N] = n ; Info [AMD_STATUS] = AMD_OK ; } /* make sure inputs exist and n is >= 0 */ if (Ai == (Int *) NULL || Ap == (Int *) NULL || P == (Int *) NULL || n < 0) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; /* arguments are invalid */ } if (n == 0) { return (AMD_OK) ; /* n is 0 so there's nothing to do */ } nz = Ap [n] ; if (info) { Info [AMD_NZ] = nz ; } if (nz < 0) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; } /* check if n or nz will cause size_t overflow */ if (((size_t) n) >= SIZE_T_MAX / sizeof (Int) || ((size_t) nz) >= SIZE_T_MAX / sizeof (Int)) { if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; /* problem too large */ } /* check the input matrix: AMD_OK, AMD_INVALID, or AMD_OK_BUT_JUMBLED */ status = AMD_valid (n, n, Ap, Ai) ; if (status == AMD_INVALID) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; /* matrix is invalid */ } /* allocate two size-n integer workspaces */ Len = amd_malloc (n * sizeof (Int)) ; Pinv = amd_malloc (n * sizeof (Int)) ; mem += n ; mem += n ; if (!Len || !Pinv) { /* :: out of memory :: */ amd_free (Len) ; amd_free (Pinv) ; if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } if (status == AMD_OK_BUT_JUMBLED) { /* sort the input matrix and remove duplicate entries */ AMD_DEBUG1 (("Matrix is jumbled\n")) ; Rp = amd_malloc ((n+1) * sizeof (Int)) ; Ri = amd_malloc (MAX (nz,1) * sizeof (Int)) ; mem += (n+1) ; mem += MAX (nz,1) ; if (!Rp || !Ri) { /* :: out of memory :: */ amd_free (Rp) ; amd_free (Ri) ; amd_free (Len) ; amd_free (Pinv) ; if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } /* use Len and Pinv as workspace to create R = A' */ AMD_preprocess (n, Ap, Ai, Rp, Ri, Len, Pinv) ; Cp = Rp ; Ci = Ri ; } else { /* order the input matrix as-is. No need to compute R = A' first */ Rp = NULL ; Ri = NULL ; Cp = (Int *) Ap ; Ci = (Int *) Ai ; } /* --------------------------------------------------------------------- */ /* determine the symmetry and count off-diagonal nonzeros in A+A' */ /* --------------------------------------------------------------------- */ nzaat = AMD_aat (n, Cp, Ci, Len, P, Info) ; AMD_DEBUG1 (("nzaat: %g\n", (double) nzaat)) ; ASSERT ((MAX (nz-n, 0) <= nzaat) && (nzaat <= 2 * (size_t) nz)) ; /* --------------------------------------------------------------------- */ /* allocate workspace for matrix, elbow room, and 6 size-n vectors */ /* --------------------------------------------------------------------- */ S = NULL ; slen = nzaat ; /* space for matrix */ ok = ((slen + nzaat/5) >= slen) ; /* check for size_t overflow */ slen += nzaat/5 ; /* add elbow room */ for (i = 0 ; ok && i < 7 ; i++) { ok = ((slen + n) > slen) ; /* check for size_t overflow */ slen += n ; /* size-n elbow room, 6 size-n work */ } mem += slen ; ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */ ok = ok && (slen < Int_MAX) ; /* S[i] for Int i must be OK */ if (ok) { S = amd_malloc (slen * sizeof (Int)) ; } AMD_DEBUG1 (("slen %g\n", (double) slen)) ; if (!S) { /* :: out of memory :: (or problem too large) */ amd_free (Rp) ; amd_free (Ri) ; amd_free (Len) ; amd_free (Pinv) ; if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } if (info) { /* memory usage, in bytes. */ Info [AMD_MEMORY] = mem * sizeof (Int) ; } /* --------------------------------------------------------------------- */ /* order the matrix */ /* --------------------------------------------------------------------- */ AMD_1 (n, Cp, Ci, P, Pinv, Len, slen, S, Control, Info) ; /* --------------------------------------------------------------------- */ /* free the workspace */ /* --------------------------------------------------------------------- */ amd_free (Rp) ; amd_free (Ri) ; amd_free (Len) ; amd_free (Pinv) ; amd_free (S) ; if (info) Info [AMD_STATUS] = status ; return (status) ; /* successful ordering */ } igraph/src/amd_dump.c0000644000176000001440000001400112325527072014276 0ustar ripleyusers/* ========================================================================= */ /* === AMD_dump ============================================================ */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* Debugging routines for AMD. Not used if NDEBUG is not defined at compile- * time (the default). See comments in amd_internal.h on how to enable * debugging. Not user-callable. */ #include "amd_internal.h" #ifndef NDEBUG /* This global variable is present only when debugging */ GLOBAL Int AMD_debug = -999 ; /* default is no debug printing */ /* ========================================================================= */ /* === AMD_debug_init ====================================================== */ /* ========================================================================= */ /* Sets the debug print level, by reading the file debug.amd (if it exists) */ GLOBAL void AMD_debug_init ( char *s ) { FILE *f ; f = fopen ("debug.amd", "r") ; if (f == (FILE *) NULL) { AMD_debug = -999 ; } else { fscanf (f, ID, &AMD_debug) ; fclose (f) ; } if (AMD_debug >= 0) { printf ("%s: AMD_debug_init, D= "ID"\n", s, AMD_debug) ; } } /* ========================================================================= */ /* === AMD_dump ============================================================ */ /* ========================================================================= */ /* Dump AMD's data structure, except for the hash buckets. This routine * cannot be called when the hash buckets are non-empty. */ GLOBAL void AMD_dump ( Int n, /* A is n-by-n */ Int Pe [ ], /* pe [0..n-1]: index in iw of start of row i */ Int Iw [ ], /* workspace of size iwlen, iwlen [0..pfree-1] * holds the matrix on input */ Int Len [ ], /* len [0..n-1]: length for row i */ Int iwlen, /* length of iw */ Int pfree, /* iw [pfree ... iwlen-1] is empty on input */ Int Nv [ ], /* nv [0..n-1] */ Int Next [ ], /* next [0..n-1] */ Int Last [ ], /* last [0..n-1] */ Int Head [ ], /* head [0..n-1] */ Int Elen [ ], /* size n */ Int Degree [ ], /* size n */ Int W [ ], /* size n */ Int nel ) { Int i, pe, elen, nv, len, e, p, k, j, deg, w, cnt, ilast ; if (AMD_debug < 0) return ; ASSERT (pfree <= iwlen) ; AMD_DEBUG3 (("\nAMD dump, pfree: "ID"\n", pfree)) ; for (i = 0 ; i < n ; i++) { pe = Pe [i] ; elen = Elen [i] ; nv = Nv [i] ; len = Len [i] ; w = W [i] ; if (elen >= EMPTY) { if (nv == 0) { AMD_DEBUG3 (("\nI "ID": nonprincipal: ", i)) ; ASSERT (elen == EMPTY) ; if (pe == EMPTY) { AMD_DEBUG3 ((" dense node\n")) ; ASSERT (w == 1) ; } else { ASSERT (pe < EMPTY) ; AMD_DEBUG3 ((" i "ID" -> parent "ID"\n", i, FLIP (Pe[i]))); } } else { AMD_DEBUG3 (("\nI "ID": active principal supervariable:\n",i)); AMD_DEBUG3 ((" nv(i): "ID" Flag: %d\n", nv, (nv < 0))) ; ASSERT (elen >= 0) ; ASSERT (nv > 0 && pe >= 0) ; p = pe ; AMD_DEBUG3 ((" e/s: ")) ; if (elen == 0) AMD_DEBUG3 ((" : ")) ; ASSERT (pe + len <= pfree) ; for (k = 0 ; k < len ; k++) { j = Iw [p] ; AMD_DEBUG3 ((" "ID"", j)) ; ASSERT (j >= 0 && j < n) ; if (k == elen-1) AMD_DEBUG3 ((" : ")) ; p++ ; } AMD_DEBUG3 (("\n")) ; } } else { e = i ; if (w == 0) { AMD_DEBUG3 (("\nE "ID": absorbed element: w "ID"\n", e, w)) ; ASSERT (nv > 0 && pe < 0) ; AMD_DEBUG3 ((" e "ID" -> parent "ID"\n", e, FLIP (Pe [e]))) ; } else { AMD_DEBUG3 (("\nE "ID": unabsorbed element: w "ID"\n", e, w)) ; ASSERT (nv > 0 && pe >= 0) ; p = pe ; AMD_DEBUG3 ((" : ")) ; ASSERT (pe + len <= pfree) ; for (k = 0 ; k < len ; k++) { j = Iw [p] ; AMD_DEBUG3 ((" "ID"", j)) ; ASSERT (j >= 0 && j < n) ; p++ ; } AMD_DEBUG3 (("\n")) ; } } } /* this routine cannot be called when the hash buckets are non-empty */ AMD_DEBUG3 (("\nDegree lists:\n")) ; if (nel >= 0) { cnt = 0 ; for (deg = 0 ; deg < n ; deg++) { if (Head [deg] == EMPTY) continue ; ilast = EMPTY ; AMD_DEBUG3 ((ID": \n", deg)) ; for (i = Head [deg] ; i != EMPTY ; i = Next [i]) { AMD_DEBUG3 ((" "ID" : next "ID" last "ID" deg "ID"\n", i, Next [i], Last [i], Degree [i])) ; ASSERT (i >= 0 && i < n && ilast == Last [i] && deg == Degree [i]) ; cnt += Nv [i] ; ilast = i ; } AMD_DEBUG3 (("\n")) ; } ASSERT (cnt == n - nel) ; } } #endif igraph/src/ivout.f0000644000176000001440000000722112325527073013670 0ustar ripleyusersC----------------------------------------------------------------------- C Routine: IVOUT C C Purpose: Integer vector output routine. C C Usage: CALL IVOUT (LOUT, N, IX, IDIGIT, IFMT) C C Arguments C N - Length of array IX. (Input) C IX - Integer array to be printed. (Input) C IFMT - Format to be used in printing array IX. (Input) C IDIGIT - Print up to ABS(IDIGIT) decimal digits / number. (Input) C If IDIGIT .LT. 0, printing is done with 72 columns. C If IDIGIT .GT. 0, printing is done with 132 columns. C C----------------------------------------------------------------------- C SUBROUTINE IGRAPHIVOUT (LOUT, N, IX, IDIGIT, IFMT) C ... C ... SPECIFICATIONS FOR ARGUMENTS INTEGER IX(*), N, IDIGIT, LOUT CHARACTER IFMT*(*) C ... C ... SPECIFICATIONS FOR LOCAL VARIABLES INTEGER I, NDIGIT, K1, K2, LLL CHARACTER*80 LINE * ... * ... SPECIFICATIONS INTRINSICS INTRINSIC MIN * C c$$$ LLL = MIN ( LEN ( IFMT ), 80 ) c$$$ DO 1 I = 1, LLL c$$$ LINE(I:I) = '-' c$$$ 1 CONTINUE c$$$C c$$$ DO 2 I = LLL+1, 80 c$$$ LINE(I:I) = ' ' c$$$ 2 CONTINUE c$$$C c$$$ WRITE ( LOUT, 2000 ) IFMT, LINE(1:LLL) c$$$ 2000 FORMAT ( /1X, A /1X, A ) c$$$C c$$$ IF (N .LE. 0) RETURN c$$$ NDIGIT = IDIGIT c$$$ IF (IDIGIT .EQ. 0) NDIGIT = 4 c$$$C c$$$C======================================================================= c$$$C CODE FOR OUTPUT USING 72 COLUMNS FORMAT c$$$C======================================================================= c$$$C c$$$ IF (IDIGIT .LT. 0) THEN c$$$C c$$$ NDIGIT = -IDIGIT c$$$ IF (NDIGIT .LE. 4) THEN c$$$ DO 10 K1 = 1, N, 10 c$$$ K2 = MIN0(N,K1+9) c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) c$$$ 10 CONTINUE c$$$C c$$$ ELSE IF (NDIGIT .LE. 6) THEN c$$$ DO 30 K1 = 1, N, 7 c$$$ K2 = MIN0(N,K1+6) c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) c$$$ 30 CONTINUE c$$$C c$$$ ELSE IF (NDIGIT .LE. 10) THEN c$$$ DO 50 K1 = 1, N, 5 c$$$ K2 = MIN0(N,K1+4) c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) c$$$ 50 CONTINUE c$$$C c$$$ ELSE c$$$ DO 70 K1 = 1, N, 3 c$$$ K2 = MIN0(N,K1+2) c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) c$$$ 70 CONTINUE c$$$ END IF c$$$C c$$$C======================================================================= c$$$C CODE FOR OUTPUT USING 132 COLUMNS FORMAT c$$$C======================================================================= c$$$C c$$$ ELSE c$$$C c$$$ IF (NDIGIT .LE. 4) THEN c$$$ DO 90 K1 = 1, N, 20 c$$$ K2 = MIN0(N,K1+19) c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) c$$$ 90 CONTINUE c$$$C c$$$ ELSE IF (NDIGIT .LE. 6) THEN c$$$ DO 110 K1 = 1, N, 15 c$$$ K2 = MIN0(N,K1+14) c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) c$$$ 110 CONTINUE c$$$C c$$$ ELSE IF (NDIGIT .LE. 10) THEN c$$$ DO 130 K1 = 1, N, 10 c$$$ K2 = MIN0(N,K1+9) c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) c$$$ 130 CONTINUE c$$$C c$$$ ELSE c$$$ DO 150 K1 = 1, N, 7 c$$$ K2 = MIN0(N,K1+6) c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) c$$$ 150 CONTINUE c$$$ END IF c$$$ END IF c$$$ WRITE (LOUT,1004) c$$$C c$$$ 1000 FORMAT(1X,I4,' - ',I4,':',20(1X,I5)) c$$$ 1001 FORMAT(1X,I4,' - ',I4,':',15(1X,I7)) c$$$ 1002 FORMAT(1X,I4,' - ',I4,':',10(1X,I11)) c$$$ 1003 FORMAT(1X,I4,' - ',I4,':',7(1X,I15)) c$$$ 1004 FORMAT(1X,' ') c$$$C RETURN END igraph/src/cs_usolve.c0000644000176000001440000000261712325527073014525 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* solve Ux=b where x and b are dense. x=b on input, solution on output. */ CS_INT cs_usolve (const cs *U, CS_ENTRY *x) { CS_INT p, j, n, *Up, *Ui ; CS_ENTRY *Ux ; if (!CS_CSC (U) || !x) return (0) ; /* check inputs */ n = U->n ; Up = U->p ; Ui = U->i ; Ux = U->x ; for (j = n-1 ; j >= 0 ; j--) { x [j] /= Ux [Up [j+1]-1] ; for (p = Up [j] ; p < Up [j+1]-1 ; p++) { x [Ui [p]] -= Ux [p] * x [j] ; } } return (1) ; } igraph/src/matching.c0000644000176000001440000011033612325527073014313 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "config.h" #include "igraph_adjlist.h" #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_dqueue.h" #include "igraph_flow.h" #include "igraph_interface.h" #include "igraph_matching.h" #include "igraph_structural.h" /* #define MATCHING_DEBUG */ #ifdef _MSC_VER /* MSVC does not support variadic macros */ #include static void debug(const char* fmt, ...) { va_list args; va_start(args, fmt); #ifdef MATCHING_DEBUG vfprintf(stderr, fmt, args); #endif va_end(args); } #else # ifdef MATCHING_DEBUG # define debug(...) fprintf(stderr, __VA_ARGS__) # else # define debug(...) # endif #endif /** * \function igraph_is_matching * Checks whether the given matching is valid for the given graph. * * This function checks a matching vector and verifies whether its length * matches the number of vertices in the given graph, its values are between * -1 (inclusive) and the number of vertices (exclusive), and whether there * exists a corresponding edge in the graph for every matched vertex pair. * For bipartite graphs, it also verifies whether the matched vertices are * in different parts of the graph. * * \param graph The input graph. It can be directed but the edge directions * will be ignored. * \param types If the graph is bipartite and you are interested in bipartite * matchings only, pass the vertex types here. If the graph is * non-bipartite, simply pass \c NULL. * \param matching The matching itself. It must be a vector where element i * contains the ID of the vertex that vertex i is matched to, * or -1 if vertex i is unmatched. * \param result Pointer to a boolean variable, the result will be returned * here. * * \sa \ref igraph_is_maximal_matching() if you are also interested in whether * the matching is maximal (i.e. non-extendable). * * Time complexity: O(|V|+|E|) where |V| is the number of vertices and * |E| is the number of edges. * * \example examples/simple/igraph_maximum_bipartite_matching.c */ int igraph_is_matching(const igraph_t* graph, const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, igraph_bool_t* result) { long int i, j, no_of_nodes = igraph_vcount(graph); igraph_bool_t conn; /* Checking match vector length */ if (igraph_vector_long_size(matching) != no_of_nodes) { *result = 0; return IGRAPH_SUCCESS; } for (i = 0; i < no_of_nodes; i++) { j = VECTOR(*matching)[i]; /* Checking range of each element in the match vector */ if (j < -1 || j >= no_of_nodes) { *result = 0; return IGRAPH_SUCCESS; } /* When i is unmatched, we're done */ if (j == -1) continue; /* Matches must be mutual */ if (VECTOR(*matching)[j] != i) { *result = 0; return IGRAPH_SUCCESS; } /* Matched vertices must be connected */ IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) i, (igraph_integer_t) j, &conn)); if (!conn) { /* Try the other direction -- for directed graphs */ IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) j, (igraph_integer_t) i, &conn)); if (!conn) { *result = 0; return IGRAPH_SUCCESS; } } } if (types != 0) { /* Matched vertices must be of different types */ for (i = 0; i < no_of_nodes; i++) { j = VECTOR(*matching)[i]; if (j == -1) continue; if (VECTOR(*types)[i] == VECTOR(*types)[j]) { *result = 0; return IGRAPH_SUCCESS; } } } *result = 1; return IGRAPH_SUCCESS; } /** * \function igraph_is_maximal_matching * Checks whether a matching in a graph is maximal. * * A matching is maximal if and only if there exists no unmatched vertex in a * graph such that one of its neighbors is also unmatched. * * \param graph The input graph. It can be directed but the edge directions * will be ignored. * \param types If the graph is bipartite and you are interested in bipartite * matchings only, pass the vertex types here. If the graph is * non-bipartite, simply pass \c NULL. * \param matching The matching itself. It must be a vector where element i * contains the ID of the vertex that vertex i is matched to, * or -1 if vertex i is unmatched. * \param result Pointer to a boolean variable, the result will be returned * here. * * \sa \ref igraph_is_matching() if you are only interested in whether a * matching vector is valid for a given graph. * * Time complexity: O(|V|+|E|) where |V| is the number of vertices and * |E| is the number of edges. * * \example examples/simple/igraph_maximum_bipartite_matching.c */ int igraph_is_maximal_matching(const igraph_t* graph, const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, igraph_bool_t* result) { long int i, j, n, no_of_nodes = igraph_vcount(graph); igraph_vector_t neis; igraph_bool_t valid; IGRAPH_CHECK(igraph_is_matching(graph, types, matching, &valid)); if (!valid) { *result = 0; return IGRAPH_SUCCESS; } IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); valid = 1; for (i = 0; i < no_of_nodes; i++) { j = VECTOR(*matching)[i]; if (j != -1) continue; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_ALL)); n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { if (VECTOR(*matching)[(long int)VECTOR(neis)[j]] == -1) { if (types == 0 || VECTOR(*types)[i] != VECTOR(*types)[(long int)VECTOR(neis)[j]]) { valid = 0; break; } } } } igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); *result = valid; return IGRAPH_SUCCESS; } int igraph_i_maximum_bipartite_matching_unweighted(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_integer_t* matching_size, igraph_vector_long_t* matching); int igraph_i_maximum_bipartite_matching_weighted(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_integer_t* matching_size, igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights, igraph_real_t eps); #define MATCHED(v) (VECTOR(match)[v] != -1) #define UNMATCHED(v) (!MATCHED(v)) /** * \function igraph_maximum_bipartite_matching * Calculates a maximum matching in a bipartite graph. * * A matching in a bipartite graph is a partial assignment of vertices * of the first kind to vertices of the second kind such that each vertex of * the first kind is matched to at most one vertex of the second kind and * vice versa, and matched vertices must be connected by an edge in the graph. * The size (or cardinality) of a matching is the number of edges. * A matching is a maximum matching if there exists no other matching with * larger cardinality. For weighted graphs, a maximum matching is a matching * whose edges have the largest possible total weight among all possible * matchings. * * * Maximum matchings in bipartite graphs are found by the push-relabel algorithm * with greedy initialization and a global relabeling after every n/2 steps where * n is the number of vertices in the graph. * * * References: Cherkassky BV, Goldberg AV, Martin P, Setubal JC and Stolfi J: * Augment or push: A computational study of bipartite matching and * unit-capacity flow algorithms. ACM Journal of Experimental Algorithmics 3, * 1998. * * * Kaya K, Langguth J, Manne F and Ucar B: Experiments on push-relabel-based * maximum cardinality matching algorithms for bipartite graphs. Technical * Report TR/PA/11/33 of the Centre Europeen de Recherche et de Formation * Avancee en Calcul Scientifique, 2011. * * \param graph The input graph. It can be directed but the edge directions * will be ignored. * \param types Boolean vector giving the vertex types of the graph. * \param matching_size The size of the matching (i.e. the number of matched * vertex pairs will be returned here). It may be \c NULL * if you don't need this. * \param matching_weight The weight of the matching if the edges are weighted, * or the size of the matching again if the edges are * unweighted. It may be \c NULL if you don't need this. * \param matching The matching itself. It must be a vector where element i * contains the ID of the vertex that vertex i is matched to, * or -1 if vertex i is unmatched. * \param weights A null pointer (=no edge weights), or a vector giving the * weights of the edges. Note that the algorithm is stable * only for integer weights. * \param eps A small real number used in equality tests in the weighted * bipartite matching algorithm. Two real numbers are considered * equal in the algorithm if their difference is smaller than * \c eps. This is required to avoid the accumulation of numerical * errors. It is advised to pass a value derived from the * \c DBL_EPSILON constant in \c float.h here. If you are * running the algorithm with no \c weights vector, this argument * is ignored. * \return Error code. * * Time complexity: O(sqrt(|V|) |E|) for unweighted graphs (according to the * technical report referenced above), O(|V||E|) for weighted graphs. * * \example examples/simple/igraph_maximum_bipartite_matching.c */ int igraph_maximum_bipartite_matching(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_integer_t* matching_size, igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights, igraph_real_t eps) { /* Sanity checks */ if (igraph_vector_bool_size(types) < igraph_vcount(graph)) { IGRAPH_ERROR("types vector too short", IGRAPH_EINVAL); } if (weights && igraph_vector_size(weights) < igraph_ecount(graph)) { IGRAPH_ERROR("weights vector too short", IGRAPH_EINVAL); } if (weights == 0) { IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted(graph, types, matching_size, matching)); if (matching_weight != 0) { *matching_weight = *matching_size; } return IGRAPH_SUCCESS; } else { return igraph_i_maximum_bipartite_matching_weighted(graph, types, matching_size, matching_weight, matching, weights, eps); } } int igraph_i_maximum_bipartite_matching_unweighted_relabel(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_vector_t* labels, igraph_vector_long_t* matching, igraph_bool_t smaller_set); /** * Finding maximum bipartite matchings on bipartite graphs using the * push-relabel algorithm. * * The implementation follows the pseudocode in Algorithm 1 of the * following paper: * * Kaya K, Langguth J, Manne F and Ucar B: Experiments on push-relabel-based * maximum cardinality matching algorithms for bipartite graphs. Technical * Report TR/PA/11/33 of CERFACS (Centre Européen de Recherche et de Formation * Avancée en Calcul Scientifique). * http://www.cerfacs.fr/algor/reports/2011/TR_PA_11_33.pdf */ int igraph_i_maximum_bipartite_matching_unweighted(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_integer_t* matching_size, igraph_vector_long_t* matching) { long int i, j, k, n, no_of_nodes = igraph_vcount(graph); long int num_matched; /* number of matched vertex pairs */ igraph_vector_long_t match; /* will store the matching */ igraph_vector_t labels; /* will store the labels */ igraph_vector_t neis; /* used to retrieve the neighbors of a node */ igraph_dqueue_long_t q; /* a FIFO for push ordering */ igraph_bool_t smaller_set; /* denotes which part of the bipartite graph is smaller */ long int label_changed = 0; /* Counter to decide when to run a global relabeling */ long int relabeling_freq = no_of_nodes / 2; /* We will use: * - FIFO push ordering * - global relabeling frequency: n/2 steps where n is the number of nodes * - simple greedy matching for initialization */ /* (1) Initialize data structures */ IGRAPH_CHECK(igraph_vector_long_init(&match, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &match); IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0)); IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); /* (2) Initially, every node is unmatched */ igraph_vector_long_fill(&match, -1); /* (3) Find an initial matching in a greedy manner. * At the same time, find which side of the graph is smaller. */ num_matched = 0; j = 0; for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i]) j++; if (MATCHED(i)) continue; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_ALL)); n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { k = (long int) VECTOR(neis)[j]; if (UNMATCHED(k)) { /* We match vertex i to vertex VECTOR(neis)[j] */ VECTOR(match)[k] = i; VECTOR(match)[i] = k; num_matched++; break; } } } smaller_set = (j <= no_of_nodes/2); /* (4) Set the initial labeling -- lines 1 and 2 in the tech report */ IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted_relabel( graph, types, &labels, &match, smaller_set)); /* (5) Fill the push queue with the unmatched nodes from the smaller set. */ for (i = 0; i < no_of_nodes; i++) { if (UNMATCHED(i) && VECTOR(*types)[i] == smaller_set) IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); } /* (6) Main loop from the referenced tech report -- lines 4--13 */ label_changed = 0; while (!igraph_dqueue_long_empty(&q)) { long int v = igraph_dqueue_long_pop(&q); /* Line 13 */ long int u = -1, label_u = 2 * no_of_nodes; long int w; if (label_changed >= relabeling_freq) { /* Run global relabeling */ IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted_relabel( graph, types, &labels, &match, smaller_set)); label_changed = 0; } debug("Considering vertex %ld\n", v); /* Line 5: find row u among the neighbors of v s.t. label(u) is minimal */ IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, IGRAPH_ALL)); n = igraph_vector_size(&neis); for (i = 0; i < n; i++) { if (VECTOR(labels)[(long int)VECTOR(neis)[i]] < label_u) { u = (long int) VECTOR(neis)[i]; label_u = (long int) VECTOR(labels)[u]; label_changed++; } } debug(" Neighbor with smallest label: %ld (label=%ld)\n", u, label_u); if (label_u < no_of_nodes) { /* Line 6 */ VECTOR(labels)[v] = VECTOR(labels)[u] + 1; /* Line 7 */ if (MATCHED(u)) { /* Line 8 */ w = VECTOR(match)[u]; debug(" Vertex %ld is matched to %ld, performing a double push\n", u, w); if (w != v) { VECTOR(match)[u] = -1; VECTOR(match)[w] = -1; /* Line 9 */ IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); /* Line 10 */ debug(" Unmatching & activating vertex %ld\n", w); num_matched--; } } VECTOR(match)[u] = v; VECTOR(match)[v] = u; /* Line 11 */ num_matched++; VECTOR(labels)[u] += 2; /* Line 12 */ label_changed++; } } /* Fill the output parameters */ if (matching != 0) { IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); } if (matching_size != 0) { *matching_size = (igraph_integer_t) num_matched; } /* Release everything */ igraph_dqueue_long_destroy(&q); igraph_vector_destroy(&neis); igraph_vector_destroy(&labels); igraph_vector_long_destroy(&match); IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; } int igraph_i_maximum_bipartite_matching_unweighted_relabel(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_vector_t* labels, igraph_vector_long_t* match, igraph_bool_t smaller_set) { long int i, j, n, no_of_nodes = igraph_vcount(graph), matched_to; igraph_dqueue_long_t q; igraph_vector_t neis; debug("Running global relabeling.\n"); /* Set all the labels to no_of_nodes first */ igraph_vector_fill(labels, no_of_nodes); /* Allocate vector for neighbors */ IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); /* Create a FIFO for the BFS and initialize it with the unmatched rows * (i.e. members of the larger set) */ IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0)); IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i] != smaller_set && VECTOR(*match)[i] == -1) { IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); VECTOR(*labels)[i] = 0; } } /* Run the BFS */ while (!igraph_dqueue_long_empty(&q)) { long int v = igraph_dqueue_long_pop(&q); long int w; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, IGRAPH_ALL)); n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { w = (long int) VECTOR(neis)[j]; if (VECTOR(*labels)[w] == no_of_nodes) { VECTOR(*labels)[w] = VECTOR(*labels)[v] + 1; matched_to = VECTOR(*match)[w]; if (matched_to != -1 && VECTOR(*labels)[matched_to] == no_of_nodes) { IGRAPH_CHECK(igraph_dqueue_long_push(&q, matched_to)); VECTOR(*labels)[matched_to] = VECTOR(*labels)[w] + 1; } } } } igraph_dqueue_long_destroy(&q); igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } /** * Finding maximum bipartite matchings on bipartite graphs using the * Hungarian algorithm (a.k.a. Kuhn-Munkres algorithm). * * The algorithm uses a maximum cardinality matching on a subset of * tight edges as a starting point. This is achieved by * \c igraph_i_maximum_bipartite_matching_unweighted on the restricted * graph. * * The algorithm works reliably only if the weights are integers. The * \c eps parameter should specity a very small number; if the slack on * an edge falls below \c eps, it will be considered tight. If all your * weights are integers, you can safely set \c eps to zero. */ int igraph_i_maximum_bipartite_matching_weighted(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_integer_t* matching_size, igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights, igraph_real_t eps) { long int i, j, k, n, no_of_nodes, no_of_edges; igraph_integer_t u, v, w, msize; igraph_t newgraph; igraph_vector_long_t match; /* will store the matching */ igraph_vector_t slack; /* will store the slack on each edge */ igraph_vector_t parent; /* parent vertices during a BFS */ igraph_vector_t vec1, vec2; /* general temporary vectors */ igraph_vector_t labels; /* will store the labels */ igraph_dqueue_long_t q; /* a FIFO for BST */ igraph_bool_t smaller_set; /* denotes which part of the bipartite graph is smaller */ long int smaller_set_size; /* size of the smaller set */ igraph_real_t dual; /* solution of the dual problem */ igraph_adjlist_t tight_phantom_edges; /* adjacency list to manage tight phantom edges */ igraph_integer_t alternating_path_endpoint; igraph_vector_t* neis; igraph_vector_int_t *neis2; igraph_inclist_t inclist; /* incidence list of the original graph */ /* The Hungarian algorithm is originally for complete bipartite graphs. * For non-complete bipartite graphs, a phantom edge of weight zero must be * added between every pair of non-connected vertices. We don't do this * explicitly of course. See the comments below about how phantom edges * are taken into account. */ no_of_nodes = igraph_vcount(graph); no_of_edges = igraph_ecount(graph); if (eps < 0) { IGRAPH_WARNING("negative epsilon given, clamping to zero"); eps = 0; } /* (1) Initialize data structures */ IGRAPH_CHECK(igraph_vector_long_init(&match, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &match); IGRAPH_CHECK(igraph_vector_init(&slack, no_of_edges)); IGRAPH_FINALLY(igraph_vector_destroy, &slack); IGRAPH_VECTOR_INIT_FINALLY(&vec1, 0); IGRAPH_VECTOR_INIT_FINALLY(&vec2, 0); IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes); IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0)); IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); IGRAPH_VECTOR_INIT_FINALLY(&parent, no_of_nodes); IGRAPH_CHECK(igraph_adjlist_init_empty(&tight_phantom_edges, (igraph_integer_t) no_of_nodes)); IGRAPH_FINALLY(igraph_adjlist_destroy, &tight_phantom_edges); IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); /* (2) Find which set is the smaller one */ j = 0; for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i] == 0) j++; } smaller_set = (j > no_of_nodes / 2); smaller_set_size = smaller_set ? (no_of_nodes - j) : j; /* (3) Calculate the initial labeling and the set of tight edges. Use the * smaller set only. Here we can assume that there are no phantom edges * among the tight ones. */ dual = 0; for (i = 0; i < no_of_nodes; i++) { igraph_real_t max_weight = 0; if (VECTOR(*types)[i] != smaller_set) { VECTOR(labels)[i] = 0; continue; } neis = igraph_inclist_get(&inclist, i); n = igraph_vector_size(neis); for (j = 0, k = 0; j < n; j++) { if (VECTOR(*weights)[(long int)VECTOR(*neis)[j]] > max_weight) { k = (long int) VECTOR(*neis)[j]; max_weight = VECTOR(*weights)[k]; } } VECTOR(labels)[i] = max_weight; dual += max_weight; } igraph_vector_clear(&vec1); IGRAPH_CHECK(igraph_get_edgelist(graph, &vec2, 0)); #define IS_TIGHT(i) (VECTOR(slack)[i] <= eps) for (i = 0, j = 0; i < no_of_edges; i++, j+=2) { u = (igraph_integer_t) VECTOR(vec2)[j]; v = (igraph_integer_t) VECTOR(vec2)[j+1]; VECTOR(slack)[i] = VECTOR(labels)[u] + VECTOR(labels)[v] - VECTOR(*weights)[i]; if (IS_TIGHT(i)) { IGRAPH_CHECK(igraph_vector_push_back(&vec1, u)); IGRAPH_CHECK(igraph_vector_push_back(&vec1, v)); } } igraph_vector_clear(&vec2); /* (4) Construct a temporary graph on which the initial maximum matching * will be calculated (only on the subset of tight edges) */ IGRAPH_CHECK(igraph_create(&newgraph, &vec1, (igraph_integer_t) no_of_nodes, 0)); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_CHECK(igraph_maximum_bipartite_matching(&newgraph, types, &msize, 0, &match, 0, 0)); igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); /* (5) Main loop until the matching becomes maximal */ while (msize < smaller_set_size) { igraph_real_t min_slack, min_slack_2; igraph_integer_t min_slack_u, min_slack_v; /* (7) Fill the push queue with the unmatched nodes from the smaller set. */ igraph_vector_clear(&vec1); igraph_vector_clear(&vec2); igraph_vector_fill(&parent, -1); for (i = 0; i < no_of_nodes; i++) { if (UNMATCHED(i) && VECTOR(*types)[i] == smaller_set) { IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); VECTOR(parent)[i] = i; IGRAPH_CHECK(igraph_vector_push_back(&vec1, i)); } } #ifdef MATCHING_DEBUG debug("Matching:"); igraph_vector_long_print(&match); debug("Unmatched vertices are marked by non-negative numbers:\n"); igraph_vector_print(&parent); debug("Labeling:"); igraph_vector_print(&labels); debug("Slacks:"); igraph_vector_print(&slack); #endif /* (8) Run the BFS */ alternating_path_endpoint = -1; while (!igraph_dqueue_long_empty(&q)) { v = (int) igraph_dqueue_long_pop(&q); debug("Considering vertex %ld\n", (long int)v); /* v is always in the smaller set. Find the neighbors of v, which * are all in the larger set. Find the pairs of these nodes in * the smaller set and push them to the queue. Mark the traversed * nodes as seen. * * Here we have to be careful as there are two types of incident * edges on v: real edges and phantom ones. Real edges are * given by igraph_inclist_get. Phantom edges are not given so we * (ab)use an adjacency list data structure that lists the * vertices connected to v by phantom edges only. */ neis = igraph_inclist_get(&inclist, v); n = igraph_vector_size(neis); for (i = 0; i < n; i++) { j = (long int) VECTOR(*neis)[i]; /* We only care about tight edges */ if (!IS_TIGHT(j)) continue; /* Have we seen the other endpoint already? */ u = IGRAPH_OTHER(graph, j, v); if (VECTOR(parent)[u] >= 0) continue; debug(" Reached vertex %ld via edge %ld\n", (long)u, (long)j); VECTOR(parent)[u] = v; IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); w = (int) VECTOR(match)[u]; if (w == -1) { /* u is unmatched and it is in the larger set. Therefore, we * could improve the matching by following the parents back * from u to the root. */ alternating_path_endpoint = u; break; /* since we don't need any more endpoints that come from v */ } else { IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); VECTOR(parent)[w] = u; } IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); } /* Now do the same with the phantom edges */ neis2 = igraph_adjlist_get(&tight_phantom_edges, v); n = igraph_vector_int_size(neis2); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(*neis2)[i]; /* Have we seen u already? */ if (VECTOR(parent)[u] >= 0) continue; /* Check if the edge is really tight; it might have happened that the * edge became non-tight in the meanwhile. We do not remove these from * tight_phantom_edges at the moment, so we check them once again here. */ if (fabs(VECTOR(labels)[(long int)v] + VECTOR(labels)[(long int)u]) > eps) continue; debug(" Reached vertex %ld via tight phantom edge\n", (long)u); VECTOR(parent)[u] = v; IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); w = (int) VECTOR(match)[u]; if (w == -1) { /* u is unmatched and it is in the larger set. Therefore, we * could improve the matching by following the parents back * from u to the root. */ alternating_path_endpoint = u; break; /* since we don't need any more endpoints that come from v */ } else { IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); VECTOR(parent)[w] = u; } IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); } } /* Okay; did we have an alternating path? */ if (alternating_path_endpoint != -1) { #ifdef MATCHING_DEBUG debug("BFS parent tree:"); igraph_vector_print(&parent); #endif /* Increase the size of the matching with the alternating path. */ v = alternating_path_endpoint; u = (igraph_integer_t) VECTOR(parent)[v]; debug("Extending matching with alternating path ending in %ld.\n", (long int)v); while (u != v) { w = (int) VECTOR(match)[v]; if (w != -1) VECTOR(match)[w] = -1; VECTOR(match)[v] = u; VECTOR(match)[v] = u; w = (int) VECTOR(match)[u]; if (w != -1) VECTOR(match)[w] = -1; VECTOR(match)[u] = v; v = (igraph_integer_t) VECTOR(parent)[u]; u = (igraph_integer_t) VECTOR(parent)[v]; } msize++; #ifdef MATCHING_DEBUG debug("New matching after update:"); igraph_vector_long_print(&match); debug("Matching size is now: %ld\n", (long)msize); #endif continue; } #ifdef MATCHING_DEBUG debug("Vertices reachable from unmatched ones via tight edges:\n"); igraph_vector_print(&vec1); igraph_vector_print(&vec2); #endif /* At this point, vec1 contains the nodes in the smaller set (A) * reachable from unmatched nodes in A via tight edges only, while vec2 * contains the nodes in the larger set (B) reachable from unmatched * nodes in A via tight edges only. Also, parent[i] >= 0 if node i * is reachable */ /* Check the edges between reachable nodes in A and unreachable * nodes in B, and find the minimum slack on them. * * Since the weights are positive, we do no harm if we first * assume that there are no "real" edges between the two sets * mentioned above and determine an upper bound for min_slack * based on this. */ min_slack = IGRAPH_INFINITY; min_slack_u = min_slack_v = 0; n = igraph_vector_size(&vec1); for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i] == smaller_set) continue; if (VECTOR(labels)[i] < min_slack) { min_slack = VECTOR(labels)[i]; min_slack_v = (igraph_integer_t) i; } } min_slack_2 = IGRAPH_INFINITY; for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec1)[i]; /* u is surely from the smaller set, but we are interested in it * only if it is reachable from an unmatched vertex */ if (VECTOR(parent)[u] < 0) continue; if (VECTOR(labels)[u] < min_slack_2) { min_slack_2 = VECTOR(labels)[u]; min_slack_u = u; } } min_slack += min_slack_2; debug("Starting approximation for min_slack = %.4f (based on vertex pair %ld--%ld)\n", min_slack, (long int)min_slack_u, (long int)min_slack_v); n = igraph_vector_size(&vec1); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec1)[i]; /* u is a reachable node in A; get its incident edges. * * There are two types of incident edges: 1) real edges, * 2) phantom edges. Phantom edges were treated earlier * when we determined the initial value for min_slack. */ debug("Trying to expand along vertex %ld\n", (long int)u); neis = igraph_inclist_get(&inclist, u); k = igraph_vector_size(neis); for (j = 0; j < k; j++) { /* v is the vertex sitting at the other end of an edge incident * on u; check whether it was reached */ v = IGRAPH_OTHER(graph, VECTOR(*neis)[j], u); debug(" Edge %ld -- %ld (ID=%ld)\n", (long int)u, (long int)v, (long int)VECTOR(*neis)[j]); if (VECTOR(parent)[v] >= 0) { /* v was reached, so we are not interested in it */ debug(" %ld was reached, so we are not interested in it\n", (long int)v); continue; } /* v is the ID of the edge from now on */ v = (igraph_integer_t) VECTOR(*neis)[j]; if (VECTOR(slack)[v] < min_slack) { min_slack = VECTOR(slack)[v]; min_slack_u = u; min_slack_v = IGRAPH_OTHER(graph, v, u); } debug(" Slack of this edge: %.4f, min slack is now: %.4f\n", VECTOR(slack)[v], min_slack); } } debug("Minimum slack: %.4f on edge %d--%d\n", min_slack, (int)min_slack_u, (int)min_slack_v); if (min_slack > 0) { /* Decrease the label of reachable nodes in A by min_slack. * Also update the dual solution */ n = igraph_vector_size(&vec1); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec1)[i]; VECTOR(labels)[u] -= min_slack; neis = igraph_inclist_get(&inclist, u); k = igraph_vector_size(neis); for (j = 0; j < k; j++) { debug(" Decreasing slack of edge %ld (%ld--%ld) by %.4f\n", (long)VECTOR(*neis)[j], (long)u, (long)IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); VECTOR(slack)[(long int)VECTOR(*neis)[j]] -= min_slack; } dual -= min_slack; } /* Increase the label of reachable nodes in B by min_slack. * Also update the dual solution */ n = igraph_vector_size(&vec2); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec2)[i]; VECTOR(labels)[u] += min_slack; neis = igraph_inclist_get(&inclist, u); k = igraph_vector_size(neis); for (j = 0; j < k; j++) { debug(" Increasing slack of edge %ld (%ld--%ld) by %.4f\n", (long)VECTOR(*neis)[j], (long)u, (long)IGRAPH_OTHER(graph, (long)VECTOR(*neis)[j], u), min_slack); VECTOR(slack)[(long int)VECTOR(*neis)[j]] += min_slack; } dual += min_slack; } } /* Update the set of tight phantom edges. * Note that we must do it even if min_slack is zero; the reason is that * it can happen that min_slack is zero in the first step if there are * isolated nodes in the input graph. * * TODO: this is O(n^2) here. Can we do it faster? */ for (u = 0; u < no_of_nodes; u++) { if (VECTOR(*types)[u] != smaller_set) continue; for (v = 0; v < no_of_nodes; v++) { if (VECTOR(*types)[v] == smaller_set) continue; if (VECTOR(labels)[(long int)u] + VECTOR(labels)[(long int)v] <= eps) { /* Tight phantom edge found. Note that we don't have to check whether * u and v are connected; if they were, then the slack of this edge * would be negative. */ neis2 = igraph_adjlist_get(&tight_phantom_edges, u); if (!igraph_vector_int_binsearch(neis2, v, &i)) { debug("New tight phantom edge: %ld -- %ld\n", (long)u, (long)v); IGRAPH_CHECK(igraph_vector_int_insert(neis2, i, v)); } } } } #ifdef MATCHING_DEBUG debug("New labels:"); igraph_vector_print(&labels); debug("Slacks after updating with min_slack:"); igraph_vector_print(&slack); #endif } /* Cleanup: remove phantom edges from the matching */ for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i] != smaller_set) continue; if (VECTOR(match)[i] != -1) { j = VECTOR(match)[i]; neis2 = igraph_adjlist_get(&tight_phantom_edges, i); if (igraph_vector_int_binsearch(neis2, j, 0)) { VECTOR(match)[i] = VECTOR(match)[j] = -1; msize--; } } } /* Fill the output parameters */ if (matching != 0) { IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); } if (matching_size != 0) { *matching_size = msize; } if (matching_weight != 0) { *matching_weight = 0; for (i = 0; i < no_of_edges; i++) { if (IS_TIGHT(i)) { IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) i, &u, &v)); if (VECTOR(match)[u] == v) *matching_weight += VECTOR(*weights)[i]; } } } /* Release everything */ #undef IS_TIGHT igraph_inclist_destroy(&inclist); igraph_adjlist_destroy(&tight_phantom_edges); igraph_vector_destroy(&parent); igraph_dqueue_long_destroy(&q); igraph_vector_destroy(&labels); igraph_vector_destroy(&vec1); igraph_vector_destroy(&vec2); igraph_vector_destroy(&slack); igraph_vector_long_destroy(&match); IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; } int igraph_maximum_matching(const igraph_t* graph, igraph_integer_t* matching_size, igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights) { IGRAPH_UNUSED(graph); IGRAPH_UNUSED(matching_size); IGRAPH_UNUSED(matching_weight); IGRAPH_UNUSED(matching); IGRAPH_UNUSED(weights); IGRAPH_ERROR("maximum matching on general graphs not implemented yet", IGRAPH_UNIMPLEMENTED); } #ifdef MATCHING_DEBUG #undef MATCHING_DEBUG #endif igraph/src/st-cuts.c0000644000176000001440000014022612325527074014125 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_flow.h" #include "igraph_flow_internal.h" #include "igraph_error.h" #include "igraph_memory.h" #include "igraph_constants.h" #include "igraph_interface.h" #include "igraph_adjlist.h" #include "igraph_conversion.h" #include "igraph_constructors.h" #include "igraph_structural.h" #include "igraph_components.h" #include "igraph_types_internal.h" #include "config.h" #include "igraph_math.h" #include "igraph_dqueue.h" #include "igraph_visitor.h" #include "igraph_marked_queue.h" #include "igraph_stack.h" #include "igraph_estack.h" /* * \function igraph_even_tarjan_reduction * Even-Tarjan reduction of a graph * * \example examples/simple/even_tarjan.c */ int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, igraph_vector_t *capacity) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); long int new_no_of_nodes=no_of_nodes*2; long int new_no_of_edges=no_of_nodes + no_of_edges * 2; igraph_vector_t edges; long int edgeptr=0, capptr=0; long int i; IGRAPH_VECTOR_INIT_FINALLY(&edges, new_no_of_edges * 2); if (capacity) { IGRAPH_CHECK(igraph_vector_resize(capacity, new_no_of_edges)); } /* Every vertex 'i' is replaced by two vertices, i' and i'' */ /* id[i'] := id[i] ; id[i''] := id[i] + no_of_nodes */ /* One edge for each original vertex, for i, we add (i',i'') */ for (i=0; i 0) { long int from=IGRAPH_FROM(graph, i); long int to=IGRAPH_TO(graph, i); igraph_real_t c=VECTOR(*capacity)[i]; VECTOR(*tmp)[edgeptr++] = from; VECTOR(*tmp)[edgeptr++] = to; if (residual_capacity) { VECTOR(*residual_capacity)[capptr++] = c; } } } IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); return 0; } int igraph_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, igraph_vector_t *residual_capacity, const igraph_vector_t *flow) { igraph_vector_t tmp; long int no_of_edges=igraph_ecount(graph); if (igraph_vector_size(capacity) != no_of_edges) { IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL); } if (igraph_vector_size(flow) != no_of_edges) { IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_i_residual_graph(graph, capacity, residual, residual_capacity, flow, &tmp)); igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_reverse_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, const igraph_vector_t *flow, igraph_vector_t *tmp) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); long int i, no_new_edges=0; long int edgeptr=0; for (i=0; i 0) { no_new_edges++; } if (VECTOR(*flow)[i] < cap) { no_new_edges++; } } IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges*2)); for (i=0; i 0) { VECTOR(*tmp)[edgeptr++] = from; VECTOR(*tmp)[edgeptr++] = to; } if (VECTOR(*flow)[i] < cap) { VECTOR(*tmp)[edgeptr++] = to; VECTOR(*tmp)[edgeptr++] = from; } } IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); return 0; } int igraph_reverse_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, const igraph_vector_t *flow) { igraph_vector_t tmp; long int no_of_edges=igraph_ecount(graph); if (capacity && igraph_vector_size(capacity) != no_of_edges) { IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL); } if (igraph_vector_size(flow) != no_of_edges) { IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_i_reverse_residual_graph(graph, capacity, residual, flow, &tmp)); igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return 0; } typedef struct igraph_i_dbucket_t { igraph_vector_long_t head; igraph_vector_long_t next; } igraph_i_dbucket_t; int igraph_i_dbucket_init(igraph_i_dbucket_t *buck, long int size) { IGRAPH_CHECK(igraph_vector_long_init(&buck->head, size)); IGRAPH_FINALLY(igraph_vector_long_destroy, &buck->head); IGRAPH_CHECK(igraph_vector_long_init(&buck->next, size)); IGRAPH_FINALLY_CLEAN(1); return 0; } void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) { igraph_vector_long_destroy(&buck->head); igraph_vector_long_destroy(&buck->next); } int igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, long int bid, long int elem) { /* Note: we can do this, since elem is not in any buckets */ VECTOR(buck->next)[elem]=VECTOR(buck->head)[bid]; VECTOR(buck->head)[bid]=elem+1; return 0; } long int igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, long int bid) { return VECTOR(buck->head)[bid] == 0; } long int igraph_i_dbucket_delete(igraph_i_dbucket_t *buck, long int bid) { long int elem=VECTOR(buck->head)[bid]-1; VECTOR(buck->head)[bid]=VECTOR(buck->next)[elem]; return elem; } int igraph_i_dominator_LINK(long int v, long int w, igraph_vector_long_t *ancestor) { VECTOR(*ancestor)[w] = v+1; return 0; } /* TODO: don't always reallocate path */ int igraph_i_dominator_COMPRESS(long int v, igraph_vector_long_t *ancestor, igraph_vector_long_t *label, igraph_vector_long_t *semi) { igraph_stack_long_t path; long int w=v; long int top, pretop; IGRAPH_CHECK(igraph_stack_long_init(&path, 10)); IGRAPH_FINALLY(igraph_stack_long_destroy, &path); while (VECTOR(*ancestor)[w] != 0) { IGRAPH_CHECK(igraph_stack_long_push(&path, w)); w=VECTOR(*ancestor)[w]-1; } top=igraph_stack_long_pop(&path); while (!igraph_stack_long_empty(&path)) { pretop=igraph_stack_long_pop(&path); if (VECTOR(*semi)[VECTOR(*label)[top]] < VECTOR(*semi)[VECTOR(*label)[pretop]]) { VECTOR(*label)[pretop] = VECTOR(*label)[top]; } VECTOR(*ancestor)[pretop]=VECTOR(*ancestor)[top]; top=pretop; } igraph_stack_long_destroy(&path); IGRAPH_FINALLY_CLEAN(1); return 0; } long int igraph_i_dominator_EVAL(long int v, igraph_vector_long_t *ancestor, igraph_vector_long_t *label, igraph_vector_long_t *semi) { if (VECTOR(*ancestor)[v] == 0) { return v; } else { igraph_i_dominator_COMPRESS(v, ancestor, label, semi); return VECTOR(*label)[v]; } } /* TODO: implement the faster version. */ /** * \function igraph_dominator_tree * Calculates the dominator tree of a flowgraph * * A flowgraph is a directed graph with a distinguished start (or * root) vertex r, such that for any vertex v, there is a path from r * to v. A vertex v dominates another vertex w (not equal to v), if * every path from r to w contains v. Vertex v is the immediate * dominator or w, v=idom(w), if v dominates w and every other * dominator of w dominates v. The edges {(idom(w), w)| w is not r} * form a directed tree, rooted at r, called the dominator tree of the * graph. Vertex v dominates vertex w if and only if v is an ancestor * of w in the dominator tree. * * This function implements the Lengauer-Tarjan algorithm * to construct the dominator tree of a directed graph. For details * please see Thomas Lengauer, Robert Endre Tarjan: A fast algorithm * for finding dominators in a flowgraph, ACM Transactions on * Programming Languages and Systems (TOPLAS) I/1, 121--141, 1979. * * \param graph A directed graph. If it is not a flowgraph, and it * contains some vertices not reachable from the root vertex, * then these vertices will be collected in the \c leftout * vector. * \param root The id of the root (or source) vertex, this will be the * root of the tree. * \param dom Pointer to an initialized vector or a null pointer. If * not a null pointer, then the immediate dominator of each * vertex will be stored here. For vertices that are not * reachable from the root, \c IGRAPH_NAN is stored here. For * the root vertex itself, -1 is added. * \param domtree Pointer to an uninitialized igraph_t, or NULL. If * not a null pointer, then the dominator tree is returned * here. The graph contains the vertices that are unreachable * from the root (if any), these will be isolates. * \param leftout Pointer to an initialized vector object, or NULL. If * not NULL, then the ids of the vertices that are unreachable * from the root vertex (and thus not part of the dominator * tree) are stored here. * \param mode Constant, must be \c IGRAPH_IN or \c IGRAPH_OUT. If it * is \c IGRAPH_IN, then all directions are considered as * opposite to the original one in the input graph. * \return Error code. * * Time complexity: very close to O(|E|+|V|), linear in the number of * edges and vertices. More precisely, it is O(|V|+|E|alpha(|E|,|V|)), * where alpha(|E|,|V|) is a functional inverse of Ackermann's * function. * * \example examples/simple/dominator_tree.c */ int igraph_dominator_tree(const igraph_t *graph, igraph_integer_t root, igraph_vector_t *dom, igraph_t *domtree, igraph_vector_t *leftout, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_adjlist_t succ, pred; igraph_vector_t parent; igraph_vector_long_t semi; /* +1 always */ igraph_vector_t vertex; /* +1 always */ igraph_i_dbucket_t bucket; igraph_vector_long_t ancestor; igraph_vector_long_t label; igraph_neimode_t invmode= mode==IGRAPH_IN ? IGRAPH_OUT: IGRAPH_IN; long int i; igraph_vector_t vdom, *mydom=dom; long int component_size=0; if (root < 0 || root >= no_of_nodes) { IGRAPH_ERROR("Invalid root vertex id for dominator tree", IGRAPH_EINVAL); } if (!igraph_is_directed(graph)) { IGRAPH_ERROR("Dominator tree of an undirected graph requested", IGRAPH_EINVAL); } if (mode == IGRAPH_ALL) { IGRAPH_ERROR("Invalid neighbor mode for dominator tree", IGRAPH_EINVAL); } if (dom) { IGRAPH_CHECK(igraph_vector_resize(dom, no_of_nodes)); } else { mydom=&vdom; IGRAPH_VECTOR_INIT_FINALLY(mydom, no_of_nodes); } igraph_vector_fill(mydom, IGRAPH_NAN); IGRAPH_CHECK(igraph_vector_init(&parent, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_destroy, &parent); IGRAPH_CHECK(igraph_vector_long_init(&semi, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &semi); IGRAPH_CHECK(igraph_vector_init(&vertex, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_destroy, &vertex); IGRAPH_CHECK(igraph_vector_long_init(&ancestor, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &ancestor); IGRAPH_CHECK(igraph_vector_long_init_seq(&label, 0, no_of_nodes-1)); IGRAPH_FINALLY(igraph_vector_long_destroy, &label); IGRAPH_CHECK(igraph_adjlist_init(graph, &succ, mode)); IGRAPH_FINALLY(igraph_adjlist_destroy, &succ); IGRAPH_CHECK(igraph_adjlist_init(graph, &pred, invmode)); IGRAPH_FINALLY(igraph_adjlist_destroy, &pred); IGRAPH_CHECK(igraph_i_dbucket_init(&bucket, no_of_nodes)); IGRAPH_FINALLY(igraph_i_dbucket_destroy, &bucket); /* DFS first, to set semi, vertex and parent, step 1 */ IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ 0, /*order=*/ &vertex, /*order_out=*/ 0, /*father=*/ &parent, /*dist=*/ 0, /*in_callback=*/ 0, /*out_callback=*/ 0, /*extra=*/ 0)); for (i=0; i0; i--) { long int w=(long int) VECTOR(vertex)[i]-1; igraph_vector_int_t *predw=igraph_adjlist_get(&pred, w); long int j, n=igraph_vector_int_size(predw); for (j=0; jstack; igraph_vector_bool_t *nomark=data->nomark; const igraph_vector_bool_t *GammaX=data->GammaX; const igraph_vector_t *map=data->map; long int realvid=(long int) VECTOR(*map)[(long int)vid]; IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); if (VECTOR(*GammaX)[(long int)realvid]) { if (!igraph_stack_empty(stack)) { long int top=(long int) igraph_stack_top(stack); VECTOR(*nomark)[top]=1; /* we just found a smaller one */ } igraph_stack_push(stack, realvid); /* TODO: error check */ } return 0; } igraph_bool_t igraph_i_all_st_cuts_minimal_dfs_otcb(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra) { igraph_i_all_st_cuts_minimal_dfs_data_t *data=extra; igraph_stack_t *stack=data->stack; const igraph_vector_t *map=data->map; long int realvid=(long int) VECTOR(*map)[(long int)vid]; IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); if (!igraph_stack_empty(stack) && igraph_stack_top(stack) == realvid) { igraph_stack_pop(stack); } return 0; } int igraph_i_all_st_cuts_minimal(const igraph_t *graph, const igraph_t *domtree, long int root, const igraph_marked_queue_t *X, const igraph_vector_bool_t *GammaX, const igraph_vector_t *invmap, igraph_vector_t *minimal) { long int no_of_nodes=igraph_vcount(graph); igraph_stack_t stack; igraph_vector_bool_t nomark; igraph_i_all_st_cuts_minimal_dfs_data_t data; long int i; IGRAPH_UNUSED(X); IGRAPH_CHECK(igraph_stack_init(&stack, 10)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_CHECK(igraph_vector_bool_init(&nomark, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &nomark); data.stack=&stack; data.nomark=&nomark; data.GammaX=GammaX; data.root=root; data.map=invmap; /* We mark all GammaX elements as minimal first. TODO: actually, we could just use GammaX to return the minimal elements. */ for (i=0; i0) { IGRAPH_CHECK(igraph_i_all_st_cuts_minimal(graph, &domtree, root, S, &GammaS, &Sbar_invmap, &M)); } igraph_vector_clear(Isv); IGRAPH_VECTOR_INIT_FINALLY(&Nuv, 0); IGRAPH_VECTOR_INIT_FINALLY(&Isv_min, 0); IGRAPH_VECTOR_INIT_FINALLY(&GammaS_vec, 0); for (i=0; i determine I(S,v)-K. I(S,v) contains all vertices that are in Nu(v) and that are reachable from Gamma(S) via a path in Nu(v). */ IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ -1, /*roots=*/ &GammaS_vec, /*mode=*/ IGRAPH_OUT, /*unreachable=*/ 0, /*restricted=*/ &Nuv, /*order=*/ &Isv_min, /*rank=*/ 0, /*father=*/ 0, /*pred=*/ 0, /*succ=*/ 0, /*dist=*/ 0, /*callback=*/ 0, /*extra=*/ 0)); for (isvlen=0; isvlenactive; long int no_of_nodes=igraph_vcount(graph); long int i; igraph_vector_t Sbar_map, Sbar_invmap; igraph_vector_t keep; igraph_t Sbar; igraph_vector_t M; long int nomin; IGRAPH_UNUSED(source); IGRAPH_UNUSED(target); if (igraph_marked_queue_size(S) == no_of_nodes) { igraph_vector_clear(Isv); return 0; } /* Create the graph induced by Sbar */ IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0); IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0); IGRAPH_VECTOR_INIT_FINALLY(&keep, 0); for (i=0; i= no_of_nodes) { IGRAPH_ERROR("Invalid `source' vertex", IGRAPH_EINVAL); } if (target < 0 || target >= no_of_nodes) { IGRAPH_ERROR("Invalid `target' vertex", IGRAPH_EINVAL); } if (source==target) { IGRAPH_ERROR("`source' and 'target' are the same vertex", IGRAPH_EINVAL); } if (!partition1s) { mypartition1s=&vpartition1s; IGRAPH_CHECK(igraph_vector_ptr_init(mypartition1s, 0)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, mypartition1s); } /* -------------------------------------------------------------------- */ /* We need to calculate the maximum flow first */ IGRAPH_VECTOR_INIT_FINALLY(&flow, 0); IGRAPH_CHECK(igraph_maxflow(graph, value, &flow, /*cut=*/ 0, /*partition1=*/ 0, /*partition2=*/ 0, /*source=*/ source, /*target=*/ target, capacity, &stats)); /* -------------------------------------------------------------------- */ /* Then we need the reverse residual graph */ IGRAPH_CHECK(igraph_reverse_residual_graph(graph, capacity, &residual, &flow)); IGRAPH_FINALLY(igraph_destroy, &residual); /* -------------------------------------------------------------------- */ /* We shrink it to its strongly connected components */ IGRAPH_VECTOR_INIT_FINALLY(&NtoL, 0); IGRAPH_CHECK(igraph_clusters(&residual, /*membership=*/ &NtoL, /*csize=*/ 0, /*no=*/ &proj_nodes, IGRAPH_STRONG)); IGRAPH_CHECK(igraph_contract_vertices(&residual, /*mapping=*/ &NtoL, /*vertex_comb=*/ 0)); IGRAPH_CHECK(igraph_simplify(&residual, /*multiple=*/ 1, /*loops=*/ 1, /*edge_comb=*/ 0)); newsource=(long int) VECTOR(NtoL)[(long int)source]; newtarget=(long int) VECTOR(NtoL)[(long int)target]; /* TODO: handle the newsource == newtarget case */ /* -------------------------------------------------------------------- */ /* Determine the active vertices in the projection */ IGRAPH_VECTOR_INIT_FINALLY(&VE1, 0); IGRAPH_CHECK(igraph_vector_bool_init(&VE1bool, proj_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &VE1bool); for (i=0; i 0) { long int from=IGRAPH_FROM(graph, i); long int to=IGRAPH_TO(graph, i); long int pfrom=(long int) VECTOR(NtoL)[from]; long int pto=(long int) VECTOR(NtoL)[to]; if (!VECTOR(VE1bool)[pfrom]) { VECTOR(VE1bool)[pfrom] = 1; VE1size++; } if (!VECTOR(VE1bool)[pto]) { VECTOR(VE1bool)[pto] = 1; VE1size++; } } } IGRAPH_CHECK(igraph_vector_reserve(&VE1, VE1size)); for (i=0; i 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include #include "igraph_error.h" #include "igraph_memory.h" #include "config.h" #include "igraph_hacks_internal.h" #include "igraph_math.h" #include "igraph_gml_tree.h" #include "foreign-gml-header.h" #include "foreign-gml-parser.h" #define yyscan_t void* int igraph_gml_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void *scanner); int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, char *s); char *igraph_gml_yyget_text (yyscan_t yyscanner ); int igraph_gml_yyget_leng (yyscan_t yyscanner ); void igraph_i_gml_get_keyword(char *s, int len, void *res); void igraph_i_gml_get_string(char *s, int len, void *res); double igraph_i_gml_get_real(char *s, int len); igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value); igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, char *v, int vlen); igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, char *value, int valuelen); igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, igraph_gml_tree_t *list); igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2); #define scanner context->scanner #define USE(x) /*(x)*/ /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 98 "igraph/src/foreign-gml-parser.y" { struct { char *s; int len; } str; void *tree; double real; } /* Line 193 of yacc.c. */ #line 195 "y.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ /* Line 216 of yacc.c. */ #line 220 "y.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int i) #else static int YYID (i) int i; #endif { return i; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss; YYSTYPE yyvs; YYLTYPE yyls; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 6 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 14 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 9 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 7 /* YYNRULES -- Number of rules. */ #define YYNRULES 12 /* YYNRULES -- Number of states. */ #define YYNSTATES 17 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 263 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint8 yyprhs[] = { 0, 0, 3, 5, 8, 10, 13, 16, 19, 24, 27, 29, 31 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 10, 0, -1, 11, -1, 11, 8, -1, 12, -1, 11, 12, -1, 13, 14, -1, 13, 15, -1, 13, 6, 11, 7, -1, 13, 13, -1, 5, -1, 4, -1, 3, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { 0, 125, 125, 126, 129, 130, 132, 134, 136, 138, 142, 145, 148 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "STRING", "NUM", "KEYWORD", "LISTOPEN", "LISTCLOSE", "EOFF", "$accept", "input", "list", "keyvalue", "key", "num", "string", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 14, 15 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 2, 1, 2, 2, 2, 4, 2, 1, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 10, 0, 2, 4, 0, 1, 3, 5, 12, 11, 0, 9, 6, 7, 0, 8 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 2, 3, 4, 5, 13, 14 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -4 static const yytype_int8 yypact[] = { 1, -4, 10, 0, -4, -2, -4, -4, -4, -4, -4, 1, -4, -4, -4, 2, -4 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -4, -4, 3, -3, 6, -4, -4 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint8 yytable[] = { 8, 9, 10, 1, 11, 1, 1, 1, 7, 16, 6, 12, 8, 0, 15 }; static const yytype_int8 yycheck[] = { 3, 3, 4, 5, 6, 5, 5, 5, 8, 7, 0, 5, 15, -1, 11 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 5, 10, 11, 12, 13, 0, 8, 12, 3, 4, 6, 13, 14, 15, 11, 7 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) #else # define YYLEX yylex (&yylval, &yylloc, scanner) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location, context); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_gml_parsedata_t* context; #endif { if (!yyvaluep) return; YYUSE (yylocationp); YYUSE (context); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_gml_parsedata_t* context; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); YY_LOCATION_PRINT (yyoutput, *yylocationp); YYFPRINTF (yyoutput, ": "); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void yy_stack_print (bottom, top) yytype_int16 *bottom; yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); for (; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_gml_parsedata_t* context) #else static void yy_reduce_print (yyvsp, yylsp, yyrule, context) YYSTYPE *yyvsp; YYLTYPE *yylsp; int yyrule; igraph_i_gml_parsedata_t* context; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , context); fprintf (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, yylsp, Rule, context); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_gml_parsedata_t* context) #else static void yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) const char *yymsg; int yytype; YYSTYPE *yyvaluep; YYLTYPE *yylocationp; igraph_i_gml_parsedata_t* context; #endif { YYUSE (yyvaluep); YYUSE (yylocationp); YYUSE (context); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { case 5: /* "KEYWORD" */ #line 120 "igraph/src/foreign-gml-parser.y" { igraph_Free((yyvaluep->str).s); }; #line 1124 "y.tab.c" break; case 11: /* "list" */ #line 121 "igraph/src/foreign-gml-parser.y" { igraph_gml_tree_destroy((yyvaluep->tree)); }; #line 1129 "y.tab.c" break; case 12: /* "keyvalue" */ #line 121 "igraph/src/foreign-gml-parser.y" { igraph_gml_tree_destroy((yyvaluep->tree)); }; #line 1134 "y.tab.c" break; case 13: /* "key" */ #line 120 "igraph/src/foreign-gml-parser.y" { igraph_Free((yyvaluep->str).s); }; #line 1139 "y.tab.c" break; case 15: /* "string" */ #line 120 "igraph/src/foreign-gml-parser.y" { igraph_Free((yyvaluep->str).s); }; #line 1144 "y.tab.c" break; default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (igraph_i_gml_parsedata_t* context); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (igraph_i_gml_parsedata_t* context) #else int yyparse (context) igraph_i_gml_parsedata_t* context; #endif #endif { /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /* Location data for the look-ahead symbol. */ YYLTYPE yylloc; int yystate; int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss = yyssa; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[2]; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; yylsp = yyls; #if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Initialize the default location before parsing starts. */ yylloc.first_line = yylloc.last_line = 1; yylloc.first_column = yylloc.last_column = 0; #endif goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyls = yyls1; yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); YYSTACK_RELOCATE (yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a look-ahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 125 "igraph/src/foreign-gml-parser.y" { context->tree=(yyvsp[(1) - (1)].tree); ;} break; case 3: #line 126 "igraph/src/foreign-gml-parser.y" { context->tree=(yyvsp[(1) - (2)].tree); ;} break; case 4: #line 129 "igraph/src/foreign-gml-parser.y" { (yyval.tree)=(yyvsp[(1) - (1)].tree); ;} break; case 5: #line 130 "igraph/src/foreign-gml-parser.y" { (yyval.tree)=igraph_i_gml_merge((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)); ;} break; case 6: #line 133 "igraph/src/foreign-gml-parser.y" { (yyval.tree)=igraph_i_gml_make_numeric((yyvsp[(1) - (2)].str).s, (yyvsp[(1) - (2)].str).len, (yyvsp[(2) - (2)].real)); ;} break; case 7: #line 135 "igraph/src/foreign-gml-parser.y" { (yyval.tree)=igraph_i_gml_make_string((yyvsp[(1) - (2)].str).s, (yyvsp[(1) - (2)].str).len, (yyvsp[(2) - (2)].str).s, (yyvsp[(2) - (2)].str).len); ;} break; case 8: #line 137 "igraph/src/foreign-gml-parser.y" { (yyval.tree)=igraph_i_gml_make_list((yyvsp[(1) - (4)].str).s, (yyvsp[(1) - (4)].str).len, (yyvsp[(3) - (4)].tree)); ;} break; case 9: #line 139 "igraph/src/foreign-gml-parser.y" { (yyval.tree)=igraph_i_gml_make_numeric2((yyvsp[(1) - (2)].str).s, (yyvsp[(1) - (2)].str).len, (yyvsp[(2) - (2)].str).s, (yyvsp[(2) - (2)].str).len); ;} break; case 10: #line 142 "igraph/src/foreign-gml-parser.y" { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner), &(yyval.str)); USE((yyvsp[(1) - (1)].str)) ;} break; case 11: #line 145 "igraph/src/foreign-gml-parser.y" { (yyval.real)=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner)); ;} break; case 12: #line 148 "igraph/src/foreign-gml-parser.y" { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner), &(yyval.str)); ;} break; /* Line 1267 of yacc.c. */ #line 1528 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (&yylloc, context, YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (&yylloc, context, yymsg); } else { yyerror (&yylloc, context, YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } yyerror_range[0] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, context); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; yyerror_range[0] = yylsp[1-yylen]; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[0] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp, context); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; yyerror_range[1] = yylloc; /* Using YYLLOC is tempting, but would change the location of the look-ahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, context, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, context); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp, context); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } #line 152 "igraph/src/foreign-gml-parser.y" int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "Parse error in GML file, line %i (%s)", locp->first_line, s); return 0; } void igraph_i_gml_get_keyword(char *s, int len, void *res) { struct { char *s; int len; } *p=res; p->s=igraph_Calloc(len+1, char); if (!p->s) { igraph_error("Cannot read GML file", __FILE__, __LINE__, IGRAPH_PARSEERROR); } memcpy(p->s, s, sizeof(char)*len); p->s[len]='\0'; p->len=len; } void igraph_i_gml_get_string(char *s, int len, void *res) { struct { char *s; int len; } *p=res; p->s=igraph_Calloc(len-1, char); if (!p->s) { igraph_error("Cannot read GML file", __FILE__, __LINE__, IGRAPH_PARSEERROR); } memcpy(p->s, s+1, sizeof(char)*(len-2)); p->s[len-2]='\0'; p->len=len-2; } double igraph_i_gml_get_real(char *s, int len) { igraph_real_t num; char tmp=s[len]; s[len]='\0'; sscanf(s, "%lf", &num); s[len]=tmp; return num; } igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } if (floor(value)==value) { igraph_gml_tree_init_integer(t, s, len, value); } else { igraph_gml_tree_init_real(t, s, len, value); } return t; } igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, char *v, int vlen) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); char tmp=v[vlen]; igraph_real_t value=0; if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } v[vlen]='\0'; if (strcasecmp(v, "inf")) { value=IGRAPH_INFINITY; } else if (strcasecmp(v, "nan")) { value=IGRAPH_NAN; } else { igraph_error("Parse error", __FILE__, __LINE__, IGRAPH_PARSEERROR); } v[vlen]=tmp; igraph_gml_tree_init_real(t, s, len, value); return t; } igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, char *value, int valuelen) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } igraph_gml_tree_init_string(t, s, len, value, valuelen); return t; } igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, igraph_gml_tree_t *list) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } igraph_gml_tree_init_tree(t, s, len, list); return t; } igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { igraph_gml_tree_mergedest(t1, t2); igraph_Free(t2); return t1; } igraph/src/pottsmodel_2.h0000644000176000001440000001534412325527074015145 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Jörg Reichardt This file was modified by Vincent Traag The original copyright notice follows here */ /*************************************************************************** pottsmodel.h - description ------------------- begin : Fri May 28 2004 copyright : (C) 2004 by email : ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef POTTSMODEL_H #define POTTSMODEL_H #include "NetDataTypes.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" #define qmax 500 class PottsModel { private: // HugeArray neg_gammalookup; // HugeArray pos_gammalookup; DL_Indexed_List *new_spins; DL_Indexed_List *previous_spins; HugeArray*> correlation; network *net; unsigned int q; unsigned int operation_mode; FILE *Qfile, *Magfile; double Qmatrix[qmax+1][qmax+1]; double* Qa; double* weights; double total_degree_sum; unsigned long num_of_nodes; unsigned long num_of_links; unsigned long k_max; double energy; double acceptance; double *neighbours; public: PottsModel(network *net, unsigned int q, int norm_by_degree); ~PottsModel(); double* color_field; unsigned long assign_initial_conf(int spin); unsigned long initialize_lookup(double kT, double gamma); double initialize_Qmatrix(void); double calculate_Q(void); double calculate_genQ(double gamma); double FindStartTemp(double gamma, double prob, double ts); long HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps); double HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps); long HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps); double HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps); double GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel=true, int repetitions=1); double GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel=true, int repetitions=1); long WriteCorrelationMatrix(char *filename); double calculate_energy(double gamma); long WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *csize, igraph_vector_t *membership, double kT, double gamma); long WriteSoftClusters(char *filename, double threshold); double Get_Energy(void) { return energy;} double FindCommunityFromStart(double gamma, double prob, char *nodename, igraph_vector_t *result, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, igraph_integer_t *outer_links); }; class PottsModelN { private: // HugeArray neg_gammalookup; // HugeArray pos_gammalookup; DL_Indexed_List *new_spins; DL_Indexed_List *previous_spins; HugeArray*> correlation; network *net; unsigned int q; //number of communities double m_p; //number of positive ties (or sum of degrees), this equals the number of edges only if it is undirected and each edge has a weight of 1 double m_n; //number of negative ties (or sum of degrees) unsigned int num_nodes; //number of nodes bool is_directed; bool is_init; double *degree_pos_in; //Postive indegree of the nodes (or sum of weights) double *degree_neg_in; //Negative indegree of the nodes (or sum of weights) double *degree_pos_out; //Postive outdegree of the nodes (or sum of weights) double *degree_neg_out; //Negative outdegree of the nodes (or sum of weights) double *degree_community_pos_in; //Positive sum of indegree for communities double *degree_community_neg_in; //Negative sum of indegree for communities double *degree_community_pos_out; //Positive sum of outegree for communities double *degree_community_neg_out; //Negative sum of outdegree for communities unsigned int *csize; //The number of nodes in each community unsigned int *spin; //The membership of each node double *neighbours; //Array of neighbours of a vertex in each community double *weights; //Weights of all possible transitions to another community public: PottsModelN(network *n, unsigned int num_communities, bool directed); ~PottsModelN(); void assign_initial_conf(bool init_spins); double FindStartTemp(double gamma, double lambda, double ts); double HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps); double HeatBathJoin(double gamma, double lambda); double HeatBathLookupZeroTemp(double gamma, double lambda, unsigned int max_sweeps); long WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *community_size, igraph_vector_t *membership, igraph_matrix_t *adhesion, igraph_matrix_t *normalised_adhesion, igraph_real_t *polarization, double t, double d_p, double d_n, double gamma, double lambda); }; #endif igraph/src/glpenv08.c0000644000176000001440000000743212325527073014166 0ustar ripleyusers/* glpenv08.c (shared library support) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include "glpenv.h" /* GNU version ********************************************************/ #if defined(HAVE_LTDL) #include void *xdlopen(const char *module) { void *h = NULL; if (lt_dlinit() != 0) { lib_err_msg(lt_dlerror()); goto done; } h = lt_dlopen(module); if (h == NULL) { lib_err_msg(lt_dlerror()); if (lt_dlexit() != 0) xerror("xdlopen: %s\n", lt_dlerror()); } done: return h; } void *xdlsym(void *h, const char *symbol) { void *ptr; xassert(h != NULL); ptr = lt_dlsym(h, symbol); if (ptr == NULL) xerror("xdlsym: %s: %s\n", symbol, lt_dlerror()); return ptr; } void xdlclose(void *h) { xassert(h != NULL); if (lt_dlclose(h) != 0) xerror("xdlclose: %s\n", lt_dlerror()); if (lt_dlexit() != 0) xerror("xdlclose: %s\n", lt_dlerror()); return; } /* POSIX version ******************************************************/ #elif defined(HAVE_DLFCN) #include void *xdlopen(const char *module) { void *h; h = dlopen(module, RTLD_NOW); if (h == NULL) lib_err_msg(dlerror()); return h; } void *xdlsym(void *h, const char *symbol) { void *ptr; xassert(h != NULL); ptr = dlsym(h, symbol); if (ptr == NULL) xerror("xdlsym: %s: %s\n", symbol, dlerror()); return ptr; } void xdlclose(void *h) { xassert(h != NULL); if (dlclose(h) != 0) xerror("xdlclose: %s\n", dlerror()); return; } /* Windows version ****************************************************/ #elif defined(__WOE__) #include void *xdlopen(const char *module) { void *h; h = LoadLibrary(module); if (h == NULL) { char msg[20]; sprintf(msg, "Error %d", GetLastError()); lib_err_msg(msg); } return h; } void *xdlsym(void *h, const char *symbol) { void *ptr; xassert(h != NULL); ptr = GetProcAddress(h, symbol); if (ptr == NULL) xerror("xdlsym: %s: Error %d\n", symbol, GetLastError()); return ptr; } void xdlclose(void *h) { xassert(h != NULL); if (!FreeLibrary(h)) xerror("xdlclose: Error %d\n", GetLastError()); return; } /* NULL version *******************************************************/ #else void *xdlopen(const char *module) { xassert(module == module); lib_err_msg("Shared libraries not supported"); return NULL; } void *xdlsym(void *h, const char *symbol) { xassert(h != h); xassert(symbol != symbol); return NULL; } void xdlclose(void *h) { xassert(h != h); return; } #endif /* eof */ igraph/src/bliss_timer.cc0000644000176000001440000000415112325527072015174 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include #ifdef HAVE_TIMES_H #include #else #include #endif #include "bliss_timer.hh" using namespace std; namespace igraph { #ifdef HAVE_TIMES_H static const double numTicksPerSec = (double)(sysconf(_SC_CLK_TCK)); #else static const double numTicksPerSec = CLOCKS_PER_SEC; #endif Timer::Timer() { start_time = 0.0; end_time = 0.0; } void Timer::start() { #ifdef HAVE_TIMES_H struct tms clkticks; times(&clkticks); start_time = ((double) clkticks.tms_utime + (double) clkticks.tms_stime) / numTicksPerSec; #else clock_t clkticks; clkticks=clock(); start_time = (double)clkticks / numTicksPerSec; #endif } void Timer::stop() { #ifdef HAVE_TIMES_H struct tms clkticks; times(&clkticks); end_time = ((double) clkticks.tms_utime + (double) clkticks.tms_stime) / numTicksPerSec; #else clock_t clkticks; clkticks=clock(); end_time = (double)clkticks / numTicksPerSec; #endif } double Timer::get_intermediate() { #ifdef HAVE_TIMES_H struct tms clkticks; times(&clkticks); double intermediate = ((double) clkticks.tms_utime + (double) clkticks.tms_stime) / numTicksPerSec; #else clock_t clkticks; clkticks=clock(); double intermediate = (double)clkticks / numTicksPerSec; #endif return intermediate - start_time; } double Timer::get_duration() { return(end_time - start_time); } } igraph/src/bliss_eqrefhash.hh0000644000176000001440000000315312325372072016033 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_EQREFHASH_HH #define BLISS_EQREFHASH_HH #include #define EqrefHash BuzzHash //#define EqrefHash PerfectHash namespace igraph { class BuzzHash { protected: unsigned int h; public: void reset() {h = 0; } void update(unsigned int); int cmp(const BuzzHash &other); bool is_lt(const BuzzHash &other) {return(cmp(other) < 0); } bool is_le(const BuzzHash &other) {return(cmp(other) <= 0); } bool is_equal(const BuzzHash &other) {return(cmp(other) == 0); } }; class PerfectHash { protected: std::vector h; public: void reset() {h.clear(); } void update(unsigned int i) {h.push_back(i); } int cmp(const PerfectHash &other); bool is_lt(const PerfectHash &other) {return(cmp(other) < 0); } bool is_le(const PerfectHash &other) {return(cmp(other) <= 0); } bool is_equal(const PerfectHash &other) {return(cmp(other) == 0); } }; } #endif igraph/src/igraph_dqueue_pmt.h0000644000176000001440000000401712325527073016226 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * Double ended queue data type. * \ingroup internal */ typedef struct TYPE(igraph_dqueue) { BASE *begin; BASE *end; BASE *stor_begin; BASE *stor_end; } TYPE(igraph_dqueue); int FUNCTION(igraph_dqueue,init) (TYPE(igraph_dqueue)* q, long int size); void FUNCTION(igraph_dqueue,destroy) (TYPE(igraph_dqueue)* q); igraph_bool_t FUNCTION(igraph_dqueue,empty) (const TYPE(igraph_dqueue)* q); void FUNCTION(igraph_dqueue,clear) (TYPE(igraph_dqueue)* q); igraph_bool_t FUNCTION(igraph_dqueue,full) (TYPE(igraph_dqueue)* q); long int FUNCTION(igraph_dqueue,size) (const TYPE(igraph_dqueue)* q); BASE FUNCTION(igraph_dqueue,pop) (TYPE(igraph_dqueue)* q); BASE FUNCTION(igraph_dqueue,pop_back)(TYPE(igraph_dqueue)* q); BASE FUNCTION(igraph_dqueue,head) (const TYPE(igraph_dqueue)* q); BASE FUNCTION(igraph_dqueue,back) (const TYPE(igraph_dqueue)* q); int FUNCTION(igraph_dqueue,push) (TYPE(igraph_dqueue)* q, BASE elem); int FUNCTION(igraph_dqueue,print)(const TYPE(igraph_dqueue)* q); int FUNCTION(igraph_dqueue,fprint)(const TYPE(igraph_dqueue)* q, FILE *file); BASE FUNCTION(igraph_dqueue,e)(const TYPE(igraph_dqueue) *q, long int idx); igraph/src/cores.c0000644000176000001440000001115012325527073013626 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_community.h" #include "igraph_memory.h" #include "igraph_interface.h" #include "igraph_iterators.h" #include "config.h" /** * \function igraph_coreness * \brief Finding the coreness of the vertices in a network. * * The k-core of a graph is a maximal subgraph in which each vertex * has at least degree k. (Degree here means the degree in the * subgraph of course.). The coreness of a vertex is the highest order * of a k-core containing the vertex. * * * This function implements the algorithm presented in Vladimir * Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores * Decomposition of Networks. * \param graph The input graph. * \param cores Pointer to an initialized vector, the result of the * computation will be stored here. It will be resized as * needed. For each vertex it contains the highest order of a * core containing the vertex. * \param mode For directed graph it specifies whether to calculate * in-cores, out-cores or the undirected version. It is ignored * for undirected graphs. Possible values: \c IGRAPH_ALL * undirected version, \c IGRAPH_IN in-cores, \c IGRAPH_OUT * out-cores. * \return Error code. * * Time complexity: O(|E|), the number of edges. */ int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); long int *bin, *vert, *pos; long int maxdeg; long int i, j=0; igraph_vector_t neis; igraph_neimode_t omode; if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { IGRAPH_ERROR("Invalid mode in k-cores", IGRAPH_EINVAL); } if (!igraph_is_directed(graph) || mode==IGRAPH_ALL) { mode=omode=IGRAPH_ALL; } else if (mode==IGRAPH_IN) { omode=IGRAPH_OUT; } else { omode=IGRAPH_IN; } vert=igraph_Calloc(no_of_nodes, long int); if (vert==0) { IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vert); pos=igraph_Calloc(no_of_nodes, long int); if (pos==0) { IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, pos); /* maximum degree + degree of vertices */ IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode, IGRAPH_LOOPS)); maxdeg = (long int) igraph_vector_max(cores); bin=igraph_Calloc(maxdeg+1, long int); if (bin==0) { IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, bin); /* degree histogram */ for (i=0; i0; i--) { bin[i] = bin[i-1]; } bin[0]=0; /* this is the main algorithm */ IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); for (i=0; i VECTOR(*cores)[v]) { long int du=(long int) VECTOR(*cores)[u]; long int pu=pos[u]; long int pw=bin[du]; long int w=vert[pw]; if (u != w) { pos[u]=pw; pos[w]=pu; vert[pu]=w; vert[pw]=u; } bin[du] += 1; VECTOR(*cores)[u] -= 1; } } } igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); igraph_free(bin); igraph_free(pos); igraph_free(vert); IGRAPH_FINALLY_CLEAN(3); return 0; } igraph/src/basic_query.c0000644000176000001440000000402512325527072015023 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_interface.h" #include "igraph_structural.h" #include "config.h" /** * \ingroup structural * \function igraph_are_connected * \brief Decides whether two vertices are connected * * \param graph The graph object. * \param v1 The first vertex. * \param v2 The second vertex. * \param res Boolean, \c TRUE if there is an edge from * \p v1 to \p v2, \c FALSE otherwise. * \return The error code \c IGRAPH_EINVVID is returned if an invalid * vertex ID is given. * * The function is of course symmetric for undirected graphs. * * * Time complexity: O( min(log(d1), log(d2)) ), * d1 is the (out-)degree of \p v1 and d2 is the (in-)degree of \p v2. */ int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res) { long int nov=igraph_vcount(graph); igraph_integer_t eid=-1; if (v1 < 0 || v2 < 0 || v1 > nov-1 || v2 > nov-1) { IGRAPH_ERROR("are connected", IGRAPH_EINVVID); } igraph_get_eid(graph, &eid, v1, v2, /*directed=*/1, /*error=*/ 0); *res = (eid >=0); return IGRAPH_SUCCESS; } igraph/src/walktrap_graph.h0000644000176000001440000000661712325527074015543 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Pascal Pons The original copyright notice follows here */ // File: graph.h //----------------------------------------------------------------------------- // Walktrap v0.2 -- Finds community structure of networks using random walks // Copyright (C) 2004-2005 Pascal Pons // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA //----------------------------------------------------------------------------- // Author : Pascal Pons // Email : pascal.pons@gmail.com // Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html // Location : Paris, France // Time : June 2005 //----------------------------------------------------------------------------- // see readme.txt for more details /* FSF address above was fixed by Tamas Nepusz */ #ifndef GRAPH_H #define GRAPH_H #include #include "igraph_community.h" namespace igraph { namespace walktrap { using namespace std; class Edge { // code an edge of a given vertex public: int neighbor; // the number of the neighbor vertex float weight; // the weight of the edge }; bool operator<(const Edge& E1, const Edge& E2); class Vertex { public: Edge* edges; // the edges of the vertex int degree; // number of neighbors float total_weight; // the total weight of the vertex Vertex(); // creates empty vertex ~Vertex(); // destructor }; class Graph { public: int nb_vertices; // number of vertices int nb_edges; // number of edges float total_weight; // total weight of the edges Vertex* vertices; // array of the vertices long memory(); // the total memory used in Bytes Graph(); // create an empty graph ~Graph(); // destructor char** index; // to keep the real name of the vertices int convert_from_igraph(const igraph_t * igraph, const igraph_vector_t *weights); }; } } /* end of namespaces */ #endif igraph/src/gengraph_mr-connected.cpp0000644000176000001440000001420712325527073017312 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gengraph_header.h" #include "gengraph_graph_molloy_optimized.h" #include "gengraph_graph_molloy_hash.h" #include "gengraph_degree_sequence.h" #include "gengraph_random.h" #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_error.h" namespace gengraph { // return negative number if program should exit int parse_options(int &argc, char** &argv); // options static const bool MONITOR_TIME = false; static const int SHUFFLE_TYPE = FINAL_HEURISTICS; static const bool RAW_DEGREES = false; static const FILE *Fdeg = stdin; //_________________________________________________________________________ // int main(int argc, char** argv) { // // options // SET_VERBOSE(VERBOSE_NONE); // if(parse_options(argc, argv) < 0) return -1; // //Read degree distribution // degree_sequence dd(Fdeg, !RAW_DEGREES); // //Allocate memory // if(VERBOSE()) fprintf(stderr,"Allocate memory for graph..."); // graph_molloy_opt g(dd); // dd.~degree_sequence(); // //Realize degree sequence // if(VERBOSE()) fprintf(stderr,"done\nRealize degree sequence..."); // bool FAILED = !g.havelhakimi(); // if(VERBOSE()) fprintf(stderr," %s\n", FAILED ? "Failed" : "Success"); // if(FAILED) return 2; // //Merge connected components together // if(VERBOSE()) fprintf(stderr,"Connecting..."); // FAILED = !g.make_connected(); // if(VERBOSE()) fprintf(stderr," %s\n", FAILED ? "Failed" : "Success"); // if(FAILED) return 3; // //Convert graph_molloy_opt to graph_molloy_hash // if(VERBOSE()) fprintf(stderr,"Convert adjacency lists into hash tables..."); // int *hc = g.hard_copy(); // g.~graph_molloy_opt(); // graph_molloy_hash gh(hc); // delete[] hc; // if(VERBOSE()) fprintf(stderr,"Done\n"); // //Shuffle // gh.shuffle(5*gh.nbarcs(), SHUFFLE_TYPE); // //Output // gh.print(); // if(MONITOR_TIME) { // double t = double(clock()) / double(CLOCKS_PER_SEC); // fprintf(stderr,"Time used: %f\n", t); // } // return 0; // } //_________________________________________________________________________ // int parse_options(int &argc, char** &argv) { // bool HELP = false; // int argc0 = argc; // argc = 1; // for(int a=1; a %s returns a graph in its standard output\n",argv[0]); // fprintf(stderr," If no file is given, %s reads its standard input\n",argv[0]); // fprintf(stderr," [-v] and [-vv] options causes extra verbose.\n"); // fprintf(stderr," [-g] option uses the Gkantsidis heuristics.\n"); // fprintf(stderr," [-b] option uses the Brute Force heuristics.\n"); // fprintf(stderr," [-f] option uses the Modified Gkantsidis heuristics.\n"); // fprintf(stderr," [-o] option uses the Optimal Gkantsidis heuristics.\n"); // fprintf(stderr," [-t] option monitors computation time\n"); // fprintf(stderr," [-s] does a srandom(0) to get a constant random graph\n"); // fprintf(stderr," [-raw] is to take raw degree sequences as input\n"); // return -1; // } // return 0; // } } // namespace gengraph using namespace gengraph; extern "C" { int igraph_degree_sequence_game_vl(igraph_t *graph, const igraph_vector_t *out_seq, const igraph_vector_t *in_seq) { long int sum=igraph_vector_sum(out_seq); if (sum % 2 != 0) { IGRAPH_ERROR("Sum of degrees should be even", IGRAPH_EINVAL); } RNG_BEGIN(); if (in_seq && igraph_vector_size(in_seq) != 0) { RNG_END(); IGRAPH_ERROR("This generator works with undirected graphs only", IGRAPH_EINVAL); } degree_sequence *dd = new degree_sequence(out_seq); graph_molloy_opt *g = new graph_molloy_opt(*dd); delete dd; if (!g->havelhakimi()) { delete g; RNG_END(); IGRAPH_ERROR("Cannot realize the given degree sequence as an undirected, simple graph", IGRAPH_EINVAL); } if (!g->make_connected()) { delete g; RNG_END(); IGRAPH_ERROR("Cannot make a connected graph from the given degree sequence", IGRAPH_EINVAL); } int *hc = g->hard_copy(); delete g; graph_molloy_hash *gh = new graph_molloy_hash(hc); delete [] hc; gh->shuffle(5*gh->nbarcs(), 100*gh->nbarcs(), SHUFFLE_TYPE); IGRAPH_CHECK(gh->print(graph)); delete gh; RNG_END(); return 0; } } igraph/src/igraph_qsort.h0000644000176000001440000000257312325527073015233 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard st, Cambridge, MA 02139, USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_QSORT_H #define IGRAPH_QSORT_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS void igraph_qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)); void igraph_qsort_r(void *base, size_t nel, size_t width, void *thunk, int (*compar)(void *, const void *, const void *)); __END_DECLS #endif igraph/src/foreign-ncol-parser.c0000644000176000001440000013306512325527073016401 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Using locations. */ #define YYLSP_NEEDED 1 /* Substitute the variable and function names. */ #define yyparse igraph_ncol_yyparse #define yylex igraph_ncol_yylex #define yyerror igraph_ncol_yyerror #define yylval igraph_ncol_yylval #define yychar igraph_ncol_yychar #define yydebug igraph_ncol_yydebug #define yynerrs igraph_ncol_yynerrs #define yylloc igraph_ncol_yylloc /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { ALNUM = 258, NEWLINE = 259 }; #endif /* Tokens. */ #define ALNUM 258 #define NEWLINE 259 /* Copy the first part of user declarations. */ #line 23 "igraph/src/foreign-ncol-parser.y" /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include "igraph_hacks_internal.h" #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_math.h" #include "igraph_memory.h" #include "igraph_error.h" #include "config.h" #include "foreign-ncol-header.h" #include "foreign-ncol-parser.h" #define yyscan_t void* int igraph_ncol_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_ncol_yyerror(YYLTYPE* locp, igraph_i_ncol_parsedata_t *context, const char *s); char *igraph_ncol_yyget_text (yyscan_t yyscanner ); int igraph_ncol_yyget_leng (yyscan_t yyscanner ); igraph_real_t igraph_ncol_get_number(const char *str, long int len); #define scanner context->scanner /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 87 "igraph/src/foreign-ncol-parser.y" { long int edgenum; double weightnum; } /* Line 193 of yacc.c. */ #line 172 "y.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ /* Line 216 of yacc.c. */ #line 197 "y.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int i) #else static int YYID (i) int i; #endif { return i; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss; YYSTYPE yyvs; YYLTYPE yyls; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 10 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 5 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 5 /* YYNRULES -- Number of rules. */ #define YYNRULES 8 /* YYNRULES -- Number of states. */ #define YYNSTATES 12 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 259 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint8 yyprhs[] = { 0, 0, 3, 4, 7, 10, 14, 19, 21 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 6, 0, -1, -1, 6, 4, -1, 6, 7, -1, 8, 8, 4, -1, 8, 8, 9, 4, -1, 3, -1, 3, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { 0, 100, 100, 101, 102, 105, 110, 118, 123 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "ALNUM", "NEWLINE", "$accept", "input", "edge", "edgeid", "weight", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 5, 6, 6, 6, 7, 7, 8, 9 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 0, 2, 2, 3, 4, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 2, 0, 1, 7, 3, 4, 0, 0, 8, 5, 0, 6 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 1, 5, 6, 10 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -3 static const yytype_int8 yypact[] = { -3, 0, -3, -3, -3, -3, 2, -2, -3, -3, 3, -3 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -3, -3, -3, 4, -3 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint8 yytable[] = { 2, 8, 9, 3, 4, 3, 0, 11, 0, 0, 7 }; static const yytype_int8 yycheck[] = { 0, 3, 4, 3, 4, 3, -1, 4, -1, -1, 6 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 6, 0, 3, 4, 7, 8, 8, 3, 4, 9, 4 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) #else # define YYLEX yylex (&yylval, &yylloc, scanner) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location, context); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_ncol_parsedata_t* context; #endif { if (!yyvaluep) return; YYUSE (yylocationp); YYUSE (context); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_ncol_parsedata_t* context; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); YY_LOCATION_PRINT (yyoutput, *yylocationp); YYFPRINTF (yyoutput, ": "); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void yy_stack_print (bottom, top) yytype_int16 *bottom; yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); for (; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_ncol_parsedata_t* context) #else static void yy_reduce_print (yyvsp, yylsp, yyrule, context) YYSTYPE *yyvsp; YYLTYPE *yylsp; int yyrule; igraph_i_ncol_parsedata_t* context; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , context); fprintf (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, yylsp, Rule, context); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_ncol_parsedata_t* context) #else static void yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) const char *yymsg; int yytype; YYSTYPE *yyvaluep; YYLTYPE *yylocationp; igraph_i_ncol_parsedata_t* context; #endif { YYUSE (yyvaluep); YYUSE (yylocationp); YYUSE (context); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (igraph_i_ncol_parsedata_t* context); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (igraph_i_ncol_parsedata_t* context) #else int yyparse (context) igraph_i_ncol_parsedata_t* context; #endif #endif { /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /* Location data for the look-ahead symbol. */ YYLTYPE yylloc; int yystate; int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss = yyssa; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[2]; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; yylsp = yyls; #if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Initialize the default location before parsing starts. */ yylloc.first_line = yylloc.last_line = 1; yylloc.first_column = yylloc.last_column = 0; #endif goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyls = yyls1; yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); YYSTACK_RELOCATE (yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a look-ahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); YY_REDUCE_PRINT (yyn); switch (yyn) { case 5: #line 105 "igraph/src/foreign-ncol-parser.y" { igraph_vector_push_back(context->vector, (yyvsp[(1) - (3)].edgenum)); igraph_vector_push_back(context->vector, (yyvsp[(2) - (3)].edgenum)); igraph_vector_push_back(context->weights, 0); ;} break; case 6: #line 110 "igraph/src/foreign-ncol-parser.y" { igraph_vector_push_back(context->vector, (yyvsp[(1) - (4)].edgenum)); igraph_vector_push_back(context->vector, (yyvsp[(2) - (4)].edgenum)); igraph_vector_push_back(context->weights, (yyvsp[(3) - (4)].weightnum)); context->has_weights = 1; ;} break; case 7: #line 118 "igraph/src/foreign-ncol-parser.y" { igraph_trie_get2(context->trie, igraph_ncol_yyget_text(scanner), igraph_ncol_yyget_leng(scanner), &(yyval.edgenum)); ;} break; case 8: #line 123 "igraph/src/foreign-ncol-parser.y" { (yyval.weightnum)=igraph_ncol_get_number(igraph_ncol_yyget_text(scanner), igraph_ncol_yyget_leng(scanner)); ;} break; /* Line 1267 of yacc.c. */ #line 1446 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (&yylloc, context, YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (&yylloc, context, yymsg); } else { yyerror (&yylloc, context, YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } yyerror_range[0] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, context); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; yyerror_range[0] = yylsp[1-yylen]; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[0] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp, context); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; yyerror_range[1] = yylloc; /* Using YYLLOC is tempting, but would change the location of the look-ahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, context, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, context); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp, context); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } #line 126 "igraph/src/foreign-ncol-parser.y" int igraph_ncol_yyerror(YYLTYPE* locp, igraph_i_ncol_parsedata_t *context, const char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "Parse error in NCOL file, line %i (%s)", locp->first_line, s); return 0; } igraph_real_t igraph_ncol_get_number(const char *str, long int length) { igraph_real_t num; char *tmp=igraph_Calloc(length+1, char); strncpy(tmp, str, length); tmp[length]='\0'; sscanf(tmp, "%lf", &num); igraph_Free(tmp); return num; } igraph/src/infomap_Greedy.cc0000644000176000001440000004516512325527073015623 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "infomap_Greedy.h" #include #define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 ) Greedy::Greedy(FlowGraph * fgraph){ graph = fgraph; Nnode = graph->Nnode; alpha = graph->alpha;// teleportation probability beta = 1.0 - alpha; // probability to take normal step Nempty = 0; vector(Nnode).swap(mod_empty); vector(Nnode).swap(node_index); vector(Nnode).swap(mod_exit); vector(Nnode).swap(mod_size); vector(Nnode).swap(mod_danglingSize); vector(Nnode).swap(mod_teleportWeight); vector(Nnode).swap(mod_members); nodeSize_log_nodeSize = graph->nodeSize_log_nodeSize; exit_log_exit = graph->exit_log_exit; size_log_size = graph->size_log_size; exitFlow = graph->exitFlow; Node ** node = graph->node; for (int i=0; iexit; mod_size[i] = node[i]->size; mod_danglingSize[i] = node[i]->danglingSize; mod_teleportWeight[i] = node[i]->teleportWeight; mod_members[i] = node[i]->members.size(); } exit = plogp(exitFlow); codeLength = exit - 2.0*exit_log_exit + size_log_size - nodeSize_log_nodeSize; } Greedy::~Greedy() { } void delete_Greedy(Greedy *greedy) { delete greedy; } /** Greedy optimizing (as in Blodel and Al.) : * for each vertex (selected in a random order) compute the best possible move within neighborhood */ bool Greedy::optimize() { bool moved = false; Node ** node = graph->node; RNG_BEGIN(); // Generate random enumeration of nodes vector randomOrder(Nnode); for (int i=0; i redirect(Nnode,0); vector > > flowNtoM(Nnode); for (int k=0; k INT_MAX) { for (int j=0;joutLinks.size(); if (NoutLinks == 0) { //dangling node, add node to calculate flow below redirect[oldM] = offset + NmodLinks; flowNtoM[NmodLinks].first = oldM; flowNtoM[NmodLinks].second.first = 0.0; flowNtoM[NmodLinks].second.second = 0.0; NmodLinks++; } else { for (int j=0; joutLinks[j].first]; // index destination du lien double nb_flow = node[flip]->outLinks[j].second; // wgt du lien if (redirect[nb_M] >= offset) { flowNtoM[redirect[nb_M] - offset].second.first += nb_flow; } else { redirect[nb_M] = offset + NmodLinks; flowNtoM[NmodLinks].first = nb_M; flowNtoM[NmodLinks].second.first = nb_flow; flowNtoM[NmodLinks].second.second = 0.0; NmodLinks++; } } } // For all inLinks int NinLinks = node[flip]->inLinks.size(); for (int j=0; jinLinks[j].first]; double nb_flow = node[flip]->inLinks[j].second; if (redirect[nb_M] >= offset) { flowNtoM[redirect[nb_M] - offset].second.second += nb_flow; } else{ redirect[nb_M] = offset + NmodLinks; flowNtoM[NmodLinks].first = nb_M; flowNtoM[NmodLinks].second.first = 0.0; flowNtoM[NmodLinks].second.second = nb_flow; NmodLinks++; } } // For teleportation and dangling nodes for (int j=0;jsize + beta*node[flip]->danglingSize)* (mod_teleportWeight[oldM]-node[flip]->teleportWeight); flowNtoM[j].second.second += (alpha*(mod_size[oldM] - node[flip]->size) + beta*(mod_danglingSize[oldM] - node[flip]->danglingSize)) * node[flip]->teleportWeight; } else { flowNtoM[j].second.first += (alpha*node[flip]->size + beta*node[flip]->danglingSize) * mod_teleportWeight[newM]; flowNtoM[j].second.second += (alpha*mod_size[newM] + beta*mod_danglingSize[newM] ) * node[flip]->teleportWeight; } } // Calculate flow to/from own module (default value if no link to // own module) double outFlowOldM = (alpha*node[flip]->size + beta*node[flip]->danglingSize) * (mod_teleportWeight[oldM] - node[flip]->teleportWeight) ; double inFlowOldM = (alpha*(mod_size[oldM] - node[flip]->size) + beta*(mod_danglingSize[oldM] - node[flip]->danglingSize)) * node[flip]->teleportWeight; if (redirect[oldM] >= offset) { outFlowOldM = flowNtoM[redirect[oldM] - offset].second.first; inFlowOldM = flowNtoM[redirect[oldM] - offset].second.second; } // Option to move to empty module (if node not already alone) if (mod_members[oldM] > static_cast(node[flip]->members.size())) { if (Nempty > 0) { flowNtoM[NmodLinks].first = mod_empty[Nempty-1]; flowNtoM[NmodLinks].second.first = 0.0; flowNtoM[NmodLinks].second.second = 0.0; NmodLinks++; } } // Randomize link order for optimized search for (int j=0;jexit + outFlowOldM + inFlowOldM) + plogp(mod_exit[newM] + node[flip]->exit - outFlowNewM - inFlowNewM); double delta_size_log_size = - plogp(mod_exit[oldM] + mod_size[oldM]) - plogp(mod_exit[newM] + mod_size[newM]) + plogp(mod_exit[oldM] + mod_size[oldM] - node[flip]->exit - node[flip]->size + outFlowOldM + inFlowOldM) + plogp(mod_exit[newM] + mod_size[newM] + node[flip]->exit + node[flip]->size - outFlowNewM - inFlowNewM); double deltaL = delta_exit - 2.0*delta_exit_log_exit + delta_size_log_size; if (deltaL - best_delta < -1e-10) { bestM = newM; best_outFlow = outFlowNewM; best_inFlow = inFlowNewM; best_delta = deltaL; } } } // Make best possible move if (bestM != oldM) { //Update empty module vector if (mod_members[bestM] == 0) { Nempty--; } if (mod_members[oldM] == static_cast(node[flip]->members.size())) { mod_empty[Nempty] = oldM; Nempty++; } exitFlow -= mod_exit[oldM] + mod_exit[bestM]; exit_log_exit -= plogp(mod_exit[oldM]) + plogp(mod_exit[bestM]); size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) + plogp(mod_exit[bestM] + mod_size[bestM]); mod_exit[oldM] -= node[flip]->exit - outFlowOldM - inFlowOldM; mod_size[oldM] -= node[flip]->size; mod_danglingSize[oldM] -= node[flip]->danglingSize; mod_teleportWeight[oldM] -= node[flip]->teleportWeight; mod_members[oldM] -= node[flip]->members.size(); mod_exit[bestM] += node[flip]->exit - best_outFlow - best_inFlow; mod_size[bestM] += node[flip]->size; mod_danglingSize[bestM] += node[flip]->danglingSize; mod_teleportWeight[bestM] += node[flip]->teleportWeight; mod_members[bestM] += node[flip]->members.size(); exitFlow += mod_exit[oldM] + mod_exit[bestM]; // Update terms in map equation exit_log_exit += plogp(mod_exit[oldM]) + plogp(mod_exit[bestM]); size_log_size += plogp(mod_exit[oldM] + mod_size[oldM]) + plogp(mod_exit[bestM] + mod_size[bestM]); exit = plogp(exitFlow); // Update code length codeLength = exit - 2.0*exit_log_exit + size_log_size - nodeSize_log_nodeSize; node_index[flip] = bestM; moved = true; } offset += Nnode; } RNG_END(); return moved; } /** Apply the move to the given network */ void Greedy::apply(bool sort) { //void Greedy::level(Node ***node_tmp, bool sort) { //old fct prepare(sort) vector modSnode; // will give ids of no-empty modules (nodes) int Nmod = 0; if (sort) { multimap Msize; for (int i=0; i 0) { Nmod++; Msize.insert(pair(mod_size[i],i)); } } for (multimap::reverse_iterator it = Msize.rbegin(); it != Msize.rend(); it++) { modSnode.push_back(it->second); } } else { for (int i=0;i 0) { Nmod++; modSnode.push_back(i); } } } //modSnode[id_when_no_empty_node] = id_in_mod_tbl // Create the new graph FlowGraph * tmp_fgraph = new FlowGraph(Nmod); IGRAPH_FINALLY(delete_FlowGraph, tmp_fgraph); Node ** node_tmp = tmp_fgraph->node ; Node ** node = graph->node; vector nodeInMod = vector(Nnode); // creation of new nodes for (int i=0;i().swap(node_tmp[i]->members); // clear membership node_tmp[i]->exit = mod_exit[modSnode[i]]; node_tmp[i]->size = mod_size[modSnode[i]]; node_tmp[i]->danglingSize = mod_danglingSize[modSnode[i]]; node_tmp[i]->teleportWeight = mod_teleportWeight[modSnode[i]]; nodeInMod[modSnode[i]] = i; } //nodeInMode[id_in_mod_tbl] = id_when_no_empty_node // Calculate outflow of links to different modules vector > outFlowNtoM(Nmod); map::iterator it_M; for (int i=0;imembers.begin(), node[i]->members.end(), back_inserter( node_tmp[i_M]->members ) ); int NoutLinks = node[i]->outLinks.size(); for (int j=0; joutLinks[j].first; int nb_M = nodeInMod[node_index[nb]]; double nb_flow = node[i]->outLinks[j].second; if (nb != i) { it_M = outFlowNtoM[i_M].find(nb_M); if (it_M != outFlowNtoM[i_M].end()) { it_M->second += nb_flow; } else { outFlowNtoM[i_M].insert(make_pair(nb_M,nb_flow)); } } } } // Create outLinks at new level for (int i=0;ifirst != i) { node_tmp[i]->outLinks.push_back(make_pair(it_M->first,it_M->second)); } } } // Calculate inflow of links from different modules vector > inFlowNtoM(Nmod); for (int i=0;iinLinks.size(); for (int j=0;jinLinks[j].first; int nb_M = nodeInMod[node_index[nb]]; double nb_flow = node[i]->inLinks[j].second; if (nb != i) { it_M = inFlowNtoM[i_M].find(nb_M); if (it_M != inFlowNtoM[i_M].end()) { it_M->second += nb_flow; } else { inFlowNtoM[i_M].insert(make_pair(nb_M,nb_flow)); } } } } // Create inLinks at new level for (int i=0;ifirst != i) { node_tmp[i]->inLinks.push_back(make_pair(it_M->first,it_M->second)); } } } // Option to move to empty module vector().swap(mod_empty); Nempty = 0; //swap node between tmp_graph and graph, then destroy tmp_fgraph graph->swap(tmp_fgraph); Nnode = Nmod; delete tmp_fgraph; IGRAPH_FINALLY_CLEAN(1); } /** * RAZ et recalcul : * - mod_exit * - mod_size * - mod_danglingSize * - mod_teleportWeight * - mod_members * and * - exit_log_exit * - size_log_size * - exitFlow * - exit * - codeLength * according to **node / node[i]->index */ void Greedy::tune(void) { exit_log_exit = 0.0; size_log_size = 0.0; exitFlow = 0.0; for (int i=0;inode; // Update all values except contribution from teleportation for (int i=0; i < Nnode; i++) { int i_M = node_index[i]; // module id of node i int Nlinks = node[i]->outLinks.size(); mod_size[i_M] += node[i]->size; mod_danglingSize[i_M] += node[i]->danglingSize; mod_teleportWeight[i_M] += node[i]->teleportWeight; mod_members[i_M]++; for (int j=0;joutLinks[j].first; double neighbor_w = node[i]->outLinks[j].second; int neighbor_M = node_index[neighbor]; if (i_M != neighbor_M) // neighbor in an other module mod_exit[i_M] += neighbor_w; } } // Update contribution from teleportation for (int i=0;inode; //printf("setMove nNode:%d \n", Nnode); for (int i=0 ; i new : %d -> %d \n", oldM, newM); if (newM != oldM) { // Si je comprend bien : // outFlow... : c'est le "flow" de i-> autre sommet du meme module // inFlow... : c'est le "flow" depuis un autre sommet du meme module --> i double outFlowOldM = (alpha*node[i]->size + beta*node[i]->danglingSize)* (mod_teleportWeight[oldM]-node[i]->teleportWeight); double inFlowOldM = (alpha*(mod_size[oldM]-node[i]->size) + beta*(mod_danglingSize[oldM] - node[i]->danglingSize)) * node[i]->teleportWeight; double outFlowNewM = (alpha*node[i]->size + beta*node[i]->danglingSize) * mod_teleportWeight[newM]; double inFlowNewM = (alpha*mod_size[newM] + beta*mod_danglingSize[newM]) * node[i]->teleportWeight; // For all outLinks int NoutLinks = node[i]->outLinks.size(); for (int j=0; joutLinks[j].first]; double nb_flow = node[i]->outLinks[j].second; if (nb_M == oldM) { outFlowOldM += nb_flow; } else if (nb_M == newM) { outFlowNewM += nb_flow; } } // For all inLinks int NinLinks = node[i]->inLinks.size(); for (int j=0; jinLinks[j].first]; double nb_flow = node[i]->inLinks[j].second; if (nb_M == oldM) { inFlowOldM += nb_flow; } else if (nb_M == newM) { inFlowNewM += nb_flow; } } // Update empty module vector // RAZ de mod_empty et Nempty ds calibrate() if (mod_members[newM] == 0) { // si le nouveau etait vide, on a un vide de moins... Nempty--; } if (mod_members[oldM] == static_cast(node[i]->members.size())) { // si l'ancien avait la taille de celui qui bouge, un vide de plus mod_empty[Nempty] = oldM; Nempty++; } exitFlow -= mod_exit[oldM] + mod_exit[newM]; exit_log_exit -= plogp(mod_exit[oldM]) + plogp(mod_exit[newM]); size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) + plogp(mod_exit[newM] + mod_size[newM]); mod_exit[oldM] -= node[i]->exit - outFlowOldM - inFlowOldM; mod_size[oldM] -= node[i]->size; mod_danglingSize[oldM] -= node[i]->danglingSize; mod_teleportWeight[oldM] -= node[i]->teleportWeight; mod_members[oldM] -= node[i]->members.size(); mod_exit[newM] += node[i]->exit - outFlowNewM - inFlowNewM; mod_size[newM] += node[i]->size; mod_danglingSize[newM] += node[i]->danglingSize; mod_teleportWeight[newM] += node[i]->teleportWeight; mod_members[newM] += node[i]->members.size(); exitFlow += mod_exit[oldM] + mod_exit[newM]; exit_log_exit += plogp(mod_exit[oldM]) + plogp(mod_exit[newM]); size_log_size += plogp(mod_exit[oldM] + mod_size[oldM]) + plogp(mod_exit[newM] + mod_size[newM]); exit = plogp(exitFlow); codeLength = exit - 2.0*exit_log_exit + size_log_size - nodeSize_log_nodeSize; node_index[i] = newM; } } } igraph/src/stat.h0000644000176000001440000000171312325527074013500 0ustar ripleyusersc %--------------------------------% c | See stat.doc for documentation | c %--------------------------------% c c\SCCS Information: @(#) c FILE: stat.h SID: 2.2 DATE OF SID: 11/16/95 RELEASE: 2 c real t0, t1, t2, t3, t4, t5 c save t0, t1, t2, t3, t4, t5 c integer nopx, nbx, nrorth, nitref, nrstrt real tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, & tmvopx, tmvbx, tgetv0, titref, trvec common /timing/ & nopx, nbx, nrorth, nitref, nrstrt, & tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, & tmvopx, tmvbx, tgetv0, titref, trvec igraph/src/cs_malloc.c0000644000176000001440000000347212325527073014457 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma clang diagnostic ignored "-Wsign-conversion" #include "cs.h" #ifdef MATLAB_MEX_FILE #define malloc mxMalloc #define free mxFree #define realloc mxRealloc #define calloc mxCalloc #endif /* wrapper for malloc */ void *cs_malloc (CS_INT n, size_t size) { return (malloc (CS_MAX (n,1) * size)) ; } /* wrapper for calloc */ void *cs_calloc (CS_INT n, size_t size) { return (calloc (CS_MAX (n,1), size)) ; } /* wrapper for free */ void *cs_free (void *p) { if (p) free (p) ; /* free p if it is not already NULL */ return (NULL) ; /* return NULL to simplify the use of cs_free */ } /* wrapper for realloc */ void *cs_realloc (void *p, CS_INT n, size_t size, CS_INT *ok) { void *pnew ; pnew = realloc (p, CS_MAX (n,1) * size) ; /* realloc the block */ *ok = (pnew != NULL) ; /* realloc fails if pnew is NULL */ return ((*ok) ? pnew : p) ; /* return original p if failure */ } igraph/src/glpapi06.c0000644000176000001440000006405412325527073014150 0ustar ripleyusers/* glpapi06.c (simplex method routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wself-assign" #endif #include "glpios.h" #include "glpnpp.h" #include "glpspx.h" /*********************************************************************** * NAME * * glp_simplex - solve LP problem with the simplex method * * SYNOPSIS * * int glp_simplex(glp_prob *P, const glp_smcp *parm); * * DESCRIPTION * * The routine glp_simplex is a driver to the LP solver based on the * simplex method. This routine retrieves problem data from the * specified problem object, calls the solver to solve the problem * instance, and stores results of computations back into the problem * object. * * The simplex solver has a set of control parameters. Values of the * control parameters can be passed in a structure glp_smcp, which the * parameter parm points to. * * The parameter parm can be specified as NULL, in which case the LP * solver uses default settings. * * RETURNS * * 0 The LP problem instance has been successfully solved. This code * does not necessarily mean that the solver has found optimal * solution. It only means that the solution process was successful. * * GLP_EBADB * Unable to start the search, because the initial basis specified * in the problem object is invalid--the number of basic (auxiliary * and structural) variables is not the same as the number of rows in * the problem object. * * GLP_ESING * Unable to start the search, because the basis matrix correspodning * to the initial basis is singular within the working precision. * * GLP_ECOND * Unable to start the search, because the basis matrix correspodning * to the initial basis is ill-conditioned, i.e. its condition number * is too large. * * GLP_EBOUND * Unable to start the search, because some double-bounded variables * have incorrect bounds. * * GLP_EFAIL * The search was prematurely terminated due to the solver failure. * * GLP_EOBJLL * The search was prematurely terminated, because the objective * function being maximized has reached its lower limit and continues * decreasing (dual simplex only). * * GLP_EOBJUL * The search was prematurely terminated, because the objective * function being minimized has reached its upper limit and continues * increasing (dual simplex only). * * GLP_EITLIM * The search was prematurely terminated, because the simplex * iteration limit has been exceeded. * * GLP_ETMLIM * The search was prematurely terminated, because the time limit has * been exceeded. * * GLP_ENOPFS * The LP problem instance has no primal feasible solution (only if * the LP presolver is used). * * GLP_ENODFS * The LP problem instance has no dual feasible solution (only if the * LP presolver is used). */ static void trivial_lp(glp_prob *P, const glp_smcp *parm) { /* solve trivial LP which has empty constraint matrix */ GLPROW *row; GLPCOL *col; int i, j; double p_infeas, d_infeas, zeta; P->valid = 0; P->pbs_stat = P->dbs_stat = GLP_FEAS; P->obj_val = P->c0; P->some = 0; p_infeas = d_infeas = 0.0; /* make all auxiliary variables basic */ for (i = 1; i <= P->m; i++) { row = P->row[i]; row->stat = GLP_BS; row->prim = row->dual = 0.0; /* check primal feasibility */ if (row->type == GLP_LO || row->type == GLP_DB || row->type == GLP_FX) { /* row has lower bound */ if (row->lb > + parm->tol_bnd) { P->pbs_stat = GLP_NOFEAS; if (P->some == 0 && parm->meth != GLP_PRIMAL) P->some = i; } if (p_infeas < + row->lb) p_infeas = + row->lb; } if (row->type == GLP_UP || row->type == GLP_DB || row->type == GLP_FX) { /* row has upper bound */ if (row->ub < - parm->tol_bnd) { P->pbs_stat = GLP_NOFEAS; if (P->some == 0 && parm->meth != GLP_PRIMAL) P->some = i; } if (p_infeas < - row->ub) p_infeas = - row->ub; } } /* determine scale factor for the objective row */ zeta = 1.0; for (j = 1; j <= P->n; j++) { col = P->col[j]; if (zeta < fabs(col->coef)) zeta = fabs(col->coef); } zeta = (P->dir == GLP_MIN ? +1.0 : -1.0) / zeta; /* make all structural variables non-basic */ for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->type == GLP_FR) col->stat = GLP_NF, col->prim = 0.0; else if (col->type == GLP_LO) lo: col->stat = GLP_NL, col->prim = col->lb; else if (col->type == GLP_UP) up: col->stat = GLP_NU, col->prim = col->ub; else if (col->type == GLP_DB) { if (zeta * col->coef > 0.0) goto lo; else if (zeta * col->coef < 0.0) goto up; else if (fabs(col->lb) <= fabs(col->ub)) goto lo; else goto up; } else if (col->type == GLP_FX) col->stat = GLP_NS, col->prim = col->lb; col->dual = col->coef; P->obj_val += col->coef * col->prim; /* check dual feasibility */ if (col->type == GLP_FR || col->type == GLP_LO) { /* column has no upper bound */ if (zeta * col->dual < - parm->tol_dj) { P->dbs_stat = GLP_NOFEAS; if (P->some == 0 && parm->meth == GLP_PRIMAL) P->some = P->m + j; } if (d_infeas < - zeta * col->dual) d_infeas = - zeta * col->dual; } if (col->type == GLP_FR || col->type == GLP_UP) { /* column has no lower bound */ if (zeta * col->dual > + parm->tol_dj) { P->dbs_stat = GLP_NOFEAS; if (P->some == 0 && parm->meth == GLP_PRIMAL) P->some = P->m + j; } if (d_infeas < + zeta * col->dual) d_infeas = + zeta * col->dual; } } /* simulate the simplex solver output */ if (parm->msg_lev >= GLP_MSG_ON && parm->out_dly == 0) { xprintf("~%6d: obj = %17.9e infeas = %10.3e\n", P->it_cnt, P->obj_val, parm->meth == GLP_PRIMAL ? p_infeas : d_infeas); } if (parm->msg_lev >= GLP_MSG_ALL && parm->out_dly == 0) { if (P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS) xprintf("OPTIMAL SOLUTION FOUND\n"); else if (P->pbs_stat == GLP_NOFEAS) xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); else if (parm->meth == GLP_PRIMAL) xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n"); else xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n"); } return; } static int solve_lp(glp_prob *P, const glp_smcp *parm) { /* solve LP directly without using the preprocessor */ int ret; if (!glp_bf_exists(P)) { ret = glp_factorize(P); if (ret == 0) ; else if (ret == GLP_EBADB) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_simplex: initial basis is invalid\n"); } else if (ret == GLP_ESING) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_simplex: initial basis is singular\n"); } else if (ret == GLP_ECOND) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf( "glp_simplex: initial basis is ill-conditioned\n"); } else xassert(ret != ret); if (ret != 0) goto done; } if (parm->meth == GLP_PRIMAL) ret = spx_primal(P, parm); else if (parm->meth == GLP_DUALP) { ret = spx_dual(P, parm); if (ret == GLP_EFAIL && P->valid) ret = spx_primal(P, parm); } else if (parm->meth == GLP_DUAL) ret = spx_dual(P, parm); else xassert(parm != parm); done: return ret; } static int preprocess_and_solve_lp(glp_prob *P, const glp_smcp *parm) { /* solve LP using the preprocessor */ NPP *npp; glp_prob *lp = NULL; glp_bfcp bfcp; int ret; if (parm->msg_lev >= GLP_MSG_ALL) xprintf("Preprocessing...\n"); /* create preprocessor workspace */ npp = npp_create_wksp(); /* load original problem into the preprocessor workspace */ npp_load_prob(npp, P, GLP_OFF, GLP_SOL, GLP_OFF); /* process LP prior to applying primal/dual simplex method */ ret = npp_simplex(npp, parm); if (ret == 0) ; else if (ret == GLP_ENOPFS) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION\n"); } else if (ret == GLP_ENODFS) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n"); } else xassert(ret != ret); if (ret != 0) goto done; /* build transformed LP */ lp = glp_create_prob(); npp_build_prob(npp, lp); /* if the transformed LP is empty, it has empty solution, which is optimal */ if (lp->m == 0 && lp->n == 0) { lp->pbs_stat = lp->dbs_stat = GLP_FEAS; lp->obj_val = lp->c0; if (parm->msg_lev >= GLP_MSG_ON && parm->out_dly == 0) { xprintf("~%6d: obj = %17.9e infeas = %10.3e\n", P->it_cnt, lp->obj_val, 0.0); } if (parm->msg_lev >= GLP_MSG_ALL) xprintf("OPTIMAL SOLUTION FOUND BY LP PREPROCESSOR\n"); goto post; } if (parm->msg_lev >= GLP_MSG_ALL) { xprintf("%d row%s, %d column%s, %d non-zero%s\n", lp->m, lp->m == 1 ? "" : "s", lp->n, lp->n == 1 ? "" : "s", lp->nnz, lp->nnz == 1 ? "" : "s"); } /* inherit basis factorization control parameters */ glp_get_bfcp(P, &bfcp); glp_set_bfcp(lp, &bfcp); /* scale the transformed problem */ { ENV *env = get_env_ptr(); int term_out = env->term_out; if (!term_out || parm->msg_lev < GLP_MSG_ALL) env->term_out = GLP_OFF; else env->term_out = GLP_ON; glp_scale_prob(lp, GLP_SF_AUTO); env->term_out = term_out; } /* build advanced initial basis */ { ENV *env = get_env_ptr(); int term_out = env->term_out; if (!term_out || parm->msg_lev < GLP_MSG_ALL) env->term_out = GLP_OFF; else env->term_out = GLP_ON; glp_adv_basis(lp, 0); env->term_out = term_out; } /* solve the transformed LP */ lp->it_cnt = P->it_cnt; ret = solve_lp(lp, parm); P->it_cnt = lp->it_cnt; /* only optimal solution can be postprocessed */ if (!(ret == 0 && lp->pbs_stat == GLP_FEAS && lp->dbs_stat == GLP_FEAS)) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_simplex: unable to recover undefined or non-op" "timal solution\n"); if (ret == 0) { if (lp->pbs_stat == GLP_NOFEAS) ret = GLP_ENOPFS; else if (lp->dbs_stat == GLP_NOFEAS) ret = GLP_ENODFS; else xassert(lp != lp); } goto done; } post: /* postprocess solution from the transformed LP */ npp_postprocess(npp, lp); /* the transformed LP is no longer needed */ glp_delete_prob(lp), lp = NULL; /* store solution to the original problem */ npp_unload_sol(npp, P); /* the original LP has been successfully solved */ ret = 0; done: /* delete the transformed LP, if it exists */ if (lp != NULL) glp_delete_prob(lp); /* delete preprocessor workspace */ npp_delete_wksp(npp); return ret; } int glp_simplex(glp_prob *P, const glp_smcp *parm) { /* solve LP problem with the simplex method */ glp_smcp _parm; int i, j, ret; /* check problem object */ if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_simplex: P = %p; invalid problem object\n", P); if (P->tree != NULL && P->tree->reason != 0) xerror("glp_simplex: operation not allowed\n"); /* check control parameters */ if (parm == NULL) parm = &_parm, glp_init_smcp((glp_smcp *)parm); if (!(parm->msg_lev == GLP_MSG_OFF || parm->msg_lev == GLP_MSG_ERR || parm->msg_lev == GLP_MSG_ON || parm->msg_lev == GLP_MSG_ALL || parm->msg_lev == GLP_MSG_DBG)) xerror("glp_simplex: msg_lev = %d; invalid parameter\n", parm->msg_lev); if (!(parm->meth == GLP_PRIMAL || parm->meth == GLP_DUALP || parm->meth == GLP_DUAL)) xerror("glp_simplex: meth = %d; invalid parameter\n", parm->meth); if (!(parm->pricing == GLP_PT_STD || parm->pricing == GLP_PT_PSE)) xerror("glp_simplex: pricing = %d; invalid parameter\n", parm->pricing); if (!(parm->r_test == GLP_RT_STD || parm->r_test == GLP_RT_HAR)) xerror("glp_simplex: r_test = %d; invalid parameter\n", parm->r_test); if (!(0.0 < parm->tol_bnd && parm->tol_bnd < 1.0)) xerror("glp_simplex: tol_bnd = %g; invalid parameter\n", parm->tol_bnd); if (!(0.0 < parm->tol_dj && parm->tol_dj < 1.0)) xerror("glp_simplex: tol_dj = %g; invalid parameter\n", parm->tol_dj); if (!(0.0 < parm->tol_piv && parm->tol_piv < 1.0)) xerror("glp_simplex: tol_piv = %g; invalid parameter\n", parm->tol_piv); if (parm->it_lim < 0) xerror("glp_simplex: it_lim = %d; invalid parameter\n", parm->it_lim); if (parm->tm_lim < 0) xerror("glp_simplex: tm_lim = %d; invalid parameter\n", parm->tm_lim); if (parm->out_frq < 1) xerror("glp_simplex: out_frq = %d; invalid parameter\n", parm->out_frq); if (parm->out_dly < 0) xerror("glp_simplex: out_dly = %d; invalid parameter\n", parm->out_dly); if (!(parm->presolve == GLP_ON || parm->presolve == GLP_OFF)) xerror("glp_simplex: presolve = %d; invalid parameter\n", parm->presolve); /* basic solution is currently undefined */ P->pbs_stat = P->dbs_stat = GLP_UNDEF; P->obj_val = 0.0; P->some = 0; /* check bounds of double-bounded variables */ for (i = 1; i <= P->m; i++) { GLPROW *row = P->row[i]; if (row->type == GLP_DB && row->lb >= row->ub) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_simplex: row %d: lb = %g, ub = %g; incorrec" "t bounds\n", i, row->lb, row->ub); ret = GLP_EBOUND; goto done; } } for (j = 1; j <= P->n; j++) { GLPCOL *col = P->col[j]; if (col->type == GLP_DB && col->lb >= col->ub) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_simplex: column %d: lb = %g, ub = %g; incor" "rect bounds\n", j, col->lb, col->ub); ret = GLP_EBOUND; goto done; } } /* solve LP problem */ if (parm->msg_lev >= GLP_MSG_ALL) { xprintf("GLPK Simplex Optimizer, v%s\n", glp_version()); xprintf("%d row%s, %d column%s, %d non-zero%s\n", P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", P->nnz, P->nnz == 1 ? "" : "s"); } if (P->nnz == 0) trivial_lp(P, parm), ret = 0; else if (!parm->presolve) ret = solve_lp(P, parm); else ret = preprocess_and_solve_lp(P, parm); done: /* return to the application program */ return ret; } /*********************************************************************** * NAME * * glp_init_smcp - initialize simplex method control parameters * * SYNOPSIS * * void glp_init_smcp(glp_smcp *parm); * * DESCRIPTION * * The routine glp_init_smcp initializes control parameters, which are * used by the simplex solver, with default values. * * Default values of the control parameters are stored in a glp_smcp * structure, which the parameter parm points to. */ void glp_init_smcp(glp_smcp *parm) { parm->msg_lev = GLP_MSG_ALL; parm->meth = GLP_PRIMAL; parm->pricing = GLP_PT_PSE; parm->r_test = GLP_RT_HAR; parm->tol_bnd = 1e-7; parm->tol_dj = 1e-7; parm->tol_piv = 1e-10; parm->obj_ll = -DBL_MAX; parm->obj_ul = +DBL_MAX; parm->it_lim = INT_MAX; parm->tm_lim = INT_MAX; parm->out_frq = 500; parm->out_dly = 0; parm->presolve = GLP_OFF; return; } /*********************************************************************** * NAME * * glp_get_status - retrieve generic status of basic solution * * SYNOPSIS * * int glp_get_status(glp_prob *lp); * * RETURNS * * The routine glp_get_status reports the generic status of the basic * solution for the specified problem object as follows: * * GLP_OPT - solution is optimal; * GLP_FEAS - solution is feasible; * GLP_INFEAS - solution is infeasible; * GLP_NOFEAS - problem has no feasible solution; * GLP_UNBND - problem has unbounded solution; * GLP_UNDEF - solution is undefined. */ int glp_get_status(glp_prob *lp) { int status; status = glp_get_prim_stat(lp); switch (status) { case GLP_FEAS: switch (glp_get_dual_stat(lp)) { case GLP_FEAS: status = GLP_OPT; break; case GLP_NOFEAS: status = GLP_UNBND; break; case GLP_UNDEF: case GLP_INFEAS: status = status; break; default: xassert(lp != lp); } break; case GLP_UNDEF: case GLP_INFEAS: case GLP_NOFEAS: status = status; break; default: xassert(lp != lp); } return status; } /*********************************************************************** * NAME * * glp_get_prim_stat - retrieve status of primal basic solution * * SYNOPSIS * * int glp_get_prim_stat(glp_prob *lp); * * RETURNS * * The routine glp_get_prim_stat reports the status of the primal basic * solution for the specified problem object as follows: * * GLP_UNDEF - primal solution is undefined; * GLP_FEAS - primal solution is feasible; * GLP_INFEAS - primal solution is infeasible; * GLP_NOFEAS - no primal feasible solution exists. */ int glp_get_prim_stat(glp_prob *lp) { int pbs_stat = lp->pbs_stat; return pbs_stat; } /*********************************************************************** * NAME * * glp_get_dual_stat - retrieve status of dual basic solution * * SYNOPSIS * * int glp_get_dual_stat(glp_prob *lp); * * RETURNS * * The routine glp_get_dual_stat reports the status of the dual basic * solution for the specified problem object as follows: * * GLP_UNDEF - dual solution is undefined; * GLP_FEAS - dual solution is feasible; * GLP_INFEAS - dual solution is infeasible; * GLP_NOFEAS - no dual feasible solution exists. */ int glp_get_dual_stat(glp_prob *lp) { int dbs_stat = lp->dbs_stat; return dbs_stat; } /*********************************************************************** * NAME * * glp_get_obj_val - retrieve objective value (basic solution) * * SYNOPSIS * * double glp_get_obj_val(glp_prob *lp); * * RETURNS * * The routine glp_get_obj_val returns value of the objective function * for basic solution. */ double glp_get_obj_val(glp_prob *lp) { /*struct LPXCPS *cps = lp->cps;*/ double z; z = lp->obj_val; /*if (cps->round && fabs(z) < 1e-9) z = 0.0;*/ return z; } /*********************************************************************** * NAME * * glp_get_row_stat - retrieve row status * * SYNOPSIS * * int glp_get_row_stat(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_stat returns current status assigned to the * auxiliary variable associated with i-th row as follows: * * GLP_BS - basic variable; * GLP_NL - non-basic variable on its lower bound; * GLP_NU - non-basic variable on its upper bound; * GLP_NF - non-basic free (unbounded) variable; * GLP_NS - non-basic fixed variable. */ int glp_get_row_stat(glp_prob *lp, int i) { if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_stat: i = %d; row number out of range\n", i); return lp->row[i]->stat; } /*********************************************************************** * NAME * * glp_get_row_prim - retrieve row primal value (basic solution) * * SYNOPSIS * * double glp_get_row_prim(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_prim returns primal value of the auxiliary * variable associated with i-th row. */ double glp_get_row_prim(glp_prob *lp, int i) { /*struct LPXCPS *cps = lp->cps;*/ double prim; if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_prim: i = %d; row number out of range\n", i); prim = lp->row[i]->prim; /*if (cps->round && fabs(prim) < 1e-9) prim = 0.0;*/ return prim; } /*********************************************************************** * NAME * * glp_get_row_dual - retrieve row dual value (basic solution) * * SYNOPSIS * * double glp_get_row_dual(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_dual returns dual value (i.e. reduced cost) * of the auxiliary variable associated with i-th row. */ double glp_get_row_dual(glp_prob *lp, int i) { /*struct LPXCPS *cps = lp->cps;*/ double dual; if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_dual: i = %d; row number out of range\n", i); dual = lp->row[i]->dual; /*if (cps->round && fabs(dual) < 1e-9) dual = 0.0;*/ return dual; } /*********************************************************************** * NAME * * glp_get_col_stat - retrieve column status * * SYNOPSIS * * int glp_get_col_stat(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_stat returns current status assigned to the * structural variable associated with j-th column as follows: * * GLP_BS - basic variable; * GLP_NL - non-basic variable on its lower bound; * GLP_NU - non-basic variable on its upper bound; * GLP_NF - non-basic free (unbounded) variable; * GLP_NS - non-basic fixed variable. */ int glp_get_col_stat(glp_prob *lp, int j) { if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_stat: j = %d; column number out of range\n" , j); return lp->col[j]->stat; } /*********************************************************************** * NAME * * glp_get_col_prim - retrieve column primal value (basic solution) * * SYNOPSIS * * double glp_get_col_prim(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_prim returns primal value of the structural * variable associated with j-th column. */ double glp_get_col_prim(glp_prob *lp, int j) { /*struct LPXCPS *cps = lp->cps;*/ double prim; if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_prim: j = %d; column number out of range\n" , j); prim = lp->col[j]->prim; /*if (cps->round && fabs(prim) < 1e-9) prim = 0.0;*/ return prim; } /*********************************************************************** * NAME * * glp_get_col_dual - retrieve column dual value (basic solution) * * SYNOPSIS * * double glp_get_col_dual(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_dual returns dual value (i.e. reduced cost) * of the structural variable associated with j-th column. */ double glp_get_col_dual(glp_prob *lp, int j) { /*struct LPXCPS *cps = lp->cps;*/ double dual; if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_dual: j = %d; column number out of range\n" , j); dual = lp->col[j]->dual; /*if (cps->round && fabs(dual) < 1e-9) dual = 0.0;*/ return dual; } /*********************************************************************** * NAME * * glp_get_unbnd_ray - determine variable causing unboundedness * * SYNOPSIS * * int glp_get_unbnd_ray(glp_prob *lp); * * RETURNS * * The routine glp_get_unbnd_ray returns the number k of a variable, * which causes primal or dual unboundedness. If 1 <= k <= m, it is * k-th auxiliary variable, and if m+1 <= k <= m+n, it is (k-m)-th * structural variable, where m is the number of rows, n is the number * of columns in the problem object. If such variable is not defined, * the routine returns 0. * * COMMENTS * * If it is not exactly known which version of the simplex solver * detected unboundedness, i.e. whether the unboundedness is primal or * dual, it is sufficient to check the status of the variable reported * with the routine glp_get_row_stat or glp_get_col_stat. If the * variable is non-basic, the unboundedness is primal, otherwise, if * the variable is basic, the unboundedness is dual (the latter case * means that the problem has no primal feasible dolution). */ int glp_get_unbnd_ray(glp_prob *lp) { int k; k = lp->some; xassert(k >= 0); if (k > lp->m + lp->n) k = 0; return k; } /* eof */ igraph/src/Makevars.in0000644000176000001440000000060612325372070014447 0ustar ripleyusersPKG_CFLAGS=-DUSING_R -I. -Ics -Iglpk -Iglpk/amd -Iglpk/colamd -Iplfit \ @CPPFLAGS@ @CFLAGS@ -DNDEBUG \ -DPACKAGE_VERSION=\"@PACKAGE_VERSION@\" -DINTERNAL_ARPACK \ -DIGRAPH_THREAD_LOCAL=/**/ PKG_CXXFLAGS= -DUSING_R -DIGRAPH_THREAD_LOCAL=/**/ -DNDEBUG -Iprpack -I. \ -DPRPACK_IGRAPH_SUPPORT PKG_LIBS=@XML2_LIBS@ @GMP_LIBS@ @GLPK_LIBS@ $(FLIBS) $(LAPACK_LIBS) $(BLAS_LIBS) all: $(SHLIB) igraph/src/amd_defaults.c0000644000176000001440000000257312325527072015153 0ustar ripleyusers/* ========================================================================= */ /* === AMD_defaults ======================================================== */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* User-callable. Sets default control parameters for AMD. See amd.h * for details. */ #include "amd_internal.h" /* ========================================================================= */ /* === AMD defaults ======================================================== */ /* ========================================================================= */ GLOBAL void AMD_defaults ( double Control [ ] ) { Int i ; if (Control != (double *) NULL) { for (i = 0 ; i < AMD_CONTROL ; i++) { Control [i] = 0 ; } Control [AMD_DENSE] = AMD_DEFAULT_DENSE ; Control [AMD_AGGRESSIVE] = AMD_DEFAULT_AGGRESSIVE ; } } igraph/src/glplpx01.c0000644000176000001440000013770512325527073014201 0ustar ripleyusers/* glplpx01.c (obsolete API routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpapi.h" struct LPXCPS { /* control parameters and statistics */ int msg_lev; /* level of messages output by the solver: 0 - no output 1 - error messages only 2 - normal output 3 - full output (includes informational messages) */ int scale; /* scaling option: 0 - no scaling 1 - equilibration scaling 2 - geometric mean scaling 3 - geometric mean scaling, then equilibration scaling */ int dual; /* dual simplex option: 0 - use primal simplex 1 - use dual simplex */ int price; /* pricing option (for both primal and dual simplex): 0 - textbook pricing 1 - steepest edge pricing */ double relax; /* relaxation parameter used in the ratio test; if it is zero, the textbook ratio test is used; if it is non-zero (should be positive), Harris' two-pass ratio test is used; in the latter case on the first pass basic variables (in the case of primal simplex) or reduced costs of non-basic variables (in the case of dual simplex) are allowed to slightly violate their bounds, but not more than (relax * tol_bnd) or (relax * tol_dj) (thus, relax is a percentage of tol_bnd or tol_dj) */ double tol_bnd; /* relative tolerance used to check if the current basic solution is primal feasible */ double tol_dj; /* absolute tolerance used to check if the current basic solution is dual feasible */ double tol_piv; /* relative tolerance used to choose eligible pivotal elements of the simplex table in the ratio test */ int round; /* solution rounding option: 0 - report all computed values and reduced costs "as is" 1 - if possible (allowed by the tolerances), replace computed values and reduced costs which are close to zero by exact zeros */ double obj_ll; /* lower limit of the objective function; if on the phase II the objective function reaches this limit and continues decreasing, the solver stops the search */ double obj_ul; /* upper limit of the objective function; if on the phase II the objective function reaches this limit and continues increasing, the solver stops the search */ int it_lim; /* simplex iterations limit; if this value is positive, it is decreased by one each time when one simplex iteration has been performed, and reaching zero value signals the solver to stop the search; negative value means no iterations limit */ double tm_lim; /* searching time limit, in seconds; if this value is positive, it is decreased each time when one simplex iteration has been performed by the amount of time spent for the iteration, and reaching zero value signals the solver to stop the search; negative value means no time limit */ int out_frq; /* output frequency, in iterations; this parameter specifies how frequently the solver sends information about the solution to the standard output */ double out_dly; /* output delay, in seconds; this parameter specifies how long the solver should delay sending information about the solution to the standard output; zero value means no delay */ int branch; /* MIP */ /* branching heuristic: 0 - branch on first variable 1 - branch on last variable 2 - branch using heuristic by Driebeck and Tomlin 3 - branch on most fractional variable */ int btrack; /* MIP */ /* backtracking heuristic: 0 - select most recent node (depth first search) 1 - select earliest node (breadth first search) 2 - select node using the best projection heuristic 3 - select node with best local bound */ double tol_int; /* MIP */ /* absolute tolerance used to check if the current basic solution is integer feasible */ double tol_obj; /* MIP */ /* relative tolerance used to check if the value of the objective function is not better than in the best known integer feasible solution */ int mps_info; /* lpx_write_mps */ /* if this flag is set, the routine lpx_write_mps outputs several comment cards that contains some information about the problem; otherwise the routine outputs no comment cards */ int mps_obj; /* lpx_write_mps */ /* this parameter tells the routine lpx_write_mps how to output the objective function row: 0 - never output objective function row 1 - always output objective function row 2 - output objective function row if and only if the problem has no free rows */ int mps_orig; /* lpx_write_mps */ /* if this flag is set, the routine lpx_write_mps uses original row and column symbolic names; otherwise the routine generates plain names using ordinal numbers of rows and columns */ int mps_wide; /* lpx_write_mps */ /* if this flag is set, the routine lpx_write_mps uses all data fields; otherwise the routine keeps fields 5 and 6 empty */ int mps_free; /* lpx_write_mps */ /* if this flag is set, the routine lpx_write_mps omits column and vector names everytime if possible (free style); otherwise the routine never omits these names (pedantic style) */ int mps_skip; /* lpx_write_mps */ /* if this flag is set, the routine lpx_write_mps skips empty columns (i.e. which has no constraint coefficients); otherwise the routine outputs all columns */ int lpt_orig; /* lpx_write_lpt */ /* if this flag is set, the routine lpx_write_lpt uses original row and column symbolic names; otherwise the routine generates plain names using ordinal numbers of rows and columns */ int presol; /* lpx_simplex */ /* LP presolver option: 0 - do not use LP presolver 1 - use LP presolver */ int binarize; /* lpx_intopt */ /* if this flag is set, the routine lpx_intopt replaces integer columns by binary ones */ int use_cuts; /* lpx_intopt */ /* if this flag is set, the routine lpx_intopt tries generating cutting planes: LPX_C_COVER - mixed cover cuts LPX_C_CLIQUE - clique cuts LPX_C_GOMORY - Gomory's mixed integer cuts LPX_C_ALL - all cuts */ double mip_gap; /* MIP */ /* relative MIP gap tolerance */ }; LPX *lpx_create_prob(void) { /* create problem object */ return glp_create_prob(); } void lpx_set_prob_name(LPX *lp, const char *name) { /* assign (change) problem name */ glp_set_prob_name(lp, name); return; } void lpx_set_obj_name(LPX *lp, const char *name) { /* assign (change) objective function name */ glp_set_obj_name(lp, name); return; } void lpx_set_obj_dir(LPX *lp, int dir) { /* set (change) optimization direction flag */ glp_set_obj_dir(lp, dir - LPX_MIN + GLP_MIN); return; } int lpx_add_rows(LPX *lp, int nrs) { /* add new rows to problem object */ return glp_add_rows(lp, nrs); } int lpx_add_cols(LPX *lp, int ncs) { /* add new columns to problem object */ return glp_add_cols(lp, ncs); } void lpx_set_row_name(LPX *lp, int i, const char *name) { /* assign (change) row name */ glp_set_row_name(lp, i, name); return; } void lpx_set_col_name(LPX *lp, int j, const char *name) { /* assign (change) column name */ glp_set_col_name(lp, j, name); return; } void lpx_set_row_bnds(LPX *lp, int i, int type, double lb, double ub) { /* set (change) row bounds */ glp_set_row_bnds(lp, i, type - LPX_FR + GLP_FR, lb, ub); return; } void lpx_set_col_bnds(LPX *lp, int j, int type, double lb, double ub) { /* set (change) column bounds */ glp_set_col_bnds(lp, j, type - LPX_FR + GLP_FR, lb, ub); return; } void lpx_set_obj_coef(glp_prob *lp, int j, double coef) { /* set (change) obj. coefficient or constant term */ glp_set_obj_coef(lp, j, coef); return; } void lpx_set_mat_row(LPX *lp, int i, int len, const int ind[], const double val[]) { /* set (replace) row of the constraint matrix */ glp_set_mat_row(lp, i, len, ind, val); return; } void lpx_set_mat_col(LPX *lp, int j, int len, const int ind[], const double val[]) { /* set (replace) column of the constraint matrix */ glp_set_mat_col(lp, j, len, ind, val); return; } void lpx_load_matrix(LPX *lp, int ne, const int ia[], const int ja[], const double ar[]) { /* load (replace) the whole constraint matrix */ glp_load_matrix(lp, ne, ia, ja, ar); return; } void lpx_del_rows(LPX *lp, int nrs, const int num[]) { /* delete specified rows from problem object */ glp_del_rows(lp, nrs, num); return; } void lpx_del_cols(LPX *lp, int ncs, const int num[]) { /* delete specified columns from problem object */ glp_del_cols(lp, ncs, num); return; } void lpx_delete_prob(LPX *lp) { /* delete problem object */ glp_delete_prob(lp); return; } const char *lpx_get_prob_name(LPX *lp) { /* retrieve problem name */ return glp_get_prob_name(lp); } const char *lpx_get_obj_name(LPX *lp) { /* retrieve objective function name */ return glp_get_obj_name(lp); } int lpx_get_obj_dir(LPX *lp) { /* retrieve optimization direction flag */ return glp_get_obj_dir(lp) - GLP_MIN + LPX_MIN; } int lpx_get_num_rows(LPX *lp) { /* retrieve number of rows */ return glp_get_num_rows(lp); } int lpx_get_num_cols(LPX *lp) { /* retrieve number of columns */ return glp_get_num_cols(lp); } const char *lpx_get_row_name(LPX *lp, int i) { /* retrieve row name */ return glp_get_row_name(lp, i); } const char *lpx_get_col_name(LPX *lp, int j) { /* retrieve column name */ return glp_get_col_name(lp, j); } int lpx_get_row_type(LPX *lp, int i) { /* retrieve row type */ return glp_get_row_type(lp, i) - GLP_FR + LPX_FR; } double lpx_get_row_lb(glp_prob *lp, int i) { /* retrieve row lower bound */ double lb; lb = glp_get_row_lb(lp, i); if (lb == -DBL_MAX) lb = 0.0; return lb; } double lpx_get_row_ub(glp_prob *lp, int i) { /* retrieve row upper bound */ double ub; ub = glp_get_row_ub(lp, i); if (ub == +DBL_MAX) ub = 0.0; return ub; } void lpx_get_row_bnds(glp_prob *lp, int i, int *typx, double *lb, double *ub) { /* retrieve row bounds */ if (typx != NULL) *typx = lpx_get_row_type(lp, i); if (lb != NULL) *lb = lpx_get_row_lb(lp, i); if (ub != NULL) *ub = lpx_get_row_ub(lp, i); return; } int lpx_get_col_type(LPX *lp, int j) { /* retrieve column type */ return glp_get_col_type(lp, j) - GLP_FR + LPX_FR; } double lpx_get_col_lb(glp_prob *lp, int j) { /* retrieve column lower bound */ double lb; lb = glp_get_col_lb(lp, j); if (lb == -DBL_MAX) lb = 0.0; return lb; } double lpx_get_col_ub(glp_prob *lp, int j) { /* retrieve column upper bound */ double ub; ub = glp_get_col_ub(lp, j); if (ub == +DBL_MAX) ub = 0.0; return ub; } void lpx_get_col_bnds(glp_prob *lp, int j, int *typx, double *lb, double *ub) { /* retrieve column bounds */ if (typx != NULL) *typx = lpx_get_col_type(lp, j); if (lb != NULL) *lb = lpx_get_col_lb(lp, j); if (ub != NULL) *ub = lpx_get_col_ub(lp, j); return; } double lpx_get_obj_coef(LPX *lp, int j) { /* retrieve obj. coefficient or constant term */ return glp_get_obj_coef(lp, j); } int lpx_get_num_nz(LPX *lp) { /* retrieve number of constraint coefficients */ return glp_get_num_nz(lp); } int lpx_get_mat_row(LPX *lp, int i, int ind[], double val[]) { /* retrieve row of the constraint matrix */ return glp_get_mat_row(lp, i, ind, val); } int lpx_get_mat_col(LPX *lp, int j, int ind[], double val[]) { /* retrieve column of the constraint matrix */ return glp_get_mat_col(lp, j, ind, val); } void lpx_create_index(LPX *lp) { /* create the name index */ glp_create_index(lp); return; } int lpx_find_row(LPX *lp, const char *name) { /* find row by its name */ return glp_find_row(lp, name); } int lpx_find_col(LPX *lp, const char *name) { /* find column by its name */ return glp_find_col(lp, name); } void lpx_delete_index(LPX *lp) { /* delete the name index */ glp_delete_index(lp); return; } void lpx_scale_prob(LPX *lp) { /* scale problem data */ switch (lpx_get_int_parm(lp, LPX_K_SCALE)) { case 0: /* no scaling */ glp_unscale_prob(lp); break; case 1: /* equilibration scaling */ glp_scale_prob(lp, GLP_SF_EQ); break; case 2: /* geometric mean scaling */ glp_scale_prob(lp, GLP_SF_GM); break; case 3: /* geometric mean scaling, then equilibration scaling */ glp_scale_prob(lp, GLP_SF_GM | GLP_SF_EQ); break; default: xassert(lp != lp); } return; } void lpx_unscale_prob(LPX *lp) { /* unscale problem data */ glp_unscale_prob(lp); return; } void lpx_set_row_stat(LPX *lp, int i, int stat) { /* set (change) row status */ glp_set_row_stat(lp, i, stat - LPX_BS + GLP_BS); return; } void lpx_set_col_stat(LPX *lp, int j, int stat) { /* set (change) column status */ glp_set_col_stat(lp, j, stat - LPX_BS + GLP_BS); return; } void lpx_std_basis(LPX *lp) { /* construct standard initial LP basis */ glp_std_basis(lp); return; } void lpx_adv_basis(LPX *lp) { /* construct advanced initial LP basis */ glp_adv_basis(lp, 0); return; } void lpx_cpx_basis(LPX *lp) { /* construct Bixby's initial LP basis */ glp_cpx_basis(lp); return; } static void fill_smcp(LPX *lp, glp_smcp *parm) { glp_init_smcp(parm); switch (lpx_get_int_parm(lp, LPX_K_MSGLEV)) { case 0: parm->msg_lev = GLP_MSG_OFF; break; case 1: parm->msg_lev = GLP_MSG_ERR; break; case 2: parm->msg_lev = GLP_MSG_ON; break; case 3: parm->msg_lev = GLP_MSG_ALL; break; default: xassert(lp != lp); } switch (lpx_get_int_parm(lp, LPX_K_DUAL)) { case 0: parm->meth = GLP_PRIMAL; break; case 1: parm->meth = GLP_DUAL; break; default: xassert(lp != lp); } switch (lpx_get_int_parm(lp, LPX_K_PRICE)) { case 0: parm->pricing = GLP_PT_STD; break; case 1: parm->pricing = GLP_PT_PSE; break; default: xassert(lp != lp); } if (lpx_get_real_parm(lp, LPX_K_RELAX) == 0.0) parm->r_test = GLP_RT_STD; else parm->r_test = GLP_RT_HAR; parm->tol_bnd = lpx_get_real_parm(lp, LPX_K_TOLBND); parm->tol_dj = lpx_get_real_parm(lp, LPX_K_TOLDJ); parm->tol_piv = lpx_get_real_parm(lp, LPX_K_TOLPIV); parm->obj_ll = lpx_get_real_parm(lp, LPX_K_OBJLL); parm->obj_ul = lpx_get_real_parm(lp, LPX_K_OBJUL); if (lpx_get_int_parm(lp, LPX_K_ITLIM) < 0) parm->it_lim = INT_MAX; else parm->it_lim = lpx_get_int_parm(lp, LPX_K_ITLIM); if (lpx_get_real_parm(lp, LPX_K_TMLIM) < 0.0) parm->tm_lim = INT_MAX; else parm->tm_lim = (int)(1000.0 * lpx_get_real_parm(lp, LPX_K_TMLIM)); parm->out_frq = lpx_get_int_parm(lp, LPX_K_OUTFRQ); parm->out_dly = (int)(1000.0 * lpx_get_real_parm(lp, LPX_K_OUTDLY)); switch (lpx_get_int_parm(lp, LPX_K_PRESOL)) { case 0: parm->presolve = GLP_OFF; break; case 1: parm->presolve = GLP_ON; break; default: xassert(lp != lp); } return; } int lpx_simplex(LPX *lp) { /* easy-to-use driver to the simplex method */ glp_smcp parm; int ret; fill_smcp(lp, &parm); ret = glp_simplex(lp, &parm); switch (ret) { case 0: ret = LPX_E_OK; break; case GLP_EBADB: case GLP_ESING: case GLP_ECOND: case GLP_EBOUND: ret = LPX_E_FAULT; break; case GLP_EFAIL: ret = LPX_E_SING; break; case GLP_EOBJLL: ret = LPX_E_OBJLL; break; case GLP_EOBJUL: ret = LPX_E_OBJUL; break; case GLP_EITLIM: ret = LPX_E_ITLIM; break; case GLP_ETMLIM: ret = LPX_E_TMLIM; break; case GLP_ENOPFS: ret = LPX_E_NOPFS; break; case GLP_ENODFS: ret = LPX_E_NODFS; break; default: xassert(ret != ret); } return ret; } int lpx_exact(LPX *lp) { /* easy-to-use driver to the exact simplex method */ glp_smcp parm; int ret; fill_smcp(lp, &parm); ret = glp_exact(lp, &parm); switch (ret) { case 0: ret = LPX_E_OK; break; case GLP_EBADB: case GLP_ESING: case GLP_EBOUND: case GLP_EFAIL: ret = LPX_E_FAULT; break; case GLP_EITLIM: ret = LPX_E_ITLIM; break; case GLP_ETMLIM: ret = LPX_E_TMLIM; break; default: xassert(ret != ret); } return ret; } int lpx_get_status(glp_prob *lp) { /* retrieve generic status of basic solution */ int status; switch (glp_get_status(lp)) { case GLP_OPT: status = LPX_OPT; break; case GLP_FEAS: status = LPX_FEAS; break; case GLP_INFEAS: status = LPX_INFEAS; break; case GLP_NOFEAS: status = LPX_NOFEAS; break; case GLP_UNBND: status = LPX_UNBND; break; case GLP_UNDEF: status = LPX_UNDEF; break; default: xassert(lp != lp); } return status; } int lpx_get_prim_stat(glp_prob *lp) { /* retrieve status of primal basic solution */ return glp_get_prim_stat(lp) - GLP_UNDEF + LPX_P_UNDEF; } int lpx_get_dual_stat(glp_prob *lp) { /* retrieve status of dual basic solution */ return glp_get_dual_stat(lp) - GLP_UNDEF + LPX_D_UNDEF; } double lpx_get_obj_val(LPX *lp) { /* retrieve objective value (basic solution) */ return glp_get_obj_val(lp); } int lpx_get_row_stat(LPX *lp, int i) { /* retrieve row status (basic solution) */ return glp_get_row_stat(lp, i) - GLP_BS + LPX_BS; } double lpx_get_row_prim(LPX *lp, int i) { /* retrieve row primal value (basic solution) */ return glp_get_row_prim(lp, i); } double lpx_get_row_dual(LPX *lp, int i) { /* retrieve row dual value (basic solution) */ return glp_get_row_dual(lp, i); } void lpx_get_row_info(glp_prob *lp, int i, int *tagx, double *vx, double *dx) { /* obtain row solution information */ if (tagx != NULL) *tagx = lpx_get_row_stat(lp, i); if (vx != NULL) *vx = lpx_get_row_prim(lp, i); if (dx != NULL) *dx = lpx_get_row_dual(lp, i); return; } int lpx_get_col_stat(LPX *lp, int j) { /* retrieve column status (basic solution) */ return glp_get_col_stat(lp, j) - GLP_BS + LPX_BS; } double lpx_get_col_prim(LPX *lp, int j) { /* retrieve column primal value (basic solution) */ return glp_get_col_prim(lp, j); } double lpx_get_col_dual(glp_prob *lp, int j) { /* retrieve column dual value (basic solution) */ return glp_get_col_dual(lp, j); } void lpx_get_col_info(glp_prob *lp, int j, int *tagx, double *vx, double *dx) { /* obtain column solution information */ if (tagx != NULL) *tagx = lpx_get_col_stat(lp, j); if (vx != NULL) *vx = lpx_get_col_prim(lp, j); if (dx != NULL) *dx = lpx_get_col_dual(lp, j); return; } int lpx_get_ray_info(LPX *lp) { /* determine what causes primal unboundness */ return glp_get_unbnd_ray(lp); } void lpx_check_kkt(LPX *lp, int scaled, LPXKKT *kkt) { /* check Karush-Kuhn-Tucker conditions */ int ae_ind, re_ind; double ae_max, re_max; xassert(scaled == scaled); _glp_check_kkt(lp, GLP_SOL, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, &re_ind); kkt->pe_ae_max = ae_max; kkt->pe_ae_row = ae_ind; kkt->pe_re_max = re_max; kkt->pe_re_row = re_ind; if (re_max <= 1e-9) kkt->pe_quality = 'H'; else if (re_max <= 1e-6) kkt->pe_quality = 'M'; else if (re_max <= 1e-3) kkt->pe_quality = 'L'; else kkt->pe_quality = '?'; _glp_check_kkt(lp, GLP_SOL, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, &re_ind); kkt->pb_ae_max = ae_max; kkt->pb_ae_ind = ae_ind; kkt->pb_re_max = re_max; kkt->pb_re_ind = re_ind; if (re_max <= 1e-9) kkt->pb_quality = 'H'; else if (re_max <= 1e-6) kkt->pb_quality = 'M'; else if (re_max <= 1e-3) kkt->pb_quality = 'L'; else kkt->pb_quality = '?'; _glp_check_kkt(lp, GLP_SOL, GLP_KKT_DE, &ae_max, &ae_ind, &re_max, &re_ind); kkt->de_ae_max = ae_max; if (ae_ind == 0) kkt->de_ae_col = 0; else kkt->de_ae_col = ae_ind - lp->m; kkt->de_re_max = re_max; if (re_ind == 0) kkt->de_re_col = 0; else kkt->de_re_col = ae_ind - lp->m; if (re_max <= 1e-9) kkt->de_quality = 'H'; else if (re_max <= 1e-6) kkt->de_quality = 'M'; else if (re_max <= 1e-3) kkt->de_quality = 'L'; else kkt->de_quality = '?'; _glp_check_kkt(lp, GLP_SOL, GLP_KKT_DB, &ae_max, &ae_ind, &re_max, &re_ind); kkt->db_ae_max = ae_max; kkt->db_ae_ind = ae_ind; kkt->db_re_max = re_max; kkt->db_re_ind = re_ind; if (re_max <= 1e-9) kkt->db_quality = 'H'; else if (re_max <= 1e-6) kkt->db_quality = 'M'; else if (re_max <= 1e-3) kkt->db_quality = 'L'; else kkt->db_quality = '?'; kkt->cs_ae_max = 0.0, kkt->cs_ae_ind = 0; kkt->cs_re_max = 0.0, kkt->cs_re_ind = 0; kkt->cs_quality = 'H'; return; } int lpx_warm_up(LPX *lp) { /* "warm up" LP basis */ int ret; ret = glp_warm_up(lp); if (ret == 0) ret = LPX_E_OK; else if (ret == GLP_EBADB) ret = LPX_E_BADB; else if (ret == GLP_ESING) ret = LPX_E_SING; else if (ret == GLP_ECOND) ret = LPX_E_SING; else xassert(ret != ret); return ret; } int lpx_eval_tab_row(LPX *lp, int k, int ind[], double val[]) { /* compute row of the simplex tableau */ return glp_eval_tab_row(lp, k, ind, val); } int lpx_eval_tab_col(LPX *lp, int k, int ind[], double val[]) { /* compute column of the simplex tableau */ return glp_eval_tab_col(lp, k, ind, val); } int lpx_transform_row(LPX *lp, int len, int ind[], double val[]) { /* transform explicitly specified row */ return glp_transform_row(lp, len, ind, val); } int lpx_transform_col(LPX *lp, int len, int ind[], double val[]) { /* transform explicitly specified column */ return glp_transform_col(lp, len, ind, val); } int lpx_prim_ratio_test(LPX *lp, int len, const int ind[], const double val[], int how, double tol) { /* perform primal ratio test */ int piv; piv = glp_prim_rtest(lp, len, ind, val, how, tol); xassert(0 <= piv && piv <= len); return piv == 0 ? 0 : ind[piv]; } int lpx_dual_ratio_test(LPX *lp, int len, const int ind[], const double val[], int how, double tol) { /* perform dual ratio test */ int piv; piv = glp_dual_rtest(lp, len, ind, val, how, tol); xassert(0 <= piv && piv <= len); return piv == 0 ? 0 : ind[piv]; } int lpx_interior(LPX *lp) { /* easy-to-use driver to the interior-point method */ int ret; ret = glp_interior(lp, NULL); switch (ret) { case 0: ret = LPX_E_OK; break; case GLP_EFAIL: ret = LPX_E_FAULT; break; case GLP_ENOFEAS: ret = LPX_E_NOFEAS; break; case GLP_ENOCVG: ret = LPX_E_NOCONV; break; case GLP_EITLIM: ret = LPX_E_ITLIM; break; case GLP_EINSTAB: ret = LPX_E_INSTAB; break; default: xassert(ret != ret); } return ret; } int lpx_ipt_status(glp_prob *lp) { /* retrieve status of interior-point solution */ int status; switch (glp_ipt_status(lp)) { case GLP_UNDEF: status = LPX_T_UNDEF; break; case GLP_OPT: status = LPX_T_OPT; break; default: xassert(lp != lp); } return status; } double lpx_ipt_obj_val(LPX *lp) { /* retrieve objective value (interior point) */ return glp_ipt_obj_val(lp); } double lpx_ipt_row_prim(LPX *lp, int i) { /* retrieve row primal value (interior point) */ return glp_ipt_row_prim(lp, i); } double lpx_ipt_row_dual(LPX *lp, int i) { /* retrieve row dual value (interior point) */ return glp_ipt_row_dual(lp, i); } double lpx_ipt_col_prim(LPX *lp, int j) { /* retrieve column primal value (interior point) */ return glp_ipt_col_prim(lp, j); } double lpx_ipt_col_dual(LPX *lp, int j) { /* retrieve column dual value (interior point) */ return glp_ipt_col_dual(lp, j); } void lpx_set_class(LPX *lp, int klass) { /* set problem class */ xassert(lp == lp); if (!(klass == LPX_LP || klass == LPX_MIP)) xerror("lpx_set_class: invalid problem class\n"); return; } int lpx_get_class(LPX *lp) { /* determine problem klass */ return glp_get_num_int(lp) == 0 ? LPX_LP : LPX_MIP; } void lpx_set_col_kind(LPX *lp, int j, int kind) { /* set (change) column kind */ glp_set_col_kind(lp, j, kind - LPX_CV + GLP_CV); return; } int lpx_get_col_kind(LPX *lp, int j) { /* retrieve column kind */ return glp_get_col_kind(lp, j) == GLP_CV ? LPX_CV : LPX_IV; } int lpx_get_num_int(LPX *lp) { /* retrieve number of integer columns */ return glp_get_num_int(lp); } int lpx_get_num_bin(LPX *lp) { /* retrieve number of binary columns */ return glp_get_num_bin(lp); } static int solve_mip(LPX *lp, int presolve) { glp_iocp parm; int ret; glp_init_iocp(&parm); switch (lpx_get_int_parm(lp, LPX_K_MSGLEV)) { case 0: parm.msg_lev = GLP_MSG_OFF; break; case 1: parm.msg_lev = GLP_MSG_ERR; break; case 2: parm.msg_lev = GLP_MSG_ON; break; case 3: parm.msg_lev = GLP_MSG_ALL; break; default: xassert(lp != lp); } switch (lpx_get_int_parm(lp, LPX_K_BRANCH)) { case 0: parm.br_tech = GLP_BR_FFV; break; case 1: parm.br_tech = GLP_BR_LFV; break; case 2: parm.br_tech = GLP_BR_DTH; break; case 3: parm.br_tech = GLP_BR_MFV; break; default: xassert(lp != lp); } switch (lpx_get_int_parm(lp, LPX_K_BTRACK)) { case 0: parm.bt_tech = GLP_BT_DFS; break; case 1: parm.bt_tech = GLP_BT_BFS; break; case 2: parm.bt_tech = GLP_BT_BPH; break; case 3: parm.bt_tech = GLP_BT_BLB; break; default: xassert(lp != lp); } parm.tol_int = lpx_get_real_parm(lp, LPX_K_TOLINT); parm.tol_obj = lpx_get_real_parm(lp, LPX_K_TOLOBJ); if (lpx_get_real_parm(lp, LPX_K_TMLIM) < 0.0 || lpx_get_real_parm(lp, LPX_K_TMLIM) > 1e6) parm.tm_lim = INT_MAX; else parm.tm_lim = (int)(1000.0 * lpx_get_real_parm(lp, LPX_K_TMLIM)); parm.mip_gap = lpx_get_real_parm(lp, LPX_K_MIPGAP); if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_GOMORY) parm.gmi_cuts = GLP_ON; else parm.gmi_cuts = GLP_OFF; if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_MIR) parm.mir_cuts = GLP_ON; else parm.mir_cuts = GLP_OFF; if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_COVER) parm.cov_cuts = GLP_ON; else parm.cov_cuts = GLP_OFF; if (lpx_get_int_parm(lp, LPX_K_USECUTS) & LPX_C_CLIQUE) parm.clq_cuts = GLP_ON; else parm.clq_cuts = GLP_OFF; parm.presolve = presolve; if (lpx_get_int_parm(lp, LPX_K_BINARIZE)) parm.binarize = GLP_ON; ret = glp_intopt(lp, &parm); switch (ret) { case 0: ret = LPX_E_OK; break; case GLP_ENOPFS: ret = LPX_E_NOPFS; break; case GLP_ENODFS: ret = LPX_E_NODFS; break; case GLP_EBOUND: case GLP_EROOT: ret = LPX_E_FAULT; break; case GLP_EFAIL: ret = LPX_E_SING; break; case GLP_EMIPGAP: ret = LPX_E_MIPGAP; break; case GLP_ETMLIM: ret = LPX_E_TMLIM; break; default: xassert(ret != ret); } return ret; } int lpx_integer(LPX *lp) { /* easy-to-use driver to the branch-and-bound method */ return solve_mip(lp, GLP_OFF); } int lpx_intopt(LPX *lp) { /* easy-to-use driver to the branch-and-bound method */ return solve_mip(lp, GLP_ON); } int lpx_mip_status(glp_prob *lp) { /* retrieve status of MIP solution */ int status; switch (glp_mip_status(lp)) { case GLP_UNDEF: status = LPX_I_UNDEF; break; case GLP_OPT: status = LPX_I_OPT; break; case GLP_FEAS: status = LPX_I_FEAS; break; case GLP_NOFEAS: status = LPX_I_NOFEAS; break; default: xassert(lp != lp); } return status; } double lpx_mip_obj_val(LPX *lp) { /* retrieve objective value (MIP solution) */ return glp_mip_obj_val(lp); } double lpx_mip_row_val(LPX *lp, int i) { /* retrieve row value (MIP solution) */ return glp_mip_row_val(lp, i); } double lpx_mip_col_val(LPX *lp, int j) { /* retrieve column value (MIP solution) */ return glp_mip_col_val(lp, j); } void lpx_check_int(LPX *lp, LPXKKT *kkt) { /* check integer feasibility conditions */ int ae_ind, re_ind; double ae_max, re_max; _glp_check_kkt(lp, GLP_MIP, GLP_KKT_PE, &ae_max, &ae_ind, &re_max, &re_ind); kkt->pe_ae_max = ae_max; kkt->pe_ae_row = ae_ind; kkt->pe_re_max = re_max; kkt->pe_re_row = re_ind; if (re_max <= 1e-9) kkt->pe_quality = 'H'; else if (re_max <= 1e-6) kkt->pe_quality = 'M'; else if (re_max <= 1e-3) kkt->pe_quality = 'L'; else kkt->pe_quality = '?'; _glp_check_kkt(lp, GLP_MIP, GLP_KKT_PB, &ae_max, &ae_ind, &re_max, &re_ind); kkt->pb_ae_max = ae_max; kkt->pb_ae_ind = ae_ind; kkt->pb_re_max = re_max; kkt->pb_re_ind = re_ind; if (re_max <= 1e-9) kkt->pb_quality = 'H'; else if (re_max <= 1e-6) kkt->pb_quality = 'M'; else if (re_max <= 1e-3) kkt->pb_quality = 'L'; else kkt->pb_quality = '?'; return; } #if 1 /* 17/XI-2009 */ static void reset_parms(LPX *lp) { /* reset control parameters to default values */ struct LPXCPS *cps = lp->parms; xassert(cps != NULL); cps->msg_lev = 3; cps->scale = 1; cps->dual = 0; cps->price = 1; cps->relax = 0.07; cps->tol_bnd = 1e-7; cps->tol_dj = 1e-7; cps->tol_piv = 1e-9; cps->round = 0; cps->obj_ll = -DBL_MAX; cps->obj_ul = +DBL_MAX; cps->it_lim = -1; #if 0 /* 02/XII-2010 */ lp->it_cnt = 0; #endif cps->tm_lim = -1.0; cps->out_frq = 200; cps->out_dly = 0.0; cps->branch = 2; cps->btrack = 3; cps->tol_int = 1e-5; cps->tol_obj = 1e-7; cps->mps_info = 1; cps->mps_obj = 2; cps->mps_orig = 0; cps->mps_wide = 1; cps->mps_free = 0; cps->mps_skip = 0; cps->lpt_orig = 0; cps->presol = 0; cps->binarize = 0; cps->use_cuts = 0; cps->mip_gap = 0.0; return; } #endif #if 1 /* 17/XI-2009 */ static struct LPXCPS *access_parms(LPX *lp) { /* allocate and initialize control parameters, if necessary */ if (lp->parms == NULL) { lp->parms = xmalloc(sizeof(struct LPXCPS)); reset_parms(lp); } return lp->parms; } #endif #if 1 /* 17/XI-2009 */ void lpx_reset_parms(LPX *lp) { /* reset control parameters to default values */ access_parms(lp); reset_parms(lp); return; } #endif void lpx_set_int_parm(LPX *lp, int parm, int val) { /* set (change) integer control parameter */ #if 0 /* 17/XI-2009 */ struct LPXCPS *cps = lp->cps; #else struct LPXCPS *cps = access_parms(lp); #endif switch (parm) { case LPX_K_MSGLEV: if (!(0 <= val && val <= 3)) xerror("lpx_set_int_parm: MSGLEV = %d; invalid value\n", val); cps->msg_lev = val; break; case LPX_K_SCALE: if (!(0 <= val && val <= 3)) xerror("lpx_set_int_parm: SCALE = %d; invalid value\n", val); cps->scale = val; break; case LPX_K_DUAL: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: DUAL = %d; invalid value\n", val); cps->dual = val; break; case LPX_K_PRICE: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: PRICE = %d; invalid value\n", val); cps->price = val; break; case LPX_K_ROUND: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: ROUND = %d; invalid value\n", val); cps->round = val; break; case LPX_K_ITLIM: cps->it_lim = val; break; case LPX_K_ITCNT: lp->it_cnt = val; break; case LPX_K_OUTFRQ: if (!(val > 0)) xerror("lpx_set_int_parm: OUTFRQ = %d; invalid value\n", val); cps->out_frq = val; break; case LPX_K_BRANCH: if (!(val == 0 || val == 1 || val == 2 || val == 3)) xerror("lpx_set_int_parm: BRANCH = %d; invalid value\n", val); cps->branch = val; break; case LPX_K_BTRACK: if (!(val == 0 || val == 1 || val == 2 || val == 3)) xerror("lpx_set_int_parm: BTRACK = %d; invalid value\n", val); cps->btrack = val; break; case LPX_K_MPSINFO: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: MPSINFO = %d; invalid value\n", val); cps->mps_info = val; break; case LPX_K_MPSOBJ: if (!(val == 0 || val == 1 || val == 2)) xerror("lpx_set_int_parm: MPSOBJ = %d; invalid value\n", val); cps->mps_obj = val; break; case LPX_K_MPSORIG: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: MPSORIG = %d; invalid value\n", val); cps->mps_orig = val; break; case LPX_K_MPSWIDE: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: MPSWIDE = %d; invalid value\n", val); cps->mps_wide = val; break; case LPX_K_MPSFREE: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: MPSFREE = %d; invalid value\n", val); cps->mps_free = val; break; case LPX_K_MPSSKIP: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: MPSSKIP = %d; invalid value\n", val); cps->mps_skip = val; break; case LPX_K_LPTORIG: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: LPTORIG = %d; invalid value\n", val); cps->lpt_orig = val; break; case LPX_K_PRESOL: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: PRESOL = %d; invalid value\n", val); cps->presol = val; break; case LPX_K_BINARIZE: if (!(val == 0 || val == 1)) xerror("lpx_set_int_parm: BINARIZE = %d; invalid value\n" , val); cps->binarize = val; break; case LPX_K_USECUTS: if (val & ~LPX_C_ALL) xerror("lpx_set_int_parm: USECUTS = 0x%X; invalid value\n", val); cps->use_cuts = val; break; case LPX_K_BFTYPE: #if 0 if (!(1 <= val && val <= 3)) xerror("lpx_set_int_parm: BFTYPE = %d; invalid value\n", val); cps->bf_type = val; #else { glp_bfcp parm; glp_get_bfcp(lp, &parm); switch (val) { case 1: parm.type = GLP_BF_FT; break; case 2: parm.type = GLP_BF_BG; break; case 3: parm.type = GLP_BF_GR; break; default: xerror("lpx_set_int_parm: BFTYPE = %d; invalid val" "ue\n", val); } glp_set_bfcp(lp, &parm); } #endif break; default: xerror("lpx_set_int_parm: parm = %d; invalid parameter\n", parm); } return; } int lpx_get_int_parm(LPX *lp, int parm) { /* query integer control parameter */ #if 0 /* 17/XI-2009 */ struct LPXCPS *cps = lp->cps; #else struct LPXCPS *cps = access_parms(lp); #endif int val = 0; switch (parm) { case LPX_K_MSGLEV: val = cps->msg_lev; break; case LPX_K_SCALE: val = cps->scale; break; case LPX_K_DUAL: val = cps->dual; break; case LPX_K_PRICE: val = cps->price; break; case LPX_K_ROUND: val = cps->round; break; case LPX_K_ITLIM: val = cps->it_lim; break; case LPX_K_ITCNT: val = lp->it_cnt; break; case LPX_K_OUTFRQ: val = cps->out_frq; break; case LPX_K_BRANCH: val = cps->branch; break; case LPX_K_BTRACK: val = cps->btrack; break; case LPX_K_MPSINFO: val = cps->mps_info; break; case LPX_K_MPSOBJ: val = cps->mps_obj; break; case LPX_K_MPSORIG: val = cps->mps_orig; break; case LPX_K_MPSWIDE: val = cps->mps_wide; break; case LPX_K_MPSFREE: val = cps->mps_free; break; case LPX_K_MPSSKIP: val = cps->mps_skip; break; case LPX_K_LPTORIG: val = cps->lpt_orig; break; case LPX_K_PRESOL: val = cps->presol; break; case LPX_K_BINARIZE: val = cps->binarize; break; case LPX_K_USECUTS: val = cps->use_cuts; break; case LPX_K_BFTYPE: #if 0 val = cps->bf_type; break; #else { glp_bfcp parm; glp_get_bfcp(lp, &parm); switch (parm.type) { case GLP_BF_FT: val = 1; break; case GLP_BF_BG: val = 2; break; case GLP_BF_GR: val = 3; break; default: xassert(lp != lp); } } break; #endif default: xerror("lpx_get_int_parm: parm = %d; invalid parameter\n", parm); } return val; } void lpx_set_real_parm(LPX *lp, int parm, double val) { /* set (change) real control parameter */ #if 0 /* 17/XI-2009 */ struct LPXCPS *cps = lp->cps; #else struct LPXCPS *cps = access_parms(lp); #endif switch (parm) { case LPX_K_RELAX: if (!(0.0 <= val && val <= 1.0)) xerror("lpx_set_real_parm: RELAX = %g; invalid value\n", val); cps->relax = val; break; case LPX_K_TOLBND: if (!(DBL_EPSILON <= val && val <= 0.001)) xerror("lpx_set_real_parm: TOLBND = %g; invalid value\n", val); #if 0 if (cps->tol_bnd > val) { /* invalidate the basic solution */ lp->p_stat = LPX_P_UNDEF; lp->d_stat = LPX_D_UNDEF; } #endif cps->tol_bnd = val; break; case LPX_K_TOLDJ: if (!(DBL_EPSILON <= val && val <= 0.001)) xerror("lpx_set_real_parm: TOLDJ = %g; invalid value\n", val); #if 0 if (cps->tol_dj > val) { /* invalidate the basic solution */ lp->p_stat = LPX_P_UNDEF; lp->d_stat = LPX_D_UNDEF; } #endif cps->tol_dj = val; break; case LPX_K_TOLPIV: if (!(DBL_EPSILON <= val && val <= 0.001)) xerror("lpx_set_real_parm: TOLPIV = %g; invalid value\n", val); cps->tol_piv = val; break; case LPX_K_OBJLL: cps->obj_ll = val; break; case LPX_K_OBJUL: cps->obj_ul = val; break; case LPX_K_TMLIM: cps->tm_lim = val; break; case LPX_K_OUTDLY: cps->out_dly = val; break; case LPX_K_TOLINT: if (!(DBL_EPSILON <= val && val <= 0.001)) xerror("lpx_set_real_parm: TOLINT = %g; invalid value\n", val); cps->tol_int = val; break; case LPX_K_TOLOBJ: if (!(DBL_EPSILON <= val && val <= 0.001)) xerror("lpx_set_real_parm: TOLOBJ = %g; invalid value\n", val); cps->tol_obj = val; break; case LPX_K_MIPGAP: if (val < 0.0) xerror("lpx_set_real_parm: MIPGAP = %g; invalid value\n", val); cps->mip_gap = val; break; default: xerror("lpx_set_real_parm: parm = %d; invalid parameter\n", parm); } return; } double lpx_get_real_parm(LPX *lp, int parm) { /* query real control parameter */ #if 0 /* 17/XI-2009 */ struct LPXCPS *cps = lp->cps; #else struct LPXCPS *cps = access_parms(lp); #endif double val = 0.0; switch (parm) { case LPX_K_RELAX: val = cps->relax; break; case LPX_K_TOLBND: val = cps->tol_bnd; break; case LPX_K_TOLDJ: val = cps->tol_dj; break; case LPX_K_TOLPIV: val = cps->tol_piv; break; case LPX_K_OBJLL: val = cps->obj_ll; break; case LPX_K_OBJUL: val = cps->obj_ul; break; case LPX_K_TMLIM: val = cps->tm_lim; break; case LPX_K_OUTDLY: val = cps->out_dly; break; case LPX_K_TOLINT: val = cps->tol_int; break; case LPX_K_TOLOBJ: val = cps->tol_obj; break; case LPX_K_MIPGAP: val = cps->mip_gap; break; default: xerror("lpx_get_real_parm: parm = %d; invalid parameter\n", parm); } return val; } LPX *lpx_read_mps(const char *fname) { /* read problem data in fixed MPS format */ LPX *lp = lpx_create_prob(); if (glp_read_mps(lp, GLP_MPS_DECK, NULL, fname)) lpx_delete_prob(lp), lp = NULL; return lp; } int lpx_write_mps(LPX *lp, const char *fname) { /* write problem data in fixed MPS format */ return glp_write_mps(lp, GLP_MPS_DECK, NULL, fname); } int lpx_read_bas(LPX *lp, const char *fname) { /* read LP basis in fixed MPS format */ #if 0 /* 13/IV-2009 */ return read_bas(lp, fname); #else xassert(lp == lp); xassert(fname == fname); xerror("lpx_read_bas: operation not supported\n"); return 0; #endif } int lpx_write_bas(LPX *lp, const char *fname) { /* write LP basis in fixed MPS format */ #if 0 /* 13/IV-2009 */ return write_bas(lp, fname); #else xassert(lp == lp); xassert(fname == fname); xerror("lpx_write_bas: operation not supported\n"); return 0; #endif } LPX *lpx_read_freemps(const char *fname) { /* read problem data in free MPS format */ LPX *lp = lpx_create_prob(); if (glp_read_mps(lp, GLP_MPS_FILE, NULL, fname)) lpx_delete_prob(lp), lp = NULL; return lp; } int lpx_write_freemps(LPX *lp, const char *fname) { /* write problem data in free MPS format */ return glp_write_mps(lp, GLP_MPS_FILE, NULL, fname); } LPX *lpx_read_cpxlp(const char *fname) { /* read problem data in CPLEX LP format */ LPX *lp; lp = lpx_create_prob(); if (glp_read_lp(lp, NULL, fname)) lpx_delete_prob(lp), lp = NULL; return lp; } int lpx_write_cpxlp(LPX *lp, const char *fname) { /* write problem data in CPLEX LP format */ return glp_write_lp(lp, NULL, fname); } LPX *lpx_read_model(const char *model, const char *data, const char *output) { /* read LP/MIP model written in GNU MathProg language */ LPX *lp = NULL; glp_tran *tran; /* allocate the translator workspace */ tran = glp_mpl_alloc_wksp(); /* read model section and optional data section */ if (glp_mpl_read_model(tran, model, data != NULL)) goto done; /* read separate data section, if required */ if (data != NULL) if (glp_mpl_read_data(tran, data)) goto done; /* generate the model */ if (glp_mpl_generate(tran, output)) goto done; /* build the problem instance from the model */ lp = glp_create_prob(); glp_mpl_build_prob(tran, lp); done: /* free the translator workspace */ glp_mpl_free_wksp(tran); /* bring the problem object to the calling program */ return lp; } int lpx_print_prob(LPX *lp, const char *fname) { /* write problem data in plain text format */ return glp_write_lp(lp, NULL, fname); } int lpx_print_sol(LPX *lp, const char *fname) { /* write LP problem solution in printable format */ return glp_print_sol(lp, fname); } int lpx_print_sens_bnds(LPX *lp, const char *fname) { /* write bounds sensitivity information */ if (glp_get_status(lp) == GLP_OPT && !glp_bf_exists(lp)) glp_factorize(lp); return glp_print_ranges(lp, 0, NULL, 0, fname); } int lpx_print_ips(LPX *lp, const char *fname) { /* write interior point solution in printable format */ return glp_print_ipt(lp, fname); } int lpx_print_mip(LPX *lp, const char *fname) { /* write MIP problem solution in printable format */ return glp_print_mip(lp, fname); } int lpx_is_b_avail(glp_prob *lp) { /* check if LP basis is available */ return glp_bf_exists(lp); } int lpx_main(int argc, const char *argv[]) { /* stand-alone LP/MIP solver */ return glp_main(argc, argv); } /* eof */ igraph/src/prpack_preprocessed_ge_graph.cpp0000644000176000001440000000376112325527074020757 0ustar ripleyusers#include "prpack_preprocessed_ge_graph.h" #include using namespace prpack; using namespace std; void prpack_preprocessed_ge_graph::initialize() { matrix = NULL; d = NULL; } void prpack_preprocessed_ge_graph::initialize_weighted(const prpack_base_graph* bg) { // initialize d fill(d, d + num_vs, 1); // fill in the matrix for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) { const int start_j = bg->tails[i]; const int end_j = (i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es; for (int j = start_j; j < end_j; ++j) d[bg->heads[j]] -= matrix[inum_vs + bg->heads[j]] = bg->vals[j]; } } void prpack_preprocessed_ge_graph::initialize_unweighted(const prpack_base_graph* bg) { // fill in the matrix for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) { const int start_j = bg->tails[i]; const int end_j = (i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es; for (int j = start_j; j < end_j; ++j) ++matrix[inum_vs + bg->heads[j]]; } // normalize the columns for (int j = 0; j < num_vs; ++j) { double sum = 0; for (int inum_vs = 0; inum_vs < num_vs*num_vs; inum_vs += num_vs) sum += matrix[inum_vs + j]; if (sum > 0) { d[j] = 0; const double coeff = 1/sum; for (int inum_vs = 0; inum_vs < num_vs*num_vs; inum_vs += num_vs) matrix[inum_vs + j] *= coeff; } else { d[j] = 1; } } } prpack_preprocessed_ge_graph::prpack_preprocessed_ge_graph(const prpack_base_graph* bg) { initialize(); num_vs = bg->num_vs; num_es = bg->num_es; matrix = new double[num_vs*num_vs]; d = new double[num_vs]; fill(matrix, matrix + num_vs*num_vs, 0); if (bg->vals != NULL) initialize_weighted(bg); else initialize_unweighted(bg); } prpack_preprocessed_ge_graph::~prpack_preprocessed_ge_graph() { delete[] matrix; delete[] d; } igraph/src/dngets.f0000644000176000001440000001772712325527073014022 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdngets c c\Description: c Given the eigenvalues of the upper Hessenberg matrix H, c computes the NP shifts AMU that are zeros of the polynomial of c degree NP which filters out components of the unwanted eigenvectors c corresponding to the AMU's based on some given criteria. c c NOTE: call this even in the case of user specified shifts in order c to sort the eigenvalues, and error bounds of H for later use. c c\Usage: c call igraphdngets c ( ISHIFT, WHICH, KEV, NP, RITZR, RITZI, BOUNDS, SHIFTR, SHIFTI ) c c\Arguments c ISHIFT Integer. (INPUT) c Method for selecting the implicit shifts at each iteration. c ISHIFT = 0: user specified shifts c ISHIFT = 1: exact shift with respect to the matrix H. c c WHICH Character*2. (INPUT) c Shift selection criteria. c 'LM' -> want the KEV eigenvalues of largest magnitude. c 'SM' -> want the KEV eigenvalues of smallest magnitude. c 'LR' -> want the KEV eigenvalues of largest real part. c 'SR' -> want the KEV eigenvalues of smallest real part. c 'LI' -> want the KEV eigenvalues of largest imaginary part. c 'SI' -> want the KEV eigenvalues of smallest imaginary part. c c KEV Integer. (INPUT/OUTPUT) c INPUT: KEV+NP is the size of the matrix H. c OUTPUT: Possibly increases KEV by one to keep complex conjugate c pairs together. c c NP Integer. (INPUT/OUTPUT) c Number of implicit shifts to be computed. c OUTPUT: Possibly decreases NP by one to keep complex conjugate c pairs together. c c RITZR, Double precision array of length KEV+NP. (INPUT/OUTPUT) c RITZI On INPUT, RITZR and RITZI contain the real and imaginary c parts of the eigenvalues of H. c On OUTPUT, RITZR and RITZI are sorted so that the unwanted c eigenvalues are in the first NP locations and the wanted c portion is in the last KEV locations. When exact shifts are c selected, the unwanted part corresponds to the shifts to c be applied. Also, if ISHIFT .eq. 1, the unwanted eigenvalues c are further sorted so that the ones with largest Ritz values c are first. c c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) c Error bounds corresponding to the ordering in RITZ. c c SHIFTR, SHIFTI *** USE deprecated as of version 2.1. *** c c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c igraphdsortc ARPACK sorting routine. c dcopy Level 1 BLAS that copies one vector to another . c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.1' c c\SCCS Information: @(#) c FILE: ngets.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 c c\Remarks c 1. xxxx c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdngets ( ishift, which, kev, np, ritzr, ritzi, & bounds, shiftr, shifti ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character*2 which integer ishift, kev, np c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & bounds(kev+np), ritzr(kev+np), ritzi(kev+np), & shiftr(1), shifti(1) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0, zero = 0.0) c c %---------------% c | Local Scalars | c %---------------% c integer msglvl c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, igraphdsortc, igraphsecond c c %----------------------% c | Intrinsics Functions | c %----------------------% c intrinsic abs c c %-----------------------% c | Executable Statements | c %-----------------------% c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = mngets c c %----------------------------------------------------% c | LM, SM, LR, SR, LI, SI case. | c | Sort the eigenvalues of H into the desired order | c | and apply the resulting order to BOUNDS. | c | The eigenvalues are sorted so that the wanted part | c | are always in the last KEV locations. | c | We first do a pre-processing sort in order to keep | c | complex conjugate pairs together | c %----------------------------------------------------% c if (which .eq. 'LM') then call igraphdsortc ('LR', .true., kev+np, ritzr, ritzi, bounds) else if (which .eq. 'SM') then call igraphdsortc ('SR', .true., kev+np, ritzr, ritzi, bounds) else if (which .eq. 'LR') then call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) else if (which .eq. 'SR') then call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) else if (which .eq. 'LI') then call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) else if (which .eq. 'SI') then call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) end if c call igraphdsortc (which, .true., kev+np, ritzr, ritzi, bounds) c c %-------------------------------------------------------% c | Increase KEV by one if the ( ritzr(np),ritzi(np) ) | c | = ( ritzr(np+1),-ritzi(np+1) ) and ritz(np) .ne. zero | c | Accordingly decrease NP by one. In other words keep | c | complex conjugate pairs together. | c %-------------------------------------------------------% c if ( ( ritzr(np+1) - ritzr(np) ) .eq. zero & .and. ( ritzi(np+1) + ritzi(np) ) .eq. zero ) then np = np - 1 kev = kev + 1 end if c if ( ishift .eq. 1 ) then c c %-------------------------------------------------------% c | Sort the unwanted Ritz values used as shifts so that | c | the ones with largest Ritz estimates are first | c | This will tend to minimize the effects of the | c | forward instability of the iteration when they shifts | c | are applied in subroutine igraphdnapps. | c | Be careful and use 'SR' since we want to sort BOUNDS! | c %-------------------------------------------------------% c call igraphdsortc ( 'SR', .true., np, bounds, ritzr, ritzi ) end if c call igraphsecond (t1) tngets = tngets + (t1 - t0) c if (msglvl .gt. 0) then call igraphivout (logfil, 1, kev, ndigit, '_ngets: KEV is') call igraphivout (logfil, 1, np, ndigit, '_ngets: NP is') call igraphdvout (logfil, kev+np, ritzr, ndigit, & '_ngets: Eigenvalues of current H matrix -- real part') call igraphdvout (logfil, kev+np, ritzi, ndigit, & '_ngets: Eigenvalues of current H matrix -- imag part') call igraphdvout (logfil, kev+np, bounds, ndigit, & '_ngets: Ritz estimates of the current KEV+NP Ritz values') end if c return c c %---------------% c | End of igraphdngets | c %---------------% c end igraph/src/dqueue.pmt0000644000176000001440000002133012325372072014357 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include /** * \section igraph_dqueue * * This is the classic data type of the double ended queue. Most of * the time it is used if a First-In-First-Out (FIFO) behavior is * needed. See the operations below. * * * * \example examples/simple/dqueue.c * */ /** * \ingroup dqueue * \function igraph_dqueue_init * \brief Initialize a double ended queue (deque). * * The queue will be always empty. * \param q Pointer to an uninitialized deque. * \param size How many elements to allocate memory for. * \return Error code. * * Time complexity: O(\p size). */ int FUNCTION(igraph_dqueue,init) (TYPE(igraph_dqueue)* q, long int size) { assert(q != 0); if (size <= 0 ) { size=1; } q->stor_begin=igraph_Calloc(size, BASE); if (q->stor_begin==0) { IGRAPH_ERROR("dqueue init failed", IGRAPH_ENOMEM); } q->stor_end=q->stor_begin + size; q->begin=q->stor_begin; q->end=NULL; return 0; } /** * \ingroup dqueue * \function igraph_dqueue_destroy * \brief Destroy a double ended queue. * * \param q The queue to destroy * * Time complexity: O(1). */ void FUNCTION(igraph_dqueue,destroy) (TYPE(igraph_dqueue)* q) { assert(q != 0); if (q->stor_begin != 0) { igraph_Free(q->stor_begin); q->stor_begin=0; } } /** * \ingroup dqueue * \function igraph_dqueue_empty * \brief Decide whether the queue is empty. * * \param q The queue. * \return Boolean, \c TRUE if \p q contains at least one element, \c * FALSE otherwise. * * Time complexity: O(1). */ igraph_bool_t FUNCTION(igraph_dqueue,empty) (const TYPE(igraph_dqueue)* q) { assert(q != 0); assert(q->stor_begin != 0); return q->end == NULL; } /** * \ingroup dqueue * \function igraph_dqueue_clear * \brief Remove all elements from the queue. * * \param q The queue * * Time complexity: O(1). */ void FUNCTION(igraph_dqueue,clear) (TYPE(igraph_dqueue)* q) { assert(q != 0); assert(q->stor_begin != 0); q->begin=q->stor_begin; q->end=NULL; } /** * \ingroup dqueue * \function igraph_dqueue_full * \brief Check whether the queue is full. * * If a queue is full the next igraph_dqueue_push() operation will allocate * more memory. * \param q The queue. * \return \c TRUE if \p q is full, \c FALSE otherwise. * * Time complecity: O(1). */ igraph_bool_t FUNCTION(igraph_dqueue,full) (TYPE(igraph_dqueue)* q) { assert(q != 0); assert(q->stor_begin != 0); return q->begin == q->end; } /** * \ingroup dqueue * \function igraph_dqueue_size * \brief Number of elements in the queue. * * \param q The queue. * \return Integer, the number of elements currently in the queue. * * Time complexity: O(1). */ long int FUNCTION(igraph_dqueue,size) (const TYPE(igraph_dqueue)* q) { assert(q != 0); assert(q->stor_begin != 0); if (q->end==NULL) { return 0; } else if (q->begin < q->end) { return q->end - q->begin; } else { return q->stor_end - q->begin + q->end - q->stor_begin; } } /** * \ingroup dqueue * \function igraph_dqueue_head * \brief Head of the queue. * * The queue must contain at least one element. * \param q The queue. * \return The first element in the queue. * * Time complexity: O(1). */ BASE FUNCTION(igraph_dqueue,head) (const TYPE(igraph_dqueue)* q) { assert(q != 0); assert(q->stor_begin != 0); return *(q->begin); } /** * \ingroup dqueue * \function igraph_dqueue_back * \brief Tail of the queue. * * The queue must contain at least one element. * \param q The queue. * \return The last element in the queue. * * Time complexity: O(1). */ BASE FUNCTION(igraph_dqueue,back) (const TYPE(igraph_dqueue)* q) { assert(q != 0); assert(q->stor_begin != 0); if (q->end == q->stor_begin) return *(q->stor_end-1); return *(q->end-1); } /** * \ingroup dqueue * \function igraph_dqueue_pop * \brief Remove the head. * * Removes and returns the first element in the queue. The queue must * be non-empty. * \param q The input queue. * \return The first element in the queue. * * Time complexity: O(1). */ BASE FUNCTION(igraph_dqueue,pop) (TYPE(igraph_dqueue)* q) { BASE tmp=*(q->begin); assert(q != 0); assert(q->stor_begin != 0); (q->begin)++; if (q->begin==q->stor_end) { q->begin=q->stor_begin; } if (q->begin==q->end) { q->end=NULL; } return tmp; } /** * \ingroup dqueue * \function igraph_dqueue_pop_back * \brief Remove the tail * * Removes and returns the last element in the queue. The queue must * be non-empty. * \param q The queue. * \return The last element in the queue. * * Time complexity: O(1). */ BASE FUNCTION(igraph_dqueue,pop_back) (TYPE(igraph_dqueue)* q) { BASE tmp; assert(q != 0); assert(q->stor_begin != 0); if (q->end != q->stor_begin) { tmp=*((q->end)-1); q->end = (q->end)-1; } else { tmp=*((q->stor_end)-1); q->end = (q->stor_end)-1; } if (q->begin==q->end) { q->end=NULL; } return tmp; } /** * \ingroup dqueue * \function igraph_dqueue_push * \brief Appends an element. * * Append an element to the end of the queue. * \param q The queue. * \param elem The element to append. * \return Error code. * * Time complexity: O(1) if no memory allocation is needed, O(n), the * number of elements in the queue otherwise. But not that by * allocating always twice as much memory as the current size of the * queue we ensure that n push operations can always be done in at * most O(n) time. (Assuming memory allocation is at most linear.) */ int FUNCTION(igraph_dqueue,push) (TYPE(igraph_dqueue)* q, BASE elem) { assert(q != 0); assert(q->stor_begin != 0); if (q->begin != q->end) { /* not full */ if (q->end==NULL) { q->end=q->begin; } *(q->end) = elem; (q->end)++; if (q->end==q->stor_end) { q->end=q->stor_begin; } } else { /* full, allocate more storage */ BASE *bigger=NULL, *old=q->stor_begin; bigger=igraph_Calloc( 2*(q->stor_end - q->stor_begin)+1, BASE ); if (bigger==0) { IGRAPH_ERROR("dqueue push failed", IGRAPH_ENOMEM); } if (q->stor_end - q->begin) { memcpy(bigger, q->begin, (size_t)(q->stor_end - q->begin) * sizeof(BASE)); } if (q->end - q->stor_begin > 0) { memcpy(bigger + (q->stor_end - q->begin), q->stor_begin, (size_t)(q->end - q->stor_begin) * sizeof(BASE)); } q->end =bigger + (q->stor_end - q->stor_begin); q->stor_end =bigger + 2*(q->stor_end - q->stor_begin) + 1; q->stor_begin=bigger; q->begin =bigger; *(q->end) = elem; (q->end)++; if (q->end==q->stor_end) { q->end=q->stor_begin; } igraph_Free(old); } return 0; } #if defined (OUT_FORMAT) #ifndef USING_R int FUNCTION(igraph_dqueue,print)(const TYPE(igraph_dqueue)* q) { return FUNCTION(igraph_dqueue,fprint)(q, stdout); } #endif int FUNCTION(igraph_dqueue,fprint)(const TYPE(igraph_dqueue)* q, FILE *file) { if (q->end != NULL) { /* There is one element at least */ BASE *p=q->begin; fprintf(file, OUT_FORMAT, *p); p++; if (q->end > q->begin) { /* Q is in one piece */ while (p != q->end) { fprintf(file, " " OUT_FORMAT, *p); p++; } } else { /* Q is in two pieces */ while (p != q->stor_end) { fprintf(file, " " OUT_FORMAT, *p); p++; } p=q->stor_begin; while (p != q->end) { fprintf(file, " " OUT_FORMAT, *p); p++; } } } fprintf(file, "\n"); return 0; } #endif BASE FUNCTION(igraph_dqueue,e)(const TYPE(igraph_dqueue) *q, long int idx) { if ((q->begin + idx < q->end) || (q->begin >= q->end && q->begin+idx < q->stor_end)) { return q->begin[idx]; } else if (q->begin >= q->end && q->stor_begin+idx < q->end) { idx = idx-(q->stor_end - q->begin); return q->stor_begin[idx]; } else { return 0; /* Error */ } } igraph/src/bliss_partition.cc0000644000176000001440000005675112325527072016102 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include #include #include #include "bliss_graph.hh" #include "bliss_partition.hh" using namespace std; namespace igraph { static const bool should_not_happen = false; Partition::Partition() { elements = 0; in_pos = 0; invariant_values = 0; cells = 0; free_cells = 0; element_to_cell_map = 0; graph = 0; } Partition::~Partition() { if(elements) { free(elements); elements = 0; } if(cells) { free(cells); cells = 0; } if(element_to_cell_map) { free(element_to_cell_map); element_to_cell_map = 0; } if(in_pos) { free(in_pos); in_pos = 0; } if(invariant_values) { free(invariant_values); invariant_values = 0; } } void Partition::init(const unsigned int N) { assert(N > 0); if(elements) free(elements); elements = (unsigned int*)malloc(N * sizeof(unsigned int)); for(unsigned int i = 0; i < N; i++) elements[i] = i; if(in_pos) free(in_pos); in_pos = (unsigned int**)malloc(N * sizeof(unsigned int*)); for(unsigned int i = 0; i < N; i++) in_pos[i] = elements + i; if(invariant_values) free(invariant_values); invariant_values = (unsigned int*)malloc(N * sizeof(unsigned int)); for(unsigned int i = 0; i < N; i++) invariant_values[i] = 0; if(cells) free(cells); cells = (Cell*)malloc(N * sizeof(Cell)); cells[0].first = 0; cells[0].length = N; cells[0].max_ival = 0; cells[0].max_ival_count = 0; cells[0].in_splitting_queue = false; cells[0].in_neighbour_heap = false; cells[0].next = 0; cells[0].prev_next_ptr = &first_cell; cells[0].next_nonsingleton = 0; cells[0].prev_nonsingleton = 0; cells[0].split_level = 0; first_cell = &cells[0]; if(N == 1) first_nonsingleton_cell = 0; else first_nonsingleton_cell = &cells[0]; for(unsigned int i = 1; i < N; i++) { cells[i].first = 0; cells[i].length = 0; cells[i].max_ival = 0; cells[i].max_ival_count = 0; cells[i].in_splitting_queue = false; cells[i].in_neighbour_heap = false; cells[i].next = (i < N-1)?&cells[i+1]:0; cells[i].prev_next_ptr = (i == 1)?&free_cells:&(cells[i-1].next); cells[i].next_nonsingleton = 0; cells[i].prev_nonsingleton = 0; } if(N > 1) free_cells = &cells[1]; else free_cells = 0; if(element_to_cell_map) free(element_to_cell_map); element_to_cell_map = (Cell **)malloc(N * sizeof(Cell *)); for(unsigned int i = 0; i < N; i++) element_to_cell_map[i] = first_cell; splitting_queue.init(N); refinement_stack.init(N); level = 0; } /* * For debugging purposes only. * Checks that the ordered list of nonsingleton cells is consistent. */ void Partition::consistency_check() { #ifdef DEBUG for(const Cell *cell = first_cell; cell; cell = cell->next) { assert(cell->prev_next_ptr && *(cell->prev_next_ptr) == cell); } const bool do_print = false; if(do_print) { fprintf(stderr, "\nRef stack: "); for(unsigned int j = 0; j < refinement_stack.size(); j++) { const RefInfo i = refinement_stack.element_at(j); fprintf(stderr, "f%u,%d,%d ", i.split_cell_first, i.prev_nonsingleton_first, i.next_nonsingleton_first); } fprintf(stderr, "\n"); for(const Cell *cell = first_nonsingleton_cell; cell; cell = cell->next_nonsingleton) { fprintf(stderr, "%u:%u->", cell->first, cell->length); if(cell->next_nonsingleton) assert(cell->first < cell->next_nonsingleton->first); } fprintf(stderr, "\n"); } const Cell *next_nonsingleton = first_nonsingleton_cell; const Cell *prev_nonsingleton = 0; if(next_nonsingleton) assert(next_nonsingleton->prev_nonsingleton == 0); for(const Cell *cell = first_cell; cell; cell = cell->next) { assert(!cell->next || cell->next->prev_next_ptr == &(cell->next)); if(cell->length > 1) { if(do_print) fprintf(stderr, "%u:%u=>", cell->first, cell->length); assert(cell == next_nonsingleton); assert(cell->prev_nonsingleton == prev_nonsingleton); next_nonsingleton = cell->next_nonsingleton; prev_nonsingleton = cell; if(next_nonsingleton) assert(next_nonsingleton->first > cell->first); } else { assert(cell != next_nonsingleton); assert(cell->next_nonsingleton == 0); assert(cell->prev_nonsingleton == 0); } } assert(next_nonsingleton == 0); if(do_print) fprintf(stderr, "\n"); #endif } Cell *Partition::aux_split_in_two(Cell * const cell, const unsigned int first_half_size) { RefInfo i; DEBUG_ASSERT(first_half_size > 0); DEBUG_ASSERT(first_half_size < cell->length); /* (Pseudo)allocate new cell */ Cell * const new_cell = free_cells; DEBUG_ASSERT(new_cell); free_cells = new_cell->next; /* Update new cell parameters */ new_cell->first = cell->first + first_half_size; new_cell->length = cell->length - first_half_size; new_cell->next = cell->next; if(new_cell->next) new_cell->next->prev_next_ptr = &(new_cell->next); new_cell->prev_next_ptr = &(cell->next); new_cell->split_level = cell->split_level; /* Update old, splitted cell parameters */ cell->length = first_half_size; cell->next = new_cell; cell->split_level = level; /* Add cell in refinement_stack for backtracking */ i.split_cell_first = cell->first; if(cell->prev_nonsingleton) i.prev_nonsingleton_first = cell->prev_nonsingleton->first; else i.prev_nonsingleton_first = -1; if(cell->next_nonsingleton) i.next_nonsingleton_first = cell->next_nonsingleton->first; else i.next_nonsingleton_first = -1; refinement_stack.push(i); /* Modify nonsingleton cell list */ if(new_cell->length > 1) { new_cell->prev_nonsingleton = cell; new_cell->next_nonsingleton = cell->next_nonsingleton; if(new_cell->next_nonsingleton) new_cell->next_nonsingleton->prev_nonsingleton = new_cell; cell->next_nonsingleton = new_cell; } else { new_cell->next_nonsingleton = 0; new_cell->prev_nonsingleton = 0; } if(cell->length == 1) { if(cell->prev_nonsingleton) cell->prev_nonsingleton->next_nonsingleton = cell->next_nonsingleton; else first_nonsingleton_cell = cell->next_nonsingleton; if(cell->next_nonsingleton) cell->next_nonsingleton->prev_nonsingleton = cell->prev_nonsingleton; cell->next_nonsingleton = 0; cell->prev_nonsingleton = 0; } return new_cell; } void Partition::add_in_splitting_queue(Cell * const cell) { static const unsigned int smallish_cell_threshold = 1; assert(!cell->in_splitting_queue); cell->in_splitting_queue = true; if(cell->length <= smallish_cell_threshold) splitting_queue.push_front(cell); else splitting_queue.push_back(cell); } void Partition::clear_splitting_queue() { while(!splitting_queue.is_empty()) { Cell * const cell = splitting_queue.pop_front(); assert(cell->in_splitting_queue); cell->in_splitting_queue = false; } } /* * Assumes that the invariant values are NOT the same * and that the cell contains more than one element */ Cell *Partition::sort_and_split_cell1(Cell *cell) { #if defined(EXPENSIVE_CONSISTENCY_CHECKS) assert(cell->length > 1); assert(cell->first + cell->length <= graph->get_nof_vertices()); bool found0 = false, found1 = false; for(unsigned int i = 0; i < cell->length; i++) { if(invariant_values[elements[cell->first + i]] == 0) found0 = true; else if(invariant_values[elements[cell->first + i]] == 1) found1 = true; else assert(should_not_happen); } assert(found0); assert(found1); #endif consistency_check(); /* Allocate new cell */ Cell *new_cell = free_cells; DEBUG_ASSERT(new_cell); free_cells = new_cell->next; if(free_cells) free_cells->prev_next_ptr = &(free_cells); /* Sort vertices in the cell according to the invariant values */ unsigned int *ep0 = elements + cell->first; unsigned int *ep1 = ep0 + cell->length; while(ep1 > ep0) { const unsigned int element = *ep0; const unsigned int ival = invariant_values[element]; invariant_values[element] = 0; DEBUG_ASSERT(ival <= 1); DEBUG_ASSERT(element_to_cell_map[element] == cell); DEBUG_ASSERT(in_pos[element] == ep0); if(ival == 0) { ep0++; } else { ep1--; *ep0 = *ep1; *ep1 = element; element_to_cell_map[element] = new_cell; in_pos[element] = ep1; in_pos[*ep0] = ep0; } } DEBUG_ASSERT(ep1 != elements + cell->first); DEBUG_ASSERT(ep0 != elements + cell->first + cell->length); /* Update new cell parameters */ new_cell->first = ep1 - elements; new_cell->length = cell->length - (new_cell->first - cell->first); new_cell->next = cell->next; if(new_cell->next) new_cell->next->prev_next_ptr = &(new_cell->next); new_cell->prev_next_ptr = &(cell->next); new_cell->split_level = cell->split_level; /* Update old, splitted cell parameters */ cell->length = new_cell->first - cell->first; cell->next = new_cell; cell->split_level = level; /* Add cell in refinement stack for backtracking */ { RefInfo i; i.split_cell_first = cell->first; if(cell->prev_nonsingleton) i.prev_nonsingleton_first = cell->prev_nonsingleton->first; else i.prev_nonsingleton_first = -1; if(cell->next_nonsingleton) i.next_nonsingleton_first = cell->next_nonsingleton->first; else i.next_nonsingleton_first = -1; /* Modify nonsingleton cell list */ if(new_cell->length > 1) { new_cell->prev_nonsingleton = cell; new_cell->next_nonsingleton = cell->next_nonsingleton; if(new_cell->next_nonsingleton) new_cell->next_nonsingleton->prev_nonsingleton = new_cell; cell->next_nonsingleton = new_cell; } else { new_cell->next_nonsingleton = 0; new_cell->prev_nonsingleton = 0; } if(cell->length == 1) { if(cell->prev_nonsingleton) cell->prev_nonsingleton->next_nonsingleton = cell->next_nonsingleton; else first_nonsingleton_cell = cell->next_nonsingleton; if(cell->next_nonsingleton) cell->next_nonsingleton->prev_nonsingleton = cell->prev_nonsingleton; cell->next_nonsingleton = 0; cell->prev_nonsingleton = 0; } refinement_stack.push(i); } /* Add cells in splitting queue */ DEBUG_ASSERT(!new_cell->in_splitting_queue); if(cell->in_splitting_queue) { /* Both cells must be included in splitting_queue in order to have refinement to equitable partition */ add_in_splitting_queue(new_cell); } else { Cell *min_cell, *max_cell; if(cell->length <= new_cell->length) { min_cell = cell; max_cell = new_cell; } else { min_cell = new_cell; max_cell = cell; } /* Put the smaller cell in splitting_queue */ add_in_splitting_queue(min_cell); if(max_cell->length == 1) { /* Put the "larger" cell also in splitting_queue */ add_in_splitting_queue(max_cell); } } consistency_check(); return new_cell; } /* * Tables and a subroutine for distribution count sorting */ static IGRAPH_THREAD_LOCAL unsigned int count[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; static IGRAPH_THREAD_LOCAL unsigned int start[256]; /* * Build start array so that start[0] = 0 and start[i+1] = start[i] + count[i] */ static void cumulate_count(const unsigned int max) { DEBUG_ASSERT(max <= 255); unsigned int *count_p = count; unsigned int *start_p = start; unsigned int sum = 0; for(unsigned int i = max+1; i > 0; i--) { *start_p = sum; start_p++; sum += *count_p; count_p++; } } /* * Distribution count sorting of cells with invariant values less than 256 */ Cell *Partition::sort_and_split_cell255(Cell * const cell, const unsigned int max_ival) { //DEBUG_ASSERT(cell->first + cell->length <= graph->vertices.size()); DEBUG_ASSERT(max_ival <= 255); if(cell->length == 1) { /* Reset invariant value */ invariant_values[elements[cell->first]] = 0; return cell; } #ifdef CONSISTENCY_CHECKS for(unsigned int i = 0; i < 256; i++) assert(count[i] == 0); #endif /* * Compute the distribution of invariant values to the count array */ { const unsigned int *ep = elements + cell->first; DEBUG_ASSERT(element_to_cell_map[*ep] == cell); const unsigned int ival = invariant_values[*ep]; DEBUG_ASSERT(ival <= 255); count[ival]++; ep++; #ifdef CONSISTENCY_CHECKS bool equal_invariant_values = true; #endif for(unsigned int i = cell->length - 1; i > 0; i--) { DEBUG_ASSERT(element_to_cell_map[*ep] == cell); const unsigned int ival2 = invariant_values[*ep]; DEBUG_ASSERT(ival2 <= 255); DEBUG_ASSERT(ival2 <= max_ival); count[ival2]++; #ifdef CONSISTENCY_CHECKS if(ival2 != ival) { equal_invariant_values = false; } #endif ep++; } #ifdef CONSISTENCY_CHECKS DEBUG_ASSERT(!equal_invariant_values); if(equal_invariant_values) { DEBUG_ASSERT(count[ival] == cell->length); count[ival] = 0; clear_ivs(cell); return cell; } #endif } /* Build start array */ cumulate_count(max_ival); //DEBUG_ASSERT(start[255] + count[255] == cell->length); DEBUG_ASSERT(start[max_ival] + count[max_ival] == cell->length); /* Do the sorting */ for(unsigned int i = 0; i <= max_ival; i++) { unsigned int *ep = elements + cell->first + start[i]; for(unsigned int j = count[i]; j > 0; j--) { while(true) { const unsigned int element = *ep; const unsigned int ival = invariant_values[element]; if(ival == i) break; DEBUG_ASSERT(ival > i); DEBUG_ASSERT(count[ival] > 0); *ep = elements[cell->first + start[ival]]; elements[cell->first + start[ival]] = element; start[ival]++; count[ival]--; } ep++; } count[i] = 0; } #if defined(CONSISTENCY_CHECKS) for(unsigned int i = 0; i < 256; i++) assert(count[i] == 0); #endif #if defined(VERBOSEDEBUG) { const unsigned int *ep = elements + cell->first; fprintf(stderr, "\n"); for(unsigned int i = cell->length; i > 0; i--, ep++) fprintf(stderr, "%u ", invariant_values[*ep]); fprintf(stderr, "\n"); } #endif /* split cell */ Cell * const cell2 = split_cell(cell); DEBUG_ASSERT(cell2 != cell); return cell2; } /* * Sort the elements in a cell according to their invariant values * The invariant values are not cleared * Warning: the in_pos array is left in incorrect state */ bool Partition::shellsort_cell(Cell *cell) { unsigned int h; unsigned int *ep; //DEBUG_ASSERT(cell->first + cell->length <= graph->vertices.size()); if(cell->length == 1) return false; /* Check whether all the elements have the same invariant value */ bool equal_invariant_values = true; { ep = elements + cell->first; const unsigned int ival = invariant_values[*ep]; DEBUG_ASSERT(element_to_cell_map[*ep] == cell); ep++; for(unsigned int i = cell->length - 1; i > 0; i--) { DEBUG_ASSERT(element_to_cell_map[*ep] == cell); if(invariant_values[*ep] != ival) { equal_invariant_values = false; break; } ep++; } } if(equal_invariant_values) return false; ep = elements + cell->first; for(h = 1; h <= cell->length/9; h = 3*h + 1) ; for( ; h > 0; h = h/3) { for(unsigned int i = h; i < cell->length; i++) { const unsigned int element = ep[i]; const unsigned int ival = invariant_values[element]; unsigned int j = i; while(j >= h && invariant_values[ep[j-h]] > ival) { ep[j] = ep[j-h]; j -= h; } ep[j] = element; } } return true; } void Partition::clear_ivs(Cell * const cell) { unsigned int *ep = elements + cell->first; for(unsigned int i = cell->length; i > 0; i--, ep++) invariant_values[*ep] = 0; } /* * Assumes that the elements in the cell are sorted according to their * invariant values */ Cell *Partition::split_cell(Cell * const original_cell) { Cell *cell = original_cell; const bool original_cell_was_in_splitting_queue = original_cell->in_splitting_queue; Cell *largest_new_cell = 0; consistency_check(); while(true) { unsigned int *ep = elements + cell->first; const unsigned int * const lp = ep + cell->length; const unsigned int ival = invariant_values[*ep]; invariant_values[*ep] = 0; element_to_cell_map[*ep] = cell; in_pos[*ep] = ep; ep++; while(ep < lp) { const unsigned int e = *ep; if(invariant_values[e] != ival) break; invariant_values[e] = 0; in_pos[e] = ep; ep++; element_to_cell_map[e] = cell; } if(ep == lp) break; Cell * const new_cell = aux_split_in_two(cell, (ep - elements) - cell->first); if(graph->in_search) { graph->eqref_hash.update(new_cell->first); graph->eqref_hash.update(new_cell->length); graph->eqref_hash.update(ival); } /* Add cells in splitting_queue */ assert(!new_cell->in_splitting_queue); if(original_cell_was_in_splitting_queue) { /* In this case, all new cells are inserted in splitting_queue */ assert(cell->in_splitting_queue); add_in_splitting_queue(new_cell); } else { /* Otherwise, we can omit one new cell from splitting_queue */ assert(!cell->in_splitting_queue); if(largest_new_cell == 0) { largest_new_cell = cell; } else { assert(!largest_new_cell->in_splitting_queue); if(cell->length > largest_new_cell->length) { add_in_splitting_queue(largest_new_cell); largest_new_cell = cell; } else { add_in_splitting_queue(cell); } } } /* Process the rest of the cell */ cell = new_cell; } consistency_check(); if(original_cell == cell) { /* All the elements in cell had the same invariant value */ return cell; } /* Add cells in splitting_queue */ if(!original_cell_was_in_splitting_queue) { /* Also consider the last new cell */ assert(largest_new_cell); if(cell->length > largest_new_cell->length) { add_in_splitting_queue(largest_new_cell); largest_new_cell = cell; } else { add_in_splitting_queue(cell); } if(largest_new_cell->length == 1) { /* Needed in certificate computation */ add_in_splitting_queue(largest_new_cell); } } return cell; } Cell *Partition::zplit_cell(Cell * const cell, const bool max_ival_info_ok) { assert(cell); Cell *last_new_cell = cell; if(!max_ival_info_ok) { /* Compute max_ival info */ assert(cell->max_ival == 0); assert(cell->max_ival_count == 0); unsigned int *ep = elements + cell->first; for(unsigned int i = cell->length; i > 0; i--, ep++) { const unsigned int ival = invariant_values[*ep]; if(ival > cell->max_ival) { cell->max_ival = ival; cell->max_ival_count = 1; } else if(ival == cell->max_ival) { cell->max_ival_count++; } } } #ifdef CONSISTENCY_CHECKS /* Verify max_ival info */ { unsigned int max_ival = 0; unsigned int max_ival_count = 0; unsigned int *ep = elements + cell->first; for(unsigned int i = cell->length; i > 0; i--, ep++) { const unsigned int ival = invariant_values[*ep]; if(ival > max_ival) { max_ival = ival; max_ival_count = 1; } else if(ival == max_ival) { max_ival_count++; } } assert(max_ival == cell->max_ival); assert(max_ival_count == cell->max_ival_count); } #endif /* max_ival info has been computed */ if(cell->max_ival_count == cell->length) { /* All invariant values are the same */ if(cell->max_ival > 0) clear_ivs(cell); goto done; } /* All invariant values are not the same */ if(cell->max_ival == 1) { /* Specialized splitting for cells with binary invariant values */ last_new_cell = sort_and_split_cell1(cell); goto done; } if(cell->max_ival < 256) { /* Specialized splitting for cells with invariant values < 256 */ last_new_cell = sort_and_split_cell255(cell, cell->max_ival); goto done; } { /* Generic sorting and splitting */ const bool sorted = shellsort_cell(cell); assert(sorted); last_new_cell = split_cell(cell); goto done; } done: cell->max_ival = 0; cell->max_ival_count = 0; return last_new_cell; } void Partition::unrefine(unsigned int dest_split_level, unsigned int dest_refinement_stack_size) { assert(refinement_stack.size() >= dest_refinement_stack_size); while(refinement_stack.size() > dest_refinement_stack_size) { RefInfo i = refinement_stack.pop(); const unsigned int first = i.split_cell_first; //const unsigned int first = refinement_stack.pop(); Cell *cell = element_to_cell_map[elements[first]]; if(cell->first != first) { assert(cell->split_level <= dest_split_level); goto done; } if(cell->split_level <= dest_split_level) { goto done; } { const unsigned int new_first = cell->first; do { Cell * const next_cell = cell->next; assert(next_cell); /* (Pseudo)free cell */ cell->first = 0; cell->length = 0; cell->next->prev_next_ptr = cell->prev_next_ptr; *(cell->prev_next_ptr) = cell->next; cell->next = free_cells; if(cell->next) cell->next->prev_next_ptr = &(cell->next); cell->prev_next_ptr = &free_cells; free_cells = cell; cell = next_cell; } while(cell->split_level > dest_split_level); /* Update element_to_cell_map values of elements added in cell */ unsigned int *ep = elements + new_first; unsigned int * const lp = elements + cell->first; while(ep < lp) { element_to_cell_map[*ep] = cell; ep++; } /* Update cell parameters */ cell->length = (cell->first + cell->length) - new_first; cell->first = new_first; } done: if(i.prev_nonsingleton_first >= 0) { Cell * const prev_cell = element_to_cell_map[elements[i.prev_nonsingleton_first]]; DEBUG_ASSERT(prev_cell->length > 1); cell->prev_nonsingleton = prev_cell; prev_cell->next_nonsingleton = cell; } else { //assert(cell->prev_nonsingleton == 0); cell->prev_nonsingleton = 0; first_nonsingleton_cell = cell; } if(i.next_nonsingleton_first >= 0) { Cell * const next_cell = element_to_cell_map[elements[i.next_nonsingleton_first]]; DEBUG_ASSERT(next_cell->length > 1); cell->next_nonsingleton = next_cell; next_cell->prev_nonsingleton = cell; } else { //assert(cell->next_nonsingleton == 0); cell->next_nonsingleton = 0; } } consistency_check(); } } igraph/src/gengraph_powerlaw.h0000644000176000001440000000554612325527073016247 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _POWERLAW_H #define _POWERLAW_H // pascalou #ifndef pascalou #include "gengraph_definitions.h" #endif // Discrete integer power-law : P(X=min+k) is proportionnal to (k+k0)^-alpha // - possibility to determine a range [Min, Max] of possible samples // - possibility to automatically compute k0 to obtain a given mean z namespace gengraph { #define POWERLAW_TABLE 10000 class powerlaw { private: double alpha; // Exponent int mini; // Minimum sample int maxi; // Maximum sample double offset; // Offset int tabulated; // Number of values to tabulate int *table; // Table containing cumulative distribution for k=mini..mini+tabulated-1 int *dt; // Table delimiters int max_dt; // number of delimiters - 1 double proba_big; // Probability to take a non-tabulated value double table_mul; // equal to (1-proba_big)/(RAND_MAX+1) // Sample a non-tabulated value >= mini+tabulated inline double big_sample(double randomfloat) { return double(mini)+pow(_a * randomfloat + _b, _exp)-offset; } inline double big_inv_sample(double s) { return (pow(s-double(mini)+offset,1.0/_exp)-_b)/_a; } double _exp, _a, _b; // Cached values used by big_sample(); // Dichotomic adjust of offset, so that to_adjust() returns value with // a precision of eps. Note that to_adjust() must be an increasing function of offset. void adjust_offset_mean(double value, double eps, double fac); public: int sample(); // Return a random integer double proba(int); // Return probability to return integer double error(); // Returns relative numerical error done by this class double mean(); // Returns mean of the sampler int median(); // Returns median of the sampler // Initialize the power-law sampler. void init_to_offset(double, int); // Same, but also returns the offset found double init_to_mean(double); double init_to_median(double); inline void init() { init_to_offset(double(mini),POWERLAW_TABLE); }; ~powerlaw(); powerlaw(double exponent, int mini, int maxi=-1); }; } // namespace gengraph #endif //_POWERLAW_H igraph/src/glpmat.c0000644000176000001440000010056212325527073014005 0ustar ripleyusers/* glpmat.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif #include "glpenv.h" #include "glpmat.h" #include "glpqmd.h" #include "amd/amd.h" #include "colamd/colamd.h" /*---------------------------------------------------------------------- -- check_fvs - check sparse vector in full-vector storage format. -- -- SYNOPSIS -- -- #include "glpmat.h" -- int check_fvs(int n, int nnz, int ind[], double vec[]); -- -- DESCRIPTION -- -- The routine check_fvs checks if a given vector of dimension n in -- full-vector storage format has correct representation. -- -- RETURNS -- -- The routine returns one of the following codes: -- -- 0 - the vector is correct; -- 1 - the number of elements (n) is negative; -- 2 - the number of non-zero elements (nnz) is negative; -- 3 - some element index is out of range; -- 4 - some element index is duplicate; -- 5 - some non-zero element is out of pattern. */ int check_fvs(int n, int nnz, int ind[], double vec[]) { int i, t, ret, *flag = NULL; /* check the number of elements */ if (n < 0) { ret = 1; goto done; } /* check the number of non-zero elements */ if (nnz < 0) { ret = 2; goto done; } /* check vector indices */ flag = xcalloc(1+n, sizeof(int)); for (i = 1; i <= n; i++) flag[i] = 0; for (t = 1; t <= nnz; t++) { i = ind[t]; if (!(1 <= i && i <= n)) { ret = 3; goto done; } if (flag[i]) { ret = 4; goto done; } flag[i] = 1; } /* check vector elements */ for (i = 1; i <= n; i++) { if (!flag[i] && vec[i] != 0.0) { ret = 5; goto done; } } /* the vector is ok */ ret = 0; done: if (flag != NULL) xfree(flag); return ret; } /*---------------------------------------------------------------------- -- check_pattern - check pattern of sparse matrix. -- -- SYNOPSIS -- -- #include "glpmat.h" -- int check_pattern(int m, int n, int A_ptr[], int A_ind[]); -- -- DESCRIPTION -- -- The routine check_pattern checks the pattern of a given mxn matrix -- in storage-by-rows format. -- -- RETURNS -- -- The routine returns one of the following codes: -- -- 0 - the pattern is correct; -- 1 - the number of rows (m) is negative; -- 2 - the number of columns (n) is negative; -- 3 - A_ptr[1] is not 1; -- 4 - some column index is out of range; -- 5 - some column indices are duplicate. */ int check_pattern(int m, int n, int A_ptr[], int A_ind[]) { int i, j, ptr, ret, *flag = NULL; /* check the number of rows */ if (m < 0) { ret = 1; goto done; } /* check the number of columns */ if (n < 0) { ret = 2; goto done; } /* check location A_ptr[1] */ if (A_ptr[1] != 1) { ret = 3; goto done; } /* check row patterns */ flag = xcalloc(1+n, sizeof(int)); for (j = 1; j <= n; j++) flag[j] = 0; for (i = 1; i <= m; i++) { /* check pattern of row i */ for (ptr = A_ptr[i]; ptr < A_ptr[i+1]; ptr++) { j = A_ind[ptr]; /* check column index */ if (!(1 <= j && j <= n)) { ret = 4; goto done; } /* check for duplication */ if (flag[j]) { ret = 5; goto done; } flag[j] = 1; } /* clear flags */ for (ptr = A_ptr[i]; ptr < A_ptr[i+1]; ptr++) { j = A_ind[ptr]; flag[j] = 0; } } /* the pattern is ok */ ret = 0; done: if (flag != NULL) xfree(flag); return ret; } /*---------------------------------------------------------------------- -- transpose - transpose sparse matrix. -- -- *Synopsis* -- -- #include "glpmat.h" -- void transpose(int m, int n, int A_ptr[], int A_ind[], -- double A_val[], int AT_ptr[], int AT_ind[], double AT_val[]); -- -- *Description* -- -- For a given mxn sparse matrix A the routine transpose builds a nxm -- sparse matrix A' which is a matrix transposed to A. -- -- The arrays A_ptr, A_ind, and A_val specify a given mxn matrix A to -- be transposed in storage-by-rows format. The parameter A_val can be -- NULL, in which case numeric values are not copied. The arrays A_ptr, -- A_ind, and A_val are not changed on exit. -- -- On entry the arrays AT_ptr, AT_ind, and AT_val must be allocated, -- but their content is ignored. On exit the routine stores a resultant -- nxm matrix A' in these arrays in storage-by-rows format. Note that -- if the parameter A_val is NULL, the array AT_val is not used. -- -- The routine transpose has a side effect that elements in rows of the -- resultant matrix A' follow in ascending their column indices. */ void transpose(int m, int n, int A_ptr[], int A_ind[], double A_val[], int AT_ptr[], int AT_ind[], double AT_val[]) { int i, j, t, beg, end, pos, len; /* determine row lengths of resultant matrix */ for (j = 1; j <= n; j++) AT_ptr[j] = 0; for (i = 1; i <= m; i++) { beg = A_ptr[i], end = A_ptr[i+1]; for (t = beg; t < end; t++) AT_ptr[A_ind[t]]++; } /* set up row pointers of resultant matrix */ pos = 1; for (j = 1; j <= n; j++) len = AT_ptr[j], pos += len, AT_ptr[j] = pos; AT_ptr[n+1] = pos; /* build resultant matrix */ for (i = m; i >= 1; i--) { beg = A_ptr[i], end = A_ptr[i+1]; for (t = beg; t < end; t++) { pos = --AT_ptr[A_ind[t]]; AT_ind[pos] = i; if (A_val != NULL) AT_val[pos] = A_val[t]; } } return; } /*---------------------------------------------------------------------- -- adat_symbolic - compute S = P*A*D*A'*P' (symbolic phase). -- -- *Synopsis* -- -- #include "glpmat.h" -- int *adat_symbolic(int m, int n, int P_per[], int A_ptr[], -- int A_ind[], int S_ptr[]); -- -- *Description* -- -- The routine adat_symbolic implements the symbolic phase to compute -- symmetric matrix S = P*A*D*A'*P', where P is a permutation matrix, -- A is a given sparse matrix, D is a diagonal matrix, A' is a matrix -- transposed to A, P' is an inverse of P. -- -- The parameter m is the number of rows in A and the order of P. -- -- The parameter n is the number of columns in A and the order of D. -- -- The array P_per specifies permutation matrix P. It is not changed on -- exit. -- -- The arrays A_ptr and A_ind specify the pattern of matrix A. They are -- not changed on exit. -- -- On exit the routine stores the pattern of upper triangular part of -- matrix S without diagonal elements in the arrays S_ptr and S_ind in -- storage-by-rows format. The array S_ptr should be allocated on entry, -- however, its content is ignored. The array S_ind is allocated by the -- routine itself which returns a pointer to it. -- -- *Returns* -- -- The routine returns a pointer to the array S_ind. */ int *adat_symbolic(int m, int n, int P_per[], int A_ptr[], int A_ind[], int S_ptr[]) { int i, j, t, ii, jj, tt, k, size, len; int *S_ind, *AT_ptr, *AT_ind, *ind, *map, *temp; /* build the pattern of A', which is a matrix transposed to A, to efficiently access A in column-wise manner */ AT_ptr = xcalloc(1+n+1, sizeof(int)); AT_ind = xcalloc(A_ptr[m+1], sizeof(int)); transpose(m, n, A_ptr, A_ind, NULL, AT_ptr, AT_ind, NULL); /* allocate the array S_ind */ size = A_ptr[m+1] - 1; if (size < m) size = m; S_ind = xcalloc(1+size, sizeof(int)); /* allocate and initialize working arrays */ ind = xcalloc(1+m, sizeof(int)); map = xcalloc(1+m, sizeof(int)); for (jj = 1; jj <= m; jj++) map[jj] = 0; /* compute pattern of S; note that symbolically S = B*B', where B = P*A, B' is matrix transposed to B */ S_ptr[1] = 1; for (ii = 1; ii <= m; ii++) { /* compute pattern of ii-th row of S */ len = 0; i = P_per[ii]; /* i-th row of A = ii-th row of B */ for (t = A_ptr[i]; t < A_ptr[i+1]; t++) { k = A_ind[t]; /* walk through k-th column of A */ for (tt = AT_ptr[k]; tt < AT_ptr[k+1]; tt++) { j = AT_ind[tt]; jj = P_per[m+j]; /* j-th row of A = jj-th row of B */ /* a[i,k] != 0 and a[j,k] != 0 ergo s[ii,jj] != 0 */ if (ii < jj && !map[jj]) ind[++len] = jj, map[jj] = 1; } } /* now (ind) is pattern of ii-th row of S */ S_ptr[ii+1] = S_ptr[ii] + len; /* at least (S_ptr[ii+1] - 1) locations should be available in the array S_ind */ if (S_ptr[ii+1] - 1 > size) { temp = S_ind; size += size; S_ind = xcalloc(1+size, sizeof(int)); memcpy(&S_ind[1], &temp[1], (S_ptr[ii] - 1) * sizeof(int)); xfree(temp); } xassert(S_ptr[ii+1] - 1 <= size); /* (ii-th row of S) := (ind) */ memcpy(&S_ind[S_ptr[ii]], &ind[1], len * sizeof(int)); /* clear the row pattern map */ for (t = 1; t <= len; t++) map[ind[t]] = 0; } /* free working arrays */ xfree(AT_ptr); xfree(AT_ind); xfree(ind); xfree(map); /* reallocate the array S_ind to free unused locations */ temp = S_ind; size = S_ptr[m+1] - 1; S_ind = xcalloc(1+size, sizeof(int)); memcpy(&S_ind[1], &temp[1], size * sizeof(int)); xfree(temp); return S_ind; } /*---------------------------------------------------------------------- -- adat_numeric - compute S = P*A*D*A'*P' (numeric phase). -- -- *Synopsis* -- -- #include "glpmat.h" -- void adat_numeric(int m, int n, int P_per[], -- int A_ptr[], int A_ind[], double A_val[], double D_diag[], -- int S_ptr[], int S_ind[], double S_val[], double S_diag[]); -- -- *Description* -- -- The routine adat_numeric implements the numeric phase to compute -- symmetric matrix S = P*A*D*A'*P', where P is a permutation matrix, -- A is a given sparse matrix, D is a diagonal matrix, A' is a matrix -- transposed to A, P' is an inverse of P. -- -- The parameter m is the number of rows in A and the order of P. -- -- The parameter n is the number of columns in A and the order of D. -- -- The matrix P is specified in the array P_per, which is not changed -- on exit. -- -- The matrix A is specified in the arrays A_ptr, A_ind, and A_val in -- storage-by-rows format. These arrays are not changed on exit. -- -- Diagonal elements of the matrix D are specified in the array D_diag, -- where D_diag[0] is not used, D_diag[i] = d[i,i] for i = 1, ..., n. -- The array D_diag is not changed on exit. -- -- The pattern of the upper triangular part of the matrix S without -- diagonal elements (previously computed by the routine adat_symbolic) -- is specified in the arrays S_ptr and S_ind, which are not changed on -- exit. Numeric values of non-diagonal elements of S are stored in -- corresponding locations of the array S_val, and values of diagonal -- elements of S are stored in locations S_diag[1], ..., S_diag[n]. */ void adat_numeric(int m, int n, int P_per[], int A_ptr[], int A_ind[], double A_val[], double D_diag[], int S_ptr[], int S_ind[], double S_val[], double S_diag[]) { int i, j, t, ii, jj, tt, beg, end, beg1, end1, k; double sum, *work; work = xcalloc(1+n, sizeof(double)); for (j = 1; j <= n; j++) work[j] = 0.0; /* compute S = B*D*B', where B = P*A, B' is a matrix transposed to B */ for (ii = 1; ii <= m; ii++) { i = P_per[ii]; /* i-th row of A = ii-th row of B */ /* (work) := (i-th row of A) */ beg = A_ptr[i], end = A_ptr[i+1]; for (t = beg; t < end; t++) work[A_ind[t]] = A_val[t]; /* compute ii-th row of S */ beg = S_ptr[ii], end = S_ptr[ii+1]; for (t = beg; t < end; t++) { jj = S_ind[t]; j = P_per[jj]; /* j-th row of A = jj-th row of B */ /* s[ii,jj] := sum a[i,k] * d[k,k] * a[j,k] */ sum = 0.0; beg1 = A_ptr[j], end1 = A_ptr[j+1]; for (tt = beg1; tt < end1; tt++) { k = A_ind[tt]; sum += work[k] * D_diag[k] * A_val[tt]; } S_val[t] = sum; } /* s[ii,ii] := sum a[i,k] * d[k,k] * a[i,k] */ sum = 0.0; beg = A_ptr[i], end = A_ptr[i+1]; for (t = beg; t < end; t++) { k = A_ind[t]; sum += A_val[t] * D_diag[k] * A_val[t]; work[k] = 0.0; } S_diag[ii] = sum; } xfree(work); return; } /*---------------------------------------------------------------------- -- min_degree - minimum degree ordering. -- -- *Synopsis* -- -- #include "glpmat.h" -- void min_degree(int n, int A_ptr[], int A_ind[], int P_per[]); -- -- *Description* -- -- The routine min_degree uses the minimum degree ordering algorithm -- to find a permutation matrix P for a given sparse symmetric positive -- matrix A which minimizes the number of non-zeros in upper triangular -- factor U for Cholesky factorization P*A*P' = U'*U. -- -- The parameter n is the order of matrices A and P. -- -- The pattern of the given matrix A is specified on entry in the arrays -- A_ptr and A_ind in storage-by-rows format. Only the upper triangular -- part without diagonal elements (which all are assumed to be non-zero) -- should be specified as if A were upper triangular. The arrays A_ptr -- and A_ind are not changed on exit. -- -- The permutation matrix P is stored by the routine in the array P_per -- on exit. -- -- *Algorithm* -- -- The routine min_degree is based on some subroutines from the package -- SPARSPAK (see comments in the module glpqmd). */ void min_degree(int n, int A_ptr[], int A_ind[], int P_per[]) { int i, j, ne, t, pos, len; int *xadj, *adjncy, *deg, *marker, *rchset, *nbrhd, *qsize, *qlink, nofsub; /* determine number of non-zeros in complete pattern */ ne = A_ptr[n+1] - 1; ne += ne; /* allocate working arrays */ xadj = xcalloc(1+n+1, sizeof(int)); adjncy = xcalloc(1+ne, sizeof(int)); deg = xcalloc(1+n, sizeof(int)); marker = xcalloc(1+n, sizeof(int)); rchset = xcalloc(1+n, sizeof(int)); nbrhd = xcalloc(1+n, sizeof(int)); qsize = xcalloc(1+n, sizeof(int)); qlink = xcalloc(1+n, sizeof(int)); /* determine row lengths in complete pattern */ for (i = 1; i <= n; i++) xadj[i] = 0; for (i = 1; i <= n; i++) { for (t = A_ptr[i]; t < A_ptr[i+1]; t++) { j = A_ind[t]; xassert(i < j && j <= n); xadj[i]++, xadj[j]++; } } /* set up row pointers for complete pattern */ pos = 1; for (i = 1; i <= n; i++) len = xadj[i], pos += len, xadj[i] = pos; xadj[n+1] = pos; xassert(pos - 1 == ne); /* construct complete pattern */ for (i = 1; i <= n; i++) { for (t = A_ptr[i]; t < A_ptr[i+1]; t++) { j = A_ind[t]; adjncy[--xadj[i]] = j, adjncy[--xadj[j]] = i; } } /* call the main minimimum degree ordering routine */ genqmd(&n, xadj, adjncy, P_per, P_per + n, deg, marker, rchset, nbrhd, qsize, qlink, &nofsub); /* make sure that permutation matrix P is correct */ for (i = 1; i <= n; i++) { j = P_per[i]; xassert(1 <= j && j <= n); xassert(P_per[n+j] == i); } /* free working arrays */ xfree(xadj); xfree(adjncy); xfree(deg); xfree(marker); xfree(rchset); xfree(nbrhd); xfree(qsize); xfree(qlink); return; } /**********************************************************************/ void amd_order1(int n, int A_ptr[], int A_ind[], int P_per[]) { /* approximate minimum degree ordering (AMD) */ int k, ret; double Control[AMD_CONTROL], Info[AMD_INFO]; /* get the default parameters */ amd_defaults(Control); #if 0 /* and print them */ amd_control(Control); #endif /* make all indices 0-based */ for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]--; for (k = 1; k <= n+1; k++) A_ptr[k]--; /* call the ordering routine */ ret = amd_order(n, &A_ptr[1], &A_ind[1], &P_per[1], Control, Info) ; #if 0 amd_info(Info); #endif xassert(ret == AMD_OK || ret == AMD_OK_BUT_JUMBLED); /* retsore 1-based indices */ for (k = 1; k <= n+1; k++) A_ptr[k]++; for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]++; /* patch up permutation matrix */ memset(&P_per[n+1], 0, n * sizeof(int)); for (k = 1; k <= n; k++) { P_per[k]++; xassert(1 <= P_per[k] && P_per[k] <= n); xassert(P_per[n+P_per[k]] == 0); P_per[n+P_per[k]] = k; } return; } /**********************************************************************/ static void *allocate(size_t n, size_t size) { void *ptr; ptr = xcalloc(n, size); memset(ptr, 0, n * size); return ptr; } static void release(void *ptr) { xfree(ptr); return; } void symamd_ord(int n, int A_ptr[], int A_ind[], int P_per[]) { /* approximate minimum degree ordering (SYMAMD) */ int k, ok; int stats[COLAMD_STATS]; /* make all indices 0-based */ for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]--; for (k = 1; k <= n+1; k++) A_ptr[k]--; /* call the ordering routine */ ok = symamd(n, &A_ind[1], &A_ptr[1], &P_per[1], NULL, stats, allocate, release); #if 0 symamd_report(stats); #endif xassert(ok); /* restore 1-based indices */ for (k = 1; k <= n+1; k++) A_ptr[k]++; for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]++; /* patch up permutation matrix */ memset(&P_per[n+1], 0, n * sizeof(int)); for (k = 1; k <= n; k++) { P_per[k]++; xassert(1 <= P_per[k] && P_per[k] <= n); xassert(P_per[n+P_per[k]] == 0); P_per[n+P_per[k]] = k; } return; } /*---------------------------------------------------------------------- -- chol_symbolic - compute Cholesky factorization (symbolic phase). -- -- *Synopsis* -- -- #include "glpmat.h" -- int *chol_symbolic(int n, int A_ptr[], int A_ind[], int U_ptr[]); -- -- *Description* -- -- The routine chol_symbolic implements the symbolic phase of Cholesky -- factorization A = U'*U, where A is a given sparse symmetric positive -- definite matrix, U is a resultant upper triangular factor, U' is a -- matrix transposed to U. -- -- The parameter n is the order of matrices A and U. -- -- The pattern of the given matrix A is specified on entry in the arrays -- A_ptr and A_ind in storage-by-rows format. Only the upper triangular -- part without diagonal elements (which all are assumed to be non-zero) -- should be specified as if A were upper triangular. The arrays A_ptr -- and A_ind are not changed on exit. -- -- The pattern of the matrix U without diagonal elements (which all are -- assumed to be non-zero) is stored on exit from the routine in the -- arrays U_ptr and U_ind in storage-by-rows format. The array U_ptr -- should be allocated on entry, however, its content is ignored. The -- array U_ind is allocated by the routine which returns a pointer to it -- on exit. -- -- *Returns* -- -- The routine returns a pointer to the array U_ind. -- -- *Method* -- -- The routine chol_symbolic computes the pattern of the matrix U in a -- row-wise manner. No pivoting is used. -- -- It is known that to compute the pattern of row k of the matrix U we -- need to merge the pattern of row k of the matrix A and the patterns -- of each row i of U, where u[i,k] is non-zero (these rows are already -- computed and placed above row k). -- -- However, to reduce the number of rows to be merged the routine uses -- an advanced algorithm proposed in: -- -- D.J.Rose, R.E.Tarjan, and G.S.Lueker. Algorithmic aspects of vertex -- elimination on graphs. SIAM J. Comput. 5, 1976, 266-83. -- -- The authors of the cited paper show that we have the same result if -- we merge row k of the matrix A and such rows of the matrix U (among -- rows 1, ..., k-1) whose leftmost non-diagonal non-zero element is -- placed in k-th column. This feature signficantly reduces the number -- of rows to be merged, especially on the final steps, where rows of -- the matrix U become quite dense. -- -- To determine rows, which should be merged on k-th step, for a fixed -- time the routine uses linked lists of row numbers of the matrix U. -- Location head[k] contains the number of a first row, whose leftmost -- non-diagonal non-zero element is placed in column k, and location -- next[i] contains the number of a next row with the same property as -- row i. */ int *chol_symbolic(int n, int A_ptr[], int A_ind[], int U_ptr[]) { int i, j, k, t, len, size, beg, end, min_j, *U_ind, *head, *next, *ind, *map, *temp; /* initially we assume that on computing the pattern of U fill-in will double the number of non-zeros in A */ size = A_ptr[n+1] - 1; if (size < n) size = n; size += size; U_ind = xcalloc(1+size, sizeof(int)); /* allocate and initialize working arrays */ head = xcalloc(1+n, sizeof(int)); for (i = 1; i <= n; i++) head[i] = 0; next = xcalloc(1+n, sizeof(int)); ind = xcalloc(1+n, sizeof(int)); map = xcalloc(1+n, sizeof(int)); for (j = 1; j <= n; j++) map[j] = 0; /* compute the pattern of matrix U */ U_ptr[1] = 1; for (k = 1; k <= n; k++) { /* compute the pattern of k-th row of U, which is the union of k-th row of A and those rows of U (among 1, ..., k-1) whose leftmost non-diagonal non-zero is placed in k-th column */ /* (ind) := (k-th row of A) */ len = A_ptr[k+1] - A_ptr[k]; memcpy(&ind[1], &A_ind[A_ptr[k]], len * sizeof(int)); for (t = 1; t <= len; t++) { j = ind[t]; xassert(k < j && j <= n); map[j] = 1; } /* walk through rows of U whose leftmost non-diagonal non-zero is placed in k-th column */ for (i = head[k]; i != 0; i = next[i]) { /* (ind) := (ind) union (i-th row of U) */ beg = U_ptr[i], end = U_ptr[i+1]; for (t = beg; t < end; t++) { j = U_ind[t]; if (j > k && !map[j]) ind[++len] = j, map[j] = 1; } } /* now (ind) is the pattern of k-th row of U */ U_ptr[k+1] = U_ptr[k] + len; /* at least (U_ptr[k+1] - 1) locations should be available in the array U_ind */ if (U_ptr[k+1] - 1 > size) { temp = U_ind; size += size; U_ind = xcalloc(1+size, sizeof(int)); memcpy(&U_ind[1], &temp[1], (U_ptr[k] - 1) * sizeof(int)); xfree(temp); } xassert(U_ptr[k+1] - 1 <= size); /* (k-th row of U) := (ind) */ memcpy(&U_ind[U_ptr[k]], &ind[1], len * sizeof(int)); /* determine column index of leftmost non-diagonal non-zero in k-th row of U and clear the row pattern map */ min_j = n + 1; for (t = 1; t <= len; t++) { j = ind[t], map[j] = 0; if (min_j > j) min_j = j; } /* include k-th row into corresponding linked list */ if (min_j <= n) next[k] = head[min_j], head[min_j] = k; } /* free working arrays */ xfree(head); xfree(next); xfree(ind); xfree(map); /* reallocate the array U_ind to free unused locations */ temp = U_ind; size = U_ptr[n+1] - 1; U_ind = xcalloc(1+size, sizeof(int)); memcpy(&U_ind[1], &temp[1], size * sizeof(int)); xfree(temp); return U_ind; } /*---------------------------------------------------------------------- -- chol_numeric - compute Cholesky factorization (numeric phase). -- -- *Synopsis* -- -- #include "glpmat.h" -- int chol_numeric(int n, -- int A_ptr[], int A_ind[], double A_val[], double A_diag[], -- int U_ptr[], int U_ind[], double U_val[], double U_diag[]); -- -- *Description* -- -- The routine chol_symbolic implements the numeric phase of Cholesky -- factorization A = U'*U, where A is a given sparse symmetric positive -- definite matrix, U is a resultant upper triangular factor, U' is a -- matrix transposed to U. -- -- The parameter n is the order of matrices A and U. -- -- Upper triangular part of the matrix A without diagonal elements is -- specified in the arrays A_ptr, A_ind, and A_val in storage-by-rows -- format. Diagonal elements of A are specified in the array A_diag, -- where A_diag[0] is not used, A_diag[i] = a[i,i] for i = 1, ..., n. -- The arrays A_ptr, A_ind, A_val, and A_diag are not changed on exit. -- -- The pattern of the matrix U without diagonal elements (previously -- computed with the routine chol_symbolic) is specified in the arrays -- U_ptr and U_ind, which are not changed on exit. Numeric values of -- non-diagonal elements of U are stored in corresponding locations of -- the array U_val, and values of diagonal elements of U are stored in -- locations U_diag[1], ..., U_diag[n]. -- -- *Returns* -- -- The routine returns the number of non-positive diagonal elements of -- the matrix U which have been replaced by a huge positive number (see -- the method description below). Zero return code means the matrix A -- has been successfully factorized. -- -- *Method* -- -- The routine chol_numeric computes the matrix U in a row-wise manner -- using standard gaussian elimination technique. No pivoting is used. -- -- Initially the routine sets U = A, and before k-th elimination step -- the matrix U is the following: -- -- 1 k n -- 1 x x x x x x x x x x -- . x x x x x x x x x -- . . x x x x x x x x -- . . . x x x x x x x -- k . . . . * * * * * * -- . . . . * * * * * * -- . . . . * * * * * * -- . . . . * * * * * * -- . . . . * * * * * * -- n . . . . * * * * * * -- -- where 'x' are elements of already computed rows, '*' are elements of -- the active submatrix. (Note that the lower triangular part of the -- active submatrix being symmetric is not stored and diagonal elements -- are stored separately in the array U_diag.) -- -- The matrix A is assumed to be positive definite. However, if it is -- close to semi-definite, on some elimination step a pivot u[k,k] may -- happen to be non-positive due to round-off errors. In this case the -- routine uses a technique proposed in: -- -- S.J.Wright. The Cholesky factorization in interior-point and barrier -- methods. Preprint MCS-P600-0596, Mathematics and Computer Science -- Division, Argonne National Laboratory, Argonne, Ill., May 1996. -- -- The routine just replaces non-positive u[k,k] by a huge positive -- number. This involves non-diagonal elements in k-th row of U to be -- close to zero that, in turn, involves k-th component of a solution -- vector to be close to zero. Note, however, that this technique works -- only if the system A*x = b is consistent. */ int chol_numeric(int n, int A_ptr[], int A_ind[], double A_val[], double A_diag[], int U_ptr[], int U_ind[], double U_val[], double U_diag[]) { int i, j, k, t, t1, beg, end, beg1, end1, count = 0; double ukk, uki, *work; work = xcalloc(1+n, sizeof(double)); for (j = 1; j <= n; j++) work[j] = 0.0; /* U := (upper triangle of A) */ /* note that the upper traingle of A is a subset of U */ for (i = 1; i <= n; i++) { beg = A_ptr[i], end = A_ptr[i+1]; for (t = beg; t < end; t++) j = A_ind[t], work[j] = A_val[t]; beg = U_ptr[i], end = U_ptr[i+1]; for (t = beg; t < end; t++) j = U_ind[t], U_val[t] = work[j], work[j] = 0.0; U_diag[i] = A_diag[i]; } /* main elimination loop */ for (k = 1; k <= n; k++) { /* transform k-th row of U */ ukk = U_diag[k]; if (ukk > 0.0) U_diag[k] = ukk = sqrt(ukk); else U_diag[k] = ukk = DBL_MAX, count++; /* (work) := (transformed k-th row) */ beg = U_ptr[k], end = U_ptr[k+1]; for (t = beg; t < end; t++) work[U_ind[t]] = (U_val[t] /= ukk); /* transform other rows of U */ for (t = beg; t < end; t++) { i = U_ind[t]; xassert(i > k); /* (i-th row) := (i-th row) - u[k,i] * (k-th row) */ uki = work[i]; beg1 = U_ptr[i], end1 = U_ptr[i+1]; for (t1 = beg1; t1 < end1; t1++) U_val[t1] -= uki * work[U_ind[t1]]; U_diag[i] -= uki * uki; } /* (work) := 0 */ for (t = beg; t < end; t++) work[U_ind[t]] = 0.0; } xfree(work); return count; } /*---------------------------------------------------------------------- -- u_solve - solve upper triangular system U*x = b. -- -- *Synopsis* -- -- #include "glpmat.h" -- void u_solve(int n, int U_ptr[], int U_ind[], double U_val[], -- double U_diag[], double x[]); -- -- *Description* -- -- The routine u_solve solves an linear system U*x = b, where U is an -- upper triangular matrix. -- -- The parameter n is the order of matrix U. -- -- The matrix U without diagonal elements is specified in the arrays -- U_ptr, U_ind, and U_val in storage-by-rows format. Diagonal elements -- of U are specified in the array U_diag, where U_diag[0] is not used, -- U_diag[i] = u[i,i] for i = 1, ..., n. All these four arrays are not -- changed on exit. -- -- The right-hand side vector b is specified on entry in the array x, -- where x[0] is not used, and x[i] = b[i] for i = 1, ..., n. On exit -- the routine stores computed components of the vector of unknowns x -- in the array x in the same manner. */ void u_solve(int n, int U_ptr[], int U_ind[], double U_val[], double U_diag[], double x[]) { int i, t, beg, end; double temp; for (i = n; i >= 1; i--) { temp = x[i]; beg = U_ptr[i], end = U_ptr[i+1]; for (t = beg; t < end; t++) temp -= U_val[t] * x[U_ind[t]]; xassert(U_diag[i] != 0.0); x[i] = temp / U_diag[i]; } return; } /*---------------------------------------------------------------------- -- ut_solve - solve lower triangular system U'*x = b. -- -- *Synopsis* -- -- #include "glpmat.h" -- void ut_solve(int n, int U_ptr[], int U_ind[], double U_val[], -- double U_diag[], double x[]); -- -- *Description* -- -- The routine ut_solve solves an linear system U'*x = b, where U is a -- matrix transposed to an upper triangular matrix. -- -- The parameter n is the order of matrix U. -- -- The matrix U without diagonal elements is specified in the arrays -- U_ptr, U_ind, and U_val in storage-by-rows format. Diagonal elements -- of U are specified in the array U_diag, where U_diag[0] is not used, -- U_diag[i] = u[i,i] for i = 1, ..., n. All these four arrays are not -- changed on exit. -- -- The right-hand side vector b is specified on entry in the array x, -- where x[0] is not used, and x[i] = b[i] for i = 1, ..., n. On exit -- the routine stores computed components of the vector of unknowns x -- in the array x in the same manner. */ void ut_solve(int n, int U_ptr[], int U_ind[], double U_val[], double U_diag[], double x[]) { int i, t, beg, end; double temp; for (i = 1; i <= n; i++) { xassert(U_diag[i] != 0.0); temp = (x[i] /= U_diag[i]); if (temp == 0.0) continue; beg = U_ptr[i], end = U_ptr[i+1]; for (t = beg; t < end; t++) x[U_ind[t]] -= U_val[t] * temp; } return; } /* eof */ igraph/src/glpios04.c0000644000176000001440000001675612325527073014175 0ustar ripleyusers/* glpios04.c (operations on sparse vectors) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpios.h" /*********************************************************************** * NAME * * ios_create_vec - create sparse vector * * SYNOPSIS * * #include "glpios.h" * IOSVEC *ios_create_vec(int n); * * DESCRIPTION * * The routine ios_create_vec creates a sparse vector of dimension n, * which initially is a null vector. * * RETURNS * * The routine returns a pointer to the vector created. */ IOSVEC *ios_create_vec(int n) { IOSVEC *v; xassert(n >= 0); v = xmalloc(sizeof(IOSVEC)); v->n = n; v->nnz = 0; v->pos = xcalloc(1+n, sizeof(int)); memset(&v->pos[1], 0, n * sizeof(int)); v->ind = xcalloc(1+n, sizeof(int)); v->val = xcalloc(1+n, sizeof(double)); return v; } /*********************************************************************** * NAME * * ios_check_vec - check that sparse vector has correct representation * * SYNOPSIS * * #include "glpios.h" * void ios_check_vec(IOSVEC *v); * * DESCRIPTION * * The routine ios_check_vec checks that a sparse vector specified by * the parameter v has correct representation. * * NOTE * * Complexity of this operation is O(n). */ void ios_check_vec(IOSVEC *v) { int j, k, nnz; xassert(v->n >= 0); nnz = 0; for (j = v->n; j >= 1; j--) { k = v->pos[j]; xassert(0 <= k && k <= v->nnz); if (k != 0) { xassert(v->ind[k] == j); nnz++; } } xassert(v->nnz == nnz); return; } /*********************************************************************** * NAME * * ios_get_vj - retrieve component of sparse vector * * SYNOPSIS * * #include "glpios.h" * double ios_get_vj(IOSVEC *v, int j); * * RETURNS * * The routine ios_get_vj returns j-th component of a sparse vector * specified by the parameter v. */ double ios_get_vj(IOSVEC *v, int j) { int k; xassert(1 <= j && j <= v->n); k = v->pos[j]; xassert(0 <= k && k <= v->nnz); return (k == 0 ? 0.0 : v->val[k]); } /*********************************************************************** * NAME * * ios_set_vj - set/change component of sparse vector * * SYNOPSIS * * #include "glpios.h" * void ios_set_vj(IOSVEC *v, int j, double val); * * DESCRIPTION * * The routine ios_set_vj assigns val to j-th component of a sparse * vector specified by the parameter v. */ void ios_set_vj(IOSVEC *v, int j, double val) { int k; xassert(1 <= j && j <= v->n); k = v->pos[j]; if (val == 0.0) { if (k != 0) { /* remove j-th component */ v->pos[j] = 0; if (k < v->nnz) { v->pos[v->ind[v->nnz]] = k; v->ind[k] = v->ind[v->nnz]; v->val[k] = v->val[v->nnz]; } v->nnz--; } } else { if (k == 0) { /* create j-th component */ k = ++(v->nnz); v->pos[j] = k; v->ind[k] = j; } v->val[k] = val; } return; } /*********************************************************************** * NAME * * ios_clear_vec - set all components of sparse vector to zero * * SYNOPSIS * * #include "glpios.h" * void ios_clear_vec(IOSVEC *v); * * DESCRIPTION * * The routine ios_clear_vec sets all components of a sparse vector * specified by the parameter v to zero. */ void ios_clear_vec(IOSVEC *v) { int k; for (k = 1; k <= v->nnz; k++) v->pos[v->ind[k]] = 0; v->nnz = 0; return; } /*********************************************************************** * NAME * * ios_clean_vec - remove zero or small components from sparse vector * * SYNOPSIS * * #include "glpios.h" * void ios_clean_vec(IOSVEC *v, double eps); * * DESCRIPTION * * The routine ios_clean_vec removes zero components and components * whose magnitude is less than eps from a sparse vector specified by * the parameter v. If eps is 0.0, only zero components are removed. */ void ios_clean_vec(IOSVEC *v, double eps) { int k, nnz; nnz = 0; for (k = 1; k <= v->nnz; k++) { if (fabs(v->val[k]) == 0.0 || fabs(v->val[k]) < eps) { /* remove component */ v->pos[v->ind[k]] = 0; } else { /* keep component */ nnz++; v->pos[v->ind[k]] = nnz; v->ind[nnz] = v->ind[k]; v->val[nnz] = v->val[k]; } } v->nnz = nnz; return; } /*********************************************************************** * NAME * * ios_copy_vec - copy sparse vector (x := y) * * SYNOPSIS * * #include "glpios.h" * void ios_copy_vec(IOSVEC *x, IOSVEC *y); * * DESCRIPTION * * The routine ios_copy_vec copies a sparse vector specified by the * parameter y to a sparse vector specified by the parameter x. */ void ios_copy_vec(IOSVEC *x, IOSVEC *y) { int j; xassert(x != y); xassert(x->n == y->n); ios_clear_vec(x); x->nnz = y->nnz; memcpy(&x->ind[1], &y->ind[1], x->nnz * sizeof(int)); memcpy(&x->val[1], &y->val[1], x->nnz * sizeof(double)); for (j = 1; j <= x->nnz; j++) x->pos[x->ind[j]] = j; return; } /*********************************************************************** * NAME * * ios_linear_comb - compute linear combination (x := x + a * y) * * SYNOPSIS * * #include "glpios.h" * void ios_linear_comb(IOSVEC *x, double a, IOSVEC *y); * * DESCRIPTION * * The routine ios_linear_comb computes the linear combination * * x := x + a * y, * * where x and y are sparse vectors, a is a scalar. */ void ios_linear_comb(IOSVEC *x, double a, IOSVEC *y) { int j, k; double xj, yj; xassert(x != y); xassert(x->n == y->n); for (k = 1; k <= y->nnz; k++) { j = y->ind[k]; xj = ios_get_vj(x, j); yj = y->val[k]; ios_set_vj(x, j, xj + a * yj); } return; } /*********************************************************************** * NAME * * ios_delete_vec - delete sparse vector * * SYNOPSIS * * #include "glpios.h" * void ios_delete_vec(IOSVEC *v); * * DESCRIPTION * * The routine ios_delete_vec deletes a sparse vector specified by the * parameter v freeing all the memory allocated to this object. */ void ios_delete_vec(IOSVEC *v) { /* delete sparse vector */ xfree(v->pos); xfree(v->ind); xfree(v->val); xfree(v); return; } /* eof */ igraph/src/maximal_cliques_template.h0000644000176000001440000002450212325527073017575 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2013 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef IGRAPH_MC_ORIG #define RESTYPE igraph_vector_ptr_t *res #define RESNAME res #define SUFFIX #define RECORD do { \ igraph_vector_t *cl=igraph_Calloc(1, igraph_vector_t); \ int j; \ if (!cl) { \ IGRAPH_ERROR("Cannot list maximal cliques", IGRAPH_ENOMEM); \ } \ igraph_vector_ptr_push_back(res, cl); \ igraph_vector_init(cl, clsize); \ for (j=0; j PE && XS > XE) { /* Found a maximum clique, report it */ int clsize=igraph_vector_int_size(R); if (min_size <= clsize && (clsize <= max_size || max_size <= 0)) { RECORD; } } else if (PS <= PE) { /* Select a pivot element */ int pivot, mynextv; igraph_i_maximal_cliques_select_pivot(PX, PS, PE, XS, XE, pos, adjlist, &pivot, nextv, oldPS, oldXE); while ((mynextv=igraph_vector_int_pop_back(nextv)) != -1) { int newPS, newXE; /* Going down, prepare */ igraph_i_maximal_cliques_down(PX, PS, PE, XS, XE, pos, adjlist, mynextv, R, &newPS, &newXE); /* Recursive call */ FUNCTION(igraph_i_maximal_cliques_bk,SUFFIX)( PX, newPS, PE, XS, newXE, PS, XE, R, pos, adjlist, RESNAME, nextv, H, min_size, max_size); /* Putting v from P to X */ if (igraph_vector_int_tail(nextv) != -1) { igraph_i_maximal_cliques_PX(PX, PS, &PE, &XS, XE, pos, adjlist, mynextv, H); } } } /* Putting back vertices from X to P, see notes in H */ igraph_i_maximal_cliques_up(PX, PS, PE, XS, XE, pos, adjlist, R, H); return 0; } int FUNCTION(igraph_maximal_cliques,SUFFIX)( const igraph_t *graph, RESTYPE, igraph_integer_t min_size, igraph_integer_t max_size) { /* Implementation details. TODO */ igraph_vector_int_t PX, R, H, pos, nextv; igraph_vector_t coreness, order; igraph_vector_int_t rank; /* TODO: this is not needed */ int i, ii, nn, no_of_nodes=igraph_vcount(graph); igraph_adjlist_t adjlist, fulladjlist; igraph_real_t pgreset=round(no_of_nodes / 100.0), pg=pgreset, pgc=0; IGRAPH_UNUSED(nn); if (igraph_is_directed(graph)) { IGRAPH_WARNING("Edge directions are ignored for maximal clique " "calculation"); } igraph_vector_init(&order, no_of_nodes); IGRAPH_FINALLY(igraph_vector_destroy, &order); igraph_vector_int_init(&rank, no_of_nodes); IGRAPH_FINALLY(igraph_vector_int_destroy, &rank); igraph_vector_init(&coreness, no_of_nodes); igraph_coreness(graph, &coreness, /*mode=*/ IGRAPH_ALL); IGRAPH_FINALLY(igraph_vector_destroy, &coreness); igraph_vector_qsort_ind(&coreness, &order, /*descending=*/ 0); for (ii=0; ii vrank) { VECTOR(PX)[Pptr] = vx; VECTOR(pos)[vx] = Pptr+1; Pptr++; } else if (VECTOR(rank)[vx] < vrank) { VECTOR(PX)[Xptr] = vx; VECTOR(pos)[vx] = Xptr+1; Xptr--; } } PE = Pptr-1; XS = Xptr+1; /* end of P, start of X in PX */ /* Create an adjacency list that is specific to the v vertex. It only contains 'v' and its neighbors. Moreover, we only deal with the vertices in P and X (and R). */ igraph_vector_int_update(igraph_adjlist_get(&adjlist, v), igraph_adjlist_get(&fulladjlist, v)); for (j=0; j<=vdeg-1; j++) { int vv=VECTOR(PX)[j]; igraph_vector_int_t *fadj=igraph_adjlist_get(&fulladjlist, vv); igraph_vector_int_t *radj=igraph_adjlist_get(&adjlist, vv); int k, fn=igraph_vector_int_size(fadj); igraph_vector_int_clear(radj); for (k=0; k= PS && neipos <= XE) { igraph_vector_int_push_back(radj, nei); } } } /* Reorder the adjacency lists, according to P and X. */ igraph_i_maximal_cliques_reorder_adjlists(&PX, PS, PE, XS, XE, &pos, &adjlist); FUNCTION(igraph_i_maximal_cliques_bk,SUFFIX)( &PX, PS, PE, XS, XE, PS, XE, &R, &pos, &adjlist, RESNAME, &nextv, &H, min_size, max_size); } IGRAPH_PROGRESS("Maximal cliques: ", 100.0, NULL); igraph_vector_int_destroy(&nextv); igraph_vector_int_destroy(&pos); igraph_vector_int_destroy(&H); igraph_vector_int_destroy(&R); igraph_vector_int_destroy(&PX); igraph_adjlist_destroy(&fulladjlist); igraph_adjlist_destroy(&adjlist); igraph_vector_int_destroy(&rank); igraph_vector_destroy(&order); IGRAPH_FINALLY_CLEAN(10); /* + res */ return 0; } #undef RESTYPE #undef RESNAME #undef SUFFIX #undef RECORD #undef FINALLY #undef FOR_LOOP_OVER_VERTICES #undef FOR_LOOP_OVER_VERTICES_PREPARE igraph/src/dsaitr.f0000644000176000001440000007452112325527073014017 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsaitr c c\Description: c Reverse communication interface for applying NP additional steps to c a K step symmetric Arnoldi factorization. c c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T c c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. c c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T c c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. c c where OP and B are as in igraphdsaupd. The B-norm of r_{k+p} is also c computed and returned. c c\Usage: c call igraphdsaitr c ( IDO, BMAT, N, K, NP, MODE, RESID, RNORM, V, LDV, H, LDH, c IPNTR, WORKD, INFO ) c c\Arguments c IDO Integer. (INPUT/OUTPUT) c Reverse communication flag. c ------------------------------------------------------------- c IDO = 0: first call to the reverse communication interface c IDO = -1: compute Y = OP * X where c IPNTR(1) is the pointer into WORK for X, c IPNTR(2) is the pointer into WORK for Y. c This is for the restart phase to force the new c starting vector into the range of OP. c IDO = 1: compute Y = OP * X where c IPNTR(1) is the pointer into WORK for X, c IPNTR(2) is the pointer into WORK for Y, c IPNTR(3) is the pointer into WORK for B * X. c IDO = 2: compute Y = B * X where c IPNTR(1) is the pointer into WORK for X, c IPNTR(2) is the pointer into WORK for Y. c IDO = 99: done c ------------------------------------------------------------- c When the routine is used in the "shift-and-invert" mode, the c vector B * Q is already available and does not need to be c recomputed in forming OP * Q. c c BMAT Character*1. (INPUT) c BMAT specifies the type of matrix B that defines the c semi-inner product for the operator OP. See igraphdsaupd. c B = 'I' -> standard eigenvalue problem A*x = lambda*x c B = 'G' -> generalized eigenvalue problem A*x = lambda*M*x c c N Integer. (INPUT) c Dimension of the eigenproblem. c c K Integer. (INPUT) c Current order of H and the number of columns of V. c c NP Integer. (INPUT) c Number of additional Arnoldi steps to take. c c MODE Integer. (INPUT) c Signifies which form for "OP". If MODE=2 then c a reduction in the number of B matrix vector multiplies c is possible since the B-norm of OP*x is equivalent to c the inv(B)-norm of A*x. c c RESID Double precision array of length N. (INPUT/OUTPUT) c On INPUT: RESID contains the residual vector r_{k}. c On OUTPUT: RESID contains the residual vector r_{k+p}. c c RNORM Double precision scalar. (INPUT/OUTPUT) c On INPUT the B-norm of r_{k}. c On OUTPUT the B-norm of the updated residual r_{k+p}. c c V Double precision N by K+NP array. (INPUT/OUTPUT) c On INPUT: V contains the Arnoldi vectors in the first K c columns. c On OUTPUT: V contains the new NP Arnoldi vectors in the next c NP columns. The first K columns are unchanged. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c H Double precision (K+NP) by 2 array. (INPUT/OUTPUT) c H is used to store the generated symmetric tridiagonal matrix c with the subdiagonal in the first column starting at H(2,1) c and the main diagonal in the igraphsecond column. c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c IPNTR Integer array of length 3. (OUTPUT) c Pointer to mark the starting locations in the WORK for c vectors used by the Arnoldi iteration. c ------------------------------------------------------------- c IPNTR(1): pointer to the current operand vector X. c IPNTR(2): pointer to the current result vector Y. c IPNTR(3): pointer to the vector B * X when used in the c shift-and-invert mode. X is the current operand. c ------------------------------------------------------------- c c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) c Distributed array to be used in the basic Arnoldi iteration c for reverse communication. The calling program should not c use WORKD as temporary workspace during the iteration !!!!!! c On INPUT, WORKD(1:N) = B*RESID where RESID is associated c with the K step Arnoldi factorization. Used to save some c computation at the first step. c On OUTPUT, WORKD(1:N) = B*RESID where RESID is associated c with the K+NP step Arnoldi factorization. c c INFO Integer. (OUTPUT) c = 0: Normal exit. c > 0: Size of an invariant subspace of OP is found that is c less than K + NP. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c igraphdgetv0 ARPACK routine to generate the initial vector. c igraphivout ARPACK utility routine that prints integers. c igraphdmout ARPACK utility routine that prints matrices. c igraphdvout ARPACK utility routine that prints vectors. c dlamch LAPACK routine that determines machine constants. c dlascl LAPACK routine for careful scaling of a matrix. c dgemv Level 2 BLAS routine for matrix vector multiplication. c daxpy Level 1 BLAS that computes a vector triad. c dscal Level 1 BLAS that scales a vector. c dcopy Level 1 BLAS that copies one vector to another . c ddot Level 1 BLAS that computes the scalar product of two vectors. c dnrm2 Level 1 BLAS that computes the norm of a vector. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/93: Version ' 2.4' c c\SCCS Information: @(#) c FILE: saitr.F SID: 2.6 DATE OF SID: 8/28/96 RELEASE: 2 c c\Remarks c The algorithm implemented is: c c restart = .false. c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; c r_{k} contains the initial residual vector even for k = 0; c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already c computed by the calling program. c c betaj = rnorm ; p_{k+1} = B*r_{k} ; c For j = k+1, ..., k+np Do c 1) if ( betaj < tol ) stop or restart depending on j. c if ( restart ) generate a new starting vector. c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; c p_{j} = p_{j}/betaj c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdsaupd c For shift-invert mode p_{j} = B*v_{j} is already available. c wnorm = || OP*v_{j} || c 4) Compute the j-th step residual vector. c w_{j} = V_{j}^T * B * OP * v_{j} c r_{j} = OP*v_{j} - V_{j} * w_{j} c alphaj <- j-th component of w_{j} c rnorm = || r_{j} || c betaj+1 = rnorm c If (rnorm > 0.717*wnorm) accept step and go back to 1) c 5) Re-orthogonalization step: c s = V_{j}'*B*r_{j} c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || c alphaj = alphaj + s_{j}; c 6) Iterative refinement step: c If (rnorm1 > 0.717*rnorm) then c rnorm = rnorm1 c accept step and go back to 1) c Else c rnorm = rnorm1 c If this is the first time in step 6), go to 5) c Else r_{j} lies in the span of V_{j} numerically. c Set r_{j} = 0 and rnorm = 0; go to 1) c EndIf c End Do c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsaitr & (ido, bmat, n, k, np, mode, resid, rnorm, v, ldv, h, ldh, & ipntr, workd, info) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat*1 integer ido, info, k, ldh, ldv, n, mode, np Double precision & rnorm c c %-----------------% c | Array Arguments | c %-----------------% c integer ipntr(3) Double precision & h(ldh,2), resid(n), v(ldv,k+np), workd(3*n) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c logical first, orth1, orth2, rstart, step3, step4 integer i, ierr, ipj, irj, ivj, iter, itry, j, msglvl, & infol, jj Double precision & rnorm1, wnorm, safmin, temp1 save orth1, orth2, rstart, step3, step4, & ierr, ipj, irj, ivj, iter, itry, j, msglvl, & rnorm1, safmin, wnorm c c %-----------------------% c | Local Array Arguments | c %-----------------------% c Double precision & xtemp(2) c c %----------------------% c | External Subroutines | c %----------------------% c external daxpy, dcopy, dscal, dgemv, igraphdgetv0, & igraphdvout, igraphdmout, & dlascl, igraphivout, igraphsecond c c %--------------------% c | External Functions | c %--------------------% c Double precision & ddot, dnrm2, dlamch external ddot, dnrm2, dlamch c c %-----------------% c | Data statements | c %-----------------% c data first / .true. / c c %-----------------------% c | Executable Statements | c %-----------------------% c if (first) then first = .false. c c %--------------------------------% c | safmin = safe minimum is such | c | that 1/sfmin does not overflow | c %--------------------------------% c safmin = dlamch('safmin') end if c if (ido .eq. 0) then c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = msaitr c c %------------------------------% c | Initial call to this routine | c %------------------------------% c info = 0 step3 = .false. step4 = .false. rstart = .false. orth1 = .false. orth2 = .false. c c %--------------------------------% c | Pointer to the current step of | c | the factorization to build | c %--------------------------------% c j = k + 1 c c %------------------------------------------% c | Pointers used for reverse communication | c | when using WORKD. | c %------------------------------------------% c ipj = 1 irj = ipj + n ivj = irj + n end if c c %-------------------------------------------------% c | When in reverse communication mode one of: | c | STEP3, STEP4, ORTH1, ORTH2, RSTART | c | will be .true. | c | STEP3: return from computing OP*v_{j}. | c | STEP4: return from computing B-norm of OP*v_{j} | c | ORTH1: return from computing B-norm of r_{j+1} | c | ORTH2: return from computing B-norm of | c | correction to the residual vector. | c | RSTART: return from OP computations needed by | c | igraphdgetv0. | c %-------------------------------------------------% c if (step3) go to 50 if (step4) go to 60 if (orth1) go to 70 if (orth2) go to 90 if (rstart) go to 30 c c %------------------------------% c | Else this is the first step. | c %------------------------------% c c %--------------------------------------------------------------% c | | c | A R N O L D I I T E R A T I O N L O O P | c | | c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | c %--------------------------------------------------------------% c 1000 continue c if (msglvl .gt. 2) then call igraphivout (logfil, 1, j, ndigit, & '_saitr: generating Arnoldi vector no.') call igraphdvout (logfil, 1, rnorm, ndigit, & '_saitr: B-norm of the current residual =') end if c c %---------------------------------------------------------% c | Check for exact zero. Equivalent to determing whether a | c | j-step Arnoldi factorization is present. | c %---------------------------------------------------------% c if (rnorm .gt. zero) go to 40 c c %---------------------------------------------------% c | Invariant subspace found, generate a new starting | c | vector which is orthogonal to the current Arnoldi | c | basis and continue the iteration. | c %---------------------------------------------------% c if (msglvl .gt. 0) then call igraphivout (logfil, 1, j, ndigit, & '_saitr: ****** restart at step ******') end if c c %---------------------------------------------% c | ITRY is the loop variable that controls the | c | maximum amount of times that a restart is | c | attempted. NRSTRT is used by stat.h | c %---------------------------------------------% c nrstrt = nrstrt + 1 itry = 1 20 continue rstart = .true. ido = 0 30 continue c c %--------------------------------------% c | If in reverse communication mode and | c | RSTART = .true. flow returns here. | c %--------------------------------------% c call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, & resid, rnorm, ipntr, workd, ierr) if (ido .ne. 99) go to 9000 if (ierr .lt. 0) then itry = itry + 1 if (itry .le. 3) go to 20 c c %------------------------------------------------% c | Give up after several restart attempts. | c | Set INFO to the size of the invariant subspace | c | which spans OP and exit. | c %------------------------------------------------% c info = j - 1 call igraphsecond (t1) tsaitr = tsaitr + (t1 - t0) ido = 99 go to 9000 end if c 40 continue c c %---------------------------------------------------------% c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | c | when reciprocating a small RNORM, test against lower | c | machine bound. | c %---------------------------------------------------------% c call dcopy (n, resid, 1, v(1,j), 1) if (rnorm .ge. safmin) then temp1 = one / rnorm call dscal (n, temp1, v(1,j), 1) call dscal (n, temp1, workd(ipj), 1) else c c %-----------------------------------------% c | To scale both v_{j} and p_{j} carefully | c | use LAPACK routine SLASCL | c %-----------------------------------------% c call dlascl ('General', i, i, rnorm, one, n, 1, & v(1,j), n, infol) call dlascl ('General', i, i, rnorm, one, n, 1, & workd(ipj), n, infol) end if c c %------------------------------------------------------% c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | c | Note that this is not quite yet r_{j}. See STEP 4 | c %------------------------------------------------------% c step3 = .true. nopx = nopx + 1 call igraphsecond (t2) call dcopy (n, v(1,j), 1, workd(ivj), 1) ipntr(1) = ivj ipntr(2) = irj ipntr(3) = ipj ido = 1 c c %-----------------------------------% c | Exit in order to compute OP*v_{j} | c %-----------------------------------% c go to 9000 50 continue c c %-----------------------------------% c | Back from reverse communication; | c | WORKD(IRJ:IRJ+N-1) := OP*v_{j}. | c %-----------------------------------% c call igraphsecond (t3) tmvopx = tmvopx + (t3 - t2) c step3 = .false. c c %------------------------------------------% c | Put another copy of OP*v_{j} into RESID. | c %------------------------------------------% c call dcopy (n, workd(irj), 1, resid, 1) c c %-------------------------------------------% c | STEP 4: Finish extending the symmetric | c | Arnoldi to length j. If MODE = 2 | c | then B*OP = B*inv(B)*A = A and | c | we don't need to compute B*OP. | c | NOTE: If MODE = 2 WORKD(IVJ:IVJ+N-1) is | c | assumed to have A*v_{j}. | c %-------------------------------------------% c if (mode .eq. 2) go to 65 call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 step4 = .true. ipntr(1) = irj ipntr(2) = ipj ido = 2 c c %-------------------------------------% c | Exit in order to compute B*OP*v_{j} | c %-------------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy(n, resid, 1 , workd(ipj), 1) end if 60 continue c c %-----------------------------------% c | Back from reverse communication; | c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j}. | c %-----------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c step4 = .false. c c %-------------------------------------% c | The following is needed for STEP 5. | c | Compute the B-norm of OP*v_{j}. | c %-------------------------------------% c 65 continue if (mode .eq. 2) then c c %----------------------------------% c | Note that the B-norm of OP*v_{j} | c | is the inv(B)-norm of A*v_{j}. | c %----------------------------------% c wnorm = ddot (n, resid, 1, workd(ivj), 1) wnorm = sqrt(abs(wnorm)) else if (bmat .eq. 'G') then wnorm = ddot (n, resid, 1, workd(ipj), 1) wnorm = sqrt(abs(wnorm)) else if (bmat .eq. 'I') then wnorm = dnrm2(n, resid, 1) end if c c %-----------------------------------------% c | Compute the j-th residual corresponding | c | to the j step factorization. | c | Use Classical Gram Schmidt and compute: | c | w_{j} <- V_{j}^T * B * OP * v_{j} | c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | c %-----------------------------------------% c c c %------------------------------------------% c | Compute the j Fourier coefficients w_{j} | c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | c %------------------------------------------% c if (mode .ne. 2 ) then call dgemv('T', n, j, one, v, ldv, workd(ipj), 1, zero, & workd(irj), 1) else if (mode .eq. 2) then call dgemv('T', n, j, one, v, ldv, workd(ivj), 1, zero, & workd(irj), 1) end if c c %--------------------------------------% c | Orthgonalize r_{j} against V_{j}. | c | RESID contains OP*v_{j}. See STEP 3. | c %--------------------------------------% c call dgemv('N', n, j, -one, v, ldv, workd(irj), 1, one, & resid, 1) c c %--------------------------------------% c | Extend H to have j rows and columns. | c %--------------------------------------% c h(j,2) = workd(irj + j - 1) if (j .eq. 1 .or. rstart) then h(j,1) = zero else h(j,1) = rnorm end if call igraphsecond (t4) c orth1 = .true. iter = 0 c call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, resid, 1, workd(irj), 1) ipntr(1) = irj ipntr(2) = ipj ido = 2 c c %----------------------------------% c | Exit in order to compute B*r_{j} | c %----------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd(ipj), 1) end if 70 continue c c %---------------------------------------------------% c | Back from reverse communication if ORTH1 = .true. | c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | c %---------------------------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c orth1 = .false. c c %------------------------------% c | Compute the B-norm of r_{j}. | c %------------------------------% c if (bmat .eq. 'G') then rnorm = ddot (n, resid, 1, workd(ipj), 1) rnorm = sqrt(abs(rnorm)) else if (bmat .eq. 'I') then rnorm = dnrm2(n, resid, 1) end if c c %-----------------------------------------------------------% c | STEP 5: Re-orthogonalization / Iterative refinement phase | c | Maximum NITER_ITREF tries. | c | | c | s = V_{j}^T * B * r_{j} | c | r_{j} = r_{j} - V_{j}*s | c | alphaj = alphaj + s_{j} | c | | c | The stopping criteria used for iterative refinement is | c | discussed in Parlett's book SEP, page 107 and in Gragg & | c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | c | Determine if we need to correct the residual. The goal is | c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | c %-----------------------------------------------------------% c if (rnorm .gt. 0.717*wnorm) go to 100 nrorth = nrorth + 1 c c %---------------------------------------------------% c | Enter the Iterative refinement phase. If further | c | refinement is necessary, loop back here. The loop | c | variable is ITER. Perform a step of Classical | c | Gram-Schmidt using all the Arnoldi vectors V_{j} | c %---------------------------------------------------% c 80 continue c if (msglvl .gt. 2) then xtemp(1) = wnorm xtemp(2) = rnorm call igraphdvout (logfil, 2, xtemp, ndigit, & '_saitr: re-orthonalization ; wnorm and rnorm are') end if c c %----------------------------------------------------% c | Compute V_{j}^T * B * r_{j}. | c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | c %----------------------------------------------------% c call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, & zero, workd(irj), 1) c c %----------------------------------------------% c | Compute the correction to the residual: | c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | c | The correction to H is v(:,1:J)*H(1:J,1:J) + | c | v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j, but only | c | H(j,j) is updated. | c %----------------------------------------------% c call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, & one, resid, 1) c if (j .eq. 1 .or. rstart) h(j,1) = zero h(j,2) = h(j,2) + workd(irj + j - 1) c orth2 = .true. call igraphsecond (t2) if (bmat .eq. 'G') then nbx = nbx + 1 call dcopy (n, resid, 1, workd(irj), 1) ipntr(1) = irj ipntr(2) = ipj ido = 2 c c %-----------------------------------% c | Exit in order to compute B*r_{j}. | c | r_{j} is the corrected residual. | c %-----------------------------------% c go to 9000 else if (bmat .eq. 'I') then call dcopy (n, resid, 1, workd(ipj), 1) end if 90 continue c c %---------------------------------------------------% c | Back from reverse communication if ORTH2 = .true. | c %---------------------------------------------------% c if (bmat .eq. 'G') then call igraphsecond (t3) tmvbx = tmvbx + (t3 - t2) end if c c %-----------------------------------------------------% c | Compute the B-norm of the corrected residual r_{j}. | c %-----------------------------------------------------% c if (bmat .eq. 'G') then rnorm1 = ddot (n, resid, 1, workd(ipj), 1) rnorm1 = sqrt(abs(rnorm1)) else if (bmat .eq. 'I') then rnorm1 = dnrm2(n, resid, 1) end if c if (msglvl .gt. 0 .and. iter .gt. 0) then call igraphivout (logfil, 1, j, ndigit, & '_saitr: Iterative refinement for Arnoldi residual') if (msglvl .gt. 2) then xtemp(1) = rnorm xtemp(2) = rnorm1 call igraphdvout (logfil, 2, xtemp, ndigit, & '_saitr: iterative refinement ; rnorm and rnorm1 are') end if end if c c %-----------------------------------------% c | Determine if we need to perform another | c | step of re-orthogonalization. | c %-----------------------------------------% c if (rnorm1 .gt. 0.717*rnorm) then c c %--------------------------------% c | No need for further refinement | c %--------------------------------% c rnorm = rnorm1 c else c c %-------------------------------------------% c | Another step of iterative refinement step | c | is required. NITREF is used by stat.h | c %-------------------------------------------% c nitref = nitref + 1 rnorm = rnorm1 iter = iter + 1 if (iter .le. 1) go to 80 c c %-------------------------------------------------% c | Otherwise RESID is numerically in the span of V | c %-------------------------------------------------% c do 95 jj = 1, n resid(jj) = zero 95 continue rnorm = zero end if c c %----------------------------------------------% c | Branch here directly if iterative refinement | c | wasn't necessary or after at most NITER_REF | c | steps of iterative refinement. | c %----------------------------------------------% c 100 continue c rstart = .false. orth2 = .false. c call igraphsecond (t5) titref = titref + (t5 - t4) c c %----------------------------------------------------------% c | Make sure the last off-diagonal element is non negative | c | If not perform a similarity transformation on H(1:j,1:j) | c | and scale v(:,j) by -1. | c %----------------------------------------------------------% c if (h(j,1) .lt. zero) then h(j,1) = -h(j,1) if ( j .lt. k+np) then call dscal(n, -one, v(1,j+1), 1) else call dscal(n, -one, resid, 1) end if end if c c %------------------------------------% c | STEP 6: Update j = j+1; Continue | c %------------------------------------% c j = j + 1 if (j .gt. k+np) then call igraphsecond (t1) tsaitr = tsaitr + (t1 - t0) ido = 99 c if (msglvl .gt. 1) then call igraphdvout (logfil, k+np, h(1,2), ndigit, & '_saitr: main diagonal of matrix H of step K+NP.') if (k+np .gt. 1) then call igraphdvout (logfil, k+np-1, h(2,1), ndigit, & '_saitr: sub diagonal of matrix H of step K+NP.') end if end if c go to 9000 end if c c %--------------------------------------------------------% c | Loop back to extend the factorization by another step. | c %--------------------------------------------------------% c go to 1000 c c %---------------------------------------------------------------% c | | c | E N D O F M A I N I T E R A T I O N L O O P | c | | c %---------------------------------------------------------------% c 9000 continue return c c %---------------% c | End of igraphdsaitr | c %---------------% c end igraph/src/glpnet03.c0000644000176000001440000006040512325527073014156 0ustar ripleyusers/* glpnet03.c (Klingman's network problem generator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * This code is the result of translation of the Fortran program NETGEN * developed by Dr. Darwin Klingman, which is publically available from * NETLIB at . * * The translation was made by Andrew Makhorin . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" /*********************************************************************** * NAME * * glp_netgen - Klingman's network problem generator * * SYNOPSIS * * int glp_netgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, * const int parm[1+15]); * * DESCRIPTION * * The routine glp_netgen is a network problem generator developed by * Dr. Darwin Klingman. It can create capacitated and uncapacitated * minimum cost flow (or transshipment), transportation, and assignment * problems. * * The parameter G specifies the graph object, to which the generated * problem data have to be stored. Note that on entry the graph object * is erased with the routine glp_erase_graph. * * The parameter v_rhs specifies an offset of the field of type double * in the vertex data block, to which the routine stores the supply or * demand value. If v_rhs < 0, the value is not stored. * * The parameter a_cap specifies an offset of the field of type double * in the arc data block, to which the routine stores the arc capacity. * If a_cap < 0, the capacity is not stored. * * The parameter a_cost specifies an offset of the field of type double * in the arc data block, to which the routine stores the per-unit cost * if the arc flow. If a_cost < 0, the cost is not stored. * * The array parm contains description of the network to be generated: * * parm[0] not used * parm[1] (iseed) 8-digit positive random number seed * parm[2] (nprob) 8-digit problem id number * parm[3] (nodes) total number of nodes * parm[4] (nsorc) total number of source nodes (including * transshipment nodes) * parm[5] (nsink) total number of sink nodes (including * transshipment nodes) * parm[6] (iarcs) number of arcs * parm[7] (mincst) minimum cost for arcs * parm[8] (maxcst) maximum cost for arcs * parm[9] (itsup) total supply * parm[10] (ntsorc) number of transshipment source nodes * parm[11] (ntsink) number of transshipment sink nodes * parm[12] (iphic) percentage of skeleton arcs to be given * the maximum cost * parm[13] (ipcap) percentage of arcs to be capacitated * parm[14] (mincap) minimum upper bound for capacitated arcs * parm[15] (maxcap) maximum upper bound for capacitated arcs * * The routine generates a transportation problem if: * * nsorc + nsink = nodes, ntsorc = 0, and ntsink = 0. * * The routine generates an assignment problem if the requirements for * a transportation problem are met and: * * nsorc = nsink and itsup = nsorc. * * RETURNS * * If the instance was successfully generated, the routine glp_netgen * returns zero; otherwise, if specified parameters are inconsistent, * the routine returns a non-zero error code. * * REFERENCES * * D.Klingman, A.Napier, and J.Stutz. NETGEN: A program for generating * large scale capacitated assignment, transportation, and minimum cost * flow networks. Management Science 20 (1974), 814-20. */ struct csa { /* common storage area */ glp_graph *G; int v_rhs, a_cap, a_cost; int nodes, iarcs, mincst, maxcst, itsup, nsorc, nsink, nonsor, nfsink, narcs, nsort, nftsor, ipcap, mincap, maxcap, ktl, nodlft, *ipred, *ihead, *itail, *iflag, *isup, *lsinks, mult, modul, i15, i16, jran; }; #define G (csa->G) #define v_rhs (csa->v_rhs) #define a_cap (csa->a_cap) #define a_cost (csa->a_cost) #define nodes (csa->nodes) #define iarcs (csa->iarcs) #define mincst (csa->mincst) #define maxcst (csa->maxcst) #define itsup (csa->itsup) #define nsorc (csa->nsorc) #define nsink (csa->nsink) #define nonsor (csa->nonsor) #define nfsink (csa->nfsink) #define narcs (csa->narcs) #define nsort (csa->nsort) #define nftsor (csa->nftsor) #define ipcap (csa->ipcap) #define mincap (csa->mincap) #define maxcap (csa->maxcap) #define ktl (csa->ktl) #define nodlft (csa->nodlft) #if 0 /* spent a day to find out this bug */ #define ist (csa->ist) #else #define ist (ipred[0]) #endif #define ipred (csa->ipred) #define ihead (csa->ihead) #define itail (csa->itail) #define iflag (csa->iflag) #define isup (csa->isup) #define lsinks (csa->lsinks) #define mult (csa->mult) #define modul (csa->modul) #define i15 (csa->i15) #define i16 (csa->i16) #define jran (csa->jran) static void cresup(struct csa *csa); static void chain(struct csa *csa, int lpick, int lsorc); static void chnarc(struct csa *csa, int lsorc); static void sort(struct csa *csa); static void pickj(struct csa *csa, int it); static void assign(struct csa *csa); static void setran(struct csa *csa, int iseed); static int iran(struct csa *csa, int ilow, int ihigh); int glp_netgen(glp_graph *G_, int _v_rhs, int _a_cap, int _a_cost, const int parm[1+15]) { struct csa _csa, *csa = &_csa; int iseed, nprob, ntsorc, ntsink, iphic, i, nskel, nltr, ltsink, ntrans, npsink, nftr, npsorc, ntravl, ntrrem, lsorc, lpick, nsksr, nsrchn, j, item, l, ks, k, ksp, li, n, ii, it, ih, icap, jcap, icost, jcost, ret; G = G_; v_rhs = _v_rhs; a_cap = _a_cap; a_cost = _a_cost; if (G != NULL) { if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) xerror("glp_netgen: v_rhs = %d; invalid offset\n", v_rhs); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_netgen: a_cap = %d; invalid offset\n", a_cap); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_netgen: a_cost = %d; invalid offset\n", a_cost); } /* Input the user's random number seed and fix it if non-positive. */ iseed = parm[1]; nprob = parm[2]; if (iseed <= 0) iseed = 13502460; setran(csa, iseed); /* Input the user's problem characteristics. */ nodes = parm[3]; nsorc = parm[4]; nsink = parm[5]; iarcs = parm[6]; mincst = parm[7]; maxcst = parm[8]; itsup = parm[9]; ntsorc = parm[10]; ntsink = parm[11]; iphic = parm[12]; ipcap = parm[13]; mincap = parm[14]; maxcap = parm[15]; /* Check the size of the problem. */ if (!(10 <= nodes && nodes <= 100000)) { ret = 1; goto done; } /* Check user supplied parameters for consistency. */ if (!(nsorc >= 0 && nsink >= 0 && nsorc + nsink <= nodes)) { ret = 2; goto done; } if (iarcs < 0) { ret = 3; goto done; } if (mincst > maxcst) { ret = 4; goto done; } if (itsup < 0) { ret = 5; goto done; } if (!(0 <= ntsorc && ntsorc <= nsorc)) { ret = 6; goto done; } if (!(0 <= ntsink && ntsink <= nsink)) { ret = 7; goto done; } if (!(0 <= iphic && iphic <= 100)) { ret = 8; goto done; } if (!(0 <= ipcap && ipcap <= 100)) { ret = 9; goto done; } if (mincap > maxcap) { ret = 10; goto done; } /* Initailize the graph object. */ if (G != NULL) { glp_erase_graph(G, G->v_size, G->a_size); glp_add_vertices(G, nodes); if (v_rhs >= 0) { double zero = 0.0; for (i = 1; i <= nodes; i++) { glp_vertex *v = G->v[i]; memcpy((char *)v->data + v_rhs, &zero, sizeof(double)); } } } /* Allocate working arrays. */ ipred = xcalloc(1+nodes, sizeof(int)); ihead = xcalloc(1+nodes, sizeof(int)); itail = xcalloc(1+nodes, sizeof(int)); iflag = xcalloc(1+nodes, sizeof(int)); isup = xcalloc(1+nodes, sizeof(int)); lsinks = xcalloc(1+nodes, sizeof(int)); /* Print the problem documentation records. */ if (G == NULL) { xprintf("BEGIN\n"); xprintf("NETGEN PROBLEM%8d%10s%10d NODES AND%10d ARCS\n", nprob, "", nodes, iarcs); xprintf("USER:%11d%11d%11d%11d%11d%11d\nDATA:%11d%11d%11d%11d%" "11d%11d\n", iseed, nsorc, nsink, mincst, maxcst, itsup, ntsorc, ntsink, iphic, ipcap, mincap, maxcap); } else glp_set_graph_name(G, "NETGEN"); /* Set various constants used in the program. */ narcs = 0; nskel = 0; nltr = nodes - nsink; ltsink = nltr + ntsink; ntrans = nltr - nsorc; nfsink = nltr + 1; nonsor = nodes - nsorc + ntsorc; npsink = nsink - ntsink; nodlft = nodes - nsink + ntsink; nftr = nsorc + 1; nftsor = nsorc - ntsorc + 1; npsorc = nsorc - ntsorc; /* Randomly distribute the supply among the source nodes. */ if (npsorc + npsink == nodes && npsorc == npsink && itsup == nsorc) { assign(csa); nskel = nsorc; goto L390; } cresup(csa); /* Print the supply records. */ if (G == NULL) { xprintf("SUPPLY\n"); for (i = 1; i <= nsorc; i++) xprintf("%6s%6d%18s%10d\n", "", i, "", isup[i]); xprintf("ARCS\n"); } else { if (v_rhs >= 0) { for (i = 1; i <= nsorc; i++) { double temp = (double)isup[i]; glp_vertex *v = G->v[i]; memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); } } } /* Make the sources point to themselves in ipred array. */ for (i = 1; i <= nsorc; i++) ipred[i] = i; if (ntrans == 0) goto L170; /* Chain the transshipment nodes together in the ipred array. */ ist = nftr; ipred[nltr] = 0; for (i = nftr; i < nltr; i++) ipred[i] = i+1; /* Form even length chains for 60 percent of the transshipments.*/ ntravl = 6 * ntrans / 10; ntrrem = ntrans - ntravl; L140: lsorc = 1; while (ntravl != 0) { lpick = iran(csa, 1, ntravl + ntrrem); ntravl--; chain(csa, lpick, lsorc); if (lsorc == nsorc) goto L140; lsorc++; } /* Add the remaining transshipments to the chains. */ while (ntrrem != 0) { lpick = iran(csa, 1, ntrrem); ntrrem--; lsorc = iran(csa, 1, nsorc); chain(csa, lpick, lsorc); } L170: /* Set all demands equal to zero. */ for (i = nfsink; i <= nodes; i++) ipred[i] = 0; /* The following loop takes one chain at a time (through the use of logic contained in the loop and calls to other routines) and creates the remaining network arcs. */ for (lsorc = 1; lsorc <= nsorc; lsorc++) { chnarc(csa, lsorc); for (i = nfsink; i <= nodes; i++) iflag[i] = 0; /* Choose the number of sinks to be hooked up to the current chain. */ if (ntrans != 0) nsksr = (nsort * 2 * nsink) / ntrans; else nsksr = nsink / nsorc + 1; if (nsksr < 2) nsksr = 2; if (nsksr > nsink) nsksr = nsink; nsrchn = nsort; /* Randomly pick nsksr sinks and put their names in lsinks. */ ktl = nsink; for (j = 1; j <= nsksr; j++) { item = iran(csa, 1, ktl); ktl--; for (l = nfsink; l <= nodes; l++) { if (iflag[l] != 1) { item--; if (item == 0) goto L230; } } break; L230: lsinks[j] = l; iflag[l] = 1; } /* If last source chain, add all sinks with zero demand to lsinks list. */ if (lsorc == nsorc) { for (j = nfsink; j <= nodes; j++) { if (ipred[j] == 0 && iflag[j] != 1) { nsksr++; lsinks[nsksr] = j; iflag[j] = 1; } } } /* Create demands for group of sinks in lsinks. */ ks = isup[lsorc] / nsksr; k = ipred[lsorc]; for (i = 1; i <= nsksr; i++) { nsort++; ksp = iran(csa, 1, ks); j = iran(csa, 1, nsksr); itail[nsort] = k; li = lsinks[i]; ihead[nsort] = li; ipred[li] += ksp; li = lsinks[j]; ipred[li] += ks - ksp; n = iran(csa, 1, nsrchn); k = lsorc; for (ii = 1; ii <= n; ii++) k = ipred[k]; } li = lsinks[1]; ipred[li] += isup[lsorc] - ks * nsksr; nskel += nsort; /* Sort the arcs in the chain from source lsorc using itail as sort key. */ sort(csa); /* Print this part of skeleton and create the arcs for these nodes. */ i = 1; itail[nsort+1] = 0; L300: for (j = nftsor; j <= nodes; j++) iflag[j] = 0; ktl = nonsor - 1; it = itail[i]; iflag[it] = 1; L320: ih = ihead[i]; iflag[ih] = 1; narcs++; ktl--; /* Determine if this skeleton arc should be capacitated. */ icap = itsup; jcap = iran(csa, 1, 100); if (jcap <= ipcap) { icap = isup[lsorc]; if (mincap > icap) icap = mincap; } /* Determine if this skeleton arc should have the maximum cost. */ icost = maxcst; jcost = iran(csa, 1, 100); if (jcost > iphic) icost = iran(csa, mincst, maxcst); if (G == NULL) xprintf("%6s%6d%6d%2s%10d%10d\n", "", it, ih, "", icost, icap); else { glp_arc *a = glp_add_arc(G, it, ih); if (a_cap >= 0) { double temp = (double)icap; memcpy((char *)a->data + a_cap, &temp, sizeof(double)); } if (a_cost >= 0) { double temp = (double)icost; memcpy((char *)a->data + a_cost, &temp, sizeof(double)); } } i++; if (itail[i] == it) goto L320; pickj(csa, it); if (i <= nsort) goto L300; } /* Create arcs from the transshipment sinks. */ if (ntsink != 0) { for (i = nfsink; i <= ltsink; i++) { for (j = nftsor; j <= nodes; j++) iflag[j] = 0; ktl = nonsor - 1; iflag[i] = 1; pickj(csa, i); } } L390: /* Print the demand records and end record. */ if (G == NULL) { xprintf("DEMAND\n"); for (i = nfsink; i <= nodes; i++) xprintf("%6s%6d%18s%10d\n", "", i, "", ipred[i]); xprintf("END\n"); } else { if (v_rhs >= 0) { for (i = nfsink; i <= nodes; i++) { double temp = - (double)ipred[i]; glp_vertex *v = G->v[i]; memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); } } } /* Free working arrays. */ xfree(ipred); xfree(ihead); xfree(itail); xfree(iflag); xfree(isup); xfree(lsinks); /* The instance has been successfully generated. */ ret = 0; done: return ret; } /*********************************************************************** * The routine cresup randomly distributes the total supply among the * source nodes. */ static void cresup(struct csa *csa) { int i, j, ks, ksp; xassert(itsup > nsorc); ks = itsup / nsorc; for (i = 1; i <= nsorc; i++) isup[i] = 0; for (i = 1; i <= nsorc; i++) { ksp = iran(csa, 1, ks); j = iran(csa, 1, nsorc); isup[i] += ksp; isup[j] += ks - ksp; } j = iran(csa, 1, nsorc); isup[j] += itsup - ks * nsorc; return; } /*********************************************************************** * The routine chain adds node lpick to the end of the chain with source * node lsorc. */ static void chain(struct csa *csa, int lpick, int lsorc) { int i, j, k, l, m; k = 0; m = ist; for (i = 1; i <= lpick; i++) { l = k; k = m; m = ipred[k]; } ipred[l] = m; j = ipred[lsorc]; ipred[k] = j; ipred[lsorc] = k; return; } /*********************************************************************** * The routine chnarc puts the arcs in the chain from source lsorc into * the ihead and itail arrays for sorting. */ static void chnarc(struct csa *csa, int lsorc) { int ito, ifrom; nsort = 0; ito = ipred[lsorc]; L10: if (ito == lsorc) return; nsort++; ifrom = ipred[ito]; ihead[nsort] = ito; itail[nsort] = ifrom; ito = ifrom; goto L10; } /*********************************************************************** * The routine sort sorts the nsort arcs in the ihead and itail arrays. * ihead is used as the sort key (i.e. forward star sort order). */ static void sort(struct csa *csa) { int i, j, k, l, m, n, it; n = nsort; m = n; L10: m /= 2; if (m == 0) return; k = n - m; j = 1; L20: i = j; L30: l = i + m; if (itail[i] <= itail[l]) goto L40; it = itail[i]; itail[i] = itail[l]; itail[l] = it; it = ihead[i]; ihead[i] = ihead[l]; ihead[l] = it; i -= m; if (i >= 1) goto L30; L40: j++; if (j <= k) goto L20; goto L10; } /*********************************************************************** * The routine pickj creates a random number of arcs out of node 'it'. * Various parameters are dynamically adjusted in an attempt to ensure * that the generated network has the correct number of arcs. */ static void pickj(struct csa *csa, int it) { int j, k, l, nn, nupbnd, icap, jcap, icost; if ((nodlft - 1) * 2 > iarcs - narcs - 1) { nodlft--; return; } if ((iarcs - narcs + nonsor - ktl - 1) / nodlft - nonsor + 1 >= 0) k = nonsor; else { nupbnd = (iarcs - narcs - nodlft) / nodlft * 2; L40: k = iran(csa, 1, nupbnd); if (nodlft == 1) k = iarcs - narcs; if ((nodlft - 1) * (nonsor - 1) < iarcs - narcs - k) goto L40; } nodlft--; for (j = 1; j <= k; j++) { nn = iran(csa, 1, ktl); ktl--; for (l = nftsor; l <= nodes; l++) { if (iflag[l] != 1) { nn--; if (nn == 0) goto L70; } } return; L70: iflag[l] = 1; icap = itsup; jcap = iran(csa, 1, 100); if (jcap <= ipcap) icap = iran(csa, mincap, maxcap); icost = iran(csa, mincst, maxcst); if (G == NULL) xprintf("%6s%6d%6d%2s%10d%10d\n", "", it, l, "", icost, icap); else { glp_arc *a = glp_add_arc(G, it, l); if (a_cap >= 0) { double temp = (double)icap; memcpy((char *)a->data + a_cap, &temp, sizeof(double)); } if (a_cost >= 0) { double temp = (double)icost; memcpy((char *)a->data + a_cost, &temp, sizeof(double)); } } narcs++; } return; } /*********************************************************************** * The routine assign generate assignment problems. It defines the unit * supplies, builds a skeleton, then calls pickj to create the arcs. */ static void assign(struct csa *csa) { int i, it, nn, l, ll, icost; if (G == NULL) xprintf("SUPPLY\n"); for (i = 1; i <= nsorc; i++) { isup[i] = 1; iflag[i] = 0; if (G == NULL) xprintf("%6s%6d%18s%10d\n", "", i, "", isup[i]); else { if (v_rhs >= 0) { double temp = (double)isup[i]; glp_vertex *v = G->v[i]; memcpy((char *)v->data + v_rhs, &temp, sizeof(double)); } } } if (G == NULL) xprintf("ARCS\n"); for (i = nfsink; i <= nodes; i++) ipred[i] = 1; for (it = 1; it <= nsorc; it++) { for (i = nfsink; i <= nodes; i++) iflag[i] = 0; ktl = nsink - 1; nn = iran(csa, 1, nsink - it + 1); for (l = 1; l <= nsorc; l++) { if (iflag[l] != 1) { nn--; if (nn == 0) break; } } narcs++; ll = nsorc + l; icost = iran(csa, mincst, maxcst); if (G == NULL) xprintf("%6s%6d%6d%2s%10d%10d\n", "", it, ll, "", icost, isup[1]); else { glp_arc *a = glp_add_arc(G, it, ll); if (a_cap >= 0) { double temp = (double)isup[1]; memcpy((char *)a->data + a_cap, &temp, sizeof(double)); } if (a_cost >= 0) { double temp = (double)icost; memcpy((char *)a->data + a_cost, &temp, sizeof(double)); } } iflag[l] = 1; iflag[ll] = 1; pickj(csa, it); } return; } /*********************************************************************** * Portable congruential (uniform) random number generator: * * next_value = ((7**5) * previous_value) modulo ((2**31)-1) * * This generator consists of three routines: * * (1) setran - initializes constants and seed * (2) iran - generates an integer random number * (3) rran - generates a real random number * * The generator requires a machine with at least 32 bits of precision. * The seed (iseed) must be in the range [1,(2**31)-1]. */ static void setran(struct csa *csa, int iseed) { xassert(iseed >= 1); mult = 16807; modul = 2147483647; i15 = 1 << 15; i16 = 1 << 16; jran = iseed; return; } /*********************************************************************** * The routine iran generates an integer random number between ilow and * ihigh. If ilow > ihigh then iran returns ihigh. */ static int iran(struct csa *csa, int ilow, int ihigh) { int ixhi, ixlo, ixalo, leftlo, ixahi, ifulhi, irtlo, iover, irthi, j; ixhi = jran / i16; ixlo = jran - ixhi * i16; ixalo = ixlo * mult; leftlo = ixalo / i16; ixahi = ixhi * mult; ifulhi = ixahi + leftlo; irtlo = ixalo - leftlo * i16; iover = ifulhi / i15; irthi = ifulhi - iover * i15; jran = ((irtlo - modul) + irthi * i16) + iover; if (jran < 0) jran += modul; j = ihigh - ilow + 1; if (j > 0) return jran % j + ilow; else return ihigh; } /**********************************************************************/ #if 0 static int scan(char card[80+1], int pos, int len) { char buf[10+1]; memcpy(buf, &card[pos-1], len); buf[len] = '\0'; return atoi(buf); } int main(void) { int parm[1+15]; char card[80+1]; xassert(fgets(card, sizeof(card), stdin) == card); parm[1] = scan(card, 1, 8); parm[2] = scan(card, 9, 8); xassert(fgets(card, sizeof(card), stdin) == card); parm[3] = scan(card, 1, 5); parm[4] = scan(card, 6, 5); parm[5] = scan(card, 11, 5); parm[6] = scan(card, 16, 5); parm[7] = scan(card, 21, 5); parm[8] = scan(card, 26, 5); parm[9] = scan(card, 31, 10); parm[10] = scan(card, 41, 5); parm[11] = scan(card, 46, 5); parm[12] = scan(card, 51, 5); parm[13] = scan(card, 56, 5); parm[14] = scan(card, 61, 10); parm[15] = scan(card, 71, 10); glp_netgen(NULL, 0, 0, 0, parm); return 0; } #endif /* eof */ igraph/src/optimal_modularity.c0000644000176000001440000001737412325527073016447 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_interface.h" #include "igraph_structural.h" #include "igraph_community.h" #include "igraph_error.h" #include "igraph_glpk_support.h" #include "igraph_interrupt_internal.h" #include "igraph_centrality.h" #include "config.h" #ifdef HAVE_GLPK #include #endif /** * \function igraph_community_optimal_modularity * Calculate the community structure with the highest modularity value * * This function calculates the optimal community structure for a * graph, in terms of maximal modularity score. * * * The calculation is done by transforming the modularity maximization * into an integer programming problem, and then calling the GLPK * library to solve that. Please see Ulrik Brandes et al.: On * Modularity Clustering, IEEE Transactions on Knowledge and Data * Engineering 20(2):172-188, 2008. * * * Note that modularity optimization is an NP-complete problem, and * all known algorithms for it have exponential time complexity. This * means that you probably don't want to run this function on larger * graphs. Graphs with up to fifty vertices should be fine, graphs * with a couple of hundred vertices might be possible. * * \param graph The input graph. It is always treated as undirected. * \param modularity Pointer to a real number, or a null pointer. * If it is not a null pointer, then a optimal modularity value * is returned here. * \param membership Pointer to a vector, or a null pointer. If not a * null pointer, then the membership vector of the optimal * community structure is stored here. * \param weights Vector giving the weights of the edges. If it is * \c NULL then each edge is supposed to have the same weight. * \return Error code. * * \sa \ref igraph_modularity(), \ref igraph_community_fastgreedy() * for an algorithm that finds a local optimum in a greedy way. * * Time complexity: exponential in the number of vertices. * * \example examples/simple/igraph_community_optimal_modularity.c */ int igraph_community_optimal_modularity(const igraph_t *graph, igraph_real_t *modularity, igraph_vector_t *membership, const igraph_vector_t *weights) { #ifndef HAVE_GLPK IGRAPH_ERROR("GLPK is not available", IGRAPH_UNIMPLEMENTED); #else igraph_integer_t no_of_nodes=(igraph_integer_t) igraph_vcount(graph); igraph_integer_t no_of_edges=(igraph_integer_t) igraph_ecount(graph); igraph_bool_t directed=igraph_is_directed(graph); int no_of_variables=no_of_nodes * (no_of_nodes+1)/2; int i, j, k, l, st; int idx[] = { 0, 0, 0, 0 }; double coef[] = { 0.0, 1.0, 1.0, -2.0 }; igraph_real_t total_weight; igraph_vector_t indegree; igraph_vector_t outdegree; glp_prob *ip; glp_iocp parm; if (weights != 0) { if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL); } if (igraph_vector_min(weights) < 0) { IGRAPH_ERROR("Negative weights are not allowed in weight vector", IGRAPH_EINVAL); } } if (weights) { total_weight = igraph_vector_sum(weights); } else { total_weight = no_of_edges; } if (!directed) { total_weight *= 2; } /* Special case */ if (no_of_edges == 0 || total_weight == 0) { if (modularity) { *modularity=IGRAPH_NAN; } if (membership) { IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); igraph_vector_null(membership); } } IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&outdegree, no_of_nodes); IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS, weights)); IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, weights)); glp_term_out(GLP_OFF); ip = glp_create_prob(); IGRAPH_FINALLY(glp_delete_prob, ip); glp_set_obj_dir(ip, GLP_MAX); st=glp_add_cols(ip, no_of_variables); /* variables are binary */ for (i=0; i j) { l = i; i = j; j = l; } c = weights ? VECTOR(*weights)[k] : 1.0; if (!directed || i == j) { c *= 2.0; } glp_set_obj_coef(ip, st+IDX(i,j), c + glp_get_obj_coef(ip, st+IDX(i,j))); } } /* solve it */ glp_init_iocp(&parm); parm.br_tech = GLP_BR_DTH; parm.bt_tech = GLP_BT_BLB; parm.presolve = GLP_ON; parm.binarize = GLP_ON; parm.cb_func = igraph_i_glpk_interruption_hook; IGRAPH_GLPK_CHECK(glp_intopt(ip, &parm), "Modularity optimization failed"); /* store the results */ if (modularity) { *modularity = glp_mip_obj_val(ip) / total_weight; } if (membership) { long int comm=0; /* id of the last community that was found */ IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); for (i=0; i * * Copyright (C) 2008 David Morton de Lachapelle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * DESCRIPTION * ----------- * This file implements algorithm 5.8 of the above reference. * The optimal_partition function returns the minimizing partition * with size 'nt' of the objective function ||v-Pv||, where P is * a problem-specific projector. So far, Symmetric (matrix=1), * Laplacian (matrix=2) and Stochastic (matrix=3) projectors * have been implemented (the cost_matrix function below). * In the stochastic case, 'p' is expected to be a valid propability * vector. In all other cases, 'p' is ignored and can be set to NULL. * The group labels are given in 'gr' as positive consecutive integers * starting from 0. */ #include "igraph_error.h" #include "igraph_memory.h" #include "igraph_matrix.h" #include "igraph_vector.h" #include "scg_headers.h" int igraph_i_optimal_partition(const igraph_real_t *v, int *gr, int n, int nt, int matrix, const igraph_real_t *p, igraph_real_t *value) { int i, non_ties, q, j, l, part_ind, col; igraph_i_scg_indval_t *vs = igraph_Calloc(n, igraph_i_scg_indval_t); igraph_real_t *Cv, temp, sumOfSquares; igraph_vector_t ps; igraph_matrix_t F; igraph_matrix_int_t Q; /*----------------------------------------------- -----Sorts v and counts non-ties----------------- -----------------------------------------------*/ if (!vs) { IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vs); for (i=0; i vs[i-1].val + 1e-14) { non_ties++; } } if (nt >= non_ties) { IGRAPH_ERROR("`Invalid number of intervals, should be smaller than " "number of unique values in V", IGRAPH_EINVAL); } /*------------------------------------------------ ------Computes Cv, the matrix of costs------------ ------------------------------------------------*/ Cv = igraph_i_real_sym_matrix(n); if (!Cv) { IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, Cv); /* if stochastic SCG orders p */ if (matrix==3) { IGRAPH_VECTOR_INIT_FINALLY(&ps, n); for (i=0; i=0;j--)", for such loops never ends!*/ IGRAPH_MATRIX_INIT_FINALLY(&F, nt, n); IGRAPH_CHECK(igraph_matrix_int_init(&Q, nt, n)); IGRAPH_FINALLY(igraph_matrix_destroy, &Q); for (i=0; i=0; j--) { for (i=MATRIX(Q, j, col)-1; i<=col; i++) gr[vs[i].ind] = part_ind-1; if (MATRIX(Q, j, col) != 2) { col = MATRIX(Q, j, col)-2; part_ind -= 1; } else{ if (j>1) { for (l=0; l<=(j-1); l++) gr[vs[l].ind] = l; break; } else{ col = MATRIX(Q, j, col)-2; part_ind -= 1; } } } sumOfSquares = MATRIX(F, nt-1, n-1); igraph_matrix_destroy(&F); igraph_matrix_int_destroy(&Q); igraph_Free(vs); IGRAPH_FINALLY_CLEAN(3); if (value) { *value=sumOfSquares; } return 0; } int igraph_i_cost_matrix(igraph_real_t*Cv, const igraph_i_scg_indval_t *vs, int n, int matrix, const igraph_vector_t *ps) { /* if symmetric of Laplacian SCG -> same Cv */ if (matrix==1 || matrix==2) { int i,j; igraph_vector_t w, w2; IGRAPH_VECTOR_INIT_FINALLY(&w, n+1); IGRAPH_VECTOR_INIT_FINALLY(&w2, n+1); VECTOR(w)[1] = vs[0].val; VECTOR(w2)[1] = vs[0].val*vs[0].val; for (i=2; i<=n; i++) { VECTOR(w)[i] = VECTOR(w)[i-1] + vs[i-1].val; VECTOR(w2)[i] = VECTOR(w2)[i-1] + vs[i-1].val*vs[i-1].val; } for (i=0; i #include "Color.h" #include "Ray.h" #include "Point.h" #include using namespace std; namespace igraph { class Shape { public: Shape(); virtual ~Shape(); virtual bool Intersect(const Ray& rRay, Point& rIntersectPoint) const = 0; virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const = 0; // returns a normalized vector that is the normal of this shape from the surface point // it also takes the rOffSurface point into account, for example: // if rSurfacePoint is on top of a triangle, then the normal returned will be going up. Ray Reflect(const Point& rReflectFrom, const Ray& rRay) const; void Name(int vName); int Name() const; const Color& ShapeColor() const; void ShapeColor(const Color& rColor); double SpecularReflectivity() const; void SpecularReflectivity(double rReflectivity); double DiffuseReflectivity() const; void DiffuseReflectivity(double rReflectivity); double AmbientReflectivity() const; void AmbientReflectivity(double rReflectivity); int SpecularSize() const; void SpecularSize(int vSpecularSize); const string& Type() const; void Type(const string& rType); private: int mName; string mType; Color mShapeColor; double mSpecularReflectivity; // from 0 to 1 int mSpecularSize; // 1 to 64 double mDiffuseReflectivity; // from 0 to 1 double mAmbientReflectivity; // from 0 to 1 }; typedef list ShapeList; typedef list::iterator ShapeListIterator; } // namespace igraph #endif igraph/src/cs_lu.c0000644000176000001440000001160212325527073013622 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma clang diagnostic ignored "-Wconversion" #include "cs.h" /* [L,U,pinv]=lu(A, [q lnz unz]). lnz and unz can be guess */ csn *cs_lu (const cs *A, const css *S, double tol) { cs *L, *U ; csn *N ; CS_ENTRY pivot, *Lx, *Ux, *x ; double a, t ; CS_INT *Lp, *Li, *Up, *Ui, *pinv, *xi, *q, n, ipiv, k, top, p, i, col, lnz,unz; if (!CS_CSC (A) || !S) return (NULL) ; /* check inputs */ n = A->n ; q = S->q ; lnz = S->lnz ; unz = S->unz ; x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get CS_ENTRY workspace */ xi = cs_malloc (2*n, sizeof (CS_INT)) ; /* get CS_INT workspace */ N = cs_calloc (1, sizeof (csn)) ; /* allocate result */ if (!x || !xi || !N) return (cs_ndone (N, NULL, xi, x, 0)) ; N->L = L = cs_spalloc (n, n, lnz, 1, 0) ; /* allocate result L */ N->U = U = cs_spalloc (n, n, unz, 1, 0) ; /* allocate result U */ N->pinv = pinv = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result pinv */ if (!L || !U || !pinv) return (cs_ndone (N, NULL, xi, x, 0)) ; Lp = L->p ; Up = U->p ; for (i = 0 ; i < n ; i++) x [i] = 0 ; /* clear workspace */ for (i = 0 ; i < n ; i++) pinv [i] = -1 ; /* no rows pivotal yet */ for (k = 0 ; k <= n ; k++) Lp [k] = 0 ; /* no cols of L yet */ lnz = unz = 0 ; for (k = 0 ; k < n ; k++) /* compute L(:,k) and U(:,k) */ { /* --- Triangular solve --------------------------------------------- */ Lp [k] = lnz ; /* L(:,k) starts here */ Up [k] = unz ; /* U(:,k) starts here */ if ((lnz + n > L->nzmax && !cs_sprealloc (L, 2*L->nzmax + n)) || (unz + n > U->nzmax && !cs_sprealloc (U, 2*U->nzmax + n))) { return (cs_ndone (N, NULL, xi, x, 0)) ; } Li = L->i ; Lx = L->x ; Ui = U->i ; Ux = U->x ; col = q ? (q [k]) : k ; top = cs_spsolve (L, A, col, xi, x, pinv, 1) ; /* x = L\A(:,col) */ /* --- Find pivot --------------------------------------------------- */ ipiv = -1 ; a = -1 ; for (p = top ; p < n ; p++) { i = xi [p] ; /* x(i) is nonzero */ if (pinv [i] < 0) /* row i is not yet pivotal */ { if ((t = CS_ABS (x [i])) > a) { a = t ; /* largest pivot candidate so far */ ipiv = i ; } } else /* x(i) is the entry U(pinv[i],k) */ { Ui [unz] = pinv [i] ; Ux [unz++] = x [i] ; } } if (ipiv == -1 || a <= 0) return (cs_ndone (N, NULL, xi, x, 0)) ; if (pinv [col] < 0 && CS_ABS (x [col]) >= a*tol) ipiv = col ; /* --- Divide by pivot ---------------------------------------------- */ pivot = x [ipiv] ; /* the chosen pivot */ Ui [unz] = k ; /* last entry in U(:,k) is U(k,k) */ Ux [unz++] = pivot ; pinv [ipiv] = k ; /* ipiv is the kth pivot row */ Li [lnz] = ipiv ; /* first entry in L(:,k) is L(k,k) = 1 */ Lx [lnz++] = 1 ; for (p = top ; p < n ; p++) /* L(k+1:n,k) = x / pivot */ { i = xi [p] ; if (pinv [i] < 0) /* x(i) is an entry in L(:,k) */ { Li [lnz] = i ; /* save unpermuted row in L */ Lx [lnz++] = x [i] / pivot ; /* scale pivot column */ } x [i] = 0 ; /* x [0..n-1] = 0 for next k */ } } /* --- Finalize L and U ------------------------------------------------- */ Lp [n] = lnz ; Up [n] = unz ; Li = L->i ; /* fix row indices of L for final pinv */ for (p = 0 ; p < lnz ; p++) Li [p] = pinv [Li [p]] ; cs_sprealloc (L, 0) ; /* remove extra space from L and U */ cs_sprealloc (U, 0) ; return (cs_ndone (N, NULL, xi, x, 1)) ; /* success */ } igraph/src/centrality.c0000644000176000001440000034533112325527072014703 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include /* memset */ #include #include "igraph_centrality.h" #include "igraph_math.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_adjlist.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_topology.h" #include "igraph_types_internal.h" #include "igraph_stack.h" #include "igraph_dqueue.h" #include "config.h" #include "bigint.h" #include "prpack.h" int igraph_personalized_pagerank_arpack(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vector_t *reset, const igraph_vector_t *weights, igraph_arpack_options_t *options); igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector) { /* Many of the centrality measures correspond to the eigenvector of some * matrix. When v is an eigenvector, c*v is also an eigenvector, therefore * it may happen that all the scores in the eigenvector are negative, in which * case we want to negate them since the centrality scores should be positive. * However, since ARPACK is not always stable, sometimes it happens that * *some* of the centrality scores are small negative numbers. This function * helps distinguish between the two cases; it should return true if most of * the values are relatively large negative numbers, in which case we should * negate the eigenvector. */ long int i, n = igraph_vector_size(vector); igraph_real_t mi, ma; if (n == 0) return 0; mi = ma = VECTOR(*vector)[0]; for (i = 1; i < n; i++) { if (VECTOR(*vector)[i] < mi) mi = VECTOR(*vector)[i]; if (VECTOR(*vector)[i] > ma) ma = VECTOR(*vector)[i]; } if (mi >= 0) return 0; if (ma <= 0) return 1; mi /= ma; return (mi < 1e-5) ? 1 : 0; } int igraph_i_eigenvector_centrality(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_adjlist_t *adjlist=extra; igraph_vector_int_t *neis; long int i, j, nlen; for (i=0; igraph; const igraph_inclist_t *inclist=data->inclist; const igraph_vector_t *weights=data->weights; igraph_vector_t *edges; long int i, j, nlen; for (i=0; in=igraph_vcount(graph); options->start=1; /* no random start vector */ if (igraph_ecount(graph) == 0) { /* special case: empty graph */ if (value) *value = 0; if (vector) { igraph_vector_resize(vector, igraph_vcount(graph)); igraph_vector_fill(vector, 1); } return IGRAPH_SUCCESS; } if (weights) { igraph_real_t min, max; if (igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid length of weights vector when calculating " "eigenvector centrality", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); if (min == 0 && max == 0) { /* special case: all weights are zeros */ if (value) *value = 0; if (vector) { igraph_vector_resize(vector, igraph_vcount(graph)); igraph_vector_fill(vector, 1); } return IGRAPH_SUCCESS; } } IGRAPH_VECTOR_INIT_FINALLY(&values, 0); IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); IGRAPH_VECTOR_INIT_FINALLY(°ree, options->n); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 0)); RNG_BEGIN(); for (i=0; in; i++) { if (VECTOR(degree)[i]) { MATRIX(vectors, i, 0) = VECTOR(degree)[i] + RNG_UNIF(-1e-4, 1e-4); } else { MATRIX(vectors, i, 0) = 1.0; } } RNG_END(); igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); options->n = igraph_vcount(graph); options->nev = 1; options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ options->which[0]='L'; options->which[1]='A'; options->start=1; /* no random start vector */ if (!weights) { igraph_adjlist_t adjlist; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_i_eigenvector_centrality_loop(&adjlist)); IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality, &adjlist, options, 0, &values, &vectors)); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); } else { igraph_inclist_t inclist; igraph_i_eigenvector_centrality_t data = { graph, &inclist, weights }; IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); IGRAPH_CHECK(igraph_inclist_remove_duplicate(graph, &inclist)); IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality2, &data, options, 0, &values, &vectors)); igraph_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(1); } if (value) { *value=VECTOR(values)[0]; } if (vector) { igraph_real_t amax=0; long int which=0; long int i; IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); if (VECTOR(values)[0] <= 0) { /* Pathological case: largest eigenvalue is zero, therefore all the * scores can also be zeros, this will be a valid eigenvector. * This usually happens with graphs that have lots of sinks and * sources only. */ igraph_vector_fill(vector, 0); } else { for (i=0; in; i++) { igraph_real_t tmp; VECTOR(*vector)[i] = MATRIX(vectors, i, 0); tmp=fabs(VECTOR(*vector)[i]); if (tmp>amax) { amax=tmp; which=i; } } if (scale && amax!=0) { igraph_vector_scale(vector, 1/VECTOR(*vector)[which]); } else if (igraph_i_vector_mostly_negative(vector)) { igraph_vector_scale(vector, -1.0); } /* Correction for numeric inaccuracies (eliminating -0.0) */ for (i=0; in; i++) { if (VECTOR(*vector)[i] < 0) VECTOR(*vector)[i] = 0; } } } if (options->info) { IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); } igraph_matrix_destroy(&vectors); igraph_vector_destroy(&values); IGRAPH_FINALLY_CLEAN(2); return 0; } /* int igraph_i_evcent_dir(igraph_real_t *to, const igraph_real_t *from, */ /* long int n, void *extra) { */ /* /\* TODO *\/ */ /* return 0; */ /* } */ /* int igraph_i_evcent_dir2(igraph_real_t *to, const igraph_real_t *from, */ /* long int n, void *extra) { */ /* /\* TODO *\/ */ /* return 0; */ /* } */ int igraph_eigenvector_centrality_directed(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options) { igraph_matrix_t values; igraph_matrix_t vectors; igraph_vector_t indegree; igraph_bool_t dag; long int i; if (igraph_ecount(graph) == 0) { /* special case: empty graph */ if (value) *value = 0; if (vector) { igraph_vector_resize(vector, igraph_vcount(graph)); igraph_vector_fill(vector, 1); } return IGRAPH_SUCCESS; } /* Quick check: if the graph is a DAG, all the eigenvector centralities are * zeros, and so is the eigenvalue */ IGRAPH_CHECK(igraph_is_dag(graph, &dag)); if (dag) { /* special case: graph is a DAG */ IGRAPH_WARNING("graph is directed and acyclic; eigenvector centralities " "will be zeros"); if (value) *value = 0; if (vector) { igraph_vector_resize(vector, igraph_vcount(graph)); igraph_vector_fill(vector, 0); } return IGRAPH_SUCCESS; } if (weights) { igraph_real_t min, max; if (igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid length of weights vector when calculating " "eigenvector centrality", IGRAPH_EINVAL); } if (igraph_is_directed(graph)) { IGRAPH_WARNING("Weighted directed graph in eigenvector centrality"); } IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); if (min < 0.0) { IGRAPH_WARNING("Negative weights, eigenpair might be complex"); } if (min == 0.0 && max == 0.0) { /* special case: all weights are zeros */ if (value) *value = 0; if (vector) { igraph_vector_resize(vector, igraph_vcount(graph)); igraph_vector_fill(vector, 1); } return IGRAPH_SUCCESS; } } options->n=igraph_vcount(graph); options->start=1; options->nev=1; options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rnsolve */ /* LM mode is not OK here because +1 and -1 can be eigenvalues at the * same time, e.g.: a -> b -> a, c -> a */ options->which[0]='L' ; options->which[1]='R'; IGRAPH_MATRIX_INIT_FINALLY(&values, 0, 0); IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); IGRAPH_VECTOR_INIT_FINALLY(&indegree, options->n); IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1, weights)); RNG_BEGIN(); for (i=0; in; i++) { if (VECTOR(indegree)[i]) { MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); } else { MATRIX(vectors, i, 0) = 1.0; } } RNG_END(); igraph_vector_destroy(&indegree); IGRAPH_FINALLY_CLEAN(1); if (!weights) { igraph_adjlist_t adjlist; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_eigenvector_centrality, &adjlist, options, 0, &values, &vectors)); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); } else { igraph_inclist_t inclist; igraph_i_eigenvector_centrality_t data={ graph, &inclist, weights }; IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_eigenvector_centrality2, &data, options, 0, &values, &vectors)); igraph_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(1); } if (value) { *value=MATRIX(values, 0, 0); } if (vector) { igraph_real_t amax=0; long int which=0; long int i; IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); if (MATRIX(values, 0, 0) <= 0) { /* Pathological case: largest eigenvalue is zero, therefore all the * scores can also be zeros, this will be a valid eigenvector. * This usually happens with graphs that have lots of sinks and * sources only. */ igraph_vector_fill(vector, 0); MATRIX(values, 0, 0) = 0; } else { for (i=0; in; i++) { igraph_real_t tmp; VECTOR(*vector)[i] = MATRIX(vectors, i, 0); tmp=fabs(VECTOR(*vector)[i]); if (tmp>amax) { amax=tmp; which=i; } } if (scale && amax!=0) { igraph_vector_scale(vector, 1/VECTOR(*vector)[which]); } else if (igraph_i_vector_mostly_negative(vector)) { igraph_vector_scale(vector, -1.0); } } /* Correction for numeric inaccuracies (eliminating -0.0) */ for (i=0; in; i++) { if (VECTOR(*vector)[i] < 0) VECTOR(*vector)[i] = 0; } } if (options->info) { IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); } igraph_matrix_destroy(&vectors); igraph_matrix_destroy(&values); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_eigenvector_centrality * Eigenvector centrality of the vertices * * Eigenvector centrality is a measure of the importance of a node in a * network. It assigns relative scores to all nodes in the network based * on the principle that connections to high-scoring nodes contribute * more to the score of the node in question than equal connections to * low-scoring nodes. In practice, this is determined by calculating the * eigenvector corresponding to the largest positive eigenvalue of the * adjacency matrix. The centrality scores returned by igraph are always * normalized such that the largest eigenvector centrality score is one * (with one exception, see below). * * * Since the eigenvector centrality scores of nodes in different components * do not affect each other, it may be beneficial for large graphs to * decompose it first into weakly connected components and calculate the * centrality scores individually for each component. * * * Also note that the adjacency matrix of a directed acyclic graph or the * adjacency matrix of an empty graph does not possess positive eigenvalues, * therefore the eigenvector centrality is not defined for these graphs. * igraph will return an eigenvalue of zero in such cases. The eigenvector * centralities will all be equal for an empty graph and will all be zeros * for a directed acyclic graph. Such pathological cases can be detected * by asking igraph to calculate the eigenvalue as well (using the \p value * parameter, see below) and checking whether the eigenvalue is very close * to zero. * * \param graph The input graph. It might be directed. * \param vector Pointer to an initialized vector, it will be resized * as needed. The result of the computation is stored here. It can * be a null pointer, then it is ignored. * \param value If not a null pointer, then the eigenvalue * corresponding to the found eigenvector is stored here. * \param directed Boolean scalar, whether to consider edge directions * in a directed graph. It is ignored for undirected graphs. * \param scale If not zero then the result will be scaled such that * the absolute value of the maximum centrality is one. * \param weights A null pointer (=no edge weights), or a vector * giving the weights of the edges. The algorithm might result * complex numbers is some weights are negative. In this case only * the real part is reported. * \param options Options to ARPACK. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices) parameter and * it always starts the calculation from a non-random vector * calculated based on the degree of the vertices. * \return Error code. * * Time complexity: depends on the input graph, usually it is O(|V|+|E|). * * \sa \ref igraph_pagerank and \ref igraph_personalized_pagerank for * modifications of eigenvector centrality. * * \example examples/simple/eigenvector_centrality.c */ int igraph_eigenvector_centrality(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t directed, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options) { if (directed && igraph_is_directed(graph)) { return igraph_eigenvector_centrality_directed(graph, vector, value, scale, weights, options); } else { return igraph_eigenvector_centrality_undirected(graph, vector, value, scale, weights, options); } } /* struct for the unweighted variant of the HITS algorithm */ typedef struct igraph_i_kleinberg_data_t { igraph_adjlist_t *in; igraph_adjlist_t *out; igraph_vector_t *tmp; } igraph_i_kleinberg_data_t; /* struct for the weighted variant of the HITS algorithm */ typedef struct igraph_i_kleinberg_data2_t { const igraph_t *graph; igraph_inclist_t *in; igraph_inclist_t *out; igraph_vector_t *tmp; const igraph_vector_t *weights; } igraph_i_kleinberg_data2_t; /* ARPACK auxiliary routine for the unweighted HITS algorithm */ int igraph_i_kleinberg_unweighted(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_kleinberg_data_t *data = (igraph_i_kleinberg_data_t*)extra; igraph_adjlist_t *in = data->in; igraph_adjlist_t *out = data->out; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; long int i, j, nlen; for (i=0; iin; igraph_inclist_t *out = data->out; igraph_vector_t *tmp = data->tmp; const igraph_vector_t *weights = data->weights; const igraph_t *g = data->graph; igraph_vector_t *neis; long int i, j, nlen; for (i=0; in=igraph_vcount(graph); options->start=1; /* no random start vector */ IGRAPH_VECTOR_INIT_FINALLY(&values, 0); IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); IGRAPH_VECTOR_INIT_FINALLY(&tmp, options->n); if (inout==0) { inadjlist=&myinadjlist; outadjlist=&myoutadjlist; ininclist=&myininclist; outinclist=&myoutinclist; } else if (inout==1) { inadjlist=&myoutadjlist; outadjlist=&myinadjlist; ininclist=&myoutinclist; outinclist=&myininclist; } else { /* This should not happen */ IGRAPH_ERROR("Invalid 'inout' argument, please do not call " "this function directly", IGRAPH_FAILURE); } if (weights == 0) { IGRAPH_CHECK(igraph_adjlist_init(graph, &myinadjlist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &myinadjlist); IGRAPH_CHECK(igraph_adjlist_init(graph, &myoutadjlist, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &myoutadjlist); } else { IGRAPH_CHECK(igraph_inclist_init(graph, &myininclist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_inclist_destroy, &myininclist); IGRAPH_CHECK(igraph_inclist_init(graph, &myoutinclist, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_inclist_destroy, &myoutinclist); } IGRAPH_CHECK(igraph_degree(graph, &tmp, igraph_vss_all(), IGRAPH_ALL, 0)); for (i=0; in; i++) { if (VECTOR(tmp)[i] != 0) { MATRIX(vectors, i, 0) = VECTOR(tmp)[i]; } else { MATRIX(vectors, i, 0) = 1.0; } } extra.in=inadjlist; extra.out=outadjlist; extra.tmp=&tmp; extra2.in=ininclist; extra2.out=outinclist; extra2.tmp=&tmp; extra2.graph=graph; extra2.weights=weights; options->nev = 1; options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ options->which[0]='L'; options->which[1]='M'; if (weights == 0) { IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_unweighted, &extra, options, 0, &values, &vectors)); igraph_adjlist_destroy(&myoutadjlist); igraph_adjlist_destroy(&myinadjlist); IGRAPH_FINALLY_CLEAN(2); } else { IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_weighted, &extra2, options, 0, &values, &vectors)); igraph_inclist_destroy(&myoutinclist); igraph_inclist_destroy(&myininclist); IGRAPH_FINALLY_CLEAN(2); } igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); if (value) { *value = VECTOR(values)[0]; } if (vector) { igraph_real_t amax=0; long int which=0; long int i; IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); for (i=0; in; i++) { igraph_real_t tmp; VECTOR(*vector)[i] = MATRIX(vectors, i, 0); tmp=fabs(VECTOR(*vector)[i]); if (tmp>amax) { amax=tmp; which=i; } } if (scale && amax!=0) { igraph_vector_scale(vector, 1/VECTOR(*vector)[which]); } else if (igraph_i_vector_mostly_negative(vector)) { igraph_vector_scale(vector, -1.0); } /* Correction for numeric inaccuracies (eliminating -0.0) */ for (i=0; in; i++) { if (VECTOR(*vector)[i] < 0) VECTOR(*vector)[i] = 0; } } if (options->info) { IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); } igraph_matrix_destroy(&vectors); igraph_vector_destroy(&values); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_hub_score * Kleinberg's hub scores * * The hub scores of the vertices are defined as the principal * eigenvector of A*A^T, where A is the adjacency * matrix of the graph, A^T is its transposed. * * See the following reference on the meaning of this score: * J. Kleinberg. Authoritative sources in a hyperlinked * environment. \emb Proc. 9th ACM-SIAM Symposium on Discrete * Algorithms, \eme 1998. Extended version in \emb Journal of the * ACM \eme 46(1999). Also appears as IBM Research Report RJ 10076, May * 1997. * \param graph The input graph. Can be directed and undirected. * \param vector Pointer to an initialized vector, the result is * stored here. If a null pointer then it is ignored. * \param value If not a null pointer then the eigenvalue * corresponding to the calculated eigenvector is stored here. * \param scale If not zero then the result will be scaled such that * the absolute value of the maximum centrality is one. * \param weights A null pointer (=no edge weights), or a vector * giving the weights of the edges. * \param options Options to ARPACK. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices) parameter and * it always starts the calculation from a non-random vector * calculated based on the degree of the vertices. * \return Error code. * * Time complexity: depends on the input graph, usually it is O(|V|), * the number of vertices. * * \sa \ref igraph_authority_score() for the companion measure, * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), * \ref igraph_eigenvector_centrality() for similar measures. */ int igraph_hub_score(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options) { return igraph_i_kleinberg(graph, vector, value, scale, weights, options, 0); } /** * \function igraph_authority_score * Kleinerg's authority scores * * The authority scores of the vertices are defined as the principal * eigenvector of A^T*A, where A is the adjacency * matrix of the graph, A^T is its transposed. * * See the following reference on the meaning of this score: * J. Kleinberg. Authoritative sources in a hyperlinked * environment. \emb Proc. 9th ACM-SIAM Symposium on Discrete * Algorithms, \eme 1998. Extended version in \emb Journal of the * ACM \eme 46(1999). Also appears as IBM Research Report RJ 10076, May * 1997. * \param graph The input graph. Can be directed and undirected. * \param vector Pointer to an initialized vector, the result is * stored here. If a null pointer then it is ignored. * \param value If not a null pointer then the eigenvalue * corresponding to the calculated eigenvector is stored here. * \param scale If not zero then the result will be scaled such that * the absolute value of the maximum centrality is one. * \param weights A null pointer (=no edge weights), or a vector * giving the weights of the edges. * \param options Options to ARPACK. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices) parameter and * it always starts the calculation from a non-random vector * calculated based on the degree of the vertices. * \return Error code. * * Time complexity: depends on the input graph, usually it is O(|V|), * the number of vertices. * * \sa \ref igraph_hub_score() for the companion measure, * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), * \ref igraph_eigenvector_centrality() for similar measures. */ int igraph_authority_score(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options) { return igraph_i_kleinberg(graph, vector, value, scale, weights, options, 1); } typedef struct igraph_i_pagerank_data_t { const igraph_t *graph; igraph_adjlist_t *adjlist; igraph_real_t damping; igraph_vector_t *outdegree; igraph_vector_t *tmp; igraph_vector_t *reset; } igraph_i_pagerank_data_t; typedef struct igraph_i_pagerank_data2_t { const igraph_t *graph; igraph_inclist_t *inclist; const igraph_vector_t *weights; igraph_real_t damping; igraph_vector_t *outdegree; igraph_vector_t *tmp; igraph_vector_t *reset; } igraph_i_pagerank_data2_t; int igraph_i_pagerank(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_pagerank_data_t *data=extra; igraph_adjlist_t *adjlist=data->adjlist; igraph_vector_t *outdegree=data->outdegree; igraph_vector_t *tmp=data->tmp; igraph_vector_t *reset=data->reset; igraph_vector_int_t *neis; long int i, j, nlen; igraph_real_t sumfrom=0.0; igraph_real_t fact=1-data->damping; /* Calculate p(x) / outdegree(x) in advance for all the vertices. * Note that we may divide by zero here; this is intentional since * we won't use those values and we save a comparison this way. * At the same time, we calculate the global probability of a * random jump in `sumfrom`. For vertices with no outgoing edges, * we will surely jump from there if we are there, hence those * vertices contribute p(x) to the teleportation probability. * For vertices with some outgoing edges, we jump from there with * probability `fact` if we are there, hence they contribute * p(x)*fact */ for (i=0; idamping; } /* Now we add the contribution from random jumps. `reset` is a vector * that defines the probability of ending up in vertex i after a jump. * `sumfrom` is the global probability of jumping as mentioned above. */ /* printf("sumfrom = %.6f\n", (float)sumfrom); */ if (reset) { /* Running personalized PageRank */ for (i=0; igraph; igraph_inclist_t *inclist=data->inclist; const igraph_vector_t *weights=data->weights; igraph_vector_t *outdegree=data->outdegree; igraph_vector_t *tmp=data->tmp; igraph_vector_t *reset=data->reset; long int i, j, nlen; igraph_real_t sumfrom=0.0; igraph_vector_t *neis; igraph_real_t fact=1-data->damping; /* printf("PageRank weighted: multiplying vector: "); for (i=0; idamping; } /* printf("sumfrom = %.6f\n", (float)sumfrom); */ if (reset) { /* Running personalized PageRank */ for (i=0; i * Please note that the PageRank of a given vertex depends on the PageRank * of all other vertices, so even if you want to calculate the PageRank for * only some of the vertices, all of them must be calculated. Requesting * the PageRank for only some of the vertices does not result in any * performance increase at all. * * * * For the explanation of the PageRank algorithm, see the following * webpage: * http://infolab.stanford.edu/~backrub/google.html , or the * following reference: * * * * Sergey Brin and Larry Page: The Anatomy of a Large-Scale Hypertextual * Web Search Engine. Proceedings of the 7th World-Wide Web Conference, * Brisbane, Australia, April 1998. * * * \param graph The graph object. * \param algo The PageRank implementation to use. Possible values: * \c IGRAPH_PAGERANK_ALGO_POWER, \c IGRAPH_PAGERANK_ALGO_ARPACK, * \c IGRAPH_PAGERANK_ALGO_PRPACK. * \param vector Pointer to an initialized vector, the result is * stored here. It is resized as needed. * \param value Pointer to a real variable, the eigenvalue * corresponding to the PageRank vector is stored here. It should * be always exactly one. * \param vids The vertex ids for which the PageRank is returned. * \param directed Boolean, whether to consider the directedness of * the edges. This is ignored for undirected graphs. * \param damping The damping factor ("d" in the original paper) * \param weights Optional edge weights, it is either a null pointer, * then the edges are not weighted, or a vector of the same length * as the number of edges. * \param options Options to the power method or ARPACK. For the power * method, \c IGRAPH_PAGERANK_ALGO_POWER it must be a pointer to * a \ref igraph_pagerank_power_options_t object. * For \c IGRAPH_PAGERANK_ALGO_ARPACK it must be a pointer to an * \ref igraph_arpack_options_t object. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices), nev (1), * ncv (3) and which (LM) parameters and * it always starts the calculation from a non-random vector * calculated based on the degree of the vertices. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id in * \p vids. * * Time complexity: depends on the input graph, usually it is O(|E|), * the number of edges. * * \sa \ref igraph_pagerank_old() for the old implementation, * \ref igraph_personalized_pagerank() and \ref igraph_personalized_pagerank_vs() * for the personalized PageRank measure, \ref igraph_arpack_rssolve() and * \ref igraph_arpack_rnsolve() for the underlying machinery. * * \example examples/simple/igraph_pagerank.c */ int igraph_pagerank(const igraph_t *graph, igraph_pagerank_algo_t algo, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, const igraph_vector_t *weights, void *options) { return igraph_personalized_pagerank(graph, algo, vector, value, vids, directed, damping, 0, weights, options); } /** * \function igraph_personalized_pagerank_vs * \brief Calculates the personalized Google PageRank for the specified vertices. * * The personalized PageRank is similar to the original PageRank measure, but the * random walk is reset in every step with probability 1-damping to a non-uniform * distribution (instead of the uniform distribution in the original PageRank measure. * * * This simplified interface takes a vertex sequence and resets the random walk to * one of the vertices in the specified vertex sequence, chosen uniformly. A typical * application of personalized PageRank is when the random walk is reset to the same * vertex every time - this can easily be achieved using \ref igraph_vss_1() which * generates a vertex sequence containing only a single vertex. * * * Please note that the personalized PageRank of a given vertex depends on the * personalized PageRank of all other vertices, so even if you want to calculate * the personalized PageRank for only some of the vertices, all of them must be * calculated. Requesting the personalized PageRank for only some of the vertices * does not result in any performance increase at all. * * * * \param graph The graph object. * \param algo The PageRank implementation to use. Possible values: * \c IGRAPH_PAGERANK_ALGO_POWER, \c IGRAPH_PAGERANK_ALGO_ARPACK, * \c IGRAPH_PAGERANK_ALGO_PRPACK. * \param vector Pointer to an initialized vector, the result is * stored here. It is resized as needed. * \param value Pointer to a real variable, the eigenvalue * corresponding to the PageRank vector is stored here. It should * be always exactly one. * \param vids The vertex ids for which the PageRank is returned. * \param directed Boolean, whether to consider the directedness of * the edges. This is ignored for undirected graphs. * \param damping The damping factor ("d" in the original paper) * \param reset_vids IDs of the vertices used when resetting the random walk. * \param weights Optional edge weights, it is either a null pointer, * then the edges are not weighted, or a vector of the same length * as the number of edges. * \param options Options to the power method or ARPACK. For the power * method, \c IGRAPH_PAGERANK_ALGO_POWER it must be a pointer to * a \ref igraph_pagerank_power_options_t object. * For \c IGRAPH_PAGERANK_ALGO_ARPACK it must be a pointer to an * \ref igraph_arpack_options_t object. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices), nev (1), * ncv (3) and which (LM) parameters and * it always starts the calculation from a non-random vector * calculated based on the degree of the vertices. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id in * \p vids or an empty reset vertex sequence in * \p vids_reset. * * Time complexity: depends on the input graph, usually it is O(|E|), * the number of edges. * * \sa \ref igraph_pagerank() for the non-personalized implementation, * \ref igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() for * the underlying machinery. */ int igraph_personalized_pagerank_vs(const igraph_t *graph, igraph_pagerank_algo_t algo, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vs_t reset_vids, const igraph_vector_t *weights, void *options) { igraph_vector_t reset; igraph_vit_t vit; IGRAPH_VECTOR_INIT_FINALLY(&reset, igraph_vcount(graph)); IGRAPH_CHECK(igraph_vit_create(graph, reset_vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); while (!IGRAPH_VIT_END(vit)) { VECTOR(reset)[(long int)IGRAPH_VIT_GET(vit)]++; IGRAPH_VIT_NEXT(vit); } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_personalized_pagerank(graph, algo, vector, value, vids, directed, damping, &reset, weights, options)); igraph_vector_destroy(&reset); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_personalized_pagerank * \brief Calculates the personalized Google PageRank for the specified vertices. * * The personalized PageRank is similar to the original PageRank measure, but the * random walk is reset in every step with probability 1-damping to a non-uniform * distribution (instead of the uniform distribution in the original PageRank measure. * * * Please note that the personalized PageRank of a given vertex depends on the * personalized PageRank of all other vertices, so even if you want to calculate * the personalized PageRank for only some of the vertices, all of them must be * calculated. Requesting the personalized PageRank for only some of the vertices * does not result in any performance increase at all. * * * * \param graph The graph object. * \param algo The PageRank implementation to use. Possible values: * \c IGRAPH_PAGERANK_ALGO_POWER, \c IGRAPH_PAGERANK_ALGO_ARPACK, * \c IGRAPH_PAGERANK_ALGO_PRPACK. * \param vector Pointer to an initialized vector, the result is * stored here. It is resized as needed. * \param value Pointer to a real variable, the eigenvalue * corresponding to the PageRank vector is stored here. It should * be always exactly one. * \param vids The vertex ids for which the PageRank is returned. * \param directed Boolean, whether to consider the directedness of * the edges. This is ignored for undirected graphs. * \param damping The damping factor ("d" in the original paper) * \param reset The probability distribution over the vertices used when * resetting the random walk. It is either a null pointer (denoting * a uniform choice that results in the original PageRank measure) * or a vector of the same length as the number of vertices. * \param weights Optional edge weights, it is either a null pointer, * then the edges are not weighted, or a vector of the same length * as the number of edges. * \param options Options to the power method or ARPACK. For the power * method, \c IGRAPH_PAGERANK_ALGO_POWER it must be a pointer to * a \ref igraph_pagerank_power_options_t object. * For \c IGRAPH_PAGERANK_ALGO_ARPACK it must be a pointer to an * \ref igraph_arpack_options_t object. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices), nev (1), * ncv (3) and which (LM) parameters and * it always starts the calculation from a non-random vector * calculated based on the degree of the vertices. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id in * \p vids or an invalid reset vector in \p reset. * * Time complexity: depends on the input graph, usually it is O(|E|), * the number of edges. * * \sa \ref igraph_pagerank() for the non-personalized implementation, * \ref igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() for * the underlying machinery. */ int igraph_personalized_pagerank(const igraph_t *graph, igraph_pagerank_algo_t algo, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vector_t *reset, const igraph_vector_t *weights, void *options) { if (algo == IGRAPH_PAGERANK_ALGO_POWER) { igraph_pagerank_power_options_t *o = (igraph_pagerank_power_options_t *) options; if (reset) { IGRAPH_WARNING("Cannot use weights with power method, " "weights will be ignored"); } return igraph_pagerank_old(graph, vector, vids, directed, o->niter, o->eps, damping, /*old=*/ 0); } else if (algo == IGRAPH_PAGERANK_ALGO_ARPACK) { igraph_arpack_options_t *o= (igraph_arpack_options_t*) options; return igraph_personalized_pagerank_arpack(graph, vector, value, vids, directed, damping, reset, weights, o); } else if (algo == IGRAPH_PAGERANK_ALGO_PRPACK) { return igraph_personalized_pagerank_prpack(graph, vector, value, vids, directed, damping, reset, weights); } else { IGRAPH_ERROR("Unknown PageRank algorithm", IGRAPH_EINVAL); } return 0; } /* * ARPACK-based implementation of \c igraph_personalized_pagerank. * * See \c igraph_personalized_pagerank for the documentation of the parameters. */ int igraph_personalized_pagerank_arpack(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t damping, igraph_vector_t *reset, const igraph_vector_t *weights, igraph_arpack_options_t *options) { igraph_matrix_t values; igraph_matrix_t vectors; igraph_neimode_t dirmode; igraph_vector_t outdegree; igraph_vector_t indegree; igraph_vector_t tmp; long int i; long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); if (no_of_edges == 0) { /* special case: empty graph */ if (value) *value = 1.0; if (vector) { igraph_vector_resize(vector, no_of_nodes); igraph_vector_fill(vector, 1.0 / no_of_nodes); } return IGRAPH_SUCCESS; } options->n = (int) no_of_nodes; options->nev = 1; options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rnsolve */ options->which[0]='L'; options->which[1]='M'; options->start = 1; /* no random start vector */ directed = directed && igraph_is_directed(graph); if (weights) { igraph_real_t min, max; if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid length of weights vector when calculating " "PageRank scores", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); if (min == 0 && max == 0) { /* special case: all weights are zeros */ if (value) *value = 1.0; if (vector) { igraph_vector_resize(vector, igraph_vcount(graph)); igraph_vector_fill(vector, 1.0 / no_of_nodes); } return IGRAPH_SUCCESS; } } if (reset && igraph_vector_size(reset) != no_of_nodes) { IGRAPH_ERROR("Invalid length of reset vector when calculating " "personalized PageRank scores", IGRAPH_EINVAL); } IGRAPH_MATRIX_INIT_FINALLY(&values, 0, 0); IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); if (directed) { dirmode=IGRAPH_IN; } else { dirmode=IGRAPH_ALL; } IGRAPH_VECTOR_INIT_FINALLY(&indegree, options->n); IGRAPH_VECTOR_INIT_FINALLY(&outdegree, options->n); IGRAPH_VECTOR_INIT_FINALLY(&tmp, options->n); RNG_BEGIN(); if (reset) { /* Normalize reset vector so the sum is 1 */ double reset_sum; if (igraph_vector_min(reset) < 0) IGRAPH_ERROR("the reset vector must not contain negative elements", IGRAPH_EINVAL); reset_sum = igraph_vector_sum(reset); if (reset_sum == 0) IGRAPH_ERROR("the sum of the elements in the reset vector must not be zero", IGRAPH_EINVAL); igraph_vector_scale(reset, 1.0/reset_sum); } if (!weights) { igraph_adjlist_t adjlist; igraph_i_pagerank_data_t data = { graph, &adjlist, damping, &outdegree, &tmp, reset }; IGRAPH_CHECK(igraph_degree(graph, &outdegree, igraph_vss_all(), directed ? IGRAPH_OUT : IGRAPH_ALL, /*loops=*/ 0)); IGRAPH_CHECK(igraph_degree(graph, &indegree, igraph_vss_all(), directed ? IGRAPH_IN : IGRAPH_ALL, /*loops=*/ 0)); /* Set up an appropriate starting vector. We start from the in-degrees * plus some small random noise to avoid convergence problems */ for (i=0; in; i++) { if (VECTOR(indegree)[i]) MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); else MATRIX(vectors, i, 0) = 1; } IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, dirmode)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_pagerank, &data, options, 0, &values, &vectors)); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); } else { igraph_inclist_t inclist; igraph_bool_t negative_weight_warned = 0; igraph_i_pagerank_data2_t data = { graph, &inclist, weights, damping, &outdegree, &tmp, reset }; IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); /* Weighted degree */ for (i=0; in; i++) { if (VECTOR(indegree)[i]) MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); else MATRIX(vectors, i, 0) = 1; } IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_pagerank2, &data, options, 0, &values, &vectors)); igraph_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(1); } RNG_END(); igraph_vector_destroy(&tmp); igraph_vector_destroy(&outdegree); igraph_vector_destroy(&indegree); IGRAPH_FINALLY_CLEAN(3); if (value) { *value=MATRIX(values, 0, 0); } if (vector) { long int i; igraph_vit_t vit; long int nodes_to_calc; igraph_real_t sum=0; for (i=0; iinfo) { IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); } igraph_matrix_destroy(&vectors); igraph_matrix_destroy(&values); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \ingroup structural * \function igraph_betweenness * \brief Betweenness centrality of some vertices. * * * The betweenness centrality of a vertex is the number of geodesics * going through it. If there are more than one geodesic between two * vertices, the value of these geodesics are weighted by one over the * number of geodesics. * \param graph The graph object. * \param res The result of the computation, a vector containing the * betweenness scores for the specified vertices. * \param vids The vertices of which the betweenness centrality scores * will be calculated. * \param directed Logical, if true directed paths will be considered * for directed graphs. It is ignored for undirected graphs. * \param weights An optional vector containing edge weights for * calculating weighted betweenness. Supply a null pointer here * for unweighted betweenness. * \param nobigint Logical, if true, then we don't use big integers * for the calculation, setting this to 1 (=true) should * work for most graphs. It is currently ignored for weighted * graphs. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id passed in * \p vids. * * Time complexity: O(|V||E|), * |V| and * |E| are the number of vertices and * edges in the graph. * Note that the time complexity is independent of the number of * vertices for which the score is calculated. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). * See \ref igraph_edge_betweenness() for calculating the betweenness score * of the edges in a graph. See \ref igraph_betweenness_estimate() to * estimate the betweenness score of the vertices in a graph. * * \example examples/simple/igraph_betweenness.c */ int igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, const igraph_vector_t* weights, igraph_bool_t nobigint) { return igraph_betweenness_estimate(graph, res, vids, directed, -1, weights, nobigint); } int igraph_i_betweenness_estimate_weighted(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t cutoff, const igraph_vector_t *weights, igraph_bool_t nobigint) { igraph_integer_t no_of_nodes=(igraph_integer_t) igraph_vcount(graph); igraph_integer_t no_of_edges=(igraph_integer_t) igraph_ecount(graph); igraph_2wheap_t Q; igraph_inclist_t inclist; igraph_adjlist_t fathers; long int source, j; igraph_stack_t S; igraph_neimode_t mode= directed ? IGRAPH_OUT : IGRAPH_ALL; igraph_vector_t dist, nrgeo, tmpscore; igraph_vector_t v_tmpres, *tmpres=&v_tmpres; igraph_vit_t vit; IGRAPH_UNUSED(nobigint); if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); } if (igraph_vector_min(weights) <= 0) { IGRAPH_ERROR("Weight vector must be positive", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); IGRAPH_CHECK(igraph_adjlist_init_empty(&fathers, no_of_nodes)); IGRAPH_FINALLY(igraph_adjlist_destroy, &fathers); IGRAPH_CHECK(igraph_stack_init(&S, no_of_nodes)); IGRAPH_FINALLY(igraph_stack_destroy, &S); IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&tmpscore, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&nrgeo, no_of_nodes); if (igraph_vs_is_all(&vids)) { IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); igraph_vector_null(res); tmpres=res; } else { IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); } for (source=0; source=0 && VECTOR(dist)[minnei] >= cutoff+1.0) { continue; } /* Now check all neighbors of 'minnei' for a shorter path */ neis=igraph_inclist_get(&inclist, minnei); nlen=igraph_vector_size(neis); for (j=0; j * The betweenness centrality of a vertex is the number of geodesics * going through it. If there are more than one geodesic between two * vertices, the value of these geodesics are weighted by one over the * number of geodesics. When estimating betweenness centrality, igraph * takes into consideration only those paths that are shorter than or * equal to a prescribed length. Note that the estimated centrality * will always be less than the real one. * * \param graph The graph object. * \param res The result of the computation, a vector containing the * estimated betweenness scores for the specified vertices. * \param vids The vertices of which the betweenness centrality scores * will be estimated. * \param directed Logical, if true directed paths will be considered * for directed graphs. It is ignored for undirected graphs. * \param cutoff The maximal length of paths that will be considered. * If zero or negative, the exact betweenness will be calculated * (no upper limit on path lengths). * \param weights An optional vector containing edge weights for * calculating weighted betweenness. Supply a null pointer here * for unweighted betweenness. * \param nobigint Logical, if true, then we don't use big integers * for the calculation, setting this to 1 (=true) should * work for most graphs. It is currently ignored for weighted * graphs. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id passed in * \p vids. * * Time complexity: O(|V||E|), * |V| and * |E| are the number of vertices and * edges in the graph. * Note that the time complexity is independent of the number of * vertices for which the score is calculated. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). * See \ref igraph_edge_betweenness() for calculating the betweenness score * of the edges in a graph. */ int igraph_betweenness_estimate(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, igraph_real_t cutoff, const igraph_vector_t *weights, igraph_bool_t nobigint) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; long int *distance; unsigned long long int *nrgeo=0; /* must be long long; consider grid graphs for example */ igraph_biguint_t *big_nrgeo=0; double *tmpscore; igraph_stack_t stack=IGRAPH_STACK_NULL; long int source; long int j, k, nneis; igraph_vector_int_t *neis; igraph_vector_t v_tmpres, *tmpres=&v_tmpres; igraph_vit_t vit; igraph_adjlist_t adjlist_out, adjlist_in; igraph_adjlist_t *adjlist_out_p, *adjlist_in_p; igraph_biguint_t D, R, T; if (weights) { return igraph_i_betweenness_estimate_weighted(graph, res, vids, directed, cutoff, weights, nobigint); } if (!igraph_vs_is_all(&vids)) { /* subset */ IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); } else { /* only */ IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); igraph_vector_null(res); tmpres=res; } directed=directed && igraph_is_directed(graph); if (directed) { IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_in); adjlist_out_p=&adjlist_out; adjlist_in_p=&adjlist_in; } else { IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_in); adjlist_out_p=&adjlist_out; adjlist_in_p=&adjlist_in; } for (j=0; j= 0 && distance[actnode] >= cutoff+1) { continue; } neis = igraph_adjlist_get(adjlist_out_p, actnode); nneis = igraph_vector_int_size(neis); for (j=0; j=0 && VECTOR(distance)[minnei] >= cutoff+1.0) { continue; } neis=igraph_inclist_get(&inclist, minnei); nlen=igraph_vector_size(neis); for (j=0; j * The betweenness centrality of an edge is the number of geodesics * going through it. If there are more than one geodesics between two * vertices, the value of these geodesics are weighted by one over the * number of geodesics. * \param graph The graph object. * \param result The result of the computation, vector containing the * betweenness scores for the edges. * \param directed Logical, if true directed paths will be considered * for directed graphs. It is ignored for undirected graphs. * \param weights An optional weight vector for weighted edge * betweenness. Supply a null pointer here for the unweighted * version. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * * Time complexity: O(|V||E|), * |V| and * |E| are the number of vertices and * edges in the graph. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). * See \ref igraph_edge_betweenness() for calculating the betweenness score * of the edges in a graph. See \ref igraph_edge_betweenness_estimate() to * estimate the betweenness score of the edges in a graph. * * \example examples/simple/igraph_edge_betweenness.c */ int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, igraph_bool_t directed, const igraph_vector_t *weights) { return igraph_edge_betweenness_estimate(graph, result, directed, -1, weights); } /** * \ingroup structural * \function igraph_edge_betweenness_estimate * \brief Estimated betweenness centrality of the edges. * * * The betweenness centrality of an edge is the number of geodesics * going through it. If there are more than one geodesics between two * vertices, the value of these geodesics are weighted by one over the * number of geodesics. When estimating betweenness centrality, igraph * takes into consideration only those paths that are shorter than or * equal to a prescribed length. Note that the estimated centrality * will always be less than the real one. * \param graph The graph object. * \param result The result of the computation, vector containing the * betweenness scores for the edges. * \param directed Logical, if true directed paths will be considered * for directed graphs. It is ignored for undirected graphs. * \param cutoff The maximal length of paths that will be considered. * If zero or negative, the exact betweenness will be calculated * (no upper limit on path lengths). * \param weights An optional weight vector for weighted * betweenness. Supply a null pointer here for unweighted * betweenness. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * * Time complexity: O(|V||E|), * |V| and * |E| are the number of vertices and * edges in the graph. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). * See \ref igraph_betweenness() for calculating the betweenness score * of the vertices in a graph. */ int igraph_edge_betweenness_estimate(const igraph_t *graph, igraph_vector_t *result, igraph_bool_t directed, igraph_real_t cutoff, const igraph_vector_t *weights) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; long int *distance; unsigned long long int *nrgeo; double *tmpscore; igraph_stack_t stack=IGRAPH_STACK_NULL; long int source; long int j; igraph_inclist_t elist_out, elist_in; igraph_inclist_t *elist_out_p, *elist_in_p; igraph_vector_t *neip; long int neino; long int i; if (weights) { return igraph_i_edge_betweenness_estimate_weighted(graph, result, directed, cutoff, weights); } directed=directed && igraph_is_directed(graph); if (directed) { IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN)); IGRAPH_FINALLY(igraph_inclist_destroy, &elist_in); elist_out_p=&elist_out; elist_in_p=&elist_in; } else { IGRAPH_CHECK(igraph_inclist_init(graph,&elist_out, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); elist_out_p=elist_in_p=&elist_out; } distance=igraph_Calloc(no_of_nodes, long int); if (distance==0) { IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, distance); nrgeo=igraph_Calloc(no_of_nodes, unsigned long long int); if (nrgeo==0) { IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nrgeo); tmpscore=igraph_Calloc(no_of_nodes, double); if (tmpscore==0) { IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmpscore); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); igraph_vector_null(result); /* here we go */ for (source=0; source 0 && distance[actnode] >= cutoff ) continue; neip=igraph_inclist_get(elist_out_p, actnode); neino=igraph_vector_size(neip); for (i=0; i * The closeness centrality of a vertex measures how easily other * vertices can be reached from it (or the other way: how easily it * can be reached from the other vertices). It is defined as the * number of the number of vertices minus one divided by the sum of the * lengths of all geodesics from/to the given vertex. * * * If the graph is not connected, and there is no path between two * vertices, the number of vertices is used instead the length of the * geodesic. This is always longer than the longest possible geodesic. * * \param graph The graph object. * \param res The result of the computation, a vector containing the * closeness centrality scores for the given vertices. * \param vids Vector giving the vertices for which the closeness * centrality scores will be computed. * \param mode The type of shortest paths to be used for the * calculation in directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the lengths of the outgoing paths are calculated. * \cli IGRAPH_IN * the lengths of the incoming paths are calculated. * \cli IGRAPH_ALL * the directed graph is considered as an * undirected one for the computation. * \endclist * \param weights An optional vector containing edge weights for * weighted closeness. Supply a null pointer here for * traditional, unweighted closeness. * \param normalized Boolean, whether to normalize results by multiplying * by the number of vertices minus one. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(n|E|), * n is the number * of vertices for which the calculation is done and * |E| is the number * of edges in the graph. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_betweenness(). * See \ref igraph_closeness_estimate() to estimate closeness values. */ int igraph_closeness(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, const igraph_vector_t *weights, igraph_bool_t normalized) { return igraph_closeness_estimate(graph, res, vids, mode, -1, weights, normalized); } int igraph_i_closeness_estimate_weighted(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_real_t cutoff, const igraph_vector_t *weights, igraph_bool_t normalized) { /* See igraph_shortest_paths_dijkstra() for the implementation details and the dirty tricks. */ long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_2wheap_t Q; igraph_vit_t vit; long int nodes_to_calc; igraph_lazy_inclist_t inclist; long int i, j; igraph_vector_t dist; igraph_vector_long_t which; long int nodes_reached; if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } if (igraph_vector_min(weights) < 0) { IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc=IGRAPH_VIT_SIZE(vit); IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); IGRAPH_CHECK(igraph_vector_long_init(&which, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &which); IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); igraph_vector_null(res); for (i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int source=IGRAPH_VIT_GET(vit); igraph_2wheap_clear(&Q); igraph_2wheap_push_with_index(&Q, source, 0); VECTOR(which)[source]=i+1; VECTOR(dist)[source]=0.0; nodes_reached=0; while (!igraph_2wheap_empty(&Q)) { igraph_integer_t minnei=(igraph_integer_t) igraph_2wheap_max_index(&Q); igraph_real_t mindist=-igraph_2wheap_delete_max(&Q); /* Now check all neighbors of minnei for a shorter path */ igraph_vector_t *neis=igraph_lazy_inclist_get(&inclist, minnei); long int nlen=igraph_vector_size(neis); VECTOR(*res)[i] += mindist; nodes_reached++; if (cutoff>0 && mindist>=cutoff) continue; /* NOT break!!! */ for (j=0; j * The closeness centrality of a vertex measures how easily other * vertices can be reached from it (or the other way: how easily it * can be reached from the other vertices). It is defined as the * number of the number of vertices minus one divided by the sum of the * lengths of all geodesics from/to the given vertex. When estimating * closeness centrality, igraph considers paths having a length less than * or equal to a prescribed cutoff value. * * * If the graph is not connected, and there is no such path between two * vertices, the number of vertices is used instead the length of the * geodesic. This is always longer than the longest possible geodesic. * * * Since the estimation considers vertex pairs with a distance greater than * the given value as disconnected, the resulting estimation will always be * lower than the actual closeness centrality. * * \param graph The graph object. * \param res The result of the computation, a vector containing the * closeness centrality scores for the given vertices. * \param vids Vector giving the vertices for which the closeness * centrality scores will be computed. * \param mode The type of shortest paths to be used for the * calculation in directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the lengths of the outgoing paths are calculated. * \cli IGRAPH_IN * the lengths of the incoming paths are calculated. * \cli IGRAPH_ALL * the directed graph is considered as an * undirected one for the computation. * \endclist * \param cutoff The maximal length of paths that will be considered. * If zero or negative, the exact closeness will be calculated * (no upper limit on path lengths). * \param weights An optional vector containing edge weights for * weighted closeness. Supply a null pointer here for * traditional, unweighted closeness. * \param normalized Boolean, whether to normalize results by multiplying * by the number of vertices minus one. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(n|E|), * n is the number * of vertices for which the calculation is done and * |E| is the number * of edges in the graph. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_betweenness(). */ int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_real_t cutoff, const igraph_vector_t *weights, igraph_bool_t normalized) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t already_counted; igraph_vector_int_t *neis; long int i, j; long int nodes_reached; igraph_adjlist_t allneis; igraph_dqueue_t q; long int nodes_to_calc; igraph_vit_t vit; if (weights) { return igraph_i_closeness_estimate_weighted(graph, res, vids, mode, cutoff, weights, normalized); } IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc=IGRAPH_VIT_SIZE(vit); if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("calculating closeness", IGRAPH_EINVMODE); } IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode)); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); igraph_vector_null(res); for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { igraph_dqueue_clear(&q); IGRAPH_CHECK(igraph_dqueue_push(&q, IGRAPH_VIT_GET(vit))); IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); nodes_reached=1; VECTOR(already_counted)[(long int)IGRAPH_VIT_GET(vit)]=i+1; IGRAPH_PROGRESS("Closeness: ", 100.0*i/no_of_nodes, NULL); IGRAPH_ALLOW_INTERRUPTION(); while (!igraph_dqueue_empty(&q)) { long int act=(long int) igraph_dqueue_pop(&q); long int actdist=(long int) igraph_dqueue_pop(&q); VECTOR(*res)[i] += actdist; if (cutoff>0 && actdist>=cutoff) continue; /* NOT break!!! */ neis=igraph_adjlist_get(&allneis, act); for (j=0; jIn order to make graphs of different sizes comparable, * the centralization index is usually normalized to a number between * zero and one, by dividing the (unnormalized) centralization score * of the most centralized structure with the same number of vertices. * * For most centrality indices the most centralized * structure is the star graph, a single center connected to all other * nodes in the network. There are some variation depending on whether * the graph is directed or not, whether loop edges are allowed, etc. * * * This function simply calculates the graph level index, if the node * level scores and the theoretical maximum are given. It is called by * all the measure-specific centralization functions. * * \param scores A vector containing the node-level centrality * scores. * \param theoretical_max The graph level centrality score of the most * centralized graph with the same number of vertices. Only used * if \c normalized set to true. * \param normalized Boolean, whether to normalize the centralization * by dividing the supplied theoretical maximum. * \return The graph level index. * * \sa \ref igraph_centralization_degree(), \ref * igraph_centralization_betweenness(), \ref * igraph_centralization_closeness(), and \ref * igraph_centralization_eigenvector_centrality() for specific * centralization functions. * * Time complexity: O(n), the length of the score vector. * * \example examples/simple/centralization.c */ igraph_real_t igraph_centralization(const igraph_vector_t *scores, igraph_real_t theoretical_max, igraph_bool_t normalized) { long int no_of_nodes=igraph_vector_size(scores); igraph_real_t maxscore=0.0; igraph_real_t cent=0.0; if (no_of_nodes != 0) { maxscore = igraph_vector_max(scores); cent = no_of_nodes * maxscore - igraph_vector_sum(scores); if (normalized) { cent = cent/theoretical_max; } } else { cent = IGRAPH_NAN; } return cent; } /** * \function igraph_centralization_degree * Calculate vertex degree and graph centralization * * This function calculates the degree of the vertices by passing its * arguments to \ref igraph_degree(); and it calculates the graph * level centralization index based on the results by calling \ref * igraph_centralization(). * \param graph The input graph. * \param res A vector if you need the node-level degree scores, or a * null pointer otherwise. * \param mode Constant the specifies the type of degree for directed * graphs. Possible values: \c IGRAPH_IN, \c IGRAPH_OUT and \c * IGRAPH_ALL. This argument is ignored for undirected graphs. * \param loops Boolean, whether to consider loop edges when * calculating the degree (and the centralization). * \param centralization Pointer to a real number, the centralization * score is placed here. * \param theoretical_max Pointer to real number or a null pointer. If * not a null pointer, then the theoretical maximum graph * centrality score for a graph with the same number vertices is * stored here. * \param normalized Boolean, whether to calculate a normalized * centralization score. See \ref igraph_centralization() for how * the normalization is done. * \return Error code. * * \sa \ref igraph_centralization(), \ref igraph_degree(). * * Time complexity: the complexity of \ref igraph_degree() plus O(n), * the number of vertices queried, for calculating the centralization * score. */ int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, igraph_neimode_t mode, igraph_bool_t loops, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized) { igraph_vector_t myscores; igraph_vector_t *scores=res; igraph_real_t *tmax=theoretical_max, mytmax; if (!tmax) { tmax=&mytmax; } if (!res) { scores=&myscores; IGRAPH_VECTOR_INIT_FINALLY(scores, 0); } IGRAPH_CHECK(igraph_degree(graph, scores, igraph_vss_all(), mode, loops)); IGRAPH_CHECK(igraph_centralization_degree_tmax(graph, 0, mode, loops, tmax)); *centralization = igraph_centralization(scores, *tmax, normalized); if (!res) { igraph_vector_destroy(scores); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_centralization_degree_tmax * Theoretical maximum for graph centralization based on degree * * This function returns the theoretical maximum graph centrality * based on vertex degree. * * * There are two ways to call this function, the first is to supply a * graph as the graph argument, and then the number of * vertices is taken from this object, and its directedness is * considered as well. The nodes argument is ignored in * this case. The mode argument is also ignored if the * supplied graph is undirected. * * * The other way is to supply a null pointer as the graph * argument. In this case the nodes and mode * arguments are considered. * * * The most centralized structure is the star. More specifically, for * undirected graphs it is the star, for directed graphs it is the * in-star or the out-star. * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the * graph argument is not a null pointer. * \param mode Constant, whether the calculation is based on in-degree * (IGRAPH_IN), out-degree (IGRAPH_OUT) * or total degree (IGRAPH_ALL). This is ignored if * the graph argument is not a null pointer and the * given graph is undirected. * \param loops Boolean scalar, whether to consider loop edges in the * calculation. * \param res Pointer to a real variable, the result is stored here. * \return Error code. * * Time complexity: O(1). * * \sa \ref igraph_centralization_degree() and \ref * igraph_centralization(). */ int igraph_centralization_degree_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_bool_t loops, igraph_real_t *res) { igraph_bool_t directed=mode != IGRAPH_ALL; igraph_real_t real_nodes; if (graph) { directed=igraph_is_directed(graph); nodes=igraph_vcount(graph); } real_nodes = nodes; /* implicit cast to igraph_real_t */ if (directed) { switch (mode) { case IGRAPH_IN: case IGRAPH_OUT: if (!loops) { *res = (real_nodes-1) * (real_nodes-1); } else { *res = (real_nodes-1) * real_nodes; } break; case IGRAPH_ALL: if (!loops) { *res = 2 * (real_nodes-1) * (real_nodes-2); } else { *res = 2 * (real_nodes-1) * (real_nodes-1); } break; } } else { if (!loops) { *res = (real_nodes-1) * (real_nodes-2); } else { *res = (real_nodes-1) * real_nodes; } } return 0; } /** * \function igraph_centralization_betweenness * Calculate vertex betweenness and graph centralization * * This function calculates the betweenness centrality of the vertices * by passing its arguments to \ref igraph_betweenness(); and it * calculates the graph level centralization index based on the * results by calling \ref igraph_centralization(). * \param graph The input graph. * \param res A vector if you need the node-level betweenness scores, or a * null pointer otherwise. * \param directed Boolean, whether to consider directed paths when * calculating betweenness. * \param nobigint Logical, if true, then we don't use big integers * for the calculation, setting this to zero (=false) should * work for most graphs. It is currently ignored for weighted * graphs. * \param centralization Pointer to a real number, the centralization * score is placed here. * \param theoretical_max Pointer to real number or a null pointer. If * not a null pointer, then the theoretical maximum graph * centrality score for a graph with the same number vertices is * stored here. * \param normalized Boolean, whether to calculate a normalized * centralization score. See \ref igraph_centralization() for how * the normalization is done. * \return Error code. * * \sa \ref igraph_centralization(), \ref igraph_betweenness(). * * Time complexity: the complexity of \ref igraph_betweenness() plus * O(n), the number of vertices queried, for calculating the * centralization score. */ int igraph_centralization_betweenness(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t directed, igraph_bool_t nobigint, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized) { igraph_vector_t myscores; igraph_vector_t *scores=res; igraph_real_t *tmax=theoretical_max, mytmax; if (!tmax) { tmax=&mytmax; } if (!res) { scores=&myscores; IGRAPH_VECTOR_INIT_FINALLY(scores, 0); } IGRAPH_CHECK(igraph_betweenness(graph, scores, igraph_vss_all(), directed, /*weights=*/ 0, nobigint)); IGRAPH_CHECK(igraph_centralization_betweenness_tmax(graph, 0, directed, tmax)); *centralization = igraph_centralization(scores, *tmax, normalized); if (!res) { igraph_vector_destroy(scores); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_centralization_betweenness_tmax * Theoretical maximum for graph centralization based on betweenness * * This function returns the theoretical maximum graph centrality * based on vertex betweenness. * * * There are two ways to call this function, the first is to supply a * graph as the graph argument, and then the number of * vertices is taken from this object, and its directedness is * considered as well. The nodes argument is ignored in * this case. The directed argument is also ignored if the * supplied graph is undirected. * * * The other way is to supply a null pointer as the graph * argument. In this case the nodes and directed * arguments are considered. * * * The most centralized structure is the star. * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the * graph argument is not a null pointer. * \param directed Boolean scalar, whether to use directed paths in * the betweenness calculation. This argument is ignored if * graph is not a null pointer and it is undirected. * \param res Pointer to a real variable, the result is stored here. * \return Error code. * * Time complexity: O(1). * * \sa \ref igraph_centralization_betweenness() and \ref * igraph_centralization(). */ int igraph_centralization_betweenness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_real_t *res) { igraph_real_t real_nodes; if (graph) { directed=directed && igraph_is_directed(graph); nodes=igraph_vcount(graph); } real_nodes = nodes; /* implicit cast to igraph_real_t */ if (directed) { *res = (real_nodes-1) * (real_nodes-1) * (real_nodes-2); } else { *res = (real_nodes-1) * (real_nodes-1) * (real_nodes-2) / 2.0; } return 0; } /** * \function igraph_centralization_closeness * Calculate vertex closeness and graph centralization * * This function calculates the closeness centrality of the vertices * by passing its arguments to \ref igraph_closeness(); and it * calculates the graph level centralization index based on the * results by calling \ref igraph_centralization(). * \param graph The input graph. * \param res A vector if you need the node-level closeness scores, or a * null pointer otherwise. * \param mode Constant the specifies the type of closeness for directed * graphs. Possible values: \c IGRAPH_IN, \c IGRAPH_OUT and \c * IGRAPH_ALL. This argument is ignored for undirected graphs. See * \ref igraph_closeness() argument with the same name for more. * \param centralization Pointer to a real number, the centralization * score is placed here. * \param theoretical_max Pointer to real number or a null pointer. If * not a null pointer, then the theoretical maximum graph * centrality score for a graph with the same number vertices is * stored here. * \param normalized Boolean, whether to calculate a normalized * centralization score. See \ref igraph_centralization() for how * the normalization is done. * \return Error code. * * \sa \ref igraph_centralization(), \ref igraph_closeness(). * * Time complexity: the complexity of \ref igraph_closeness() plus * O(n), the number of vertices queried, for calculating the * centralization score. */ int igraph_centralization_closeness(const igraph_t *graph, igraph_vector_t *res, igraph_neimode_t mode, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized) { igraph_vector_t myscores; igraph_vector_t *scores=res; igraph_real_t *tmax=theoretical_max, mytmax; if (!tmax) { tmax=&mytmax; } if (!res) { scores=&myscores; IGRAPH_VECTOR_INIT_FINALLY(scores, 0); } IGRAPH_CHECK(igraph_closeness(graph, scores, igraph_vss_all(), mode, /*weights=*/ 0, /*normalize=*/ 1)); IGRAPH_CHECK(igraph_centralization_closeness_tmax(graph, 0, mode, tmax)); *centralization = igraph_centralization(scores, *tmax, normalized); if (!res) { igraph_vector_destroy(scores); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_centralization_closeness_tmax * Theoretical maximum for graph centralization based on closeness * * This function returns the theoretical maximum graph centrality * based on vertex closeness. * * * There are two ways to call this function, the first is to supply a * graph as the graph argument, and then the number of * vertices is taken from this object, and its directedness is * considered as well. The nodes argument is ignored in * this case. The mode argument is also ignored if the * supplied graph is undirected. * * * The other way is to supply a null pointer as the graph * argument. In this case the nodes and mode * arguments are considered. * * * The most centralized structure is the star. * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the * graph argument is not a null pointer. * \param mode Constant, specifies what kinf of distances to consider * to calculate closeness. See the mode argument of * \ref igraph_closeness() for details. This argument is ignored * if graph is not a null pointer and it is * undirected. * \param res Pointer to a real variable, the result is stored here. * \return Error code. * * Time complexity: O(1). * * \sa \ref igraph_centralization_closeness() and \ref * igraph_centralization(). */ int igraph_centralization_closeness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_real_t *res) { igraph_real_t real_nodes; if (graph) { nodes=igraph_vcount(graph); if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } } real_nodes = nodes; /* implicit cast to igraph_real_t */ if (mode != IGRAPH_ALL) { *res = (real_nodes-1) * (1.0-1.0/real_nodes); } else { *res = (real_nodes-1) * (real_nodes-2) / (2.0*real_nodes-3); } return 0; } /** * \function igraph_centralization_eigenvector_centrality * Calculate eigenvector centrality scores and graph centralization * * This function calculates the eigenvector centrality of the vertices * by passing its arguments to \ref igraph_eigenvector_centrality); * and it calculates the graph level centralization index based on the * results by calling \ref igraph_centralization(). * \param graph The input graph. * \param vector A vector if you need the node-level eigenvector * centrality scores, or a null pointer otherwise. * \param value If not a null pointer, then the leading eigenvalue is * stored here. * \param scale If not zero then the result will be scaled, such that * the absolute value of the maximum centrality is one. * \param options Options to ARPACK. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices) parameter and * it always starts the calculation from a non-random vector * calculated based on the degree of the vertices. * \param centralization Pointer to a real number, the centralization * score is placed here. * \param theoretical_max Pointer to real number or a null pointer. If * not a null pointer, then the theoretical maximum graph * centrality score for a graph with the same number vertices is * stored here. * \param normalized Boolean, whether to calculate a normalized * centralization score. See \ref igraph_centralization() for how * the normalization is done. * \return Error code. * * \sa \ref igraph_centralization(), \ref igraph_eigenvector_centrality(). * * Time complexity: the complexity of \ref * igraph_eigenvector_centrality() plus O(|V|), the number of vertices * for the calculating the centralization. */ int igraph_centralization_eigenvector_centrality( const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t directed, igraph_bool_t scale, igraph_arpack_options_t *options, igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized) { igraph_vector_t myscores; igraph_vector_t *scores=vector; igraph_real_t realvalue, *myvalue=value; igraph_real_t *tmax=theoretical_max, mytmax; if (!tmax) { tmax=&mytmax; } if (!vector) { scores=&myscores; IGRAPH_VECTOR_INIT_FINALLY(scores, 0); } if (!value) { myvalue=&realvalue; } IGRAPH_CHECK(igraph_eigenvector_centrality(graph, scores, myvalue, directed, scale, /*weights=*/ 0, options)); IGRAPH_CHECK(igraph_centralization_eigenvector_centrality_tmax( graph, 0, directed, scale, tmax)); *centralization = igraph_centralization(scores, *tmax, normalized); if (!vector) { igraph_vector_destroy(scores); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_centralization_eigenvector_centrality_tmax * Theoretical maximum centralization for eigenvector centrality * * This function returns the theoretical maximum graph centrality * based on vertex eigenvector centrality. * * * There are two ways to call this function, the first is to supply a * graph as the graph argument, and then the number of * vertices is taken from this object, and its directedness is * considered as well. The nodes argument is ignored in * this case. The directed argument is also ignored if the * supplied graph is undirected. * * * The other way is to supply a null pointer as the graph * argument. In this case the nodes and directed * arguments are considered. * * * The most centralized directed structure is the in-star. The most * centralized undirected structure is the graph with a single edge. * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the * graph argument is not a null pointer. * \param directed Boolean scalar, whether to consider edge * directions. This argument is ignored if * graph is not a null pointer and it is undirected. * \param scale Whether to rescale the node-level centrality scores to * have a maximum of one. * \param res Pointer to a real variable, the result is stored here. * \return Error code. * * Time complexity: O(1). * * \sa \ref igraph_centralization_closeness() and \ref * igraph_centralization(). */ int igraph_centralization_eigenvector_centrality_tmax( const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_bool_t scale, igraph_real_t *res) { if (graph) { nodes=igraph_vcount(graph); directed=directed && igraph_is_directed(graph); } if (directed) { *res = nodes - 1; } else { if (scale) { *res = nodes - 2; } else { *res = (nodes-2.0) / M_SQRT2; } } return 0; } igraph/src/igraph_stack_pmt.h0000644000176000001440000000340012325527073016036 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include /** * Stack data type. * \ingroup internal */ typedef struct TYPE(igraph_stack) { BASE* stor_begin; BASE* stor_end; BASE* end; } TYPE(igraph_stack); int FUNCTION(igraph_stack,init)(TYPE(igraph_stack)* s, long int size); void FUNCTION(igraph_stack,destroy)(TYPE(igraph_stack)* s); int FUNCTION(igraph_stack,reserve)(TYPE(igraph_stack)* s, long int size); igraph_bool_t FUNCTION(igraph_stack,empty)(TYPE(igraph_stack)* s); long int FUNCTION(igraph_stack,size)(const TYPE(igraph_stack)* s); void FUNCTION(igraph_stack,clear)(TYPE(igraph_stack)* s); int FUNCTION(igraph_stack,push)(TYPE(igraph_stack)* s, BASE elem); BASE FUNCTION(igraph_stack,pop)(TYPE(igraph_stack)* s); BASE FUNCTION(igraph_stack,top)(const TYPE(igraph_stack)* s); int FUNCTION(igraph_stack,print)(const TYPE(igraph_stack)* s); int FUNCTION(igraph_stack,fprint)(const TYPE(igraph_stack)* s, FILE *file); igraph/src/glpnet05.c0000644000176000001440000002600512325527073014156 0ustar ripleyusers/* glpnet05.c (Goldfarb's maximum flow problem generator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * This code is a modified version of the program RMFGEN, a maxflow * problem generator developed by D.Goldfarb and M.Grigoriadis, and * originally implemented by Tamas Badics . * The original code is publically available on the DIMACS ftp site at: * . * * All changes concern only the program interface, so this modified * version produces exactly the same instances as the original version. * * Changes were made by Andrew Makhorin . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" #include "glprng.h" /*********************************************************************** * NAME * * glp_rmfgen - Goldfarb's maximum flow problem generator * * SYNOPSIS * * int glp_rmfgen(glp_graph *G, int *s, int *t, int a_cap, * const int parm[1+5]); * * DESCRIPTION * * The routine glp_rmfgen is a maximum flow problem generator developed * by D.Goldfarb and M.Grigoriadis. * * The parameter G specifies the graph object, to which the generated * problem data have to be stored. Note that on entry the graph object * is erased with the routine glp_erase_graph. * * The pointer s specifies a location, to which the routine stores the * source node number. If s is NULL, the node number is not stored. * * The pointer t specifies a location, to which the routine stores the * sink node number. If t is NULL, the node number is not stored. * * The parameter a_cap specifies an offset of the field of type double * in the arc data block, to which the routine stores the arc capacity. * If a_cap < 0, the capacity is not stored. * * The array parm contains description of the network to be generated: * * parm[0] not used * parm[1] (seed) random number seed (a positive integer) * parm[2] (a) frame size * parm[3] (b) depth * parm[4] (c1) minimal arc capacity * parm[5] (c2) maximal arc capacity * * RETURNS * * If the instance was successfully generated, the routine glp_netgen * returns zero; otherwise, if specified parameters are inconsistent, * the routine returns a non-zero error code. * * COMMENTS * * The generated network is as follows. It has b pieces of frames of * size a * a. (So alltogether the number of vertices is a * a * b) * * In each frame all the vertices are connected with their neighbours * (forth and back). In addition the vertices of a frame are connected * one to one with the vertices of next frame using a random permutation * of those vertices. * * The source is the lower left vertex of the first frame, the sink is * the upper right vertex of the b'th frame. * * t * +-------+ * | .| * | . | * / | / | * +-------+/ -+ b * | | |/. * a | -v- |/ * | | |/ * +-------+ 1 * s a * * The capacities are randomly chosen integers from the range of [c1,c2] * in the case of interconnecting edges, and c2 * a * a for the in-frame * edges. * * REFERENCES * * D.Goldfarb and M.D.Grigoriadis, "A computational comparison of the * Dinic and network simplex methods for maximum flow." Annals of Op. * Res. 13 (1988), pp. 83-123. * * U.Derigs and W.Meier, "Implementing Goldberg's max-flow algorithm: * A computational investigation." Zeitschrift fuer Operations Research * 33 (1989), pp. 383-403. */ typedef struct VERTEX { struct EDGE **edgelist; /* Pointer to the list of pointers to the adjacent edges. (No matter that to or from edges) */ struct EDGE **current; /* Pointer to the current edge */ int degree; /* Number of adjacent edges (both direction) */ int index; } vertex; typedef struct EDGE { int from; int to; int cap; /* Capacity */ } edge; typedef struct NETWORK { struct NETWORK *next, *prev; int vertnum; int edgenum; vertex *verts; /* Vertex array[1..vertnum] */ edge *edges; /* Edge array[1..edgenum] */ int source; /* Pointer to the source */ int sink; /* Pointer to the sink */ } network; struct csa { /* common storage area */ glp_graph *G; int *s, *t, a_cap; RNG *rand; network *N; int *Parr; int A, AA, C2AA, Ec; }; #define G (csa->G) #define s (csa->s) #define t (csa->t) #define a_cap (csa->a_cap) #define N (csa->N) #define Parr (csa->Parr) #define A (csa->A) #define AA (csa->AA) #define C2AA (csa->C2AA) #define Ec (csa->Ec) #undef random #define random(A) (int)(rng_unif_01(csa->rand) * (double)(A)) #define RANDOM(A, B) (int)(random((B) - (A) + 1) + (A)) #define sgn(A) (((A) > 0) ? 1 : ((A) == 0) ? 0 : -1) static void make_edge(struct csa *csa, int from, int to, int c1, int c2) { Ec++; N->edges[Ec].from = from; N->edges[Ec].to = to; N->edges[Ec].cap = RANDOM(c1, c2); return; } static void permute(struct csa *csa) { int i, j, tmp; for (i = 1; i < AA; i++) { j = RANDOM(i, AA); tmp = Parr[i]; Parr[i] = Parr[j]; Parr[j] = tmp; } return; } static void connect(struct csa *csa, int offset, int cv, int x1, int y1) { int cv1; cv1 = offset + (x1 - 1) * A + y1; Ec++; N->edges[Ec].from = cv; N->edges[Ec].to = cv1; N->edges[Ec].cap = C2AA; return; } static network *gen_rmf(struct csa *csa, int a, int b, int c1, int c2) { /* generates a network with a*a*b nodes and 6a*a*b-4ab-2a*a edges random_frame network: Derigs & Meier, Methods & Models of OR (1989), 33:383-403 */ int x, y, z, offset, cv; A = a; AA = a * a; C2AA = c2 * AA; Ec = 0; N = (network *)xmalloc(sizeof(network)); N->vertnum = AA * b; N->edgenum = 5 * AA * b - 4 * A * b - AA; N->edges = (edge *)xcalloc(N->edgenum + 1, sizeof(edge)); N->source = 1; N->sink = N->vertnum; Parr = (int *)xcalloc(AA + 1, sizeof(int)); for (x = 1; x <= AA; x++) Parr[x] = x; for (z = 1; z <= b; z++) { offset = AA * (z - 1); if (z != b) permute(csa); for (x = 1; x <= A; x++) { for (y = 1; y <= A; y++) { cv = offset + (x - 1) * A + y; if (z != b) make_edge(csa, cv, offset + AA + Parr[cv - offset], c1, c2); /* the intermediate edges */ if (y < A) connect(csa, offset, cv, x, y + 1); if (y > 1) connect(csa, offset, cv, x, y - 1); if (x < A) connect(csa, offset, cv, x + 1, y); if (x > 1) connect(csa, offset, cv, x - 1, y); } } } xfree(Parr); return N; } static void print_max_format(struct csa *csa, network *n, char *comm[], int dim) { /* prints a network heading with dim lines of comments (no \n needs at the ends) */ int i, vnum, e_num; edge *e; vnum = n->vertnum; e_num = n->edgenum; if (G == NULL) { for (i = 0; i < dim; i++) xprintf("c %s\n", comm[i]); xprintf("p max %7d %10d\n", vnum, e_num); xprintf("n %7d s\n", n->source); xprintf("n %7d t\n", n->sink); } else { glp_add_vertices(G, vnum); if (s != NULL) *s = n->source; if (t != NULL) *t = n->sink; } for (i = 1; i <= e_num; i++) { e = &n->edges[i]; if (G == NULL) xprintf("a %7d %7d %10d\n", e->from, e->to, (int)e->cap); else { glp_arc *a = glp_add_arc(G, e->from, e->to); if (a_cap >= 0) { double temp = (double)e->cap; memcpy((char *)a->data + a_cap, &temp, sizeof(double)); } } } return; } static void gen_free_net(network *n) { xfree(n->edges); xfree(n); return; } int glp_rmfgen(glp_graph *G_, int *_s, int *_t, int _a_cap, const int parm[1+5]) { struct csa _csa, *csa = &_csa; network *n; char comm[10][80], *com1[10]; int seed, a, b, c1, c2, ret; G = G_; s = _s; t = _t; a_cap = _a_cap; if (G != NULL) { if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_rmfgen: a_cap = %d; invalid offset\n", a_cap); } seed = parm[1]; a = parm[2]; b = parm[3]; c1 = parm[4]; c2 = parm[5]; if (!(seed > 0 && 1 <= a && a <= 1000 && 1 <= b && b <= 1000 && 0 <= c1 && c1 <= c2 && c2 <= 1000)) { ret = 1; goto done; } if (G != NULL) { glp_erase_graph(G, G->v_size, G->a_size); glp_set_graph_name(G, "RMFGEN"); } csa->rand = rng_create_rand(); rng_init_rand(csa->rand, seed); n = gen_rmf(csa, a, b, c1, c2); sprintf(comm[0], "This file was generated by genrmf."); sprintf(comm[1], "The parameters are: a: %d b: %d c1: %d c2: %d", a, b, c1, c2); com1[0] = comm[0]; com1[1] = comm[1]; print_max_format(csa, n, com1, 2); gen_free_net(n); rng_delete_rand(csa->rand); ret = 0; done: return ret; } /**********************************************************************/ #if 0 int main(int argc, char *argv[]) { int seed, a, b, c1, c2, i, parm[1+5]; seed = 123; a = b = c1 = c2 = -1; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-seed") == 0) seed = atoi(argv[++i]); else if (strcmp(argv[i], "-a") == 0) a = atoi(argv[++i]); else if (strcmp(argv[i], "-b") == 0) b = atoi(argv[++i]); else if (strcmp(argv[i], "-c1") == 0) c1 = atoi(argv[++i]); else if (strcmp(argv[i], "-c2") == 0) c2 = atoi(argv[++i]); } if (a < 0 || b < 0 || c1 < 0 || c2 < 0) { xprintf("Usage:\n"); xprintf("genrmf [-seed seed] -a frame_size -b depth\n"); xprintf(" -c1 cap_range1 -c2 cap_range2\n"); } else { parm[1] = seed; parm[2] = a; parm[3] = b; parm[4] = c1; parm[5] = c2; glp_rmfgen(NULL, NULL, NULL, 0, parm); } return 0; } #endif /* eof */ igraph/src/Point.cpp0000755000176000001440000000253312325527072014153 0ustar ripleyusers#include "Point.h" #include namespace igraph { Point::Point() { X(0.0); Y(0.0); Z(0.0); Name(0); } Point::Point(double vX, double vY, double vZ, int vName) { X(vX); Y(vY); Z(vZ); Name(vName); } Point::Point(double vX, double vY, double vZ) { X(vX); Y(vY); Z(vZ); Name(0); } Point::~Point() {} double Point::X() const { return mX; } void Point::X(double vX) { mX = vX; } double Point::Y() const { return mY; } void Point::Y(double vY) { mY = vY; } double Point::Z() const { return mZ; } void Point::Z(double vZ) { mZ = vZ; } int Point::Name() const { return mName; } void Point::Name(int vName) { mName = vName; } double Point::Distance(const Point& rPoint) const { return sqrt( (rPoint.X() - mX)*(rPoint.X() - mX) + (rPoint.Y() - mY)*(rPoint.Y() - mY) + (rPoint.Z() - mZ)*(rPoint.Z() - mZ) ); } bool Point::operator==(const Point& vRhs) const { bool result = true; /* if ( mX + .001 <= vRhs.X() ) result = false; if ( mX - .001 >= vRhs.X() ) result = false; if ( mY + .001 <= vRhs.Y() ) result = false; if ( mY - .001 >= vRhs.Y() ) result = false; if ( mZ + .001 <= vRhs.Z() ) result = false; if ( mZ - .001 >= vRhs.Z() ) result = false; */ if ( mX != vRhs.X() ) result = false; if ( mY != vRhs.Y() ) result = false; if ( mZ != vRhs.Z() ) result = false; return result; } } // namespace igraph igraph/src/feedback_arc_set.c0000644000176000001440000005412712325527073015752 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_centrality.h" #include "igraph_components.h" #include "igraph_constants.h" #include "igraph_datatype.h" #include "igraph_dqueue.h" #include "igraph_error.h" #include "igraph_glpk_support.h" #include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_structural.h" #include "igraph_types.h" #include "igraph_visitor.h" int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights); /** * \ingroup structural * \function igraph_feedback_arc_set * \brief Calculates a feedback arc set of the graph using different * algorithms. * * * A feedback arc set is a set of edges whose removal makes the graph acyclic. * We are usually interested in \em minimum feedback arc sets, i.e. sets of edges * whose total weight is minimal among all the feedback arc sets. * * * For undirected graphs, the problem is simple: one has to find a maximum weight * spanning tree and then remove all the edges not in the spanning tree. For directed * graphs, this is an NP-hard problem, and various heuristics are usually used to * find an approximate solution to the problem. This function implements a few of * these heuristics. * * \param graph The graph object. * \param result An initialized vector, the result will be returned here. * \param weights Weight vector or NULL if no weights are specified. * \param algo The algorithm to use to solve the problem if the graph is directed. * Possible values: * \clist * \cli IGRAPH_FAS_EXACT_IP * Finds a \em minimum feedback arc set using integer programming (IP). * The complexity of this algorithm is exponential of course. * \cli IGRAPH_FAS_APPROX_EADES * Finds a feedback arc set using the heuristic of Eades, Lin and * Smyth (1993). This is guaranteed to be smaller than |E|/2 - |V|/6, * and it is linear in the number of edges (i.e. O(|E|)). * For more details, see Eades P, Lin X and Smyth WF: A fast and effective * heuristic for the feedback arc set problem. In: Proc Inf Process Lett * 319-323, 1993. * \endclist * * \return Error code: * \c IGRAPH_EINVAL if an unknown method was specified or the weight vector * is invalid. * * \example examples/simple/igraph_feedback_arc_set.c * \example examples/simple/igraph_feedback_arc_set_ip.c * * Time complexity: depends on \p algo, see the time complexities there. */ int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights, igraph_fas_algorithm_t algo) { if (weights && igraph_vector_size(weights) < igraph_ecount(graph)) IGRAPH_ERROR("cannot calculate feedback arc set, weight vector too short", IGRAPH_EINVAL); if (!igraph_is_directed(graph)) return igraph_i_feedback_arc_set_undirected(graph, result, weights, 0); switch (algo) { case IGRAPH_FAS_EXACT_IP: return igraph_i_feedback_arc_set_ip(graph, result, weights); case IGRAPH_FAS_APPROX_EADES: return igraph_i_feedback_arc_set_eades(graph, result, weights, 0); default: IGRAPH_ERROR("Invalid algorithm", IGRAPH_EINVAL); } } /** * Solves the feedback arc set problem for undirected graphs. */ int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights, igraph_vector_t *layering) { igraph_vector_t edges; long int i, j, n, no_of_nodes = igraph_vcount(graph); IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_nodes-1); if (weights) { /* Find a maximum weight spanning tree. igraph has a routine for minimum * spanning trees, so we negate the weights */ igraph_vector_t vcopy; IGRAPH_CHECK(igraph_vector_copy(&vcopy, weights)); IGRAPH_FINALLY(igraph_vector_destroy, &vcopy); igraph_vector_scale(&vcopy, -1); IGRAPH_CHECK(igraph_minimum_spanning_tree(graph, &edges, &vcopy)); igraph_vector_destroy(&vcopy); IGRAPH_FINALLY_CLEAN(1); } else { /* Any spanning tree will do */ IGRAPH_CHECK(igraph_minimum_spanning_tree(graph, &edges, 0)); } /* Now we have a bunch of edges that constitute a spanning forest. We have * to come up with a layering, and return those edges that are not in the * spanning forest */ igraph_vector_sort(&edges); IGRAPH_CHECK(igraph_vector_push_back(&edges, -1)); /* guard element */ if (result != 0) { igraph_vector_clear(result); n = igraph_ecount(graph); for (i = 0, j = 0; i < n; i++) { if (i == VECTOR(edges)[j]) { j++; continue; } IGRAPH_CHECK(igraph_vector_push_back(result, i)); } } if (layering != 0) { igraph_vector_t degrees; igraph_vector_t roots; IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes); IGRAPH_CHECK(igraph_strength(graph, °rees, igraph_vss_all(), IGRAPH_ALL, 0, weights)); IGRAPH_CHECK((int) igraph_vector_qsort_ind(°rees, &roots, /* descending = */ 1)); IGRAPH_CHECK(igraph_bfs(graph, /* root = */ 0, /* roots = */ &roots, /* mode = */ IGRAPH_OUT, /* unreachable = */ 0, /* restricted = */ 0, /* order = */ 0, /* rank = */ 0, /* father = */ 0, /* pred = */ 0, /* succ = */ 0, /* dist = */ layering, /* callback = */ 0, /* extra = */ 0)); igraph_vector_destroy(°rees); igraph_vector_destroy(&roots); IGRAPH_FINALLY_CLEAN(2); } igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * Solves the feedback arc set problem using the heuristics of Eades et al. */ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights, igraph_vector_t *layers) { long int i, j, k, v, eid, no_of_nodes=igraph_vcount(graph), nodes_left; igraph_dqueue_t sources, sinks; igraph_vector_t neis; igraph_vector_t indegrees, outdegrees; igraph_vector_t instrengths, outstrengths; long int* ordering; long int order_next_pos = 0, order_next_neg = -1; igraph_real_t diff, maxdiff; ordering = igraph_Calloc(no_of_nodes, long int); IGRAPH_FINALLY(igraph_free, ordering); IGRAPH_VECTOR_INIT_FINALLY(&indegrees, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&outdegrees, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&instrengths, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&outstrengths, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_dqueue_init(&sources, 0)); IGRAPH_FINALLY(igraph_dqueue_destroy, &sources); IGRAPH_CHECK(igraph_dqueue_init(&sinks, 0)); IGRAPH_FINALLY(igraph_dqueue_destroy, &sinks); IGRAPH_CHECK(igraph_degree(graph, &indegrees, igraph_vss_all(), IGRAPH_IN, 0)); IGRAPH_CHECK(igraph_degree(graph, &outdegrees, igraph_vss_all(), IGRAPH_OUT, 0)); if (weights) { IGRAPH_CHECK(igraph_strength(graph, &instrengths, igraph_vss_all(), IGRAPH_IN, 0, weights)); IGRAPH_CHECK(igraph_strength(graph, &outstrengths, igraph_vss_all(), IGRAPH_OUT, 0, weights)); } else { IGRAPH_CHECK(igraph_vector_update(&instrengths, &indegrees)); IGRAPH_CHECK(igraph_vector_update(&outstrengths, &outdegrees)); } /* Find initial sources and sinks */ nodes_left = no_of_nodes; for (i = 0; i < no_of_nodes; i++) { if (VECTOR(indegrees)[i] == 0) { if (VECTOR(outdegrees)[i] == 0) { /* Isolated vertex, we simply ignore it */ nodes_left--; ordering[i] = order_next_pos++; VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; } else { /* This is a source */ igraph_dqueue_push(&sources, i); } } else if (VECTOR(outdegrees)[i] == 0) { /* This is a sink */ igraph_dqueue_push(&sinks, i); } } /* While we have any nodes left... */ while (nodes_left > 0) { /* (1) Remove the sources one by one */ while (!igraph_dqueue_empty(&sources)) { i=(long)igraph_dqueue_pop(&sources); /* Add the node to the ordering */ ordering[i] = order_next_pos++; /* Exclude the node from further searches */ VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; /* Get the neighbors and decrease their degrees */ IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT)); j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { eid = (long int) VECTOR(neis)[i]; k = IGRAPH_TO(graph, eid); if (VECTOR(indegrees)[k] <= 0) { /* Already removed, continue */ continue; } VECTOR(indegrees)[k]--; VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(indegrees)[k] == 0) IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); } nodes_left--; } /* (2) Remove the sinks one by one */ while (!igraph_dqueue_empty(&sinks)) { i=(long)igraph_dqueue_pop(&sinks); /* Maybe the vertex became sink and source at the same time, hence it * was already removed in the previous iteration. Check it. */ if (VECTOR(indegrees)[i] < 0) continue; /* Add the node to the ordering */ ordering[i] = order_next_neg--; /* Exclude the node from further searches */ VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; /* Get the neighbors and decrease their degrees */ IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, IGRAPH_IN)); j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { eid = (long int) VECTOR(neis)[i]; k = IGRAPH_FROM(graph, eid); if (VECTOR(outdegrees)[k] <= 0) { /* Already removed, continue */ continue; } VECTOR(outdegrees)[k]--; VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(outdegrees)[k] == 0) IGRAPH_CHECK(igraph_dqueue_push(&sinks, k)); } nodes_left--; } /* (3) No more sources or sinks. Find the node with the largest * difference between its out-strength and in-strength */ v = -1; maxdiff = -IGRAPH_INFINITY; for (i = 0; i < no_of_nodes; i++) { if (VECTOR(outdegrees)[i] < 0) continue; diff = VECTOR(outstrengths)[i]-VECTOR(instrengths)[i]; if (diff > maxdiff) { maxdiff = diff; v = i; } } if (v >= 0) { /* Remove vertex v */ ordering[v] = order_next_pos++; /* Remove outgoing edges */ IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, IGRAPH_OUT)); j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { eid = (long int) VECTOR(neis)[i]; k = IGRAPH_TO(graph, eid); if (VECTOR(indegrees)[k] <= 0) { /* Already removed, continue */ continue; } VECTOR(indegrees)[k]--; VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(indegrees)[k] == 0) IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); } /* Remove incoming edges */ IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, IGRAPH_IN)); j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { eid = (long int) VECTOR(neis)[i]; k = IGRAPH_FROM(graph, eid); if (VECTOR(outdegrees)[k] <= 0) { /* Already removed, continue */ continue; } VECTOR(outdegrees)[k]--; VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(outdegrees)[k] == 0 && VECTOR(indegrees)[k] > 0) IGRAPH_CHECK(igraph_dqueue_push(&sinks, k)); } VECTOR(outdegrees)[v] = -1; VECTOR(indegrees)[v] = -1; nodes_left--; } } igraph_dqueue_destroy(&sinks); igraph_dqueue_destroy(&sources); igraph_vector_destroy(&neis); igraph_vector_destroy(&outstrengths); igraph_vector_destroy(&instrengths); igraph_vector_destroy(&outdegrees); igraph_vector_destroy(&indegrees); IGRAPH_FINALLY_CLEAN(7); /* Tidy up the ordering */ for (i = 0; i < no_of_nodes; i++) { if (ordering[i] < 0) ordering[i] += no_of_nodes; } /* Find the feedback edges based on the ordering */ if (result != 0) { igraph_vector_clear(result); j = igraph_ecount(graph); for (i = 0; i < j; i++) { long int from = IGRAPH_FROM(graph, i), to = IGRAPH_TO(graph, i); if (from == to || ordering[from] > ordering[to]) IGRAPH_CHECK(igraph_vector_push_back(result, i)); } } /* If we have also requested a layering, return that as well */ if (layers != 0) { igraph_vector_t ranks; igraph_vector_long_t order_vec; IGRAPH_CHECK(igraph_vector_resize(layers, no_of_nodes)); igraph_vector_null(layers); igraph_vector_long_view(&order_vec, ordering, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&ranks, 0); IGRAPH_CHECK((int) igraph_vector_long_qsort_ind(&order_vec, &ranks, 0)); for (i = 0; i < no_of_nodes; i++) { long int from = (long int) VECTOR(ranks)[i]; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) from, IGRAPH_OUT)); k = igraph_vector_size(&neis); for (j = 0; j < k; j++) { long int to = (long int) VECTOR(neis)[j]; if (from == to) continue; if (ordering[from] > ordering[to]) continue; if (VECTOR(*layers)[to] < VECTOR(*layers)[from] + 1) VECTOR(*layers)[to] = VECTOR(*layers)[from] + 1; } } igraph_vector_destroy(&neis); igraph_vector_destroy(&ranks); IGRAPH_FINALLY_CLEAN(2); } /* Free the ordering vector */ igraph_free(ordering); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * Solves the feedback arc set problem using integer programming. */ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights) { #ifndef HAVE_GLPK IGRAPH_ERROR("GLPK is not available", IGRAPH_UNIMPLEMENTED); #else igraph_integer_t no_of_components; igraph_integer_t no_of_vertices = igraph_vcount(graph); igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_vector_t membership, ordering, vertex_remapping; igraph_vector_ptr_t vertices_by_components, edges_by_components; long int i, j, k, l, m, n, from, to; igraph_real_t weight; glp_prob *ip; glp_iocp parm; IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); IGRAPH_VECTOR_INIT_FINALLY(&ordering, 0); IGRAPH_VECTOR_INIT_FINALLY(&vertex_remapping, no_of_vertices); igraph_vector_clear(result); /* Decompose the graph into connected components */ IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components, IGRAPH_WEAK)); /* Construct vertex and edge lists for each of the components */ IGRAPH_CHECK(igraph_vector_ptr_init(&vertices_by_components, no_of_components)); IGRAPH_CHECK(igraph_vector_ptr_init(&edges_by_components, no_of_components)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vertices_by_components); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &edges_by_components); for (i = 0; i < no_of_components; i++) { igraph_vector_t* vptr; vptr = igraph_Calloc(1, igraph_vector_t); if (vptr == 0) IGRAPH_ERROR("cannot calculate feedback arc set using IP", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, vptr); IGRAPH_CHECK(igraph_vector_init(vptr, 0)); IGRAPH_FINALLY_CLEAN(1); VECTOR(vertices_by_components)[i] = vptr; } IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&vertices_by_components, igraph_vector_destroy); for (i = 0; i < no_of_components; i++) { igraph_vector_t* vptr; vptr = igraph_Calloc(1, igraph_vector_t); if (vptr == 0) IGRAPH_ERROR("cannot calculate feedback arc set using IP", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, vptr); IGRAPH_CHECK(igraph_vector_init(vptr, 0)); IGRAPH_FINALLY_CLEAN(1); VECTOR(edges_by_components)[i] = vptr; } IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&edges_by_components, igraph_vector_destroy); for (i = 0; i < no_of_vertices; i++) { j = (long int) VECTOR(membership)[i]; IGRAPH_CHECK(igraph_vector_push_back(VECTOR(vertices_by_components)[j], i)); } for (i = 0; i < no_of_edges; i++) { j = (long int) VECTOR(membership)[(long)IGRAPH_FROM(graph, i)]; IGRAPH_CHECK(igraph_vector_push_back(VECTOR(edges_by_components)[j], i)); } #define VAR2IDX(i, j) (i*(n-1)+j-(i+1)*i/2) /* Configure GLPK */ glp_term_out(GLP_OFF); glp_init_iocp(&parm); parm.br_tech = GLP_BR_DTH; parm.bt_tech = GLP_BT_BLB; parm.pp_tech = GLP_PP_ALL; parm.presolve = GLP_ON; parm.binarize = GLP_OFF; parm.cb_func = igraph_i_glpk_interruption_hook; /* Solve an IP for feedback arc sets in each of the components */ for (i = 0; i < no_of_components; i++) { igraph_vector_t* vertices_in_comp = (igraph_vector_t*)VECTOR(vertices_by_components)[i]; igraph_vector_t* edges_in_comp = (igraph_vector_t*)VECTOR(edges_by_components)[i]; /* * Let x_ij denote whether layer(i) < layer(j). * * The standard formulation of the problem is as follows: * * max sum_{i,j} w_ij x_ij * * subject to * * (1) x_ij + x_ji = 1 (i.e. either layer(i) < layer(j) or layer(i) > layer(j)) * for all i < j * (2) x_ij + x_jk + x_ki <= 2 for all i < j, i < k, j != k * * Note that x_ij = 1 implies that x_ji = 0 and vice versa; in other words, * x_ij = 1 - x_ji. Thus, we can get rid of the (1) constraints and half of the * x_ij variables (where j < i) if we rewrite constraints of type (2) as follows: * * (2a) x_ij + x_jk - x_ik <= 1 for all i < j, i < k, j < k * (2b) x_ij - x_kj - x_ik <= 0 for all i < j, i < k, j > k * * The goal function then becomes: * * max sum_{i 0) { /* j comes earlier in the ordering than k */ VECTOR(ordering)[j]++; } else { /* k comes earlier in the ordering than j */ VECTOR(ordering)[k]++; } k++; if (k == n) { j++; k = j+1; } } /* Find the feedback edges */ k = igraph_vector_size(edges_in_comp); for (j = 0; j < k; j++) { l = (long int) VECTOR(*edges_in_comp)[j]; from = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_FROM(graph, l)]; to = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_TO(graph, l)]; if (from == to || VECTOR(ordering)[from] < VECTOR(ordering)[to]) IGRAPH_CHECK(igraph_vector_push_back(result, l)); } /* Clean up */ glp_delete_prob(ip); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_ptr_destroy_all(&vertices_by_components); igraph_vector_ptr_destroy_all(&edges_by_components); igraph_vector_destroy(&vertex_remapping); igraph_vector_destroy(&ordering); igraph_vector_destroy(&membership); IGRAPH_FINALLY_CLEAN(5); return IGRAPH_SUCCESS; #endif } igraph/src/sir.c0000644000176000001440000002010212325527074013306 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2014 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_epidemics.h" #include "igraph_random.h" #include "igraph_adjlist.h" #include "igraph_interface.h" #include "igraph_psumtree.h" #include "igraph_memory.h" #include "igraph_structural.h" int igraph_sir_init(igraph_sir_t *sir) { igraph_vector_init(&sir->times, 1); IGRAPH_FINALLY(igraph_vector_destroy, &sir->times); igraph_vector_int_init(&sir->no_s, 1); IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_s); igraph_vector_int_init(&sir->no_i, 1); IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_i); igraph_vector_int_init(&sir->no_r, 1); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \function igraph_sir_destroy * Deallocate memory associated with a SIR simulation run * * \param sir The \ref igraph_sir_t object storing the simulation. */ void igraph_sir_destroy(igraph_sir_t *sir) { igraph_vector_destroy(&sir->times); igraph_vector_int_destroy(&sir->no_s); igraph_vector_int_destroy(&sir->no_i); igraph_vector_int_destroy(&sir->no_r); } void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { int i, n=igraph_vector_ptr_size(v); for (i=0; i * This function runs multiple simulations, all starting with a * single uniformly randomly chosen infected individual. * * \param graph The graph to perform the model on. For directed graphs * edge directions are ignored and a warning is given. * \param beta The rate of infection of an individual that is * susceptible and has a single infected neighbor. * The infection rate of a susceptible individual with n * infected neighbors is n times beta. Formally * this is the rate parameter of an exponential distribution. * \param gamma The rate of recovery of an infected individual. * Formally, this is the rate parameter of an exponential * distribution. * \param no_sim The number of simulation runs to perform. * \param result The result of the simulation is stored here, * in a list of \ref igraph_sir_t objects. To deallocate * memory, the user needs to call \ref igraph_sir_destroy on * each element, before destroying the pointer vector itself. * \return Error code. * * Time complexity: O(no_sim * (|V| + |E| log(|V|))). */ int igraph_sir(const igraph_t *graph, igraph_real_t beta, igraph_real_t gamma, igraph_integer_t no_sim, igraph_vector_ptr_t *result) { int infected; igraph_vector_int_t status; igraph_adjlist_t adjlist; int no_of_nodes=igraph_vcount(graph); int i, j, ns, ni, nr; igraph_vector_int_t *neis; igraph_psumtree_t tree; igraph_real_t psum; int neilen; igraph_bool_t simple; if (no_of_nodes==0) { IGRAPH_ERROR("Cannot run SIR model on empty graph", IGRAPH_EINVAL); } if (igraph_is_directed(graph)) { IGRAPH_WARNING("Edge directions are ignored in SIR model"); } if (beta < 0) { IGRAPH_ERROR("Beta must be non-negative in SIR model", IGRAPH_EINVAL); } if (gamma < 0) { IGRAPH_ERROR("Gamma must be non-negative in SIR model", IGRAPH_EINVAL); } if (no_sim <= 0) { IGRAPH_ERROR("Number of SIR simulations must be positive", IGRAPH_EINVAL); } igraph_is_simple(graph, &simple); if (!simple) { IGRAPH_ERROR("SIR model only works with simple graphs", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_int_init(&status, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_int_destroy, &status); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_psumtree_init(&tree, no_of_nodes)); IGRAPH_FINALLY(igraph_psumtree_destroy, &tree); IGRAPH_CHECK(igraph_vector_ptr_resize(result, no_sim)); igraph_vector_ptr_null(result); IGRAPH_FINALLY(igraph_i_sir_destroy, result); for (i=0; itimes; igraph_vector_int_t *no_s_v = &sir->no_s; igraph_vector_int_t *no_i_v = &sir->no_i; igraph_vector_int_t *no_r_v = &sir->no_r; infected = RNG_INTEGER(0, no_of_nodes-1); /* Initially infected */ igraph_vector_int_null(&status); VECTOR(status)[infected] = S_I; ns = no_of_nodes - 1; ni = 1; nr = 0; VECTOR(*times_v)[0] = 0.0; VECTOR(*no_s_v)[0] = ns; VECTOR(*no_i_v)[0] = ni; VECTOR(*no_r_v)[0] = nr; if (igraph_psumtree_sum(&tree) != 0) { IGRAPH_ERROR("Internal SIR error", IGRAPH_EINTERNAL); } /* Rates */ igraph_psumtree_update(&tree, infected, gamma); neis=igraph_adjlist_get(&adjlist, infected); neilen=igraph_vector_int_size(neis); for (i=0; i 0) { igraph_real_t tt=igraph_rng_get_exp(igraph_rng_default(), psum); igraph_real_t r=RNG_UNIF(0, psum); long int vchange; igraph_psumtree_search(&tree, &vchange, r); neis=igraph_adjlist_get(&adjlist, vchange); neilen=igraph_vector_int_size(neis); if (VECTOR(status)[vchange] == S_I) { VECTOR(status)[vchange] = S_R; ni--; nr++; psum -= igraph_psumtree_get(&tree, vchange); igraph_psumtree_update(&tree, vchange, 0.0); for (i=0; i 0 */ } /* j < no_sim */ RNG_END(); igraph_psumtree_destroy(&tree); igraph_adjlist_destroy(&adjlist); igraph_vector_int_destroy(&status); IGRAPH_FINALLY_CLEAN(4); /* + result */ return 0; } igraph/src/glpios01.c0000644000176000001440000015050512325527073014161 0ustar ripleyusers/* glpios01.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wself-assign" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpios.h" /*********************************************************************** * NAME * * ios_create_tree - create branch-and-bound tree * * SYNOPSIS * * #include "glpios.h" * glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm); * * DESCRIPTION * * The routine ios_create_tree creates the branch-and-bound tree. * * Being created the tree consists of the only root subproblem whose * reference number is 1. Note that initially the root subproblem is in * frozen state and therefore needs to be revived. * * RETURNS * * The routine returns a pointer to the tree created. */ static IOSNPD *new_node(glp_tree *tree, IOSNPD *parent); glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm) { int m = mip->m; int n = mip->n; glp_tree *tree; int i, j; xassert(mip->tree == NULL); mip->tree = tree = xmalloc(sizeof(glp_tree)); tree->pool = dmp_create_pool(); tree->n = n; /* save original problem components */ tree->orig_m = m; tree->orig_type = xcalloc(1+m+n, sizeof(char)); tree->orig_lb = xcalloc(1+m+n, sizeof(double)); tree->orig_ub = xcalloc(1+m+n, sizeof(double)); tree->orig_stat = xcalloc(1+m+n, sizeof(char)); tree->orig_prim = xcalloc(1+m+n, sizeof(double)); tree->orig_dual = xcalloc(1+m+n, sizeof(double)); for (i = 1; i <= m; i++) { GLPROW *row = mip->row[i]; tree->orig_type[i] = (char)row->type; tree->orig_lb[i] = row->lb; tree->orig_ub[i] = row->ub; tree->orig_stat[i] = (char)row->stat; tree->orig_prim[i] = row->prim; tree->orig_dual[i] = row->dual; } for (j = 1; j <= n; j++) { GLPCOL *col = mip->col[j]; tree->orig_type[m+j] = (char)col->type; tree->orig_lb[m+j] = col->lb; tree->orig_ub[m+j] = col->ub; tree->orig_stat[m+j] = (char)col->stat; tree->orig_prim[m+j] = col->prim; tree->orig_dual[m+j] = col->dual; } tree->orig_obj = mip->obj_val; /* initialize the branch-and-bound tree */ tree->nslots = 0; tree->avail = 0; tree->slot = NULL; tree->head = tree->tail = NULL; tree->a_cnt = tree->n_cnt = tree->t_cnt = 0; /* the root subproblem is not solved yet, so its final components are unknown so far */ tree->root_m = 0; tree->root_type = NULL; tree->root_lb = tree->root_ub = NULL; tree->root_stat = NULL; /* the current subproblem does not exist yet */ tree->curr = NULL; tree->mip = mip; /*tree->solved = 0;*/ tree->non_int = xcalloc(1+n, sizeof(char)); memset(&tree->non_int[1], 0, n); /* arrays to save parent subproblem components will be allocated later */ tree->pred_m = tree->pred_max = 0; tree->pred_type = NULL; tree->pred_lb = tree->pred_ub = NULL; tree->pred_stat = NULL; /* cut generator */ tree->local = ios_create_pool(tree); /*tree->first_attempt = 1;*/ /*tree->max_added_cuts = 0;*/ /*tree->min_eff = 0.0;*/ /*tree->miss = 0;*/ /*tree->just_selected = 0;*/ tree->mir_gen = NULL; tree->clq_gen = NULL; /*tree->round = 0;*/ #if 0 /* create the conflict graph */ tree->n_ref = xcalloc(1+n, sizeof(int)); memset(&tree->n_ref[1], 0, n * sizeof(int)); tree->c_ref = xcalloc(1+n, sizeof(int)); memset(&tree->c_ref[1], 0, n * sizeof(int)); tree->g = scg_create_graph(0); tree->j_ref = xcalloc(1+tree->g->n_max, sizeof(int)); #endif /* pseudocost branching */ tree->pcost = NULL; tree->iwrk = xcalloc(1+n, sizeof(int)); tree->dwrk = xcalloc(1+n, sizeof(double)); /* initialize control parameters */ tree->parm = parm; tree->tm_beg = xtime(); tree->tm_lag = xlset(0); tree->sol_cnt = 0; /* initialize advanced solver interface */ tree->reason = 0; tree->reopt = 0; tree->reinv = 0; tree->br_var = 0; tree->br_sel = 0; tree->child = 0; tree->next_p = 0; /*tree->btrack = NULL;*/ tree->stop = 0; /* create the root subproblem, which initially is identical to the original MIP */ new_node(tree, NULL); return tree; } /*********************************************************************** * NAME * * ios_revive_node - revive specified subproblem * * SYNOPSIS * * #include "glpios.h" * void ios_revive_node(glp_tree *tree, int p); * * DESCRIPTION * * The routine ios_revive_node revives the specified subproblem, whose * reference number is p, and thereby makes it the current subproblem. * Note that the specified subproblem must be active. Besides, if the * current subproblem already exists, it must be frozen before reviving * another subproblem. */ void ios_revive_node(glp_tree *tree, int p) { glp_prob *mip = tree->mip; IOSNPD *node, *root; /* obtain pointer to the specified subproblem */ xassert(1 <= p && p <= tree->nslots); node = tree->slot[p].node; xassert(node != NULL); /* the specified subproblem must be active */ xassert(node->count == 0); /* the current subproblem must not exist */ xassert(tree->curr == NULL); /* the specified subproblem becomes current */ tree->curr = node; /*tree->solved = 0;*/ /* obtain pointer to the root subproblem */ root = tree->slot[1].node; xassert(root != NULL); /* at this point problem object components correspond to the root subproblem, so if the root subproblem should be revived, there is nothing more to do */ if (node == root) goto done; xassert(mip->m == tree->root_m); /* build path from the root to the current node */ node->temp = NULL; for (node = node; node != NULL; node = node->up) { if (node->up == NULL) xassert(node == root); else node->up->temp = node; } /* go down from the root to the current node and make necessary changes to restore components of the current subproblem */ for (node = root; node != NULL; node = node->temp) { int m = mip->m; int n = mip->n; /* if the current node is reached, the problem object at this point corresponds to its parent, so save attributes of rows and columns for the parent subproblem */ if (node->temp == NULL) { int i, j; tree->pred_m = m; /* allocate/reallocate arrays, if necessary */ if (tree->pred_max < m + n) { int new_size = m + n + 100; if (tree->pred_type != NULL) xfree(tree->pred_type); if (tree->pred_lb != NULL) xfree(tree->pred_lb); if (tree->pred_ub != NULL) xfree(tree->pred_ub); if (tree->pred_stat != NULL) xfree(tree->pred_stat); tree->pred_max = new_size; tree->pred_type = xcalloc(1+new_size, sizeof(char)); tree->pred_lb = xcalloc(1+new_size, sizeof(double)); tree->pred_ub = xcalloc(1+new_size, sizeof(double)); tree->pred_stat = xcalloc(1+new_size, sizeof(char)); } /* save row attributes */ for (i = 1; i <= m; i++) { GLPROW *row = mip->row[i]; tree->pred_type[i] = (char)row->type; tree->pred_lb[i] = row->lb; tree->pred_ub[i] = row->ub; tree->pred_stat[i] = (char)row->stat; } /* save column attributes */ for (j = 1; j <= n; j++) { GLPCOL *col = mip->col[j]; tree->pred_type[mip->m+j] = (char)col->type; tree->pred_lb[mip->m+j] = col->lb; tree->pred_ub[mip->m+j] = col->ub; tree->pred_stat[mip->m+j] = (char)col->stat; } } /* change bounds of rows and columns */ { IOSBND *b; for (b = node->b_ptr; b != NULL; b = b->next) { if (b->k <= m) glp_set_row_bnds(mip, b->k, b->type, b->lb, b->ub); else glp_set_col_bnds(mip, b->k-m, b->type, b->lb, b->ub); } } /* change statuses of rows and columns */ { IOSTAT *s; for (s = node->s_ptr; s != NULL; s = s->next) { if (s->k <= m) glp_set_row_stat(mip, s->k, s->stat); else glp_set_col_stat(mip, s->k-m, s->stat); } } /* add new rows */ if (node->r_ptr != NULL) { IOSROW *r; IOSAIJ *a; int i, len, *ind; double *val; ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); for (r = node->r_ptr; r != NULL; r = r->next) { i = glp_add_rows(mip, 1); glp_set_row_name(mip, i, r->name); #if 1 /* 20/IX-2008 */ xassert(mip->row[i]->level == 0); mip->row[i]->level = node->level; mip->row[i]->origin = r->origin; mip->row[i]->klass = r->klass; #endif glp_set_row_bnds(mip, i, r->type, r->lb, r->ub); len = 0; for (a = r->ptr; a != NULL; a = a->next) len++, ind[len] = a->j, val[len] = a->val; glp_set_mat_row(mip, i, len, ind, val); glp_set_rii(mip, i, r->rii); glp_set_row_stat(mip, i, r->stat); } xfree(ind); xfree(val); } #if 0 /* add new edges to the conflict graph */ /* add new cliques to the conflict graph */ /* (not implemented yet) */ xassert(node->own_nn == 0); xassert(node->own_nc == 0); xassert(node->e_ptr == NULL); #endif } /* the specified subproblem has been revived */ node = tree->curr; /* delete its bound change list */ while (node->b_ptr != NULL) { IOSBND *b; b = node->b_ptr; node->b_ptr = b->next; dmp_free_atom(tree->pool, b, sizeof(IOSBND)); } /* delete its status change list */ while (node->s_ptr != NULL) { IOSTAT *s; s = node->s_ptr; node->s_ptr = s->next; dmp_free_atom(tree->pool, s, sizeof(IOSTAT)); } #if 1 /* 20/XI-2009 */ /* delete its row addition list (additional rows may appear, for example, due to branching on GUB constraints */ while (node->r_ptr != NULL) { IOSROW *r; r = node->r_ptr; node->r_ptr = r->next; xassert(r->name == NULL); while (r->ptr != NULL) { IOSAIJ *a; a = r->ptr; r->ptr = a->next; dmp_free_atom(tree->pool, a, sizeof(IOSAIJ)); } dmp_free_atom(tree->pool, r, sizeof(IOSROW)); } #endif done: return; } /*********************************************************************** * NAME * * ios_freeze_node - freeze current subproblem * * SYNOPSIS * * #include "glpios.h" * void ios_freeze_node(glp_tree *tree); * * DESCRIPTION * * The routine ios_freeze_node freezes the current subproblem. */ void ios_freeze_node(glp_tree *tree) { glp_prob *mip = tree->mip; int m = mip->m; int n = mip->n; IOSNPD *node; /* obtain pointer to the current subproblem */ node = tree->curr; xassert(node != NULL); if (node->up == NULL) { /* freeze the root subproblem */ int k; xassert(node->p == 1); xassert(tree->root_m == 0); xassert(tree->root_type == NULL); xassert(tree->root_lb == NULL); xassert(tree->root_ub == NULL); xassert(tree->root_stat == NULL); tree->root_m = m; tree->root_type = xcalloc(1+m+n, sizeof(char)); tree->root_lb = xcalloc(1+m+n, sizeof(double)); tree->root_ub = xcalloc(1+m+n, sizeof(double)); tree->root_stat = xcalloc(1+m+n, sizeof(char)); for (k = 1; k <= m+n; k++) { if (k <= m) { GLPROW *row = mip->row[k]; tree->root_type[k] = (char)row->type; tree->root_lb[k] = row->lb; tree->root_ub[k] = row->ub; tree->root_stat[k] = (char)row->stat; } else { GLPCOL *col = mip->col[k-m]; tree->root_type[k] = (char)col->type; tree->root_lb[k] = col->lb; tree->root_ub[k] = col->ub; tree->root_stat[k] = (char)col->stat; } } } else { /* freeze non-root subproblem */ int root_m = tree->root_m; int pred_m = tree->pred_m; int i, j, k; xassert(pred_m <= m); /* build change lists for rows and columns which exist in the parent subproblem */ xassert(node->b_ptr == NULL); xassert(node->s_ptr == NULL); for (k = 1; k <= pred_m + n; k++) { int pred_type, pred_stat, type, stat; double pred_lb, pred_ub, lb, ub; /* determine attributes in the parent subproblem */ pred_type = tree->pred_type[k]; pred_lb = tree->pred_lb[k]; pred_ub = tree->pred_ub[k]; pred_stat = tree->pred_stat[k]; /* determine attributes in the current subproblem */ if (k <= pred_m) { GLPROW *row = mip->row[k]; type = row->type; lb = row->lb; ub = row->ub; stat = row->stat; } else { GLPCOL *col = mip->col[k - pred_m]; type = col->type; lb = col->lb; ub = col->ub; stat = col->stat; } /* save type and bounds of a row/column, if changed */ if (!(pred_type == type && pred_lb == lb && pred_ub == ub)) { IOSBND *b; b = dmp_get_atom(tree->pool, sizeof(IOSBND)); b->k = k; b->type = (unsigned char)type; b->lb = lb; b->ub = ub; b->next = node->b_ptr; node->b_ptr = b; } /* save status of a row/column, if changed */ if (pred_stat != stat) { IOSTAT *s; s = dmp_get_atom(tree->pool, sizeof(IOSTAT)); s->k = k; s->stat = (unsigned char)stat; s->next = node->s_ptr; node->s_ptr = s; } } /* save new rows added to the current subproblem */ xassert(node->r_ptr == NULL); if (pred_m < m) { int i, len, *ind; double *val; ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); for (i = m; i > pred_m; i--) { GLPROW *row = mip->row[i]; IOSROW *r; const char *name; r = dmp_get_atom(tree->pool, sizeof(IOSROW)); name = glp_get_row_name(mip, i); if (name == NULL) r->name = NULL; else { r->name = dmp_get_atom(tree->pool, strlen(name)+1); strcpy(r->name, name); } #if 1 /* 20/IX-2008 */ r->origin = row->origin; r->klass = row->klass; #endif r->type = (unsigned char)row->type; r->lb = row->lb; r->ub = row->ub; r->ptr = NULL; len = glp_get_mat_row(mip, i, ind, val); for (k = 1; k <= len; k++) { IOSAIJ *a; a = dmp_get_atom(tree->pool, sizeof(IOSAIJ)); a->j = ind[k]; a->val = val[k]; a->next = r->ptr; r->ptr = a; } r->rii = row->rii; r->stat = (unsigned char)row->stat; r->next = node->r_ptr; node->r_ptr = r; } xfree(ind); xfree(val); } /* remove all rows missing in the root subproblem */ if (m != root_m) { int nrs, *num; nrs = m - root_m; xassert(nrs > 0); num = xcalloc(1+nrs, sizeof(int)); for (i = 1; i <= nrs; i++) num[i] = root_m + i; glp_del_rows(mip, nrs, num); xfree(num); } m = mip->m; /* and restore attributes of all rows and columns for the root subproblem */ xassert(m == root_m); for (i = 1; i <= m; i++) { glp_set_row_bnds(mip, i, tree->root_type[i], tree->root_lb[i], tree->root_ub[i]); glp_set_row_stat(mip, i, tree->root_stat[i]); } for (j = 1; j <= n; j++) { glp_set_col_bnds(mip, j, tree->root_type[m+j], tree->root_lb[m+j], tree->root_ub[m+j]); glp_set_col_stat(mip, j, tree->root_stat[m+j]); } #if 1 /* remove all edges and cliques missing in the conflict graph for the root subproblem */ /* (not implemented yet) */ #endif } /* the current subproblem has been frozen */ tree->curr = NULL; return; } /*********************************************************************** * NAME * * ios_clone_node - clone specified subproblem * * SYNOPSIS * * #include "glpios.h" * void ios_clone_node(glp_tree *tree, int p, int nnn, int ref[]); * * DESCRIPTION * * The routine ios_clone_node clones the specified subproblem, whose * reference number is p, creating its nnn exact copies. Note that the * specified subproblem must be active and must be in the frozen state * (i.e. it must not be the current subproblem). * * Each clone, an exact copy of the specified subproblem, becomes a new * active subproblem added to the end of the active list. After cloning * the specified subproblem becomes inactive. * * The reference numbers of clone subproblems are stored to locations * ref[1], ..., ref[nnn]. */ static int get_slot(glp_tree *tree) { int p; /* if no free slots are available, increase the room */ if (tree->avail == 0) { int nslots = tree->nslots; IOSLOT *save = tree->slot; if (nslots == 0) tree->nslots = 20; else { tree->nslots = nslots + nslots; xassert(tree->nslots > nslots); } tree->slot = xcalloc(1+tree->nslots, sizeof(IOSLOT)); if (save != NULL) { memcpy(&tree->slot[1], &save[1], nslots * sizeof(IOSLOT)); xfree(save); } /* push more free slots into the stack */ for (p = tree->nslots; p > nslots; p--) { tree->slot[p].node = NULL; tree->slot[p].next = tree->avail; tree->avail = p; } } /* pull a free slot from the stack */ p = tree->avail; tree->avail = tree->slot[p].next; xassert(tree->slot[p].node == NULL); tree->slot[p].next = 0; return p; } static IOSNPD *new_node(glp_tree *tree, IOSNPD *parent) { IOSNPD *node; int p; /* pull a free slot for the new node */ p = get_slot(tree); /* create descriptor of the new subproblem */ node = dmp_get_atom(tree->pool, sizeof(IOSNPD)); tree->slot[p].node = node; node->p = p; node->up = parent; node->level = (parent == NULL ? 0 : parent->level + 1); node->count = 0; node->b_ptr = NULL; node->s_ptr = NULL; node->r_ptr = NULL; node->solved = 0; #if 0 node->own_nn = node->own_nc = 0; node->e_ptr = NULL; #endif #if 1 /* 04/X-2008 */ node->lp_obj = (parent == NULL ? (tree->mip->dir == GLP_MIN ? -DBL_MAX : +DBL_MAX) : parent->lp_obj); #endif node->bound = (parent == NULL ? (tree->mip->dir == GLP_MIN ? -DBL_MAX : +DBL_MAX) : parent->bound); node->br_var = 0; node->br_val = 0.0; node->ii_cnt = 0; node->ii_sum = 0.0; #if 1 /* 30/XI-2009 */ node->changed = 0; #endif if (tree->parm->cb_size == 0) node->data = NULL; else { node->data = dmp_get_atom(tree->pool, tree->parm->cb_size); memset(node->data, 0, tree->parm->cb_size); } node->temp = NULL; node->prev = tree->tail; node->next = NULL; /* add the new subproblem to the end of the active list */ if (tree->head == NULL) tree->head = node; else tree->tail->next = node; tree->tail = node; tree->a_cnt++; tree->n_cnt++; tree->t_cnt++; /* increase the number of child subproblems */ if (parent == NULL) xassert(p == 1); else parent->count++; return node; } void ios_clone_node(glp_tree *tree, int p, int nnn, int ref[]) { IOSNPD *node; int k; /* obtain pointer to the subproblem to be cloned */ xassert(1 <= p && p <= tree->nslots); node = tree->slot[p].node; xassert(node != NULL); /* the specified subproblem must be active */ xassert(node->count == 0); /* and must be in the frozen state */ xassert(tree->curr != node); /* remove the specified subproblem from the active list, because it becomes inactive */ if (node->prev == NULL) tree->head = node->next; else node->prev->next = node->next; if (node->next == NULL) tree->tail = node->prev; else node->next->prev = node->prev; node->prev = node->next = NULL; tree->a_cnt--; /* create clone subproblems */ xassert(nnn > 0); for (k = 1; k <= nnn; k++) ref[k] = new_node(tree, node)->p; return; } /*********************************************************************** * NAME * * ios_delete_node - delete specified subproblem * * SYNOPSIS * * #include "glpios.h" * void ios_delete_node(glp_tree *tree, int p); * * DESCRIPTION * * The routine ios_delete_node deletes the specified subproblem, whose * reference number is p. The subproblem must be active and must be in * the frozen state (i.e. it must not be the current subproblem). * * Note that deletion is performed recursively, i.e. if a subproblem to * be deleted is the only child of its parent, the parent subproblem is * also deleted, etc. */ void ios_delete_node(glp_tree *tree, int p) { IOSNPD *node, *temp; /* obtain pointer to the subproblem to be deleted */ xassert(1 <= p && p <= tree->nslots); node = tree->slot[p].node; xassert(node != NULL); /* the specified subproblem must be active */ xassert(node->count == 0); /* and must be in the frozen state */ xassert(tree->curr != node); /* remove the specified subproblem from the active list, because it is gone from the tree */ if (node->prev == NULL) tree->head = node->next; else node->prev->next = node->next; if (node->next == NULL) tree->tail = node->prev; else node->next->prev = node->prev; node->prev = node->next = NULL; tree->a_cnt--; loop: /* recursive deletion starts here */ /* delete the bound change list */ { IOSBND *b; while (node->b_ptr != NULL) { b = node->b_ptr; node->b_ptr = b->next; dmp_free_atom(tree->pool, b, sizeof(IOSBND)); } } /* delete the status change list */ { IOSTAT *s; while (node->s_ptr != NULL) { s = node->s_ptr; node->s_ptr = s->next; dmp_free_atom(tree->pool, s, sizeof(IOSTAT)); } } /* delete the row addition list */ while (node->r_ptr != NULL) { IOSROW *r; r = node->r_ptr; if (r->name != NULL) dmp_free_atom(tree->pool, r->name, strlen(r->name)+1); while (r->ptr != NULL) { IOSAIJ *a; a = r->ptr; r->ptr = a->next; dmp_free_atom(tree->pool, a, sizeof(IOSAIJ)); } node->r_ptr = r->next; dmp_free_atom(tree->pool, r, sizeof(IOSROW)); } #if 0 /* delete the edge addition list */ /* delete the clique addition list */ /* (not implemented yet) */ xassert(node->own_nn == 0); xassert(node->own_nc == 0); xassert(node->e_ptr == NULL); #endif /* free application-specific data */ if (tree->parm->cb_size == 0) xassert(node->data == NULL); else dmp_free_atom(tree->pool, node->data, tree->parm->cb_size); /* free the corresponding node slot */ p = node->p; xassert(tree->slot[p].node == node); tree->slot[p].node = NULL; tree->slot[p].next = tree->avail; tree->avail = p; /* save pointer to the parent subproblem */ temp = node->up; /* delete the subproblem descriptor */ dmp_free_atom(tree->pool, node, sizeof(IOSNPD)); tree->n_cnt--; /* take pointer to the parent subproblem */ node = temp; if (node != NULL) { /* the parent subproblem exists; decrease the number of its child subproblems */ xassert(node->count > 0); node->count--; /* if now the parent subproblem has no childs, it also must be deleted */ if (node->count == 0) goto loop; } return; } /*********************************************************************** * NAME * * ios_delete_tree - delete branch-and-bound tree * * SYNOPSIS * * #include "glpios.h" * void ios_delete_tree(glp_tree *tree); * * DESCRIPTION * * The routine ios_delete_tree deletes the branch-and-bound tree, which * the parameter tree points to, and frees all the memory allocated to * this program object. * * On exit components of the problem object are restored to correspond * to the original MIP passed to the routine ios_create_tree. */ void ios_delete_tree(glp_tree *tree) { glp_prob *mip = tree->mip; int i, j; int m = mip->m; int n = mip->n; xassert(mip->tree == tree); /* remove all additional rows */ if (m != tree->orig_m) { int nrs, *num; nrs = m - tree->orig_m; xassert(nrs > 0); num = xcalloc(1+nrs, sizeof(int)); for (i = 1; i <= nrs; i++) num[i] = tree->orig_m + i; glp_del_rows(mip, nrs, num); xfree(num); } m = tree->orig_m; /* restore original attributes of rows and columns */ xassert(m == tree->orig_m); xassert(n == tree->n); for (i = 1; i <= m; i++) { glp_set_row_bnds(mip, i, tree->orig_type[i], tree->orig_lb[i], tree->orig_ub[i]); glp_set_row_stat(mip, i, tree->orig_stat[i]); mip->row[i]->prim = tree->orig_prim[i]; mip->row[i]->dual = tree->orig_dual[i]; } for (j = 1; j <= n; j++) { glp_set_col_bnds(mip, j, tree->orig_type[m+j], tree->orig_lb[m+j], tree->orig_ub[m+j]); glp_set_col_stat(mip, j, tree->orig_stat[m+j]); mip->col[j]->prim = tree->orig_prim[m+j]; mip->col[j]->dual = tree->orig_dual[m+j]; } mip->pbs_stat = mip->dbs_stat = GLP_FEAS; mip->obj_val = tree->orig_obj; /* delete the branch-and-bound tree */ xassert(tree->local != NULL); ios_delete_pool(tree, tree->local); dmp_delete_pool(tree->pool); xfree(tree->orig_type); xfree(tree->orig_lb); xfree(tree->orig_ub); xfree(tree->orig_stat); xfree(tree->orig_prim); xfree(tree->orig_dual); xfree(tree->slot); if (tree->root_type != NULL) xfree(tree->root_type); if (tree->root_lb != NULL) xfree(tree->root_lb); if (tree->root_ub != NULL) xfree(tree->root_ub); if (tree->root_stat != NULL) xfree(tree->root_stat); xfree(tree->non_int); #if 0 xfree(tree->n_ref); xfree(tree->c_ref); xfree(tree->j_ref); #endif if (tree->pcost != NULL) ios_pcost_free(tree); xfree(tree->iwrk); xfree(tree->dwrk); #if 0 scg_delete_graph(tree->g); #endif if (tree->pred_type != NULL) xfree(tree->pred_type); if (tree->pred_lb != NULL) xfree(tree->pred_lb); if (tree->pred_ub != NULL) xfree(tree->pred_ub); if (tree->pred_stat != NULL) xfree(tree->pred_stat); #if 0 xassert(tree->cut_gen == NULL); #endif xassert(tree->mir_gen == NULL); xassert(tree->clq_gen == NULL); xfree(tree); mip->tree = NULL; return; } /*********************************************************************** * NAME * * ios_eval_degrad - estimate obj. degrad. for down- and up-branches * * SYNOPSIS * * #include "glpios.h" * void ios_eval_degrad(glp_tree *tree, int j, double *dn, double *up); * * DESCRIPTION * * Given optimal basis to LP relaxation of the current subproblem the * routine ios_eval_degrad performs the dual ratio test to compute the * objective values in the adjacent basis for down- and up-branches, * which are stored in locations *dn and *up, assuming that x[j] is a * variable chosen to branch upon. */ void ios_eval_degrad(glp_tree *tree, int j, double *dn, double *up) { glp_prob *mip = tree->mip; int m = mip->m, n = mip->n; int len, kase, k, t, stat; double alfa, beta, gamma, delta, dz; int *ind = tree->iwrk; double *val = tree->dwrk; /* current basis must be optimal */ xassert(glp_get_status(mip) == GLP_OPT); /* basis factorization must exist */ xassert(glp_bf_exists(mip)); /* obtain (fractional) value of x[j] in optimal basic solution to LP relaxation of the current subproblem */ xassert(1 <= j && j <= n); beta = mip->col[j]->prim; /* since the value of x[j] is fractional, it is basic; compute corresponding row of the simplex table */ len = lpx_eval_tab_row(mip, m+j, ind, val); /* kase < 0 means down-branch; kase > 0 means up-branch */ for (kase = -1; kase <= +1; kase += 2) { /* for down-branch we introduce new upper bound floor(beta) for x[j]; similarly, for up-branch we introduce new lower bound ceil(beta) for x[j]; in the current basis this new upper/lower bound is violated, so in the adjacent basis x[j] will leave the basis and go to its new upper/lower bound; we need to know which non-basic variable x[k] should enter the basis to keep dual feasibility */ #if 0 /* 23/XI-2009 */ k = lpx_dual_ratio_test(mip, len, ind, val, kase, 1e-7); #else k = lpx_dual_ratio_test(mip, len, ind, val, kase, 1e-9); #endif /* if no variable has been chosen, current basis being primal infeasible due to the new upper/lower bound of x[j] is dual unbounded, therefore, LP relaxation to corresponding branch has no primal feasible solution */ if (k == 0) { if (mip->dir == GLP_MIN) { if (kase < 0) *dn = +DBL_MAX; else *up = +DBL_MAX; } else if (mip->dir == GLP_MAX) { if (kase < 0) *dn = -DBL_MAX; else *up = -DBL_MAX; } else xassert(mip != mip); continue; } xassert(1 <= k && k <= m+n); /* row of the simplex table corresponding to specified basic variable x[j] is the following: x[j] = ... + alfa * x[k] + ... ; we need to know influence coefficient, alfa, at non-basic variable x[k] chosen with the dual ratio test */ for (t = 1; t <= len; t++) if (ind[t] == k) break; xassert(1 <= t && t <= len); alfa = val[t]; /* determine status and reduced cost of variable x[k] */ if (k <= m) { stat = mip->row[k]->stat; gamma = mip->row[k]->dual; } else { stat = mip->col[k-m]->stat; gamma = mip->col[k-m]->dual; } /* x[k] cannot be basic or fixed non-basic */ xassert(stat == GLP_NL || stat == GLP_NU || stat == GLP_NF); /* if the current basis is dual degenerative, some reduced costs, which are close to zero, may have wrong sign due to round-off errors, so correct the sign of gamma */ if (mip->dir == GLP_MIN) { if (stat == GLP_NL && gamma < 0.0 || stat == GLP_NU && gamma > 0.0 || stat == GLP_NF) gamma = 0.0; } else if (mip->dir == GLP_MAX) { if (stat == GLP_NL && gamma > 0.0 || stat == GLP_NU && gamma < 0.0 || stat == GLP_NF) gamma = 0.0; } else xassert(mip != mip); /* determine the change of x[j] in the adjacent basis: delta x[j] = new x[j] - old x[j] */ delta = (kase < 0 ? floor(beta) : ceil(beta)) - beta; /* compute the change of x[k] in the adjacent basis: delta x[k] = new x[k] - old x[k] = delta x[j] / alfa */ delta /= alfa; /* compute the change of the objective in the adjacent basis: delta z = new z - old z = gamma * delta x[k] */ dz = gamma * delta; if (mip->dir == GLP_MIN) xassert(dz >= 0.0); else if (mip->dir == GLP_MAX) xassert(dz <= 0.0); else xassert(mip != mip); /* compute the new objective value in the adjacent basis: new z = old z + delta z */ if (kase < 0) *dn = mip->obj_val + dz; else *up = mip->obj_val + dz; } /*xprintf("obj = %g; dn = %g; up = %g\n", mip->obj_val, *dn, *up);*/ return; } /*********************************************************************** * NAME * * ios_round_bound - improve local bound by rounding * * SYNOPSIS * * #include "glpios.h" * double ios_round_bound(glp_tree *tree, double bound); * * RETURNS * * For the given local bound for any integer feasible solution to the * current subproblem the routine ios_round_bound returns an improved * local bound for the same integer feasible solution. * * BACKGROUND * * Let the current subproblem has the following objective function: * * z = sum c[j] * x[j] + s >= b, (1) * j in J * * where J = {j: c[j] is non-zero and integer, x[j] is integer}, s is * the sum of terms corresponding to fixed variables, b is an initial * local bound (minimization). * * From (1) it follows that: * * d * sum (c[j] / d) * x[j] + s >= b, (2) * j in J * * or, equivalently, * * sum (c[j] / d) * x[j] >= (b - s) / d = h, (3) * j in J * * where d = gcd(c[j]). Since the left-hand side of (3) is integer, * h = (b - s) / d can be rounded up to the nearest integer: * * h' = ceil(h) = (b' - s) / d, (4) * * that gives an rounded, improved local bound: * * b' = d * h' + s. (5) * * In case of maximization '>=' in (1) should be replaced by '<=' that * leads to the following formula: * * h' = floor(h) = (b' - s) / d, (6) * * which should used in the same way as (4). * * NOTE: If b is a valid local bound for a child of the current * subproblem, b' is also valid for that child subproblem. */ double ios_round_bound(glp_tree *tree, double bound) { glp_prob *mip = tree->mip; int n = mip->n; int d, j, nn, *c = tree->iwrk; double s, h; /* determine c[j] and compute s */ nn = 0, s = mip->c0, d = 0; for (j = 1; j <= n; j++) { GLPCOL *col = mip->col[j]; if (col->coef == 0.0) continue; if (col->type == GLP_FX) { /* fixed variable */ s += col->coef * col->prim; } else { /* non-fixed variable */ if (col->kind != GLP_IV) goto skip; if (col->coef != floor(col->coef)) goto skip; if (fabs(col->coef) <= (double)INT_MAX) c[++nn] = (int)fabs(col->coef); else d = 1; } } /* compute d = gcd(c[1],...c[nn]) */ if (d == 0) { if (nn == 0) goto skip; d = gcdn(nn, c); } xassert(d > 0); /* compute new local bound */ if (mip->dir == GLP_MIN) { if (bound != +DBL_MAX) { h = (bound - s) / (double)d; if (h >= floor(h) + 0.001) { /* round up */ h = ceil(h); /*xprintf("d = %d; old = %g; ", d, bound);*/ bound = (double)d * h + s; /*xprintf("new = %g\n", bound);*/ } } } else if (mip->dir == GLP_MAX) { if (bound != -DBL_MAX) { h = (bound - s) / (double)d; if (h <= ceil(h) - 0.001) { /* round down */ h = floor(h); bound = (double)d * h + s; } } } else xassert(mip != mip); skip: return bound; } /*********************************************************************** * NAME * * ios_is_hopeful - check if subproblem is hopeful * * SYNOPSIS * * #include "glpios.h" * int ios_is_hopeful(glp_tree *tree, double bound); * * DESCRIPTION * * Given the local bound of a subproblem the routine ios_is_hopeful * checks if the subproblem can have an integer optimal solution which * is better than the best one currently known. * * RETURNS * * If the subproblem can have a better integer optimal solution, the * routine returns non-zero; otherwise, if the corresponding branch can * be pruned, the routine returns zero. */ int ios_is_hopeful(glp_tree *tree, double bound) { glp_prob *mip = tree->mip; int ret = 1; double eps; if (mip->mip_stat == GLP_FEAS) { eps = tree->parm->tol_obj * (1.0 + fabs(mip->mip_obj)); switch (mip->dir) { case GLP_MIN: if (bound >= mip->mip_obj - eps) ret = 0; break; case GLP_MAX: if (bound <= mip->mip_obj + eps) ret = 0; break; default: xassert(mip != mip); } } else { switch (mip->dir) { case GLP_MIN: if (bound == +DBL_MAX) ret = 0; break; case GLP_MAX: if (bound == -DBL_MAX) ret = 0; break; default: xassert(mip != mip); } } return ret; } /*********************************************************************** * NAME * * ios_best_node - find active node with best local bound * * SYNOPSIS * * #include "glpios.h" * int ios_best_node(glp_tree *tree); * * DESCRIPTION * * The routine ios_best_node finds an active node whose local bound is * best among other active nodes. * * It is understood that the integer optimal solution of the original * mip problem cannot be better than the best bound, so the best bound * is an lower (minimization) or upper (maximization) global bound for * the original problem. * * RETURNS * * The routine ios_best_node returns the subproblem reference number * for the best node. However, if the tree is empty, it returns zero. */ int ios_best_node(glp_tree *tree) { IOSNPD *node, *best = NULL; switch (tree->mip->dir) { case GLP_MIN: /* minimization */ for (node = tree->head; node != NULL; node = node->next) if (best == NULL || best->bound > node->bound) best = node; break; case GLP_MAX: /* maximization */ for (node = tree->head; node != NULL; node = node->next) if (best == NULL || best->bound < node->bound) best = node; break; default: xassert(tree != tree); } return best == NULL ? 0 : best->p; } /*********************************************************************** * NAME * * ios_relative_gap - compute relative mip gap * * SYNOPSIS * * #include "glpios.h" * double ios_relative_gap(glp_tree *tree); * * DESCRIPTION * * The routine ios_relative_gap computes the relative mip gap using the * formula: * * gap = |best_mip - best_bnd| / (|best_mip| + DBL_EPSILON), * * where best_mip is the best integer feasible solution found so far, * best_bnd is the best (global) bound. If no integer feasible solution * has been found yet, rel_gap is set to DBL_MAX. * * RETURNS * * The routine ios_relative_gap returns the relative mip gap. */ double ios_relative_gap(glp_tree *tree) { glp_prob *mip = tree->mip; int p; double best_mip, best_bnd, gap; if (mip->mip_stat == GLP_FEAS) { best_mip = mip->mip_obj; p = ios_best_node(tree); if (p == 0) { /* the tree is empty */ gap = 0.0; } else { best_bnd = tree->slot[p].node->bound; gap = fabs(best_mip - best_bnd) / (fabs(best_mip) + DBL_EPSILON); } } else { /* no integer feasible solution has been found yet */ gap = DBL_MAX; } return gap; } /*********************************************************************** * NAME * * ios_solve_node - solve LP relaxation of current subproblem * * SYNOPSIS * * #include "glpios.h" * int ios_solve_node(glp_tree *tree); * * DESCRIPTION * * The routine ios_solve_node re-optimizes LP relaxation of the current * subproblem using the dual simplex method. * * RETURNS * * The routine returns the code which is reported by glp_simplex. */ int ios_solve_node(glp_tree *tree) { glp_prob *mip = tree->mip; glp_smcp parm; int ret; /* the current subproblem must exist */ xassert(tree->curr != NULL); /* set some control parameters */ glp_init_smcp(&parm); switch (tree->parm->msg_lev) { case GLP_MSG_OFF: parm.msg_lev = GLP_MSG_OFF; break; case GLP_MSG_ERR: parm.msg_lev = GLP_MSG_ERR; break; case GLP_MSG_ON: case GLP_MSG_ALL: parm.msg_lev = GLP_MSG_ON; break; case GLP_MSG_DBG: parm.msg_lev = GLP_MSG_ALL; break; default: xassert(tree != tree); } parm.meth = GLP_DUALP; if (tree->parm->msg_lev < GLP_MSG_DBG) parm.out_dly = tree->parm->out_dly; else parm.out_dly = 0; /* if the incumbent objective value is already known, use it to prematurely terminate the dual simplex search */ if (mip->mip_stat == GLP_FEAS) { switch (tree->mip->dir) { case GLP_MIN: parm.obj_ul = mip->mip_obj; break; case GLP_MAX: parm.obj_ll = mip->mip_obj; break; default: xassert(mip != mip); } } /* try to solve/re-optimize the LP relaxation */ ret = glp_simplex(mip, &parm); tree->curr->solved++; #if 0 xprintf("ret = %d; status = %d; pbs = %d; dbs = %d; some = %d\n", ret, glp_get_status(mip), mip->pbs_stat, mip->dbs_stat, mip->some); lpx_print_sol(mip, "sol"); #endif return ret; } /**********************************************************************/ IOSPOOL *ios_create_pool(glp_tree *tree) { /* create cut pool */ IOSPOOL *pool; #if 0 pool = dmp_get_atom(tree->pool, sizeof(IOSPOOL)); #else xassert(tree == tree); pool = xmalloc(sizeof(IOSPOOL)); #endif pool->size = 0; pool->head = pool->tail = NULL; pool->ord = 0, pool->curr = NULL; return pool; } int ios_add_row(glp_tree *tree, IOSPOOL *pool, const char *name, int klass, int flags, int len, const int ind[], const double val[], int type, double rhs) { /* add row (constraint) to the cut pool */ IOSCUT *cut; IOSAIJ *aij; int k; xassert(pool != NULL); cut = dmp_get_atom(tree->pool, sizeof(IOSCUT)); if (name == NULL || name[0] == '\0') cut->name = NULL; else { for (k = 0; name[k] != '\0'; k++) { if (k == 256) xerror("glp_ios_add_row: cut name too long\n"); if (iscntrl((unsigned char)name[k])) xerror("glp_ios_add_row: cut name contains invalid chara" "cter(s)\n"); } cut->name = dmp_get_atom(tree->pool, strlen(name)+1); strcpy(cut->name, name); } if (!(0 <= klass && klass <= 255)) xerror("glp_ios_add_row: klass = %d; invalid cut class\n", klass); cut->klass = (unsigned char)klass; if (flags != 0) xerror("glp_ios_add_row: flags = %d; invalid cut flags\n", flags); cut->ptr = NULL; if (!(0 <= len && len <= tree->n)) xerror("glp_ios_add_row: len = %d; invalid cut length\n", len); for (k = 1; k <= len; k++) { aij = dmp_get_atom(tree->pool, sizeof(IOSAIJ)); if (!(1 <= ind[k] && ind[k] <= tree->n)) xerror("glp_ios_add_row: ind[%d] = %d; column index out of " "range\n", k, ind[k]); aij->j = ind[k]; aij->val = val[k]; aij->next = cut->ptr; cut->ptr = aij; } if (!(type == GLP_LO || type == GLP_UP || type == GLP_FX)) xerror("glp_ios_add_row: type = %d; invalid cut type\n", type); cut->type = (unsigned char)type; cut->rhs = rhs; cut->prev = pool->tail; cut->next = NULL; if (cut->prev == NULL) pool->head = cut; else cut->prev->next = cut; pool->tail = cut; pool->size++; return pool->size; } IOSCUT *ios_find_row(IOSPOOL *pool, int i) { /* find row (constraint) in the cut pool */ /* (smart linear search) */ xassert(pool != NULL); xassert(1 <= i && i <= pool->size); if (pool->ord == 0) { xassert(pool->curr == NULL); pool->ord = 1; pool->curr = pool->head; } xassert(pool->curr != NULL); if (i < pool->ord) { if (i < pool->ord - i) { pool->ord = 1; pool->curr = pool->head; while (pool->ord != i) { pool->ord++; xassert(pool->curr != NULL); pool->curr = pool->curr->next; } } else { while (pool->ord != i) { pool->ord--; xassert(pool->curr != NULL); pool->curr = pool->curr->prev; } } } else if (i > pool->ord) { if (i - pool->ord < pool->size - i) { while (pool->ord != i) { pool->ord++; xassert(pool->curr != NULL); pool->curr = pool->curr->next; } } else { pool->ord = pool->size; pool->curr = pool->tail; while (pool->ord != i) { pool->ord--; xassert(pool->curr != NULL); pool->curr = pool->curr->prev; } } } xassert(pool->ord == i); xassert(pool->curr != NULL); return pool->curr; } void ios_del_row(glp_tree *tree, IOSPOOL *pool, int i) { /* remove row (constraint) from the cut pool */ IOSCUT *cut; IOSAIJ *aij; xassert(pool != NULL); if (!(1 <= i && i <= pool->size)) xerror("glp_ios_del_row: i = %d; cut number out of range\n", i); cut = ios_find_row(pool, i); xassert(pool->curr == cut); if (cut->next != NULL) pool->curr = cut->next; else if (cut->prev != NULL) pool->ord--, pool->curr = cut->prev; else pool->ord = 0, pool->curr = NULL; if (cut->name != NULL) dmp_free_atom(tree->pool, cut->name, strlen(cut->name)+1); if (cut->prev == NULL) { xassert(pool->head == cut); pool->head = cut->next; } else { xassert(cut->prev->next == cut); cut->prev->next = cut->next; } if (cut->next == NULL) { xassert(pool->tail == cut); pool->tail = cut->prev; } else { xassert(cut->next->prev == cut); cut->next->prev = cut->prev; } while (cut->ptr != NULL) { aij = cut->ptr; cut->ptr = aij->next; dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); } dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); pool->size--; return; } void ios_clear_pool(glp_tree *tree, IOSPOOL *pool) { /* remove all rows (constraints) from the cut pool */ xassert(pool != NULL); while (pool->head != NULL) { IOSCUT *cut = pool->head; pool->head = cut->next; if (cut->name != NULL) dmp_free_atom(tree->pool, cut->name, strlen(cut->name)+1); while (cut->ptr != NULL) { IOSAIJ *aij = cut->ptr; cut->ptr = aij->next; dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); } dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); } pool->size = 0; pool->head = pool->tail = NULL; pool->ord = 0, pool->curr = NULL; return; } void ios_delete_pool(glp_tree *tree, IOSPOOL *pool) { /* delete cut pool */ xassert(pool != NULL); ios_clear_pool(tree, pool); xfree(pool); return; } /**********************************************************************/ #if 0 static int refer_to_node(glp_tree *tree, int j) { /* determine node number corresponding to binary variable x[j] or its complement */ glp_prob *mip = tree->mip; int n = mip->n; int *ref; if (j > 0) ref = tree->n_ref; else ref = tree->c_ref, j = - j; xassert(1 <= j && j <= n); if (ref[j] == 0) { /* new node is needed */ SCG *g = tree->g; int n_max = g->n_max; ref[j] = scg_add_nodes(g, 1); if (g->n_max > n_max) { int *save = tree->j_ref; tree->j_ref = xcalloc(1+g->n_max, sizeof(int)); memcpy(&tree->j_ref[1], &save[1], g->n * sizeof(int)); xfree(save); } xassert(ref[j] == g->n); tree->j_ref[ref[j]] = j; xassert(tree->curr != NULL); if (tree->curr->level > 0) tree->curr->own_nn++; } return ref[j]; } #endif #if 0 void ios_add_edge(glp_tree *tree, int j1, int j2) { /* add new edge to the conflict graph */ glp_prob *mip = tree->mip; int n = mip->n; SCGRIB *e; int first, i1, i2; xassert(-n <= j1 && j1 <= +n && j1 != 0); xassert(-n <= j2 && j2 <= +n && j2 != 0); xassert(j1 != j2); /* determine number of the first node, which was added for the current subproblem */ xassert(tree->curr != NULL); first = tree->g->n - tree->curr->own_nn + 1; /* determine node numbers for both endpoints */ i1 = refer_to_node(tree, j1); i2 = refer_to_node(tree, j2); /* add edge (i1,i2) to the conflict graph */ e = scg_add_edge(tree->g, i1, i2); /* if the current subproblem is not the root and both endpoints were created on some previous levels, save the edge */ if (tree->curr->level > 0 && i1 < first && i2 < first) { IOSRIB *rib; rib = dmp_get_atom(tree->pool, sizeof(IOSRIB)); rib->j1 = j1; rib->j2 = j2; rib->e = e; rib->next = tree->curr->e_ptr; tree->curr->e_ptr = rib; } return; } #endif /* eof */ igraph/src/cs_maxtrans.c0000644000176000001440000001243112325527073015040 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* find an augmenting path starting at column k and extend the match if found */ static void cs_augment (CS_INT k, const cs *A, CS_INT *jmatch, CS_INT *cheap, CS_INT *w, CS_INT *js, CS_INT *is, CS_INT *ps) { CS_INT found = 0, p, i = -1, *Ap = A->p, *Ai = A->i, head = 0, j ; js [0] = k ; /* start with just node k in jstack */ while (head >= 0) { /* --- Start (or continue) depth-first-search at node j ------------- */ j = js [head] ; /* get j from top of jstack */ if (w [j] != k) /* 1st time j visited for kth path */ { w [j] = k ; /* mark j as visited for kth path */ for (p = cheap [j] ; p < Ap [j+1] && !found ; p++) { i = Ai [p] ; /* try a cheap assignment (i,j) */ found = (jmatch [i] == -1) ; } cheap [j] = p ; /* start here next time j is traversed*/ if (found) { is [head] = i ; /* column j matched with row i */ break ; /* end of augmenting path */ } ps [head] = Ap [j] ; /* no cheap match: start dfs for j */ } /* --- Depth-first-search of neighbors of j ------------------------- */ for (p = ps [head] ; p < Ap [j+1] ; p++) { i = Ai [p] ; /* consider row i */ if (w [jmatch [i]] == k) continue ; /* skip jmatch [i] if marked */ ps [head] = p + 1 ; /* pause dfs of node j */ is [head] = i ; /* i will be matched with j if found */ js [++head] = jmatch [i] ; /* start dfs at column jmatch [i] */ break ; } if (p == Ap [j+1]) head-- ; /* node j is done; pop from stack */ } /* augment the match if path found: */ if (found) for (p = head ; p >= 0 ; p--) jmatch [is [p]] = js [p] ; } /* find a maximum transveral */ CS_INT *cs_maxtrans (const cs *A, CS_INT seed) /*[jmatch [0..m-1]; imatch [0..n-1]]*/ { CS_INT i, j, k, n, m, p, n2 = 0, m2 = 0, *Ap, *jimatch, *w, *cheap, *js, *is, *ps, *Ai, *Cp, *jmatch, *imatch, *q ; cs *C ; if (!CS_CSC (A)) return (NULL) ; /* check inputs */ n = A->n ; m = A->m ; Ap = A->p ; Ai = A->i ; w = jimatch = cs_calloc (m+n, sizeof (CS_INT)) ; /* allocate result */ if (!jimatch) return (NULL) ; for (k = 0, j = 0 ; j < n ; j++) /* count nonempty rows and columns */ { n2 += (Ap [j] < Ap [j+1]) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { w [Ai [p]] = 1 ; k += (j == Ai [p]) ; /* count entries already on diagonal */ } } if (k == CS_MIN (m,n)) /* quick return if diagonal zero-free */ { jmatch = jimatch ; imatch = jimatch + m ; for (i = 0 ; i < k ; i++) jmatch [i] = i ; for ( ; i < m ; i++) jmatch [i] = -1 ; for (j = 0 ; j < k ; j++) imatch [j] = j ; for ( ; j < n ; j++) imatch [j] = -1 ; return (cs_idone (jimatch, NULL, NULL, 1)) ; } for (i = 0 ; i < m ; i++) m2 += w [i] ; C = (m2 < n2) ? cs_transpose (A,0) : ((cs *) A) ; /* transpose if needed */ if (!C) return (cs_idone (jimatch, (m2 < n2) ? C : NULL, NULL, 0)) ; n = C->n ; m = C->m ; Cp = C->p ; jmatch = (m2 < n2) ? jimatch + n : jimatch ; imatch = (m2 < n2) ? jimatch : jimatch + m ; w = cs_malloc (5*n, sizeof (CS_INT)) ; /* get workspace */ if (!w) return (cs_idone (jimatch, (m2 < n2) ? C : NULL, w, 0)) ; cheap = w + n ; js = w + 2*n ; is = w + 3*n ; ps = w + 4*n ; for (j = 0 ; j < n ; j++) cheap [j] = Cp [j] ; /* for cheap assignment */ for (j = 0 ; j < n ; j++) w [j] = -1 ; /* all columns unflagged */ for (i = 0 ; i < m ; i++) jmatch [i] = -1 ; /* nothing matched yet */ q = cs_randperm (n, seed) ; /* q = random permutation */ for (k = 0 ; k < n ; k++) /* augment, starting at column q[k] */ { cs_augment (q ? q [k]: k, C, jmatch, cheap, w, js, is, ps) ; } cs_free (q) ; for (j = 0 ; j < n ; j++) imatch [j] = -1 ; /* find row match */ for (i = 0 ; i < m ; i++) if (jmatch [i] >= 0) imatch [jmatch [i]] = i ; return (cs_idone (jimatch, (m2 < n2) ? C : NULL, w, 1)) ; } igraph/src/debug.h0000644000176000001440000000135112325527073013610 0ustar ripleyusersc c\SCCS Information: @(#) c FILE: debug.h SID: 2.3 DATE OF SID: 11/16/95 RELEASE: 2 c c %---------------------------------% c | See debug.doc for documentation | c %---------------------------------% integer logfil, ndigit, mgetv0, & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd common /debug/ & logfil, ndigit, mgetv0, & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd igraph/src/igraph_topology.h0000644000176000001440000002602312325527073015733 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_TOPOLOGY_H #define IGRAPH_TOPOLOGY_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Degree sequences */ /* -------------------------------------------------- */ int igraph_is_degree_sequence(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); /* -------------------------------------------------- */ /* Directed acyclic graphs */ /* -------------------------------------------------- */ int igraph_topological_sorting(const igraph_t *graph, igraph_vector_t *res, igraph_neimode_t mode); int igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); int igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure); /* -------------------------------------------------- */ /* Graph isomorphisms */ /* -------------------------------------------------- */ /* Common functions */ int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, const igraph_vector_t *permutation); /* Generic interface */ int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); /* LAD */ int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, igraph_vector_ptr_t *domains, igraph_bool_t *iso, igraph_vector_t *map, igraph_vector_ptr_t *maps, igraph_bool_t induced, int time_limit); /* VF2 family*/ /** * \typedef igraph_isohandler_t * Callback type, called when an isomorphism was found * * See the details at the documentation of \ref * igraph_isomorphic_function_vf2(). * \param map12 The mapping from the first graph to the second. * \param map21 The mapping from the second graph to the first, the * inverse of \p map12 basically. * \param arg This extra argument was passed to \ref * igraph_isomorphic_function_vf2() when it was called. * \return Boolean, whether to continue with the isomorphism search. */ typedef igraph_bool_t igraph_isohandler_t(const igraph_vector_t *map12, const igraph_vector_t *map21, void *arg); /** * \typedef igraph_isocompat_t * Callback type, called to check whether two vertices or edges are compatible * * VF2 (subgraph) isomorphism functions can be restricted by defining * relations on the vertices and/or edges of the graphs, and then checking * whether the vertices (edges) match according to these relations. * * This feature is implemented by two callbacks, one for * vertices, one for edges. Every time igraph tries to match a vertex (edge) * of the first (sub)graph to a vertex of the second graph, the vertex * (edge) compatibility callback is called. The callback returns a * logical value, giving whether the two vertices match. * * Both callback functions are of type \c igraph_isocompat_t. * \param graph1 The first graph. * \param graph2 The second graph. * \param g1_num The id of a vertex or edge in the first graph. * \param g2_num The id of a vertex or edge in the second graph. * \param arg Extra argument to pass to the callback functions. * \return Logical scalar, whether vertex (or edge) \p g1_num in \p graph1 * is compatible with vertex (or edge) \p g2_num in \p graph2. */ typedef igraph_bool_t igraph_isocompat_t(const igraph_t *graph1, const igraph_t *graph2, const igraph_integer_t g1_num, const igraph_integer_t g2_num, void *arg); int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_bool_t *iso, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_integer_t *count, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); int igraph_get_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_bool_t *iso, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); int igraph_subisomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_t *map12, igraph_vector_t *map21, igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_integer_t *count, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); int igraph_get_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, const igraph_vector_int_t *vertex_color1, const igraph_vector_int_t *vertex_color2, const igraph_vector_int_t *edge_color1, const igraph_vector_int_t *edge_color2, igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); /* BLISS family */ /** * \struct igraph_bliss_info_t * Information about a BLISS run * * Some secondary information found by the BLISS algorithm is stored * here. It is useful if you wany to study the internal working of the * algorithm. * \member nof_nodes The number of nodes in the search tree. * \member nof_leaf_nodes The number of leaf nodes in the search tree. * \member nof_bad_nodes Number of bad nodes. * \member nof_canupdates Number of canrep updates. * \member max_level Maximum level. * \member group_size The size of the automorphism group of the graph, * given as a string. It should be deallocated via * free() if not needed any more. * * See http://www.tcs.hut.fi/Software/bliss/index.html * for details about the algorithm and these parameters. */ typedef struct igraph_bliss_info_t { unsigned long nof_nodes; unsigned long nof_leaf_nodes; unsigned long nof_bad_nodes; unsigned long nof_canupdates; unsigned long max_level; char *group_size; } igraph_bliss_info_t; /** * \typedef igraph_bliss_sh_t * Splitting heuristics for BLISS * * \enumval IGRAPH_BLISS_F First non-singleton cell. * \enumval IGRAPH_BLISS_FL First largest non-singleton cell. * \enumval IGRAPH_BLISS_FS First smallest non-singleton cell. * \enumval IGRAPH_BLISS_FM First maximally non-trivially connected * non-singleton cell. * \enumval IGRAPH_BLISS_FLM Largest maximally non-trivially connected * non-singleton cell. * \enumval IGRAPH_BLISS_FSM Smallest maximally non-trivially * connected non-singletion cell. */ typedef enum { IGRAPH_BLISS_F=0, IGRAPH_BLISS_FL, IGRAPH_BLISS_FS, IGRAPH_BLISS_FM, IGRAPH_BLISS_FLM, IGRAPH_BLISS_FSM } igraph_bliss_sh_t; int igraph_canonical_permutation(const igraph_t *graph, igraph_vector_t *labeling, igraph_bliss_sh_t sh, igraph_bliss_info_t *info); int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso, igraph_vector_t *map12, igraph_vector_t *map21, igraph_bliss_sh_t sh1, igraph_bliss_sh_t sh2, igraph_bliss_info_t *info1, igraph_bliss_info_t *info2); int igraph_automorphisms(const igraph_t *graph, igraph_bliss_sh_t sh, igraph_bliss_info_t *info); /* Functions for 3-4 graphs */ int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); int igraph_isoclass_subgraph(const igraph_t *graph, igraph_vector_t *vids, igraph_integer_t *isoclass); int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, igraph_integer_t number, igraph_bool_t directed); __END_DECLS #endif igraph/src/Color.h0000755000176000001440000000156012325527072013604 0ustar ripleyusers/** Color.h */ #ifndef COLOR_H #define COLOR_H namespace igraph { class Color { public: Color(); Color(double vRed, double vGreen, double vBlue, double vTransparent=1.0); ~Color(); Color operator* (double vRhs) const; // returns multiplication of a scalar with a vector Color operator+ (const Color& vRhs) const; // returns the addition of this color with another color void Red(double vRed); double Red() const; void Green(double vGreen); double Green() const; void Blue(double vBlue); double Blue() const; void Transparent(double vTransparent); double Transparent() const; unsigned char RedByte() const; unsigned char GreenByte() const; unsigned char BlueByte() const; unsigned char TransparentByte() const; private: unsigned char ByteValue(double vZeroToOne) const; double mRed, mGreen, mBlue, mTransparent; }; } // namespace igraph #endif igraph/src/flow.c0000644000176000001440000024764612325527073013507 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_flow.h" #include "igraph_error.h" #include "igraph_memory.h" #include "igraph_constants.h" #include "igraph_interface.h" #include "igraph_adjlist.h" #include "igraph_conversion.h" #include "igraph_constructors.h" #include "igraph_progress.h" #include "igraph_structural.h" #include "igraph_components.h" #include "igraph_types_internal.h" #include "config.h" #include "igraph_math.h" #include "igraph_dqueue.h" #include "igraph_visitor.h" #include "igraph_interrupt_internal.h" #include "igraph_topology.h" #include #include /* * Some general remarks about the functions in this file. * * The following measures can be calculated: * ( 1) s-t maximum flow value, directed graph * ( 2) s-t maximum flow value, undirected graph * ( 3) s-t maximum flow, directed graph * ( 4) s-t maximum flow, undirected graph * ( 5) s-t minimum cut value, directed graph * ( 6) s-t minimum cut value, undirected graph * ( 7) minimum cut value, directed graph * ( 8) minimum cut value, undirected graph * ( 9) s-t minimum cut, directed graph * (10) s-t minimum cut, undirected graph * (11) minimum cut, directed graph * (12) minimum cut, undirected graph * (13) s-t edge connectivity, directed graph * (14) s-t edge connectivity, undirected graph * (15) edge connectivity, directed graph * (16) edge connectivity, undirected graph * (17) s-t vertex connectivity, directed graph * (18) s-t vertex connectivity, undirected graph * (19) vertex connectivity, directed graph * (20) vertex connectivity, undirected graph * (21) s-t number of edge disjoint paths, directed graph * (22) s-t number of edge disjoint paths, undirected graph * (23) s-t number of vertex disjoint paths, directed graph * (24) s-t number of vertex disjoint paths, undirected graph * (25) graph adhesion, directed graph * (26) graph adhesion, undirected graph * (27) graph cohesion, directed graph * (28) graph cohesion, undirected graph * * This is how they are calculated: * ( 1) igraph_maxflow_value, calls igraph_maxflow. * ( 2) igraph_maxflow_value, calls igraph_maxflow, this calls * igraph_i_maxflow_undirected. This transforms the graph into a * directed graph, including two mutual edges instead of every * undirected edge, then igraph_maxflow is called again with the * directed graph. * ( 3) igraph_maxflow, does the push-relabel algorithm, optionally * calculates the cut, the partitions and the flow itself. * ( 4) igraph_maxflow calls igraph_i_maxflow_undirected, this converts * the undirected graph into a directed one, adding two mutual edges * for each undirected edge, then igraph_maxflow is called again, * with the directed graph. After igraph_maxflow returns, we need * to edit the flow (and the cut) to make it sense for the * original graph. * ( 5) igraph_st_mincut_value, we just call igraph_maxflow_value * ( 6) igraph_st_mincut_value, we just call igraph_maxflow_value * ( 7) igraph_mincut_value, we call igraph_maxflow_value (|V|-1)*2 * times, from vertex 0 to all other vertices and from all other * vertices to vertex 0 * ( 8) We call igraph_i_mincut_value_undirected, that calls * igraph_i_mincut_undirected with partition=partition2=cut=NULL * The Stoer-Wagner algorithm is used. * ( 9) igraph_st_mincut, just calls igraph_maxflow. * (10) igraph_st_mincut, just calls igraph_maxflow. * (11) igraph_mincut, calls igraph_i_mincut_directed, which runs * the maximum flow algorithm 2(|V|-1) times, from vertex zero to * and from all other vertices and stores the smallest cut. * (12) igraph_mincut, igraph_i_mincut_undirected is called, * this is the Stoer-Wagner algorithm * (13) We just call igraph_maxflow_value, back to (1) * (14) We just call igraph_maxflow_value, back to (2) * (15) We just call igraph_mincut_value (possibly after some basic * checks). Back to (7) * (16) We just call igraph_mincut_value (possibly after some basic * checks). Back to (8). * (17) We call igraph_i_st_vertex_connectivity_directed. * That creates a new graph with 2*|V| vertices and smartly chosen * edges, so that the s-t edge connectivity of this graph is the * same as the s-t vertex connectivity of the original graph. * So finally it calls igraph_maxflow_value, go to (1) * (18) We call igraph_i_st_vertex_connectivity_undirected. * We convert the graph to a directed one, * IGRAPH_TO_DIRECTED_MUTUAL method. Then we call * igraph_i_st_vertex_connectivity_directed, see (17). * (19) We call igraph_i_vertex_connectivity_directed. * That calls igraph_st_vertex_connectivity for all pairs of * vertices. Back to (17). * (20) We call igraph_i_vertex_connectivity_undirected. * That converts the graph into a directed one * (IGRAPH_TO_DIRECTED_MUTUAL) and calls the directed version, * igraph_i_vertex_connectivity_directed, see (19). * (21) igraph_edge_disjoint_paths, we just call igraph_maxflow_value, (1). * (22) igraph_edge_disjoint_paths, we just call igraph_maxflow_value, (2). * (23) igraph_vertex_disjoint_paths, if there is a connection between * the two vertices, then we remove that (or all of them if there * are many), as this could mess up vertex connectivity * calculation. The we call * igraph_i_st_vertex_connectivity_directed, see (19). * (24) igraph_vertex_disjoint_paths, if there is a connection between * the two vertices, then we remove that (or all of them if there * are many), as this could mess up vertex connectivity * calculation. The we call * igraph_i_st_vertex_connectivity_undirected, see (20). * (25) We just call igraph_edge_connectivity, see (15). * (26) We just call igraph_edge_connectivity, see (16). * (27) We just call igraph_vertex_connectivity, see (19). * (28) We just call igraph_vertex_connectivity, see (20). */ /* * This is an internal function that calculates the maximum flow value * on undirected graphs, either for an s-t vertex pair or for the * graph (i.e. all vertex pairs). * * It does it by converting the undirected graph to a corresponding * directed graph, including reciprocal directed edges instead of each * undirected edge. */ int igraph_i_maxflow_undirected(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *flow, igraph_vector_t *cut, igraph_vector_t *partition, igraph_vector_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity, igraph_maxflow_stats_t *stats) { igraph_integer_t no_of_edges=(igraph_integer_t) igraph_ecount(graph); igraph_integer_t no_of_nodes=(igraph_integer_t) igraph_vcount(graph); igraph_vector_t edges; igraph_vector_t newcapacity; igraph_t newgraph; long int i; /* We need to convert this to directed by hand, since we need to be sure that the edge ids will be handled properly to build the new capacity vector. */ IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges*2); IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*4)); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges*4)); for (i=0; i= no_of_edges) { VECTOR(*cut)[i] -= no_of_edges; } } } /* The flow has one non-zero value for each real-nonreal edge pair, by definition, we convert it to a positive-negative vector. If for an edge the flow is negative that means that it is going from the bigger vertex id to the smaller one. For positive values the direction is the opposite. */ if (flow) { long int i; for (i=0; inogap)++; for (bo=b+1; bo <= no_of_nodes; bo++) { while (!igraph_dbuckets_empty_bucket(ibuckets, bo)) { long int n=igraph_dbuckets_pop(ibuckets, bo); (stats->nogapnodes)++; DIST(n)=no_of_nodes; } } } void igraph_i_mf_relabel(long int v, long int no_of_nodes, igraph_vector_long_t *distance, igraph_vector_long_t *first, igraph_vector_t *rescap, igraph_vector_long_t *to, igraph_vector_long_t *current, igraph_maxflow_stats_t *stats, int *nrelabelsince) { long int min=no_of_nodes; long int k, l, min_edge=0; (stats->norelabel)++; (*nrelabelsince)++; DIST(v)=no_of_nodes; for (k=FIRST(v), l=LAST(v); k 0 && DIST(HEAD(k)) < min) { min=DIST(HEAD(k)); min_edge=k; } } min++; if (min < no_of_nodes) { DIST(v) = min; CURRENT(v) = min_edge; } } void igraph_i_mf_push(long int v, long int e, long int n, igraph_vector_long_t *current, igraph_vector_t *rescap, igraph_vector_t *excess, long int target, long int source, igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, igraph_vector_long_t *distance, igraph_vector_long_t *rev, igraph_maxflow_stats_t *stats, int *npushsince) { igraph_real_t delta= RESCAP(e) < EXCESS(v) ? RESCAP(e) : EXCESS(v); (stats->nopush)++; (*npushsince)++; if (EXCESS(n) == 0 && n != target) { igraph_dbuckets_delete(ibuckets, DIST(n), n); igraph_buckets_add(buckets, (long int) DIST(n), n); } RESCAP(e) -= delta; RESCAP(REV(e)) += delta; EXCESS(n) += delta; EXCESS(v) -= delta; } void igraph_i_mf_discharge(long int v, igraph_vector_long_t *current, igraph_vector_long_t *first, igraph_vector_t *rescap, igraph_vector_long_t *to, igraph_vector_long_t *distance, igraph_vector_t *excess, long int no_of_nodes, long int source, long int target, igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, igraph_vector_long_t *rev, igraph_maxflow_stats_t *stats, int *npushsince, int *nrelabelsince) { do { long int i; long int start=(long int) CURRENT(v); long int stop =(long int) LAST(v); for (i = start; i < stop; i++) { if (RESCAP(i) > 0) { long int nei=HEAD(i); if (DIST(v) == DIST(nei)+1) { PUSH((v), i, nei); if (EXCESS(v) == 0) { break; } } } } if (i == stop) { long int origdist=DIST(v); RELABEL(v); if (igraph_buckets_empty_bucket(buckets, origdist) && igraph_dbuckets_empty_bucket(ibuckets, origdist)) { GAP(origdist); } if (DIST(v) == no_of_nodes) { break; } } else { CURRENT(v) = i; igraph_dbuckets_add(ibuckets, DIST(v), v); break; } } while (1); } void igraph_i_mf_bfs(igraph_dqueue_long_t *bfsq, long int source, long int target, long int no_of_nodes, igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, igraph_vector_long_t *distance, igraph_vector_long_t *first, igraph_vector_long_t *current, igraph_vector_long_t *to, igraph_vector_t *excess, igraph_vector_t *rescap, igraph_vector_long_t *rev) { long int k, l; igraph_buckets_clear(buckets); igraph_dbuckets_clear(ibuckets); igraph_vector_long_fill(distance, no_of_nodes); DIST(target) = 0; igraph_dqueue_long_push(bfsq, target); while (!igraph_dqueue_long_empty(bfsq)) { long int node=igraph_dqueue_long_pop(bfsq); long int ndist=DIST(node)+1; for (k=FIRST(node), l=LAST(node); k 0) { long int nei=HEAD(k); if (DIST(nei) == no_of_nodes) { DIST(nei) = ndist; CURRENT(nei) = FIRST(nei); if (EXCESS(nei) > 0) { igraph_buckets_add(buckets, ndist, nei); } else { igraph_dbuckets_add(ibuckets, ndist, nei); } igraph_dqueue_long_push(bfsq, nei); } } } } } /** * \function igraph_maxflow * Maximum network flow between a pair of vertices * * This function implements the Goldberg-Tarjan algorithm for * calculating value of the maximum flow in a directed or undirected * graph. The algorithm was given in Andrew V. Goldberg, Robert * E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of * the ACM, 35(4), 921-940, 1988. * * The input of the function is a graph, a vector * of real numbers giving the capacity of the edges and two vertices * of the graph, the source and the target. A flow is a function * assigning positive real numbers to the edges and satisfying two * requirements: (1) the flow value is less than the capacity of the * edge and (2) at each vertex except the source and the target, the * incoming flow (ie. the sum of the flow on the incoming edges) is * the same as the outgoing flow (ie. the sum of the flow on the * outgoing edges). The value of the flow is the incoming flow at the * target vertex. The maximum flow is the flow with the maximum * value. * * \param graph The input graph, either directed or undirected. * \param value Pointer to a real number, the value of the maximum * will be placed here, unless it is a null pointer. * \param flow If not a null pointer, then it must be a pointer to an * initialized vector. The vector will be resized, and the flow * on each edge will be placed in it, in the order of the edge * ids. For undirected graphs this argument is bit trickier, * since for these the flow direction is not predetermined by * the edge direction. For these graphs the elements of the * \p flow vector can be negative, this means that the flow * goes from the bigger vertex id to the smaller one. Positive * values mean that the flow goes from the smaller vertex id to * the bigger one. * \param cut A null pointer or a pointer to an initialized vector. * If not a null pointer, then the minimum cut corresponding to * the maximum flow is stored here, i.e. all edge ids that are * part of the minimum cut are stored in the vector. * \param partition A null pointer or a pointer to an initialized * vector. If not a null pointer, then the first partition of * the minimum cut that corresponds to the maximum flow will be * placed here. The first partition is always the one that * contains the source vertex. * \param partition2 A null pointer or a pointer to an initialized * vector. If not a null pointer, then the second partition of * the minimum cut that corresponds to the maximum flow will be * placed here. The second partition is always the one that * contains the target vertex. * \param source The id of the source vertex. * \param target The id of the target vertex. * \param capacity Vector containing the capacity of the edges. If NULL, then * every edge is considered to have capacity 1.0. * \param stats Counts of the number of different operations * preformed by the algorithm are stored here. * \return Error code. * * Time complexity: O(|V|^3). In practice it is much faster, but i * cannot prove a better lower bound for the data structure i've * used. In fact, this implementation runs much faster than the * \c hi_pr implementation discussed in * B. V. Cherkassky and A. V. Goldberg: On implementing the * push-relabel method for the maximum flow problem, (Algorithmica, * 19:390--410, 1997) on all the graph classes i've tried. * * \sa \ref igraph_mincut_value(), \ref igraph_edge_connectivity(), * \ref igraph_vertex_connectivity() for * properties based on the maximum flow. * * \example examples/simple/flow.c * \example examples/simple/flow2.c */ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *flow, igraph_vector_t *cut, igraph_vector_t *partition, igraph_vector_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity, igraph_maxflow_stats_t *stats) { igraph_integer_t no_of_nodes=(igraph_integer_t) igraph_vcount(graph); igraph_integer_t no_of_orig_edges=(igraph_integer_t) igraph_ecount(graph); igraph_integer_t no_of_edges=2*no_of_orig_edges; igraph_vector_t rescap, excess; igraph_vector_long_t from, to, rev, distance; igraph_vector_t edges, rank; igraph_vector_long_t current, first; igraph_buckets_t buckets; igraph_dbuckets_t ibuckets; igraph_dqueue_long_t bfsq; long int i, j, idx; int npushsince=0, nrelabelsince=0; igraph_maxflow_stats_t local_stats; /* used if the user passed a null pointer for stats */ if (stats == 0) { stats = &local_stats; } if (!igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_maxflow_undirected(graph, value, flow, cut, partition, partition2, source, target, capacity, stats)); return 0; } if (capacity && igraph_vector_size(capacity) != no_of_orig_edges) { IGRAPH_ERROR("Invalid capacity vector", IGRAPH_EINVAL); } if (source<0 || source>=no_of_nodes || target<0 || target>=no_of_nodes) { IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); } stats->nopush = stats->norelabel = stats->nogap = stats->nogapnodes = stats->nobfs = 0; /* * The data structure: * - First of all, we consider every edge twice, first the edge * itself, but also its opposite. * - (from, to) contain all edges (original + opposite), ordered by * the id of the source vertex. During the algorithm we just need * 'to', so from is destroyed soon. We only need it in the * beginning, to create the 'first' pointers. * - 'first' is a pointer vector for 'to', first[i] points to the * first neighbor of vertex i and first[i+1]-1 is the last * neighbor of vertex i. (Unless vertex i is isolate, in which * case first[i]==first[i+1]). * - 'rev' contains a mapping from an edge to its opposite pair * - 'rescap' contains the residual capacities of the edges, this is * initially equal to the capacity of the edges for the original * edges and it is zero for the opposite edges. * - 'excess' contains the excess flow for the vertices. I.e. the flow * that is coming in, but it is not going out. * - 'current' stores the next neighboring vertex to check, for every * vertex, when excess flow is being pushed to neighbors. * - 'distance' stores the distance of the vertices from the source. * - 'rank' and 'edges' are only needed temporarily, for ordering and * storing the edges. * - we use an igraph_buckets_t data structure ('buckets') to find * the vertices with the highest 'distance' values quickly. * This always contains the vertices that have a positive excess * flow. */ #undef FIRST #undef LAST #undef CURRENT #undef RESCAP #undef REV #undef HEAD #undef EXCESS #undef DIST #define FIRST(i) (VECTOR(first)[(i)]) #define LAST(i) (VECTOR(first)[(i)+1]) #define CURRENT(i) (VECTOR(current)[(i)]) #define RESCAP(i) (VECTOR(rescap)[(i)]) #define REV(i) (VECTOR(rev)[(i)]) #define HEAD(i) (VECTOR(to)[(i)]) #define EXCESS(i) (VECTOR(excess)[(i)]) #define DIST(i) (VECTOR(distance)[(i)]) igraph_dqueue_long_init(&bfsq, no_of_nodes); IGRAPH_FINALLY(igraph_dqueue_long_destroy, &bfsq); IGRAPH_VECTOR_LONG_INIT_FINALLY(&to, no_of_edges); IGRAPH_VECTOR_LONG_INIT_FINALLY(&rev, no_of_edges); IGRAPH_VECTOR_INIT_FINALLY(&rescap, no_of_edges); IGRAPH_VECTOR_INIT_FINALLY(&excess, no_of_nodes); IGRAPH_VECTOR_LONG_INIT_FINALLY(&distance, no_of_nodes); IGRAPH_VECTOR_LONG_INIT_FINALLY(&first, no_of_nodes+1); IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_edges); IGRAPH_VECTOR_LONG_INIT_FINALLY(&from, no_of_edges); IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges); /* Create the basic data structure */ IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); IGRAPH_CHECK(igraph_vector_rank(&edges, &rank, no_of_nodes)); for (i=0; inobfs)++; while (!igraph_buckets_empty(&buckets)) { long int vertex=igraph_buckets_popmax(&buckets); DISCHARGE(vertex); if (npushsince > no_of_nodes / 2 && nrelabelsince > no_of_nodes) { (stats->nobfs)++; BFS(); npushsince = nrelabelsince = 0; } } /* Store the result */ if (value) { *value=EXCESS(target); } /* If we also need the minimum cut */ if (cut || partition || partition2) { /* We need to find all vertices from which the target is reachable in the residual graph. We do a breadth-first search, going backwards. */ igraph_dqueue_t Q; igraph_vector_bool_t added; long int marked=0; IGRAPH_CHECK(igraph_vector_bool_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &added); IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); igraph_dqueue_push(&Q, target); VECTOR(added)[(long int)target]=1; marked++; while (!igraph_dqueue_empty(&Q)) { long int actnode=(long int) igraph_dqueue_pop(&Q); for (i=FIRST(actnode), j=LAST(actnode); i 0.0) { VECTOR(added)[nei]=1; marked++; IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); } } } igraph_dqueue_destroy(&Q); IGRAPH_FINALLY_CLEAN(1); /* Now we marked each vertex that is on one side of the cut, check the crossing edges */ if (cut) { igraph_vector_clear(cut); for (i=0; i 0.0) { VECTOR(added)[nei]=1; IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); IGRAPH_CHECK(igraph_dqueue_push(&Q, actdist+1)); } } } /* !igraph_dqueue_empty(&Q) */ igraph_vector_int_destroy(&added); igraph_dqueue_destroy(&Q); IGRAPH_FINALLY_CLEAN(2); /* Reinitialize the buckets */ igraph_buckets_clear(&buckets); for (i=0; i 0.0 && i != source && i != target) { igraph_buckets_add(&buckets, (long int) DIST(i), i); } } /* Now we return the flow to the source */ while (!igraph_buckets_empty(&buckets)) { long int vertex=igraph_buckets_popmax(&buckets); /* DISCHARGE(vertex) comes here */ do { for (i=(long int) CURRENT(vertex), j=LAST(vertex); i 0) { long int nei=HEAD(i); if (DIST(vertex) == DIST(nei)+1) { igraph_real_t delta= RESCAP(i) < EXCESS(vertex) ? RESCAP(i) : EXCESS(vertex); RESCAP(i) -= delta; RESCAP(REV(i)) += delta; if (nei != source && EXCESS(nei) == 0.0 && DIST(nei) != no_of_nodes) { igraph_buckets_add(&buckets, (long int) DIST(nei), nei); } EXCESS(nei) += delta; EXCESS(vertex) -= delta; if (EXCESS(vertex) == 0) break; } } } if (i==j) { /* RELABEL(vertex) comes here */ igraph_real_t min; long int min_edge=0; DIST(vertex)=min=no_of_nodes; for (k=FIRST(vertex), l=LAST(vertex); k 0) { if (DIST(HEAD(k)) < min) { min=DIST(HEAD(k)); min_edge=k; } } } min++; if (min < no_of_nodes) { DIST(vertex)=min; CURRENT(vertex)=min_edge; /* Vertex is still active */ igraph_buckets_add(&buckets, (long int) DIST(vertex), vertex); } /* TODO: gap heuristics here ??? */ } else { CURRENT(vertex) = FIRST(vertex); } break; } while (1); } /* We need to eliminate flow cycles now. Before that we check that there is a cycle in the flow graph. First we do a couple of DFSes from the source vertex to the target and factor out the paths we find. If there is no more path to the target, then all remaining flow must be in flow cycles, so we don't need it at all. Some details. 'stack' contains the whole path of the DFS, both the vertices and the edges, they are alternating in the stack. 'current' helps finding the next outgoing edge of a vertex quickly, the next edge of 'v' is FIRST(v)+CURRENT(v). If this is LAST(v), then there are no more edges to try. The 'added' vector contains 0 if the vertex was not visited before, 1 if it is currently in 'stack', and 2 if it is not in 'stack', but it was visited before. */ IGRAPH_VECTOR_INIT_FINALLY(&flow_edges, 0); for (i=0, j=0; i RESCAP(pos)) { IGRAPH_CHECK(igraph_vector_push_back(&flow_edges, IGRAPH_FROM(graph, j))); IGRAPH_CHECK(igraph_vector_push_back(&flow_edges, IGRAPH_TO(graph, j))); } } IGRAPH_CHECK(igraph_create(&flow_graph, &flow_edges, no_of_nodes, IGRAPH_DIRECTED)); igraph_vector_destroy(&flow_edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &flow_graph); IGRAPH_CHECK(igraph_is_dag(&flow_graph, &dag)); igraph_destroy(&flow_graph); IGRAPH_FINALLY_CLEAN(1); if (!dag) { igraph_vector_long_t stack; igraph_vector_t mycap; IGRAPH_CHECK(igraph_vector_long_init(&stack, 0)); IGRAPH_FINALLY(igraph_vector_long_destroy, &stack); IGRAPH_CHECK(igraph_vector_int_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_int_destroy, &added); IGRAPH_VECTOR_INIT_FINALLY(&mycap, no_of_edges); #define MYCAP(i) (VECTOR(mycap)[(i)]) for (i=0; i= 0 && VECTOR(stack)[idx+1] != nei; idx-=2) { long int e=VECTOR(stack)[idx]; igraph_real_t rcap= e >= 0 ? MYCAP(e) : MYCAP(edge); if (rcap < thisflow) { thisflow=rcap; } } MYCAP(edge) -= thisflow; RESCAP(edge) += thisflow; for (idx=igraph_vector_long_size(&stack)-2; idx >= 0 && VECTOR(stack)[idx+1] != nei; idx-=2) { long int e=VECTOR(stack)[idx]; if (e >= 0) { MYCAP(e) -= thisflow; RESCAP(e) += thisflow; } } CURRENT(actnode) += 1; } else if (edge < LAST(actnode)) { /* && VECTOR(added)[nei]==2 */ /* The next edge leads to a vertex that was visited before, but it is currently not in 'stack' */ CURRENT(actnode) += 1; } else { /* Go backward, take out the node and the edge that leads to it */ igraph_vector_long_pop_back(&stack); igraph_vector_long_pop_back(&stack); VECTOR(added)[actnode]=2; } } /* If non-empty, then it contains a path from source to target in the residual graph. We factor out this path from the flow. */ if (!igraph_vector_long_empty(&stack)) { long int pl=igraph_vector_long_size(&stack); igraph_real_t thisflow=EXCESS(target); for (i=2; iThis function implements the Goldberg-Tarjan algorithm for * calculating value of the maximum flow in a directed or undirected * graph. The algorithm was given in Andrew V. Goldberg, Robert * E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of * the ACM, 35(4), 921-940, 1988. * * The input of the function is a graph, a vector * of real numbers giving the capacity of the edges and two vertices * of the graph, the source and the target. A flow is a function * assigning positive real numbers to the edges and satisfying two * requirements: (1) the flow value is less than the capacity of the * edge and (2) at each vertex except the source and the target, the * incoming flow (ie. the sum of the flow on the incoming edges) is * the same as the outgoing flow (ie. the sum of the flow on the * outgoing edges). The value of the flow is the incoming flow at the * target vertex. The maximum flow is the flow with the maximum * value. * * According to a theorem by Ford and Fulkerson * (L. R. Ford Jr. and D. R. Fulkerson. Maximal flow through a * network. Canadian J. Math., 8:399-404, 1956.) the maximum flow * between two vertices is the same as the * minimum cut between them (also called the minimum s-t cut). So \ref * igraph_st_mincut_value() gives the same result in all cases as \c * igraph_maxflow_value(). * * Note that the value of the maximum flow is the same as the * minimum cut in the graph. * \param graph The input graph, either directed or undirected. * \param value Pointer to a real number, the result will be placed here. * \param source The id of the source vertex. * \param target The id of the target vertex. * \param capacity Vector containing the capacity of the edges. If NULL, then * every edge is considered to have capacity 1.0. * \param stats Counts of the number of different operations * preformed by the algorithm are stored here. * \return Error code. * * Time complexity: O(|V|^3). * * \sa \ref igraph_maxflow() to calculate the actual flow. * \ref igraph_mincut_value(), \ref igraph_edge_connectivity(), * \ref igraph_vertex_connectivity() for * properties based on the maximum flow. */ int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity, igraph_maxflow_stats_t *stats) { return igraph_maxflow(graph, value, /*flow=*/ 0, /*cut=*/ 0, /*partition=*/ 0, /*partition1=*/ 0, source, target, capacity, stats); } /** * \function igraph_st_mincut_value * \brief The minimum s-t cut in a graph * * The minimum s-t cut in a weighted (=valued) graph is the * total minimum edge weight needed to remove from the graph to * eliminate all paths from a given vertex (\c source) to * another vertex (\c target). Directed paths are considered in * directed graphs, and undirected paths in undirected graphs. * * The minimum s-t cut between two vertices is known to be same * as the maximum flow between these two vertices. So this function * calls \ref igraph_maxflow_value() to do the calculation. * \param graph The input graph. * \param value Pointer to a real variable, the result will be stored * here. * \param source The id of the source vertex. * \param target The id of the target vertex. * \param capacity Pointer to the capacity vector, it should contain * non-negative numbers and its length should be the same the * the number of edges in the graph. It can be a null pointer, then * every edge has unit capacity. * \return Error code. * * Time complexity: O(|V|^3), see also the discussion for \ref * igraph_maxflow_value(), |V| is the number of vertices. */ int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity) { if (source == target) { IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_maxflow_value(graph, value, source, target, capacity, 0)); return 0; } /** * \function igraph_st_mincut * Minimum cut between a source and a target vertex * * Finds the edge set that has the smallest total capacity among all * edge sets that disconnect the source and target vertices. * * The calculation is performed using maximum flow * techniques, by calling \ref igraph_maxflow(). * \param graph The input graph. * \param value Pointer to a real variable, the value of the cut is * stored here. * \param cut Pointer to a real vector, the edge ids that are included * in the cut are stored here. This argument is ignored if it * is a null pointer. * \param partition Pointer to a real vector, the vertex ids of the * vertices in the first partition of the cut are stored * here. The first partition is always the one that contains the * source vertex. This argument is ignored if it is a null pointer. * \param partition2 Pointer to a real vector, the vertex ids of the * vertices in the second partition of the cut are stored here. * The second partition is always the one that contains the * target vertex. This argument is ignored if it is a null pointer. * \param source Integer, the id of the source vertex. * \param target Integer, the id of the target vertex. * \param capacity Vector containing the capacity of the edges. If a * null pointer, then every edge is considered to have capacity * 1.0. * \return Error code. * * \sa \ref igraph_maxflow(). * * Time complexity: see \ref igraph_maxflow(). */ int igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *cut, igraph_vector_t *partition, igraph_vector_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity) { return igraph_maxflow(graph, value, /*flow=*/ 0, cut, partition, partition2, source, target, capacity, 0); } /* This is a flow-based version, but there is a better one for undirected graphs */ /* int igraph_i_mincut_value_undirected(const igraph_t *graph, */ /* igraph_real_t *res, */ /* const igraph_vector_t *capacity) { */ /* long int no_of_edges=igraph_ecount(graph); */ /* long int no_of_nodes=igraph_vcount(graph); */ /* igraph_vector_t edges; */ /* igraph_vector_t newcapacity; */ /* igraph_t newgraph; */ /* long int i; */ /* /\* We need to convert this to directed by hand, since we need to be */ /* sure that the edge ids will be handled properly to build the new */ /* capacity vector. *\/ */ /* IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); */ /* IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges*2); */ /* IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*4)); */ /* IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); */ /* IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges*4)); */ /* for (i=0; i= 2) { long int last; igraph_real_t acut; long int a, n; igraph_vector_t *edges, *edges2; igraph_vector_int_t *neis, *neis2; do { a=igraph_i_cutheap_popmax(&heap); /* update the weights of the active vertices connected to a */ edges=igraph_inclist_get(&inclist, a); neis=igraph_adjlist_get(&adjlist, a); n=igraph_vector_size(edges); for (i=0; i 1); /* Now, there is only one active vertex left, calculate the cut of the phase */ acut=igraph_i_cutheap_maxvalue(&heap); last=igraph_i_cutheap_popmax(&heap); if (acut < mincut) { mincut=acut; mincut_step=act_step; } if (mincut == 0) { break; } /* And contract the last and the remaining vertex (a and last) */ /* Before actually doing that, make some notes */ act_step++; if (calc_cut) { IGRAPH_CHECK(igraph_vector_push_back(&mergehist, a)); IGRAPH_CHECK(igraph_vector_push_back(&mergehist, last)); } /* First remove the a--last edge if there is one, a is still the last deactivated vertex */ edges=igraph_inclist_get(&inclist, a); neis=igraph_adjlist_get(&adjlist, a); n=igraph_vector_size(edges); for (i=0; i=0; i--) { if ( mark[ (long int) VECTOR(mergehist)[2*i] ] ) { size++; mark [ (long int) VECTOR(mergehist)[2*i+1] ]=1; } } /* now store them, if requested */ if (partition) { IGRAPH_CHECK(igraph_vector_resize(partition, size)); idx=0; VECTOR(*partition)[idx++]=bignode; for (i=mincut_step-1; i>=0; i--) { if (mark[ (long int) VECTOR(mergehist)[2*i] ]) { VECTOR(*partition)[idx++] = VECTOR(mergehist)[2*i+1]; } } } /* The other partition too? */ if (partition2) { IGRAPH_CHECK(igraph_vector_resize(partition2, no_of_nodes-size)); idx=0; for (i=0; i For directed graphs an implementation based on * calculating 2|V|-2 maximum flows is used. * For undirected graphs we use the Stoer-Wagner * algorithm, as described in M. Stoer and F. Wagner: A simple min-cut * algorithm, Journal of the ACM, 44 585-591, 1997. * * * The first implementation of the actual cut calculation for * undirected graphs was made by Gregory Benison, thanks Greg. * \param graph The input graph. * \param value Pointer to a float, the value of the cut will be * stored here. * \param partition Pointer to an initialized vector, the ids * of the vertices in the first partition after separating the * graph will be stored here. The vector will be resized as * needed. This argument is ignored if it is a NULL pointer. * \param partition2 Pointer to an initialized vector the ids * of the vertices in the second partition will be stored here. * The vector will be resized as needed. This argument is ignored * if it is a NULL pointer. * \param cut Pointer to an initialized vector, the ids of the edges * in the cut will be stored here. This argument is ignored if it * is a NULL pointer. * \param capacity A numeric vector giving the capacities of the * edges. If a null pointer then all edges have unit capacity. * \return Error code. * * \sa \ref igraph_mincut_value(), a simpler interface for calculating * the value of the cut only. * * Time complexity: for directed graphs it is O(|V|^4), but see the * remarks at \ref igraph_maxflow(). For undirected graphs it is * O(|V||E|+|V|^2 log|V|). |V| and |E| are the number of vertices and * edges respectively. * * \example examples/simple/igraph_mincut.c */ int igraph_mincut(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *partition, igraph_vector_t *partition2, igraph_vector_t *cut, const igraph_vector_t *capacity) { if (igraph_is_directed(graph)) { if (partition || partition2 || cut) { igraph_i_mincut_directed(graph, value, partition, partition2, cut, capacity); } else { return igraph_mincut_value(graph, value, capacity); } } else { IGRAPH_CHECK(igraph_i_mincut_undirected(graph, value, partition, partition2, cut, capacity)); return IGRAPH_SUCCESS; } return 0; } int igraph_i_mincut_value_undirected(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *capacity) { return igraph_i_mincut_undirected(graph, res, 0, 0, 0, capacity); } /** * \function igraph_mincut_value * \brief The minimum edge cut in a graph * * The minimum edge cut in a graph is the total minimum * weight of the edges needed to remove from the graph to make the * graph \em not strongly connected. (If the original graph is not * strongly connected then this is zero.) Note that in undirected * graphs strong connectedness is the same as weak connectedness. * * The minimum cut can be calculated with maximum flow * techniques, although the current implementation does this only for * directed graphs and a separate non-flow based implementation is * used for undirected graphs. See Mechthild Stoer and Frank Wagner: A * simple min-cut algorithm, Journal of the ACM 44 585--591, 1997. * For directed graphs * the maximum flow is calculated between a fixed vertex and all the * other vertices in the graph and this is done in both * directions. Then the minimum is taken to get the minimum cut. * * \param graph The input graph. * \param res Pointer to a real variable, the result will be stored * here. * \param capacity Pointer to the capacity vector, it should contain * the same number of non-negative numbers as the number of edges in * the graph. If a null pointer then all edges will have unit capacity. * \return Error code. * * \sa \ref igraph_mincut(), \ref igraph_maxflow_value(), \ref * igraph_st_mincut_value(). * * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and * O(|V|^4) for directed graphs, but see also the discussion at the * documentation of \ref igraph_maxflow_value(). */ int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *capacity) { long int no_of_nodes=igraph_vcount(graph); igraph_real_t minmaxflow, flow; long int i; minmaxflow=IGRAPH_INFINITY; if (!igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_mincut_value_undirected(graph, res, capacity)); return 0; } for (i=1; i=no_of_nodes || target<0 || target>=no_of_nodes) { IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); } switch (neighbors) { case IGRAPH_VCONN_NEI_ERROR: IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); if (conn1) { IGRAPH_ERROR("vertices connected", IGRAPH_EINVAL); return 0; } break; case IGRAPH_VCONN_NEI_NEGATIVE: IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); if (conn1) { *res=-1; return 0; } break; case IGRAPH_VCONN_NEI_NUMBER_OF_NODES: IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); if (conn1) { *res=no_of_nodes; return 0; } break; case IGRAPH_VCONN_NEI_IGNORE: break; default: IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL); break; } /* Create the new graph */ IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, 2*(no_of_edges+no_of_nodes))); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); IGRAPH_CHECK(igraph_vector_resize(&edges, 2*(no_of_edges+no_of_nodes))); for (i=0; i<2*no_of_edges; i+=2) { igraph_integer_t to=(igraph_integer_t) VECTOR(edges)[i+1]; if (to != source && to != target) { VECTOR(edges)[i+1] = no_of_nodes + to; } } for (i=0; i=no_of_nodes || target<0 || target>=no_of_nodes) { IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); } switch (neighbors) { case IGRAPH_VCONN_NEI_ERROR: IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); if (conn) { IGRAPH_ERROR("vertices connected", IGRAPH_EINVAL); return 0; } break; case IGRAPH_VCONN_NEI_NEGATIVE: IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); if (conn) { *res=-1; return 0; } break; case IGRAPH_VCONN_NEI_NUMBER_OF_NODES: IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); if (conn) { *res=no_of_nodes; return 0; } break; case IGRAPH_VCONN_NEI_IGNORE: break; default: IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL); break; } IGRAPH_CHECK(igraph_copy(&newgraph, graph)); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_CHECK(igraph_to_directed(&newgraph, IGRAPH_TO_DIRECTED_MUTUAL)); IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res, source, target, IGRAPH_VCONN_NEI_IGNORE)); igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_st_vertex_connectivity * \brief The vertex connectivity of a pair of vertices * * The vertex connectivity of two vertices (\c source and * \c target) is the minimum number of vertices that have to be * deleted to eliminate all paths from \c source to \c * target. Directed paths are considered in directed graphs. * * The vertex connectivity of a pair is the same as the number * of different (ie. node-independent) paths from source to * target. * * The current implementation uses maximum flow calculations to * obtain the result. * \param graph The input graph. * \param res Pointer to an integer, the result will be stored here. * \param source The id of the source vertex. * \param target The id of the target vertex. * \param neighbors A constant giving what to do if the two vertices * are connected. Possible values: * \c IGRAPH_VCONN_NEI_ERROR, stop with an error message, * \c IGRAPH_VCONN_NEGATIVE, return -1. * \c IGRAPH_VCONN_NUMBER_OF_NODES, return the number of nodes. * \c IGRAPH_VCONN_IGNORE, ignore the fact that the two vertices * are connected and calculated the number of vertices needed * to eliminate all paths except for the trivial (direct) paths * between \c source and \c vertex. TOOD: what about neighbors? * \return Error code. * * Time complexity: O(|V|^3), but see the discussion at \ref * igraph_maxflow_value(). * * \sa \ref igraph_vertex_connectivity(), * \ref igraph_edge_connectivity(), * \ref igraph_maxflow_value(). */ int igraph_st_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target, igraph_vconn_nei_t neighbors) { if (source == target) { IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL); } if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res, source, target, neighbors)); } else { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res, source, target, neighbors)); } return 0; } int igraph_i_vertex_connectivity_directed(const igraph_t *graph, igraph_integer_t *res) { igraph_integer_t no_of_nodes=(igraph_integer_t) igraph_vcount(graph); long int i, j; igraph_integer_t minconn=no_of_nodes-1, conn; for (i=0; i The vertex connectivity of a graph is the minimum * vertex connectivity along each pairs of vertices in the graph. * * The vertex connectivity of a graph is the same as group * cohesion as defined in Douglas R. White and Frank Harary: The * cohesiveness of blocks in social networks: node connectivity and * conditional density, Sociological Methodology 31:305--359, 2001. * \param graph The input graph. * \param res Pointer to an integer, the result will be stored here. * \param checks Logical constant. Whether to check that the graph is * connected and also the degree of the vertices. If the graph is * not (strongly) connected then the connectivity is obviously zero. Otherwise * if the minimum degree is one then the vertex connectivity is also * one. It is a good idea to perform these checks, as they can be * done quickly compared to the connectivity calculation itself. * They were suggested by Peter McMahan, thanks Peter. * \return Error code. * * Time complexity: O(|V|^5). * * \sa \ref igraph_st_vertex_connectivity(), \ref igraph_maxflow_value(), * and \ref igraph_edge_connectivity(). */ int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { igraph_bool_t ret=0; if (checks) { IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); } /* Are we done yet? */ if (!ret) { if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res)); } else { IGRAPH_CHECK(igraph_i_vertex_connectivity_undirected(graph, res)); } } return 0; } /** * \function igraph_st_edge_connectivity * \brief Edge connectivity of a pair of vertices * * The edge connectivity of two vertices (\c source and * \c target) in a graph is the minimum number of edges that * have to be deleted from the graph to eliminate all paths from \c * source to \c target. * * This function uses the maximum flow algorithm to calculate * the edge connectivity. * \param graph The input graph, it has to be directed. * \param res Pointer to an integer, the result will be stored here. * \param source The id of the source vertex. * \param target The id of the target vertex. * \return Error code. * * Time complexity: O(|V|^3). * * \sa \ref igraph_maxflow_value(), \ref igraph_edge_connectivity(), * \ref igraph_st_vertex_connectivity(), \ref * igraph_vertex_connectivity(). */ int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { igraph_real_t flow; if (source == target) { IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); *res = (igraph_integer_t) flow; return 0; } /** * \function igraph_edge_connectivity * \brief The minimum edge connectivity in a graph. * * This is the minimum of the edge connectivity over all * pairs of vertices in the graph. * * * The edge connectivity of a graph is the same as group adhesion as * defined in Douglas R. White and Frank Harary: The cohesiveness of * blocks in social networks: node connectivity and conditional * density, Sociological Methodology 31:305--359, 2001. * \param graph The input graph. * \param res Pointer to an integer, the result will be stored here. * \param checks Logical constant. Whether to check that the graph is * connected and also the degree of the vertices. If the graph is * not (strongly) connected then the connectivity is obviously zero. Otherwise * if the minimum degree is one then the edge connectivity is also * one. It is a good idea to perform these checks, as they can be * done quickly compared to the connectivity calculation itself. * They were suggested by Peter McMahan, thanks Peter. * \return Error code. * * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and * O(|V|^4) for directed graphs, but see also the discussion at the * documentation of \ref igraph_maxflow_value(). * * \sa \ref igraph_st_edge_connectivity(), \ref igraph_maxflow_value(), * \ref igraph_vertex_connectivity(). */ int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { igraph_bool_t ret=0; /* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ if (checks) { IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); } if (!ret) { igraph_real_t real_res; IGRAPH_CHECK(igraph_mincut_value(graph, &real_res, 0)); *res = (igraph_integer_t)real_res; } return 0; } /** * \function igraph_edge_disjoint_paths * \brief The maximum number of edge-disjoint paths between two vertices. * * A set of paths between two vertices is called * edge-disjoint if they do not share any edges. The maximum number of * edge-disjoint paths are calculated by this function using maximum * flow techniques. Directed paths are considered in directed * graphs. * * Note that the number of disjoint paths is the same as the * edge connectivity of the two vertices using uniform edge weights. * \param graph The input graph, can be directed or undirected. * \param res Pointer to an integer variable, the result will be * stored here. * \param source The id of the source vertex. * \param target The id of the target vertex. * \return Error code. * * Time complexity: O(|V|^3), but see the discussion at \ref * igraph_maxflow_value(). * * \sa \ref igraph_vertex_disjoint_paths(), \ref * igraph_st_edge_connectivity(), \ref igraph_maxflow_value(). */ int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { igraph_real_t flow; if (source == target) { IGRAPH_ERROR("Not implemented for source=target", IGRAPH_UNIMPLEMENTED); } IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); *res = (igraph_integer_t) flow; return 0; } /** * \function igraph_vertex_disjoint_paths * \brief Maximum number of vertex-disjoint paths between two vertices. * * A set of paths between two vertices is called * vertex-disjoint if they share no vertices. The calculation is * performed by using maximum flow techniques. * * Note that the number of vertex-disjoint paths is the same as * the vertex connectivity of the two vertices in most cases (if the * two vertices are not connected by an edge). * \param graph The input graph. * \param res Pointer to an integer variable, the result will be * stored here. * \param source The id of the source vertex. * \param target The id of the target vertex. * \return Error code. * * Time complexity: O(|V|^3). * * \sa \ref igraph_edge_disjoint_paths(), \ref * igraph_vertex_connectivity(), \ref igraph_maxflow_value(). */ int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { igraph_bool_t conn; if (source==target) { IGRAPH_ERROR("The source==target case is not implemented", IGRAPH_UNIMPLEMENTED); } igraph_are_connected(graph, source, target, &conn); if (conn) { /* We need to remove every (possibly directed) edge between source and target and calculate the disjoint paths on the new graph. Finally we add 1 for the removed connection(s). */ igraph_es_t es; igraph_vector_t v; igraph_t newgraph; IGRAPH_VECTOR_INIT_FINALLY(&v, 2); VECTOR(v)[0]=source; VECTOR(v)[1]=target; IGRAPH_CHECK(igraph_es_multipairs(&es, &v, IGRAPH_DIRECTED)); IGRAPH_FINALLY(igraph_es_destroy, &es); IGRAPH_CHECK(igraph_copy(&newgraph, graph)); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_CHECK(igraph_delete_edges(&newgraph, es)); if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res, source, target, IGRAPH_VCONN_NEI_IGNORE)); } else { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(&newgraph, res, source, target, IGRAPH_VCONN_NEI_IGNORE)); } if (res) { *res += 1; } IGRAPH_FINALLY_CLEAN(3); igraph_destroy(&newgraph); igraph_es_destroy(&es); igraph_vector_destroy(&v); } /* These do nothing if the two vertices are connected, so it is safe to call them. */ if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res, source, target, IGRAPH_VCONN_NEI_IGNORE)); } else { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res, source, target, IGRAPH_VCONN_NEI_IGNORE)); } return 0; } /** * \function igraph_adhesion * \brief Graph adhesion, this is (almost) the same as edge connectivity. * * This quantity is defined by White and Harary in * The cohesiveness of blocks in social networks: node connectivity and * conditional density, (Sociological Methodology 31:305--359, 2001) * and basically it is the edge connectivity of the graph * with uniform edge weights. * \param graph The input graph, either directed or undirected. * \param res Pointer to an integer, the result will be stored here. * \param checks Logical constant. Whether to check that the graph is * connected and also the degree of the vertices. If the graph is * not (strongly) connected then the adhesion is obviously zero. Otherwise * if the minimum degree is one then the adhesion is also * one. It is a good idea to perform these checks, as they can be * done quickly compared to the edge connectivity calculation itself. * They were suggested by Peter McMahan, thanks Peter. * \return Error code. * * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and * O(|V|^4) for directed graphs, but see also the discussion at the * documentation of \ref igraph_maxflow_value(). * * \sa \ref igraph_cohesion(), \ref igraph_maxflow_value(), \ref * igraph_edge_connectivity(), \ref igraph_mincut_value(). */ int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { return igraph_edge_connectivity(graph, res, checks); } /** * \function igraph_cohesion * \brief Graph cohesion, this is the same as vertex connectivity. * * This quantity was defined by White and Harary in The * cohesiveness of blocks in social networks: node connectivity and * conditional density, (Sociological Methodology 31:305--359, 2001) * and it is the same as the vertex connectivity of a * graph. * \param graph The input graph. * \param res Pointer to an integer variable, the result will be * stored here. * \param checks Logical constant. Whether to check that the graph is * connected and also the degree of the vertices. If the graph is * not (strongly) connected then the cohesion is obviously zero. Otherwise * if the minimum degree is one then the cohesion is also * one. It is a good idea to perform these checks, as they can be * done quickly compared to the vertex connectivity calculation itself. * They were suggested by Peter McMahan, thanks Peter. * \return Error code. * * Time complexity: O(|V|^4), |V| is the number of vertices. In * practice it is more like O(|V|^2), see \ref igraph_maxflow_value(). * * \sa \ref igraph_vertex_connectivity(), \ref igraph_adhesion(), * \ref igraph_maxflow_value(). */ int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { IGRAPH_CHECK(igraph_vertex_connectivity(graph, res, checks)); return 0; } /** * \function igraph_gomory_hu_tree * \brief Gomory-Hu tree of a graph. * * * The Gomory-Hu tree is a concise representation of the value of all the * maximum flows (or minimum cuts) in a graph. The vertices of the tree * correspond exactly to the vertices of the original graph in the same order. * Edges of the Gomory-Hu tree are annotated by flow values. The value of * the maximum flow (or minimum cut) between an arbitrary (u,v) vertex * pair in the original graph is then given by the minimum flow value (i.e. * edge annotation) along the shortest path between u and v in the * Gomory-Hu tree. * * This implementation uses Gusfield's algorithm to construct the * Gomory-Hu tree. See the following paper for more details: * * * Gusfield D: Very simple methods for all pairs network flow analysis. SIAM J * Comput 19(1):143-155, 1990. * * \param graph The input graph. * \param tree Pointer to an uninitialized graph; the result will be * stored here. * \param flows Pointer to an uninitialized vector; the flow values * corresponding to each edge in the Gomory-Hu tree will * be returned here. You may pass a NULL pointer here if you are * not interested in the flow values. * \param capacity Vector containing the capacity of the edges. If NULL, then * every edge is considered to have capacity 1.0. * \return Error code. * * Time complexity: O(|V|^4) since it performs a max-flow calculation * between vertex zero and every other vertex and max-flow is * O(|V|^3). * * \sa \ref igraph_maxflow() */ int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, igraph_vector_t *flows, const igraph_vector_t *capacity) { igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t source, target, mid, i, n; igraph_vector_t neighbors; igraph_vector_t flow_values; igraph_vector_t partition; igraph_vector_t partition2; igraph_real_t flow_value; if (igraph_is_directed(graph)) { IGRAPH_ERROR("Gomory-Hu tree can only be calculated for undirected graphs", IGRAPH_EINVAL); } /* Allocate memory */ IGRAPH_VECTOR_INIT_FINALLY(&neighbors, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&flow_values, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&partition, 0); IGRAPH_VECTOR_INIT_FINALLY(&partition2, 0); /* Initialize the tree: every edge points to node 0 */ /* Actually, this is done implicitly since both 'neighbors' and 'flow_values' are * initialized to zero already */ /* For each source vertex except vertex zero... */ for (source = 1; source < no_of_nodes; source++) { IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_PROGRESS("Gomory-Hu tree", (100.0 * (source - 1)) / (no_of_nodes-1), 0); /* Find its current neighbor in the tree */ target = VECTOR(neighbors)[(long int)source]; /* Find the maximum flow between source and target */ IGRAPH_CHECK(igraph_maxflow(graph, &flow_value, 0, 0, &partition, &partition2, source, target, capacity, 0)); /* Store the maximum flow and determine which side each node is on */ VECTOR(flow_values)[(long int)source] = flow_value; /* Update the tree */ /* igraph_maxflow() guarantees that the source vertex will be in &partition * and not in &partition2 */ n = igraph_vector_size(&partition); for (i = 0; i < n; i++) { mid = VECTOR(partition)[i]; if (mid > source && VECTOR(neighbors)[(long int)mid] == target) { VECTOR(neighbors)[(long int)mid] = source; } } } IGRAPH_PROGRESS("Gomory-Hu tree", 100.0, 0); /* Re-use the 'partition' vector as an edge list now */ IGRAPH_CHECK(igraph_vector_resize(&partition, 2*(no_of_nodes-1))); for (i = 1, mid = 0; i < no_of_nodes; i++, mid += 2) { VECTOR(partition)[(long int)mid] = i; VECTOR(partition)[(long int)mid+1] = VECTOR(neighbors)[(long int)i]; } /* Create the tree graph; we use igraph_subgraph_edges here to keep the * graph and vertex attributes */ IGRAPH_CHECK(igraph_subgraph_edges(graph, tree, igraph_ess_none(), 0)); IGRAPH_CHECK(igraph_add_edges(tree, &partition, 0)); /* Free the allocated memory */ igraph_vector_destroy(&partition2); igraph_vector_destroy(&partition); igraph_vector_destroy(&neighbors); IGRAPH_FINALLY_CLEAN(3); /* Return the flow values to the caller */ if (flows != 0) { IGRAPH_CHECK(igraph_vector_update(flows, &flow_values)); if (no_of_nodes > 0) { igraph_vector_remove(flows, 0); } } /* Free the remaining allocated memory */ igraph_vector_destroy(&flow_values); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } igraph/src/cs_symperm.c0000644000176000001440000000516512325527073014705 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* C = A(p,p) where A and C are symmetric the upper part stored; pinv not p */ cs *cs_symperm (const cs *A, const CS_INT *pinv, CS_INT values) { CS_INT i, j, p, q, i2, j2, n, *Ap, *Ai, *Cp, *Ci, *w ; CS_ENTRY *Cx, *Ax ; cs *C ; if (!CS_CSC (A)) return (NULL) ; /* check inputs */ n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; C = cs_spalloc (n, n, Ap [n], values && (Ax != NULL), 0) ; /* alloc result*/ w = cs_calloc (n, sizeof (CS_INT)) ; /* get workspace */ if (!C || !w) return (cs_done (C, w, NULL, 0)) ; /* out of memory */ Cp = C->p ; Ci = C->i ; Cx = C->x ; for (j = 0 ; j < n ; j++) /* count entries in each column of C */ { j2 = pinv ? pinv [j] : j ; /* column j of A is column j2 of C */ for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; if (i > j) continue ; /* skip lower triangular part of A */ i2 = pinv ? pinv [i] : i ; /* row i of A is row i2 of C */ w [CS_MAX (i2, j2)]++ ; /* column count of C */ } } cs_cumsum (Cp, w, n) ; /* compute column pointers of C */ for (j = 0 ; j < n ; j++) { j2 = pinv ? pinv [j] : j ; /* column j of A is column j2 of C */ for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; if (i > j) continue ; /* skip lower triangular part of A*/ i2 = pinv ? pinv [i] : i ; /* row i of A is row i2 of C */ Ci [q = w [CS_MAX (i2, j2)]++] = CS_MIN (i2, j2) ; if (Cx) Cx [q] = (i2 <= j2) ? Ax [p] : CS_CONJ (Ax [p]) ; } } return (cs_done (C, w, NULL, 1)) ; /* success; free workspace, return C */ } igraph/src/glptsp.c0000644000176000001440000005730412325527073014037 0ustar ripleyusers/* glptsp.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #define _GLPSTD_ERRNO #define _GLPSTD_STDIO #include "glpenv.h" #include "glptsp.h" #define xfault xerror /*---------------------------------------------------------------------- -- tsp_read_data - read TSP instance data. -- -- *Synopsis* -- -- #include "glptsp.h" -- TSP *tsp_read_data(char *fname); -- -- *Description* -- -- The routine tsp_read_data reads a TSP (or related problem) instance -- data from the text file, whose name is the character string fname. -- -- For detailed description of the format recognized by the routine see -- the report: G.Reinelt, TSPLIB 95. -- -- *Returns* -- -- If no error occurred, the routine tsp_read_data returns a pointer to -- the TSP instance data block, which contains loaded data. In the case -- of error the routine prints an error message and returns NULL. */ struct dsa { /* dynamic storage area used by the routine tsp_read_data */ char *fname; /* name of the input text file */ FILE *fp; /* stream assigned to the input text file */ int seqn; /* line sequential number */ int c; /* current character */ char token[255+1]; /* current token */ }; static int get_char(struct dsa *dsa) { dsa->c = fgetc(dsa->fp); if (ferror(dsa->fp)) { xprintf("%s:%d: read error - %s\n", dsa->fname, dsa->seqn, strerror(errno)); return 1; } if (feof(dsa->fp)) dsa->c = EOF; else if (dsa->c == '\n') dsa->seqn++; else if (isspace(dsa->c)) dsa->c = ' '; else if (iscntrl(dsa->c)) { xprintf("%s:%d: invalid control character 0x%02X\n", dsa->fname, dsa->seqn, dsa->c); return 1; } return 0; } static int skip_spaces(struct dsa *dsa, int across) { while (dsa->c == ' ' || (across && dsa->c == '\n')) if (get_char(dsa)) return 1; return 0; } static int scan_keyword(struct dsa *dsa) { int len = 0; if (skip_spaces(dsa, 0)) return 1; dsa->token[0] = '\0'; while (isalnum(dsa->c) || dsa->c == '_') { if (len == 31) { xprintf("%s:%d: keyword `%s...' too long\n", dsa->fname, dsa->seqn, dsa->token); return 1; } dsa->token[len++] = (char)dsa->c, dsa->token[len] = '\0'; if (get_char(dsa)) return 1; } if (len == 0) { xprintf("%s:%d: missing keyword\n", dsa->fname, dsa->seqn); return 1; } return 0; } static int check_colon(struct dsa *dsa) { if (skip_spaces(dsa, 0)) return 1; if (dsa->c != ':') { xprintf("%s:%d: missing colon after `%s'\n", dsa->fname, dsa->seqn, dsa->token); return 1; } if (get_char(dsa)) return 1; return 0; } static int scan_token(struct dsa *dsa, int across) { int len = 0; if (skip_spaces(dsa, across)) return 1; dsa->token[0] = '\0'; while (!(dsa->c == EOF || dsa->c == '\n' || dsa->c == ' ')) { if (len == 255) { dsa->token[31] = '\0'; xprintf("%s:%d: token `%s...' too long\n", dsa->fname, dsa->seqn, dsa->token); return 1; } dsa->token[len++] = (char)dsa->c, dsa->token[len] = '\0'; if (get_char(dsa)) return 1; } return 0; } static int check_newline(struct dsa *dsa) { if (skip_spaces(dsa, 0)) return 1; if (!(dsa->c == EOF || dsa->c == '\n')) { xprintf("%s:%d: extra symbols detected\n", dsa->fname, dsa->seqn); return 1; } if (get_char(dsa)) return 1; return 0; } static int scan_comment(struct dsa *dsa) { int len = 0; if (skip_spaces(dsa, 0)) return 1; dsa->token[0] = '\0'; while (!(dsa->c == EOF || dsa->c == '\n')) { if (len == 255) { xprintf("%s:%d: comment too long\n", dsa->fname, dsa->seqn) ; return 1; } dsa->token[len++] = (char)dsa->c, dsa->token[len] = '\0'; if (get_char(dsa)) return 1; } return 0; } static int scan_integer(struct dsa *dsa, int across, int *val) { if (scan_token(dsa, across)) return 1; if (strlen(dsa->token) == 0) { xprintf("%s:%d: missing integer\n", dsa->fname, dsa->seqn); return 1; } if (str2int(dsa->token, val)) { xprintf("%s:%d: integer `%s' invalid\n", dsa->fname, dsa->seqn , dsa->token); return 1; } return 0; } static int scan_number(struct dsa *dsa, int across, double *val) { if (scan_token(dsa, across)) return 1; if (strlen(dsa->token) == 0) { xprintf("%s:%d: missing number\n", dsa->fname, dsa->seqn); return 1; } if (str2num(dsa->token, val)) { xprintf("%s:%d: number `%s' invalid\n", dsa->fname, dsa->seqn, dsa->token); return 1; } return 0; } TSP *tsp_read_data(char *fname) { struct dsa _dsa, *dsa = &_dsa; TSP *tsp = NULL; dsa->fname = fname; xprintf("tsp_read_data: reading TSP data from `%s'...\n", dsa->fname); dsa->fp = fopen(dsa->fname, "r"); if (dsa->fp == NULL) { xprintf("tsp_read_data: unable to open `%s' - %s\n", dsa->fname, strerror(errno)); goto fail; } tsp = xmalloc(sizeof(TSP)); tsp->name = NULL; tsp->type = TSP_UNDEF; tsp->comment = NULL; tsp->dimension = 0; tsp->edge_weight_type = TSP_UNDEF; tsp->edge_weight_format = TSP_UNDEF; tsp->display_data_type = TSP_UNDEF; tsp->node_x_coord = NULL; tsp->node_y_coord = NULL; tsp->dply_x_coord = NULL; tsp->dply_y_coord = NULL; tsp->tour = NULL; tsp->edge_weight = NULL; dsa->seqn = 1; if (get_char(dsa)) goto fail; loop: if (scan_keyword(dsa)) goto fail; if (strcmp(dsa->token, "NAME") == 0) { if (tsp->name != NULL) { xprintf("%s:%d: NAME entry multiply defined\n", dsa->fname, dsa->seqn); goto fail; } if (check_colon(dsa)) goto fail; if (scan_token(dsa, 0)) goto fail; if (strlen(dsa->token) == 0) { xprintf("%s:%d: NAME entry incomplete\n", dsa->fname, dsa->seqn); goto fail; } tsp->name = xmalloc(strlen(dsa->token) + 1); strcpy(tsp->name, dsa->token); xprintf("tsp_read_data: NAME: %s\n", tsp->name); if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "TYPE") == 0) { if (tsp->type != TSP_UNDEF) { xprintf("%s:%d: TYPE entry multiply defined\n", dsa->fname, dsa->seqn); goto fail; } if (check_colon(dsa)) goto fail; if (scan_keyword(dsa)) goto fail; if (strcmp(dsa->token, "TSP") == 0) tsp->type = TSP_TSP; else if (strcmp(dsa->token, "ATSP") == 0) tsp->type = TSP_ATSP; else if (strcmp(dsa->token, "TOUR") == 0) tsp->type = TSP_TOUR; else { xprintf("%s:%d: data type `%s' not recognized\n", dsa->fname, dsa->seqn, dsa->token); goto fail; } xprintf("tsp_read_data: TYPE: %s\n", dsa->token); if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "COMMENT") == 0) { if (tsp->comment != NULL) { xprintf("%s:%d: COMMENT entry multiply defined\n", dsa->fname, dsa->seqn); goto fail; } if (check_colon(dsa)) goto fail; if (scan_comment(dsa)) goto fail; tsp->comment = xmalloc(strlen(dsa->token) + 1); strcpy(tsp->comment, dsa->token); xprintf("tsp_read_data: COMMENT: %s\n", tsp->comment); if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "DIMENSION") == 0) { if (tsp->dimension != 0) { xprintf("%s:%d: DIMENSION entry multiply defined\n", dsa->fname, dsa->seqn); goto fail; } if (check_colon(dsa)) goto fail; if (scan_integer(dsa, 0, &tsp->dimension)) goto fail; if (tsp->dimension < 1) { xprintf("%s:%d: invalid dimension\n", dsa->fname, dsa->seqn); goto fail; } xprintf("tsp_read_data: DIMENSION: %d\n", tsp->dimension); if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "EDGE_WEIGHT_TYPE") == 0) { if (tsp->edge_weight_type != TSP_UNDEF) { xprintf("%s:%d: EDGE_WEIGHT_TYPE entry multiply defined\n", dsa->fname, dsa->seqn); goto fail; } if (check_colon(dsa)) goto fail; if (scan_keyword(dsa)) goto fail; if (strcmp(dsa->token, "GEO") == 0) tsp->edge_weight_type = TSP_GEO; else if (strcmp(dsa->token, "EUC_2D") == 0) tsp->edge_weight_type = TSP_EUC_2D; else if (strcmp(dsa->token, "ATT") == 0) tsp->edge_weight_type = TSP_ATT; else if (strcmp(dsa->token, "EXPLICIT") == 0) tsp->edge_weight_type = TSP_EXPLICIT; else if (strcmp(dsa->token, "CEIL_2D") == 0) tsp->edge_weight_type = TSP_CEIL_2D; else { xprintf("%s:%d: edge weight type `%s' not recognized\n", dsa->fname, dsa->seqn, dsa->token); goto fail; } xprintf("tsp_read_data: EDGE_WEIGHT_TYPE: %s\n", dsa->token); if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "EDGE_WEIGHT_FORMAT") == 0) { if (tsp->edge_weight_format != TSP_UNDEF) { xprintf( "%s:%d: EDGE_WEIGHT_FORMAT entry multiply defined\n", dsa->fname, dsa->seqn); goto fail; } if (check_colon(dsa)) goto fail; if (scan_keyword(dsa)) goto fail; if (strcmp(dsa->token, "UPPER_ROW") == 0) tsp->edge_weight_format = TSP_UPPER_ROW; else if (strcmp(dsa->token, "FULL_MATRIX") == 0) tsp->edge_weight_format = TSP_FULL_MATRIX; else if (strcmp(dsa->token, "FUNCTION") == 0) tsp->edge_weight_format = TSP_FUNCTION; else if (strcmp(dsa->token, "LOWER_DIAG_ROW") == 0) tsp->edge_weight_format = TSP_LOWER_DIAG_ROW; else { xprintf("%s:%d: edge weight format `%s' not recognized\n", dsa->fname, dsa->seqn, dsa->token); goto fail; } xprintf("tsp_read_data: EDGE_WEIGHT_FORMAT: %s\n", dsa->token); if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "DISPLAY_DATA_TYPE") == 0) { if (tsp->display_data_type != TSP_UNDEF) { xprintf("%s:%d: DISPLAY_DATA_TYPE entry multiply defined\n", dsa->fname, dsa->seqn); goto fail; } if (check_colon(dsa)) goto fail; if (scan_keyword(dsa)) goto fail; if (strcmp(dsa->token, "COORD_DISPLAY") == 0) tsp->display_data_type = TSP_COORD_DISPLAY; else if (strcmp(dsa->token, "TWOD_DISPLAY") == 0) tsp->display_data_type = TSP_TWOD_DISPLAY; else { xprintf("%s:%d: display data type `%s' not recognized\n", dsa->fname, dsa->seqn, dsa->token); goto fail; } xprintf("tsp_read_data: DISPLAY_DATA_TYPE: %s\n", dsa->token); if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "NODE_COORD_SECTION") == 0) { int n = tsp->dimension, k, node; if (n == 0) { xprintf("%s:%d: DIMENSION entry not specified\n", dsa->fname, dsa->seqn); goto fail; } if (tsp->node_x_coord != NULL) { xprintf("%s:%d: NODE_COORD_SECTION multiply specified\n", dsa->fname, dsa->seqn); goto fail; } if (check_newline(dsa)) goto fail; tsp->node_x_coord = xcalloc(1+n, sizeof(double)); tsp->node_y_coord = xcalloc(1+n, sizeof(double)); for (node = 1; node <= n; node++) tsp->node_x_coord[node] = tsp->node_y_coord[node] = DBL_MAX; for (k = 1; k <= n; k++) { if (scan_integer(dsa, 0, &node)) goto fail; if (!(1 <= node && node <= n)) { xprintf("%s:%d: invalid node number %d\n", dsa->fname, dsa->seqn, node); goto fail; } if (tsp->node_x_coord[node] != DBL_MAX) { xprintf("%s:%d: node number %d multiply specified\n", dsa->fname, dsa->seqn, node); goto fail; } if (scan_number(dsa, 0, &tsp->node_x_coord[node])) goto fail; if (scan_number(dsa, 0, &tsp->node_y_coord[node])) goto fail; if (check_newline(dsa)) goto fail; } } else if (strcmp(dsa->token, "DISPLAY_DATA_SECTION") == 0) { int n = tsp->dimension, k, node; if (n == 0) { xprintf("%s:%d: DIMENSION entry not specified\n", dsa->fname, dsa->seqn); goto fail; } if (tsp->dply_x_coord != NULL) { xprintf("%s:%d: DISPLAY_DATA_SECTION multiply specified\n", dsa->fname, dsa->seqn); goto fail; } if (check_newline(dsa)) goto fail; tsp->dply_x_coord = xcalloc(1+n, sizeof(double)); tsp->dply_y_coord = xcalloc(1+n, sizeof(double)); for (node = 1; node <= n; node++) tsp->dply_x_coord[node] = tsp->dply_y_coord[node] = DBL_MAX; for (k = 1; k <= n; k++) { if (scan_integer(dsa, 0, &node)) goto fail; if (!(1 <= node && node <= n)) { xprintf("%s:%d: invalid node number %d\n", dsa->fname, dsa->seqn, node); goto fail; } if (tsp->dply_x_coord[node] != DBL_MAX) { xprintf("%s:%d: node number %d multiply specified\n", dsa->fname, dsa->seqn, node); goto fail; } if (scan_number(dsa, 0, &tsp->dply_x_coord[node])) goto fail; if (scan_number(dsa, 0, &tsp->dply_y_coord[node])) goto fail; if (check_newline(dsa)) goto fail; } } else if (strcmp(dsa->token, "TOUR_SECTION") == 0) { int n = tsp->dimension, k, node; if (n == 0) { xprintf("%s:%d: DIMENSION entry not specified\n", dsa->fname, dsa->seqn); goto fail; } if (tsp->tour != NULL) { xprintf("%s:%d: TOUR_SECTION multiply specified\n", dsa->fname, dsa->seqn); goto fail; } if (check_newline(dsa)) goto fail; tsp->tour = xcalloc(1+n, sizeof(int)); for (k = 1; k <= n; k++) { if (scan_integer(dsa, 1, &node)) goto fail; if (!(1 <= node && node <= n)) { xprintf("%s:%d: invalid node number %d\n", dsa->fname, dsa->seqn, node); goto fail; } tsp->tour[k] = node; } if (scan_integer(dsa, 1, &node)) goto fail; if (node != -1) { xprintf("%s:%d: extra node(s) detected\n", dsa->fname, dsa->seqn); goto fail; } if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "EDGE_WEIGHT_SECTION") == 0) { int n = tsp->dimension, i, j, temp; if (n == 0) { xprintf("%s:%d: DIMENSION entry not specified\n", dsa->fname, dsa->seqn); goto fail; } if (tsp->edge_weight_format == TSP_UNDEF) { xprintf("%s:%d: EDGE_WEIGHT_FORMAT entry not specified\n", dsa->fname, dsa->seqn); goto fail; } if (tsp->edge_weight != NULL) { xprintf("%s:%d: EDGE_WEIGHT_SECTION multiply specified\n", dsa->fname, dsa->seqn); goto fail; } if (check_newline(dsa)) goto fail; tsp->edge_weight = xcalloc(1+n*n, sizeof(int)); switch (tsp->edge_weight_format) { case TSP_FULL_MATRIX: for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { if (scan_integer(dsa, 1, &temp)) goto fail; tsp->edge_weight[(i - 1) * n + j] = temp; } } break; case TSP_UPPER_ROW: for (i = 1; i <= n; i++) { tsp->edge_weight[(i - 1) * n + i] = 0; for (j = i + 1; j <= n; j++) { if (scan_integer(dsa, 1, &temp)) goto fail; tsp->edge_weight[(i - 1) * n + j] = temp; tsp->edge_weight[(j - 1) * n + i] = temp; } } break; case TSP_LOWER_DIAG_ROW: for (i = 1; i <= n; i++) { for (j = 1; j <= i; j++) { if (scan_integer(dsa, 1, &temp)) goto fail; tsp->edge_weight[(i - 1) * n + j] = temp; tsp->edge_weight[(j - 1) * n + i] = temp; } } break; default: goto fail; } if (check_newline(dsa)) goto fail; } else if (strcmp(dsa->token, "EOF") == 0) { if (check_newline(dsa)) goto fail; goto done; } else { xprintf("%s:%d: keyword `%s' not recognized\n", dsa->fname, dsa->seqn, dsa->token); goto fail; } goto loop; done: xprintf("tsp_read_data: %d lines were read\n", dsa->seqn-1); fclose(dsa->fp); return tsp; fail: if (tsp != NULL) { if (tsp->name != NULL) xfree(tsp->name); if (tsp->comment != NULL) xfree(tsp->comment); if (tsp->node_x_coord != NULL) xfree(tsp->node_x_coord); if (tsp->node_y_coord != NULL) xfree(tsp->node_y_coord); if (tsp->dply_x_coord != NULL) xfree(tsp->dply_x_coord); if (tsp->dply_y_coord != NULL) xfree(tsp->dply_y_coord); if (tsp->tour != NULL) xfree(tsp->tour); if (tsp->edge_weight != NULL) xfree(tsp->edge_weight); xfree(tsp); } if (dsa->fp != NULL) fclose(dsa->fp); return NULL; } /*---------------------------------------------------------------------- -- tsp_free_data - free TSP instance data. -- -- *Synopsis* -- -- #include "glptsp.h" -- void tsp_free_data(TSP *tsp); -- -- *Description* -- -- The routine tsp_free_data frees all the memory allocated to the TSP -- instance data block, which the parameter tsp points to. */ void tsp_free_data(TSP *tsp) { if (tsp->name != NULL) xfree(tsp->name); if (tsp->comment != NULL) xfree(tsp->comment); if (tsp->node_x_coord != NULL) xfree(tsp->node_x_coord); if (tsp->node_y_coord != NULL) xfree(tsp->node_y_coord); if (tsp->dply_x_coord != NULL) xfree(tsp->dply_x_coord); if (tsp->dply_y_coord != NULL) xfree(tsp->dply_y_coord); if (tsp->tour != NULL) xfree(tsp->tour); if (tsp->edge_weight != NULL) xfree(tsp->edge_weight); xfree(tsp); return; } /*---------------------------------------------------------------------- -- tsp_distance - compute distance between two nodes. -- -- *Synopsis* -- -- #include "glptsp.h" -- int tsp_distance(TSP *tsp, int i, int j); -- -- *Description* -- -- The routine tsp_distance computes the distance between i-th and j-th -- nodes for the TSP instance, which tsp points to. -- -- *Returns* -- -- The routine tsp_distance returns the computed distance. */ #define nint(x) ((int)((x) + 0.5)) static double rad(double x) { /* convert input coordinate to longitude/latitude, in radians */ double pi = 3.141592, deg, min; deg = (int)x; min = x - deg; return pi * (deg + 5.0 * min / 3.0) / 180.0; } int tsp_distance(TSP *tsp, int i, int j) { int n = tsp->dimension, dij; if (!(tsp->type == TSP_TSP || tsp->type == TSP_ATSP)) xfault("tsp_distance: invalid TSP instance\n"); if (!(1 <= i && i <= n && 1 <= j && j <= n)) xfault("tsp_distance: node number out of range\n"); switch (tsp->edge_weight_type) { case TSP_UNDEF: xfault("tsp_distance: edge weight type not specified\n"); case TSP_EXPLICIT: if (tsp->edge_weight == NULL) xfault("tsp_distance: edge weights not specified\n"); dij = tsp->edge_weight[(i - 1) * n + j]; break; case TSP_EUC_2D: if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) xfault("tsp_distance: node coordinates not specified\n"); { double xd, yd; xd = tsp->node_x_coord[i] - tsp->node_x_coord[j]; yd = tsp->node_y_coord[i] - tsp->node_y_coord[j]; dij = nint(sqrt(xd * xd + yd * yd)); } break; case TSP_CEIL_2D: if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) xfault("tsp_distance: node coordinates not specified\n"); { double xd, yd; xd = tsp->node_x_coord[i] - tsp->node_x_coord[j]; yd = tsp->node_y_coord[i] - tsp->node_y_coord[j]; dij = (int)ceil(sqrt(xd * xd + yd * yd)); } break; case TSP_GEO: if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) xfault("tsp_distance: node coordinates not specified\n"); { double rrr = 6378.388; double latitude_i = rad(tsp->node_x_coord[i]); double latitude_j = rad(tsp->node_x_coord[j]); double longitude_i = rad(tsp->node_y_coord[i]); double longitude_j = rad(tsp->node_y_coord[j]); double q1 = cos(longitude_i - longitude_j); double q2 = cos(latitude_i - latitude_j); double q3 = cos(latitude_i + latitude_j); dij = (int)(rrr * acos(0.5 * ((1.0 + q1) * q2 - (1.0 - q1) *q3)) + 1.0); } break; case TSP_ATT: if (tsp->node_x_coord == NULL || tsp->node_y_coord == NULL) xfault("tsp_distance: node coordinates not specified\n"); { int tij; double xd, yd, rij; xd = tsp->node_x_coord[i] - tsp->node_x_coord[j]; yd = tsp->node_y_coord[i] - tsp->node_y_coord[j]; rij = sqrt((xd * xd + yd * yd) / 10.0); tij = nint(rij); if (tij < rij) dij = tij + 1; else dij = tij; } break; default: xassert(tsp->edge_weight_type != tsp->edge_weight_type); } return dij; } /* eof */ igraph/src/igraph_stack.c0000644000176000001440000000400512325527073015153 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_stack.h" #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "stack.pmt" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "stack.pmt" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "stack.pmt" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "stack.pmt" #include "igraph_pmt_off.h" #undef BASE_BOOL #define BASE_PTR #include "igraph_pmt.h" #include "stack.pmt" #include "igraph_pmt_off.h" #undef BASE_PTR /** * \ingroup stack * \brief Calls free() on all elements of a pointer stack. */ void igraph_stack_ptr_free_all (igraph_stack_ptr_t* v) { void **ptr; assert(v != 0); assert(v->stor_begin != 0); for (ptr=v->stor_begin; ptrend; ptr++) { igraph_Free(*ptr); } } /** * \ingroup stack * \brief Calls free() on all elements and destroys the stack. */ void igraph_stack_ptr_destroy_all (igraph_stack_ptr_t* v) { assert(v != 0); assert(v->stor_begin != 0); igraph_stack_ptr_free_all(v); igraph_stack_ptr_destroy(v); } igraph/src/dstats.f0000644000176000001440000000223212325527073014021 0ustar ripleyusersc c\SCCS Information: @(#) c FILE: stats.F SID: 2.1 DATE OF SID: 4/19/96 RELEASE: 2 c %---------------------------------------------% c | Initialize statistic and timing information | c | for symmetric Arnoldi code. | c %---------------------------------------------% subroutine igraphdstats c %--------------------------------% c | See stat.doc for documentation | c %--------------------------------% include 'stat.h' c %-----------------------% c | Executable Statements | c %-----------------------% nopx = 0 nbx = 0 nrorth = 0 nitref = 0 nrstrt = 0 tsaupd = 0.0D+0 tsaup2 = 0.0D+0 tsaitr = 0.0D+0 tseigt = 0.0D+0 tsgets = 0.0D+0 tsapps = 0.0D+0 tsconv = 0.0D+0 titref = 0.0D+0 tgetv0 = 0.0D+0 trvec = 0.0D+0 c %----------------------------------------------------% c | User time including reverse communication overhead | c %----------------------------------------------------% tmvopx = 0.0D+0 tmvbx = 0.0D+0 return c c End of igraphdstats c end igraph/src/igraph_visitor.h0000644000176000001440000001212112325527073015550 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_VISITOR_H #define IGRAPH_VISITOR_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Visitor-like functions */ /* -------------------------------------------------- */ /** * \typedef igraph_bfshandler_t * Callback type for BFS function * * \ref igraph_bfs() is able to call a callback function, whenever a * new vertex is found, while doing the breadth-first search. This * callback function must be of type \c igraph_bfshandler_t. It has * the following arguments: * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vid The id of the vertex just found by the breadth-first * search. * \param pred The id of the previous vertex visited. It is -1 if * there is no previous vertex, because the current vertex is the root * is a search tree. * \param succ The id of the next vertex that will be visited. It is * -1 if there is no next vertex, because the current vertex is the * last one in a search tree. * \param rank The rank of the current vertex, it starts with zero. * \param dist The distance (number of hops) of the current vertex * from the root of the current search tree. * \param extra The extra argument that was passed to \ref * igraph_bfs(). * \return A logical value, if TRUE (=non-zero), that is interpreted * as a request to stop the BFS and return to the caller. If a BFS * is terminated like this, then all elements of the result vectors * that were not yet calculated at the point of the termination * contain \c IGRAPH_NAN. * * \sa \ref igraph_bfs() */ typedef igraph_bool_t igraph_bfshandler_t(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t pred, igraph_integer_t succ, igraph_integer_t rank, igraph_integer_t dist, void *extra); int igraph_bfs(const igraph_t *graph, igraph_integer_t root, const igraph_vector_t *roots, igraph_neimode_t mode, igraph_bool_t unreachable, const igraph_vector_t *restricted, igraph_vector_t *order, igraph_vector_t *rank, igraph_vector_t *father, igraph_vector_t *pred, igraph_vector_t *succ, igraph_vector_t *dist, igraph_bfshandler_t *callback, void *extra); int igraph_i_bfs(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mode, igraph_vector_t *vids, igraph_vector_t *layers, igraph_vector_t *parents); /** * \function igraph_dfshandler_t * Callback type for the DFS function * * \ref igraph_dfs() is able to call a callback function, whenever a * new vertex is discovered, and/or whenever a subtree is * completed. These callbacks must be of type \c * igraph_dfshandler_t. They have the following arguments: * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vid The id of the vertex just found by the depth-first * search. * \param dist The distance (number of hops) of the current vertex * from the root of the current search tree. * \param extra The extra argument that was passed to \ref * igraph_dfs(). * \return A logical value, if TRUE (=non-zero), that is interpreted * as a request to stop the DFS and return to the caller. If a DFS * is terminated like this, then all elements of the result vectors * that were not yet calculated at the point of the termination * contain \c IGRAPH_NAN. * * \sa \ref igraph_dfs() */ typedef igraph_bool_t igraph_dfshandler_t(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra); int igraph_dfs(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, igraph_bool_t unreachable, igraph_vector_t *order, igraph_vector_t *order_out, igraph_vector_t *father, igraph_vector_t *dist, igraph_dfshandler_t *in_callback, igraph_dfshandler_t *out_callback, void *extra); __END_DECLS #endif igraph/src/glpnet06.c0000644000176000001440000003073312325527073014162 0ustar ripleyusers/* glpnet06.c (out-of-kilter algorithm) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpenv.h" #include "glpnet.h" /*********************************************************************** * NAME * * okalg - out-of-kilter algorithm * * SYNOPSIS * * #include "glpnet.h" * int okalg(int nv, int na, const int tail[], const int head[], * const int low[], const int cap[], const int cost[], int x[], * int pi[]); * * DESCRIPTION * * The routine okalg implements the out-of-kilter algorithm to find a * minimal-cost circulation in the specified flow network. * * INPUT PARAMETERS * * nv is the number of nodes, nv >= 0. * * na is the number of arcs, na >= 0. * * tail[a], a = 1,...,na, is the index of tail node of arc a. * * head[a], a = 1,...,na, is the index of head node of arc a. * * low[a], a = 1,...,na, is an lower bound to the flow through arc a. * * cap[a], a = 1,...,na, is an upper bound to the flow through arc a, * which is the capacity of the arc. * * cost[a], a = 1,...,na, is a per-unit cost of the flow through arc a. * * NOTES * * 1. Multiple arcs are allowed, but self-loops are not allowed. * * 2. It is required that 0 <= low[a] <= cap[a] for all arcs. * * 3. Arc costs may have any sign. * * OUTPUT PARAMETERS * * x[a], a = 1,...,na, is optimal value of the flow through arc a. * * pi[i], i = 1,...,nv, is Lagrange multiplier for flow conservation * equality constraint corresponding to node i (the node potential). * * RETURNS * * 0 optimal circulation found; * * 1 there is no feasible circulation; * * 2 integer overflow occured; * * 3 optimality test failed (logic error). * * REFERENCES * * L.R.Ford, Jr., and D.R.Fulkerson, "Flows in Networks," The RAND * Corp., Report R-375-PR (August 1962), Chap. III "Minimal Cost Flow * Problems," pp.113-26. */ static int overflow(int u, int v) { /* check for integer overflow on computing u + v */ if (u > 0 && v > 0 && u + v < 0) return 1; if (u < 0 && v < 0 && u + v > 0) return 1; return 0; } int okalg(int nv, int na, const int tail[], const int head[], const int low[], const int cap[], const int cost[], int x[], int pi[]) { int a, aok, delta, i, j, k, lambda, pos1, pos2, s, t, temp, ret, *ptr, *arc, *link, *list; /* sanity checks */ xassert(nv >= 0); xassert(na >= 0); for (a = 1; a <= na; a++) { i = tail[a], j = head[a]; xassert(1 <= i && i <= nv); xassert(1 <= j && j <= nv); xassert(i != j); xassert(0 <= low[a] && low[a] <= cap[a]); } /* allocate working arrays */ ptr = xcalloc(1+nv+1, sizeof(int)); arc = xcalloc(1+na+na, sizeof(int)); link = xcalloc(1+nv, sizeof(int)); list = xcalloc(1+nv, sizeof(int)); /* ptr[i] := (degree of node i) */ for (i = 1; i <= nv; i++) ptr[i] = 0; for (a = 1; a <= na; a++) { ptr[tail[a]]++; ptr[head[a]]++; } /* initialize arc pointers */ ptr[1]++; for (i = 1; i < nv; i++) ptr[i+1] += ptr[i]; ptr[nv+1] = ptr[nv]; /* build arc lists */ for (a = 1; a <= na; a++) { arc[--ptr[tail[a]]] = a; arc[--ptr[head[a]]] = a; } xassert(ptr[1] == 1); xassert(ptr[nv+1] == na+na+1); /* now the indices of arcs incident to node i are stored in locations arc[ptr[i]], arc[ptr[i]+1], ..., arc[ptr[i+1]-1] */ /* initialize arc flows and node potentials */ for (a = 1; a <= na; a++) x[a] = 0; for (i = 1; i <= nv; i++) pi[i] = 0; loop: /* main loop starts here */ /* find out-of-kilter arc */ aok = 0; for (a = 1; a <= na; a++) { i = tail[a], j = head[a]; if (overflow(cost[a], pi[i] - pi[j])) { ret = 2; goto done; } lambda = cost[a] + (pi[i] - pi[j]); if (x[a] < low[a] || lambda < 0 && x[a] < cap[a]) { /* arc a = i->j is out of kilter, and we need to increase the flow through this arc */ aok = a, s = j, t = i; break; } if (x[a] > cap[a] || lambda > 0 && x[a] > low[a]) { /* arc a = i->j is out of kilter, and we need to decrease the flow through this arc */ aok = a, s = i, t = j; break; } } if (aok == 0) { /* all arcs are in kilter */ /* check for feasibility */ for (a = 1; a <= na; a++) { if (!(low[a] <= x[a] && x[a] <= cap[a])) { ret = 3; goto done; } } for (i = 1; i <= nv; i++) { temp = 0; for (k = ptr[i]; k < ptr[i+1]; k++) { a = arc[k]; if (tail[a] == i) { /* a is outgoing arc */ temp += x[a]; } else if (head[a] == i) { /* a is incoming arc */ temp -= x[a]; } else xassert(a != a); } if (temp != 0) { ret = 3; goto done; } } /* check for optimality */ for (a = 1; a <= na; a++) { i = tail[a], j = head[a]; lambda = cost[a] + (pi[i] - pi[j]); if (lambda > 0 && x[a] != low[a] || lambda < 0 && x[a] != cap[a]) { ret = 3; goto done; } } /* current circulation is optimal */ ret = 0; goto done; } /* now we need to find a cycle (t, a, s, ..., t), which allows increasing the flow along it, where a is the out-of-kilter arc just found */ /* link[i] = 0 means that node i is not labelled yet; link[i] = a means that arc a immediately precedes node i */ /* initially only node s is labelled */ for (i = 1; i <= nv; i++) link[i] = 0; link[s] = aok, list[1] = s, pos1 = pos2 = 1; /* breadth first search */ while (pos1 <= pos2) { /* dequeue node i */ i = list[pos1++]; /* consider all arcs incident to node i */ for (k = ptr[i]; k < ptr[i+1]; k++) { a = arc[k]; if (tail[a] == i) { /* a = i->j is a forward arc from s to t */ j = head[a]; /* if node j has been labelled, skip the arc */ if (link[j] != 0) continue; /* if the arc does not allow increasing the flow through it, skip the arc */ if (x[a] >= cap[a]) continue; if (overflow(cost[a], pi[i] - pi[j])) { ret = 2; goto done; } lambda = cost[a] + (pi[i] - pi[j]); if (lambda > 0 && x[a] >= low[a]) continue; } else if (head[a] == i) { /* a = i<-j is a backward arc from s to t */ j = tail[a]; /* if node j has been labelled, skip the arc */ if (link[j] != 0) continue; /* if the arc does not allow decreasing the flow through it, skip the arc */ if (x[a] <= low[a]) continue; if (overflow(cost[a], pi[j] - pi[i])) { ret = 2; goto done; } lambda = cost[a] + (pi[j] - pi[i]); if (lambda < 0 && x[a] <= cap[a]) continue; } else xassert(a != a); /* label node j and enqueue it */ link[j] = a, list[++pos2] = j; /* check for breakthrough */ if (j == t) goto brkt; } } /* NONBREAKTHROUGH */ /* consider all arcs, whose one endpoint is labelled and other is not, and determine maximal change of node potentials */ delta = 0; for (a = 1; a <= na; a++) { i = tail[a], j = head[a]; if (link[i] != 0 && link[j] == 0) { /* a = i->j, where node i is labelled, node j is not */ if (overflow(cost[a], pi[i] - pi[j])) { ret = 2; goto done; } lambda = cost[a] + (pi[i] - pi[j]); if (x[a] <= cap[a] && lambda > 0) if (delta == 0 || delta > + lambda) delta = + lambda; } else if (link[i] == 0 && link[j] != 0) { /* a = j<-i, where node j is labelled, node i is not */ if (overflow(cost[a], pi[i] - pi[j])) { ret = 2; goto done; } lambda = cost[a] + (pi[i] - pi[j]); if (x[a] >= low[a] && lambda < 0) if (delta == 0 || delta > - lambda) delta = - lambda; } } if (delta == 0) { /* there is no feasible circulation */ ret = 1; goto done; } /* increase potentials of all unlabelled nodes */ for (i = 1; i <= nv; i++) { if (link[i] == 0) { if (overflow(pi[i], delta)) { ret = 2; goto done; } pi[i] += delta; } } goto loop; brkt: /* BREAKTHROUGH */ /* walk through arcs of the cycle (t, a, s, ..., t) found in the reverse order and determine maximal change of the flow */ delta = 0; for (j = t;; j = i) { /* arc a immediately precedes node j in the cycle */ a = link[j]; if (head[a] == j) { /* a = i->j is a forward arc of the cycle */ i = tail[a]; lambda = cost[a] + (pi[i] - pi[j]); if (lambda > 0 && x[a] < low[a]) { /* x[a] may be increased until its lower bound */ temp = low[a] - x[a]; } else if (lambda <= 0 && x[a] < cap[a]) { /* x[a] may be increased until its upper bound */ temp = cap[a] - x[a]; } else xassert(a != a); } else if (tail[a] == j) { /* a = i<-j is a backward arc of the cycle */ i = head[a]; lambda = cost[a] + (pi[j] - pi[i]); if (lambda < 0 && x[a] > cap[a]) { /* x[a] may be decreased until its upper bound */ temp = x[a] - cap[a]; } else if (lambda >= 0 && x[a] > low[a]) { /* x[a] may be decreased until its lower bound */ temp = x[a] - low[a]; } else xassert(a != a); } else xassert(a != a); if (delta == 0 || delta > temp) delta = temp; /* check for end of the cycle */ if (i == t) break; } xassert(delta > 0); /* increase the flow along the cycle */ for (j = t;; j = i) { /* arc a immediately precedes node j in the cycle */ a = link[j]; if (head[a] == j) { /* a = i->j is a forward arc of the cycle */ i = tail[a]; /* overflow cannot occur */ x[a] += delta; } else if (tail[a] == j) { /* a = i<-j is a backward arc of the cycle */ i = head[a]; /* overflow cannot occur */ x[a] -= delta; } else xassert(a != a); /* check for end of the cycle */ if (i == t) break; } goto loop; done: /* free working arrays */ xfree(ptr); xfree(arc); xfree(link); xfree(list); return ret; } /* eof */ igraph/src/infomap_Node.h0000644000176000001440000000255112325527073015123 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NODE_H #define NODE_H #include #include #include "igraph_interface.h" class Node; using namespace std; class Node{ public: Node(); Node(int modulenr,double tpweight); vector members; vector< pair > inLinks; vector< pair > outLinks; double selfLink; double teleportWeight; double danglingSize; double exit; double size; }; void cpyNode(Node *newNode, Node *oldNode); #endif igraph/src/foreign-gml-parser.h0000644000176000001440000000530212325527073016222 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton interface for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { STRING = 258, NUM = 259, KEYWORD = 260, LISTOPEN = 261, LISTCLOSE = 262, EOFF = 263 }; #endif /* Tokens. */ #define STRING 258 #define NUM 259 #define KEYWORD 260 #define LISTOPEN 261 #define LISTCLOSE 262 #define EOFF 263 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 98 "igraph/src/foreign-gml-parser.y" { struct { char *s; int len; } str; void *tree; double real; } /* Line 1529 of yacc.c. */ #line 74 "y.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif igraph/src/igraph_neighborhood.h0000644000176000001440000000317012325527073016524 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_NEIGHBORHOOD_H #define IGRAPH_NEIGHBORHOOD_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode); int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode); int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode); __END_DECLS #endif igraph/src/gengraph_graph_molloy_hash.h0000644000176000001440000001633312325527073020102 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GRAPH_MOLLOY_HASH_H #define GRAPH_MOLLOY_HASH_H #include "gengraph_definitions.h" #include "gengraph_hash.h" #include "gengraph_degree_sequence.h" #include #include // This class handles graphs with a constant degree sequence. #define FINAL_HEURISTICS 0 #define GKAN_HEURISTICS 1 #define FAB_HEURISTICS 2 #define OPTIMAL_HEURISTICS 3 #define BRUTE_FORCE_HEURISTICS 4 namespace gengraph { //**************************** // class graph_molloy_hash //**************************** class graph_molloy_hash { private: // Number of vertices int n; //Number of arcs ( = #edges * 2 ) int a; //Total size of links[] int size; // The degree sequence of the graph int *deg; // The array containing all links int *links; // The array containing pointers to adjacency list of every vertices int **neigh; // Counts total size void compute_size(); // Build neigh with deg and links void compute_neigh(); // Allocate memory according to degree_sequence (for constructor use only!!) int alloc(degree_sequence &); // Add edge (a,b). Return FALSE if vertex a is already full. // WARNING : only to be used by havelhakimi(), restore() or constructors inline bool add_edge(int a,int b,int *realdeg) { int deg_a = realdeg[a]; if(deg_a == deg[a]) return false; // Check that edge was not already inserted assert(fast_search(neigh[a],int((a==n-1 ? links+size : neigh[a+1])-neigh[a]),b)==NULL); assert(fast_search(neigh[b],int((b==n-1 ? links+size : neigh[b+1])-neigh[b]),a)==NULL); assert(deg[a] dmax. void depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited); public: //degree of v inline int degree(const int v) { return deg[v]; }; // For debug purposes : verify validity of the graph (symetry, simplicity) bool verify(); // Destroy deg[], neigh[] and links[] ~graph_molloy_hash(); // Allocate memory for the graph. Create deg and links. No edge is created. graph_molloy_hash(degree_sequence &); // Create graph from hard copy graph_molloy_hash(int *); // Create hard copy of graph int *hard_copy(); // Restore from backup void restore(int* back); //Clear hash tables void init(); // nb arcs inline int nbarcs() { return a; }; // nb vertices inline int nbvertices() { return n; }; // print graph in SUCC_LIST mode, in stdout void print(FILE *f = stdout); int print(igraph_t *graph); // Test if graph is connected bool is_connected(); // is edge ? inline bool is_edge(int a, int b) { assert(H_is(neigh[a],deg[a],b) == (fast_search(neigh[a],HASH_SIZE(deg[a]),b)!=NULL)); assert(H_is(neigh[b],deg[b],a) == (fast_search(neigh[b],HASH_SIZE(deg[b]),a)!=NULL)); assert(H_is(neigh[a],deg[a],b) == H_is(neigh[b],deg[b],a)); if(deg[a]. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_init_cpxcp - initialize CPLEX LP format control parameters * * SYNOPSIS * * void glp_init_cpxcp(glp_cpxcp *parm): * * The routine glp_init_cpxcp initializes control parameters used by * the CPLEX LP input/output routines glp_read_lp and glp_write_lp with * default values. * * Default values of the control parameters are stored in the glp_cpxcp * structure, which the parameter parm points to. */ void glp_init_cpxcp(glp_cpxcp *parm) { xassert(parm != NULL); return; } static void check_parm(const char *func, const glp_cpxcp *parm) { /* check control parameters */ xassert(func != NULL); xassert(parm != NULL); return; } /*********************************************************************** * NAME * * glp_read_lp - read problem data in CPLEX LP format * * SYNOPSIS * * int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char * *fname); * * DESCRIPTION * * The routine glp_read_lp reads problem data in CPLEX LP format from * a text file. * * The parameter parm is a pointer to the structure glp_cpxcp, which * specifies control parameters used by the routine. If parm is NULL, * the routine uses default settings. * * The character string fname specifies a name of the text file to be * read. * * Note that before reading data the current content of the problem * object is completely erased with the routine glp_erase_prob. * * RETURNS * * If the operation was successful, the routine glp_read_lp returns * zero. Otherwise, it prints an error message and returns non-zero. */ struct csa { /* common storage area */ glp_prob *P; /* LP/MIP problem object */ const glp_cpxcp *parm; /* pointer to control parameters */ const char *fname; /* name of input CPLEX LP file */ XFILE *fp; /* stream assigned to input CPLEX LP file */ jmp_buf jump; /* label for go to in case of error */ int count; /* line count */ int c; /* current character or XEOF */ int token; /* current token: */ #define T_EOF 0x00 /* end of file */ #define T_MINIMIZE 0x01 /* keyword 'minimize' */ #define T_MAXIMIZE 0x02 /* keyword 'maximize' */ #define T_SUBJECT_TO 0x03 /* keyword 'subject to' */ #define T_BOUNDS 0x04 /* keyword 'bounds' */ #define T_GENERAL 0x05 /* keyword 'general' */ #define T_INTEGER 0x06 /* keyword 'integer' */ #define T_BINARY 0x07 /* keyword 'binary' */ #define T_END 0x08 /* keyword 'end' */ #define T_NAME 0x09 /* symbolic name */ #define T_NUMBER 0x0A /* numeric constant */ #define T_PLUS 0x0B /* delimiter '+' */ #define T_MINUS 0x0C /* delimiter '-' */ #define T_COLON 0x0D /* delimiter ':' */ #define T_LE 0x0E /* delimiter '<=' */ #define T_GE 0x0F /* delimiter '>=' */ #define T_EQ 0x10 /* delimiter '=' */ char image[255+1]; /* image of current token */ int imlen; /* length of token image */ double value; /* value of numeric constant */ int n_max; /* length of the following five arrays (enlarged automatically, if necessary) */ int *ind; /* int ind[1+n_max]; */ double *val; /* double val[1+n_max]; */ char *flag; /* char flag[1+n_max]; */ /* working arrays used to construct linear forms */ double *lb; /* double lb[1+n_max]; */ double *ub; /* double ub[1+n_max]; */ /* lower and upper bounds of variables (columns) */ }; #define CHAR_SET "!\"#$%&()/,.;?@_`'{}|~" /* characters, which may appear in symbolic names */ static void error(struct csa *csa, const char *fmt, ...) { /* print error message and terminate processing */ va_list arg; xprintf("%s:%d: ", csa->fname, csa->count); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); longjmp(csa->jump, 1); /* no return */ } static void warning(struct csa *csa, const char *fmt, ...) { /* print warning message and continue processing */ va_list arg; xprintf("%s:%d: warning: ", csa->fname, csa->count); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); return; } static void read_char(struct csa *csa) { /* read next character from input file */ int c; xassert(csa->c != XEOF); if (csa->c == '\n') csa->count++; c = xfgetc(csa->fp); if (c < 0) { if (xferror(csa->fp)) error(csa, "read error - %s\n", xerrmsg()); else if (csa->c == '\n') { csa->count--; c = XEOF; } else { warning(csa, "missing final end of line\n"); c = '\n'; } } else if (c == '\n') ; else if (isspace(c)) c = ' '; else if (iscntrl(c)) error(csa, "invalid control character 0x%02X\n", c); csa->c = c; return; } static void add_char(struct csa *csa) { /* append current character to current token */ if (csa->imlen == sizeof(csa->image)-1) error(csa, "token `%.15s...' too long\n", csa->image); csa->image[csa->imlen++] = (char)csa->c; csa->image[csa->imlen] = '\0'; read_char(csa); return; } static int the_same(char *s1, char *s2) { /* compare two character strings ignoring case sensitivity */ for (; *s1 != '\0'; s1++, s2++) { if (tolower((unsigned char)*s1) != tolower((unsigned char)*s2)) return 0; } return 1; } static void scan_token(struct csa *csa) { /* scan next token */ int flag; csa->token = -1; csa->image[0] = '\0'; csa->imlen = 0; csa->value = 0.0; loop: flag = 0; /* skip non-significant characters */ while (csa->c == ' ') read_char(csa); /* recognize and scan current token */ if (csa->c == XEOF) csa->token = T_EOF; else if (csa->c == '\n') { read_char(csa); /* if the next character is letter, it may begin a keyword */ if (isalpha(csa->c)) { flag = 1; goto name; } goto loop; } else if (csa->c == '\\') { /* comment; ignore everything until end-of-line */ while (csa->c != '\n') read_char(csa); goto loop; } else if (isalpha(csa->c) || csa->c != '.' && strchr(CHAR_SET, csa->c) != NULL) name: { /* symbolic name */ csa->token = T_NAME; while (isalnum(csa->c) || strchr(CHAR_SET, csa->c) != NULL) add_char(csa); if (flag) { /* check for keyword */ if (the_same(csa->image, "minimize")) csa->token = T_MINIMIZE; else if (the_same(csa->image, "minimum")) csa->token = T_MINIMIZE; else if (the_same(csa->image, "min")) csa->token = T_MINIMIZE; else if (the_same(csa->image, "maximize")) csa->token = T_MAXIMIZE; else if (the_same(csa->image, "maximum")) csa->token = T_MAXIMIZE; else if (the_same(csa->image, "max")) csa->token = T_MAXIMIZE; else if (the_same(csa->image, "subject")) { if (csa->c == ' ') { read_char(csa); if (tolower(csa->c) == 't') { csa->token = T_SUBJECT_TO; csa->image[csa->imlen++] = ' '; csa->image[csa->imlen] = '\0'; add_char(csa); if (tolower(csa->c) != 'o') error(csa, "keyword `subject to' incomplete\n"); add_char(csa); if (isalpha(csa->c)) error(csa, "keyword `%s%c...' not recognized\n", csa->image, csa->c); } } } else if (the_same(csa->image, "such")) { if (csa->c == ' ') { read_char(csa); if (tolower(csa->c) == 't') { csa->token = T_SUBJECT_TO; csa->image[csa->imlen++] = ' '; csa->image[csa->imlen] = '\0'; add_char(csa); if (tolower(csa->c) != 'h') err: error(csa, "keyword `such that' incomplete\n"); add_char(csa); if (tolower(csa->c) != 'a') goto err; add_char(csa); if (tolower(csa->c) != 't') goto err; add_char(csa); if (isalpha(csa->c)) error(csa, "keyword `%s%c...' not recognized\n", csa->image, csa->c); } } } else if (the_same(csa->image, "st")) csa->token = T_SUBJECT_TO; else if (the_same(csa->image, "s.t.")) csa->token = T_SUBJECT_TO; else if (the_same(csa->image, "st.")) csa->token = T_SUBJECT_TO; else if (the_same(csa->image, "bounds")) csa->token = T_BOUNDS; else if (the_same(csa->image, "bound")) csa->token = T_BOUNDS; else if (the_same(csa->image, "general")) csa->token = T_GENERAL; else if (the_same(csa->image, "generals")) csa->token = T_GENERAL; else if (the_same(csa->image, "gen")) csa->token = T_GENERAL; else if (the_same(csa->image, "integer")) csa->token = T_INTEGER; else if (the_same(csa->image, "integers")) csa->token = T_INTEGER; else if (the_same(csa->image, "int")) csa->token = T_INTEGER; else if (the_same(csa->image, "binary")) csa->token = T_BINARY; else if (the_same(csa->image, "binaries")) csa->token = T_BINARY; else if (the_same(csa->image, "bin")) csa->token = T_BINARY; else if (the_same(csa->image, "end")) csa->token = T_END; } } else if (isdigit(csa->c) || csa->c == '.') { /* numeric constant */ csa->token = T_NUMBER; /* scan integer part */ while (isdigit(csa->c)) add_char(csa); /* scan optional fractional part (it is mandatory, if there is no integer part) */ if (csa->c == '.') { add_char(csa); if (csa->imlen == 1 && !isdigit(csa->c)) error(csa, "invalid use of decimal point\n"); while (isdigit(csa->c)) add_char(csa); } /* scan optional decimal exponent */ if (csa->c == 'e' || csa->c == 'E') { add_char(csa); if (csa->c == '+' || csa->c == '-') add_char(csa); if (!isdigit(csa->c)) error(csa, "numeric constant `%s' incomplete\n", csa->image); while (isdigit(csa->c)) add_char(csa); } /* convert the numeric constant to floating-point */ if (str2num(csa->image, &csa->value)) error(csa, "numeric constant `%s' out of range\n", csa->image); } else if (csa->c == '+') csa->token = T_PLUS, add_char(csa); else if (csa->c == '-') csa->token = T_MINUS, add_char(csa); else if (csa->c == ':') csa->token = T_COLON, add_char(csa); else if (csa->c == '<') { csa->token = T_LE, add_char(csa); if (csa->c == '=') add_char(csa); } else if (csa->c == '>') { csa->token = T_GE, add_char(csa); if (csa->c == '=') add_char(csa); } else if (csa->c == '=') { csa->token = T_EQ, add_char(csa); if (csa->c == '<') csa->token = T_LE, add_char(csa); else if (csa->c == '>') csa->token = T_GE, add_char(csa); } else error(csa, "character `%c' not recognized\n", csa->c); /* skip non-significant characters */ while (csa->c == ' ') read_char(csa); return; } static int find_col(struct csa *csa, char *name) { /* find column by its symbolic name */ int j; j = glp_find_col(csa->P, name); if (j == 0) { /* not found; create new column */ j = glp_add_cols(csa->P, 1); glp_set_col_name(csa->P, j, name); /* enlarge working arrays, if necessary */ if (csa->n_max < j) { int n_max = csa->n_max; int *ind = csa->ind; double *val = csa->val; char *flag = csa->flag; double *lb = csa->lb; double *ub = csa->ub; csa->n_max += csa->n_max; csa->ind = xcalloc(1+csa->n_max, sizeof(int)); memcpy(&csa->ind[1], &ind[1], n_max * sizeof(int)); xfree(ind); csa->val = xcalloc(1+csa->n_max, sizeof(double)); memcpy(&csa->val[1], &val[1], n_max * sizeof(double)); xfree(val); csa->flag = xcalloc(1+csa->n_max, sizeof(char)); memset(&csa->flag[1], 0, csa->n_max * sizeof(char)); memcpy(&csa->flag[1], &flag[1], n_max * sizeof(char)); xfree(flag); csa->lb = xcalloc(1+csa->n_max, sizeof(double)); memcpy(&csa->lb[1], &lb[1], n_max * sizeof(double)); xfree(lb); csa->ub = xcalloc(1+csa->n_max, sizeof(double)); memcpy(&csa->ub[1], &ub[1], n_max * sizeof(double)); xfree(ub); } csa->lb[j] = +DBL_MAX, csa->ub[j] = -DBL_MAX; } return j; } /*********************************************************************** * parse_linear_form - parse linear form * * This routine parses the linear form using the following syntax: * * ::= * ::= * ::= | * ::= | + | - | * + | - * * The routine returns the number of terms in the linear form. */ static int parse_linear_form(struct csa *csa) { int j, k, len = 0, newlen; double s, coef; loop: /* parse an optional sign */ if (csa->token == T_PLUS) s = +1.0, scan_token(csa); else if (csa->token == T_MINUS) s = -1.0, scan_token(csa); else s = +1.0; /* parse an optional coefficient */ if (csa->token == T_NUMBER) coef = csa->value, scan_token(csa); else coef = 1.0; /* parse a variable name */ if (csa->token != T_NAME) error(csa, "missing variable name\n"); /* find the corresponding column */ j = find_col(csa, csa->image); /* check if the variable is already used in the linear form */ if (csa->flag[j]) error(csa, "multiple use of variable `%s' not allowed\n", csa->image); /* add new term to the linear form */ len++, csa->ind[len] = j, csa->val[len] = s * coef; /* and mark that the variable is used in the linear form */ csa->flag[j] = 1; scan_token(csa); /* if the next token is a sign, there is another term */ if (csa->token == T_PLUS || csa->token == T_MINUS) goto loop; /* clear marks of the variables used in the linear form */ for (k = 1; k <= len; k++) csa->flag[csa->ind[k]] = 0; /* remove zero coefficients */ newlen = 0; for (k = 1; k <= len; k++) { if (csa->val[k] != 0.0) { newlen++; csa->ind[newlen] = csa->ind[k]; csa->val[newlen] = csa->val[k]; } } return newlen; } /*********************************************************************** * parse_objective - parse objective function * * This routine parses definition of the objective function using the * following syntax: * * ::= minimize | minimum | min | maximize | maximum | max * ::= | : * ::= */ static void parse_objective(struct csa *csa) { /* parse objective sense */ int k, len; /* parse the keyword 'minimize' or 'maximize' */ if (csa->token == T_MINIMIZE) glp_set_obj_dir(csa->P, GLP_MIN); else if (csa->token == T_MAXIMIZE) glp_set_obj_dir(csa->P, GLP_MAX); else xassert(csa != csa); scan_token(csa); /* parse objective name */ if (csa->token == T_NAME && csa->c == ':') { /* objective name is followed by a colon */ glp_set_obj_name(csa->P, csa->image); scan_token(csa); xassert(csa->token == T_COLON); scan_token(csa); } else { /* objective name is not specified; use default */ glp_set_obj_name(csa->P, "obj"); } /* parse linear form */ len = parse_linear_form(csa); for (k = 1; k <= len; k++) glp_set_obj_coef(csa->P, csa->ind[k], csa->val[k]); return; } /*********************************************************************** * parse_constraints - parse constraints section * * This routine parses the constraints section using the following * syntax: * * ::= | : * ::= < | <= | =< | > | >= | => | = * ::= | + | * - * ::= * * ::= subject to | such that | st | s.t. | st. * ::= | * */ static void parse_constraints(struct csa *csa) { int i, len, type; double s; /* parse the keyword 'subject to' */ xassert(csa->token == T_SUBJECT_TO); scan_token(csa); loop: /* create new row (constraint) */ i = glp_add_rows(csa->P, 1); /* parse row name */ if (csa->token == T_NAME && csa->c == ':') { /* row name is followed by a colon */ if (glp_find_row(csa->P, csa->image) != 0) error(csa, "constraint `%s' multiply defined\n", csa->image); glp_set_row_name(csa->P, i, csa->image); scan_token(csa); xassert(csa->token == T_COLON); scan_token(csa); } else { /* row name is not specified; use default */ char name[50]; sprintf(name, "r.%d", csa->count); glp_set_row_name(csa->P, i, name); } /* parse linear form */ len = parse_linear_form(csa); glp_set_mat_row(csa->P, i, len, csa->ind, csa->val); /* parse constraint sense */ if (csa->token == T_LE) type = GLP_UP, scan_token(csa); else if (csa->token == T_GE) type = GLP_LO, scan_token(csa); else if (csa->token == T_EQ) type = GLP_FX, scan_token(csa); else error(csa, "missing constraint sense\n"); /* parse right-hand side */ if (csa->token == T_PLUS) s = +1.0, scan_token(csa); else if (csa->token == T_MINUS) s = -1.0, scan_token(csa); else s = +1.0; if (csa->token != T_NUMBER) error(csa, "missing right-hand side\n"); glp_set_row_bnds(csa->P, i, type, s * csa->value, s * csa->value); /* the rest of the current line must be empty */ if (!(csa->c == '\n' || csa->c == XEOF)) error(csa, "invalid symbol(s) beyond right-hand side\n"); scan_token(csa); /* if the next token is a sign, numeric constant, or a symbolic name, here is another constraint */ if (csa->token == T_PLUS || csa->token == T_MINUS || csa->token == T_NUMBER || csa->token == T_NAME) goto loop; return; } static void set_lower_bound(struct csa *csa, int j, double lb) { /* set lower bound of j-th variable */ if (csa->lb[j] != +DBL_MAX) { warning(csa, "lower bound of variable `%s' redefined\n", glp_get_col_name(csa->P, j)); } csa->lb[j] = lb; return; } static void set_upper_bound(struct csa *csa, int j, double ub) { /* set upper bound of j-th variable */ if (csa->ub[j] != -DBL_MAX) { warning(csa, "upper bound of variable `%s' redefined\n", glp_get_col_name(csa->P, j)); } csa->ub[j] = ub; return; } /*********************************************************************** * parse_bounds - parse bounds section * * This routine parses the bounds section using the following syntax: * * ::= * ::= infinity | inf * ::= | + | * - | + | - * ::= < | <= | =< * ::= > | >= | => * ::= | * | | * | = | free * ::= bounds | bound * ::= | * */ static void parse_bounds(struct csa *csa) { int j, lb_flag; double lb, s; /* parse the keyword 'bounds' */ xassert(csa->token == T_BOUNDS); scan_token(csa); loop: /* bound definition can start with a sign, numeric constant, or a symbolic name */ if (!(csa->token == T_PLUS || csa->token == T_MINUS || csa->token == T_NUMBER || csa->token == T_NAME)) goto done; /* parse bound definition */ if (csa->token == T_PLUS || csa->token == T_MINUS) { /* parse signed lower bound */ lb_flag = 1; s = (csa->token == T_PLUS ? +1.0 : -1.0); scan_token(csa); if (csa->token == T_NUMBER) lb = s * csa->value, scan_token(csa); else if (the_same(csa->image, "infinity") || the_same(csa->image, "inf")) { if (s > 0.0) error(csa, "invalid use of `+inf' as lower bound\n"); lb = -DBL_MAX, scan_token(csa); } else error(csa, "missing lower bound\n"); } else if (csa->token == T_NUMBER) { /* parse unsigned lower bound */ lb_flag = 1; lb = csa->value, scan_token(csa); } else { /* lower bound is not specified */ lb_flag = 0; } /* parse the token that should follow the lower bound */ if (lb_flag) { if (csa->token != T_LE) error(csa, "missing `<', `<=', or `=<' after lower bound\n") ; scan_token(csa); } /* parse variable name */ if (csa->token != T_NAME) error(csa, "missing variable name\n"); j = find_col(csa, csa->image); /* set lower bound */ if (lb_flag) set_lower_bound(csa, j, lb); scan_token(csa); /* parse the context that follows the variable name */ if (csa->token == T_LE) { /* parse upper bound */ scan_token(csa); if (csa->token == T_PLUS || csa->token == T_MINUS) { /* parse signed upper bound */ s = (csa->token == T_PLUS ? +1.0 : -1.0); scan_token(csa); if (csa->token == T_NUMBER) { set_upper_bound(csa, j, s * csa->value); scan_token(csa); } else if (the_same(csa->image, "infinity") || the_same(csa->image, "inf")) { if (s < 0.0) error(csa, "invalid use of `-inf' as upper bound\n"); set_upper_bound(csa, j, +DBL_MAX); scan_token(csa); } else error(csa, "missing upper bound\n"); } else if (csa->token == T_NUMBER) { /* parse unsigned upper bound */ set_upper_bound(csa, j, csa->value); scan_token(csa); } else error(csa, "missing upper bound\n"); } else if (csa->token == T_GE) { /* parse lower bound */ if (lb_flag) { /* the context '... <= x >= ...' is invalid */ error(csa, "invalid bound definition\n"); } scan_token(csa); if (csa->token == T_PLUS || csa->token == T_MINUS) { /* parse signed lower bound */ s = (csa->token == T_PLUS ? +1.0 : -1.0); scan_token(csa); if (csa->token == T_NUMBER) { set_lower_bound(csa, j, s * csa->value); scan_token(csa); } else if (the_same(csa->image, "infinity") || the_same(csa->image, "inf") == 0) { if (s > 0.0) error(csa, "invalid use of `+inf' as lower bound\n"); set_lower_bound(csa, j, -DBL_MAX); scan_token(csa); } else error(csa, "missing lower bound\n"); } else if (csa->token == T_NUMBER) { /* parse unsigned lower bound */ set_lower_bound(csa, j, csa->value); scan_token(csa); } else error(csa, "missing lower bound\n"); } else if (csa->token == T_EQ) { /* parse fixed value */ if (lb_flag) { /* the context '... <= x = ...' is invalid */ error(csa, "invalid bound definition\n"); } scan_token(csa); if (csa->token == T_PLUS || csa->token == T_MINUS) { /* parse signed fixed value */ s = (csa->token == T_PLUS ? +1.0 : -1.0); scan_token(csa); if (csa->token == T_NUMBER) { set_lower_bound(csa, j, s * csa->value); set_upper_bound(csa, j, s * csa->value); scan_token(csa); } else error(csa, "missing fixed value\n"); } else if (csa->token == T_NUMBER) { /* parse unsigned fixed value */ set_lower_bound(csa, j, csa->value); set_upper_bound(csa, j, csa->value); scan_token(csa); } else error(csa, "missing fixed value\n"); } else if (the_same(csa->image, "free")) { /* parse the keyword 'free' */ if (lb_flag) { /* the context '... <= x free ...' is invalid */ error(csa, "invalid bound definition\n"); } set_lower_bound(csa, j, -DBL_MAX); set_upper_bound(csa, j, +DBL_MAX); scan_token(csa); } else if (!lb_flag) { /* neither lower nor upper bounds are specified */ error(csa, "invalid bound definition\n"); } goto loop; done: return; } /*********************************************************************** * parse_integer - parse general, integer, or binary section * * ::= * ::= general | generals | gen * ::= integer | integers | int * ::= binary | binaries | bin *
::= * ::=
| * */ static void parse_integer(struct csa *csa) { int j, binary; /* parse the keyword 'general', 'integer', or 'binary' */ if (csa->token == T_GENERAL) binary = 0, scan_token(csa); else if (csa->token == T_INTEGER) binary = 0, scan_token(csa); else if (csa->token == T_BINARY) binary = 1, scan_token(csa); else xassert(csa != csa); /* parse list of variables (may be empty) */ while (csa->token == T_NAME) { /* find the corresponding column */ j = find_col(csa, csa->image); /* change kind of the variable */ glp_set_col_kind(csa->P, j, GLP_IV); /* set 0-1 bounds for the binary variable */ if (binary) { set_lower_bound(csa, j, 0.0); set_upper_bound(csa, j, 1.0); } scan_token(csa); } return; } int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname) { /* read problem data in CPLEX LP format */ glp_cpxcp _parm; struct csa _csa, *csa = &_csa; int ret; xprintf("Reading problem data from `%s'...\n", fname); if (parm == NULL) glp_init_cpxcp(&_parm), parm = &_parm; /* check control parameters */ check_parm("glp_read_lp", parm); /* initialize common storage area */ csa->P = P; csa->parm = parm; csa->fname = fname; csa->fp = NULL; if (setjmp(csa->jump)) { ret = 1; goto done; } csa->count = 0; csa->c = '\n'; csa->token = T_EOF; csa->image[0] = '\0'; csa->imlen = 0; csa->value = 0.0; csa->n_max = 100; csa->ind = xcalloc(1+csa->n_max, sizeof(int)); csa->val = xcalloc(1+csa->n_max, sizeof(double)); csa->flag = xcalloc(1+csa->n_max, sizeof(char)); memset(&csa->flag[1], 0, csa->n_max * sizeof(char)); csa->lb = xcalloc(1+csa->n_max, sizeof(double)); csa->ub = xcalloc(1+csa->n_max, sizeof(double)); /* erase problem object */ glp_erase_prob(P); glp_create_index(P); /* open input CPLEX LP file */ csa->fp = xfopen(fname, "r"); if (csa->fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* scan very first token */ scan_token(csa); /* parse definition of the objective function */ if (!(csa->token == T_MINIMIZE || csa->token == T_MAXIMIZE)) error(csa, "`minimize' or `maximize' keyword missing\n"); parse_objective(csa); /* parse constraints section */ if (csa->token != T_SUBJECT_TO) error(csa, "constraints section missing\n"); parse_constraints(csa); /* parse optional bounds section */ if (csa->token == T_BOUNDS) parse_bounds(csa); /* parse optional general, integer, and binary sections */ while (csa->token == T_GENERAL || csa->token == T_INTEGER || csa->token == T_BINARY) parse_integer(csa); /* check for the keyword 'end' */ if (csa->token == T_END) scan_token(csa); else if (csa->token == T_EOF) warning(csa, "keyword `end' missing\n"); else error(csa, "symbol `%s' in wrong position\n", csa->image); /* nothing must follow the keyword 'end' (except comments) */ if (csa->token != T_EOF) error(csa, "extra symbol(s) detected beyond `end'\n"); /* set bounds of variables */ { int j, type; double lb, ub; for (j = 1; j <= P->n; j++) { lb = csa->lb[j]; ub = csa->ub[j]; if (lb == +DBL_MAX) lb = 0.0; /* default lb */ if (ub == -DBL_MAX) ub = +DBL_MAX; /* default ub */ if (lb == -DBL_MAX && ub == +DBL_MAX) type = GLP_FR; else if (ub == +DBL_MAX) type = GLP_LO; else if (lb == -DBL_MAX) type = GLP_UP; else if (lb != ub) type = GLP_DB; else type = GLP_FX; glp_set_col_bnds(csa->P, j, type, lb, ub); } } /* print some statistics */ xprintf("%d row%s, %d column%s, %d non-zero%s\n", P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", P->nnz, P->nnz == 1 ? "" : "s"); if (glp_get_num_int(P) > 0) { int ni = glp_get_num_int(P); int nb = glp_get_num_bin(P); if (ni == 1) { if (nb == 0) xprintf("One variable is integer\n"); else xprintf("One variable is binary\n"); } else { xprintf("%d integer variables, ", ni); if (nb == 0) xprintf("none"); else if (nb == 1) xprintf("one"); else if (nb == ni) xprintf("all"); else xprintf("%d", nb); xprintf(" of which %s binary\n", nb == 1 ? "is" : "are"); } } xprintf("%d lines were read\n", csa->count); /* problem data has been successfully read */ glp_delete_index(P); glp_sort_matrix(P); ret = 0; done: if (csa->fp != NULL) xfclose(csa->fp); xfree(csa->ind); xfree(csa->val); xfree(csa->flag); xfree(csa->lb); xfree(csa->ub); if (ret != 0) glp_erase_prob(P); return ret; } /*********************************************************************** * NAME * * glp_write_lp - write problem data in CPLEX LP format * * SYNOPSIS * * int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char * *fname); * * DESCRIPTION * * The routine glp_write_lp writes problem data in CPLEX LP format to * a text file. * * The parameter parm is a pointer to the structure glp_cpxcp, which * specifies control parameters used by the routine. If parm is NULL, * the routine uses default settings. * * The character string fname specifies a name of the text file to be * written. * * RETURNS * * If the operation was successful, the routine glp_write_lp returns * zero. Otherwise, it prints an error message and returns non-zero. */ #define csa csa1 struct csa { /* common storage area */ glp_prob *P; /* pointer to problem object */ const glp_cpxcp *parm; /* pointer to control parameters */ }; static int check_name(char *name) { /* check if specified name is valid for CPLEX LP format */ if (*name == '.') return 1; if (isdigit((unsigned char)*name)) return 1; for (; *name; name++) { if (!isalnum((unsigned char)*name) && strchr(CHAR_SET, (unsigned char)*name) == NULL) return 1; } return 0; /* name is ok */ } static void adjust_name(char *name) { /* attempt to adjust specified name to make it valid for CPLEX LP format */ for (; *name; name++) { if (*name == ' ') *name = '_'; else if (*name == '-') *name = '~'; else if (*name == '[') *name = '('; else if (*name == ']') *name = ')'; } return; } static char *row_name(struct csa *csa, int i, char rname[255+1]) { /* construct symbolic name of i-th row (constraint) */ const char *name; if (i == 0) name = glp_get_obj_name(csa->P); else name = glp_get_row_name(csa->P, i); if (name == NULL) goto fake; strcpy(rname, name); adjust_name(rname); if (check_name(rname)) goto fake; return rname; fake: if (i == 0) strcpy(rname, "obj"); else sprintf(rname, "r_%d", i); return rname; } static char *col_name(struct csa *csa, int j, char cname[255+1]) { /* construct symbolic name of j-th column (variable) */ const char *name; name = glp_get_col_name(csa->P, j); if (name == NULL) goto fake; strcpy(cname, name); adjust_name(cname); if (check_name(cname)) goto fake; return cname; fake: sprintf(cname, "x_%d", j); return cname; } int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname) { /* write problem data in CPLEX LP format */ glp_cpxcp _parm; struct csa _csa, *csa = &_csa; XFILE *fp; GLPROW *row; GLPCOL *col; GLPAIJ *aij; int i, j, len, flag, count, ret; char line[1000+1], term[500+1], name[255+1]; xprintf("Writing problem data to `%s'...\n", fname); if (parm == NULL) glp_init_cpxcp(&_parm), parm = &_parm; /* check control parameters */ check_parm("glp_write_lp", parm); /* initialize common storage area */ csa->P = P; csa->parm = parm; /* create output CPLEX LP file */ fp = xfopen(fname, "w"), count = 0; if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* write problem name */ xfprintf(fp, "\\* Problem: %s *\\\n", P->name == NULL ? "Unknown" : P->name), count++; xfprintf(fp, "\n"), count++; /* the problem should contain at least one row and one column */ if (!(P->m > 0 && P->n > 0)) { xprintf("Warning: problem has no rows/columns\n"); xfprintf(fp, "\\* WARNING: PROBLEM HAS NO ROWS/COLUMNS *\\\n"), count++; xfprintf(fp, "\n"), count++; goto skip; } /* write the objective function definition */ if (P->dir == GLP_MIN) xfprintf(fp, "Minimize\n"), count++; else if (P->dir == GLP_MAX) xfprintf(fp, "Maximize\n"), count++; else xassert(P != P); row_name(csa, 0, name); sprintf(line, " %s:", name); len = 0; for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->coef != 0.0 || col->ptr == NULL) { len++; col_name(csa, j, name); if (col->coef == 0.0) sprintf(term, " + 0 %s", name); /* empty column */ else if (col->coef == +1.0) sprintf(term, " + %s", name); else if (col->coef == -1.0) sprintf(term, " - %s", name); else if (col->coef > 0.0) sprintf(term, " + %.*g %s", DBL_DIG, +col->coef, name); else sprintf(term, " - %.*g %s", DBL_DIG, -col->coef, name); if (strlen(line) + strlen(term) > 72) xfprintf(fp, "%s\n", line), line[0] = '\0', count++; strcat(line, term); } } if (len == 0) { /* empty objective */ sprintf(term, " 0 %s", col_name(csa, 1, name)); strcat(line, term); } xfprintf(fp, "%s\n", line), count++; if (P->c0 != 0.0) xfprintf(fp, "\\* constant term = %.*g *\\\n", DBL_DIG, P->c0), count++; xfprintf(fp, "\n"), count++; /* write the constraints section */ xfprintf(fp, "Subject To\n"), count++; for (i = 1; i <= P->m; i++) { row = P->row[i]; if (row->type == GLP_FR) continue; /* skip free row */ row_name(csa, i, name); sprintf(line, " %s:", name); /* linear form */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) { col_name(csa, aij->col->j, name); if (aij->val == +1.0) sprintf(term, " + %s", name); else if (aij->val == -1.0) sprintf(term, " - %s", name); else if (aij->val > 0.0) sprintf(term, " + %.*g %s", DBL_DIG, +aij->val, name); else sprintf(term, " - %.*g %s", DBL_DIG, -aij->val, name); if (strlen(line) + strlen(term) > 72) xfprintf(fp, "%s\n", line), line[0] = '\0', count++; strcat(line, term); } if (row->type == GLP_DB) { /* double-bounded (ranged) constraint */ sprintf(term, " - ~r_%d", i); if (strlen(line) + strlen(term) > 72) xfprintf(fp, "%s\n", line), line[0] = '\0', count++; strcat(line, term); } else if (row->ptr == NULL) { /* empty constraint */ sprintf(term, " 0 %s", col_name(csa, 1, name)); strcat(line, term); } /* right hand-side */ if (row->type == GLP_LO) sprintf(term, " >= %.*g", DBL_DIG, row->lb); else if (row->type == GLP_UP) sprintf(term, " <= %.*g", DBL_DIG, row->ub); else if (row->type == GLP_DB || row->type == GLP_FX) sprintf(term, " = %.*g", DBL_DIG, row->lb); else xassert(row != row); if (strlen(line) + strlen(term) > 72) xfprintf(fp, "%s\n", line), line[0] = '\0', count++; strcat(line, term); xfprintf(fp, "%s\n", line), count++; } xfprintf(fp, "\n"), count++; /* write the bounds section */ flag = 0; for (i = 1; i <= P->m; i++) { row = P->row[i]; if (row->type != GLP_DB) continue; if (!flag) xfprintf(fp, "Bounds\n"), flag = 1, count++; xfprintf(fp, " 0 <= ~r_%d <= %.*g\n", i, DBL_DIG, row->ub - row->lb), count++; } for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->type == GLP_LO && col->lb == 0.0) continue; if (!flag) xfprintf(fp, "Bounds\n"), flag = 1, count++; col_name(csa, j, name); if (col->type == GLP_FR) xfprintf(fp, " %s free\n", name), count++; else if (col->type == GLP_LO) xfprintf(fp, " %s >= %.*g\n", name, DBL_DIG, col->lb), count++; else if (col->type == GLP_UP) xfprintf(fp, " -Inf <= %s <= %.*g\n", name, DBL_DIG, col->ub), count++; else if (col->type == GLP_DB) xfprintf(fp, " %.*g <= %s <= %.*g\n", DBL_DIG, col->lb, name, DBL_DIG, col->ub), count++; else if (col->type == GLP_FX) xfprintf(fp, " %s = %.*g\n", name, DBL_DIG, col->lb), count++; else xassert(col != col); } if (flag) xfprintf(fp, "\n"), count++; /* write the integer section */ flag = 0; for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->kind == GLP_CV) continue; xassert(col->kind == GLP_IV); if (!flag) xfprintf(fp, "Generals\n"), flag = 1, count++; xfprintf(fp, " %s\n", col_name(csa, j, name)), count++; } if (flag) xfprintf(fp, "\n"), count++; skip: /* write the end keyword */ xfprintf(fp, "End\n"), count++; xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* problem data has been successfully written */ xprintf("%d lines were written\n", count); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /* eof */ igraph/src/bliss_bignum.hh0000644000176000001440000000316312325372072015347 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_BIGNUM_HH #define BLISS_BIGNUM_HH #include #include #include #include "bliss_defs.hh" #include "igraph_math.h" #include "igraph_memory.h" #include "igraph_error.h" /* * Simple class for big integers (or approximation of such) in order * compute group sizes. * Set BLISS_USE_GMP in defs.hh to use the GMP library. */ #if defined(BLISS_USE_GMP) #include namespace igraph { class BigNum { mpz_t v; public: BigNum() {mpz_init(v); } ~BigNum() {mpz_clear(v); } void assign(const int n) {mpz_set_si(v, n); } void multiply(const int n) {mpz_mul_si(v, v, n); } int tostring(char **str); }; } #else namespace igraph { class BigNum { long double v; public: BigNum(): v(0.0) {} void assign(const int n) {v = (long double)n; } void multiply(const int n) {v *= (long double)n; } int tostring(char **str); }; } #endif #endif igraph/src/amd_valid.c0000644000176000001440000000651512325527072014443 0ustar ripleyusers/* ========================================================================= */ /* === AMD_valid =========================================================== */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* Check if a column-form matrix is valid or not. The matrix A is * n_row-by-n_col. The row indices of entries in column j are in * Ai [Ap [j] ... Ap [j+1]-1]. Required conditions are: * * n_row >= 0 * n_col >= 0 * nz = Ap [n_col] >= 0 number of entries in the matrix * Ap [0] == 0 * Ap [j] <= Ap [j+1] for all j in the range 0 to n_col. * Ai [0 ... nz-1] must be in the range 0 to n_row-1. * * If any of the above conditions hold, AMD_INVALID is returned. If the * following condition holds, AMD_OK_BUT_JUMBLED is returned (a warning, * not an error): * * row indices in Ai [Ap [j] ... Ap [j+1]-1] are not sorted in ascending * order, and/or duplicate entries exist. * * Otherwise, AMD_OK is returned. * * In v1.2 and earlier, this function returned TRUE if the matrix was valid * (now returns AMD_OK), or FALSE otherwise (now returns AMD_INVALID or * AMD_OK_BUT_JUMBLED). */ #include "amd_internal.h" GLOBAL Int AMD_valid ( /* inputs, not modified on output: */ Int n_row, /* A is n_row-by-n_col */ Int n_col, const Int Ap [ ], /* column pointers of A, of size n_col+1 */ const Int Ai [ ] /* row indices of A, of size nz = Ap [n_col] */ ) { Int nz, j, p1, p2, ilast, i, p, result = AMD_OK ; if (n_row < 0 || n_col < 0 || Ap == NULL || Ai == NULL) { return (AMD_INVALID) ; } nz = Ap [n_col] ; if (Ap [0] != 0 || nz < 0) { /* column pointers must start at Ap [0] = 0, and Ap [n] must be >= 0 */ AMD_DEBUG0 (("column 0 pointer bad or nz < 0\n")) ; return (AMD_INVALID) ; } for (j = 0 ; j < n_col ; j++) { p1 = Ap [j] ; p2 = Ap [j+1] ; AMD_DEBUG2 (("\nColumn: "ID" p1: "ID" p2: "ID"\n", j, p1, p2)) ; if (p1 > p2) { /* column pointers must be ascending */ AMD_DEBUG0 (("column "ID" pointer bad\n", j)) ; return (AMD_INVALID) ; } ilast = EMPTY ; for (p = p1 ; p < p2 ; p++) { i = Ai [p] ; AMD_DEBUG3 (("row: "ID"\n", i)) ; if (i < 0 || i >= n_row) { /* row index out of range */ AMD_DEBUG0 (("index out of range, col "ID" row "ID"\n", j, i)); return (AMD_INVALID) ; } if (i <= ilast) { /* row index unsorted, or duplicate entry present */ AMD_DEBUG1 (("index unsorted/dupl col "ID" row "ID"\n", j, i)); result = AMD_OK_BUT_JUMBLED ; } ilast = i ; } } return (result) ; } igraph/src/igraph_strvector.h0000644000176000001440000000710012325527073016105 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_STRVECTOR_H #define IGRAPH_STRVECTOR_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_vector.h" __BEGIN_DECLS /** * Vector of strings * \ingroup internal */ typedef struct s_igraph_strvector { char **data; long int len; } igraph_strvector_t; /** * \define STR * Indexing string vectors * * This is a macro which allows to query the elements of a string vector in * simpler way than \ref igraph_strvector_get(). Note this macro cannot be * used to set an element, for that use \ref igraph_strvector_set(). * \param sv The string vector * \param i The the index of the element. * \return The element at position \p i. * * Time complexity: O(1). */ #define STR(sv,i) ((const char *)((sv).data[(i)])) #define IGRAPH_STRVECTOR_NULL { 0,0 } #define IGRAPH_STRVECTOR_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_strvector_init(v, size)); \ IGRAPH_FINALLY( (igraph_finally_func_t*) igraph_strvector_destroy, v); } while (0) int igraph_strvector_init(igraph_strvector_t *sv, long int len); void igraph_strvector_destroy(igraph_strvector_t *sv); long int igraph_strvector_size(const igraph_strvector_t *sv); void igraph_strvector_get(const igraph_strvector_t *sv, long int idx, char **value); int igraph_strvector_set(igraph_strvector_t *sv, long int idx, const char *value); int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, const char *value, int len); void igraph_strvector_clear(igraph_strvector_t *sv); void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, long int to); void igraph_strvector_remove(igraph_strvector_t *v, long int elem); void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, long int end, long int to); int igraph_strvector_copy(igraph_strvector_t *to, const igraph_strvector_t *from); int igraph_strvector_append(igraph_strvector_t *to, const igraph_strvector_t *from); int igraph_strvector_resize(igraph_strvector_t* v, long int newsize); int igraph_strvector_add(igraph_strvector_t *v, const char *value); void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, long int nremove); void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg, long int nremove); int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, const char *sep); int igraph_strvector_index(const igraph_strvector_t *v, igraph_strvector_t *newv, const igraph_vector_t *idx); __END_DECLS #endif igraph/src/glpdmx.c0000644000176000001440000014314612325527073014021 0ustar ripleyusers/* glpdmx.c (reading/writing data in DIMACS format) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #define _GLPSTD_STDIO #include "glpapi.h" struct csa { /* common storage area */ jmp_buf jump; /* label for go to in case of error */ const char *fname; /* name of input text file */ XFILE *fp; /* stream assigned to input text file */ int count; /* line count */ int c; /* current character */ char field[255+1]; /* data field */ int empty; /* warning 'empty line ignored' was printed */ int nonint; /* warning 'non-integer data detected' was printed */ }; static void error(struct csa *csa, const char *fmt, ...) { /* print error message and terminate processing */ va_list arg; xprintf("%s:%d: error: ", csa->fname, csa->count); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); xprintf("\n"); longjmp(csa->jump, 1); /* no return */ } static void warning(struct csa *csa, const char *fmt, ...) { /* print warning message and continue processing */ va_list arg; xprintf("%s:%d: warning: ", csa->fname, csa->count); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); xprintf("\n"); return; } static void read_char(struct csa *csa) { /* read character from input text file */ int c; if (csa->c == '\n') csa->count++; c = xfgetc(csa->fp); if (c < 0) { if (xferror(csa->fp)) error(csa, "read error - %s", xerrmsg()); else if (csa->c == '\n') error(csa, "unexpected end of file"); else { warning(csa, "missing final end of line"); c = '\n'; } } else if (c == '\n') ; else if (isspace(c)) c = ' '; else if (iscntrl(c)) error(csa, "invalid control character 0x%02X", c); csa->c = c; return; } static void read_designator(struct csa *csa) { /* read one-character line designator */ xassert(csa->c == '\n'); read_char(csa); for (;;) { /* skip preceding white-space characters */ while (csa->c == ' ') read_char(csa); if (csa->c == '\n') { /* ignore empty line */ if (!csa->empty) { warning(csa, "empty line ignored"); csa->empty = 1; } read_char(csa); } else if (csa->c == 'c') { /* skip comment line */ while (csa->c != '\n') read_char(csa); read_char(csa); } else { /* hmm... looks like a line designator */ csa->field[0] = (char)csa->c, csa->field[1] = '\0'; /* check that it is followed by a white-space character */ read_char(csa); if (!(csa->c == ' ' || csa->c == '\n')) error(csa, "line designator missing or invalid"); break; } } return; } static void read_field(struct csa *csa) { /* read data field */ int len = 0; /* skip preceding white-space characters */ while (csa->c == ' ') read_char(csa); /* scan data field */ if (csa->c == '\n') error(csa, "unexpected end of line"); while (!(csa->c == ' ' || csa->c == '\n')) { if (len == sizeof(csa->field)-1) error(csa, "data field `%.15s...' too long", csa->field); csa->field[len++] = (char)csa->c; read_char(csa); } csa->field[len] = '\0'; return; } static void end_of_line(struct csa *csa) { /* skip white-space characters until end of line */ while (csa->c == ' ') read_char(csa); if (csa->c != '\n') error(csa, "too many data fields specified"); return; } static void check_int(struct csa *csa, double num) { /* print a warning if non-integer data are detected */ if (!csa->nonint && num != floor(num)) { warning(csa, "non-integer data detected"); csa->nonint = 1; } return; } /*********************************************************************** * NAME * * glp_read_mincost - read min-cost flow problem data in DIMACS format * * SYNOPSIS * * int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, * int a_cost, const char *fname); * * DESCRIPTION * * The routine glp_read_mincost reads minimum cost flow problem data in * DIMACS format from a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, int a_cost, const char *fname) { struct csa _csa, *csa = &_csa; glp_vertex *v; glp_arc *a; int i, j, k, nv, na, ret = 0; double rhs, low, cap, cost; char *flag = NULL; if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) xerror("glp_read_mincost: v_rhs = %d; invalid offset\n", v_rhs); if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) xerror("glp_read_mincost: a_low = %d; invalid offset\n", a_low); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_read_mincost: a_cap = %d; invalid offset\n", a_cap); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_read_mincost: a_cost = %d; invalid offset\n", a_cost); glp_erase_graph(G, G->v_size, G->a_size); if (setjmp(csa->jump)) { ret = 1; goto done; } csa->fname = fname; csa->fp = NULL; csa->count = 0; csa->c = '\n'; csa->field[0] = '\0'; csa->empty = csa->nonint = 0; xprintf("Reading min-cost flow problem data from `%s'...\n", fname); csa->fp = xfopen(fname, "r"); if (csa->fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); longjmp(csa->jump, 1); } /* read problem line */ read_designator(csa); if (strcmp(csa->field, "p") != 0) error(csa, "problem line missing or invalid"); read_field(csa); if (strcmp(csa->field, "min") != 0) error(csa, "wrong problem designator; `min' expected"); read_field(csa); if (!(str2int(csa->field, &nv) == 0 && nv >= 0)) error(csa, "number of nodes missing or invalid"); read_field(csa); if (!(str2int(csa->field, &na) == 0 && na >= 0)) error(csa, "number of arcs missing or invalid"); xprintf("Flow network has %d node%s and %d arc%s\n", nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s"); if (nv > 0) glp_add_vertices(G, nv); end_of_line(csa); /* read node descriptor lines */ flag = xcalloc(1+nv, sizeof(char)); memset(&flag[1], 0, nv * sizeof(char)); if (v_rhs >= 0) { rhs = 0.0; for (i = 1; i <= nv; i++) { v = G->v[i]; memcpy((char *)v->data + v_rhs, &rhs, sizeof(double)); } } for (;;) { read_designator(csa); if (strcmp(csa->field, "n") != 0) break; read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "node number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "node number %d out of range", i); if (flag[i]) error(csa, "duplicate descriptor of node %d", i); read_field(csa); if (str2num(csa->field, &rhs) != 0) error(csa, "node supply/demand missing or invalid"); check_int(csa, rhs); if (v_rhs >= 0) { v = G->v[i]; memcpy((char *)v->data + v_rhs, &rhs, sizeof(double)); } flag[i] = 1; end_of_line(csa); } xfree(flag), flag = NULL; /* read arc descriptor lines */ for (k = 1; k <= na; k++) { if (k > 1) read_designator(csa); if (strcmp(csa->field, "a") != 0) error(csa, "wrong line designator; `a' expected"); read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "starting node number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "starting node number %d out of range", i); read_field(csa); if (str2int(csa->field, &j) != 0) error(csa, "ending node number missing or invalid"); if (!(1 <= j && j <= nv)) error(csa, "ending node number %d out of range", j); read_field(csa); if (!(str2num(csa->field, &low) == 0 && low >= 0.0)) error(csa, "lower bound of arc flow missing or invalid"); check_int(csa, low); read_field(csa); if (!(str2num(csa->field, &cap) == 0 && cap >= low)) error(csa, "upper bound of arc flow missing or invalid"); check_int(csa, cap); read_field(csa); if (str2num(csa->field, &cost) != 0) error(csa, "per-unit cost of arc flow missing or invalid"); check_int(csa, cost); a = glp_add_arc(G, i, j); if (a_low >= 0) memcpy((char *)a->data + a_low, &low, sizeof(double)); if (a_cap >= 0) memcpy((char *)a->data + a_cap, &cap, sizeof(double)); if (a_cost >= 0) memcpy((char *)a->data + a_cost, &cost, sizeof(double)); end_of_line(csa); } xprintf("%d lines were read\n", csa->count); done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); if (csa->fp != NULL) xfclose(csa->fp); if (flag != NULL) xfree(flag); return ret; } /*********************************************************************** * NAME * * glp_write_mincost - write min-cost flow problem data in DIMACS format * * SYNOPSIS * * int glp_write_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, * int a_cost, const char *fname); * * DESCRIPTION * * The routine glp_write_mincost writes minimum cost flow problem data * in DIMACS format to a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_write_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, int a_cost, const char *fname) { XFILE *fp; glp_vertex *v; glp_arc *a; int i, count = 0, ret; double rhs, low, cap, cost; if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) xerror("glp_write_mincost: v_rhs = %d; invalid offset\n", v_rhs); if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) xerror("glp_write_mincost: a_low = %d; invalid offset\n", a_low); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_write_mincost: a_cap = %d; invalid offset\n", a_cap); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_write_mincost: a_cost = %d; invalid offset\n", a_cost); xprintf("Writing min-cost flow problem data to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "c %s\n", G->name == NULL ? "unknown" : G->name), count++; xfprintf(fp, "p min %d %d\n", G->nv, G->na), count++; if (v_rhs >= 0) { for (i = 1; i <= G->nv; i++) { v = G->v[i]; memcpy(&rhs, (char *)v->data + v_rhs, sizeof(double)); if (rhs != 0.0) xfprintf(fp, "n %d %.*g\n", i, DBL_DIG, rhs), count++; } } for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { if (a_low >= 0) memcpy(&low, (char *)a->data + a_low, sizeof(double)); else low = 0.0; if (a_cap >= 0) memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); else cap = 1.0; if (a_cost >= 0) memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); else cost = 0.0; xfprintf(fp, "a %d %d %.*g %.*g %.*g\n", a->tail->i, a->head->i, DBL_DIG, low, DBL_DIG, cap, DBL_DIG, cost), count++; } } xfprintf(fp, "c eof\n"), count++; xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", count); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /*********************************************************************** * NAME * * glp_read_maxflow - read maximum flow problem data in DIMACS format * * SYNOPSIS * * int glp_read_maxflow(glp_graph *G, int *s, int *t, int a_cap, * const char *fname); * * DESCRIPTION * * The routine glp_read_maxflow reads maximum flow problem data in * DIMACS format from a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_read_maxflow(glp_graph *G, int *_s, int *_t, int a_cap, const char *fname) { struct csa _csa, *csa = &_csa; glp_arc *a; int i, j, k, s, t, nv, na, ret = 0; double cap; if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_read_maxflow: a_cap = %d; invalid offset\n", a_cap); glp_erase_graph(G, G->v_size, G->a_size); if (setjmp(csa->jump)) { ret = 1; goto done; } csa->fname = fname; csa->fp = NULL; csa->count = 0; csa->c = '\n'; csa->field[0] = '\0'; csa->empty = csa->nonint = 0; xprintf("Reading maximum flow problem data from `%s'...\n", fname); csa->fp = xfopen(fname, "r"); if (csa->fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); longjmp(csa->jump, 1); } /* read problem line */ read_designator(csa); if (strcmp(csa->field, "p") != 0) error(csa, "problem line missing or invalid"); read_field(csa); if (strcmp(csa->field, "max") != 0) error(csa, "wrong problem designator; `max' expected"); read_field(csa); if (!(str2int(csa->field, &nv) == 0 && nv >= 2)) error(csa, "number of nodes missing or invalid"); read_field(csa); if (!(str2int(csa->field, &na) == 0 && na >= 0)) error(csa, "number of arcs missing or invalid"); xprintf("Flow network has %d node%s and %d arc%s\n", nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s"); if (nv > 0) glp_add_vertices(G, nv); end_of_line(csa); /* read node descriptor lines */ s = t = 0; for (;;) { read_designator(csa); if (strcmp(csa->field, "n") != 0) break; read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "node number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "node number %d out of range", i); read_field(csa); if (strcmp(csa->field, "s") == 0) { if (s > 0) error(csa, "only one source node allowed"); s = i; } else if (strcmp(csa->field, "t") == 0) { if (t > 0) error(csa, "only one sink node allowed"); t = i; } else error(csa, "wrong node designator; `s' or `t' expected"); if (s > 0 && s == t) error(csa, "source and sink nodes must be distinct"); end_of_line(csa); } if (s == 0) error(csa, "source node descriptor missing\n"); if (t == 0) error(csa, "sink node descriptor missing\n"); if (_s != NULL) *_s = s; if (_t != NULL) *_t = t; /* read arc descriptor lines */ for (k = 1; k <= na; k++) { if (k > 1) read_designator(csa); if (strcmp(csa->field, "a") != 0) error(csa, "wrong line designator; `a' expected"); read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "starting node number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "starting node number %d out of range", i); read_field(csa); if (str2int(csa->field, &j) != 0) error(csa, "ending node number missing or invalid"); if (!(1 <= j && j <= nv)) error(csa, "ending node number %d out of range", j); read_field(csa); if (!(str2num(csa->field, &cap) == 0 && cap >= 0.0)) error(csa, "arc capacity missing or invalid"); check_int(csa, cap); a = glp_add_arc(G, i, j); if (a_cap >= 0) memcpy((char *)a->data + a_cap, &cap, sizeof(double)); end_of_line(csa); } xprintf("%d lines were read\n", csa->count); done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); if (csa->fp != NULL) xfclose(csa->fp); return ret; } /*********************************************************************** * NAME * * glp_write_maxflow - write maximum flow problem data in DIMACS format * * SYNOPSIS * * int glp_write_maxflow(glp_graph *G, int s, int t, int a_cap, * const char *fname); * * DESCRIPTION * * The routine glp_write_maxflow writes maximum flow problem data in * DIMACS format to a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_write_maxflow(glp_graph *G, int s, int t, int a_cap, const char *fname) { XFILE *fp; glp_vertex *v; glp_arc *a; int i, count = 0, ret; double cap; if (!(1 <= s && s <= G->nv)) xerror("glp_write_maxflow: s = %d; source node number out of r" "ange\n", s); if (!(1 <= t && t <= G->nv)) xerror("glp_write_maxflow: t = %d: sink node number out of ran" "ge\n", t); if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) xerror("glp_write_mincost: a_cap = %d; invalid offset\n", a_cap); xprintf("Writing maximum flow problem data to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "c %s\n", G->name == NULL ? "unknown" : G->name), count++; xfprintf(fp, "p max %d %d\n", G->nv, G->na), count++; xfprintf(fp, "n %d s\n", s), count++; xfprintf(fp, "n %d t\n", t), count++; for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { if (a_cap >= 0) memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); else cap = 1.0; xfprintf(fp, "a %d %d %.*g\n", a->tail->i, a->head->i, DBL_DIG, cap), count++; } } xfprintf(fp, "c eof\n"), count++; xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", count); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /*********************************************************************** * NAME * * glp_read_asnprob - read assignment problem data in DIMACS format * * SYNOPSIS * * int glp_read_asnprob(glp_graph *G, int v_set, int a_cost, * const char *fname); * * DESCRIPTION * * The routine glp_read_asnprob reads assignment problem data in DIMACS * format from a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_read_asnprob(glp_graph *G, int v_set, int a_cost, const char *fname) { struct csa _csa, *csa = &_csa; glp_vertex *v; glp_arc *a; int nv, na, n1, i, j, k, ret = 0; double cost; char *flag = NULL; if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) xerror("glp_read_asnprob: v_set = %d; invalid offset\n", v_set); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_read_asnprob: a_cost = %d; invalid offset\n", a_cost); glp_erase_graph(G, G->v_size, G->a_size); if (setjmp(csa->jump)) { ret = 1; goto done; } csa->fname = fname; csa->fp = NULL; csa->count = 0; csa->c = '\n'; csa->field[0] = '\0'; csa->empty = csa->nonint = 0; xprintf("Reading assignment problem data from `%s'...\n", fname); csa->fp = xfopen(fname, "r"); if (csa->fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); longjmp(csa->jump, 1); } /* read problem line */ read_designator(csa); if (strcmp(csa->field, "p") != 0) error(csa, "problem line missing or invalid"); read_field(csa); if (strcmp(csa->field, "asn") != 0) error(csa, "wrong problem designator; `asn' expected"); read_field(csa); if (!(str2int(csa->field, &nv) == 0 && nv >= 0)) error(csa, "number of nodes missing or invalid"); read_field(csa); if (!(str2int(csa->field, &na) == 0 && na >= 0)) error(csa, "number of arcs missing or invalid"); if (nv > 0) glp_add_vertices(G, nv); end_of_line(csa); /* read node descriptor lines */ flag = xcalloc(1+nv, sizeof(char)); memset(&flag[1], 0, nv * sizeof(char)); n1 = 0; for (;;) { read_designator(csa); if (strcmp(csa->field, "n") != 0) break; read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "node number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "node number %d out of range", i); if (flag[i]) error(csa, "duplicate descriptor of node %d", i); flag[i] = 1, n1++; end_of_line(csa); } xprintf( "Assignment problem has %d + %d = %d node%s and %d arc%s\n", n1, nv - n1, nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s"); if (v_set >= 0) { for (i = 1; i <= nv; i++) { v = G->v[i]; k = (flag[i] ? 0 : 1); memcpy((char *)v->data + v_set, &k, sizeof(int)); } } /* read arc descriptor lines */ for (k = 1; k <= na; k++) { if (k > 1) read_designator(csa); if (strcmp(csa->field, "a") != 0) error(csa, "wrong line designator; `a' expected"); read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "starting node number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "starting node number %d out of range", i); if (!flag[i]) error(csa, "node %d cannot be a starting node", i); read_field(csa); if (str2int(csa->field, &j) != 0) error(csa, "ending node number missing or invalid"); if (!(1 <= j && j <= nv)) error(csa, "ending node number %d out of range", j); if (flag[j]) error(csa, "node %d cannot be an ending node", j); read_field(csa); if (str2num(csa->field, &cost) != 0) error(csa, "arc cost missing or invalid"); check_int(csa, cost); a = glp_add_arc(G, i, j); if (a_cost >= 0) memcpy((char *)a->data + a_cost, &cost, sizeof(double)); end_of_line(csa); } xprintf("%d lines were read\n", csa->count); done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); if (csa->fp != NULL) xfclose(csa->fp); if (flag != NULL) xfree(flag); return ret; } /*********************************************************************** * NAME * * glp_write_asnprob - write assignment problem data in DIMACS format * * SYNOPSIS * * int glp_write_asnprob(glp_graph *G, int v_set, int a_cost, * const char *fname); * * DESCRIPTION * * The routine glp_write_asnprob writes assignment problem data in * DIMACS format to a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_write_asnprob(glp_graph *G, int v_set, int a_cost, const char *fname) { XFILE *fp; glp_vertex *v; glp_arc *a; int i, k, count = 0, ret; double cost; if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) xerror("glp_write_asnprob: v_set = %d; invalid offset\n", v_set); if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) xerror("glp_write_asnprob: a_cost = %d; invalid offset\n", a_cost); xprintf("Writing assignment problem data to `%s'...\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "c %s\n", G->name == NULL ? "unknown" : G->name), count++; xfprintf(fp, "p asn %d %d\n", G->nv, G->na), count++; for (i = 1; i <= G->nv; i++) { v = G->v[i]; if (v_set >= 0) memcpy(&k, (char *)v->data + v_set, sizeof(int)); else k = (v->out != NULL ? 0 : 1); if (k == 0) xfprintf(fp, "n %d\n", i), count++; } for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) { if (a_cost >= 0) memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); else cost = 1.0; xfprintf(fp, "a %d %d %.*g\n", a->tail->i, a->head->i, DBL_DIG, cost), count++; } } xfprintf(fp, "c eof\n"), count++; xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", count); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /*********************************************************************** * NAME * * glp_read_ccdata - read graph in DIMACS clique/coloring format * * SYNOPSIS * * int glp_read_ccdata(glp_graph *G, int v_wgt, const char *fname); * * DESCRIPTION * * The routine glp_read_ccdata reads an (undirected) graph in DIMACS * clique/coloring format from a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_read_ccdata(glp_graph *G, int v_wgt, const char *fname) { struct csa _csa, *csa = &_csa; glp_vertex *v; int i, j, k, nv, ne, ret = 0; double w; char *flag = NULL; if (v_wgt >= 0 && v_wgt > G->v_size - (int)sizeof(double)) xerror("glp_read_ccdata: v_wgt = %d; invalid offset\n", v_wgt); glp_erase_graph(G, G->v_size, G->a_size); if (setjmp(csa->jump)) { ret = 1; goto done; } csa->fname = fname; csa->fp = NULL; csa->count = 0; csa->c = '\n'; csa->field[0] = '\0'; csa->empty = csa->nonint = 0; xprintf("Reading graph from `%s'...\n", fname); csa->fp = xfopen(fname, "r"); if (csa->fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); longjmp(csa->jump, 1); } /* read problem line */ read_designator(csa); if (strcmp(csa->field, "p") != 0) error(csa, "problem line missing or invalid"); read_field(csa); if (strcmp(csa->field, "edge") != 0) error(csa, "wrong problem designator; `edge' expected"); read_field(csa); if (!(str2int(csa->field, &nv) == 0 && nv >= 0)) error(csa, "number of vertices missing or invalid"); read_field(csa); if (!(str2int(csa->field, &ne) == 0 && ne >= 0)) error(csa, "number of edges missing or invalid"); xprintf("Graph has %d vert%s and %d edge%s\n", nv, nv == 1 ? "ex" : "ices", ne, ne == 1 ? "" : "s"); if (nv > 0) glp_add_vertices(G, nv); end_of_line(csa); /* read node descriptor lines */ flag = xcalloc(1+nv, sizeof(char)); memset(&flag[1], 0, nv * sizeof(char)); if (v_wgt >= 0) { w = 1.0; for (i = 1; i <= nv; i++) { v = G->v[i]; memcpy((char *)v->data + v_wgt, &w, sizeof(double)); } } for (;;) { read_designator(csa); if (strcmp(csa->field, "n") != 0) break; read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "vertex number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "vertex number %d out of range", i); if (flag[i]) error(csa, "duplicate descriptor of vertex %d", i); read_field(csa); if (str2num(csa->field, &w) != 0) error(csa, "vertex weight missing or invalid"); check_int(csa, w); if (v_wgt >= 0) { v = G->v[i]; memcpy((char *)v->data + v_wgt, &w, sizeof(double)); } flag[i] = 1; end_of_line(csa); } xfree(flag), flag = NULL; /* read edge descriptor lines */ for (k = 1; k <= ne; k++) { if (k > 1) read_designator(csa); if (strcmp(csa->field, "e") != 0) error(csa, "wrong line designator; `e' expected"); read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "first vertex number missing or invalid"); if (!(1 <= i && i <= nv)) error(csa, "first vertex number %d out of range", i); read_field(csa); if (str2int(csa->field, &j) != 0) error(csa, "second vertex number missing or invalid"); if (!(1 <= j && j <= nv)) error(csa, "second vertex number %d out of range", j); glp_add_arc(G, i, j); end_of_line(csa); } xprintf("%d lines were read\n", csa->count); done: if (ret) glp_erase_graph(G, G->v_size, G->a_size); if (csa->fp != NULL) xfclose(csa->fp); if (flag != NULL) xfree(flag); return ret; } /*********************************************************************** * NAME * * glp_write_ccdata - write graph in DIMACS clique/coloring format * * SYNOPSIS * * int glp_write_ccdata(glp_graph *G, int v_wgt, const char *fname); * * DESCRIPTION * * The routine glp_write_ccdata writes the specified graph in DIMACS * clique/coloring format to a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_write_ccdata(glp_graph *G, int v_wgt, const char *fname) { XFILE *fp; glp_vertex *v; glp_arc *e; int i, count = 0, ret; double w; if (v_wgt >= 0 && v_wgt > G->v_size - (int)sizeof(double)) xerror("glp_write_ccdata: v_wgt = %d; invalid offset\n", v_wgt); xprintf("Writing graph to `%s'\n", fname); fp = xfopen(fname, "w"); if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "c %s\n", G->name == NULL ? "unknown" : G->name), count++; xfprintf(fp, "p edge %d %d\n", G->nv, G->na), count++; if (v_wgt >= 0) { for (i = 1; i <= G->nv; i++) { v = G->v[i]; memcpy(&w, (char *)v->data + v_wgt, sizeof(double)); if (w != 1.0) xfprintf(fp, "n %d %.*g\n", i, DBL_DIG, w), count++; } } for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (e = v->out; e != NULL; e = e->t_next) xfprintf(fp, "e %d %d\n", e->tail->i, e->head->i), count++; } xfprintf(fp, "c eof\n"), count++; xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", count); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /*********************************************************************** * NAME * * glp_read_prob - read problem data in GLPK format * * SYNOPSIS * * int glp_read_prob(glp_prob *P, int flags, const char *fname); * * The routine glp_read_prob reads problem data in GLPK LP/MIP format * from a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_read_prob(glp_prob *P, int flags, const char *fname) { struct csa _csa, *csa = &_csa; int mip, m, n, nnz, ne, i, j, k, type, kind, ret, *ln = NULL, *ia = NULL, *ja = NULL; double lb, ub, temp, *ar = NULL; char *rf = NULL, *cf = NULL; if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_read_prob: P = %p; invalid problem object\n", P); if (flags != 0) xerror("glp_read_prob: flags = %d; invalid parameter\n", flags); if (fname == NULL) xerror("glp_read_prob: fname = %d; invalid parameter\n", fname); glp_erase_prob(P); if (setjmp(csa->jump)) { ret = 1; goto done; } csa->fname = fname; csa->fp = NULL; csa->count = 0; csa->c = '\n'; csa->field[0] = '\0'; csa->empty = csa->nonint = 0; xprintf("Reading problem data from `%s'...\n", fname); csa->fp = xfopen(fname, "r"); if (csa->fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); longjmp(csa->jump, 1); } /* read problem line */ read_designator(csa); if (strcmp(csa->field, "p") != 0) error(csa, "problem line missing or invalid"); read_field(csa); if (strcmp(csa->field, "lp") == 0) mip = 0; else if (strcmp(csa->field, "mip") == 0) mip = 1; else error(csa, "wrong problem designator; `lp' or `mip' expected\n" ); read_field(csa); if (strcmp(csa->field, "min") == 0) glp_set_obj_dir(P, GLP_MIN); else if (strcmp(csa->field, "max") == 0) glp_set_obj_dir(P, GLP_MAX); else error(csa, "objective sense missing or invalid"); read_field(csa); if (!(str2int(csa->field, &m) == 0 && m >= 0)) error(csa, "number of rows missing or invalid"); read_field(csa); if (!(str2int(csa->field, &n) == 0 && n >= 0)) error(csa, "number of columns missing or invalid"); read_field(csa); if (!(str2int(csa->field, &nnz) == 0 && nnz >= 0)) error(csa, "number of constraint coefficients missing or inval" "id"); if (m > 0) { glp_add_rows(P, m); for (i = 1; i <= m; i++) glp_set_row_bnds(P, i, GLP_FX, 0.0, 0.0); } if (n > 0) { glp_add_cols(P, n); for (j = 1; j <= n; j++) { if (!mip) glp_set_col_bnds(P, j, GLP_LO, 0.0, 0.0); else glp_set_col_kind(P, j, GLP_BV); } } end_of_line(csa); /* allocate working arrays */ rf = xcalloc(1+m, sizeof(char)); memset(rf, 0, 1+m); cf = xcalloc(1+n, sizeof(char)); memset(cf, 0, 1+n); ln = xcalloc(1+nnz, sizeof(int)); ia = xcalloc(1+nnz, sizeof(int)); ja = xcalloc(1+nnz, sizeof(int)); ar = xcalloc(1+nnz, sizeof(double)); /* read descriptor lines */ ne = 0; for (;;) { read_designator(csa); if (strcmp(csa->field, "i") == 0) { /* row descriptor */ read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "row number missing or invalid"); if (!(1 <= i && i <= m)) error(csa, "row number out of range"); read_field(csa); if (strcmp(csa->field, "f") == 0) type = GLP_FR; else if (strcmp(csa->field, "l") == 0) type = GLP_LO; else if (strcmp(csa->field, "u") == 0) type = GLP_UP; else if (strcmp(csa->field, "d") == 0) type = GLP_DB; else if (strcmp(csa->field, "s") == 0) type = GLP_FX; else error(csa, "row type missing or invalid"); if (type == GLP_LO || type == GLP_DB || type == GLP_FX) { read_field(csa); if (str2num(csa->field, &lb) != 0) error(csa, "row lower bound/fixed value missing or in" "valid"); } else lb = 0.0; if (type == GLP_UP || type == GLP_DB) { read_field(csa); if (str2num(csa->field, &ub) != 0) error(csa, "row upper bound missing or invalid"); } else ub = 0.0; if (rf[i] & 0x01) error(csa, "duplicate row descriptor"); glp_set_row_bnds(P, i, type, lb, ub), rf[i] |= 0x01; } else if (strcmp(csa->field, "j") == 0) { /* column descriptor */ read_field(csa); if (str2int(csa->field, &j) != 0) error(csa, "column number missing or invalid"); if (!(1 <= j && j <= n)) error(csa, "column number out of range"); if (!mip) kind = GLP_CV; else { read_field(csa); if (strcmp(csa->field, "c") == 0) kind = GLP_CV; else if (strcmp(csa->field, "i") == 0) kind = GLP_IV; else if (strcmp(csa->field, "b") == 0) { kind = GLP_IV; type = GLP_DB, lb = 0.0, ub = 1.0; goto skip; } else error(csa, "column kind missing or invalid"); } read_field(csa); if (strcmp(csa->field, "f") == 0) type = GLP_FR; else if (strcmp(csa->field, "l") == 0) type = GLP_LO; else if (strcmp(csa->field, "u") == 0) type = GLP_UP; else if (strcmp(csa->field, "d") == 0) type = GLP_DB; else if (strcmp(csa->field, "s") == 0) type = GLP_FX; else error(csa, "column type missing or invalid"); if (type == GLP_LO || type == GLP_DB || type == GLP_FX) { read_field(csa); if (str2num(csa->field, &lb) != 0) error(csa, "column lower bound/fixed value missing or" " invalid"); } else lb = 0.0; if (type == GLP_UP || type == GLP_DB) { read_field(csa); if (str2num(csa->field, &ub) != 0) error(csa, "column upper bound missing or invalid"); } else ub = 0.0; skip: if (cf[j] & 0x01) error(csa, "duplicate column descriptor"); glp_set_col_kind(P, j, kind); glp_set_col_bnds(P, j, type, lb, ub), cf[j] |= 0x01; } else if (strcmp(csa->field, "a") == 0) { /* coefficient descriptor */ read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "row number missing or invalid"); if (!(0 <= i && i <= m)) error(csa, "row number out of range"); read_field(csa); if (str2int(csa->field, &j) != 0) error(csa, "column number missing or invalid"); if (!((i == 0 ? 0 : 1) <= j && j <= n)) error(csa, "column number out of range"); read_field(csa); if (i == 0) { if (str2num(csa->field, &temp) != 0) error(csa, "objective %s missing or invalid", j == 0 ? "constant term" : "coefficient"); if (cf[j] & 0x10) error(csa, "duplicate objective %s", j == 0 ? "constant term" : "coefficient"); glp_set_obj_coef(P, j, temp), cf[j] |= 0x10; } else { if (str2num(csa->field, &temp) != 0) error(csa, "constraint coefficient missing or invalid" ); if (ne == nnz) error(csa, "too many constraint coefficient descripto" "rs"); ln[++ne] = csa->count; ia[ne] = i, ja[ne] = j, ar[ne] = temp; } } else if (strcmp(csa->field, "n") == 0) { /* symbolic name descriptor */ read_field(csa); if (strcmp(csa->field, "p") == 0) { /* problem name */ read_field(csa); if (P->name != NULL) error(csa, "duplicate problem name"); glp_set_prob_name(P, csa->field); } else if (strcmp(csa->field, "z") == 0) { /* objective name */ read_field(csa); if (P->obj != NULL) error(csa, "duplicate objective name"); glp_set_obj_name(P, csa->field); } else if (strcmp(csa->field, "i") == 0) { /* row name */ read_field(csa); if (str2int(csa->field, &i) != 0) error(csa, "row number missing or invalid"); if (!(1 <= i && i <= m)) error(csa, "row number out of range"); read_field(csa); if (P->row[i]->name != NULL) error(csa, "duplicate row name"); glp_set_row_name(P, i, csa->field); } else if (strcmp(csa->field, "j") == 0) { /* column name */ read_field(csa); if (str2int(csa->field, &j) != 0) error(csa, "column number missing or invalid"); if (!(1 <= j && j <= n)) error(csa, "column number out of range"); read_field(csa); if (P->col[j]->name != NULL) error(csa, "duplicate column name"); glp_set_col_name(P, j, csa->field); } else error(csa, "object designator missing or invalid"); } else if (strcmp(csa->field, "e") == 0) break; else error(csa, "line designator missing or invalid"); end_of_line(csa); } if (ne < nnz) error(csa, "too few constraint coefficient descriptors"); xassert(ne == nnz); k = glp_check_dup(m, n, ne, ia, ja); xassert(0 <= k && k <= nnz); if (k > 0) { csa->count = ln[k]; error(csa, "duplicate constraint coefficient"); } glp_load_matrix(P, ne, ia, ja, ar); /* print some statistics */ if (P->name != NULL) xprintf("Problem: %s\n", P->name); if (P->obj != NULL) xprintf("Objective: %s\n", P->obj); xprintf("%d row%s, %d column%s, %d non-zero%s\n", m, m == 1 ? "" : "s", n, n == 1 ? "" : "s", nnz, nnz == 1 ? "" : "s"); if (glp_get_num_int(P) > 0) { int ni = glp_get_num_int(P); int nb = glp_get_num_bin(P); if (ni == 1) { if (nb == 0) xprintf("One variable is integer\n"); else xprintf("One variable is binary\n"); } else { xprintf("%d integer variables, ", ni); if (nb == 0) xprintf("none"); else if (nb == 1) xprintf("one"); else if (nb == ni) xprintf("all"); else xprintf("%d", nb); xprintf(" of which %s binary\n", nb == 1 ? "is" : "are"); } } xprintf("%d lines were read\n", csa->count); /* problem data has been successfully read */ glp_sort_matrix(P); ret = 0; done: if (csa->fp != NULL) xfclose(csa->fp); if (rf != NULL) xfree(rf); if (cf != NULL) xfree(cf); if (ln != NULL) xfree(ln); if (ia != NULL) xfree(ia); if (ja != NULL) xfree(ja); if (ar != NULL) xfree(ar); if (ret) glp_erase_prob(P); return ret; } /*********************************************************************** * NAME * * glp_write_prob - write problem data in GLPK format * * SYNOPSIS * * int glp_write_prob(glp_prob *P, int flags, const char *fname); * * The routine glp_write_prob writes problem data in GLPK LP/MIP format * to a text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_write_prob(glp_prob *P, int flags, const char *fname) { XFILE *fp; GLPROW *row; GLPCOL *col; GLPAIJ *aij; int mip, i, j, count, ret; if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_write_prob: P = %p; invalid problem object\n", P); if (flags != 0) xerror("glp_write_prob: flags = %d; invalid parameter\n", flags); if (fname == NULL) xerror("glp_write_prob: fname = %d; invalid parameter\n", fname); xprintf("Writing problem data to `%s'...\n", fname); fp = xfopen(fname, "w"), count = 0; if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } /* write problem line */ mip = (glp_get_num_int(P) > 0); xfprintf(fp, "p %s %s %d %d %d\n", !mip ? "lp" : "mip", P->dir == GLP_MIN ? "min" : P->dir == GLP_MAX ? "max" : "???", P->m, P->n, P->nnz), count++; if (P->name != NULL) xfprintf(fp, "n p %s\n", P->name), count++; if (P->obj != NULL) xfprintf(fp, "n z %s\n", P->obj), count++; /* write row descriptors */ for (i = 1; i <= P->m; i++) { row = P->row[i]; if (row->type == GLP_FX && row->lb == 0.0) goto skip1; xfprintf(fp, "i %d ", i), count++; if (row->type == GLP_FR) xfprintf(fp, "f\n"); else if (row->type == GLP_LO) xfprintf(fp, "l %.*g\n", DBL_DIG, row->lb); else if (row->type == GLP_UP) xfprintf(fp, "u %.*g\n", DBL_DIG, row->ub); else if (row->type == GLP_DB) xfprintf(fp, "d %.*g %.*g\n", DBL_DIG, row->lb, DBL_DIG, row->ub); else if (row->type == GLP_FX) xfprintf(fp, "s %.*g\n", DBL_DIG, row->lb); else xassert(row != row); skip1: if (row->name != NULL) xfprintf(fp, "n i %d %s\n", i, row->name), count++; } /* write column descriptors */ for (j = 1; j <= P->n; j++) { col = P->col[j]; if (!mip && col->type == GLP_LO && col->lb == 0.0) goto skip2; if (mip && col->kind == GLP_IV && col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0) goto skip2; xfprintf(fp, "j %d ", j), count++; if (mip) { if (col->kind == GLP_CV) xfprintf(fp, "c "); else if (col->kind == GLP_IV) xfprintf(fp, "i "); else xassert(col != col); } if (col->type == GLP_FR) xfprintf(fp, "f\n"); else if (col->type == GLP_LO) xfprintf(fp, "l %.*g\n", DBL_DIG, col->lb); else if (col->type == GLP_UP) xfprintf(fp, "u %.*g\n", DBL_DIG, col->ub); else if (col->type == GLP_DB) xfprintf(fp, "d %.*g %.*g\n", DBL_DIG, col->lb, DBL_DIG, col->ub); else if (col->type == GLP_FX) xfprintf(fp, "s %.*g\n", DBL_DIG, col->lb); else xassert(col != col); skip2: if (col->name != NULL) xfprintf(fp, "n j %d %s\n", j, col->name), count++; } /* write objective coefficient descriptors */ if (P->c0 != 0.0) xfprintf(fp, "a 0 0 %.*g\n", DBL_DIG, P->c0), count++; for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->coef != 0.0) xfprintf(fp, "a 0 %d %.*g\n", j, DBL_DIG, col->coef), count++; } /* write constraint coefficient descriptors */ for (i = 1; i <= P->m; i++) { row = P->row[i]; for (aij = row->ptr; aij != NULL; aij = aij->r_next) xfprintf(fp, "a %d %d %.*g\n", i, aij->col->j, DBL_DIG, aij->val), count++; } /* write end line */ xfprintf(fp, "e o f\n"), count++; xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", count); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /* eof */ igraph/src/DensityGrid_3d.cpp0000644000176000001440000002167412325527072015701 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file contains the member definitions of the DensityGrid.h class // This code is modified from the original code by B.N. Wylie #include #include #include #include #include using namespace std; #include "drl_Node_3d.h" #include "DensityGrid_3d.h" #include "igraph_error.h" #define GET_BIN(z, y, x) (Bins[(z*GRID_SIZE+y)*GRID_SIZE+x]) namespace drl3d { //******************************************************* // Density Grid Destructor -- deallocates memory used // for Density matrix, fall_off matrix, and node deque. DensityGrid::~DensityGrid () { delete[] Density; delete[] fall_off; delete[] Bins; } /********************************************* * Function: Density_Grid::Reset * * Description: Reset the density grid * *********************************************/ // changed from reset to init since we will only // call this once in the parallel version of layout void DensityGrid::Init() { try { Density = new float[GRID_SIZE][GRID_SIZE][GRID_SIZE]; fall_off = new float[RADIUS*2+1][RADIUS*2+1][RADIUS*2+1]; Bins = new deque[GRID_SIZE*GRID_SIZE*GRID_SIZE]; } catch (bad_alloc errora) { // cout << "Error: Out of memory! Program stopped." << endl; #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else igraph_error("DrL is out of memory", __FILE__, __LINE__, IGRAPH_ENOMEM); #endif } // Clear Grid int i; for (i=0; i< GRID_SIZE; i++) for (int j=0; j< GRID_SIZE; j++) for (int k=0; k < GRID_SIZE; k++) { Density[i][j][k] = 0; GET_BIN(i,j,k).erase(GET_BIN(i,j,k).begin(),GET_BIN(i,j,k).end()); } // Compute fall off for(i=-RADIUS; i<=RADIUS; i++) for(int j=-RADIUS; j<=RADIUS; j++) for (int k=-RADIUS; k<=RADIUS; k++) { fall_off[i+RADIUS][j+RADIUS][k+RADIUS] = (float)((RADIUS-fabs((float)i))/RADIUS) * (float)((RADIUS-fabs((float)j))/RADIUS) * (float)((RADIUS-fabs((float)k))/RADIUS); } } /*************************************************** * Function: DensityGrid::GetDensity * * Description: Get_Density from density grid * **************************************************/ float DensityGrid::GetDensity(float Nx, float Ny, float Nz,bool fineDensity) { deque::iterator BI; int x_grid, y_grid, z_grid; float x_dist, y_dist, z_dist, distance, density=0; int boundary=10; // boundary around plane /* Where to look */ x_grid = (int)((Nx+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((Ny+HALF_VIEW+.5)*VIEW_TO_GRID); z_grid = (int)((Nz+HALF_VIEW+.5)*VIEW_TO_GRID); // Check for edges of density grid (10000 is arbitrary high density) if (x_grid > GRID_SIZE-boundary || x_grid < boundary) return 10000; if (y_grid > GRID_SIZE-boundary || y_grid < boundary) return 10000; if (z_grid > GRID_SIZE-boundary || z_grid < boundary) return 10000; // Fine density? if (fineDensity) { // Go through nearest bins for (int k=z_grid-1; k<=z_grid+1; k++) for(int i=y_grid-1; i<=y_grid+1; i++) for(int j=x_grid-1; j<=x_grid+1; j++) { // Look through bin and add fine repulsions for(BI = GET_BIN(k,i,j).begin(); BI < GET_BIN(k,i,j).end(); ++BI) { x_dist = Nx-(BI->x); y_dist = Ny-(BI->y); z_dist = Nz-(BI->z); distance = x_dist*x_dist+y_dist*y_dist+z_dist*z_dist; density += 1e-4/(distance + 1e-50); } } // Course density } else { // Add rough estimate density = Density[z_grid][y_grid][x_grid]; density *= density; } return density; } /// Wrapper functions for the Add and subtract methods /// Nodes should all be passed by constant ref void DensityGrid::Add(Node &n, bool fineDensity) { if(fineDensity) fineAdd(n); else Add(n); } void DensityGrid::Subtract( Node &n, bool first_add, bool fine_first_add, bool fineDensity) { if ( fineDensity && !fine_first_add ) fineSubtract (n); else if ( !first_add ) Subtract(n); } /*************************************************** * Function: DensityGrid::Subtract * * Description: Subtract a node from density grid * **************************************************/ void DensityGrid::Subtract(Node &N) { int x_grid, y_grid, z_grid, diam; float *den_ptr, *fall_ptr; /* Where to subtract */ x_grid = (int)((N.sub_x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.sub_y+HALF_VIEW+.5)*VIEW_TO_GRID); z_grid = (int)((N.sub_z+HALF_VIEW+.5)*VIEW_TO_GRID); x_grid -= RADIUS; y_grid -= RADIUS; z_grid -= RADIUS; diam = 2*RADIUS; /* Subtract density values */ den_ptr = &Density[z_grid][y_grid][x_grid]; fall_ptr = &fall_off[0][0][0]; for(int i = 0; i <= diam; i++) { for(int j = 0; j <= diam; j++) for (int k=0; k <= diam; k++) *den_ptr++ -= *fall_ptr++; den_ptr += GRID_SIZE - (diam+1); } } /*************************************************** * Function: DensityGrid::Add * * Description: Add a node to the density grid * **************************************************/ void DensityGrid::Add(Node &N) { int x_grid, y_grid, z_grid, diam; float *den_ptr, *fall_ptr; /* Where to add */ x_grid = (int)((N.x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.y+HALF_VIEW+.5)*VIEW_TO_GRID); z_grid = (int)((N.z+HALF_VIEW+.5)*VIEW_TO_GRID); N.sub_x = N.x; N.sub_y = N.y; N.sub_z = N.z; x_grid -= RADIUS; y_grid -= RADIUS; z_grid -= RADIUS; diam = 2*RADIUS; // check to see that we are inside grid if ( (x_grid >= GRID_SIZE) || (x_grid < 0) || (y_grid >= GRID_SIZE) || (y_grid < 0) || (z_grid >= GRID_SIZE) || (z_grid < 0) ) { // cout << endl << "Error: Exceeded density grid with x_grid = " << x_grid // << " and y_grid = " << y_grid << ". Program stopped." << endl; #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else igraph_error("Exceeded density grid in DrL", __FILE__, __LINE__, IGRAPH_EDRL); #endif } /* Add density values */ den_ptr = &Density[z_grid][y_grid][x_grid]; fall_ptr = &fall_off[0][0][0]; for(int i = 0; i <= diam; i++) { for(int j = 0; j <= diam; j++) for (int k = 0; k <= diam; k++) *den_ptr++ += *fall_ptr++; den_ptr += GRID_SIZE - (diam+1); } } /*************************************************** * Function: DensityGrid::fineSubtract * * Description: Subtract a node from bins * **************************************************/ void DensityGrid::fineSubtract(Node &N) { int x_grid, y_grid, z_grid; /* Where to subtract */ x_grid = (int)((N.sub_x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.sub_y+HALF_VIEW+.5)*VIEW_TO_GRID); z_grid = (int)((N.sub_z+HALF_VIEW+.5)*VIEW_TO_GRID); GET_BIN(z_grid,y_grid,x_grid).pop_front(); } /*************************************************** * Function: DensityGrid::fineAdd * * Description: Add a node to the bins * **************************************************/ void DensityGrid::fineAdd(Node &N) { int x_grid, y_grid, z_grid; /* Where to add */ x_grid = (int)((N.x+HALF_VIEW+.5)*VIEW_TO_GRID); y_grid = (int)((N.y+HALF_VIEW+.5)*VIEW_TO_GRID); z_grid = (int)((N.z+HALF_VIEW+.5)*VIEW_TO_GRID); N.sub_x = N.x; N.sub_y = N.y; N.sub_z = N.z; GET_BIN(z_grid,y_grid,x_grid).push_back(N); } } // namespace drl3d igraph/src/qsort.c0000644000176000001440000001352412325527074013673 0ustar ripleyusers/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif #ifdef _MSC_VER /* MSVC does not have inline when compiling C source files */ #define inline __inline #define __unused #endif #ifndef __unused #define __unused __attribute__ ((unused)) #endif #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ /*#include */ #include #ifdef I_AM_QSORT_R typedef int cmp_t(void *, const void *, const void *); #else typedef int cmp_t(const void *, const void *); #endif static inline char *med3(char *, char *, char *, cmp_t *, void *); static inline void swapfunc(char *, char *, int, int); #define igraph_min(a, b) (a) < (b) ? a : b /* * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". */ #define swapcode(TYPE, parmi, parmj, n) { \ long i = (n) / sizeof (TYPE); \ TYPE *pi = (TYPE *) (parmi); \ TYPE *pj = (TYPE *) (parmj); \ do { \ TYPE t = *pi; \ *pi++ = *pj; \ *pj++ = t; \ } while (--i > 0); \ } #define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; static inline void swapfunc(a, b, n, swaptype) char *a, *b; int n, swaptype; { if(swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) } #define swap(a, b) \ if (swaptype == 0) { \ long t = *(long *)(a); \ *(long *)(a) = *(long *)(b); \ *(long *)(b) = t; \ } else \ swapfunc(a, b, es, swaptype) #define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) #ifdef I_AM_QSORT_R #define CMP(t, x, y) (cmp((t), (x), (y))) #else #define CMP(t, x, y) (cmp((x), (y))) #endif static inline char * med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk #ifndef I_AM_QSORT_R __unused #endif ) { return CMP(thunk, a, b) < 0 ? (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a )) :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c )); } #ifdef I_AM_QSORT_R void igraph_qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) #else #define thunk NULL void igraph_qsort(void *a, size_t n, size_t es, cmp_t *cmp) #endif { char *pa, *pb, *pc, *pd, *pl, *pm, *pn; int d, r, swaptype, swap_cnt; loop: SWAPINIT(a, es); swap_cnt = 0; if (n < 7) { for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; pl > (char *)a && CMP(thunk, pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } pm = (char *)a + (n / 2) * es; if (n > 7) { pl = a; pn = (char *)a + (n - 1) * es; if (n > 40) { d = (n / 8) * es; pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk); pm = med3(pm - d, pm, pm + d, cmp, thunk); pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk); } pm = med3(pl, pm, pn, cmp, thunk); } swap(a, pm); pa = pb = (char *)a + es; pc = pd = (char *)a + (n - 1) * es; for (;;) { while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) { if (r == 0) { swap_cnt = 1; swap(pa, pb); pa += es; } pb += es; } while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) { if (r == 0) { swap_cnt = 1; swap(pc, pd); pd -= es; } pc -= es; } if (pb > pc) break; swap(pb, pc); swap_cnt = 1; pb += es; pc -= es; } if (swap_cnt == 0) { /* Switch to insertion sort */ for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; pl > (char *)a && CMP(thunk, pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } pn = (char *)a + n * es; r = igraph_min(pa - (char *)a, pb - pa); vecswap(a, pb - r, r); r = igraph_min((size_t)(pd - pc), (size_t)(pn - pd - es)); vecswap(pb, pn - r, r); if ((size_t)(r = pb - pa) > es) #ifdef I_AM_QSORT_R igraph_qsort_r(a, r / es, es, thunk, cmp); #else igraph_qsort(a, r / es, es, cmp); #endif if ((size_t)(r = pd - pc) > es) { /* Iterate rather than recurse to save stack space */ a = pn - r; n = r / es; goto loop; } /* qsort(pn - r, r / es, es, cmp);*/ } igraph/src/glpk/0000755000176000001440000000000012325372074013305 5ustar ripleyusersigraph/src/glpk/amd/0000755000176000001440000000000012325372074014046 5ustar ripleyusersigraph/src/glpk/amd/amd.h0000644000176000001440000000327612325527073014771 0ustar ripleyusers/* amd.h */ /* Written by Andrew Makhorin . */ #ifndef GLPAMD_H #define GLPAMD_H #define AMD_DATE "May 31, 2007" #define AMD_VERSION_CODE(main, sub) ((main) * 1000 + (sub)) #define AMD_MAIN_VERSION 2 #define AMD_SUB_VERSION 2 #define AMD_SUBSUB_VERSION 0 #define AMD_VERSION AMD_VERSION_CODE(AMD_MAIN_VERSION, AMD_SUB_VERSION) #define AMD_CONTROL 5 #define AMD_INFO 20 #define AMD_DENSE 0 #define AMD_AGGRESSIVE 1 #define AMD_DEFAULT_DENSE 10.0 #define AMD_DEFAULT_AGGRESSIVE 1 #define AMD_STATUS 0 #define AMD_N 1 #define AMD_NZ 2 #define AMD_SYMMETRY 3 #define AMD_NZDIAG 4 #define AMD_NZ_A_PLUS_AT 5 #define AMD_NDENSE 6 #define AMD_MEMORY 7 #define AMD_NCMPA 8 #define AMD_LNZ 9 #define AMD_NDIV 10 #define AMD_NMULTSUBS_LDL 11 #define AMD_NMULTSUBS_LU 12 #define AMD_DMAX 13 #define AMD_OK 0 #define AMD_OUT_OF_MEMORY (-1) #define AMD_INVALID (-2) #define AMD_OK_BUT_JUMBLED 1 #define amd_order _glp_amd_order int amd_order(int n, const int Ap[], const int Ai[], int P[], double Control[], double Info[]); #define amd_2 _glp_amd_2 void amd_2(int n, int Pe[], int Iw[], int Len[], int iwlen, int pfree, int Nv[], int Next[], int Last[], int Head[], int Elen[], int Degree[], int W[], double Control[], double Info[]); #define amd_valid _glp_amd_valid int amd_valid(int n_row, int n_col, const int Ap[], const int Ai[]); #define amd_defaults _glp_amd_defaults void amd_defaults(double Control[]); #define amd_control _glp_amd_control void amd_control(double Control[]); #define amd_info _glp_amd_info void amd_info(double Info[]); #endif /* eof */ igraph/src/glpk/amd/amd_internal.h0000644000176000001440000000577712325527073016675 0ustar ripleyusers/* amd_internal.h */ /* Written by Andrew Makhorin . */ #ifndef AMD_INTERNAL_H #define AMD_INTERNAL_H /* AMD will be exceedingly slow when running in debug mode. */ #if 1 #ifndef NDEBUG #define NDEBUG #endif #endif #include "amd.h" #define _GLPSTD_STDIO #include "glpenv.h" #define Int int #define ID "%d" #define Int_MAX INT_MAX #ifndef SIZE_T_MAX #define SIZE_T_MAX ((size_t)(-1)) #endif #define EMPTY (-1) #define FLIP(i) (-(i)-2) #define UNFLIP(i) ((i < EMPTY) ? FLIP (i) : (i)) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define IMPLIES(p, q) (!(p) || (q)) #define GLOBAL #define AMD_order amd_order #define AMD_defaults amd_defaults #define AMD_control amd_control #define AMD_info amd_info #define AMD_1 amd_1 #define AMD_2 amd_2 #define AMD_valid amd_valid #define AMD_aat amd_aat #define AMD_postorder amd_postorder #define AMD_post_tree amd_post_tree #define AMD_dump amd_dump #define AMD_debug amd_debug #define AMD_debug_init amd_debug_init #define AMD_preprocess amd_preprocess #define amd_malloc xmalloc #if 0 /* 24/V-2009 */ #define amd_free xfree #else #define amd_free(ptr) { if ((ptr) != NULL) xfree(ptr); } #endif #define amd_printf xprintf #define PRINTF(params) { amd_printf params; } #ifndef NDEBUG #define ASSERT(expr) xassert(expr) #define AMD_DEBUG0(params) { PRINTF(params); } #define AMD_DEBUG1(params) { if (AMD_debug >= 1) PRINTF(params); } #define AMD_DEBUG2(params) { if (AMD_debug >= 2) PRINTF(params); } #define AMD_DEBUG3(params) { if (AMD_debug >= 3) PRINTF(params); } #define AMD_DEBUG4(params) { if (AMD_debug >= 4) PRINTF(params); } #else #define ASSERT(expression) #define AMD_DEBUG0(params) #define AMD_DEBUG1(params) #define AMD_DEBUG2(params) #define AMD_DEBUG3(params) #define AMD_DEBUG4(params) #endif #define amd_aat _glp_amd_aat size_t AMD_aat(Int n, const Int Ap[], const Int Ai[], Int Len[], Int Tp[], double Info[]); #define amd_1 _glp_amd_1 void AMD_1(Int n, const Int Ap[], const Int Ai[], Int P[], Int Pinv[], Int Len[], Int slen, Int S[], double Control[], double Info[]); #define amd_postorder _glp_amd_postorder void AMD_postorder(Int nn, Int Parent[], Int Npiv[], Int Fsize[], Int Order[], Int Child[], Int Sibling[], Int Stack[]); #define amd_post_tree _glp_amd_post_tree #ifndef NDEBUG Int AMD_post_tree(Int root, Int k, Int Child[], const Int Sibling[], Int Order[], Int Stack[], Int nn); #else Int AMD_post_tree(Int root, Int k, Int Child[], const Int Sibling[], Int Order[], Int Stack[]); #endif #define amd_preprocess _glp_amd_preprocess void AMD_preprocess(Int n, const Int Ap[], const Int Ai[], Int Rp[], Int Ri[], Int W[], Int Flag[]); #define amd_debug _glp_amd_debug extern Int AMD_debug; #define amd_debug_init _glp_amd_debug_init void AMD_debug_init(char *s); #define amd_dump _glp_amd_dump void AMD_dump(Int n, Int Pe[], Int Iw[], Int Len[], Int iwlen, Int pfree, Int Nv[], Int Next[], Int Last[], Int Head[], Int Elen[], Int Degree[], Int W[], Int nel); #endif /* eof */ igraph/src/glpk/glpssx.h0000644000176000001440000004013012325527073014775 0ustar ripleyusers/* glpssx.h (simplex method, bignum arithmetic) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPSSX_H #define GLPSSX_H #include "glpbfx.h" #include "glpenv.h" typedef struct SSX SSX; struct SSX { /* simplex solver workspace */ /*---------------------------------------------------------------------- // LP PROBLEM DATA // // It is assumed that LP problem has the following statement: // // minimize (or maximize) // // z = c[1]*x[1] + ... + c[m+n]*x[m+n] + c[0] (1) // // subject to equality constraints // // x[1] - a[1,1]*x[m+1] - ... - a[1,n]*x[m+n] = 0 // // . . . . . . . (2) // // x[m] - a[m,1]*x[m+1] + ... - a[m,n]*x[m+n] = 0 // // and bounds of variables // // l[1] <= x[1] <= u[1] // // . . . . . . . (3) // // l[m+n] <= x[m+n] <= u[m+n] // // where: // x[1], ..., x[m] - auxiliary variables; // x[m+1], ..., x[m+n] - structural variables; // z - objective function; // c[1], ..., c[m+n] - coefficients of the objective function; // c[0] - constant term of the objective function; // a[1,1], ..., a[m,n] - constraint coefficients; // l[1], ..., l[m+n] - lower bounds of variables; // u[1], ..., u[m+n] - upper bounds of variables. // // Bounds of variables can be finite as well as inifinite. Besides, // lower and upper bounds can be equal to each other. So the following // five types of variables are possible: // // Bounds of variable Type of variable // ------------------------------------------------- // -inf < x[k] < +inf Free (unbounded) variable // l[k] <= x[k] < +inf Variable with lower bound // -inf < x[k] <= u[k] Variable with upper bound // l[k] <= x[k] <= u[k] Double-bounded variable // l[k] = x[k] = u[k] Fixed variable // // Using vector-matrix notations the LP problem (1)-(3) can be written // as follows: // // minimize (or maximize) // // z = c * x + c[0] (4) // // subject to equality constraints // // xR - A * xS = 0 (5) // // and bounds of variables // // l <= x <= u (6) // // where: // xR - vector of auxiliary variables; // xS - vector of structural variables; // x = (xR, xS) - vector of all variables; // z - objective function; // c - vector of objective coefficients; // c[0] - constant term of the objective function; // A - matrix of constraint coefficients (has m rows // and n columns); // l - vector of lower bounds of variables; // u - vector of upper bounds of variables. // // The simplex method makes no difference between auxiliary and // structural variables, so it is convenient to think the system of // equality constraints (5) written in a homogeneous form: // // (I | -A) * x = 0, (7) // // where (I | -A) is an augmented (m+n)xm constraint matrix, I is mxm // unity matrix whose columns correspond to auxiliary variables, and A // is the original mxn constraint matrix whose columns correspond to // structural variables. Note that only the matrix A is stored. ----------------------------------------------------------------------*/ int m; /* number of rows (auxiliary variables), m > 0 */ int n; /* number of columns (structural variables), n > 0 */ int *type; /* int type[1+m+n]; */ /* type[0] is not used; type[k], 1 <= k <= m+n, is the type of variable x[k]: */ #define SSX_FR 0 /* free (unbounded) variable */ #define SSX_LO 1 /* variable with lower bound */ #define SSX_UP 2 /* variable with upper bound */ #define SSX_DB 3 /* double-bounded variable */ #define SSX_FX 4 /* fixed variable */ mpq_t *lb; /* mpq_t lb[1+m+n]; alias: l */ /* lb[0] is not used; lb[k], 1 <= k <= m+n, is an lower bound of variable x[k]; if x[k] has no lower bound, lb[k] is zero */ mpq_t *ub; /* mpq_t ub[1+m+n]; alias: u */ /* ub[0] is not used; ub[k], 1 <= k <= m+n, is an upper bound of variable x[k]; if x[k] has no upper bound, ub[k] is zero; if x[k] is of fixed type, ub[k] is equal to lb[k] */ int dir; /* optimization direction (sense of the objective function): */ #define SSX_MIN 0 /* minimization */ #define SSX_MAX 1 /* maximization */ mpq_t *coef; /* mpq_t coef[1+m+n]; alias: c */ /* coef[0] is a constant term of the objective function; coef[k], 1 <= k <= m+n, is a coefficient of the objective function at variable x[k]; note that auxiliary variables also may have non-zero objective coefficients */ int *A_ptr; /* int A_ptr[1+n+1]; */ int *A_ind; /* int A_ind[A_ptr[n+1]]; */ mpq_t *A_val; /* mpq_t A_val[A_ptr[n+1]]; */ /* constraint matrix A (see (5)) in storage-by-columns format */ /*---------------------------------------------------------------------- // LP BASIS AND CURRENT BASIC SOLUTION // // The LP basis is defined by the following partition of the augmented // constraint matrix (7): // // (B | N) = (I | -A) * Q, (8) // // where B is a mxm non-singular basis matrix whose columns correspond // to basic variables xB, N is a mxn matrix whose columns correspond to // non-basic variables xN, and Q is a permutation (m+n)x(m+n) matrix. // // From (7) and (8) it follows that // // (I | -A) * x = (I | -A) * Q * Q' * x = (B | N) * (xB, xN), // // therefore // // (xB, xN) = Q' * x, (9) // // where x is the vector of all variables in the original order, xB is // a vector of basic variables, xN is a vector of non-basic variables, // Q' = inv(Q) is a matrix transposed to Q. // // Current values of non-basic variables xN[j], j = 1, ..., n, are not // stored; they are defined implicitly by their statuses as follows: // // 0, if xN[j] is free variable // lN[j], if xN[j] is on its lower bound (10) // uN[j], if xN[j] is on its upper bound // lN[j] = uN[j], if xN[j] is fixed variable // // where lN[j] and uN[j] are lower and upper bounds of xN[j]. // // Current values of basic variables xB[i], i = 1, ..., m, are computed // as follows: // // beta = - inv(B) * N * xN, (11) // // where current values of xN are defined by (10). // // Current values of simplex multipliers pi[i], i = 1, ..., m (which // are values of Lagrange multipliers for equality constraints (7) also // called shadow prices) are computed as follows: // // pi = inv(B') * cB, (12) // // where B' is a matrix transposed to B, cB is a vector of objective // coefficients at basic variables xB. // // Current values of reduced costs d[j], j = 1, ..., n, (which are // values of Langrange multipliers for active inequality constraints // corresponding to non-basic variables) are computed as follows: // // d = cN - N' * pi, (13) // // where N' is a matrix transposed to N, cN is a vector of objective // coefficients at non-basic variables xN. ----------------------------------------------------------------------*/ int *stat; /* int stat[1+m+n]; */ /* stat[0] is not used; stat[k], 1 <= k <= m+n, is the status of variable x[k]: */ #define SSX_BS 0 /* basic variable */ #define SSX_NL 1 /* non-basic variable on lower bound */ #define SSX_NU 2 /* non-basic variable on upper bound */ #define SSX_NF 3 /* non-basic free variable */ #define SSX_NS 4 /* non-basic fixed variable */ int *Q_row; /* int Q_row[1+m+n]; */ /* matrix Q in row-like format; Q_row[0] is not used; Q_row[i] = j means that q[i,j] = 1 */ int *Q_col; /* int Q_col[1+m+n]; */ /* matrix Q in column-like format; Q_col[0] is not used; Q_col[j] = i means that q[i,j] = 1 */ /* if k-th column of the matrix (I | A) is k'-th column of the matrix (B | N), then Q_row[k] = k' and Q_col[k'] = k; if x[k] is xB[i], then Q_row[k] = i and Q_col[i] = k; if x[k] is xN[j], then Q_row[k] = m+j and Q_col[m+j] = k */ BFX *binv; /* invertable form of the basis matrix B */ mpq_t *bbar; /* mpq_t bbar[1+m]; alias: beta */ /* bbar[0] is a value of the objective function; bbar[i], 1 <= i <= m, is a value of basic variable xB[i] */ mpq_t *pi; /* mpq_t pi[1+m]; */ /* pi[0] is not used; pi[i], 1 <= i <= m, is a simplex multiplier corresponding to i-th row (equality constraint) */ mpq_t *cbar; /* mpq_t cbar[1+n]; alias: d */ /* cbar[0] is not used; cbar[j], 1 <= j <= n, is a reduced cost of non-basic variable xN[j] */ /*---------------------------------------------------------------------- // SIMPLEX TABLE // // Due to (8) and (9) the system of equality constraints (7) for the // current basis can be written as follows: // // xB = A~ * xN, (14) // // where // // A~ = - inv(B) * N (15) // // is a mxn matrix called the simplex table. // // The revised simplex method uses only two components of A~, namely, // pivot column corresponding to non-basic variable xN[q] chosen to // enter the basis, and pivot row corresponding to basic variable xB[p] // chosen to leave the basis. // // Pivot column alfa_q is q-th column of A~, so // // alfa_q = A~ * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], (16) // // where N[q] is q-th column of the matrix N. // // Pivot row alfa_p is p-th row of A~ or, equivalently, p-th column of // A~', a matrix transposed to A~, so // // alfa_p = A~' * e[p] = - N' * inv(B') * e[p] = - N' * rho_p, (17) // // where (*)' means transposition, and // // rho_p = inv(B') * e[p], (18) // // is p-th column of inv(B') or, that is the same, p-th row of inv(B). ----------------------------------------------------------------------*/ int p; /* number of basic variable xB[p], 1 <= p <= m, chosen to leave the basis */ mpq_t *rho; /* mpq_t rho[1+m]; */ /* p-th row of the inverse inv(B); see (18) */ mpq_t *ap; /* mpq_t ap[1+n]; */ /* p-th row of the simplex table; see (17) */ int q; /* number of non-basic variable xN[q], 1 <= q <= n, chosen to enter the basis */ mpq_t *aq; /* mpq_t aq[1+m]; */ /* q-th column of the simplex table; see (16) */ /*--------------------------------------------------------------------*/ int q_dir; /* direction in which non-basic variable xN[q] should change on moving to the adjacent vertex of the polyhedron: +1 means that xN[q] increases -1 means that xN[q] decreases */ int p_stat; /* non-basic status which should be assigned to basic variable xB[p] when it has left the basis and become xN[q] */ mpq_t delta; /* actual change of xN[q] in the adjacent basis (it has the same sign as q_dir) */ /*--------------------------------------------------------------------*/ int it_lim; /* simplex iterations limit; if this value is positive, it is decreased by one each time when one simplex iteration has been performed, and reaching zero value signals the solver to stop the search; negative value means no iterations limit */ int it_cnt; /* simplex iterations count; this count is increased by one each time when one simplex iteration has been performed */ double tm_lim; /* searching time limit, in seconds; if this value is positive, it is decreased each time when one simplex iteration has been performed by the amount of time spent for the iteration, and reaching zero value signals the solver to stop the search; negative value means no time limit */ double out_frq; /* output frequency, in seconds; this parameter specifies how frequently the solver sends information about the progress of the search to the standard output */ glp_long tm_beg; /* starting time of the search, in seconds; the total time of the search is the difference between xtime() and tm_beg */ glp_long tm_lag; /* the most recent time, in seconds, at which the progress of the the search was displayed */ }; #define ssx_create _glp_ssx_create #define ssx_factorize _glp_ssx_factorize #define ssx_get_xNj _glp_ssx_get_xNj #define ssx_eval_bbar _glp_ssx_eval_bbar #define ssx_eval_pi _glp_ssx_eval_pi #define ssx_eval_dj _glp_ssx_eval_dj #define ssx_eval_cbar _glp_ssx_eval_cbar #define ssx_eval_rho _glp_ssx_eval_rho #define ssx_eval_row _glp_ssx_eval_row #define ssx_eval_col _glp_ssx_eval_col #define ssx_chuzc _glp_ssx_chuzc #define ssx_chuzr _glp_ssx_chuzr #define ssx_update_bbar _glp_ssx_update_bbar #define ssx_update_pi _glp_ssx_update_pi #define ssx_update_cbar _glp_ssx_update_cbar #define ssx_change_basis _glp_ssx_change_basis #define ssx_delete _glp_ssx_delete #define ssx_phase_I _glp_ssx_phase_I #define ssx_phase_II _glp_ssx_phase_II #define ssx_driver _glp_ssx_driver SSX *ssx_create(int m, int n, int nnz); /* create simplex solver workspace */ int ssx_factorize(SSX *ssx); /* factorize the current basis matrix */ void ssx_get_xNj(SSX *ssx, int j, mpq_t x); /* determine value of non-basic variable */ void ssx_eval_bbar(SSX *ssx); /* compute values of basic variables */ void ssx_eval_pi(SSX *ssx); /* compute values of simplex multipliers */ void ssx_eval_dj(SSX *ssx, int j, mpq_t dj); /* compute reduced cost of non-basic variable */ void ssx_eval_cbar(SSX *ssx); /* compute reduced costs of all non-basic variables */ void ssx_eval_rho(SSX *ssx); /* compute p-th row of the inverse */ void ssx_eval_row(SSX *ssx); /* compute pivot row of the simplex table */ void ssx_eval_col(SSX *ssx); /* compute pivot column of the simplex table */ void ssx_chuzc(SSX *ssx); /* choose pivot column */ void ssx_chuzr(SSX *ssx); /* choose pivot row */ void ssx_update_bbar(SSX *ssx); /* update values of basic variables */ void ssx_update_pi(SSX *ssx); /* update simplex multipliers */ void ssx_update_cbar(SSX *ssx); /* update reduced costs of non-basic variables */ void ssx_change_basis(SSX *ssx); /* change current basis to adjacent one */ void ssx_delete(SSX *ssx); /* delete simplex solver workspace */ int ssx_phase_I(SSX *ssx); /* find primal feasible solution */ int ssx_phase_II(SSX *ssx); /* find optimal solution */ int ssx_driver(SSX *ssx); /* base driver to exact simplex method */ #endif /* eof */ igraph/src/glpk/glpdmp.h0000644000176000001440000000522612325527073014747 0ustar ripleyusers/* glpdmp.h (dynamic memory pool) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPDMP_H #define GLPDMP_H #include "glpenv.h" typedef struct DMP DMP; #define DMP_BLK_SIZE 8000 /* size of memory blocks, in bytes, allocated for memory pools */ struct DMP { /* dynamic memory pool */ #if 0 int size; /* size of atoms, in bytes, 1 <= size <= 256; if size = 0, atoms may have different sizes */ #endif void *avail[32]; /* avail[k], 0 <= k <= 31, is a pointer to the first available (free) cell of (k+1)*8 bytes long; in the beginning of each free cell there is a pointer to another free cell of the same length */ void *block; /* pointer to the most recently allocated memory block; in the beginning of each allocated memory block there is a pointer to the previously allocated memory block */ int used; /* number of bytes used in the most recently allocated memory block */ glp_long count; /* number of atoms which are currently in use */ }; #define dmp_create_pool _glp_dmp_create_pool DMP *dmp_create_pool(void); /* create dynamic memory pool */ #define dmp_get_atom _glp_dmp_get_atom void *dmp_get_atom(DMP *pool, int size); /* get free atom from dynamic memory pool */ #define dmp_free_atom _glp_dmp_free_atom void dmp_free_atom(DMP *pool, void *atom, int size); /* return atom to dynamic memory pool */ #define dmp_in_use _glp_dmp_in_use glp_long dmp_in_use(DMP *pool); /* determine how many atoms are still in use */ #define dmp_delete_pool _glp_dmp_delete_pool void dmp_delete_pool(DMP *pool); /* delete dynamic memory pool */ #endif /* eof */ igraph/src/glpk/glplib.h0000644000176000001440000000765112325527073014741 0ustar ripleyusers/* glplib.h (miscellaneous library routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPLIB_H #define GLPLIB_H #define bigmul _glp_lib_bigmul void bigmul(int n, int m, unsigned short x[], unsigned short y[]); /* multiply unsigned integer numbers of arbitrary precision */ #define bigdiv _glp_lib_bigdiv void bigdiv(int n, int m, unsigned short x[], unsigned short y[]); /* divide unsigned integer numbers of arbitrary precision */ #ifndef GLP_LONG_DEFINED #define GLP_LONG_DEFINED typedef struct { int lo, hi; } glp_long; /* long integer data type */ #endif typedef struct { glp_long quot, rem; } glp_ldiv; /* result of long integer division */ #define xlset _glp_lib_xlset glp_long xlset(int x); /* expand integer to long integer */ #define xlneg _glp_lib_xlneg glp_long xlneg(glp_long x); /* negate long integer */ #define xladd _glp_lib_xladd glp_long xladd(glp_long x, glp_long y); /* add long integers */ #define xlsub _glp_lib_xlsub glp_long xlsub(glp_long x, glp_long y); /* subtract long integers */ #define xlcmp _glp_lib_xlcmp int xlcmp(glp_long x, glp_long y); /* compare long integers */ #define xlmul _glp_lib_xlmul glp_long xlmul(glp_long x, glp_long y); /* multiply long integers */ #define xldiv _glp_lib_xldiv glp_ldiv xldiv(glp_long x, glp_long y); /* divide long integers */ #define xltod _glp_lib_xltod double xltod(glp_long x); /* convert long integer to double */ #define xltoa _glp_lib_xltoa char *xltoa(glp_long x, char *s); /* convert long integer to character string */ #define str2int _glp_lib_str2int int str2int(const char *str, int *val); /* convert character string to value of int type */ #define str2num _glp_lib_str2num int str2num(const char *str, double *val); /* convert character string to value of double type */ #define strspx _glp_lib_strspx char *strspx(char *str); /* remove all spaces from character string */ #define strtrim _glp_lib_strtrim char *strtrim(char *str); /* remove trailing spaces from character string */ #define strrev _glp_lib_strrev char *strrev(char *s); /* reverse character string */ #define gcd _glp_lib_gcd int gcd(int x, int y); /* find greatest common divisor of two integers */ #define gcdn _glp_lib_gcdn int gcdn(int n, int x[]); /* find greatest common divisor of n integers */ #define lcm _glp_lib_lcm int lcm(int x, int y); /* find least common multiple of two integers */ #define lcmn _glp_lib_lcmn int lcmn(int n, int x[]); /* find least common multiple of n integers */ #define round2n _glp_lib_round2n double round2n(double x); /* round floating-point number to nearest power of two */ #define fp2rat _glp_lib_fp2rat int fp2rat(double x, double eps, double *p, double *q); /* convert floating-point number to rational number */ #define jday _glp_lib_jday int jday(int d, int m, int y); /* convert calendar date to Julian day number */ #define jdate _glp_lib_jdate int jdate(int j, int *d, int *m, int *y); /* convert Julian day number to calendar date */ #endif /* eof */ igraph/src/glpk/glpspm.h0000644000176000001440000001174012325527073014764 0ustar ripleyusers/* glpspm.h (general sparse matrix) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPSPM_H #define GLPSPM_H #include "glpdmp.h" typedef struct SPM SPM; typedef struct SPME SPME; struct SPM { /* general sparse matrix */ int m; /* number of rows, m >= 0 */ int n; /* number of columns, n >= 0 */ DMP *pool; /* memory pool to store matrix elements */ SPME **row; /* SPME *row[1+m]; */ /* row[i], 1 <= i <= m, is a pointer to i-th row list */ SPME **col; /* SPME *col[1+n]; */ /* col[j], 1 <= j <= n, is a pointer to j-th column list */ }; struct SPME { /* sparse matrix element */ int i; /* row number */ int j; /* column number */ double val; /* element value */ SPME *r_prev; /* pointer to previous element in the same row */ SPME *r_next; /* pointer to next element in the same row */ SPME *c_prev; /* pointer to previous element in the same column */ SPME *c_next; /* pointer to next element in the same column */ }; typedef struct PER PER; struct PER { /* permutation matrix */ int n; /* matrix order, n >= 0 */ int *row; /* int row[1+n]; */ /* row[i] = j means p[i,j] = 1 */ int *col; /* int col[1+n]; */ /* col[j] = i means p[i,j] = 1 */ }; #define spm_create_mat _glp_spm_create_mat SPM *spm_create_mat(int m, int n); /* create general sparse matrix */ #define spm_new_elem _glp_spm_new_elem SPME *spm_new_elem(SPM *A, int i, int j, double val); /* add new element to sparse matrix */ #define spm_delete_mat _glp_spm_delete_mat void spm_delete_mat(SPM *A); /* delete general sparse matrix */ #define spm_test_mat_e _glp_spm_test_mat_e SPM *spm_test_mat_e(int n, int c); /* create test sparse matrix of E(n,c) class */ #define spm_test_mat_d _glp_spm_test_mat_d SPM *spm_test_mat_d(int n, int c); /* create test sparse matrix of D(n,c) class */ #define spm_show_mat _glp_spm_show_mat int spm_show_mat(const SPM *A, const char *fname); /* write sparse matrix pattern in BMP file format */ #define spm_read_hbm _glp_spm_read_hbm SPM *spm_read_hbm(const char *fname); /* read sparse matrix in Harwell-Boeing format */ #define spm_count_nnz _glp_spm_count_nnz int spm_count_nnz(const SPM *A); /* determine number of non-zeros in sparse matrix */ #define spm_drop_zeros _glp_spm_drop_zeros int spm_drop_zeros(SPM *A, double eps); /* remove zero elements from sparse matrix */ #define spm_read_mat _glp_spm_read_mat SPM *spm_read_mat(const char *fname); /* read sparse matrix from text file */ #define spm_write_mat _glp_spm_write_mat int spm_write_mat(const SPM *A, const char *fname); /* write sparse matrix to text file */ #define spm_transpose _glp_spm_transpose SPM *spm_transpose(const SPM *A); /* transpose sparse matrix */ #define spm_add_sym _glp_spm_add_sym SPM *spm_add_sym(const SPM *A, const SPM *B); /* add two sparse matrices (symbolic phase) */ #define spm_add_num _glp_spm_add_num void spm_add_num(SPM *C, double alfa, const SPM *A, double beta, const SPM *B); /* add two sparse matrices (numeric phase) */ #define spm_add_mat _glp_spm_add_mat SPM *spm_add_mat(double alfa, const SPM *A, double beta, const SPM *B); /* add two sparse matrices (driver routine) */ #define spm_mul_sym _glp_spm_mul_sym SPM *spm_mul_sym(const SPM *A, const SPM *B); /* multiply two sparse matrices (symbolic phase) */ #define spm_mul_num _glp_spm_mul_num void spm_mul_num(SPM *C, const SPM *A, const SPM *B); /* multiply two sparse matrices (numeric phase) */ #define spm_mul_mat _glp_spm_mul_mat SPM *spm_mul_mat(const SPM *A, const SPM *B); /* multiply two sparse matrices (driver routine) */ #define spm_create_per _glp_spm_create_per PER *spm_create_per(int n); /* create permutation matrix */ #define spm_check_per _glp_spm_check_per void spm_check_per(PER *P); /* check permutation matrix for correctness */ #define spm_delete_per _glp_spm_delete_per void spm_delete_per(PER *P); /* delete permutation matrix */ #endif /* eof */ igraph/src/glpk/glpnet.h0000644000176000001440000000437312325527073014757 0ustar ripleyusers/* glpnet.h (graph and network algorithms) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPNET_H #define GLPNET_H #define mc21a _glp_mc21a int mc21a(int n, const int icn[], const int ip[], const int lenr[], int iperm[], int pr[], int arp[], int cv[], int out[]); /* permutations for zero-free diagonal */ #define mc13d _glp_mc13d int mc13d(int n, const int icn[], const int ip[], const int lenr[], int ior[], int ib[], int lowl[], int numb[], int prev[]); /* permutations to block triangular form */ #define okalg _glp_okalg int okalg(int nv, int na, const int tail[], const int head[], const int low[], const int cap[], const int cost[], int x[], int pi[]); /* out-of-kilter algorithm */ #define ffalg _glp_ffalg void ffalg(int nv, int na, const int tail[], const int head[], int s, int t, const int cap[], int x[], char cut[]); /* Ford-Fulkerson algorithm */ #define wclique _glp_wclique int wclique(int n, const int w[], const unsigned char a[], int ind[]); /* find maximum weight clique with Ostergard's algorithm */ #define kellerman _glp_kellerman int kellerman(int n, int (*func)(void *info, int i, int ind[]), void *info, void /* glp_graph */ *H); /* cover edges by cliques with Kellerman's heuristic */ #endif /* eof */ igraph/src/glpk/glpbfx.h0000644000176000001440000000455512325527073014752 0ustar ripleyusers/* glpbfx.h (basis factorization interface, bignum arithmetic) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPBFX_H #define GLPBFX_H #include "glpgmp.h" #ifndef GLPBFX_DEFINED #define GLPBFX_DEFINED typedef struct { double _opaque_bfx; } BFX; #endif #define bfx_create_binv _glp_bfx_create_binv #define bfx_is_valid _glp_bfx_is_valid #define bfx_invalidate _glp_bfx_invalidate #define bfx_factorize _glp_bfx_factorize #define bfx_ftran _glp_bfx_ftran #define bfx_btran _glp_bfx_btran #define bfx_update _glp_bfx_update #define bfx_delete_binv _glp_bfx_delete_binv BFX *bfx_create_binv(void); /* create factorization of the basis matrix */ int bfx_is_valid(BFX *binv); /* check if factorization is valid */ void bfx_invalidate(BFX *binv); /* invalidate factorization of the basis matrix */ int bfx_factorize(BFX *binv, int m, int (*col)(void *info, int j, int ind[], mpq_t val[]), void *info); /* compute factorization of the basis matrix */ void bfx_ftran(BFX *binv, mpq_t x[], int save); /* perform forward transformation (FTRAN) */ void bfx_btran(BFX *binv, mpq_t x[]); /* perform backward transformation (BTRAN) */ int bfx_update(BFX *binv, int j); /* update factorization of the basis matrix */ void bfx_delete_binv(BFX *binv); /* delete factorization of the basis matrix */ #endif /* eof */ igraph/src/glpk/glprng.h0000644000176000001440000000434612325527073014757 0ustar ripleyusers/* glprng.h (pseudo-random number generator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPRNG_H #define GLPRNG_H typedef struct RNG RNG; struct RNG { /* Knuth's portable pseudo-random number generator */ int A[56]; /* pseudo-random values */ int *fptr; /* the next A value to be exported */ }; #define rng_create_rand _glp_rng_create_rand RNG *rng_create_rand(void); /* create pseudo-random number generator */ #define rng_init_rand _glp_rng_init_rand void rng_init_rand(RNG *rand, int seed); /* initialize pseudo-random number generator */ #define rng_next_rand _glp_rng_next_rand int rng_next_rand(RNG *rand); /* obtain pseudo-random integer in the range [0, 2^31-1] */ #define rng_unif_rand _glp_rng_unif_rand int rng_unif_rand(RNG *rand, int m); /* obtain pseudo-random integer in the range [0, m-1] */ #define rng_delete_rand _glp_rng_delete_rand void rng_delete_rand(RNG *rand); /* delete pseudo-random number generator */ #define rng_unif_01 _glp_rng_unif_01 double rng_unif_01(RNG *rand); /* obtain pseudo-random number in the range [0, 1] */ #define rng_uniform _glp_rng_uniform double rng_uniform(RNG *rand, double a, double b); /* obtain pseudo-random number in the range [a, b] */ #endif /* eof */ igraph/src/glpk/glpmpl.h0000644000176000001440000025714712325527073014772 0ustar ripleyusers/* glpmpl.h (GNU MathProg translator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPMPL_H #define GLPMPL_H #include "glpavl.h" #include "glprng.h" typedef struct MPL MPL; typedef char STRING; typedef struct SYMBOL SYMBOL; typedef struct TUPLE TUPLE; typedef struct ARRAY ELEMSET; typedef struct ELEMVAR ELEMVAR; typedef struct FORMULA FORMULA; typedef struct ELEMCON ELEMCON; typedef union VALUE VALUE; typedef struct ARRAY ARRAY; typedef struct MEMBER MEMBER; #if 1 /* many C compilers have DOMAIN declared in :( */ #undef DOMAIN #define DOMAIN DOMAIN1 #endif typedef struct DOMAIN DOMAIN; typedef struct DOMAIN_BLOCK DOMAIN_BLOCK; typedef struct DOMAIN_SLOT DOMAIN_SLOT; typedef struct SET SET; typedef struct WITHIN WITHIN; typedef struct GADGET GADGET; typedef struct PARAMETER PARAMETER; typedef struct CONDITION CONDITION; typedef struct VARIABLE VARIABLE; typedef struct CONSTRAINT CONSTRAINT; typedef struct TABLE TABLE; typedef struct TABARG TABARG; typedef struct TABFLD TABFLD; typedef struct TABIN TABIN; typedef struct TABOUT TABOUT; typedef struct TABDCA TABDCA; typedef union OPERANDS OPERANDS; typedef struct ARG_LIST ARG_LIST; typedef struct CODE CODE; typedef struct CHECK CHECK; typedef struct DISPLAY DISPLAY; typedef struct DISPLAY1 DISPLAY1; typedef struct PRINTF PRINTF; typedef struct PRINTF1 PRINTF1; typedef struct FOR FOR; typedef struct STATEMENT STATEMENT; typedef struct TUPLE SLICE; /**********************************************************************/ /* * * TRANSLATOR DATABASE * * */ /**********************************************************************/ #define A_BINARY 101 /* something binary */ #define A_CHECK 102 /* check statement */ #define A_CONSTRAINT 103 /* model constraint */ #define A_DISPLAY 104 /* display statement */ #define A_ELEMCON 105 /* elemental constraint/objective */ #define A_ELEMSET 106 /* elemental set */ #define A_ELEMVAR 107 /* elemental variable */ #define A_EXPRESSION 108 /* expression */ #define A_FOR 109 /* for statement */ #define A_FORMULA 110 /* formula */ #define A_INDEX 111 /* dummy index */ #define A_INPUT 112 /* input table */ #define A_INTEGER 113 /* something integer */ #define A_LOGICAL 114 /* something logical */ #define A_MAXIMIZE 115 /* objective has to be maximized */ #define A_MINIMIZE 116 /* objective has to be minimized */ #define A_NONE 117 /* nothing */ #define A_NUMERIC 118 /* something numeric */ #define A_OUTPUT 119 /* output table */ #define A_PARAMETER 120 /* model parameter */ #define A_PRINTF 121 /* printf statement */ #define A_SET 122 /* model set */ #define A_SOLVE 123 /* solve statement */ #define A_SYMBOLIC 124 /* something symbolic */ #define A_TABLE 125 /* data table */ #define A_TUPLE 126 /* n-tuple */ #define A_VARIABLE 127 /* model variable */ #define MAX_LENGTH 100 /* maximal length of any symbolic value (this includes symbolic names, numeric and string literals, and all symbolic values that may appear during the evaluation phase) */ #define CONTEXT_SIZE 60 /* size of the context queue, in characters */ #define OUTBUF_SIZE 1024 /* size of the output buffer, in characters */ struct MPL { /* translator database */ /*--------------------------------------------------------------*/ /* scanning segment */ int line; /* number of the current text line */ int c; /* the current character or EOF */ int token; /* the current token: */ #define T_EOF 201 /* end of file */ #define T_NAME 202 /* symbolic name (model section only) */ #define T_SYMBOL 203 /* symbol (data section only) */ #define T_NUMBER 204 /* numeric literal */ #define T_STRING 205 /* string literal */ #define T_AND 206 /* and && */ #define T_BY 207 /* by */ #define T_CROSS 208 /* cross */ #define T_DIFF 209 /* diff */ #define T_DIV 210 /* div */ #define T_ELSE 211 /* else */ #define T_IF 212 /* if */ #define T_IN 213 /* in */ #define T_INFINITY 214 /* Infinity */ #define T_INTER 215 /* inter */ #define T_LESS 216 /* less */ #define T_MOD 217 /* mod */ #define T_NOT 218 /* not ! */ #define T_OR 219 /* or || */ #define T_SPTP 220 /* s.t. */ #define T_SYMDIFF 221 /* symdiff */ #define T_THEN 222 /* then */ #define T_UNION 223 /* union */ #define T_WITHIN 224 /* within */ #define T_PLUS 225 /* + */ #define T_MINUS 226 /* - */ #define T_ASTERISK 227 /* * */ #define T_SLASH 228 /* / */ #define T_POWER 229 /* ^ ** */ #define T_LT 230 /* < */ #define T_LE 231 /* <= */ #define T_EQ 232 /* = == */ #define T_GE 233 /* >= */ #define T_GT 234 /* > */ #define T_NE 235 /* <> != */ #define T_CONCAT 236 /* & */ #define T_BAR 237 /* | */ #define T_POINT 238 /* . */ #define T_COMMA 239 /* , */ #define T_COLON 240 /* : */ #define T_SEMICOLON 241 /* ; */ #define T_ASSIGN 242 /* := */ #define T_DOTS 243 /* .. */ #define T_LEFT 244 /* ( */ #define T_RIGHT 245 /* ) */ #define T_LBRACKET 246 /* [ */ #define T_RBRACKET 247 /* ] */ #define T_LBRACE 248 /* { */ #define T_RBRACE 249 /* } */ #define T_APPEND 250 /* >> */ #define T_TILDE 251 /* ~ */ #define T_INPUT 252 /* <- */ int imlen; /* length of the current token */ char *image; /* char image[MAX_LENGTH+1]; */ /* image of the current token */ double value; /* value of the current token (for T_NUMBER only) */ int b_token; /* the previous token */ int b_imlen; /* length of the previous token */ char *b_image; /* char b_image[MAX_LENGTH+1]; */ /* image of the previous token */ double b_value; /* value of the previous token (if token is T_NUMBER) */ int f_dots; /* if this flag is set, the next token should be recognized as T_DOTS, not as T_POINT */ int f_scan; /* if this flag is set, the next token is already scanned */ int f_token; /* the next token */ int f_imlen; /* length of the next token */ char *f_image; /* char f_image[MAX_LENGTH+1]; */ /* image of the next token */ double f_value; /* value of the next token (if token is T_NUMBER) */ char *context; /* char context[CONTEXT_SIZE]; */ /* context circular queue (not null-terminated!) */ int c_ptr; /* pointer to the current position in the context queue */ int flag_d; /* if this flag is set, the data section is being processed */ /*--------------------------------------------------------------*/ /* translating segment */ DMP *pool; /* memory pool used to allocate all data instances created during the translation phase */ AVL *tree; /* symbolic name table: node.type = A_INDEX => node.link -> DOMAIN_SLOT node.type = A_SET => node.link -> SET node.type = A_PARAMETER => node.link -> PARAMETER node.type = A_VARIABLE => node.link -> VARIABLE node.type = A_CONSTRANT => node.link -> CONSTRAINT */ STATEMENT *model; /* linked list of model statements in the original order */ int flag_x; /* if this flag is set, the current token being left parenthesis begins a slice that allows recognizing any undeclared symbolic names as dummy indices; this flag is automatically reset once the next token has been scanned */ int as_within; /* the warning "in understood as within" has been issued */ int as_in; /* the warning "within understood as in" has been issued */ int as_binary; /* the warning "logical understood as binary" has been issued */ int flag_s; /* if this flag is set, the solve statement has been parsed */ /*--------------------------------------------------------------*/ /* common segment */ DMP *strings; /* memory pool to allocate STRING data structures */ DMP *symbols; /* memory pool to allocate SYMBOL data structures */ DMP *tuples; /* memory pool to allocate TUPLE data structures */ DMP *arrays; /* memory pool to allocate ARRAY data structures */ DMP *members; /* memory pool to allocate MEMBER data structures */ DMP *elemvars; /* memory pool to allocate ELEMVAR data structures */ DMP *formulae; /* memory pool to allocate FORMULA data structures */ DMP *elemcons; /* memory pool to allocate ELEMCON data structures */ ARRAY *a_list; /* linked list of all arrays in the database */ char *sym_buf; /* char sym_buf[255+1]; */ /* working buffer used by the routine format_symbol */ char *tup_buf; /* char tup_buf[255+1]; */ /* working buffer used by the routine format_tuple */ /*--------------------------------------------------------------*/ /* generating/postsolving segment */ RNG *rand; /* pseudo-random number generator */ int flag_p; /* if this flag is set, the postsolving phase is in effect */ STATEMENT *stmt; /* model statement being currently executed */ TABDCA *dca; /* pointer to table driver communication area for table statement currently executed */ int m; /* number of rows in the problem, m >= 0 */ int n; /* number of columns in the problem, n >= 0 */ ELEMCON **row; /* ELEMCON *row[1+m]; */ /* row[0] is not used; row[i] is elemental constraint or objective, which corresponds to i-th row of the problem, 1 <= i <= m */ ELEMVAR **col; /* ELEMVAR *col[1+n]; */ /* col[0] is not used; col[j] is elemental variable, which corresponds to j-th column of the problem, 1 <= j <= n */ /*--------------------------------------------------------------*/ /* input/output segment */ XFILE *in_fp; /* stream assigned to the input text file */ char *in_file; /* name of the input text file */ XFILE *out_fp; /* stream assigned to the output text file used to write all data produced by display and printf statements; NULL means the data should be sent to stdout via the routine xprintf */ char *out_file; /* name of the output text file */ #if 0 /* 08/XI-2009 */ char *out_buf; /* char out_buf[OUTBUF_SIZE] */ /* buffer to accumulate output data */ int out_cnt; /* count of data bytes stored in the output buffer */ #endif XFILE *prt_fp; /* stream assigned to the print text file; may be NULL */ char *prt_file; /* name of the output print file */ /*--------------------------------------------------------------*/ /* solver interface segment */ jmp_buf jump; /* jump address for non-local go to in case of error */ int phase; /* phase of processing: 0 - database is being or has been initialized 1 - model section is being or has been read 2 - data section is being or has been read 3 - model is being or has been generated/postsolved 4 - model processing error has occurred */ char *mod_file; /* name of the input text file, which contains model section */ char *mpl_buf; /* char mpl_buf[255+1]; */ /* working buffer used by some interface routines */ }; /**********************************************************************/ /* * * PROCESSING MODEL SECTION * * */ /**********************************************************************/ #define alloc(type) ((type *)dmp_get_atomv(mpl->pool, sizeof(type))) /* allocate atom of given type */ #define enter_context _glp_mpl_enter_context void enter_context(MPL *mpl); /* enter current token into context queue */ #define print_context _glp_mpl_print_context void print_context(MPL *mpl); /* print current content of context queue */ #define get_char _glp_mpl_get_char void get_char(MPL *mpl); /* scan next character from input text file */ #define append_char _glp_mpl_append_char void append_char(MPL *mpl); /* append character to current token */ #define get_token _glp_mpl_get_token void get_token(MPL *mpl); /* scan next token from input text file */ #define unget_token _glp_mpl_unget_token void unget_token(MPL *mpl); /* return current token back to input stream */ #define is_keyword _glp_mpl_is_keyword int is_keyword(MPL *mpl, char *keyword); /* check if current token is given non-reserved keyword */ #define is_reserved _glp_mpl_is_reserved int is_reserved(MPL *mpl); /* check if current token is reserved keyword */ #define make_code _glp_mpl_make_code CODE *make_code(MPL *mpl, int op, OPERANDS *arg, int type, int dim); /* generate pseudo-code (basic routine) */ #define make_unary _glp_mpl_make_unary CODE *make_unary(MPL *mpl, int op, CODE *x, int type, int dim); /* generate pseudo-code for unary operation */ #define make_binary _glp_mpl_make_binary CODE *make_binary(MPL *mpl, int op, CODE *x, CODE *y, int type, int dim); /* generate pseudo-code for binary operation */ #define make_ternary _glp_mpl_make_ternary CODE *make_ternary(MPL *mpl, int op, CODE *x, CODE *y, CODE *z, int type, int dim); /* generate pseudo-code for ternary operation */ #define numeric_literal _glp_mpl_numeric_literal CODE *numeric_literal(MPL *mpl); /* parse reference to numeric literal */ #define string_literal _glp_mpl_string_literal CODE *string_literal(MPL *mpl); /* parse reference to string literal */ #define create_arg_list _glp_mpl_create_arg_list ARG_LIST *create_arg_list(MPL *mpl); /* create empty operands list */ #define expand_arg_list _glp_mpl_expand_arg_list ARG_LIST *expand_arg_list(MPL *mpl, ARG_LIST *list, CODE *x); /* append operand to operands list */ #define arg_list_len _glp_mpl_arg_list_len int arg_list_len(MPL *mpl, ARG_LIST *list); /* determine length of operands list */ #define subscript_list _glp_mpl_subscript_list ARG_LIST *subscript_list(MPL *mpl); /* parse subscript list */ #define object_reference _glp_mpl_object_reference CODE *object_reference(MPL *mpl); /* parse reference to named object */ #define numeric_argument _glp_mpl_numeric_argument CODE *numeric_argument(MPL *mpl, char *func); /* parse argument passed to built-in function */ #define symbolic_argument _glp_mpl_symbolic_argument CODE *symbolic_argument(MPL *mpl, char *func); #define elemset_argument _glp_mpl_elemset_argument CODE *elemset_argument(MPL *mpl, char *func); #define function_reference _glp_mpl_function_reference CODE *function_reference(MPL *mpl); /* parse reference to built-in function */ #define create_domain _glp_mpl_create_domain DOMAIN *create_domain(MPL *mpl); /* create empty domain */ #define create_block _glp_mpl_create_block DOMAIN_BLOCK *create_block(MPL *mpl); /* create empty domain block */ #define append_block _glp_mpl_append_block void append_block(MPL *mpl, DOMAIN *domain, DOMAIN_BLOCK *block); /* append domain block to specified domain */ #define append_slot _glp_mpl_append_slot DOMAIN_SLOT *append_slot(MPL *mpl, DOMAIN_BLOCK *block, char *name, CODE *code); /* create and append new slot to domain block */ #define expression_list _glp_mpl_expression_list CODE *expression_list(MPL *mpl); /* parse expression list */ #define literal_set _glp_mpl_literal_set CODE *literal_set(MPL *mpl, CODE *code); /* parse literal set */ #define indexing_expression _glp_mpl_indexing_expression DOMAIN *indexing_expression(MPL *mpl); /* parse indexing expression */ #define close_scope _glp_mpl_close_scope void close_scope(MPL *mpl, DOMAIN *domain); /* close scope of indexing expression */ #define iterated_expression _glp_mpl_iterated_expression CODE *iterated_expression(MPL *mpl); /* parse iterated expression */ #define domain_arity _glp_mpl_domain_arity int domain_arity(MPL *mpl, DOMAIN *domain); /* determine arity of domain */ #define set_expression _glp_mpl_set_expression CODE *set_expression(MPL *mpl); /* parse set expression */ #define branched_expression _glp_mpl_branched_expression CODE *branched_expression(MPL *mpl); /* parse conditional expression */ #define primary_expression _glp_mpl_primary_expression CODE *primary_expression(MPL *mpl); /* parse primary expression */ #define error_preceding _glp_mpl_error_preceding void error_preceding(MPL *mpl, char *opstr); /* raise error if preceding operand has wrong type */ #define error_following _glp_mpl_error_following void error_following(MPL *mpl, char *opstr); /* raise error if following operand has wrong type */ #define error_dimension _glp_mpl_error_dimension void error_dimension(MPL *mpl, char *opstr, int dim1, int dim2); /* raise error if operands have different dimension */ #define expression_0 _glp_mpl_expression_0 CODE *expression_0(MPL *mpl); /* parse expression of level 0 */ #define expression_1 _glp_mpl_expression_1 CODE *expression_1(MPL *mpl); /* parse expression of level 1 */ #define expression_2 _glp_mpl_expression_2 CODE *expression_2(MPL *mpl); /* parse expression of level 2 */ #define expression_3 _glp_mpl_expression_3 CODE *expression_3(MPL *mpl); /* parse expression of level 3 */ #define expression_4 _glp_mpl_expression_4 CODE *expression_4(MPL *mpl); /* parse expression of level 4 */ #define expression_5 _glp_mpl_expression_5 CODE *expression_5(MPL *mpl); /* parse expression of level 5 */ #define expression_6 _glp_mpl_expression_6 CODE *expression_6(MPL *mpl); /* parse expression of level 6 */ #define expression_7 _glp_mpl_expression_7 CODE *expression_7(MPL *mpl); /* parse expression of level 7 */ #define expression_8 _glp_mpl_expression_8 CODE *expression_8(MPL *mpl); /* parse expression of level 8 */ #define expression_9 _glp_mpl_expression_9 CODE *expression_9(MPL *mpl); /* parse expression of level 9 */ #define expression_10 _glp_mpl_expression_10 CODE *expression_10(MPL *mpl); /* parse expression of level 10 */ #define expression_11 _glp_mpl_expression_11 CODE *expression_11(MPL *mpl); /* parse expression of level 11 */ #define expression_12 _glp_mpl_expression_12 CODE *expression_12(MPL *mpl); /* parse expression of level 12 */ #define expression_13 _glp_mpl_expression_13 CODE *expression_13(MPL *mpl); /* parse expression of level 13 */ #define set_statement _glp_mpl_set_statement SET *set_statement(MPL *mpl); /* parse set statement */ #define parameter_statement _glp_mpl_parameter_statement PARAMETER *parameter_statement(MPL *mpl); /* parse parameter statement */ #define variable_statement _glp_mpl_variable_statement VARIABLE *variable_statement(MPL *mpl); /* parse variable statement */ #define constraint_statement _glp_mpl_constraint_statement CONSTRAINT *constraint_statement(MPL *mpl); /* parse constraint statement */ #define objective_statement _glp_mpl_objective_statement CONSTRAINT *objective_statement(MPL *mpl); /* parse objective statement */ #define table_statement _glp_mpl_table_statement TABLE *table_statement(MPL *mpl); /* parse table statement */ #define solve_statement _glp_mpl_solve_statement void *solve_statement(MPL *mpl); /* parse solve statement */ #define check_statement _glp_mpl_check_statement CHECK *check_statement(MPL *mpl); /* parse check statement */ #define display_statement _glp_mpl_display_statement DISPLAY *display_statement(MPL *mpl); /* parse display statement */ #define printf_statement _glp_mpl_printf_statement PRINTF *printf_statement(MPL *mpl); /* parse printf statement */ #define for_statement _glp_mpl_for_statement FOR *for_statement(MPL *mpl); /* parse for statement */ #define end_statement _glp_mpl_end_statement void end_statement(MPL *mpl); /* parse end statement */ #define simple_statement _glp_mpl_simple_statement STATEMENT *simple_statement(MPL *mpl, int spec); /* parse simple statement */ #define model_section _glp_mpl_model_section void model_section(MPL *mpl); /* parse model section */ /**********************************************************************/ /* * * PROCESSING DATA SECTION * * */ /**********************************************************************/ #if 2 + 2 == 5 struct SLICE /* see TUPLE */ { /* component of slice; the slice itself is associated with its first component; slices are similar to n-tuples with exception that some slice components (which are indicated by asterisks) don't refer to any symbols */ SYMBOL *sym; /* symbol, which this component refers to; can be NULL */ SLICE *next; /* the next component of slice */ }; #endif #define create_slice _glp_mpl_create_slice SLICE *create_slice(MPL *mpl); /* create slice */ #define expand_slice _glp_mpl_expand_slice SLICE *expand_slice ( MPL *mpl, SLICE *slice, /* destroyed */ SYMBOL *sym /* destroyed */ ); /* append new component to slice */ #define slice_dimen _glp_mpl_slice_dimen int slice_dimen ( MPL *mpl, SLICE *slice /* not changed */ ); /* determine dimension of slice */ #define slice_arity _glp_mpl_slice_arity int slice_arity ( MPL *mpl, SLICE *slice /* not changed */ ); /* determine arity of slice */ #define fake_slice _glp_mpl_fake_slice SLICE *fake_slice(MPL *mpl, int dim); /* create fake slice of all asterisks */ #define delete_slice _glp_mpl_delete_slice void delete_slice ( MPL *mpl, SLICE *slice /* destroyed */ ); /* delete slice */ #define is_number _glp_mpl_is_number int is_number(MPL *mpl); /* check if current token is number */ #define is_symbol _glp_mpl_is_symbol int is_symbol(MPL *mpl); /* check if current token is symbol */ #define is_literal _glp_mpl_is_literal int is_literal(MPL *mpl, char *literal); /* check if current token is given symbolic literal */ #define read_number _glp_mpl_read_number double read_number(MPL *mpl); /* read number */ #define read_symbol _glp_mpl_read_symbol SYMBOL *read_symbol(MPL *mpl); /* read symbol */ #define read_slice _glp_mpl_read_slice SLICE *read_slice ( MPL *mpl, char *name, /* not changed */ int dim ); /* read slice */ #define select_set _glp_mpl_select_set SET *select_set ( MPL *mpl, char *name /* not changed */ ); /* select set to saturate it with elemental sets */ #define simple_format _glp_mpl_simple_format void simple_format ( MPL *mpl, SET *set, /* not changed */ MEMBER *memb, /* modified */ SLICE *slice /* not changed */ ); /* read set data block in simple format */ #define matrix_format _glp_mpl_matrix_format void matrix_format ( MPL *mpl, SET *set, /* not changed */ MEMBER *memb, /* modified */ SLICE *slice, /* not changed */ int tr ); /* read set data block in matrix format */ #define set_data _glp_mpl_set_data void set_data(MPL *mpl); /* read set data */ #define select_parameter _glp_mpl_select_parameter PARAMETER *select_parameter ( MPL *mpl, char *name /* not changed */ ); /* select parameter to saturate it with data */ #define set_default _glp_mpl_set_default void set_default ( MPL *mpl, PARAMETER *par, /* not changed */ SYMBOL *altval /* destroyed */ ); /* set default parameter value */ #define read_value _glp_mpl_read_value MEMBER *read_value ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* destroyed */ ); /* read value and assign it to parameter member */ #define plain_format _glp_mpl_plain_format void plain_format ( MPL *mpl, PARAMETER *par, /* not changed */ SLICE *slice /* not changed */ ); /* read parameter data block in plain format */ #define tabular_format _glp_mpl_tabular_format void tabular_format ( MPL *mpl, PARAMETER *par, /* not changed */ SLICE *slice, /* not changed */ int tr ); /* read parameter data block in tabular format */ #define tabbing_format _glp_mpl_tabbing_format void tabbing_format ( MPL *mpl, SYMBOL *altval /* not changed */ ); /* read parameter data block in tabbing format */ #define parameter_data _glp_mpl_parameter_data void parameter_data(MPL *mpl); /* read parameter data */ #define data_section _glp_mpl_data_section void data_section(MPL *mpl); /* read data section */ /**********************************************************************/ /* * * FLOATING-POINT NUMBERS * * */ /**********************************************************************/ #define fp_add _glp_mpl_fp_add double fp_add(MPL *mpl, double x, double y); /* floating-point addition */ #define fp_sub _glp_mpl_fp_sub double fp_sub(MPL *mpl, double x, double y); /* floating-point subtraction */ #define fp_less _glp_mpl_fp_less double fp_less(MPL *mpl, double x, double y); /* floating-point non-negative subtraction */ #define fp_mul _glp_mpl_fp_mul double fp_mul(MPL *mpl, double x, double y); /* floating-point multiplication */ #define fp_div _glp_mpl_fp_div double fp_div(MPL *mpl, double x, double y); /* floating-point division */ #define fp_idiv _glp_mpl_fp_idiv double fp_idiv(MPL *mpl, double x, double y); /* floating-point quotient of exact division */ #define fp_mod _glp_mpl_fp_mod double fp_mod(MPL *mpl, double x, double y); /* floating-point remainder of exact division */ #define fp_power _glp_mpl_fp_power double fp_power(MPL *mpl, double x, double y); /* floating-point exponentiation (raise to power) */ #define fp_exp _glp_mpl_fp_exp double fp_exp(MPL *mpl, double x); /* floating-point base-e exponential */ #define fp_log _glp_mpl_fp_log double fp_log(MPL *mpl, double x); /* floating-point natural logarithm */ #define fp_log10 _glp_mpl_fp_log10 double fp_log10(MPL *mpl, double x); /* floating-point common (decimal) logarithm */ #define fp_sqrt _glp_mpl_fp_sqrt double fp_sqrt(MPL *mpl, double x); /* floating-point square root */ #define fp_sin _glp_mpl_fp_sin double fp_sin(MPL *mpl, double x); /* floating-point trigonometric sine */ #define fp_cos _glp_mpl_fp_cos double fp_cos(MPL *mpl, double x); /* floating-point trigonometric cosine */ #define fp_atan _glp_mpl_fp_atan double fp_atan(MPL *mpl, double x); /* floating-point trigonometric arctangent */ #define fp_atan2 _glp_mpl_fp_atan2 double fp_atan2(MPL *mpl, double y, double x); /* floating-point trigonometric arctangent */ #define fp_round _glp_mpl_fp_round double fp_round(MPL *mpl, double x, double n); /* round floating-point value to n fractional digits */ #define fp_trunc _glp_mpl_fp_trunc double fp_trunc(MPL *mpl, double x, double n); /* truncate floating-point value to n fractional digits */ /**********************************************************************/ /* * * PSEUDO-RANDOM NUMBER GENERATORS * * */ /**********************************************************************/ #define fp_irand224 _glp_mpl_fp_irand224 double fp_irand224(MPL *mpl); /* pseudo-random integer in the range [0, 2^24) */ #define fp_uniform01 _glp_mpl_fp_uniform01 double fp_uniform01(MPL *mpl); /* pseudo-random number in the range [0, 1) */ #define fp_uniform _glp_mpl_uniform double fp_uniform(MPL *mpl, double a, double b); /* pseudo-random number in the range [a, b) */ #define fp_normal01 _glp_mpl_fp_normal01 double fp_normal01(MPL *mpl); /* Gaussian random variate with mu = 0 and sigma = 1 */ #define fp_normal _glp_mpl_fp_normal double fp_normal(MPL *mpl, double mu, double sigma); /* Gaussian random variate with specified mu and sigma */ /**********************************************************************/ /* * * DATE/TIME * * */ /**********************************************************************/ #define fn_gmtime _glp_mpl_fn_gmtime double fn_gmtime(MPL *mpl); /* obtain the current calendar time (UTC) */ #define fn_str2time _glp_mpl_fn_str2time double fn_str2time(MPL *mpl, const char *str, const char *fmt); /* convert character string to the calendar time */ #define fn_time2str _glp_mpl_fn_time2str void fn_time2str(MPL *mpl, char *str, double t, const char *fmt); /* convert the calendar time to character string */ /**********************************************************************/ /* * * CHARACTER STRINGS * * */ /**********************************************************************/ #define create_string _glp_mpl_create_string STRING *create_string ( MPL *mpl, char buf[MAX_LENGTH+1] /* not changed */ ); /* create character string */ #define copy_string _glp_mpl_copy_string STRING *copy_string ( MPL *mpl, STRING *str /* not changed */ ); /* make copy of character string */ #define compare_strings _glp_mpl_compare_strings int compare_strings ( MPL *mpl, STRING *str1, /* not changed */ STRING *str2 /* not changed */ ); /* compare one character string with another */ #define fetch_string _glp_mpl_fetch_string char *fetch_string ( MPL *mpl, STRING *str, /* not changed */ char buf[MAX_LENGTH+1] /* modified */ ); /* extract content of character string */ #define delete_string _glp_mpl_delete_string void delete_string ( MPL *mpl, STRING *str /* destroyed */ ); /* delete character string */ /**********************************************************************/ /* * * SYMBOLS * * */ /**********************************************************************/ struct SYMBOL { /* symbol (numeric or abstract quantity) */ double num; /* numeric value of symbol (used only if str == NULL) */ STRING *str; /* abstract value of symbol (used only if str != NULL) */ }; #define create_symbol_num _glp_mpl_create_symbol_num SYMBOL *create_symbol_num(MPL *mpl, double num); /* create symbol of numeric type */ #define create_symbol_str _glp_mpl_create_symbol_str SYMBOL *create_symbol_str ( MPL *mpl, STRING *str /* destroyed */ ); /* create symbol of abstract type */ #define copy_symbol _glp_mpl_copy_symbol SYMBOL *copy_symbol ( MPL *mpl, SYMBOL *sym /* not changed */ ); /* make copy of symbol */ #define compare_symbols _glp_mpl_compare_symbols int compare_symbols ( MPL *mpl, SYMBOL *sym1, /* not changed */ SYMBOL *sym2 /* not changed */ ); /* compare one symbol with another */ #define delete_symbol _glp_mpl_delete_symbol void delete_symbol ( MPL *mpl, SYMBOL *sym /* destroyed */ ); /* delete symbol */ #define format_symbol _glp_mpl_format_symbol char *format_symbol ( MPL *mpl, SYMBOL *sym /* not changed */ ); /* format symbol for displaying or printing */ #define concat_symbols _glp_mpl_concat_symbols SYMBOL *concat_symbols ( MPL *mpl, SYMBOL *sym1, /* destroyed */ SYMBOL *sym2 /* destroyed */ ); /* concatenate one symbol with another */ /**********************************************************************/ /* * * N-TUPLES * * */ /**********************************************************************/ struct TUPLE { /* component of n-tuple; the n-tuple itself is associated with its first component; (note that 0-tuple has no components) */ SYMBOL *sym; /* symbol, which the component refers to; cannot be NULL */ TUPLE *next; /* the next component of n-tuple */ }; #define create_tuple _glp_mpl_create_tuple TUPLE *create_tuple(MPL *mpl); /* create n-tuple */ #define expand_tuple _glp_mpl_expand_tuple TUPLE *expand_tuple ( MPL *mpl, TUPLE *tuple, /* destroyed */ SYMBOL *sym /* destroyed */ ); /* append symbol to n-tuple */ #define tuple_dimen _glp_mpl_tuple_dimen int tuple_dimen ( MPL *mpl, TUPLE *tuple /* not changed */ ); /* determine dimension of n-tuple */ #define copy_tuple _glp_mpl_copy_tuple TUPLE *copy_tuple ( MPL *mpl, TUPLE *tuple /* not changed */ ); /* make copy of n-tuple */ #define compare_tuples _glp_mpl_compare_tuples int compare_tuples ( MPL *mpl, TUPLE *tuple1, /* not changed */ TUPLE *tuple2 /* not changed */ ); /* compare one n-tuple with another */ #define build_subtuple _glp_mpl_build_subtuple TUPLE *build_subtuple ( MPL *mpl, TUPLE *tuple, /* not changed */ int dim ); /* build subtuple of given n-tuple */ #define delete_tuple _glp_mpl_delete_tuple void delete_tuple ( MPL *mpl, TUPLE *tuple /* destroyed */ ); /* delete n-tuple */ #define format_tuple _glp_mpl_format_tuple char *format_tuple ( MPL *mpl, int c, TUPLE *tuple /* not changed */ ); /* format n-tuple for displaying or printing */ /**********************************************************************/ /* * * ELEMENTAL SETS * * */ /**********************************************************************/ #if 2 + 2 == 5 struct ELEMSET /* see ARRAY */ { /* elemental set of n-tuples; formally it is a "value" assigned to members of model sets (like numbers and symbols, which are values assigned to members of model parameters); note that a simple model set is not an elemental set, it is 0-dimensional array, the only member of which (if it exists) is assigned an elemental set */ #endif #define create_elemset _glp_mpl_create_elemset ELEMSET *create_elemset(MPL *mpl, int dim); /* create elemental set */ #define find_tuple _glp_mpl_find_tuple MEMBER *find_tuple ( MPL *mpl, ELEMSET *set, /* not changed */ TUPLE *tuple /* not changed */ ); /* check if elemental set contains given n-tuple */ #define add_tuple _glp_mpl_add_tuple MEMBER *add_tuple ( MPL *mpl, ELEMSET *set, /* modified */ TUPLE *tuple /* destroyed */ ); /* add new n-tuple to elemental set */ #define check_then_add _glp_mpl_check_then_add MEMBER *check_then_add ( MPL *mpl, ELEMSET *set, /* modified */ TUPLE *tuple /* destroyed */ ); /* check and add new n-tuple to elemental set */ #define copy_elemset _glp_mpl_copy_elemset ELEMSET *copy_elemset ( MPL *mpl, ELEMSET *set /* not changed */ ); /* make copy of elemental set */ #define delete_elemset _glp_mpl_delete_elemset void delete_elemset ( MPL *mpl, ELEMSET *set /* destroyed */ ); /* delete elemental set */ #define arelset_size _glp_mpl_arelset_size int arelset_size(MPL *mpl, double t0, double tf, double dt); /* compute size of "arithmetic" elemental set */ #define arelset_member _glp_mpl_arelset_member double arelset_member(MPL *mpl, double t0, double tf, double dt, int j); /* compute member of "arithmetic" elemental set */ #define create_arelset _glp_mpl_create_arelset ELEMSET *create_arelset(MPL *mpl, double t0, double tf, double dt); /* create "arithmetic" elemental set */ #define set_union _glp_mpl_set_union ELEMSET *set_union ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ); /* union of two elemental sets */ #define set_diff _glp_mpl_set_diff ELEMSET *set_diff ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ); /* difference between two elemental sets */ #define set_symdiff _glp_mpl_set_symdiff ELEMSET *set_symdiff ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ); /* symmetric difference between two elemental sets */ #define set_inter _glp_mpl_set_inter ELEMSET *set_inter ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ); /* intersection of two elemental sets */ #define set_cross _glp_mpl_set_cross ELEMSET *set_cross ( MPL *mpl, ELEMSET *X, /* destroyed */ ELEMSET *Y /* destroyed */ ); /* cross (Cartesian) product of two elemental sets */ /**********************************************************************/ /* * * ELEMENTAL VARIABLES * * */ /**********************************************************************/ struct ELEMVAR { /* elemental variable; formally it is a "value" assigned to members of model variables (like numbers and symbols, which are values assigned to members of model parameters) */ int j; /* LP column number assigned to this elemental variable */ VARIABLE *var; /* model variable, which contains this elemental variable */ MEMBER *memb; /* array member, which is assigned this elemental variable */ double lbnd; /* lower bound */ double ubnd; /* upper bound */ double temp; /* working quantity used in operations on linear forms; normally it contains floating-point zero */ #if 1 /* 15/V-2010 */ int stat; double prim, dual; /* solution components provided by the solver */ #endif }; /**********************************************************************/ /* * * LINEAR FORMS * * */ /**********************************************************************/ struct FORMULA { /* term of linear form c * x, where c is a coefficient, x is an elemental variable; the linear form itself is the sum of terms and is associated with its first term; (note that the linear form may be empty that means the sum is equal to zero) */ double coef; /* coefficient at elemental variable or constant term */ ELEMVAR *var; /* reference to elemental variable; NULL means constant term */ FORMULA *next; /* the next term of linear form */ }; #define constant_term _glp_mpl_constant_term FORMULA *constant_term(MPL *mpl, double coef); /* create constant term */ #define single_variable _glp_mpl_single_variable FORMULA *single_variable ( MPL *mpl, ELEMVAR *var /* referenced */ ); /* create single variable */ #define copy_formula _glp_mpl_copy_formula FORMULA *copy_formula ( MPL *mpl, FORMULA *form /* not changed */ ); /* make copy of linear form */ #define delete_formula _glp_mpl_delete_formula void delete_formula ( MPL *mpl, FORMULA *form /* destroyed */ ); /* delete linear form */ #define linear_comb _glp_mpl_linear_comb FORMULA *linear_comb ( MPL *mpl, double a, FORMULA *fx, /* destroyed */ double b, FORMULA *fy /* destroyed */ ); /* linear combination of two linear forms */ #define remove_constant _glp_mpl_remove_constant FORMULA *remove_constant ( MPL *mpl, FORMULA *form, /* destroyed */ double *coef /* modified */ ); /* remove constant term from linear form */ #define reduce_terms _glp_mpl_reduce_terms FORMULA *reduce_terms ( MPL *mpl, FORMULA *form /* destroyed */ ); /* reduce identical terms in linear form */ /**********************************************************************/ /* * * ELEMENTAL CONSTRAINTS * * */ /**********************************************************************/ struct ELEMCON { /* elemental constraint; formally it is a "value" assigned to members of model constraints (like numbers or symbols, which are values assigned to members of model parameters) */ int i; /* LP row number assigned to this elemental constraint */ CONSTRAINT *con; /* model constraint, which contains this elemental constraint */ MEMBER *memb; /* array member, which is assigned this elemental constraint */ FORMULA *form; /* linear form */ double lbnd; /* lower bound */ double ubnd; /* upper bound */ #if 1 /* 15/V-2010 */ int stat; double prim, dual; /* solution components provided by the solver */ #endif }; /**********************************************************************/ /* * * GENERIC VALUES * * */ /**********************************************************************/ union VALUE { /* generic value, which can be assigned to object member or be a result of evaluation of expression */ /* indicator that specifies the particular type of generic value is stored in the corresponding array or pseudo-code descriptor and can be one of the following: A_NONE - no value A_NUMERIC - floating-point number A_SYMBOLIC - symbol A_LOGICAL - logical value A_TUPLE - n-tuple A_ELEMSET - elemental set A_ELEMVAR - elemental variable A_FORMULA - linear form A_ELEMCON - elemental constraint */ void *none; /* null */ double num; /* value */ SYMBOL *sym; /* value */ int bit; /* value */ TUPLE *tuple; /* value */ ELEMSET *set; /* value */ ELEMVAR *var; /* reference */ FORMULA *form; /* value */ ELEMCON *con; /* reference */ }; #define delete_value _glp_mpl_delete_value void delete_value ( MPL *mpl, int type, VALUE *value /* content destroyed */ ); /* delete generic value */ /**********************************************************************/ /* * * SYMBOLICALLY INDEXED ARRAYS * * */ /**********************************************************************/ struct ARRAY { /* multi-dimensional array, a set of members indexed over simple or compound sets of symbols; arrays are used to represent the contents of model objects (i.e. sets, parameters, variables, constraints, and objectives); arrays also are used as "values" that are assigned to members of set objects, in which case the array itself represents an elemental set */ int type; /* type of generic values assigned to the array members: A_NONE - none (members have no assigned values) A_NUMERIC - floating-point numbers A_SYMBOLIC - symbols A_ELEMSET - elemental sets A_ELEMVAR - elemental variables A_ELEMCON - elemental constraints */ int dim; /* dimension of the array that determines number of components in n-tuples for all members of the array, dim >= 0; dim = 0 means the array is 0-dimensional */ int size; /* size of the array, i.e. number of its members */ MEMBER *head; /* the first array member; NULL means the array is empty */ MEMBER *tail; /* the last array member; NULL means the array is empty */ AVL *tree; /* the search tree intended to find array members for logarithmic time; NULL means the search tree doesn't exist */ ARRAY *prev; /* the previous array in the translator database */ ARRAY *next; /* the next array in the translator database */ }; struct MEMBER { /* array member */ TUPLE *tuple; /* n-tuple, which identifies the member; number of its components is the same for all members within the array and determined by the array dimension; duplicate members are not allowed */ MEMBER *next; /* the next array member */ VALUE value; /* generic value assigned to the member */ }; #define create_array _glp_mpl_create_array ARRAY *create_array(MPL *mpl, int type, int dim); /* create array */ #define find_member _glp_mpl_find_member MEMBER *find_member ( MPL *mpl, ARRAY *array, /* not changed */ TUPLE *tuple /* not changed */ ); /* find array member with given n-tuple */ #define add_member _glp_mpl_add_member MEMBER *add_member ( MPL *mpl, ARRAY *array, /* modified */ TUPLE *tuple /* destroyed */ ); /* add new member to array */ #define delete_array _glp_mpl_delete_array void delete_array ( MPL *mpl, ARRAY *array /* destroyed */ ); /* delete array */ /**********************************************************************/ /* * * DOMAINS AND DUMMY INDICES * * */ /**********************************************************************/ struct DOMAIN { /* domain (a simple or compound set); syntactically domain looks like '{ i in I, (j,k) in S, t in T : }'; domains are used to define sets, over which model objects are indexed, and also as constituents of iterated operators */ DOMAIN_BLOCK *list; /* linked list of domain blocks (in the example above such blocks are 'i in I', '(j,k) in S', and 't in T'); this list cannot be empty */ CODE *code; /* pseudo-code for computing the logical predicate, which follows the colon; NULL means no predicate is specified */ }; struct DOMAIN_BLOCK { /* domain block; syntactically domain blocks look like 'i in I', '(j,k) in S', and 't in T' in the example above (in the sequel sets like I, S, and T are called basic sets) */ DOMAIN_SLOT *list; /* linked list of domain slots (i.e. indexing positions); number of slots in this list is the same as dimension of n-tuples in the basic set; this list cannot be empty */ CODE *code; /* pseudo-code for computing basic set; cannot be NULL */ TUPLE *backup; /* if this n-tuple is not empty, current values of dummy indices in the domain block are the same as components of this n-tuple (note that this n-tuple may have larger dimension than number of dummy indices in this block, in which case extra components are ignored); this n-tuple is used to restore former values of dummy indices, if they were changed due to recursive calls to the domain block */ DOMAIN_BLOCK *next; /* the next block in the same domain */ }; struct DOMAIN_SLOT { /* domain slot; it specifies an individual indexing position and defines the corresponding dummy index */ char *name; /* symbolic name of the dummy index; null pointer means the dummy index is not explicitly specified */ CODE *code; /* pseudo-code for computing symbolic value, at which the dummy index is bound; NULL means the dummy index is free within the domain scope */ SYMBOL *value; /* current value assigned to the dummy index; NULL means no value is assigned at the moment */ CODE *list; /* linked list of pseudo-codes with operation O_INDEX referring to this slot; this linked list is used to invalidate resultant values of the operation, which depend on this dummy index */ DOMAIN_SLOT *next; /* the next slot in the same domain block */ }; #define assign_dummy_index _glp_mpl_assign_dummy_index void assign_dummy_index ( MPL *mpl, DOMAIN_SLOT *slot, /* modified */ SYMBOL *value /* not changed */ ); /* assign new value to dummy index */ #define update_dummy_indices _glp_mpl_update_dummy_indices void update_dummy_indices ( MPL *mpl, DOMAIN_BLOCK *block /* not changed */ ); /* update current values of dummy indices */ #define enter_domain_block _glp_mpl_enter_domain_block int enter_domain_block ( MPL *mpl, DOMAIN_BLOCK *block, /* not changed */ TUPLE *tuple, /* not changed */ void *info, void (*func)(MPL *mpl, void *info) ); /* enter domain block */ #define eval_within_domain _glp_mpl_eval_within_domain int eval_within_domain ( MPL *mpl, DOMAIN *domain, /* not changed */ TUPLE *tuple, /* not changed */ void *info, void (*func)(MPL *mpl, void *info) ); /* perform evaluation within domain scope */ #define loop_within_domain _glp_mpl_loop_within_domain void loop_within_domain ( MPL *mpl, DOMAIN *domain, /* not changed */ void *info, int (*func)(MPL *mpl, void *info) ); /* perform iterations within domain scope */ #define out_of_domain _glp_mpl_out_of_domain void out_of_domain ( MPL *mpl, char *name, /* not changed */ TUPLE *tuple /* not changed */ ); /* raise domain exception */ #define get_domain_tuple _glp_mpl_get_domain_tuple TUPLE *get_domain_tuple ( MPL *mpl, DOMAIN *domain /* not changed */ ); /* obtain current n-tuple from domain */ #define clean_domain _glp_mpl_clean_domain void clean_domain(MPL *mpl, DOMAIN *domain); /* clean domain */ /**********************************************************************/ /* * * MODEL SETS * * */ /**********************************************************************/ struct SET { /* model set */ char *name; /* symbolic name; cannot be NULL */ char *alias; /* alias; NULL means alias is not specified */ int dim; /* aka arity */ /* dimension (number of subscripts); dim = 0 means 0-dimensional (unsubscripted) set, dim > 0 means set of sets */ DOMAIN *domain; /* subscript domain; NULL for 0-dimensional set */ int dimen; /* dimension of n-tuples, which members of this set consist of (note that the model set itself is an array of elemental sets, which are its members; so, don't confuse this dimension with dimension of the model set); always non-zero */ WITHIN *within; /* list of supersets, which restrict each member of the set to be in every superset from this list; this list can be empty */ CODE *assign; /* pseudo-code for computing assigned value; can be NULL */ CODE *option; /* pseudo-code for computing default value; can be NULL */ GADGET *gadget; /* plain set used to initialize the array of sets; can be NULL */ int data; /* data status flag: 0 - no data are provided in the data section 1 - data are provided, but not checked yet 2 - data are provided and have been checked */ ARRAY *array; /* array of members, which are assigned elemental sets */ }; struct WITHIN { /* restricting superset list entry */ CODE *code; /* pseudo-code for computing the superset; cannot be NULL */ WITHIN *next; /* the next entry for the same set or parameter */ }; struct GADGET { /* plain set used to initialize the array of sets with data */ SET *set; /* pointer to plain set; cannot be NULL */ int ind[20]; /* ind[dim+dimen]; */ /* permutation of integers 1, 2, ..., dim+dimen */ }; #define check_elem_set _glp_mpl_check_elem_set void check_elem_set ( MPL *mpl, SET *set, /* not changed */ TUPLE *tuple, /* not changed */ ELEMSET *refer /* not changed */ ); /* check elemental set assigned to set member */ #define take_member_set _glp_mpl_take_member_set ELEMSET *take_member_set /* returns reference, not value */ ( MPL *mpl, SET *set, /* not changed */ TUPLE *tuple /* not changed */ ); /* obtain elemental set assigned to set member */ #define eval_member_set _glp_mpl_eval_member_set ELEMSET *eval_member_set /* returns reference, not value */ ( MPL *mpl, SET *set, /* not changed */ TUPLE *tuple /* not changed */ ); /* evaluate elemental set assigned to set member */ #define eval_whole_set _glp_mpl_eval_whole_set void eval_whole_set(MPL *mpl, SET *set); /* evaluate model set over entire domain */ #define clean_set _glp_mpl_clean_set void clean_set(MPL *mpl, SET *set); /* clean model set */ /**********************************************************************/ /* * * MODEL PARAMETERS * * */ /**********************************************************************/ struct PARAMETER { /* model parameter */ char *name; /* symbolic name; cannot be NULL */ char *alias; /* alias; NULL means alias is not specified */ int dim; /* aka arity */ /* dimension (number of subscripts); dim = 0 means 0-dimensional (unsubscripted) parameter */ DOMAIN *domain; /* subscript domain; NULL for 0-dimensional parameter */ int type; /* parameter type: A_NUMERIC - numeric A_INTEGER - integer A_BINARY - binary A_SYMBOLIC - symbolic */ CONDITION *cond; /* list of conditions, which restrict each parameter member to satisfy to every condition from this list; this list is used only for numeric parameters and can be empty */ WITHIN *in; /* list of supersets, which restrict each parameter member to be in every superset from this list; this list is used only for symbolic parameters and can be empty */ CODE *assign; /* pseudo-code for computing assigned value; can be NULL */ CODE *option; /* pseudo-code for computing default value; can be NULL */ int data; /* data status flag: 0 - no data are provided in the data section 1 - data are provided, but not checked yet 2 - data are provided and have been checked */ SYMBOL *defval; /* default value provided in the data section; can be NULL */ ARRAY *array; /* array of members, which are assigned numbers or symbols */ }; struct CONDITION { /* restricting condition list entry */ int rho; /* flag that specifies the form of the condition: O_LT - less than O_LE - less than or equal to O_EQ - equal to O_GE - greater than or equal to O_GT - greater than O_NE - not equal to */ CODE *code; /* pseudo-code for computing the reference value */ CONDITION *next; /* the next entry for the same parameter */ }; #define check_value_num _glp_mpl_check_value_num void check_value_num ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple, /* not changed */ double value ); /* check numeric value assigned to parameter member */ #define take_member_num _glp_mpl_take_member_num double take_member_num ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ); /* obtain numeric value assigned to parameter member */ #define eval_member_num _glp_mpl_eval_member_num double eval_member_num ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ); /* evaluate numeric value assigned to parameter member */ #define check_value_sym _glp_mpl_check_value_sym void check_value_sym ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple, /* not changed */ SYMBOL *value /* not changed */ ); /* check symbolic value assigned to parameter member */ #define take_member_sym _glp_mpl_take_member_sym SYMBOL *take_member_sym /* returns value, not reference */ ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ); /* obtain symbolic value assigned to parameter member */ #define eval_member_sym _glp_mpl_eval_member_sym SYMBOL *eval_member_sym /* returns value, not reference */ ( MPL *mpl, PARAMETER *par, /* not changed */ TUPLE *tuple /* not changed */ ); /* evaluate symbolic value assigned to parameter member */ #define eval_whole_par _glp_mpl_eval_whole_par void eval_whole_par(MPL *mpl, PARAMETER *par); /* evaluate model parameter over entire domain */ #define clean_parameter _glp_mpl_clean_parameter void clean_parameter(MPL *mpl, PARAMETER *par); /* clean model parameter */ /**********************************************************************/ /* * * MODEL VARIABLES * * */ /**********************************************************************/ struct VARIABLE { /* model variable */ char *name; /* symbolic name; cannot be NULL */ char *alias; /* alias; NULL means alias is not specified */ int dim; /* aka arity */ /* dimension (number of subscripts); dim = 0 means 0-dimensional (unsubscripted) variable */ DOMAIN *domain; /* subscript domain; NULL for 0-dimensional variable */ int type; /* variable type: A_NUMERIC - continuous A_INTEGER - integer A_BINARY - binary */ CODE *lbnd; /* pseudo-code for computing lower bound; NULL means lower bound is not specified */ CODE *ubnd; /* pseudo-code for computing upper bound; NULL means upper bound is not specified */ /* if both the pointers lbnd and ubnd refer to the same code, the variable is fixed at the corresponding value */ ARRAY *array; /* array of members, which are assigned elemental variables */ }; #define take_member_var _glp_mpl_take_member_var ELEMVAR *take_member_var /* returns reference */ ( MPL *mpl, VARIABLE *var, /* not changed */ TUPLE *tuple /* not changed */ ); /* obtain reference to elemental variable */ #define eval_member_var _glp_mpl_eval_member_var ELEMVAR *eval_member_var /* returns reference */ ( MPL *mpl, VARIABLE *var, /* not changed */ TUPLE *tuple /* not changed */ ); /* evaluate reference to elemental variable */ #define eval_whole_var _glp_mpl_eval_whole_var void eval_whole_var(MPL *mpl, VARIABLE *var); /* evaluate model variable over entire domain */ #define clean_variable _glp_mpl_clean_variable void clean_variable(MPL *mpl, VARIABLE *var); /* clean model variable */ /**********************************************************************/ /* * * MODEL CONSTRAINTS AND OBJECTIVES * * */ /**********************************************************************/ struct CONSTRAINT { /* model constraint or objective */ char *name; /* symbolic name; cannot be NULL */ char *alias; /* alias; NULL means alias is not specified */ int dim; /* aka arity */ /* dimension (number of subscripts); dim = 0 means 0-dimensional (unsubscripted) constraint */ DOMAIN *domain; /* subscript domain; NULL for 0-dimensional constraint */ int type; /* constraint type: A_CONSTRAINT - constraint A_MINIMIZE - objective (minimization) A_MAXIMIZE - objective (maximization) */ CODE *code; /* pseudo-code for computing main linear form; cannot be NULL */ CODE *lbnd; /* pseudo-code for computing lower bound; NULL means lower bound is not specified */ CODE *ubnd; /* pseudo-code for computing upper bound; NULL means upper bound is not specified */ /* if both the pointers lbnd and ubnd refer to the same code, the constraint has the form of equation */ ARRAY *array; /* array of members, which are assigned elemental constraints */ }; #define take_member_con _glp_mpl_take_member_con ELEMCON *take_member_con /* returns reference */ ( MPL *mpl, CONSTRAINT *con, /* not changed */ TUPLE *tuple /* not changed */ ); /* obtain reference to elemental constraint */ #define eval_member_con _glp_mpl_eval_member_con ELEMCON *eval_member_con /* returns reference */ ( MPL *mpl, CONSTRAINT *con, /* not changed */ TUPLE *tuple /* not changed */ ); /* evaluate reference to elemental constraint */ #define eval_whole_con _glp_mpl_eval_whole_con void eval_whole_con(MPL *mpl, CONSTRAINT *con); /* evaluate model constraint over entire domain */ #define clean_constraint _glp_mpl_clean_constraint void clean_constraint(MPL *mpl, CONSTRAINT *con); /* clean model constraint */ /**********************************************************************/ /* * * DATA TABLES * * */ /**********************************************************************/ struct TABLE { /* data table */ char *name; /* symbolic name; cannot be NULL */ char *alias; /* alias; NULL means alias is not specified */ int type; /* table type: A_INPUT - input table A_OUTPUT - output table */ TABARG *arg; /* argument list; cannot be empty */ union { struct { SET *set; /* input set; NULL means the set is not specified */ TABFLD *fld; /* field list; cannot be empty */ TABIN *list; /* input list; can be empty */ } in; struct { DOMAIN *domain; /* subscript domain; cannot be NULL */ TABOUT *list; /* output list; cannot be empty */ } out; } u; }; struct TABARG { /* table argument list entry */ CODE *code; /* pseudo-code for computing the argument */ TABARG *next; /* next entry for the same table */ }; struct TABFLD { /* table field list entry */ char *name; /* field name; cannot be NULL */ TABFLD *next; /* next entry for the same table */ }; struct TABIN { /* table input list entry */ PARAMETER *par; /* parameter to be read; cannot be NULL */ char *name; /* column name; cannot be NULL */ TABIN *next; /* next entry for the same table */ }; struct TABOUT { /* table output list entry */ CODE *code; /* pseudo-code for computing the value to be written */ char *name; /* column name; cannot be NULL */ TABOUT *next; /* next entry for the same table */ }; struct TABDCA { /* table driver communication area */ int id; /* driver identifier (set by mpl_tab_drv_open) */ void *link; /* driver link pointer (set by mpl_tab_drv_open) */ int na; /* number of arguments */ char **arg; /* char *arg[1+ns]; */ /* arg[k], 1 <= k <= ns, is pointer to k-th argument */ int nf; /* number of fields */ char **name; /* char *name[1+nc]; */ /* name[k], 1 <= k <= nc, is name of k-th field */ int *type; /* int type[1+nc]; */ /* type[k], 1 <= k <= nc, is type of k-th field: '?' - value not assigned 'N' - number 'S' - character string */ double *num; /* double num[1+nc]; */ /* num[k], 1 <= k <= nc, is numeric value of k-th field */ char **str; /* str[k], 1 <= k <= nc, is string value of k-th field */ }; #define mpl_tab_num_args _glp_mpl_tab_num_args int mpl_tab_num_args(TABDCA *dca); #define mpl_tab_get_arg _glp_mpl_tab_get_arg const char *mpl_tab_get_arg(TABDCA *dca, int k); #define mpl_tab_num_flds _glp_mpl_tab_num_flds int mpl_tab_num_flds(TABDCA *dca); #define mpl_tab_get_name _glp_mpl_tab_get_name const char *mpl_tab_get_name(TABDCA *dca, int k); #define mpl_tab_get_type _glp_mpl_tab_get_type int mpl_tab_get_type(TABDCA *dca, int k); #define mpl_tab_get_num _glp_mpl_tab_get_num double mpl_tab_get_num(TABDCA *dca, int k); #define mpl_tab_get_str _glp_mpl_tab_get_str const char *mpl_tab_get_str(TABDCA *dca, int k); #define mpl_tab_set_num _glp_mpl_tab_set_num void mpl_tab_set_num(TABDCA *dca, int k, double num); #define mpl_tab_set_str _glp_mpl_tab_set_str void mpl_tab_set_str(TABDCA *dca, int k, const char *str); #define mpl_tab_drv_open _glp_mpl_tab_drv_open void mpl_tab_drv_open(MPL *mpl, int mode); #define mpl_tab_drv_read _glp_mpl_tab_drv_read int mpl_tab_drv_read(MPL *mpl); #define mpl_tab_drv_write _glp_mpl_tab_drv_write void mpl_tab_drv_write(MPL *mpl); #define mpl_tab_drv_close _glp_mpl_tab_drv_close void mpl_tab_drv_close(MPL *mpl); /**********************************************************************/ /* * * PSEUDO-CODE * * */ /**********************************************************************/ union OPERANDS { /* operands that participate in pseudo-code operation (choice of particular operands depends on the operation code) */ /*--------------------------------------------------------------*/ double num; /* O_NUMBER */ /* floaing-point number to be taken */ /*--------------------------------------------------------------*/ char *str; /* O_STRING */ /* character string to be taken */ /*--------------------------------------------------------------*/ struct /* O_INDEX */ { DOMAIN_SLOT *slot; /* domain slot, which contains dummy index to be taken */ CODE *next; /* the next pseudo-code with op = O_INDEX, which refers to the same slot as this one; pointer to the beginning of this list is stored in the corresponding domain slot */ } index; /*--------------------------------------------------------------*/ struct /* O_MEMNUM, O_MEMSYM */ { PARAMETER *par; /* model parameter, which contains member to be taken */ ARG_LIST *list; /* list of subscripts; NULL for 0-dimensional parameter */ } par; /*--------------------------------------------------------------*/ struct /* O_MEMSET */ { SET *set; /* model set, which contains member to be taken */ ARG_LIST *list; /* list of subscripts; NULL for 0-dimensional set */ } set; /*--------------------------------------------------------------*/ struct /* O_MEMVAR */ { VARIABLE *var; /* model variable, which contains member to be taken */ ARG_LIST *list; /* list of subscripts; NULL for 0-dimensional variable */ #if 1 /* 15/V-2010 */ int suff; /* suffix specified: */ #define DOT_NONE 0x00 /* none (means variable itself) */ #define DOT_LB 0x01 /* .lb (lower bound) */ #define DOT_UB 0x02 /* .ub (upper bound) */ #define DOT_STATUS 0x03 /* .status (status) */ #define DOT_VAL 0x04 /* .val (primal value) */ #define DOT_DUAL 0x05 /* .dual (dual value) */ #endif } var; #if 1 /* 15/V-2010 */ /*--------------------------------------------------------------*/ struct /* O_MEMCON */ { CONSTRAINT *con; /* model constraint, which contains member to be taken */ ARG_LIST *list; /* list of subscripys; NULL for 0-dimensional constraint */ int suff; /* suffix specified (see O_MEMVAR above) */ } con; #endif /*--------------------------------------------------------------*/ ARG_LIST *list; /* O_TUPLE, O_MAKE, n-ary operations */ /* list of operands */ /*--------------------------------------------------------------*/ DOMAIN_BLOCK *slice; /* O_SLICE */ /* domain block, which specifies slice (i.e. n-tuple that contains free dummy indices); this operation is never evaluated */ /*--------------------------------------------------------------*/ struct /* unary, binary, ternary operations */ { CODE *x; /* pseudo-code for computing first operand */ CODE *y; /* pseudo-code for computing second operand */ CODE *z; /* pseudo-code for computing third operand */ } arg; /*--------------------------------------------------------------*/ struct /* iterated operations */ { DOMAIN *domain; /* domain, over which the operation is performed */ CODE *x; /* pseudo-code for computing "integrand" */ } loop; /*--------------------------------------------------------------*/ }; struct ARG_LIST { /* operands list entry */ CODE *x; /* pseudo-code for computing operand */ ARG_LIST *next; /* the next operand of the same operation */ }; struct CODE { /* pseudo-code (internal form of expressions) */ int op; /* operation code: */ #define O_NUMBER 301 /* take floating-point number */ #define O_STRING 302 /* take character string */ #define O_INDEX 303 /* take dummy index */ #define O_MEMNUM 304 /* take member of numeric parameter */ #define O_MEMSYM 305 /* take member of symbolic parameter */ #define O_MEMSET 306 /* take member of set */ #define O_MEMVAR 307 /* take member of variable */ #define O_MEMCON 308 /* take member of constraint */ #define O_TUPLE 309 /* make n-tuple */ #define O_MAKE 310 /* make elemental set of n-tuples */ #define O_SLICE 311 /* define domain block (dummy op) */ /* 0-ary operations --------------------*/ #define O_IRAND224 312 /* pseudo-random in [0, 2^24-1] */ #define O_UNIFORM01 313 /* pseudo-random in [0, 1) */ #define O_NORMAL01 314 /* gaussian random, mu = 0, sigma = 1 */ #define O_GMTIME 315 /* current calendar time (UTC) */ /* unary operations --------------------*/ #define O_CVTNUM 316 /* conversion to numeric */ #define O_CVTSYM 317 /* conversion to symbolic */ #define O_CVTLOG 318 /* conversion to logical */ #define O_CVTTUP 319 /* conversion to 1-tuple */ #define O_CVTLFM 320 /* conversion to linear form */ #define O_PLUS 321 /* unary plus */ #define O_MINUS 322 /* unary minus */ #define O_NOT 323 /* negation (logical "not") */ #define O_ABS 324 /* absolute value */ #define O_CEIL 325 /* round upward ("ceiling of x") */ #define O_FLOOR 326 /* round downward ("floor of x") */ #define O_EXP 327 /* base-e exponential */ #define O_LOG 328 /* natural logarithm */ #define O_LOG10 329 /* common (decimal) logarithm */ #define O_SQRT 330 /* square root */ #define O_SIN 331 /* trigonometric sine */ #define O_COS 332 /* trigonometric cosine */ #define O_ATAN 333 /* trigonometric arctangent */ #define O_ROUND 334 /* round to nearest integer */ #define O_TRUNC 335 /* truncate to nearest integer */ #define O_CARD 336 /* cardinality of set */ #define O_LENGTH 337 /* length of symbolic value */ /* binary operations -------------------*/ #define O_ADD 338 /* addition */ #define O_SUB 339 /* subtraction */ #define O_LESS 340 /* non-negative subtraction */ #define O_MUL 341 /* multiplication */ #define O_DIV 342 /* division */ #define O_IDIV 343 /* quotient of exact division */ #define O_MOD 344 /* remainder of exact division */ #define O_POWER 345 /* exponentiation (raise to power) */ #define O_ATAN2 346 /* trigonometric arctangent */ #define O_ROUND2 347 /* round to n fractional digits */ #define O_TRUNC2 348 /* truncate to n fractional digits */ #define O_UNIFORM 349 /* pseudo-random in [a, b) */ #define O_NORMAL 350 /* gaussian random, given mu and sigma */ #define O_CONCAT 351 /* concatenation */ #define O_LT 352 /* comparison on 'less than' */ #define O_LE 353 /* comparison on 'not greater than' */ #define O_EQ 354 /* comparison on 'equal to' */ #define O_GE 355 /* comparison on 'not less than' */ #define O_GT 356 /* comparison on 'greater than' */ #define O_NE 357 /* comparison on 'not equal to' */ #define O_AND 358 /* conjunction (logical "and") */ #define O_OR 359 /* disjunction (logical "or") */ #define O_UNION 360 /* union */ #define O_DIFF 361 /* difference */ #define O_SYMDIFF 362 /* symmetric difference */ #define O_INTER 363 /* intersection */ #define O_CROSS 364 /* cross (Cartesian) product */ #define O_IN 365 /* test on 'x in Y' */ #define O_NOTIN 366 /* test on 'x not in Y' */ #define O_WITHIN 367 /* test on 'X within Y' */ #define O_NOTWITHIN 368 /* test on 'X not within Y' */ #define O_SUBSTR 369 /* substring */ #define O_STR2TIME 370 /* convert string to time */ #define O_TIME2STR 371 /* convert time to string */ /* ternary operations ------------------*/ #define O_DOTS 372 /* build "arithmetic" set */ #define O_FORK 373 /* if-then-else */ #define O_SUBSTR3 374 /* substring */ /* n-ary operations --------------------*/ #define O_MIN 375 /* minimal value (n-ary) */ #define O_MAX 376 /* maximal value (n-ary) */ /* iterated operations -----------------*/ #define O_SUM 377 /* summation */ #define O_PROD 378 /* multiplication */ #define O_MINIMUM 379 /* minimum */ #define O_MAXIMUM 380 /* maximum */ #define O_FORALL 381 /* conjunction (A-quantification) */ #define O_EXISTS 382 /* disjunction (E-quantification) */ #define O_SETOF 383 /* compute elemental set */ #define O_BUILD 384 /* build elemental set */ OPERANDS arg; /* operands that participate in the operation */ int type; /* type of the resultant value: A_NUMERIC - numeric A_SYMBOLIC - symbolic A_LOGICAL - logical A_TUPLE - n-tuple A_ELEMSET - elemental set A_FORMULA - linear form */ int dim; /* dimension of the resultant value; for A_TUPLE and A_ELEMSET it is the dimension of the corresponding n-tuple(s) and cannot be zero; for other resultant types it is always zero */ CODE *up; /* parent pseudo-code, which refers to this pseudo-code as to its operand; NULL means this pseudo-code has no parent and defines an expression, which is not contained in another expression */ int vflag; /* volatile flag; being set this flag means that this operation has a side effect; for primary expressions this flag is set directly by corresponding parsing routines (for example, if primary expression is a reference to a function that generates pseudo-random numbers); in other cases this flag is inherited from operands */ int valid; /* if this flag is set, the resultant value, which is a temporary result of evaluating this operation on particular values of operands, is valid; if this flag is clear, the resultant value doesn't exist and therefore not valid; having been evaluated the resultant value is stored here and not destroyed until the dummy indices, which this value depends on, have been changed (and if it doesn't depend on dummy indices at all, it is never destroyed); thus, if the resultant value is valid, evaluating routine can immediately take its copy not computing the result from scratch; this mechanism is similar to moving invariants out of loops and allows improving efficiency at the expense of some extra memory needed to keep temporary results */ /* however, if the volatile flag (see above) is set, even if the resultant value is valid, evaluating routine computes it as if it were not valid, i.e. caching is not used in this case */ VALUE value; /* resultant value in generic format */ }; #define eval_numeric _glp_mpl_eval_numeric double eval_numeric(MPL *mpl, CODE *code); /* evaluate pseudo-code to determine numeric value */ #define eval_symbolic _glp_mpl_eval_symbolic SYMBOL *eval_symbolic(MPL *mpl, CODE *code); /* evaluate pseudo-code to determine symbolic value */ #define eval_logical _glp_mpl_eval_logical int eval_logical(MPL *mpl, CODE *code); /* evaluate pseudo-code to determine logical value */ #define eval_tuple _glp_mpl_eval_tuple TUPLE *eval_tuple(MPL *mpl, CODE *code); /* evaluate pseudo-code to construct n-tuple */ #define eval_elemset _glp_mpl_eval_elemset ELEMSET *eval_elemset(MPL *mpl, CODE *code); /* evaluate pseudo-code to construct elemental set */ #define is_member _glp_mpl_is_member int is_member(MPL *mpl, CODE *code, TUPLE *tuple); /* check if n-tuple is in set specified by pseudo-code */ #define eval_formula _glp_mpl_eval_formula FORMULA *eval_formula(MPL *mpl, CODE *code); /* evaluate pseudo-code to construct linear form */ #define clean_code _glp_mpl_clean_code void clean_code(MPL *mpl, CODE *code); /* clean pseudo-code */ /**********************************************************************/ /* * * MODEL STATEMENTS * * */ /**********************************************************************/ struct CHECK { /* check statement */ DOMAIN *domain; /* subscript domain; NULL means domain is not used */ CODE *code; /* code for computing the predicate to be checked */ }; struct DISPLAY { /* display statement */ DOMAIN *domain; /* subscript domain; NULL means domain is not used */ DISPLAY1 *list; /* display list; cannot be empty */ }; struct DISPLAY1 { /* display list entry */ int type; /* item type: A_INDEX - dummy index A_SET - model set A_PARAMETER - model parameter A_VARIABLE - model variable A_CONSTRAINT - model constraint/objective A_EXPRESSION - expression */ union { DOMAIN_SLOT *slot; SET *set; PARAMETER *par; VARIABLE *var; CONSTRAINT *con; CODE *code; } u; /* item to be displayed */ #if 0 /* 15/V-2010 */ ARG_LIST *list; /* optional subscript list (for constraint/objective only) */ #endif DISPLAY1 *next; /* the next entry for the same statement */ }; struct PRINTF { /* printf statement */ DOMAIN *domain; /* subscript domain; NULL means domain is not used */ CODE *fmt; /* pseudo-code for computing format string */ PRINTF1 *list; /* printf list; can be empty */ CODE *fname; /* pseudo-code for computing filename to redirect the output; NULL means the output goes to stdout */ int app; /* if this flag is set, the output is appended */ }; struct PRINTF1 { /* printf list entry */ CODE *code; /* pseudo-code for computing value to be printed */ PRINTF1 *next; /* the next entry for the same statement */ }; struct FOR { /* for statement */ DOMAIN *domain; /* subscript domain; cannot be NULL */ STATEMENT *list; /* linked list of model statements within this for statement in the original order */ }; struct STATEMENT { /* model statement */ int line; /* number of source text line, where statement begins */ int type; /* statement type: A_SET - set statement A_PARAMETER - parameter statement A_VARIABLE - variable statement A_CONSTRAINT - constraint/objective statement A_TABLE - table statement A_SOLVE - solve statement A_CHECK - check statement A_DISPLAY - display statement A_PRINTF - printf statement A_FOR - for statement */ union { SET *set; PARAMETER *par; VARIABLE *var; CONSTRAINT *con; TABLE *tab; void *slv; /* currently not used (set to NULL) */ CHECK *chk; DISPLAY *dpy; PRINTF *prt; FOR *fur; } u; /* specific part of statement */ STATEMENT *next; /* the next statement; in this list statements follow in the same order as they appear in the model section */ }; #define execute_table _glp_mpl_execute_table void execute_table(MPL *mpl, TABLE *tab); /* execute table statement */ #define free_dca _glp_mpl_free_dca void free_dca(MPL *mpl); /* free table driver communucation area */ #define clean_table _glp_mpl_clean_table void clean_table(MPL *mpl, TABLE *tab); /* clean table statement */ #define execute_check _glp_mpl_execute_check void execute_check(MPL *mpl, CHECK *chk); /* execute check statement */ #define clean_check _glp_mpl_clean_check void clean_check(MPL *mpl, CHECK *chk); /* clean check statement */ #define execute_display _glp_mpl_execute_display void execute_display(MPL *mpl, DISPLAY *dpy); /* execute display statement */ #define clean_display _glp_mpl_clean_display void clean_display(MPL *mpl, DISPLAY *dpy); /* clean display statement */ #define execute_printf _glp_mpl_execute_printf void execute_printf(MPL *mpl, PRINTF *prt); /* execute printf statement */ #define clean_printf _glp_mpl_clean_printf void clean_printf(MPL *mpl, PRINTF *prt); /* clean printf statement */ #define execute_for _glp_mpl_execute_for void execute_for(MPL *mpl, FOR *fur); /* execute for statement */ #define clean_for _glp_mpl_clean_for void clean_for(MPL *mpl, FOR *fur); /* clean for statement */ #define execute_statement _glp_mpl_execute_statement void execute_statement(MPL *mpl, STATEMENT *stmt); /* execute specified model statement */ #define clean_statement _glp_mpl_clean_statement void clean_statement(MPL *mpl, STATEMENT *stmt); /* clean specified model statement */ /**********************************************************************/ /* * * GENERATING AND POSTSOLVING MODEL * * */ /**********************************************************************/ #define alloc_content _glp_mpl_alloc_content void alloc_content(MPL *mpl); /* allocate content arrays for all model objects */ #define generate_model _glp_mpl_generate_model void generate_model(MPL *mpl); /* generate model */ #define build_problem _glp_mpl_build_problem void build_problem(MPL *mpl); /* build problem instance */ #define postsolve_model _glp_mpl_postsolve_model void postsolve_model(MPL *mpl); /* postsolve model */ #define clean_model _glp_mpl_clean_model void clean_model(MPL *mpl); /* clean model content */ /**********************************************************************/ /* * * INPUT/OUTPUT * * */ /**********************************************************************/ #define open_input _glp_mpl_open_input void open_input(MPL *mpl, char *file); /* open input text file */ #define read_char _glp_mpl_read_char int read_char(MPL *mpl); /* read next character from input text file */ #define close_input _glp_mpl_close_input void close_input(MPL *mpl); /* close input text file */ #define open_output _glp_mpl_open_output void open_output(MPL *mpl, char *file); /* open output text file */ #define write_char _glp_mpl_write_char void write_char(MPL *mpl, int c); /* write next character to output text file */ #define write_text _glp_mpl_write_text void write_text(MPL *mpl, char *fmt, ...); /* format and write text to output text file */ #define flush_output _glp_mpl_flush_output void flush_output(MPL *mpl); /* finalize writing data to output text file */ /**********************************************************************/ /* * * SOLVER INTERFACE * * */ /**********************************************************************/ #define MPL_FR 401 /* free (unbounded) */ #define MPL_LO 402 /* lower bound */ #define MPL_UP 403 /* upper bound */ #define MPL_DB 404 /* both lower and upper bounds */ #define MPL_FX 405 /* fixed */ #define MPL_ST 411 /* constraint */ #define MPL_MIN 412 /* objective (minimization) */ #define MPL_MAX 413 /* objective (maximization) */ #define MPL_NUM 421 /* continuous */ #define MPL_INT 422 /* integer */ #define MPL_BIN 423 /* binary */ #define error _glp_mpl_error void error(MPL *mpl, char *fmt, ...); /* print error message and terminate model processing */ #define warning _glp_mpl_warning void warning(MPL *mpl, char *fmt, ...); /* print warning message and continue model processing */ #define mpl_initialize _glp_mpl_initialize MPL *mpl_initialize(void); /* create and initialize translator database */ #define mpl_read_model _glp_mpl_read_model int mpl_read_model(MPL *mpl, char *file, int skip_data); /* read model section and optional data section */ #define mpl_read_data _glp_mpl_read_data int mpl_read_data(MPL *mpl, char *file); /* read data section */ #define mpl_generate _glp_mpl_generate int mpl_generate(MPL *mpl, char *file); /* generate model */ #define mpl_get_prob_name _glp_mpl_get_prob_name char *mpl_get_prob_name(MPL *mpl); /* obtain problem (model) name */ #define mpl_get_num_rows _glp_mpl_get_num_rows int mpl_get_num_rows(MPL *mpl); /* determine number of rows */ #define mpl_get_num_cols _glp_mpl_get_num_cols int mpl_get_num_cols(MPL *mpl); /* determine number of columns */ #define mpl_get_row_name _glp_mpl_get_row_name char *mpl_get_row_name(MPL *mpl, int i); /* obtain row name */ #define mpl_get_row_kind _glp_mpl_get_row_kind int mpl_get_row_kind(MPL *mpl, int i); /* determine row kind */ #define mpl_get_row_bnds _glp_mpl_get_row_bnds int mpl_get_row_bnds(MPL *mpl, int i, double *lb, double *ub); /* obtain row bounds */ #define mpl_get_mat_row _glp_mpl_get_mat_row int mpl_get_mat_row(MPL *mpl, int i, int ndx[], double val[]); /* obtain row of the constraint matrix */ #define mpl_get_row_c0 _glp_mpl_get_row_c0 double mpl_get_row_c0(MPL *mpl, int i); /* obtain constant term of free row */ #define mpl_get_col_name _glp_mpl_get_col_name char *mpl_get_col_name(MPL *mpl, int j); /* obtain column name */ #define mpl_get_col_kind _glp_mpl_get_col_kind int mpl_get_col_kind(MPL *mpl, int j); /* determine column kind */ #define mpl_get_col_bnds _glp_mpl_get_col_bnds int mpl_get_col_bnds(MPL *mpl, int j, double *lb, double *ub); /* obtain column bounds */ #define mpl_has_solve_stmt _glp_mpl_has_solve_stmt int mpl_has_solve_stmt(MPL *mpl); /* check if model has solve statement */ #if 1 /* 15/V-2010 */ #define mpl_put_row_soln _glp_mpl_put_row_soln void mpl_put_row_soln(MPL *mpl, int i, int stat, double prim, double dual); /* store row (constraint/objective) solution components */ #endif #if 1 /* 15/V-2010 */ #define mpl_put_col_soln _glp_mpl_put_col_soln void mpl_put_col_soln(MPL *mpl, int j, int stat, double prim, double dual); /* store column (variable) solution components */ #endif #if 0 /* 15/V-2010 */ #define mpl_put_col_value _glp_mpl_put_col_value void mpl_put_col_value(MPL *mpl, int j, double val); /* store column value */ #endif #define mpl_postsolve _glp_mpl_postsolve int mpl_postsolve(MPL *mpl); /* postsolve model */ #define mpl_terminate _glp_mpl_terminate void mpl_terminate(MPL *mpl); /* free all resources used by translator */ #endif /* eof */ igraph/src/glpk/glpk.h0000644000176000001440000016523212325527073014425 0ustar ripleyusers/* glpk.h */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPK_H #define GLPK_H #include #include #ifdef __cplusplus extern "C" { #endif /* library version numbers: */ #define GLP_MAJOR_VERSION 4 #define GLP_MINOR_VERSION 45 #ifndef GLP_PROB_DEFINED #define GLP_PROB_DEFINED typedef struct { double _opaque_prob[100]; } glp_prob; /* LP/MIP problem object */ #endif /* optimization direction flag: */ #define GLP_MIN 1 /* minimization */ #define GLP_MAX 2 /* maximization */ /* kind of structural variable: */ #define GLP_CV 1 /* continuous variable */ #define GLP_IV 2 /* integer variable */ #define GLP_BV 3 /* binary variable */ /* type of auxiliary/structural variable: */ #define GLP_FR 1 /* free variable */ #define GLP_LO 2 /* variable with lower bound */ #define GLP_UP 3 /* variable with upper bound */ #define GLP_DB 4 /* double-bounded variable */ #define GLP_FX 5 /* fixed variable */ /* status of auxiliary/structural variable: */ #define GLP_BS 1 /* basic variable */ #define GLP_NL 2 /* non-basic variable on lower bound */ #define GLP_NU 3 /* non-basic variable on upper bound */ #define GLP_NF 4 /* non-basic free variable */ #define GLP_NS 5 /* non-basic fixed variable */ /* scaling options: */ #define GLP_SF_GM 0x01 /* perform geometric mean scaling */ #define GLP_SF_EQ 0x10 /* perform equilibration scaling */ #define GLP_SF_2N 0x20 /* round scale factors to power of two */ #define GLP_SF_SKIP 0x40 /* skip if problem is well scaled */ #define GLP_SF_AUTO 0x80 /* choose scaling options automatically */ /* solution indicator: */ #define GLP_SOL 1 /* basic solution */ #define GLP_IPT 2 /* interior-point solution */ #define GLP_MIP 3 /* mixed integer solution */ /* solution status: */ #define GLP_UNDEF 1 /* solution is undefined */ #define GLP_FEAS 2 /* solution is feasible */ #define GLP_INFEAS 3 /* solution is infeasible */ #define GLP_NOFEAS 4 /* no feasible solution exists */ #define GLP_OPT 5 /* solution is optimal */ #define GLP_UNBND 6 /* solution is unbounded */ typedef struct { /* basis factorization control parameters */ int msg_lev; /* (reserved) */ int type; /* factorization type: */ #define GLP_BF_FT 1 /* LUF + Forrest-Tomlin */ #define GLP_BF_BG 2 /* LUF + Schur compl. + Bartels-Golub */ #define GLP_BF_GR 3 /* LUF + Schur compl. + Givens rotation */ int lu_size; /* luf.sv_size */ double piv_tol; /* luf.piv_tol */ int piv_lim; /* luf.piv_lim */ int suhl; /* luf.suhl */ double eps_tol; /* luf.eps_tol */ double max_gro; /* luf.max_gro */ int nfs_max; /* fhv.hh_max */ double upd_tol; /* fhv.upd_tol */ int nrs_max; /* lpf.n_max */ int rs_size; /* lpf.v_size */ double foo_bar[38]; /* (reserved) */ } glp_bfcp; typedef struct { /* simplex method control parameters */ int msg_lev; /* message level: */ #define GLP_MSG_OFF 0 /* no output */ #define GLP_MSG_ERR 1 /* warning and error messages only */ #define GLP_MSG_ON 2 /* normal output */ #define GLP_MSG_ALL 3 /* full output */ #define GLP_MSG_DBG 4 /* debug output */ int meth; /* simplex method option: */ #define GLP_PRIMAL 1 /* use primal simplex */ #define GLP_DUALP 2 /* use dual; if it fails, use primal */ #define GLP_DUAL 3 /* use dual simplex */ int pricing; /* pricing technique: */ #define GLP_PT_STD 0x11 /* standard (Dantzig rule) */ #define GLP_PT_PSE 0x22 /* projected steepest edge */ int r_test; /* ratio test technique: */ #define GLP_RT_STD 0x11 /* standard (textbook) */ #define GLP_RT_HAR 0x22 /* two-pass Harris' ratio test */ double tol_bnd; /* spx.tol_bnd */ double tol_dj; /* spx.tol_dj */ double tol_piv; /* spx.tol_piv */ double obj_ll; /* spx.obj_ll */ double obj_ul; /* spx.obj_ul */ int it_lim; /* spx.it_lim */ int tm_lim; /* spx.tm_lim (milliseconds) */ int out_frq; /* spx.out_frq */ int out_dly; /* spx.out_dly (milliseconds) */ int presolve; /* enable/disable using LP presolver */ double foo_bar[36]; /* (reserved) */ } glp_smcp; typedef struct { /* interior-point solver control parameters */ int msg_lev; /* message level (see glp_smcp) */ int ord_alg; /* ordering algorithm: */ #define GLP_ORD_NONE 0 /* natural (original) ordering */ #define GLP_ORD_QMD 1 /* quotient minimum degree (QMD) */ #define GLP_ORD_AMD 2 /* approx. minimum degree (AMD) */ #define GLP_ORD_SYMAMD 3 /* approx. minimum degree (SYMAMD) */ double foo_bar[48]; /* (reserved) */ } glp_iptcp; #ifndef GLP_TREE_DEFINED #define GLP_TREE_DEFINED typedef struct { double _opaque_tree[100]; } glp_tree; /* branch-and-bound tree */ #endif typedef struct { /* integer optimizer control parameters */ int msg_lev; /* message level (see glp_smcp) */ int br_tech; /* branching technique: */ #define GLP_BR_FFV 1 /* first fractional variable */ #define GLP_BR_LFV 2 /* last fractional variable */ #define GLP_BR_MFV 3 /* most fractional variable */ #define GLP_BR_DTH 4 /* heuristic by Driebeck and Tomlin */ #define GLP_BR_PCH 5 /* hybrid pseudocost heuristic */ int bt_tech; /* backtracking technique: */ #define GLP_BT_DFS 1 /* depth first search */ #define GLP_BT_BFS 2 /* breadth first search */ #define GLP_BT_BLB 3 /* best local bound */ #define GLP_BT_BPH 4 /* best projection heuristic */ double tol_int; /* mip.tol_int */ double tol_obj; /* mip.tol_obj */ int tm_lim; /* mip.tm_lim (milliseconds) */ int out_frq; /* mip.out_frq (milliseconds) */ int out_dly; /* mip.out_dly (milliseconds) */ void (*cb_func)(glp_tree *T, void *info); /* mip.cb_func */ void *cb_info; /* mip.cb_info */ int cb_size; /* mip.cb_size */ int pp_tech; /* preprocessing technique: */ #define GLP_PP_NONE 0 /* disable preprocessing */ #define GLP_PP_ROOT 1 /* preprocessing only on root level */ #define GLP_PP_ALL 2 /* preprocessing on all levels */ double mip_gap; /* relative MIP gap tolerance */ int mir_cuts; /* MIR cuts (GLP_ON/GLP_OFF) */ int gmi_cuts; /* Gomory's cuts (GLP_ON/GLP_OFF) */ int cov_cuts; /* cover cuts (GLP_ON/GLP_OFF) */ int clq_cuts; /* clique cuts (GLP_ON/GLP_OFF) */ int presolve; /* enable/disable using MIP presolver */ int binarize; /* try to binarize integer variables */ int fp_heur; /* feasibility pump heuristic */ #if 1 /* 28/V-2010 */ int alien; /* use alien solver */ #endif double foo_bar[29]; /* (reserved) */ } glp_iocp; typedef struct { /* additional row attributes */ int level; /* subproblem level at which the row was added */ int origin; /* row origin flag: */ #define GLP_RF_REG 0 /* regular constraint */ #define GLP_RF_LAZY 1 /* "lazy" constraint */ #define GLP_RF_CUT 2 /* cutting plane constraint */ int klass; /* row class descriptor: */ #define GLP_RF_GMI 1 /* Gomory's mixed integer cut */ #define GLP_RF_MIR 2 /* mixed integer rounding cut */ #define GLP_RF_COV 3 /* mixed cover cut */ #define GLP_RF_CLQ 4 /* clique cut */ double foo_bar[7]; /* (reserved) */ } glp_attr; /* enable/disable flag: */ #define GLP_ON 1 /* enable something */ #define GLP_OFF 0 /* disable something */ /* reason codes: */ #define GLP_IROWGEN 0x01 /* request for row generation */ #define GLP_IBINGO 0x02 /* better integer solution found */ #define GLP_IHEUR 0x03 /* request for heuristic solution */ #define GLP_ICUTGEN 0x04 /* request for cut generation */ #define GLP_IBRANCH 0x05 /* request for branching */ #define GLP_ISELECT 0x06 /* request for subproblem selection */ #define GLP_IPREPRO 0x07 /* request for preprocessing */ /* branch selection indicator: */ #define GLP_NO_BRNCH 0 /* select no branch */ #define GLP_DN_BRNCH 1 /* select down-branch */ #define GLP_UP_BRNCH 2 /* select up-branch */ /* return codes: */ #define GLP_EBADB 0x01 /* invalid basis */ #define GLP_ESING 0x02 /* singular matrix */ #define GLP_ECOND 0x03 /* ill-conditioned matrix */ #define GLP_EBOUND 0x04 /* invalid bounds */ #define GLP_EFAIL 0x05 /* solver failed */ #define GLP_EOBJLL 0x06 /* objective lower limit reached */ #define GLP_EOBJUL 0x07 /* objective upper limit reached */ #define GLP_EITLIM 0x08 /* iteration limit exceeded */ #define GLP_ETMLIM 0x09 /* time limit exceeded */ #define GLP_ENOPFS 0x0A /* no primal feasible solution */ #define GLP_ENODFS 0x0B /* no dual feasible solution */ #define GLP_EROOT 0x0C /* root LP optimum not provided */ #define GLP_ESTOP 0x0D /* search terminated by application */ #define GLP_EMIPGAP 0x0E /* relative mip gap tolerance reached */ #define GLP_ENOFEAS 0x0F /* no primal/dual feasible solution */ #define GLP_ENOCVG 0x10 /* no convergence */ #define GLP_EINSTAB 0x11 /* numerical instability */ #define GLP_EDATA 0x12 /* invalid data */ #define GLP_ERANGE 0x13 /* result out of range */ /* condition indicator: */ #define GLP_KKT_PE 1 /* primal equalities */ #define GLP_KKT_PB 2 /* primal bounds */ #define GLP_KKT_DE 3 /* dual equalities */ #define GLP_KKT_DB 4 /* dual bounds */ #define GLP_KKT_CS 5 /* complementary slackness */ /* MPS file format: */ #define GLP_MPS_DECK 1 /* fixed (ancient) */ #define GLP_MPS_FILE 2 /* free (modern) */ typedef struct { /* MPS format control parameters */ int blank; /* character code to replace blanks in symbolic names */ char *obj_name; /* objective row name */ double tol_mps; /* zero tolerance for MPS data */ double foo_bar[17]; /* (reserved for use in the future) */ } glp_mpscp; typedef struct { /* CPLEX LP format control parameters */ double foo_bar[20]; /* (reserved for use in the future) */ } glp_cpxcp; #ifndef GLP_TRAN_DEFINED #define GLP_TRAN_DEFINED typedef struct { double _opaque_tran[100]; } glp_tran; /* MathProg translator workspace */ #endif glp_prob *glp_create_prob(void); /* create problem object */ void glp_set_prob_name(glp_prob *P, const char *name); /* assign (change) problem name */ void glp_set_obj_name(glp_prob *P, const char *name); /* assign (change) objective function name */ void glp_set_obj_dir(glp_prob *P, int dir); /* set (change) optimization direction flag */ int glp_add_rows(glp_prob *P, int nrs); /* add new rows to problem object */ int glp_add_cols(glp_prob *P, int ncs); /* add new columns to problem object */ void glp_set_row_name(glp_prob *P, int i, const char *name); /* assign (change) row name */ void glp_set_col_name(glp_prob *P, int j, const char *name); /* assign (change) column name */ void glp_set_row_bnds(glp_prob *P, int i, int type, double lb, double ub); /* set (change) row bounds */ void glp_set_col_bnds(glp_prob *P, int j, int type, double lb, double ub); /* set (change) column bounds */ void glp_set_obj_coef(glp_prob *P, int j, double coef); /* set (change) obj. coefficient or constant term */ void glp_set_mat_row(glp_prob *P, int i, int len, const int ind[], const double val[]); /* set (replace) row of the constraint matrix */ void glp_set_mat_col(glp_prob *P, int j, int len, const int ind[], const double val[]); /* set (replace) column of the constraint matrix */ void glp_load_matrix(glp_prob *P, int ne, const int ia[], const int ja[], const double ar[]); /* load (replace) the whole constraint matrix */ int glp_check_dup(int m, int n, int ne, const int ia[], const int ja[]); /* check for duplicate elements in sparse matrix */ void glp_sort_matrix(glp_prob *P); /* sort elements of the constraint matrix */ void glp_del_rows(glp_prob *P, int nrs, const int num[]); /* delete specified rows from problem object */ void glp_del_cols(glp_prob *P, int ncs, const int num[]); /* delete specified columns from problem object */ void glp_copy_prob(glp_prob *dest, glp_prob *prob, int names); /* copy problem object content */ void glp_erase_prob(glp_prob *P); /* erase problem object content */ void glp_delete_prob(glp_prob *P); /* delete problem object */ const char *glp_get_prob_name(glp_prob *P); /* retrieve problem name */ const char *glp_get_obj_name(glp_prob *P); /* retrieve objective function name */ int glp_get_obj_dir(glp_prob *P); /* retrieve optimization direction flag */ int glp_get_num_rows(glp_prob *P); /* retrieve number of rows */ int glp_get_num_cols(glp_prob *P); /* retrieve number of columns */ const char *glp_get_row_name(glp_prob *P, int i); /* retrieve row name */ const char *glp_get_col_name(glp_prob *P, int j); /* retrieve column name */ int glp_get_row_type(glp_prob *P, int i); /* retrieve row type */ double glp_get_row_lb(glp_prob *P, int i); /* retrieve row lower bound */ double glp_get_row_ub(glp_prob *P, int i); /* retrieve row upper bound */ int glp_get_col_type(glp_prob *P, int j); /* retrieve column type */ double glp_get_col_lb(glp_prob *P, int j); /* retrieve column lower bound */ double glp_get_col_ub(glp_prob *P, int j); /* retrieve column upper bound */ double glp_get_obj_coef(glp_prob *P, int j); /* retrieve obj. coefficient or constant term */ int glp_get_num_nz(glp_prob *P); /* retrieve number of constraint coefficients */ int glp_get_mat_row(glp_prob *P, int i, int ind[], double val[]); /* retrieve row of the constraint matrix */ int glp_get_mat_col(glp_prob *P, int j, int ind[], double val[]); /* retrieve column of the constraint matrix */ void glp_create_index(glp_prob *P); /* create the name index */ int glp_find_row(glp_prob *P, const char *name); /* find row by its name */ int glp_find_col(glp_prob *P, const char *name); /* find column by its name */ void glp_delete_index(glp_prob *P); /* delete the name index */ void glp_set_rii(glp_prob *P, int i, double rii); /* set (change) row scale factor */ void glp_set_sjj(glp_prob *P, int j, double sjj); /* set (change) column scale factor */ double glp_get_rii(glp_prob *P, int i); /* retrieve row scale factor */ double glp_get_sjj(glp_prob *P, int j); /* retrieve column scale factor */ void glp_scale_prob(glp_prob *P, int flags); /* scale problem data */ void glp_unscale_prob(glp_prob *P); /* unscale problem data */ void glp_set_row_stat(glp_prob *P, int i, int stat); /* set (change) row status */ void glp_set_col_stat(glp_prob *P, int j, int stat); /* set (change) column status */ void glp_std_basis(glp_prob *P); /* construct standard initial LP basis */ void glp_adv_basis(glp_prob *P, int flags); /* construct advanced initial LP basis */ void glp_cpx_basis(glp_prob *P); /* construct Bixby's initial LP basis */ int glp_simplex(glp_prob *P, const glp_smcp *parm); /* solve LP problem with the simplex method */ int glp_exact(glp_prob *P, const glp_smcp *parm); /* solve LP problem in exact arithmetic */ void glp_init_smcp(glp_smcp *parm); /* initialize simplex method control parameters */ int glp_get_status(glp_prob *P); /* retrieve generic status of basic solution */ int glp_get_prim_stat(glp_prob *P); /* retrieve status of primal basic solution */ int glp_get_dual_stat(glp_prob *P); /* retrieve status of dual basic solution */ double glp_get_obj_val(glp_prob *P); /* retrieve objective value (basic solution) */ int glp_get_row_stat(glp_prob *P, int i); /* retrieve row status */ double glp_get_row_prim(glp_prob *P, int i); /* retrieve row primal value (basic solution) */ double glp_get_row_dual(glp_prob *P, int i); /* retrieve row dual value (basic solution) */ int glp_get_col_stat(glp_prob *P, int j); /* retrieve column status */ double glp_get_col_prim(glp_prob *P, int j); /* retrieve column primal value (basic solution) */ double glp_get_col_dual(glp_prob *P, int j); /* retrieve column dual value (basic solution) */ int glp_get_unbnd_ray(glp_prob *P); /* determine variable causing unboundedness */ int glp_interior(glp_prob *P, const glp_iptcp *parm); /* solve LP problem with the interior-point method */ void glp_init_iptcp(glp_iptcp *parm); /* initialize interior-point solver control parameters */ int glp_ipt_status(glp_prob *P); /* retrieve status of interior-point solution */ double glp_ipt_obj_val(glp_prob *P); /* retrieve objective value (interior point) */ double glp_ipt_row_prim(glp_prob *P, int i); /* retrieve row primal value (interior point) */ double glp_ipt_row_dual(glp_prob *P, int i); /* retrieve row dual value (interior point) */ double glp_ipt_col_prim(glp_prob *P, int j); /* retrieve column primal value (interior point) */ double glp_ipt_col_dual(glp_prob *P, int j); /* retrieve column dual value (interior point) */ void glp_set_col_kind(glp_prob *P, int j, int kind); /* set (change) column kind */ int glp_get_col_kind(glp_prob *P, int j); /* retrieve column kind */ int glp_get_num_int(glp_prob *P); /* retrieve number of integer columns */ int glp_get_num_bin(glp_prob *P); /* retrieve number of binary columns */ int glp_intopt(glp_prob *P, const glp_iocp *parm); /* solve MIP problem with the branch-and-bound method */ void glp_init_iocp(glp_iocp *parm); /* initialize integer optimizer control parameters */ int glp_mip_status(glp_prob *P); /* retrieve status of MIP solution */ double glp_mip_obj_val(glp_prob *P); /* retrieve objective value (MIP solution) */ double glp_mip_row_val(glp_prob *P, int i); /* retrieve row value (MIP solution) */ double glp_mip_col_val(glp_prob *P, int j); /* retrieve column value (MIP solution) */ int glp_print_sol(glp_prob *P, const char *fname); /* write basic solution in printable format */ int glp_read_sol(glp_prob *P, const char *fname); /* read basic solution from text file */ int glp_write_sol(glp_prob *P, const char *fname); /* write basic solution to text file */ int glp_print_ranges(glp_prob *P, int len, const int list[], int flags, const char *fname); /* print sensitivity analysis report */ int glp_print_ipt(glp_prob *P, const char *fname); /* write interior-point solution in printable format */ int glp_read_ipt(glp_prob *P, const char *fname); /* read interior-point solution from text file */ int glp_write_ipt(glp_prob *P, const char *fname); /* write interior-point solution to text file */ int glp_print_mip(glp_prob *P, const char *fname); /* write MIP solution in printable format */ int glp_read_mip(glp_prob *P, const char *fname); /* read MIP solution from text file */ int glp_write_mip(glp_prob *P, const char *fname); /* write MIP solution to text file */ int glp_bf_exists(glp_prob *P); /* check if the basis factorization exists */ int glp_factorize(glp_prob *P); /* compute the basis factorization */ int glp_bf_updated(glp_prob *P); /* check if the basis factorization has been updated */ void glp_get_bfcp(glp_prob *P, glp_bfcp *parm); /* retrieve basis factorization control parameters */ void glp_set_bfcp(glp_prob *P, const glp_bfcp *parm); /* change basis factorization control parameters */ int glp_get_bhead(glp_prob *P, int k); /* retrieve the basis header information */ int glp_get_row_bind(glp_prob *P, int i); /* retrieve row index in the basis header */ int glp_get_col_bind(glp_prob *P, int j); /* retrieve column index in the basis header */ void glp_ftran(glp_prob *P, double x[]); /* perform forward transformation (solve system B*x = b) */ void glp_btran(glp_prob *P, double x[]); /* perform backward transformation (solve system B'*x = b) */ int glp_warm_up(glp_prob *P); /* "warm up" LP basis */ int glp_eval_tab_row(glp_prob *P, int k, int ind[], double val[]); /* compute row of the simplex tableau */ int glp_eval_tab_col(glp_prob *P, int k, int ind[], double val[]); /* compute column of the simplex tableau */ int glp_transform_row(glp_prob *P, int len, int ind[], double val[]); /* transform explicitly specified row */ int glp_transform_col(glp_prob *P, int len, int ind[], double val[]); /* transform explicitly specified column */ int glp_prim_rtest(glp_prob *P, int len, const int ind[], const double val[], int dir, double eps); /* perform primal ratio test */ int glp_dual_rtest(glp_prob *P, int len, const int ind[], const double val[], int dir, double eps); /* perform dual ratio test */ void glp_analyze_bound(glp_prob *P, int k, double *value1, int *var1, double *value2, int *var2); /* analyze active bound of non-basic variable */ void glp_analyze_coef(glp_prob *P, int k, double *coef1, int *var1, double *value1, double *coef2, int *var2, double *value2); /* analyze objective coefficient at basic variable */ int glp_ios_reason(glp_tree *T); /* determine reason for calling the callback routine */ glp_prob *glp_ios_get_prob(glp_tree *T); /* access the problem object */ void glp_ios_tree_size(glp_tree *T, int *a_cnt, int *n_cnt, int *t_cnt); /* determine size of the branch-and-bound tree */ int glp_ios_curr_node(glp_tree *T); /* determine current active subproblem */ int glp_ios_next_node(glp_tree *T, int p); /* determine next active subproblem */ int glp_ios_prev_node(glp_tree *T, int p); /* determine previous active subproblem */ int glp_ios_up_node(glp_tree *T, int p); /* determine parent subproblem */ int glp_ios_node_level(glp_tree *T, int p); /* determine subproblem level */ double glp_ios_node_bound(glp_tree *T, int p); /* determine subproblem local bound */ int glp_ios_best_node(glp_tree *T); /* find active subproblem with best local bound */ double glp_ios_mip_gap(glp_tree *T); /* compute relative MIP gap */ void *glp_ios_node_data(glp_tree *T, int p); /* access subproblem application-specific data */ void glp_ios_row_attr(glp_tree *T, int i, glp_attr *attr); /* retrieve additional row attributes */ int glp_ios_pool_size(glp_tree *T); /* determine current size of the cut pool */ int glp_ios_add_row(glp_tree *T, const char *name, int klass, int flags, int len, const int ind[], const double val[], int type, double rhs); /* add row (constraint) to the cut pool */ void glp_ios_del_row(glp_tree *T, int i); /* remove row (constraint) from the cut pool */ void glp_ios_clear_pool(glp_tree *T); /* remove all rows (constraints) from the cut pool */ int glp_ios_can_branch(glp_tree *T, int j); /* check if can branch upon specified variable */ void glp_ios_branch_upon(glp_tree *T, int j, int sel); /* choose variable to branch upon */ void glp_ios_select_node(glp_tree *T, int p); /* select subproblem to continue the search */ int glp_ios_heur_sol(glp_tree *T, const double x[]); /* provide solution found by heuristic */ void glp_ios_terminate(glp_tree *T); /* terminate the solution process */ void glp_init_mpscp(glp_mpscp *parm); /* initialize MPS format control parameters */ int glp_read_mps(glp_prob *P, int fmt, const glp_mpscp *parm, const char *fname); /* read problem data in MPS format */ int glp_write_mps(glp_prob *P, int fmt, const glp_mpscp *parm, const char *fname); /* write problem data in MPS format */ void glp_init_cpxcp(glp_cpxcp *parm); /* initialize CPLEX LP format control parameters */ int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname); /* read problem data in CPLEX LP format */ int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname); /* write problem data in CPLEX LP format */ int glp_read_prob(glp_prob *P, int flags, const char *fname); /* read problem data in GLPK format */ int glp_write_prob(glp_prob *P, int flags, const char *fname); /* write problem data in GLPK format */ glp_tran *glp_mpl_alloc_wksp(void); /* allocate the MathProg translator workspace */ int glp_mpl_read_model(glp_tran *tran, const char *fname, int skip); /* read and translate model section */ int glp_mpl_read_data(glp_tran *tran, const char *fname); /* read and translate data section */ int glp_mpl_generate(glp_tran *tran, const char *fname); /* generate the model */ void glp_mpl_build_prob(glp_tran *tran, glp_prob *prob); /* build LP/MIP problem instance from the model */ int glp_mpl_postsolve(glp_tran *tran, glp_prob *prob, int sol); /* postsolve the model */ void glp_mpl_free_wksp(glp_tran *tran); /* free the MathProg translator workspace */ int glp_main(int argc, const char *argv[]); /* stand-alone LP/MIP solver */ /**********************************************************************/ #ifndef GLP_LONG_DEFINED #define GLP_LONG_DEFINED typedef struct { int lo, hi; } glp_long; /* long integer data type */ #endif int glp_init_env(void); /* initialize GLPK environment */ const char *glp_version(void); /* determine library version */ int glp_free_env(void); /* free GLPK environment */ void glp_printf(const char *fmt, ...); /* write formatted output to terminal */ void glp_vprintf(const char *fmt, va_list arg); /* write formatted output to terminal */ int glp_term_out(int flag); /* enable/disable terminal output */ void glp_term_hook(int (*func)(void *info, const char *s), void *info); /* install hook to intercept terminal output */ int glp_open_tee(const char *fname); /* start copying terminal output to text file */ int glp_close_tee(void); /* stop copying terminal output to text file */ #ifndef GLP_ERROR_DEFINED #define GLP_ERROR_DEFINED typedef void (*_glp_error)(const char *fmt, ...); #endif #define glp_error glp_error_(__FILE__, __LINE__) _glp_error glp_error_(const char *file, int line); /* display error message and terminate execution */ #define glp_assert(expr) \ ((void)((expr) || (glp_assert_(#expr, __FILE__, __LINE__), 1))) void glp_assert_(const char *expr, const char *file, int line); /* check for logical condition */ void glp_error_hook(void (*func)(void *info), void *info); /* install hook to intercept abnormal termination */ void *glp_malloc(int size); /* allocate memory block */ void *glp_calloc(int n, int size); /* allocate memory block */ void glp_free(void *ptr); /* free memory block */ void glp_mem_limit(int limit); /* set memory usage limit */ void glp_mem_usage(int *count, int *cpeak, glp_long *total, glp_long *tpeak); /* get memory usage information */ glp_long glp_time(void); /* determine current universal time */ double glp_difftime(glp_long t1, glp_long t0); /* compute difference between two time values */ /**********************************************************************/ #ifndef GLP_DATA_DEFINED #define GLP_DATA_DEFINED typedef struct { double _opaque_data[100]; } glp_data; /* plain data file */ #endif glp_data *glp_sdf_open_file(const char *fname); /* open plain data file */ void glp_sdf_set_jump(glp_data *data, void *jump); /* set up error handling */ void glp_sdf_error(glp_data *data, const char *fmt, ...); /* print error message */ void glp_sdf_warning(glp_data *data, const char *fmt, ...); /* print warning message */ int glp_sdf_read_int(glp_data *data); /* read integer number */ double glp_sdf_read_num(glp_data *data); /* read floating-point number */ const char *glp_sdf_read_item(glp_data *data); /* read data item */ const char *glp_sdf_read_text(glp_data *data); /* read text until end of line */ int glp_sdf_line(glp_data *data); /* determine current line number */ void glp_sdf_close_file(glp_data *data); /* close plain data file */ /**********************************************************************/ typedef struct _glp_graph glp_graph; typedef struct _glp_vertex glp_vertex; typedef struct _glp_arc glp_arc; struct _glp_graph { /* graph descriptor */ void *pool; /* DMP *pool; */ /* memory pool to store graph components */ char *name; /* graph name (1 to 255 chars); NULL means no name is assigned to the graph */ int nv_max; /* length of the vertex list (enlarged automatically) */ int nv; /* number of vertices in the graph, 0 <= nv <= nv_max */ int na; /* number of arcs in the graph, na >= 0 */ glp_vertex **v; /* glp_vertex *v[1+nv_max]; */ /* v[i], 1 <= i <= nv, is a pointer to i-th vertex */ void *index; /* AVL *index; */ /* vertex index to find vertices by their names; NULL means the index does not exist */ int v_size; /* size of data associated with each vertex (0 to 256 bytes) */ int a_size; /* size of data associated with each arc (0 to 256 bytes) */ }; struct _glp_vertex { /* vertex descriptor */ int i; /* vertex ordinal number, 1 <= i <= nv */ char *name; /* vertex name (1 to 255 chars); NULL means no name is assigned to the vertex */ void *entry; /* AVLNODE *entry; */ /* pointer to corresponding entry in the vertex index; NULL means that either the index does not exist or the vertex has no name assigned */ void *data; /* pointer to data associated with the vertex */ void *temp; /* working pointer */ glp_arc *in; /* pointer to the (unordered) list of incoming arcs */ glp_arc *out; /* pointer to the (unordered) list of outgoing arcs */ }; struct _glp_arc { /* arc descriptor */ glp_vertex *tail; /* pointer to the tail endpoint */ glp_vertex *head; /* pointer to the head endpoint */ void *data; /* pointer to data associated with the arc */ void *temp; /* working pointer */ glp_arc *t_prev; /* pointer to previous arc having the same tail endpoint */ glp_arc *t_next; /* pointer to next arc having the same tail endpoint */ glp_arc *h_prev; /* pointer to previous arc having the same head endpoint */ glp_arc *h_next; /* pointer to next arc having the same head endpoint */ }; glp_graph *glp_create_graph(int v_size, int a_size); /* create graph */ void glp_set_graph_name(glp_graph *G, const char *name); /* assign (change) graph name */ int glp_add_vertices(glp_graph *G, int nadd); /* add new vertices to graph */ void glp_set_vertex_name(glp_graph *G, int i, const char *name); /* assign (change) vertex name */ glp_arc *glp_add_arc(glp_graph *G, int i, int j); /* add new arc to graph */ void glp_del_vertices(glp_graph *G, int ndel, const int num[]); /* delete vertices from graph */ void glp_del_arc(glp_graph *G, glp_arc *a); /* delete arc from graph */ void glp_erase_graph(glp_graph *G, int v_size, int a_size); /* erase graph content */ void glp_delete_graph(glp_graph *G); /* delete graph */ void glp_create_v_index(glp_graph *G); /* create vertex name index */ int glp_find_vertex(glp_graph *G, const char *name); /* find vertex by its name */ void glp_delete_v_index(glp_graph *G); /* delete vertex name index */ int glp_read_graph(glp_graph *G, const char *fname); /* read graph from plain text file */ int glp_write_graph(glp_graph *G, const char *fname); /* write graph to plain text file */ void glp_mincost_lp(glp_prob *P, glp_graph *G, int names, int v_rhs, int a_low, int a_cap, int a_cost); /* convert minimum cost flow problem to LP */ int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, int a_cap, int a_cost, double *sol, int a_x, int v_pi); /* find minimum-cost flow with out-of-kilter algorithm */ void glp_maxflow_lp(glp_prob *P, glp_graph *G, int names, int s, int t, int a_cap); /* convert maximum flow problem to LP */ int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap, double *sol, int a_x, int v_cut); /* find maximal flow with Ford-Fulkerson algorithm */ int glp_check_asnprob(glp_graph *G, int v_set); /* check correctness of assignment problem data */ /* assignment problem formulation: */ #define GLP_ASN_MIN 1 /* perfect matching (minimization) */ #define GLP_ASN_MAX 2 /* perfect matching (maximization) */ #define GLP_ASN_MMP 3 /* maximum matching */ int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, int v_set, int a_cost); /* convert assignment problem to LP */ int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost, double *sol, int a_x); /* solve assignment problem with out-of-kilter algorithm */ int glp_asnprob_hall(glp_graph *G, int v_set, int a_x); /* find bipartite matching of maximum cardinality */ double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls); /* solve critical path problem */ int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, int a_cost, const char *fname); /* read min-cost flow problem data in DIMACS format */ int glp_write_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap, int a_cost, const char *fname); /* write min-cost flow problem data in DIMACS format */ int glp_read_maxflow(glp_graph *G, int *s, int *t, int a_cap, const char *fname); /* read maximum flow problem data in DIMACS format */ int glp_write_maxflow(glp_graph *G, int s, int t, int a_cap, const char *fname); /* write maximum flow problem data in DIMACS format */ int glp_read_asnprob(glp_graph *G, int v_set, int a_cost, const char *fname); /* read assignment problem data in DIMACS format */ int glp_write_asnprob(glp_graph *G, int v_set, int a_cost, const char *fname); /* write assignment problem data in DIMACS format */ int glp_read_ccdata(glp_graph *G, int v_wgt, const char *fname); /* read graph in DIMACS clique/coloring format */ int glp_write_ccdata(glp_graph *G, int v_wgt, const char *fname); /* write graph in DIMACS clique/coloring format */ int glp_netgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, const int parm[1+15]); /* Klingman's network problem generator */ int glp_gridgen(glp_graph *G, int v_rhs, int a_cap, int a_cost, const int parm[1+14]); /* grid-like network problem generator */ int glp_rmfgen(glp_graph *G, int *s, int *t, int a_cap, const int parm[1+5]); /* Goldfarb's maximum flow problem generator */ int glp_weak_comp(glp_graph *G, int v_num); /* find all weakly connected components of graph */ int glp_strong_comp(glp_graph *G, int v_num); /* find all strongly connected components of graph */ int glp_top_sort(glp_graph *G, int v_num); /* topological sorting of acyclic digraph */ int glp_wclique_exact(glp_graph *G, int v_wgt, double *sol, int v_set); /* find maximum weight clique with exact algorithm */ /*********************************************************************** * NOTE: All symbols defined below are obsolete and kept here only for * backward compatibility. ***********************************************************************/ #define LPX glp_prob /* problem class: */ #define LPX_LP 100 /* linear programming (LP) */ #define LPX_MIP 101 /* mixed integer programming (MIP) */ /* type of auxiliary/structural variable: */ #define LPX_FR 110 /* free variable */ #define LPX_LO 111 /* variable with lower bound */ #define LPX_UP 112 /* variable with upper bound */ #define LPX_DB 113 /* double-bounded variable */ #define LPX_FX 114 /* fixed variable */ /* optimization direction flag: */ #define LPX_MIN 120 /* minimization */ #define LPX_MAX 121 /* maximization */ /* status of primal basic solution: */ #define LPX_P_UNDEF 132 /* primal solution is undefined */ #define LPX_P_FEAS 133 /* solution is primal feasible */ #define LPX_P_INFEAS 134 /* solution is primal infeasible */ #define LPX_P_NOFEAS 135 /* no primal feasible solution exists */ /* status of dual basic solution: */ #define LPX_D_UNDEF 136 /* dual solution is undefined */ #define LPX_D_FEAS 137 /* solution is dual feasible */ #define LPX_D_INFEAS 138 /* solution is dual infeasible */ #define LPX_D_NOFEAS 139 /* no dual feasible solution exists */ /* status of auxiliary/structural variable: */ #define LPX_BS 140 /* basic variable */ #define LPX_NL 141 /* non-basic variable on lower bound */ #define LPX_NU 142 /* non-basic variable on upper bound */ #define LPX_NF 143 /* non-basic free variable */ #define LPX_NS 144 /* non-basic fixed variable */ /* status of interior-point solution: */ #define LPX_T_UNDEF 150 /* interior solution is undefined */ #define LPX_T_OPT 151 /* interior solution is optimal */ /* kind of structural variable: */ #define LPX_CV 160 /* continuous variable */ #define LPX_IV 161 /* integer variable */ /* status of integer solution: */ #define LPX_I_UNDEF 170 /* integer solution is undefined */ #define LPX_I_OPT 171 /* integer solution is optimal */ #define LPX_I_FEAS 172 /* integer solution is feasible */ #define LPX_I_NOFEAS 173 /* no integer solution exists */ /* status codes reported by the routine lpx_get_status: */ #define LPX_OPT 180 /* optimal */ #define LPX_FEAS 181 /* feasible */ #define LPX_INFEAS 182 /* infeasible */ #define LPX_NOFEAS 183 /* no feasible */ #define LPX_UNBND 184 /* unbounded */ #define LPX_UNDEF 185 /* undefined */ /* exit codes returned by solver routines: */ #define LPX_E_OK 200 /* success */ #define LPX_E_EMPTY 201 /* empty problem */ #define LPX_E_BADB 202 /* invalid initial basis */ #define LPX_E_INFEAS 203 /* infeasible initial solution */ #define LPX_E_FAULT 204 /* unable to start the search */ #define LPX_E_OBJLL 205 /* objective lower limit reached */ #define LPX_E_OBJUL 206 /* objective upper limit reached */ #define LPX_E_ITLIM 207 /* iterations limit exhausted */ #define LPX_E_TMLIM 208 /* time limit exhausted */ #define LPX_E_NOFEAS 209 /* no feasible solution */ #define LPX_E_INSTAB 210 /* numerical instability */ #define LPX_E_SING 211 /* problems with basis matrix */ #define LPX_E_NOCONV 212 /* no convergence (interior) */ #define LPX_E_NOPFS 213 /* no primal feas. sol. (LP presolver) */ #define LPX_E_NODFS 214 /* no dual feas. sol. (LP presolver) */ #define LPX_E_MIPGAP 215 /* relative mip gap tolerance reached */ /* control parameter identifiers: */ #define LPX_K_MSGLEV 300 /* lp->msg_lev */ #define LPX_K_SCALE 301 /* lp->scale */ #define LPX_K_DUAL 302 /* lp->dual */ #define LPX_K_PRICE 303 /* lp->price */ #define LPX_K_RELAX 304 /* lp->relax */ #define LPX_K_TOLBND 305 /* lp->tol_bnd */ #define LPX_K_TOLDJ 306 /* lp->tol_dj */ #define LPX_K_TOLPIV 307 /* lp->tol_piv */ #define LPX_K_ROUND 308 /* lp->round */ #define LPX_K_OBJLL 309 /* lp->obj_ll */ #define LPX_K_OBJUL 310 /* lp->obj_ul */ #define LPX_K_ITLIM 311 /* lp->it_lim */ #define LPX_K_ITCNT 312 /* lp->it_cnt */ #define LPX_K_TMLIM 313 /* lp->tm_lim */ #define LPX_K_OUTFRQ 314 /* lp->out_frq */ #define LPX_K_OUTDLY 315 /* lp->out_dly */ #define LPX_K_BRANCH 316 /* lp->branch */ #define LPX_K_BTRACK 317 /* lp->btrack */ #define LPX_K_TOLINT 318 /* lp->tol_int */ #define LPX_K_TOLOBJ 319 /* lp->tol_obj */ #define LPX_K_MPSINFO 320 /* lp->mps_info */ #define LPX_K_MPSOBJ 321 /* lp->mps_obj */ #define LPX_K_MPSORIG 322 /* lp->mps_orig */ #define LPX_K_MPSWIDE 323 /* lp->mps_wide */ #define LPX_K_MPSFREE 324 /* lp->mps_free */ #define LPX_K_MPSSKIP 325 /* lp->mps_skip */ #define LPX_K_LPTORIG 326 /* lp->lpt_orig */ #define LPX_K_PRESOL 327 /* lp->presol */ #define LPX_K_BINARIZE 328 /* lp->binarize */ #define LPX_K_USECUTS 329 /* lp->use_cuts */ #define LPX_K_BFTYPE 330 /* lp->bfcp->type */ #define LPX_K_MIPGAP 331 /* lp->mip_gap */ #define LPX_C_COVER 0x01 /* mixed cover cuts */ #define LPX_C_CLIQUE 0x02 /* clique cuts */ #define LPX_C_GOMORY 0x04 /* Gomory's mixed integer cuts */ #define LPX_C_MIR 0x08 /* mixed integer rounding cuts */ #define LPX_C_ALL 0xFF /* all cuts */ typedef struct { /* this structure contains results reported by the routines which checks Karush-Kuhn-Tucker conditions (for details see comments to those routines) */ /*--------------------------------------------------------------*/ /* xR - A * xS = 0 (KKT.PE) */ double pe_ae_max; /* largest absolute error */ int pe_ae_row; /* number of row with largest absolute error */ double pe_re_max; /* largest relative error */ int pe_re_row; /* number of row with largest relative error */ int pe_quality; /* quality of primal solution: 'H' - high 'M' - medium 'L' - low '?' - primal solution is wrong */ /*--------------------------------------------------------------*/ /* l[k] <= x[k] <= u[k] (KKT.PB) */ double pb_ae_max; /* largest absolute error */ int pb_ae_ind; /* number of variable with largest absolute error */ double pb_re_max; /* largest relative error */ int pb_re_ind; /* number of variable with largest relative error */ int pb_quality; /* quality of primal feasibility: 'H' - high 'M' - medium 'L' - low '?' - primal solution is infeasible */ /*--------------------------------------------------------------*/ /* A' * (dR - cR) + (dS - cS) = 0 (KKT.DE) */ double de_ae_max; /* largest absolute error */ int de_ae_col; /* number of column with largest absolute error */ double de_re_max; /* largest relative error */ int de_re_col; /* number of column with largest relative error */ int de_quality; /* quality of dual solution: 'H' - high 'M' - medium 'L' - low '?' - dual solution is wrong */ /*--------------------------------------------------------------*/ /* d[k] >= 0 or d[k] <= 0 (KKT.DB) */ double db_ae_max; /* largest absolute error */ int db_ae_ind; /* number of variable with largest absolute error */ double db_re_max; /* largest relative error */ int db_re_ind; /* number of variable with largest relative error */ int db_quality; /* quality of dual feasibility: 'H' - high 'M' - medium 'L' - low '?' - dual solution is infeasible */ /*--------------------------------------------------------------*/ /* (x[k] - bound of x[k]) * d[k] = 0 (KKT.CS) */ double cs_ae_max; /* largest absolute error */ int cs_ae_ind; /* number of variable with largest absolute error */ double cs_re_max; /* largest relative error */ int cs_re_ind; /* number of variable with largest relative error */ int cs_quality; /* quality of complementary slackness: 'H' - high 'M' - medium 'L' - low '?' - primal and dual solutions are not complementary */ } LPXKKT; #define lpx_create_prob _glp_lpx_create_prob LPX *lpx_create_prob(void); /* create problem object */ #define lpx_set_prob_name _glp_lpx_set_prob_name void lpx_set_prob_name(LPX *lp, const char *name); /* assign (change) problem name */ #define lpx_set_obj_name _glp_lpx_set_obj_name void lpx_set_obj_name(LPX *lp, const char *name); /* assign (change) objective function name */ #define lpx_set_obj_dir _glp_lpx_set_obj_dir void lpx_set_obj_dir(LPX *lp, int dir); /* set (change) optimization direction flag */ #define lpx_add_rows _glp_lpx_add_rows int lpx_add_rows(LPX *lp, int nrs); /* add new rows to problem object */ #define lpx_add_cols _glp_lpx_add_cols int lpx_add_cols(LPX *lp, int ncs); /* add new columns to problem object */ #define lpx_set_row_name _glp_lpx_set_row_name void lpx_set_row_name(LPX *lp, int i, const char *name); /* assign (change) row name */ #define lpx_set_col_name _glp_lpx_set_col_name void lpx_set_col_name(LPX *lp, int j, const char *name); /* assign (change) column name */ #define lpx_set_row_bnds _glp_lpx_set_row_bnds void lpx_set_row_bnds(LPX *lp, int i, int type, double lb, double ub); /* set (change) row bounds */ #define lpx_set_col_bnds _glp_lpx_set_col_bnds void lpx_set_col_bnds(LPX *lp, int j, int type, double lb, double ub); /* set (change) column bounds */ #define lpx_set_obj_coef _glp_lpx_set_obj_coef void lpx_set_obj_coef(glp_prob *lp, int j, double coef); /* set (change) obj. coefficient or constant term */ #define lpx_set_mat_row _glp_lpx_set_mat_row void lpx_set_mat_row(LPX *lp, int i, int len, const int ind[], const double val[]); /* set (replace) row of the constraint matrix */ #define lpx_set_mat_col _glp_lpx_set_mat_col void lpx_set_mat_col(LPX *lp, int j, int len, const int ind[], const double val[]); /* set (replace) column of the constraint matrix */ #define lpx_load_matrix _glp_lpx_load_matrix void lpx_load_matrix(LPX *lp, int ne, const int ia[], const int ja[], const double ar[]); /* load (replace) the whole constraint matrix */ #define lpx_del_rows _glp_lpx_del_rows void lpx_del_rows(LPX *lp, int nrs, const int num[]); /* delete specified rows from problem object */ #define lpx_del_cols _glp_lpx_del_cols void lpx_del_cols(LPX *lp, int ncs, const int num[]); /* delete specified columns from problem object */ #define lpx_delete_prob _glp_lpx_delete_prob void lpx_delete_prob(LPX *lp); /* delete problem object */ #define lpx_get_prob_name _glp_lpx_get_prob_name const char *lpx_get_prob_name(LPX *lp); /* retrieve problem name */ #define lpx_get_obj_name _glp_lpx_get_obj_name const char *lpx_get_obj_name(LPX *lp); /* retrieve objective function name */ #define lpx_get_obj_dir _glp_lpx_get_obj_dir int lpx_get_obj_dir(LPX *lp); /* retrieve optimization direction flag */ #define lpx_get_num_rows _glp_lpx_get_num_rows int lpx_get_num_rows(LPX *lp); /* retrieve number of rows */ #define lpx_get_num_cols _glp_lpx_get_num_cols int lpx_get_num_cols(LPX *lp); /* retrieve number of columns */ #define lpx_get_row_name _glp_lpx_get_row_name const char *lpx_get_row_name(LPX *lp, int i); /* retrieve row name */ #define lpx_get_col_name _glp_lpx_get_col_name const char *lpx_get_col_name(LPX *lp, int j); /* retrieve column name */ #define lpx_get_row_type _glp_lpx_get_row_type int lpx_get_row_type(LPX *lp, int i); /* retrieve row type */ #define lpx_get_row_lb _glp_lpx_get_row_lb double lpx_get_row_lb(LPX *lp, int i); /* retrieve row lower bound */ #define lpx_get_row_ub _glp_lpx_get_row_ub double lpx_get_row_ub(LPX *lp, int i); /* retrieve row upper bound */ #define lpx_get_row_bnds _glp_lpx_get_row_bnds void lpx_get_row_bnds(LPX *lp, int i, int *typx, double *lb, double *ub); /* retrieve row bounds */ #define lpx_get_col_type _glp_lpx_get_col_type int lpx_get_col_type(LPX *lp, int j); /* retrieve column type */ #define lpx_get_col_lb _glp_lpx_get_col_lb double lpx_get_col_lb(LPX *lp, int j); /* retrieve column lower bound */ #define lpx_get_col_ub _glp_lpx_get_col_ub double lpx_get_col_ub(LPX *lp, int j); /* retrieve column upper bound */ #define lpx_get_col_bnds _glp_lpx_get_col_bnds void lpx_get_col_bnds(LPX *lp, int j, int *typx, double *lb, double *ub); /* retrieve column bounds */ #define lpx_get_obj_coef _glp_lpx_get_obj_coef double lpx_get_obj_coef(LPX *lp, int j); /* retrieve obj. coefficient or constant term */ #define lpx_get_num_nz _glp_lpx_get_num_nz int lpx_get_num_nz(LPX *lp); /* retrieve number of constraint coefficients */ #define lpx_get_mat_row _glp_lpx_get_mat_row int lpx_get_mat_row(LPX *lp, int i, int ind[], double val[]); /* retrieve row of the constraint matrix */ #define lpx_get_mat_col _glp_lpx_get_mat_col int lpx_get_mat_col(LPX *lp, int j, int ind[], double val[]); /* retrieve column of the constraint matrix */ #define lpx_create_index _glp_lpx_create_index void lpx_create_index(LPX *lp); /* create the name index */ #define lpx_find_row _glp_lpx_find_row int lpx_find_row(LPX *lp, const char *name); /* find row by its name */ #define lpx_find_col _glp_lpx_find_col int lpx_find_col(LPX *lp, const char *name); /* find column by its name */ #define lpx_delete_index _glp_lpx_delete_index void lpx_delete_index(LPX *lp); /* delete the name index */ #define lpx_scale_prob _glp_lpx_scale_prob void lpx_scale_prob(LPX *lp); /* scale problem data */ #define lpx_unscale_prob _glp_lpx_unscale_prob void lpx_unscale_prob(LPX *lp); /* unscale problem data */ #define lpx_set_row_stat _glp_lpx_set_row_stat void lpx_set_row_stat(LPX *lp, int i, int stat); /* set (change) row status */ #define lpx_set_col_stat _glp_lpx_set_col_stat void lpx_set_col_stat(LPX *lp, int j, int stat); /* set (change) column status */ #define lpx_std_basis _glp_lpx_std_basis void lpx_std_basis(LPX *lp); /* construct standard initial LP basis */ #define lpx_adv_basis _glp_lpx_adv_basis void lpx_adv_basis(LPX *lp); /* construct advanced initial LP basis */ #define lpx_cpx_basis _glp_lpx_cpx_basis void lpx_cpx_basis(LPX *lp); /* construct Bixby's initial LP basis */ #define lpx_simplex _glp_lpx_simplex int lpx_simplex(LPX *lp); /* easy-to-use driver to the simplex method */ #define lpx_exact _glp_lpx_exact int lpx_exact(LPX *lp); /* easy-to-use driver to the exact simplex method */ #define lpx_get_status _glp_lpx_get_status int lpx_get_status(LPX *lp); /* retrieve generic status of basic solution */ #define lpx_get_prim_stat _glp_lpx_get_prim_stat int lpx_get_prim_stat(LPX *lp); /* retrieve primal status of basic solution */ #define lpx_get_dual_stat _glp_lpx_get_dual_stat int lpx_get_dual_stat(LPX *lp); /* retrieve dual status of basic solution */ #define lpx_get_obj_val _glp_lpx_get_obj_val double lpx_get_obj_val(LPX *lp); /* retrieve objective value (basic solution) */ #define lpx_get_row_stat _glp_lpx_get_row_stat int lpx_get_row_stat(LPX *lp, int i); /* retrieve row status (basic solution) */ #define lpx_get_row_prim _glp_lpx_get_row_prim double lpx_get_row_prim(LPX *lp, int i); /* retrieve row primal value (basic solution) */ #define lpx_get_row_dual _glp_lpx_get_row_dual double lpx_get_row_dual(LPX *lp, int i); /* retrieve row dual value (basic solution) */ #define lpx_get_row_info _glp_lpx_get_row_info void lpx_get_row_info(LPX *lp, int i, int *tagx, double *vx, double *dx); /* obtain row solution information */ #define lpx_get_col_stat _glp_lpx_get_col_stat int lpx_get_col_stat(LPX *lp, int j); /* retrieve column status (basic solution) */ #define lpx_get_col_prim _glp_lpx_get_col_prim double lpx_get_col_prim(LPX *lp, int j); /* retrieve column primal value (basic solution) */ #define lpx_get_col_dual _glp_lpx_get_col_dual double lpx_get_col_dual(glp_prob *lp, int j); /* retrieve column dual value (basic solution) */ #define lpx_get_col_info _glp_lpx_get_col_info void lpx_get_col_info(LPX *lp, int j, int *tagx, double *vx, double *dx); /* obtain column solution information (obsolete) */ #define lpx_get_ray_info _glp_lpx_get_ray_info int lpx_get_ray_info(LPX *lp); /* determine what causes primal unboundness */ #define lpx_check_kkt _glp_lpx_check_kkt void lpx_check_kkt(LPX *lp, int scaled, LPXKKT *kkt); /* check Karush-Kuhn-Tucker conditions */ #define lpx_warm_up _glp_lpx_warm_up int lpx_warm_up(LPX *lp); /* "warm up" LP basis */ #define lpx_eval_tab_row _glp_lpx_eval_tab_row int lpx_eval_tab_row(LPX *lp, int k, int ind[], double val[]); /* compute row of the simplex table */ #define lpx_eval_tab_col _glp_lpx_eval_tab_col int lpx_eval_tab_col(LPX *lp, int k, int ind[], double val[]); /* compute column of the simplex table */ #define lpx_transform_row _glp_lpx_transform_row int lpx_transform_row(LPX *lp, int len, int ind[], double val[]); /* transform explicitly specified row */ #define lpx_transform_col _glp_lpx_transform_col int lpx_transform_col(LPX *lp, int len, int ind[], double val[]); /* transform explicitly specified column */ #define lpx_prim_ratio_test _glp_lpx_prim_ratio_test int lpx_prim_ratio_test(LPX *lp, int len, const int ind[], const double val[], int how, double tol); /* perform primal ratio test */ #define lpx_dual_ratio_test _glp_lpx_dual_ratio_test int lpx_dual_ratio_test(LPX *lp, int len, const int ind[], const double val[], int how, double tol); /* perform dual ratio test */ #define lpx_interior _glp_lpx_interior int lpx_interior(LPX *lp); /* easy-to-use driver to the interior point method */ #define lpx_ipt_status _glp_lpx_ipt_status int lpx_ipt_status(LPX *lp); /* retrieve status of interior-point solution */ #define lpx_ipt_obj_val _glp_lpx_ipt_obj_val double lpx_ipt_obj_val(LPX *lp); /* retrieve objective value (interior point) */ #define lpx_ipt_row_prim _glp_lpx_ipt_row_prim double lpx_ipt_row_prim(LPX *lp, int i); /* retrieve row primal value (interior point) */ #define lpx_ipt_row_dual _glp_lpx_ipt_row_dual double lpx_ipt_row_dual(LPX *lp, int i); /* retrieve row dual value (interior point) */ #define lpx_ipt_col_prim _glp_lpx_ipt_col_prim double lpx_ipt_col_prim(LPX *lp, int j); /* retrieve column primal value (interior point) */ #define lpx_ipt_col_dual _glp_lpx_ipt_col_dual double lpx_ipt_col_dual(LPX *lp, int j); /* retrieve column dual value (interior point) */ #define lpx_set_class _glp_lpx_set_class void lpx_set_class(LPX *lp, int klass); /* set problem class */ #define lpx_get_class _glp_lpx_get_class int lpx_get_class(LPX *lp); /* determine problem klass */ #define lpx_set_col_kind _glp_lpx_set_col_kind void lpx_set_col_kind(LPX *lp, int j, int kind); /* set (change) column kind */ #define lpx_get_col_kind _glp_lpx_get_col_kind int lpx_get_col_kind(LPX *lp, int j); /* retrieve column kind */ #define lpx_get_num_int _glp_lpx_get_num_int int lpx_get_num_int(LPX *lp); /* retrieve number of integer columns */ #define lpx_get_num_bin _glp_lpx_get_num_bin int lpx_get_num_bin(LPX *lp); /* retrieve number of binary columns */ #define lpx_integer _glp_lpx_integer int lpx_integer(LPX *lp); /* easy-to-use driver to the branch-and-bound method */ #define lpx_intopt _glp_lpx_intopt int lpx_intopt(LPX *lp); /* easy-to-use driver to the branch-and-bound method */ #define lpx_mip_status _glp_lpx_mip_status int lpx_mip_status(LPX *lp); /* retrieve status of MIP solution */ #define lpx_mip_obj_val _glp_lpx_mip_obj_val double lpx_mip_obj_val(LPX *lp); /* retrieve objective value (MIP solution) */ #define lpx_mip_row_val _glp_lpx_mip_row_val double lpx_mip_row_val(LPX *lp, int i); /* retrieve row value (MIP solution) */ #define lpx_mip_col_val _glp_lpx_mip_col_val double lpx_mip_col_val(LPX *lp, int j); /* retrieve column value (MIP solution) */ #define lpx_check_int _glp_lpx_check_int void lpx_check_int(LPX *lp, LPXKKT *kkt); /* check integer feasibility conditions */ #define lpx_reset_parms _glp_lpx_reset_parms void lpx_reset_parms(LPX *lp); /* reset control parameters to default values */ #define lpx_set_int_parm _glp_lpx_set_int_parm void lpx_set_int_parm(LPX *lp, int parm, int val); /* set (change) integer control parameter */ #define lpx_get_int_parm _glp_lpx_get_int_parm int lpx_get_int_parm(LPX *lp, int parm); /* query integer control parameter */ #define lpx_set_real_parm _glp_lpx_set_real_parm void lpx_set_real_parm(LPX *lp, int parm, double val); /* set (change) real control parameter */ #define lpx_get_real_parm _glp_lpx_get_real_parm double lpx_get_real_parm(LPX *lp, int parm); /* query real control parameter */ #define lpx_read_mps _glp_lpx_read_mps LPX *lpx_read_mps(const char *fname); /* read problem data in fixed MPS format */ #define lpx_write_mps _glp_lpx_write_mps int lpx_write_mps(LPX *lp, const char *fname); /* write problem data in fixed MPS format */ #define lpx_read_bas _glp_lpx_read_bas int lpx_read_bas(LPX *lp, const char *fname); /* read LP basis in fixed MPS format */ #define lpx_write_bas _glp_lpx_write_bas int lpx_write_bas(LPX *lp, const char *fname); /* write LP basis in fixed MPS format */ #define lpx_read_freemps _glp_lpx_read_freemps LPX *lpx_read_freemps(const char *fname); /* read problem data in free MPS format */ #define lpx_write_freemps _glp_lpx_write_freemps int lpx_write_freemps(LPX *lp, const char *fname); /* write problem data in free MPS format */ #define lpx_read_cpxlp _glp_lpx_read_cpxlp LPX *lpx_read_cpxlp(const char *fname); /* read problem data in CPLEX LP format */ #define lpx_write_cpxlp _glp_lpx_write_cpxlp int lpx_write_cpxlp(LPX *lp, const char *fname); /* write problem data in CPLEX LP format */ #define lpx_read_model _glp_lpx_read_model LPX *lpx_read_model(const char *model, const char *data, const char *output); /* read LP/MIP model written in GNU MathProg language */ #define lpx_print_prob _glp_lpx_print_prob int lpx_print_prob(LPX *lp, const char *fname); /* write problem data in plain text format */ #define lpx_print_sol _glp_lpx_print_sol int lpx_print_sol(LPX *lp, const char *fname); /* write LP problem solution in printable format */ #define lpx_print_sens_bnds _glp_lpx_print_sens_bnds int lpx_print_sens_bnds(LPX *lp, const char *fname); /* write bounds sensitivity information */ #define lpx_print_ips _glp_lpx_print_ips int lpx_print_ips(LPX *lp, const char *fname); /* write interior point solution in printable format */ #define lpx_print_mip _glp_lpx_print_mip int lpx_print_mip(LPX *lp, const char *fname); /* write MIP problem solution in printable format */ #define lpx_is_b_avail _glp_lpx_is_b_avail int lpx_is_b_avail(LPX *lp); /* check if LP basis is available */ #define lpx_write_pb _glp_lpx_write_pb int lpx_write_pb(LPX *lp, const char *fname, int normalized, int binarize); /* write problem data in (normalized) OPB format */ #define lpx_main _glp_lpx_main int lpx_main(int argc, const char *argv[]); /* stand-alone LP/MIP solver */ #ifdef __cplusplus } #endif #endif /* eof */ igraph/src/glpk/glpscf.h0000644000176000001440000001074412325527073014743 0ustar ripleyusers/* glpscf.h (Schur complement factorization) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPSCF_H #define GLPSCF_H /*********************************************************************** * The structure SCF defines the following factorization of a square * nxn matrix C (which is the Schur complement): * * F * C = U * P, * * where F is a square transforming matrix, U is an upper triangular * matrix, P is a permutation matrix. * * It is assumed that matrix C is small and dense, so matrices F and U * are stored in the dense format by rows as follows: * * 1 n n_max 1 n n_max * 1 * * * * * * x x x x 1 * * * * * * x x x x * * * * * * * x x x x . * * * * * x x x x * * * * * * * x x x x . . * * * * x x x x * * * * * * * x x x x . . . * * * x x x x * * * * * * * x x x x . . . . * * x x x x * n * * * * * * x x x x n . . . . . * x x x x * x x x x x x x x x x . . . . . . x x x x * x x x x x x x x x x . . . . . . . x x x * x x x x x x x x x x . . . . . . . . x x * n_max x x x x x x x x x x n_max . . . . . . . . . x * * matrix F matrix U * * where '*' are matrix elements, 'x' are reserved locations. * * Permutation matrix P is stored in row-like format. * * Matrix C normally is not stored. * * REFERENCES * * 1. M.A.Saunders, "LUSOL: A basis package for constrained optimiza- * tion," SCCM, Stanford University, 2006. * * 2. M.A.Saunders, "Notes 5: Basis Updates," CME 318, Stanford Univer- * sity, Spring 2006. * * 3. M.A.Saunders, "Notes 6: LUSOL---a Basis Factorization Package," * ibid. */ typedef struct SCF SCF; struct SCF { /* Schur complement factorization */ int n_max; /* maximal order of matrices C, F, U, P; n_max >= 1 */ int n; /* current order of matrices C, F, U, P; n >= 0 */ double *f; /* double f[1+n_max*n_max]; */ /* matrix F stored by rows */ double *u; /* double u[1+n_max*(n_max+1)/2]; */ /* upper triangle of matrix U stored by rows */ int *p; /* int p[1+n_max]; */ /* matrix P; p[i] = j means that P[i,j] = 1 */ int t_opt; /* type of transformation used to restore triangular structure of matrix U: */ #define SCF_TBG 1 /* Bartels-Golub elimination */ #define SCF_TGR 2 /* Givens plane rotation */ int rank; /* estimated rank of matrices C and U */ double *c; /* double c[1+n_max*n_max]; */ /* matrix C stored in the same format as matrix F and used only for debugging; normally this array is not allocated */ double *w; /* double w[1+n_max]; */ /* working array */ }; /* return codes: */ #define SCF_ESING 1 /* singular matrix */ #define SCF_ELIMIT 2 /* update limit reached */ #define scf_create_it _glp_scf_create_it SCF *scf_create_it(int n_max); /* create Schur complement factorization */ #define scf_update_exp _glp_scf_update_exp int scf_update_exp(SCF *scf, const double x[], const double y[], double z); /* update factorization on expanding C */ #define scf_solve_it _glp_scf_solve_it void scf_solve_it(SCF *scf, int tr, double x[]); /* solve either system C * x = b or C' * x = b */ #define scf_reset_it _glp_scf_reset_it void scf_reset_it(SCF *scf); /* reset factorization for empty matrix C */ #define scf_delete_it _glp_scf_delete_it void scf_delete_it(SCF *scf); /* delete Schur complement factorization */ #endif /* eof */ igraph/src/glpk/glpluf.h0000644000176000001440000003546212325527073014762 0ustar ripleyusers/* glpluf.h (LU-factorization) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPLUF_H #define GLPLUF_H /*********************************************************************** * The structure LUF defines LU-factorization of a square matrix A and * is the following quartet: * * [A] = (F, V, P, Q), (1) * * where F and V are such matrices that * * A = F * V, (2) * * and P and Q are such permutation matrices that the matrix * * L = P * F * inv(P) (3) * * is lower triangular with unity diagonal, and the matrix * * U = P * V * Q (4) * * is upper triangular. All the matrices have the order n. * * Matrices F and V are stored in row- and column-wise sparse format * as row and column linked lists of non-zero elements. Unity elements * on the main diagonal of matrix F are not stored. Pivot elements of * matrix V (which correspond to diagonal elements of matrix U) are * stored separately in an ordinary array. * * Permutation matrices P and Q are stored in ordinary arrays in both * row- and column-like formats. * * Matrices L and U are completely defined by matrices F, V, P, and Q * and therefore not stored explicitly. * * The factorization (1)-(4) is a version of LU-factorization. Indeed, * from (3) and (4) it follows that: * * F = inv(P) * L * P, * * U = inv(P) * U * inv(Q), * * and substitution into (2) leads to: * * A = F * V = inv(P) * L * U * inv(Q). * * For more details see the program documentation. */ typedef struct LUF LUF; struct LUF { /* LU-factorization of a square matrix */ int n_max; /* maximal value of n (increased automatically, if necessary) */ int n; /* the order of matrices A, F, V, P, Q */ int valid; /* the factorization is valid only if this flag is set */ /*--------------------------------------------------------------*/ /* matrix F in row-wise format */ int *fr_ptr; /* int fr_ptr[1+n_max]; */ /* fr_ptr[i], i = 1,...,n, is a pointer to the first element of i-th row in SVA */ int *fr_len; /* int fr_len[1+n_max]; */ /* fr_len[i], i = 1,...,n, is the number of elements in i-th row (except unity diagonal element) */ /*--------------------------------------------------------------*/ /* matrix F in column-wise format */ int *fc_ptr; /* int fc_ptr[1+n_max]; */ /* fc_ptr[j], j = 1,...,n, is a pointer to the first element of j-th column in SVA */ int *fc_len; /* int fc_len[1+n_max]; */ /* fc_len[j], j = 1,...,n, is the number of elements in j-th column (except unity diagonal element) */ /*--------------------------------------------------------------*/ /* matrix V in row-wise format */ int *vr_ptr; /* int vr_ptr[1+n_max]; */ /* vr_ptr[i], i = 1,...,n, is a pointer to the first element of i-th row in SVA */ int *vr_len; /* int vr_len[1+n_max]; */ /* vr_len[i], i = 1,...,n, is the number of elements in i-th row (except pivot element) */ int *vr_cap; /* int vr_cap[1+n_max]; */ /* vr_cap[i], i = 1,...,n, is the capacity of i-th row, i.e. maximal number of elements which can be stored in the row without relocating it, vr_cap[i] >= vr_len[i] */ double *vr_piv; /* double vr_piv[1+n_max]; */ /* vr_piv[p], p = 1,...,n, is the pivot element v[p,q] which corresponds to a diagonal element of matrix U = P*V*Q */ /*--------------------------------------------------------------*/ /* matrix V in column-wise format */ int *vc_ptr; /* int vc_ptr[1+n_max]; */ /* vc_ptr[j], j = 1,...,n, is a pointer to the first element of j-th column in SVA */ int *vc_len; /* int vc_len[1+n_max]; */ /* vc_len[j], j = 1,...,n, is the number of elements in j-th column (except pivot element) */ int *vc_cap; /* int vc_cap[1+n_max]; */ /* vc_cap[j], j = 1,...,n, is the capacity of j-th column, i.e. maximal number of elements which can be stored in the column without relocating it, vc_cap[j] >= vc_len[j] */ /*--------------------------------------------------------------*/ /* matrix P */ int *pp_row; /* int pp_row[1+n_max]; */ /* pp_row[i] = j means that P[i,j] = 1 */ int *pp_col; /* int pp_col[1+n_max]; */ /* pp_col[j] = i means that P[i,j] = 1 */ /* if i-th row or column of matrix F is i'-th row or column of matrix L, or if i-th row of matrix V is i'-th row of matrix U, then pp_row[i'] = i and pp_col[i] = i' */ /*--------------------------------------------------------------*/ /* matrix Q */ int *qq_row; /* int qq_row[1+n_max]; */ /* qq_row[i] = j means that Q[i,j] = 1 */ int *qq_col; /* int qq_col[1+n_max]; */ /* qq_col[j] = i means that Q[i,j] = 1 */ /* if j-th column of matrix V is j'-th column of matrix U, then qq_row[j] = j' and qq_col[j'] = j */ /*--------------------------------------------------------------*/ /* the Sparse Vector Area (SVA) is a set of locations used to store sparse vectors representing rows and columns of matrices F and V; each location is a doublet (ind, val), where ind is an index, and val is a numerical value of a sparse vector element; in the whole each sparse vector is a set of adjacent locations defined by a pointer to the first element and the number of elements; these pointer and number are stored in the corresponding matrix data structure (see above); the left part of SVA is used to store rows and columns of matrix V, and its right part is used to store rows and columns of matrix F; the middle part of SVA contains free (unused) locations */ int sv_size; /* the size of SVA, in locations; all locations are numbered by integers 1, ..., n, and location 0 is not used; if necessary, the SVA size is automatically increased */ int sv_beg, sv_end; /* SVA partitioning pointers: locations from 1 to sv_beg-1 belong to the left part locations from sv_beg to sv_end-1 belong to the middle part locations from sv_end to sv_size belong to the right part the size of the middle part is (sv_end - sv_beg) */ int *sv_ind; /* sv_ind[1+sv_size]; */ /* sv_ind[k], 1 <= k <= sv_size, is the index field of k-th location */ double *sv_val; /* sv_val[1+sv_size]; */ /* sv_val[k], 1 <= k <= sv_size, is the value field of k-th location */ /*--------------------------------------------------------------*/ /* in order to efficiently defragment the left part of SVA there is a doubly linked list of rows and columns of matrix V, where rows are numbered by 1, ..., n, while columns are numbered by n+1, ..., n+n, that allows uniquely identifying each row and column of V by only one integer; in this list rows and columns are ordered by ascending their pointers vr_ptr and vc_ptr */ int sv_head; /* the number of leftmost row/column */ int sv_tail; /* the number of rightmost row/column */ int *sv_prev; /* int sv_prev[1+n_max+n_max]; */ /* sv_prev[k], k = 1,...,n+n, is the number of a row/column which precedes k-th row/column */ int *sv_next; /* int sv_next[1+n_max+n_max]; */ /* sv_next[k], k = 1,...,n+n, is the number of a row/column which succedes k-th row/column */ /*--------------------------------------------------------------*/ /* working segment (used only during factorization) */ double *vr_max; /* int vr_max[1+n_max]; */ /* vr_max[i], 1 <= i <= n, is used only if i-th row of matrix V is active (i.e. belongs to the active submatrix), and is the largest magnitude of elements in i-th row; if vr_max[i] < 0, the largest magnitude is not known yet and should be computed by the pivoting routine */ /*--------------------------------------------------------------*/ /* in order to efficiently implement Markowitz strategy and Duff search technique there are two families {R[0], R[1], ..., R[n]} and {C[0], C[1], ..., C[n]}; member R[k] is the set of active rows of matrix V, which have k non-zeros, and member C[k] is the set of active columns of V, which have k non-zeros in the active submatrix (i.e. in the active rows); each set R[k] and C[k] is implemented as a separate doubly linked list */ int *rs_head; /* int rs_head[1+n_max]; */ /* rs_head[k], 0 <= k <= n, is the number of first active row, which has k non-zeros */ int *rs_prev; /* int rs_prev[1+n_max]; */ /* rs_prev[i], 1 <= i <= n, is the number of previous row, which has the same number of non-zeros as i-th row */ int *rs_next; /* int rs_next[1+n_max]; */ /* rs_next[i], 1 <= i <= n, is the number of next row, which has the same number of non-zeros as i-th row */ int *cs_head; /* int cs_head[1+n_max]; */ /* cs_head[k], 0 <= k <= n, is the number of first active column, which has k non-zeros (in the active rows) */ int *cs_prev; /* int cs_prev[1+n_max]; */ /* cs_prev[j], 1 <= j <= n, is the number of previous column, which has the same number of non-zeros (in the active rows) as j-th column */ int *cs_next; /* int cs_next[1+n_max]; */ /* cs_next[j], 1 <= j <= n, is the number of next column, which has the same number of non-zeros (in the active rows) as j-th column */ /* (end of working segment) */ /*--------------------------------------------------------------*/ /* working arrays */ int *flag; /* int flag[1+n_max]; */ /* integer working array */ double *work; /* double work[1+n_max]; */ /* floating-point working array */ /*--------------------------------------------------------------*/ /* control parameters */ int new_sva; /* new required size of the sparse vector area, in locations; set automatically by the factorizing routine */ double piv_tol; /* threshold pivoting tolerance, 0 < piv_tol < 1; element v[i,j] of the active submatrix fits to be pivot if it satisfies to the stability criterion |v[i,j]| >= piv_tol * max |v[i,*]|, i.e. if it is not very small in the magnitude among other elements in the same row; decreasing this parameter gives better sparsity at the expense of numerical accuracy and vice versa */ int piv_lim; /* maximal allowable number of pivot candidates to be considered; if piv_lim pivot candidates have been considered, the pivoting routine terminates the search with the best candidate found */ int suhl; /* if this flag is set, the pivoting routine applies a heuristic proposed by Uwe Suhl: if a column of the active submatrix has no eligible pivot candidates (i.e. all its elements do not satisfy to the stability criterion), the routine excludes it from futher consideration until it becomes column singleton; in many cases this allows reducing the time needed for pivot searching */ double eps_tol; /* epsilon tolerance; each element of the active submatrix, whose magnitude is less than eps_tol, is replaced by exact zero */ double max_gro; /* maximal allowable growth of elements of matrix V during all the factorization process; if on some eliminaion step the ratio big_v / max_a (see below) becomes greater than max_gro, matrix A is considered as ill-conditioned (assuming that the pivoting tolerance piv_tol has an appropriate value) */ /*--------------------------------------------------------------*/ /* some statistics */ int nnz_a; /* the number of non-zeros in matrix A */ int nnz_f; /* the number of non-zeros in matrix F (except diagonal elements, which are not stored) */ int nnz_v; /* the number of non-zeros in matrix V (except its pivot elements, which are stored in a separate array) */ double max_a; /* the largest magnitude of elements of matrix A */ double big_v; /* the largest magnitude of elements of matrix V appeared in the active submatrix during all the factorization process */ int rank; /* estimated rank of matrix A */ }; /* return codes: */ #define LUF_ESING 1 /* singular matrix */ #define LUF_ECOND 2 /* ill-conditioned matrix */ #define luf_create_it _glp_luf_create_it LUF *luf_create_it(void); /* create LU-factorization */ #define luf_defrag_sva _glp_luf_defrag_sva void luf_defrag_sva(LUF *luf); /* defragment the sparse vector area */ #define luf_enlarge_row _glp_luf_enlarge_row int luf_enlarge_row(LUF *luf, int i, int cap); /* enlarge row capacity */ #define luf_enlarge_col _glp_luf_enlarge_col int luf_enlarge_col(LUF *luf, int j, int cap); /* enlarge column capacity */ #define luf_factorize _glp_luf_factorize int luf_factorize(LUF *luf, int n, int (*col)(void *info, int j, int ind[], double val[]), void *info); /* compute LU-factorization */ #define luf_f_solve _glp_luf_f_solve void luf_f_solve(LUF *luf, int tr, double x[]); /* solve system F*x = b or F'*x = b */ #define luf_v_solve _glp_luf_v_solve void luf_v_solve(LUF *luf, int tr, double x[]); /* solve system V*x = b or V'*x = b */ #define luf_a_solve _glp_luf_a_solve void luf_a_solve(LUF *luf, int tr, double x[]); /* solve system A*x = b or A'*x = b */ #define luf_delete_it _glp_luf_delete_it void luf_delete_it(LUF *luf); /* delete LU-factorization */ #endif /* eof */ igraph/src/glpk/glplux.h0000644000176000001440000002122212325527073014771 0ustar ripleyusers/* glplux.h (LU-factorization, bignum arithmetic) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPLUX_H #define GLPLUX_H #include "glpdmp.h" #include "glpgmp.h" /*---------------------------------------------------------------------- // The structure LUX defines LU-factorization of a square matrix A, // which is the following quartet: // // [A] = (F, V, P, Q), (1) // // where F and V are such matrices that // // A = F * V, (2) // // and P and Q are such permutation matrices that the matrix // // L = P * F * inv(P) (3) // // is lower triangular with unity diagonal, and the matrix // // U = P * V * Q (4) // // is upper triangular. All the matrices have the order n. // // The matrices F and V are stored in row/column-wise sparse format as // row and column linked lists of non-zero elements. Unity elements on // the main diagonal of the matrix F are not stored. Pivot elements of // the matrix V (that correspond to diagonal elements of the matrix U) // are also missing from the row and column lists and stored separately // in an ordinary array. // // The permutation matrices P and Q are stored as ordinary arrays using // both row- and column-like formats. // // The matrices L and U being completely defined by the matrices F, V, // P, and Q are not stored explicitly. // // It is easy to show that the factorization (1)-(3) is some version of // LU-factorization. Indeed, from (3) and (4) it follows that: // // F = inv(P) * L * P, // // V = inv(P) * U * inv(Q), // // and substitution into (2) gives: // // A = F * V = inv(P) * L * U * inv(Q). // // For more details see the program documentation. */ typedef struct LUX LUX; typedef struct LUXELM LUXELM; typedef struct LUXWKA LUXWKA; struct LUX { /* LU-factorization of a square matrix */ int n; /* the order of matrices A, F, V, P, Q */ DMP *pool; /* memory pool for elements of matrices F and V */ LUXELM **F_row; /* LUXELM *F_row[1+n]; */ /* F_row[0] is not used; F_row[i], 1 <= i <= n, is a pointer to the list of elements in i-th row of matrix F (diagonal elements are not stored) */ LUXELM **F_col; /* LUXELM *F_col[1+n]; */ /* F_col[0] is not used; F_col[j], 1 <= j <= n, is a pointer to the list of elements in j-th column of matrix F (diagonal elements are not stored) */ mpq_t *V_piv; /* mpq_t V_piv[1+n]; */ /* V_piv[0] is not used; V_piv[p], 1 <= p <= n, is a pivot element v[p,q] corresponding to a diagonal element u[k,k] of matrix U = P*V*Q (used on k-th elimination step, k = 1, 2, ..., n) */ LUXELM **V_row; /* LUXELM *V_row[1+n]; */ /* V_row[0] is not used; V_row[i], 1 <= i <= n, is a pointer to the list of elements in i-th row of matrix V (except pivot elements) */ LUXELM **V_col; /* LUXELM *V_col[1+n]; */ /* V_col[0] is not used; V_col[j], 1 <= j <= n, is a pointer to the list of elements in j-th column of matrix V (except pivot elements) */ int *P_row; /* int P_row[1+n]; */ /* P_row[0] is not used; P_row[i] = j means that p[i,j] = 1, where p[i,j] is an element of permutation matrix P */ int *P_col; /* int P_col[1+n]; */ /* P_col[0] is not used; P_col[j] = i means that p[i,j] = 1, where p[i,j] is an element of permutation matrix P */ /* if i-th row or column of matrix F is i'-th row or column of matrix L = P*F*inv(P), or if i-th row of matrix V is i'-th row of matrix U = P*V*Q, then P_row[i'] = i and P_col[i] = i' */ int *Q_row; /* int Q_row[1+n]; */ /* Q_row[0] is not used; Q_row[i] = j means that q[i,j] = 1, where q[i,j] is an element of permutation matrix Q */ int *Q_col; /* int Q_col[1+n]; */ /* Q_col[0] is not used; Q_col[j] = i means that q[i,j] = 1, where q[i,j] is an element of permutation matrix Q */ /* if j-th column of matrix V is j'-th column of matrix U = P*V*Q, then Q_row[j] = j' and Q_col[j'] = j */ int rank; /* the (exact) rank of matrices A and V */ }; struct LUXELM { /* element of matrix F or V */ int i; /* row index, 1 <= i <= m */ int j; /* column index, 1 <= j <= n */ mpq_t val; /* numeric (non-zero) element value */ LUXELM *r_prev; /* pointer to previous element in the same row */ LUXELM *r_next; /* pointer to next element in the same row */ LUXELM *c_prev; /* pointer to previous element in the same column */ LUXELM *c_next; /* pointer to next element in the same column */ }; struct LUXWKA { /* working area (used only during factorization) */ /* in order to efficiently implement Markowitz strategy and Duff search technique there are two families {R[0], R[1], ..., R[n]} and {C[0], C[1], ..., C[n]}; member R[k] is a set of active rows of matrix V having k non-zeros, and member C[k] is a set of active columns of matrix V having k non-zeros (in the active submatrix); each set R[k] and C[k] is implemented as a separate doubly linked list */ int *R_len; /* int R_len[1+n]; */ /* R_len[0] is not used; R_len[i], 1 <= i <= n, is the number of non-zero elements in i-th row of matrix V (that is the length of i-th row) */ int *R_head; /* int R_head[1+n]; */ /* R_head[k], 0 <= k <= n, is the number of a first row, which is active and whose length is k */ int *R_prev; /* int R_prev[1+n]; */ /* R_prev[0] is not used; R_prev[i], 1 <= i <= n, is the number of a previous row, which is active and has the same length as i-th row */ int *R_next; /* int R_next[1+n]; */ /* R_prev[0] is not used; R_prev[i], 1 <= i <= n, is the number of a next row, which is active and has the same length as i-th row */ int *C_len; /* int C_len[1+n]; */ /* C_len[0] is not used; C_len[j], 1 <= j <= n, is the number of non-zero elements in j-th column of the active submatrix of matrix V (that is the length of j-th column in the active submatrix) */ int *C_head; /* int C_head[1+n]; */ /* C_head[k], 0 <= k <= n, is the number of a first column, which is active and whose length is k */ int *C_prev; /* int C_prev[1+n]; */ /* C_prev[0] is not used; C_prev[j], 1 <= j <= n, is the number of a previous column, which is active and has the same length as j-th column */ int *C_next; /* int C_next[1+n]; */ /* C_next[0] is not used; C_next[j], 1 <= j <= n, is the number of a next column, which is active and has the same length as j-th column */ }; #define lux_create _glp_lux_create #define lux_decomp _glp_lux_decomp #define lux_f_solve _glp_lux_f_solve #define lux_v_solve _glp_lux_v_solve #define lux_solve _glp_lux_solve #define lux_delete _glp_lux_delete LUX *lux_create(int n); /* create LU-factorization */ int lux_decomp(LUX *lux, int (*col)(void *info, int j, int ind[], mpq_t val[]), void *info); /* compute LU-factorization */ void lux_f_solve(LUX *lux, int tr, mpq_t x[]); /* solve system F*x = b or F'*x = b */ void lux_v_solve(LUX *lux, int tr, mpq_t x[]); /* solve system V*x = b or V'*x = b */ void lux_solve(LUX *lux, int tr, mpq_t x[]); /* solve system A*x = b or A'*x = b */ void lux_delete(LUX *lux); /* delete LU-factorization */ #endif /* eof */ igraph/src/glpk/glpsql.h0000644000176000001440000000414312325527073014763 0ustar ripleyusers/* glpsql.h */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Author: Heinrich Schuchardt . * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPSQL_H #define GLPSQL_H #define db_iodbc_open _glp_db_iodbc_open void *db_iodbc_open(TABDCA *dca, int mode); /* open iODBC database connection */ #define db_iodbc_read _glp_db_iodbc_read int db_iodbc_read(TABDCA *dca, void *link); /* read data from iODBC */ #define db_iodbc_write _glp_db_iodbc_write int db_iodbc_write(TABDCA *dca, void *link); /* write data to iODBC */ #define db_iodbc_close _glp_db_iodbc_close int db_iodbc_close(TABDCA *dca, void *link); /* close iODBC database connection */ #define db_mysql_open _glp_db_mysql_open void *db_mysql_open(TABDCA *dca, int mode); /* open MySQL database connection */ #define db_mysql_read _glp_db_mysql_read int db_mysql_read(TABDCA *dca, void *link); /* read data from MySQL */ #define db_mysql_write _glp_db_mysql_write int db_mysql_write(TABDCA *dca, void *link); /* write data to MySQL */ #define db_mysql_close _glp_db_mysql_close int db_mysql_close(TABDCA *dca, void *link); /* close MySQL database connection */ #endif /* eof */ igraph/src/glpk/glptsp.h0000644000176000001440000001051712325527073014774 0ustar ripleyusers/* glptsp.h (TSP format) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPTSP_H #define GLPTSP_H typedef struct TSP TSP; struct TSP { /* TSP (or related problem) instance in the format described in the report [G.Reinelt, TSPLIB 95] */ /*--------------------------------------------------------------*/ /* the specification part */ char *name; /* identifies the data file */ int type; /* specifies the type of data: */ #define TSP_UNDEF 0 /* undefined */ #define TSP_TSP 1 /* symmetric TSP */ #define TSP_ATSP 2 /* asymmetric TSP */ #define TSP_TOUR 3 /* collection of tours */ char *comment; /* additional comments (usually the name of the contributor or creator of the problem instance is given here) */ int dimension; /* for a TSP or ATSP, the dimension is the number of its nodes for a TOUR it is the dimension of the corresponding problem */ int edge_weight_type; /* specifies how the edge weights (or distances) are given: */ #define TSP_UNDEF 0 /* undefined */ #define TSP_EXPLICIT 1 /* listed explicitly */ #define TSP_EUC_2D 2 /* Eucl. distances in 2-D */ #define TSP_CEIL_2D 3 /* Eucl. distances in 2-D rounded up */ #define TSP_GEO 4 /* geographical distances */ #define TSP_ATT 5 /* special distance function */ int edge_weight_format; /* describes the format of the edge weights if they are given explicitly: */ #define TSP_UNDEF 0 /* undefined */ #define TSP_FUNCTION 1 /* given by a function */ #define TSP_FULL_MATRIX 2 /* given by a full matrix */ #define TSP_UPPER_ROW 3 /* upper triangulat matrix (row-wise without diagonal entries) */ #define TSP_LOWER_DIAG_ROW 4 /* lower triangular matrix (row-wise including diagonal entries) */ int display_data_type; /* specifies how a graphical display of the nodes can be obtained: */ #define TSP_UNDEF 0 /* undefined */ #define TSP_COORD_DISPLAY 1 /* display is generated from the node coordinates */ #define TSP_TWOD_DISPLAY 2 /* explicit coordinates in 2-D are given */ /*--------------------------------------------------------------*/ /* data part */ /* NODE_COORD_SECTION: */ double *node_x_coord; /* double node_x_coord[1+dimension]; */ double *node_y_coord; /* double node_y_coord[1+dimension]; */ /* DISPLAY_DATA_SECTION: */ double *dply_x_coord; /* double dply_x_coord[1+dimension]; */ double *dply_y_coord; /* double dply_y_coord[1+dimension]; */ /* TOUR_SECTION: */ int *tour; /* int tour[1+dimension]; */ /* EDGE_WEIGHT_SECTION: */ int *edge_weight; /* int edge_weight[1+dimension*dimension]; */ }; #define tsp_read_data _glp_tsp_read_data #define tsp_free_data _glp_tsp_free_data #define tsp_distance _glp_tsp_distance TSP *tsp_read_data(char *fname); /* read TSP instance data */ void tsp_free_data(TSP *tsp); /* free TSP instance data */ int tsp_distance(TSP *tsp, int i, int j); /* compute distance between two nodes */ #endif /* eof */ igraph/src/glpk/glpipm.h0000644000176000001440000000245612325527073014756 0ustar ripleyusers/* glpipm.h (primal-dual interior-point method) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPIPM_H #define GLPIPM_H #include "glpapi.h" #define ipm_solve _glp_ipm_solve int ipm_solve(glp_prob *P, const glp_iptcp *parm); /* core LP solver based on the interior-point method */ #endif /* eof */ igraph/src/glpk/glpenv.h0000644000176000001440000001551012325527073014754 0ustar ripleyusers/* glpenv.h (GLPK environment) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPENV_H #define GLPENV_H #include "glpstd.h" #include "glplib.h" typedef struct ENV ENV; typedef struct MEM MEM; typedef struct XFILE XFILE; #define ENV_MAGIC 0x454E5631 /* environment block magic value */ #define TERM_BUF_SIZE 4096 /* terminal output buffer size, in bytes */ #define IOERR_MSG_SIZE 1024 /* i/o error message buffer size, in bytes */ #define MEM_MAGIC 0x4D454D31 /* memory block descriptor magic value */ struct ENV { /* environment block */ int magic; /* magic value used for debugging */ char version[7+1]; /* version string returned by the routine glp_version */ /*--------------------------------------------------------------*/ /* terminal output */ char *term_buf; /* char term_buf[TERM_BUF_SIZE]; */ /* terminal output buffer */ int term_out; /* flag to enable/disable terminal output */ int (*term_hook)(void *info, const char *s); /* user-defined routine to intercept terminal output */ void *term_info; /* transit pointer (cookie) passed to the routine term_hook */ FILE *tee_file; /* output stream used to copy terminal output */ /*--------------------------------------------------------------*/ /* error handling */ const char *err_file; /* value of the __FILE__ macro passed to glp_error */ int err_line; /* value of the __LINE__ macro passed to glp_error */ void (*err_hook)(void *info); /* user-defined routine to intercept abnormal termination */ void *err_info; /* transit pointer (cookie) passed to the routine err_hook */ /*--------------------------------------------------------------*/ /* memory allocation */ glp_long mem_limit; /* maximal amount of memory (in bytes) available for dynamic allocation */ MEM *mem_ptr; /* pointer to the linked list of allocated memory blocks */ int mem_count; /* total number of currently allocated memory blocks */ int mem_cpeak; /* peak value of mem_count */ glp_long mem_total; /* total amount of currently allocated memory (in bytes; is the sum of the size field over all memory block descriptors) */ glp_long mem_tpeak; /* peak value of mem_total */ /*--------------------------------------------------------------*/ /* stream input/output */ XFILE *file_ptr; /* pointer to the linked list of active stream descriptors */ char *ioerr_msg; /* char ioerr_msg[IOERR_MSG_SIZE]; */ /* input/output error message buffer */ /*--------------------------------------------------------------*/ /* shared libraries support */ void *h_odbc; /* handle to ODBC shared library */ void *h_mysql; /* handle to MySQL shared library */ }; struct MEM { /* memory block descriptor */ int flag; /* descriptor flag */ int size; /* size of block (in bytes, including descriptor) */ MEM *prev; /* pointer to previous memory block descriptor */ MEM *next; /* pointer to next memory block descriptor */ }; struct XFILE { /* input/output stream descriptor */ int type; /* stream handle type: */ #define FH_FILE 0x11 /* FILE */ #define FH_ZLIB 0x22 /* gzFile */ void *fh; /* pointer to stream handle */ XFILE *prev; /* pointer to previous stream descriptor */ XFILE *next; /* pointer to next stream descriptor */ }; #define XEOF (-1) #define get_env_ptr _glp_get_env_ptr ENV *get_env_ptr(void); /* retrieve pointer to environment block */ #define tls_set_ptr _glp_tls_set_ptr void tls_set_ptr(void *ptr); /* store global pointer in TLS */ #define tls_get_ptr _glp_tls_get_ptr void *tls_get_ptr(void); /* retrieve global pointer from TLS */ #define xprintf glp_printf void glp_printf(const char *fmt, ...); /* write formatted output to the terminal */ #define xvprintf glp_vprintf void glp_vprintf(const char *fmt, va_list arg); /* write formatted output to the terminal */ #ifndef GLP_ERROR_DEFINED #define GLP_ERROR_DEFINED typedef void (*_glp_error)(const char *fmt, ...); #endif #define xerror glp_error_(__FILE__, __LINE__) _glp_error glp_error_(const char *file, int line); /* display error message and terminate execution */ #define xassert(expr) \ ((void)((expr) || (glp_assert_(#expr, __FILE__, __LINE__), 1))) void glp_assert_(const char *expr, const char *file, int line); /* check for logical condition */ #define xmalloc glp_malloc void *glp_malloc(int size); /* allocate memory block */ #define xcalloc glp_calloc void *glp_calloc(int n, int size); /* allocate memory block */ #define xfree glp_free void glp_free(void *ptr); /* free memory block */ #define xtime glp_time glp_long glp_time(void); /* determine current universal time */ #define xdifftime glp_difftime double glp_difftime(glp_long t1, glp_long t0); /* compute difference between two time values, in seconds */ #define lib_err_msg _glp_lib_err_msg void lib_err_msg(const char *msg); #define xerrmsg _glp_lib_xerrmsg const char *xerrmsg(void); #define xfopen _glp_lib_xfopen XFILE *xfopen(const char *fname, const char *mode); #define xferror _glp_lib_xferror int xferror(XFILE *file); #define xfeof _glp_lib_xfeof int xfeof(XFILE *file); #define xfgetc _glp_lib_xfgetc int xfgetc(XFILE *file); #define xfputc _glp_lib_xfputc int xfputc(int c, XFILE *file); #define xfflush _glp_lib_xfflush int xfflush(XFILE *fp); #define xfclose _glp_lib_xfclose int xfclose(XFILE *file); #define xfprintf _glp_lib_xfprintf int xfprintf(XFILE *file, const char *fmt, ...); #define xdlopen _glp_xdlopen void *xdlopen(const char *module); #define xdlsym _glp_xdlsym void *xdlsym(void *h, const char *symbol); #define xdlclose _glp_xdlclose void xdlclose(void *h); #endif /* eof */ igraph/src/glpk/glpbfd.h0000644000176000001440000000507412325527073014723 0ustar ripleyusers/* glpbfd.h (LP basis factorization driver) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPBFD_H #define GLPBFD_H #ifndef GLPBFD_PRIVATE typedef struct { double _opaque_bfd[100]; } BFD; #endif /* return codes: */ #define BFD_ESING 1 /* singular matrix */ #define BFD_ECOND 2 /* ill-conditioned matrix */ #define BFD_ECHECK 3 /* insufficient accuracy */ #define BFD_ELIMIT 4 /* update limit reached */ #define BFD_EROOM 5 /* SVA overflow */ #define bfd_create_it _glp_bfd_create_it BFD *bfd_create_it(void); /* create LP basis factorization */ #define bfd_set_parm _glp_bfd_set_parm void bfd_set_parm(BFD *bfd, const void *parm); /* change LP basis factorization control parameters */ #define bfd_factorize _glp_bfd_factorize int bfd_factorize(BFD *bfd, int m, const int bh[], int (*col) (void *info, int j, int ind[], double val[]), void *info); /* compute LP basis factorization */ #define bfd_ftran _glp_bfd_ftran void bfd_ftran(BFD *bfd, double x[]); /* perform forward transformation (solve system B*x = b) */ #define bfd_btran _glp_bfd_btran void bfd_btran(BFD *bfd, double x[]); /* perform backward transformation (solve system B'*x = b) */ #define bfd_update_it _glp_bfd_update_it int bfd_update_it(BFD *bfd, int j, int bh, int len, const int ind[], const double val[]); /* update LP basis factorization */ #define bfd_get_count _glp_bfd_get_count int bfd_get_count(BFD *bfd); /* determine factorization update count */ #define bfd_delete_it _glp_bfd_delete_it void bfd_delete_it(BFD *bfd); /* delete LP basis factorization */ #endif /* eof */ igraph/src/glpk/glpspx.h0000644000176000001440000000265412325527073015003 0ustar ripleyusers/* glpspx.h (core simplex solvers) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPSPX_H #define GLPSPX_H #include "glpapi.h" #define spx_primal _glp_spx_primal int spx_primal(glp_prob *lp, const glp_smcp *parm); /* core LP solver based on the primal simplex method */ #define spx_dual _glp_spx_dual int spx_dual(glp_prob *lp, const glp_smcp *parm); /* core LP solver based on the dual simplex method */ #endif /* eof */ igraph/src/glpk/glpapi.h0000644000176000001440000003030212325527073014731 0ustar ripleyusers/* glpapi.h (application program interface) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPAPI_H #define GLPAPI_H #define GLP_PROB_DEFINED typedef struct glp_prob glp_prob; #include "glpk.h" #include "glpavl.h" #include "glpbfd.h" typedef struct GLPROW GLPROW; typedef struct GLPCOL GLPCOL; typedef struct GLPAIJ GLPAIJ; #define GLP_PROB_MAGIC 0xD7D9D6C2 struct glp_prob { /* LP/MIP problem object */ int magic; /* magic value used for debugging */ DMP *pool; /* memory pool to store problem object components */ glp_tree *tree; /* pointer to the search tree; set by the MIP solver when this object is used in the tree as a core MIP object */ void *parms; /* reserved for backward compatibility */ /*--------------------------------------------------------------*/ /* LP/MIP data */ char *name; /* problem name (1 to 255 chars); NULL means no name is assigned to the problem */ char *obj; /* objective function name (1 to 255 chars); NULL means no name is assigned to the objective function */ int dir; /* optimization direction flag (objective "sense"): GLP_MIN - minimization GLP_MAX - maximization */ double c0; /* constant term of the objective function ("shift") */ int m_max; /* length of the array of rows (enlarged automatically) */ int n_max; /* length of the array of columns (enlarged automatically) */ int m; /* number of rows, 0 <= m <= m_max */ int n; /* number of columns, 0 <= n <= n_max */ int nnz; /* number of non-zero constraint coefficients, nnz >= 0 */ GLPROW **row; /* GLPROW *row[1+m_max]; */ /* row[i], 1 <= i <= m, is a pointer to i-th row */ GLPCOL **col; /* GLPCOL *col[1+n_max]; */ /* col[j], 1 <= j <= n, is a pointer to j-th column */ AVL *r_tree; /* row index to find rows by their names; NULL means this index does not exist */ AVL *c_tree; /* column index to find columns by their names; NULL means this index does not exist */ /*--------------------------------------------------------------*/ /* basis factorization (LP) */ int valid; /* the factorization is valid only if this flag is set */ int *head; /* int head[1+m_max]; */ /* basis header (valid only if the factorization is valid); head[i] = k is the ordinal number of auxiliary (1 <= k <= m) or structural (m+1 <= k <= m+n) variable which corresponds to i-th basic variable xB[i], 1 <= i <= m */ glp_bfcp *bfcp; /* basis factorization control parameters; may be NULL */ BFD *bfd; /* BFD bfd[1:m,1:m]; */ /* basis factorization driver; may be NULL */ /*--------------------------------------------------------------*/ /* basic solution (LP) */ int pbs_stat; /* primal basic solution status: GLP_UNDEF - primal solution is undefined GLP_FEAS - primal solution is feasible GLP_INFEAS - primal solution is infeasible GLP_NOFEAS - no primal feasible solution exists */ int dbs_stat; /* dual basic solution status: GLP_UNDEF - dual solution is undefined GLP_FEAS - dual solution is feasible GLP_INFEAS - dual solution is infeasible GLP_NOFEAS - no dual feasible solution exists */ double obj_val; /* objective function value */ int it_cnt; /* simplex method iteration count; increased by one on performing one simplex iteration */ int some; /* ordinal number of some auxiliary or structural variable having certain property, 0 <= some <= m+n */ /*--------------------------------------------------------------*/ /* interior-point solution (LP) */ int ipt_stat; /* interior-point solution status: GLP_UNDEF - interior solution is undefined GLP_OPT - interior solution is optimal GLP_INFEAS - interior solution is infeasible GLP_NOFEAS - no feasible solution exists */ double ipt_obj; /* objective function value */ /*--------------------------------------------------------------*/ /* integer solution (MIP) */ int mip_stat; /* integer solution status: GLP_UNDEF - integer solution is undefined GLP_OPT - integer solution is optimal GLP_FEAS - integer solution is feasible GLP_NOFEAS - no integer solution exists */ double mip_obj; /* objective function value */ }; struct GLPROW { /* LP/MIP row (auxiliary variable) */ int i; /* ordinal number (1 to m) assigned to this row */ char *name; /* row name (1 to 255 chars); NULL means no name is assigned to this row */ AVLNODE *node; /* pointer to corresponding node in the row index; NULL means that either the row index does not exist or this row has no name assigned */ #if 1 /* 20/IX-2008 */ int level; unsigned char origin; unsigned char klass; #endif int type; /* type of the auxiliary variable: GLP_FR - free variable GLP_LO - variable with lower bound GLP_UP - variable with upper bound GLP_DB - double-bounded variable GLP_FX - fixed variable */ double lb; /* non-scaled */ /* lower bound; if the row has no lower bound, lb is zero */ double ub; /* non-scaled */ /* upper bound; if the row has no upper bound, ub is zero */ /* if the row type is GLP_FX, ub is equal to lb */ GLPAIJ *ptr; /* non-scaled */ /* pointer to doubly linked list of constraint coefficients which are placed in this row */ double rii; /* diagonal element r[i,i] of scaling matrix R for this row; if the scaling is not used, r[i,i] is 1 */ int stat; /* status of the auxiliary variable: GLP_BS - basic variable GLP_NL - non-basic variable on lower bound GLP_NU - non-basic variable on upper bound GLP_NF - non-basic free variable GLP_NS - non-basic fixed variable */ int bind; /* if the auxiliary variable is basic, head[bind] refers to this row, otherwise, bind is 0; this attribute is valid only if the basis factorization is valid */ double prim; /* non-scaled */ /* primal value of the auxiliary variable in basic solution */ double dual; /* non-scaled */ /* dual value of the auxiliary variable in basic solution */ double pval; /* non-scaled */ /* primal value of the auxiliary variable in interior solution */ double dval; /* non-scaled */ /* dual value of the auxiliary variable in interior solution */ double mipx; /* non-scaled */ /* primal value of the auxiliary variable in integer solution */ }; struct GLPCOL { /* LP/MIP column (structural variable) */ int j; /* ordinal number (1 to n) assigned to this column */ char *name; /* column name (1 to 255 chars); NULL means no name is assigned to this column */ AVLNODE *node; /* pointer to corresponding node in the column index; NULL means that either the column index does not exist or the column has no name assigned */ int kind; /* kind of the structural variable: GLP_CV - continuous variable GLP_IV - integer or binary variable */ int type; /* type of the structural variable: GLP_FR - free variable GLP_LO - variable with lower bound GLP_UP - variable with upper bound GLP_DB - double-bounded variable GLP_FX - fixed variable */ double lb; /* non-scaled */ /* lower bound; if the column has no lower bound, lb is zero */ double ub; /* non-scaled */ /* upper bound; if the column has no upper bound, ub is zero */ /* if the column type is GLP_FX, ub is equal to lb */ double coef; /* non-scaled */ /* objective coefficient at the structural variable */ GLPAIJ *ptr; /* non-scaled */ /* pointer to doubly linked list of constraint coefficients which are placed in this column */ double sjj; /* diagonal element s[j,j] of scaling matrix S for this column; if the scaling is not used, s[j,j] is 1 */ int stat; /* status of the structural variable: GLP_BS - basic variable GLP_NL - non-basic variable on lower bound GLP_NU - non-basic variable on upper bound GLP_NF - non-basic free variable GLP_NS - non-basic fixed variable */ int bind; /* if the structural variable is basic, head[bind] refers to this column; otherwise, bind is 0; this attribute is valid only if the basis factorization is valid */ double prim; /* non-scaled */ /* primal value of the structural variable in basic solution */ double dual; /* non-scaled */ /* dual value of the structural variable in basic solution */ double pval; /* non-scaled */ /* primal value of the structural variable in interior solution */ double dval; /* non-scaled */ /* dual value of the structural variable in interior solution */ double mipx; /* non-scaled */ /* primal value of the structural variable in integer solution */ }; struct GLPAIJ { /* constraint coefficient a[i,j] */ GLPROW *row; /* pointer to row, where this coefficient is placed */ GLPCOL *col; /* pointer to column, where this coefficient is placed */ double val; /* numeric (non-zero) value of this coefficient */ GLPAIJ *r_prev; /* pointer to previous coefficient in the same row */ GLPAIJ *r_next; /* pointer to next coefficient in the same row */ GLPAIJ *c_prev; /* pointer to previous coefficient in the same column */ GLPAIJ *c_next; /* pointer to next coefficient in the same column */ }; void _glp_check_kkt(glp_prob *P, int sol, int cond, double *ae_max, int *ae_ind, double *re_max, int *re_ind); /* check feasibility and optimality conditions */ #define lpx_put_solution _glp_put_solution void lpx_put_solution(glp_prob *lp, int inval, const int *p_stat, const int *d_stat, const double *obj_val, const int r_stat[], const double r_prim[], const double r_dual[], const int c_stat[], const double c_prim[], const double c_dual[]); /* store basic solution components */ #define lpx_put_mip_soln _glp_put_mip_soln void lpx_put_mip_soln(LPX *lp, int i_stat, double row_mipx[], double col_mipx[]); /* store mixed integer solution components */ #if 1 /* 28/XI-2009 */ int _glp_analyze_row(glp_prob *P, int len, const int ind[], const double val[], int type, double rhs, double eps, int *_piv, double *_x, double *_dx, double *_y, double *_dy, double *_dz); /* simulate one iteration of dual simplex method */ #endif #if 1 /* 08/XII-2009 */ void _glp_mpl_init_rand(glp_tran *tran, int seed); #endif #define glp_skpgen _glp_skpgen void glp_skpgen(int n, int r, int type, int v, int s, int a[], int *b, int c[]); /* Pisinger's 0-1 single knapsack problem generator */ #if 1 /* 28/V-2010 */ int _glp_intopt1(glp_prob *P, const glp_iocp *parm); #endif #endif /* eof */ igraph/src/glpk/glpios.h0000644000176000001440000005356612325527073014773 0ustar ripleyusers/* glpios.h (integer optimization suite) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPIOS_H #define GLPIOS_H #define GLP_TREE_DEFINED typedef struct glp_tree glp_tree; #include "glpapi.h" typedef struct IOSLOT IOSLOT; typedef struct IOSNPD IOSNPD; typedef struct IOSBND IOSBND; typedef struct IOSTAT IOSTAT; typedef struct IOSROW IOSROW; typedef struct IOSAIJ IOSAIJ; typedef struct IOSPOOL IOSPOOL; typedef struct IOSCUT IOSCUT; struct glp_tree { /* branch-and-bound tree */ int magic; /* magic value used for debugging */ DMP *pool; /* memory pool to store all IOS components */ int n; /* number of columns (variables) */ /*--------------------------------------------------------------*/ /* problem components corresponding to the original MIP and its LP relaxation (used to restore the original problem object on exit from the solver) */ int orig_m; /* number of rows */ unsigned char *orig_type; /* uchar orig_type[1+orig_m+n]; */ /* types of all variables */ double *orig_lb; /* double orig_lb[1+orig_m+n]; */ /* lower bounds of all variables */ double *orig_ub; /* double orig_ub[1+orig_m+n]; */ /* upper bounds of all variables */ unsigned char *orig_stat; /* uchar orig_stat[1+orig_m+n]; */ /* statuses of all variables */ double *orig_prim; /* double orig_prim[1+orig_m+n]; */ /* primal values of all variables */ double *orig_dual; /* double orig_dual[1+orig_m+n]; */ /* dual values of all variables */ double orig_obj; /* optimal objective value for LP relaxation */ /*--------------------------------------------------------------*/ /* branch-and-bound tree */ int nslots; /* length of the array of slots (enlarged automatically) */ int avail; /* index of the first free slot; 0 means all slots are in use */ IOSLOT *slot; /* IOSLOT slot[1+nslots]; */ /* array of slots: slot[0] is not used; slot[p], 1 <= p <= nslots, either contains a pointer to some node of the branch-and-bound tree, in which case p is used on API level as the reference number of corresponding subproblem, or is free; all free slots are linked into single linked list; slot[1] always contains a pointer to the root node (it is free only if the tree is empty) */ IOSNPD *head; /* pointer to the head of the active list */ IOSNPD *tail; /* pointer to the tail of the active list */ /* the active list is a doubly linked list of active subproblems which correspond to leaves of the tree; all subproblems in the active list are ordered chronologically (each a new subproblem is always added to the tail of the list) */ int a_cnt; /* current number of active nodes (including the current one) */ int n_cnt; /* current number of all (active and inactive) nodes */ int t_cnt; /* total number of nodes including those which have been already removed from the tree; this count is increased by one whenever a new node is created and never decreased */ /*--------------------------------------------------------------*/ /* problem components corresponding to the root subproblem */ int root_m; /* number of rows */ unsigned char *root_type; /* uchar root_type[1+root_m+n]; */ /* types of all variables */ double *root_lb; /* double root_lb[1+root_m+n]; */ /* lower bounds of all variables */ double *root_ub; /* double root_ub[1+root_m+n]; */ /* upper bounds of all variables */ unsigned char *root_stat; /* uchar root_stat[1+root_m+n]; */ /* statuses of all variables */ /*--------------------------------------------------------------*/ /* current subproblem and its LP relaxation */ IOSNPD *curr; /* pointer to the current subproblem (which can be only active); NULL means the current subproblem does not exist */ glp_prob *mip; /* original problem object passed to the solver; if the current subproblem exists, its LP segment corresponds to LP relaxation of the current subproblem; if the current subproblem does not exist, its LP segment corresponds to LP relaxation of the root subproblem (note that the root subproblem may differ from the original MIP, because it may be preprocessed and/or may have additional rows) */ unsigned char *non_int; /* uchar non_int[1+n]; */ /* these column flags are set each time when LP relaxation of the current subproblem has been solved; non_int[0] is not used; non_int[j], 1 <= j <= n, is j-th column flag; if this flag is set, corresponding variable is required to be integer, but its value in basic solution is fractional */ /*--------------------------------------------------------------*/ /* problem components corresponding to the parent (predecessor) subproblem for the current subproblem; used to inspect changes on freezing the current subproblem */ int pred_m; /* number of rows */ int pred_max; /* length of the following four arrays (enlarged automatically), pred_max >= pred_m + n */ unsigned char *pred_type; /* uchar pred_type[1+pred_m+n]; */ /* types of all variables */ double *pred_lb; /* double pred_lb[1+pred_m+n]; */ /* lower bounds of all variables */ double *pred_ub; /* double pred_ub[1+pred_m+n]; */ /* upper bounds of all variables */ unsigned char *pred_stat; /* uchar pred_stat[1+pred_m+n]; */ /* statuses of all variables */ /****************************************************************/ /* built-in cut generators segment */ IOSPOOL *local; /* local cut pool */ void *mir_gen; /* pointer to working area used by the MIR cut generator */ void *clq_gen; /* pointer to working area used by the clique cut generator */ /*--------------------------------------------------------------*/ void *pcost; /* pointer to working area used on pseudocost branching */ int *iwrk; /* int iwrk[1+n]; */ /* working array */ double *dwrk; /* double dwrk[1+n]; */ /* working array */ /*--------------------------------------------------------------*/ /* control parameters and statistics */ const glp_iocp *parm; /* copy of control parameters passed to the solver */ glp_long tm_beg; /* starting time of the search, in seconds; the total time of the search is the difference between xtime() and tm_beg */ glp_long tm_lag; /* the most recent time, in seconds, at which the progress of the the search was displayed */ int sol_cnt; /* number of integer feasible solutions found */ /*--------------------------------------------------------------*/ /* advanced solver interface */ int reason; /* flag indicating the reason why the callback routine is being called (see glpk.h) */ int stop; /* flag indicating that the callback routine requires premature termination of the search */ int next_p; /* reference number of active subproblem selected to continue the search; 0 means no subproblem has been selected */ int reopt; /* flag indicating that the current LP relaxation needs to be re-optimized */ int reinv; /* flag indicating that some (non-active) rows were removed from the current LP relaxation, so if there no new rows appear, the basis must be re-factorized */ int br_var; /* the number of variable chosen to branch on */ int br_sel; /* flag indicating which branch (subproblem) is suggested to be selected to continue the search: GLP_DN_BRNCH - select down-branch GLP_UP_BRNCH - select up-branch GLP_NO_BRNCH - use general selection technique */ int child; /* subproblem reference number corresponding to br_sel */ }; struct IOSLOT { /* node subproblem slot */ IOSNPD *node; /* pointer to subproblem descriptor; NULL means free slot */ int next; /* index of another free slot (only if this slot is free) */ }; struct IOSNPD { /* node subproblem descriptor */ int p; /* subproblem reference number (it is the index to corresponding slot, i.e. slot[p] points to this descriptor) */ IOSNPD *up; /* pointer to the parent subproblem; NULL means this node is the root of the tree, in which case p = 1 */ int level; /* node level (the root node has level 0) */ int count; /* if count = 0, this subproblem is active; if count > 0, this subproblem is inactive, in which case count is the number of its child subproblems */ /* the following three linked lists are destroyed on reviving and built anew on freezing the subproblem: */ IOSBND *b_ptr; /* linked list of rows and columns of the parent subproblem whose types and bounds were changed */ IOSTAT *s_ptr; /* linked list of rows and columns of the parent subproblem whose statuses were changed */ IOSROW *r_ptr; /* linked list of rows (cuts) added to the parent subproblem */ int solved; /* how many times LP relaxation of this subproblem was solved; for inactive subproblem this count is always non-zero; for active subproblem, which is not current, this count may be non-zero, if the subproblem was temporarily suspended */ double lp_obj; /* optimal objective value to LP relaxation of this subproblem; on creating a subproblem this value is inherited from its parent; for the root subproblem, which has no parent, this value is initially set to -DBL_MAX (minimization) or +DBL_MAX (maximization); each time the subproblem is re-optimized, this value is appropriately changed */ double bound; /* local lower (minimization) or upper (maximization) bound for integer optimal solution to *this* subproblem; this bound is local in the sense that only subproblems in the subtree rooted at this node cannot have better integer feasible solutions; on creating a subproblem its local bound is inherited from its parent and then can be made stronger (never weaker); for the root subproblem its local bound is initially set to -DBL_MAX (minimization) or +DBL_MAX (maximization) and then improved as the root LP relaxation has been solved */ /* the following two quantities are defined only if LP relaxation of this subproblem was solved at least once (solved > 0): */ int ii_cnt; /* number of integer variables whose value in optimal solution to LP relaxation of this subproblem is fractional */ double ii_sum; /* sum of integer infeasibilities */ #if 1 /* 30/XI-2009 */ int changed; /* how many times this subproblem was re-formulated (by adding cutting plane constraints) */ #endif int br_var; /* ordinal number of branching variable, 1 <= br_var <= n, used to split this subproblem; 0 means that either this subproblem is active or branching was made on a constraint */ double br_val; /* (fractional) value of branching variable in optimal solution to final LP relaxation of this subproblem */ void *data; /* char data[tree->cb_size]; */ /* pointer to the application-specific data */ IOSNPD *temp; /* working pointer used by some routines */ IOSNPD *prev; /* pointer to previous subproblem in the active list */ IOSNPD *next; /* pointer to next subproblem in the active list */ }; struct IOSBND { /* bounds change entry */ int k; /* ordinal number of corresponding row (1 <= k <= m) or column (m+1 <= k <= m+n), where m and n are the number of rows and columns, resp., in the parent subproblem */ unsigned char type; /* new type */ double lb; /* new lower bound */ double ub; /* new upper bound */ IOSBND *next; /* pointer to next entry for the same subproblem */ }; struct IOSTAT { /* status change entry */ int k; /* ordinal number of corresponding row (1 <= k <= m) or column (m+1 <= k <= m+n), where m and n are the number of rows and columns, resp., in the parent subproblem */ unsigned char stat; /* new status */ IOSTAT *next; /* pointer to next entry for the same subproblem */ }; struct IOSROW { /* row (constraint) addition entry */ char *name; /* row name or NULL */ unsigned char origin; /* row origin flag (see glp_attr.origin) */ unsigned char klass; /* row class descriptor (see glp_attr.klass) */ unsigned char type; /* row type (GLP_LO, GLP_UP, etc.) */ double lb; /* row lower bound */ double ub; /* row upper bound */ IOSAIJ *ptr; /* pointer to the row coefficient list */ double rii; /* row scale factor */ unsigned char stat; /* row status (GLP_BS, GLP_NL, etc.) */ IOSROW *next; /* pointer to next entry for the same subproblem */ }; struct IOSAIJ { /* constraint coefficient */ int j; /* variable (column) number, 1 <= j <= n */ double val; /* non-zero coefficient value */ IOSAIJ *next; /* pointer to next coefficient for the same row */ }; struct IOSPOOL { /* cut pool */ int size; /* pool size = number of cuts in the pool */ IOSCUT *head; /* pointer to the first cut */ IOSCUT *tail; /* pointer to the last cut */ int ord; /* ordinal number of the current cut, 1 <= ord <= size */ IOSCUT *curr; /* pointer to the current cut */ }; struct IOSCUT { /* cut (cutting plane constraint) */ char *name; /* cut name or NULL */ unsigned char klass; /* cut class descriptor (see glp_attr.klass) */ IOSAIJ *ptr; /* pointer to the cut coefficient list */ unsigned char type; /* cut type: GLP_LO: sum a[j] * x[j] >= b GLP_UP: sum a[j] * x[j] <= b GLP_FX: sum a[j] * x[j] = b */ double rhs; /* cut right-hand side */ IOSCUT *prev; /* pointer to previous cut */ IOSCUT *next; /* pointer to next cut */ }; #define ios_create_tree _glp_ios_create_tree glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm); /* create branch-and-bound tree */ #define ios_revive_node _glp_ios_revive_node void ios_revive_node(glp_tree *tree, int p); /* revive specified subproblem */ #define ios_freeze_node _glp_ios_freeze_node void ios_freeze_node(glp_tree *tree); /* freeze current subproblem */ #define ios_clone_node _glp_ios_clone_node void ios_clone_node(glp_tree *tree, int p, int nnn, int ref[]); /* clone specified subproblem */ #define ios_delete_node _glp_ios_delete_node void ios_delete_node(glp_tree *tree, int p); /* delete specified subproblem */ #define ios_delete_tree _glp_ios_delete_tree void ios_delete_tree(glp_tree *tree); /* delete branch-and-bound tree */ #define ios_eval_degrad _glp_ios_eval_degrad void ios_eval_degrad(glp_tree *tree, int j, double *dn, double *up); /* estimate obj. degrad. for down- and up-branches */ #define ios_round_bound _glp_ios_round_bound double ios_round_bound(glp_tree *tree, double bound); /* improve local bound by rounding */ #define ios_is_hopeful _glp_ios_is_hopeful int ios_is_hopeful(glp_tree *tree, double bound); /* check if subproblem is hopeful */ #define ios_best_node _glp_ios_best_node int ios_best_node(glp_tree *tree); /* find active node with best local bound */ #define ios_relative_gap _glp_ios_relative_gap double ios_relative_gap(glp_tree *tree); /* compute relative mip gap */ #define ios_solve_node _glp_ios_solve_node int ios_solve_node(glp_tree *tree); /* solve LP relaxation of current subproblem */ #define ios_create_pool _glp_ios_create_pool IOSPOOL *ios_create_pool(glp_tree *tree); /* create cut pool */ #define ios_add_row _glp_ios_add_row int ios_add_row(glp_tree *tree, IOSPOOL *pool, const char *name, int klass, int flags, int len, const int ind[], const double val[], int type, double rhs); /* add row (constraint) to the cut pool */ #define ios_find_row _glp_ios_find_row IOSCUT *ios_find_row(IOSPOOL *pool, int i); /* find row (constraint) in the cut pool */ #define ios_del_row _glp_ios_del_row void ios_del_row(glp_tree *tree, IOSPOOL *pool, int i); /* remove row (constraint) from the cut pool */ #define ios_clear_pool _glp_ios_clear_pool void ios_clear_pool(glp_tree *tree, IOSPOOL *pool); /* remove all rows (constraints) from the cut pool */ #define ios_delete_pool _glp_ios_delete_pool void ios_delete_pool(glp_tree *tree, IOSPOOL *pool); /* delete cut pool */ #define ios_preprocess_node _glp_ios_preprocess_node int ios_preprocess_node(glp_tree *tree, int max_pass); /* preprocess current subproblem */ #define ios_driver _glp_ios_driver int ios_driver(glp_tree *tree); /* branch-and-bound driver */ /**********************************************************************/ typedef struct IOSVEC IOSVEC; struct IOSVEC { /* sparse vector v = (v[j]) */ int n; /* dimension, n >= 0 */ int nnz; /* number of non-zero components, 0 <= nnz <= n */ int *pos; /* int pos[1+n]; */ /* pos[j] = k, 1 <= j <= n, is position of (non-zero) v[j] in the arrays ind and val, where 1 <= k <= nnz; pos[j] = 0 means that v[j] is structural zero */ int *ind; /* int ind[1+n]; */ /* ind[k] = j, 1 <= k <= nnz, is index of v[j] */ double *val; /* double val[1+n]; */ /* val[k], 1 <= k <= nnz, is a numeric value of v[j] */ }; #define ios_create_vec _glp_ios_create_vec IOSVEC *ios_create_vec(int n); /* create sparse vector */ #define ios_check_vec _glp_ios_check_vec void ios_check_vec(IOSVEC *v); /* check that sparse vector has correct representation */ #define ios_get_vj _glp_ios_get_vj double ios_get_vj(IOSVEC *v, int j); /* retrieve component of sparse vector */ #define ios_set_vj _glp_ios_set_vj void ios_set_vj(IOSVEC *v, int j, double val); /* set/change component of sparse vector */ #define ios_clear_vec _glp_ios_clear_vec void ios_clear_vec(IOSVEC *v); /* set all components of sparse vector to zero */ #define ios_clean_vec _glp_ios_clean_vec void ios_clean_vec(IOSVEC *v, double eps); /* remove zero or small components from sparse vector */ #define ios_copy_vec _glp_ios_copy_vec void ios_copy_vec(IOSVEC *x, IOSVEC *y); /* copy sparse vector (x := y) */ #define ios_linear_comb _glp_ios_linear_comb void ios_linear_comb(IOSVEC *x, double a, IOSVEC *y); /* compute linear combination (x := x + a * y) */ #define ios_delete_vec _glp_ios_delete_vec void ios_delete_vec(IOSVEC *v); /* delete sparse vector */ /**********************************************************************/ #define ios_gmi_gen _glp_ios_gmi_gen void ios_gmi_gen(glp_tree *tree); /* generate Gomory's mixed integer cuts */ #define ios_mir_init _glp_ios_mir_init void *ios_mir_init(glp_tree *tree); /* initialize MIR cut generator */ #define ios_mir_gen _glp_ios_mir_gen void ios_mir_gen(glp_tree *tree, void *gen); /* generate MIR cuts */ #define ios_mir_term _glp_ios_mir_term void ios_mir_term(void *gen); /* terminate MIR cut generator */ #define ios_cov_gen _glp_ios_cov_gen void ios_cov_gen(glp_tree *tree); /* generate mixed cover cuts */ #define ios_clq_init _glp_ios_clq_init void *ios_clq_init(glp_tree *tree); /* initialize clique cut generator */ #define ios_clq_gen _glp_ios_clq_gen void ios_clq_gen(glp_tree *tree, void *gen); /* generate clique cuts */ #define ios_clq_term _glp_ios_clq_term void ios_clq_term(void *gen); /* terminate clique cut generator */ #define ios_pcost_init _glp_ios_pcost_init void *ios_pcost_init(glp_tree *tree); /* initialize working data used on pseudocost branching */ #define ios_pcost_branch _glp_ios_pcost_branch int ios_pcost_branch(glp_tree *T, int *next); /* choose branching variable with pseudocost branching */ #define ios_pcost_update _glp_ios_pcost_update void ios_pcost_update(glp_tree *tree); /* update history information for pseudocost branching */ #define ios_pcost_free _glp_ios_pcost_free void ios_pcost_free(glp_tree *tree); /* free working area used on pseudocost branching */ #define ios_feas_pump _glp_ios_feas_pump void ios_feas_pump(glp_tree *T); /* feasibility pump heuristic */ #define ios_process_cuts _glp_ios_process_cuts void ios_process_cuts(glp_tree *T); /* process cuts stored in the local cut pool */ #define ios_choose_node _glp_ios_choose_node int ios_choose_node(glp_tree *T); /* select subproblem to continue the search */ #define ios_choose_var _glp_ios_choose_var int ios_choose_var(glp_tree *T, int *next); /* select variable to branch on */ #endif /* eof */ igraph/src/glpk/glpgmp.h0000644000176000001440000001431512325527073014751 0ustar ripleyusers/* glpgmp.h (bignum arithmetic) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPGMP_H #define GLPGMP_H #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_GMP /* use GNU MP bignum library */ #include #define gmp_pool_count _glp_gmp_pool_count #define gmp_free_mem _glp_gmp_free_mem int gmp_pool_count(void); void gmp_free_mem(void); #else /* use GLPK bignum module */ /*---------------------------------------------------------------------- // INTEGER NUMBERS // // Depending on its magnitude an integer number of arbitrary precision // is represented either in short format or in long format. // // Short format corresponds to the int type and allows representing // integer numbers in the range [-(2^31-1), +(2^31-1)]. Note that for // the most negative number of int type the short format is not used. // // In long format integer numbers are represented using the positional // system with the base (radix) 2^16 = 65536: // // x = (-1)^s sum{j in 0..n-1} d[j] * 65536^j, // // where x is the integer to be represented, s is its sign (+1 or -1), // d[j] are its digits (0 <= d[j] <= 65535). // // RATIONAL NUMBERS // // A rational number is represented as an irreducible fraction: // // p / q, // // where p (numerator) and q (denominator) are integer numbers (q > 0) // having no common divisors. */ struct mpz { /* integer number */ int val; /* if ptr is a null pointer, the number is in short format, and val is its value; otherwise, the number is in long format, and val is its sign (+1 or -1) */ struct mpz_seg *ptr; /* pointer to the linked list of the number segments ordered in ascending of powers of the base */ }; struct mpz_seg { /* integer number segment */ unsigned short d[6]; /* six digits of the number ordered in ascending of powers of the base */ struct mpz_seg *next; /* pointer to the next number segment */ }; struct mpq { /* rational number (p / q) */ struct mpz p; /* numerator */ struct mpz q; /* denominator */ }; typedef struct mpz *mpz_t; typedef struct mpq *mpq_t; #define gmp_get_atom _glp_gmp_get_atom #define gmp_free_atom _glp_gmp_free_atom #define gmp_pool_count _glp_gmp_pool_count #define gmp_get_work _glp_gmp_get_work #define gmp_free_mem _glp_gmp_free_mem #define _mpz_init _glp_mpz_init #define mpz_clear _glp_mpz_clear #define mpz_set _glp_mpz_set #define mpz_set_si _glp_mpz_set_si #define mpz_get_d _glp_mpz_get_d #define mpz_get_d_2exp _glp_mpz_get_d_2exp #define mpz_swap _glp_mpz_swap #define mpz_add _glp_mpz_add #define mpz_sub _glp_mpz_sub #define mpz_mul _glp_mpz_mul #define mpz_neg _glp_mpz_neg #define mpz_abs _glp_mpz_abs #define mpz_div _glp_mpz_div #define mpz_gcd _glp_mpz_gcd #define mpz_cmp _glp_mpz_cmp #define mpz_sgn _glp_mpz_sgn #define mpz_out_str _glp_mpz_out_str #define _mpq_init _glp_mpq_init #define mpq_clear _glp_mpq_clear #define mpq_canonicalize _glp_mpq_canonicalize #define mpq_set _glp_mpq_set #define mpq_set_si _glp_mpq_set_si #define mpq_get_d _glp_mpq_get_d #define mpq_set_d _glp_mpq_set_d #define mpq_add _glp_mpq_add #define mpq_sub _glp_mpq_sub #define mpq_mul _glp_mpq_mul #define mpq_div _glp_mpq_div #define mpq_neg _glp_mpq_neg #define mpq_abs _glp_mpq_abs #define mpq_cmp _glp_mpq_cmp #define mpq_sgn _glp_mpq_sgn #define mpq_out_str _glp_mpq_out_str void *gmp_get_atom(int size); void gmp_free_atom(void *ptr, int size); int gmp_pool_count(void); unsigned short *gmp_get_work(int size); void gmp_free_mem(void); mpz_t _mpz_init(void); #define mpz_init(x) (void)((x) = _mpz_init()) void mpz_clear(mpz_t x); void mpz_set(mpz_t z, mpz_t x); void mpz_set_si(mpz_t x, int val); double mpz_get_d(mpz_t x); double mpz_get_d_2exp(int *exp, mpz_t x); void mpz_swap(mpz_t x, mpz_t y); void mpz_add(mpz_t, mpz_t, mpz_t); void mpz_sub(mpz_t, mpz_t, mpz_t); void mpz_mul(mpz_t, mpz_t, mpz_t); void mpz_neg(mpz_t z, mpz_t x); void mpz_abs(mpz_t z, mpz_t x); void mpz_div(mpz_t q, mpz_t r, mpz_t x, mpz_t y); void mpz_gcd(mpz_t z, mpz_t x, mpz_t y); int mpz_cmp(mpz_t x, mpz_t y); int mpz_sgn(mpz_t x); int mpz_out_str(void *fp, int base, mpz_t x); mpq_t _mpq_init(void); #define mpq_init(x) (void)((x) = _mpq_init()) void mpq_clear(mpq_t x); void mpq_canonicalize(mpq_t x); void mpq_set(mpq_t z, mpq_t x); void mpq_set_si(mpq_t x, int p, unsigned int q); double mpq_get_d(mpq_t x); void mpq_set_d(mpq_t x, double val); void mpq_add(mpq_t z, mpq_t x, mpq_t y); void mpq_sub(mpq_t z, mpq_t x, mpq_t y); void mpq_mul(mpq_t z, mpq_t x, mpq_t y); void mpq_div(mpq_t z, mpq_t x, mpq_t y); void mpq_neg(mpq_t z, mpq_t x); void mpq_abs(mpq_t z, mpq_t x); int mpq_cmp(mpq_t x, mpq_t y); int mpq_sgn(mpq_t x); int mpq_out_str(void *fp, int base, mpq_t x); #endif #endif /* eof */ igraph/src/glpk/colamd/0000755000176000001440000000000012325372074014544 5ustar ripleyusersigraph/src/glpk/colamd/colamd.h0000644000176000001440000000411412325527073016155 0ustar ripleyusers/* colamd.h */ /* Written by Andrew Makhorin . */ #ifndef COLAMD_H #define COLAMD_H #define _GLPSTD_STDIO #include "glpenv.h" #define COLAMD_DATE "Nov 1, 2007" #define COLAMD_VERSION_CODE(main, sub) ((main) * 1000 + (sub)) #define COLAMD_MAIN_VERSION 2 #define COLAMD_SUB_VERSION 7 #define COLAMD_SUBSUB_VERSION 1 #define COLAMD_VERSION \ COLAMD_VERSION_CODE(COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION) #define COLAMD_KNOBS 20 #define COLAMD_STATS 20 #define COLAMD_DENSE_ROW 0 #define COLAMD_DENSE_COL 1 #define COLAMD_AGGRESSIVE 2 #define COLAMD_DEFRAG_COUNT 2 #define COLAMD_STATUS 3 #define COLAMD_INFO1 4 #define COLAMD_INFO2 5 #define COLAMD_INFO3 6 #define COLAMD_OK (0) #define COLAMD_OK_BUT_JUMBLED (1) #define COLAMD_ERROR_A_not_present (-1) #define COLAMD_ERROR_p_not_present (-2) #define COLAMD_ERROR_nrow_negative (-3) #define COLAMD_ERROR_ncol_negative (-4) #define COLAMD_ERROR_nnz_negative (-5) #define COLAMD_ERROR_p0_nonzero (-6) #define COLAMD_ERROR_A_too_small (-7) #define COLAMD_ERROR_col_length_negative (-8) #define COLAMD_ERROR_row_index_out_of_bounds (-9) #define COLAMD_ERROR_out_of_memory (-10) #define COLAMD_ERROR_internal_error (-999) #define colamd_recommended _glp_colamd_recommended size_t colamd_recommended(int nnz, int n_row, int n_col); #define colamd_set_defaults _glp_colamd_set_defaults void colamd_set_defaults(double knobs [COLAMD_KNOBS]); #define colamd _glp_colamd int colamd(int n_row, int n_col, int Alen, int A[], int p[], double knobs[COLAMD_KNOBS], int stats[COLAMD_STATS]); #define symamd _glp_symamd int symamd(int n, int A[], int p[], int perm[], double knobs[COLAMD_KNOBS], int stats[COLAMD_STATS], void *(*allocate)(size_t, size_t), void(*release)(void *)); #define colamd_report _glp_colamd_report void colamd_report(int stats[COLAMD_STATS]); #define symamd_report _glp_symamd_report void symamd_report(int stats[COLAMD_STATS]); #define colamd_printf xprintf #endif /* eof */ igraph/src/glpk/glphbm.h0000644000176000001440000001102012325527073014722 0ustar ripleyusers/* glphbm.h (Harwell-Boeing sparse matrix format) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPHBM_H #define GLPHBM_H typedef struct HBM HBM; struct HBM { /* sparse matrix in Harwell-Boeing format; for details see the report: I.S.Duff, R.G.Grimes, J.G.Lewis. User's Guide for the Harwell-Boeing Sparse Matrix Collection (Release I), 1992 */ char title[72+1]; /* matrix title (informative) */ char key[8+1]; /* matrix key (informative) */ char mxtype[3+1]; /* matrix type: R.. real matrix C.. complex matrix P.. pattern only (no numerical values supplied) .S. symmetric (lower triangle + main diagonal) .U. unsymmetric .H. hermitian (lower triangle + main diagonal) .Z. skew symmetric (lower triangle only) .R. rectangular ..A assembled ..E elemental (unassembled) */ char rhstyp[3+1]; /* optional types: F.. right-hand sides in dense format M.. right-hand sides in same format as matrix .G. starting vector(s) (guess) is supplied ..X exact solution vector(s) is supplied */ char ptrfmt[16+1]; /* format for pointers */ char indfmt[16+1]; /* format for row (or variable) indices */ char valfmt[20+1]; /* format for numerical values of coefficient matrix */ char rhsfmt[20+1]; /* format for numerical values of right-hand sides */ int totcrd; /* total number of cards excluding header */ int ptrcrd; /* number of cards for ponters */ int indcrd; /* number of cards for row (or variable) indices */ int valcrd; /* number of cards for numerical values */ int rhscrd; /* number of lines for right-hand sides; including starting guesses and solution vectors if present; zero indicates no right-hand side data is present */ int nrow; /* number of rows (or variables) */ int ncol; /* number of columns (or elements) */ int nnzero; /* number of row (or variable) indices; equal to number of entries for assembled matrix */ int neltvl; /* number of elemental matrix entries; zero in case of assembled matrix */ int nrhs; /* number of right-hand sides */ int nrhsix; /* number of row indices; ignored in case of unassembled matrix */ int nrhsvl; /* total number of entries in all right-hand sides */ int nguess; /* total number of entries in all starting guesses */ int nexact; /* total number of entries in all solution vectors */ int *colptr; /* alias: eltptr */ /* column pointers (in case of assembled matrix); elemental matrix pointers (in case of unassembled matrix) */ int *rowind; /* alias: varind */ /* row indices (in case of assembled matrix); variable indices (in case of unassembled matrix) */ int *rhsptr; /* right-hand side pointers */ int *rhsind; /* right-hand side indices */ double *values; /* matrix values */ double *rhsval; /* right-hand side values */ double *sguess; /* starting guess values */ double *xexact; /* solution vector values */ }; #define hbm_read_mat _glp_hbm_read_mat HBM *hbm_read_mat(const char *fname); /* read sparse matrix in Harwell-Boeing format */ #define hbm_free_mat _glp_hbm_free_mat void hbm_free_mat(HBM *hbm); /* free sparse matrix in Harwell-Boeing format */ #endif /* eof */ igraph/src/glpk/glpfhv.h0000644000176000001440000001522312325527073014750 0ustar ripleyusers/* glpfhv.h (LP basis factorization, FHV eta file version) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPFHV_H #define GLPFHV_H #include "glpluf.h" /*********************************************************************** * The structure FHV defines the factorization of the basis mxm-matrix * B, where m is the number of rows in corresponding problem instance. * * This factorization is the following sextet: * * [B] = (F, H, V, P0, P, Q), (1) * * where F, H, and V are such matrices that * * B = F * H * V, (2) * * and P0, P, and Q are such permutation matrices that the matrix * * L = P0 * F * inv(P0) (3) * * is lower triangular with unity diagonal, and the matrix * * U = P * V * Q (4) * * is upper triangular. All the matrices have the same order m, which * is the order of the basis matrix B. * * The matrices F, V, P, and Q are stored in the structure LUF (see the * module GLPLUF), which is a member of the structure FHV. * * The matrix H is stored in the form of eta file using row-like format * as follows: * * H = H[1] * H[2] * ... * H[nfs], (5) * * where H[k], k = 1, 2, ..., nfs, is a row-like factor, which differs * from the unity matrix only by one row, nfs is current number of row- * like factors. After the factorization has been built for some given * basis matrix B the matrix H has no factors and thus it is the unity * matrix. Then each time when the factorization is recomputed for an * adjacent basis matrix, the next factor H[k], k = 1, 2, ... is built * and added to the end of the eta file H. * * Being sparse vectors non-trivial rows of the factors H[k] are stored * in the right part of the sparse vector area (SVA) in the same manner * as rows and columns of the matrix F. * * For more details see the program documentation. */ typedef struct FHV FHV; struct FHV { /* LP basis factorization */ int m_max; /* maximal value of m (increased automatically, if necessary) */ int m; /* the order of matrices B, F, H, V, P0, P, Q */ int valid; /* the factorization is valid only if this flag is set */ LUF *luf; /* LU-factorization (contains the matrices F, V, P, Q) */ /*--------------------------------------------------------------*/ /* matrix H in the form of eta file */ int hh_max; /* maximal number of row-like factors (which limits the number of updates of the factorization) */ int hh_nfs; /* current number of row-like factors (0 <= hh_nfs <= hh_max) */ int *hh_ind; /* int hh_ind[1+hh_max]; */ /* hh_ind[k], k = 1, ..., nfs, is the number of a non-trivial row of factor H[k] */ int *hh_ptr; /* int hh_ptr[1+hh_max]; */ /* hh_ptr[k], k = 1, ..., nfs, is a pointer to the first element of the non-trivial row of factor H[k] in the SVA */ int *hh_len; /* int hh_len[1+hh_max]; */ /* hh_len[k], k = 1, ..., nfs, is the number of non-zero elements in the non-trivial row of factor H[k] */ /*--------------------------------------------------------------*/ /* matrix P0 */ int *p0_row; /* int p0_row[1+m_max]; */ /* p0_row[i] = j means that p0[i,j] = 1 */ int *p0_col; /* int p0_col[1+m_max]; */ /* p0_col[j] = i means that p0[i,j] = 1 */ /* if i-th row or column of the matrix F corresponds to i'-th row or column of the matrix L = P0*F*inv(P0), then p0_row[i'] = i and p0_col[i] = i' */ /*--------------------------------------------------------------*/ /* working arrays */ int *cc_ind; /* int cc_ind[1+m_max]; */ /* integer working array */ double *cc_val; /* double cc_val[1+m_max]; */ /* floating-point working array */ /*--------------------------------------------------------------*/ /* control parameters */ double upd_tol; /* update tolerance; if after updating the factorization absolute value of some diagonal element u[k,k] of matrix U = P*V*Q is less than upd_tol * max(|u[k,*]|, |u[*,k]|), the factorization is considered as inaccurate */ /*--------------------------------------------------------------*/ /* some statistics */ int nnz_h; /* current number of non-zeros in all factors of matrix H */ }; /* return codes: */ #define FHV_ESING 1 /* singular matrix */ #define FHV_ECOND 2 /* ill-conditioned matrix */ #define FHV_ECHECK 3 /* insufficient accuracy */ #define FHV_ELIMIT 4 /* update limit reached */ #define FHV_EROOM 5 /* SVA overflow */ #define fhv_create_it _glp_fhv_create_it FHV *fhv_create_it(void); /* create LP basis factorization */ #define fhv_factorize _glp_fhv_factorize int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j, int ind[], double val[]), void *info); /* compute LP basis factorization */ #define fhv_h_solve _glp_fhv_h_solve void fhv_h_solve(FHV *fhv, int tr, double x[]); /* solve system H*x = b or H'*x = b */ #define fhv_ftran _glp_fhv_ftran void fhv_ftran(FHV *fhv, double x[]); /* perform forward transformation (solve system B*x = b) */ #define fhv_btran _glp_fhv_btran void fhv_btran(FHV *fhv, double x[]); /* perform backward transformation (solve system B'*x = b) */ #define fhv_update_it _glp_fhv_update_it int fhv_update_it(FHV *fhv, int j, int len, const int ind[], const double val[]); /* update LP basis factorization */ #define fhv_delete_it _glp_fhv_delete_it void fhv_delete_it(FHV *fhv); /* delete LP basis factorization */ #endif /* eof */ igraph/src/glpk/glplpf.h0000644000176000001440000001712212325527073014746 0ustar ripleyusers/* glplpf.h (LP basis factorization, Schur complement version) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPLPF_H #define GLPLPF_H #include "glpscf.h" #include "glpluf.h" /*********************************************************************** * The structure LPF defines the factorization of the basis mxm matrix * B, where m is the number of rows in corresponding problem instance. * * This factorization is the following septet: * * [B] = (L0, U0, R, S, C, P, Q), (1) * * and is based on the following main equality: * * ( B F^) ( B0 F ) ( L0 0 ) ( U0 R ) * ( ) = P ( ) Q = P ( ) ( ) Q, (2) * ( G^ H^) ( G H ) ( S I ) ( 0 C ) * * where: * * B is the current basis matrix (not stored); * * F^, G^, H^ are some additional matrices (not stored); * * B0 is some initial basis matrix (not stored); * * F, G, H are some additional matrices (not stored); * * P, Q are permutation matrices (stored in both row- and column-like * formats); * * L0, U0 are some matrices that defines a factorization of the initial * basis matrix B0 = L0 * U0 (stored in an invertable form); * * R is a matrix defined from L0 * R = F, so R = inv(L0) * F (stored in * a column-wise sparse format); * * S is a matrix defined from S * U0 = G, so S = G * inv(U0) (stored in * a row-wise sparse format); * * C is the Schur complement for matrix (B0 F G H). It is defined from * S * R + C = H, so C = H - S * R = H - G * inv(U0) * inv(L0) * F = * = H - G * inv(B0) * F. Matrix C is stored in an invertable form. * * REFERENCES * * 1. M.A.Saunders, "LUSOL: A basis package for constrained optimiza- * tion," SCCM, Stanford University, 2006. * * 2. M.A.Saunders, "Notes 5: Basis Updates," CME 318, Stanford Univer- * sity, Spring 2006. * * 3. M.A.Saunders, "Notes 6: LUSOL---a Basis Factorization Package," * ibid. */ typedef struct LPF LPF; struct LPF { /* LP basis factorization */ int valid; /* the factorization is valid only if this flag is set */ /*--------------------------------------------------------------*/ /* initial basis matrix B0 */ int m0_max; /* maximal value of m0 (increased automatically, if necessary) */ int m0; /* the order of B0 */ LUF *luf; /* LU-factorization of B0 */ /*--------------------------------------------------------------*/ /* current basis matrix B */ int m; /* the order of B */ double *B; /* double B[1+m*m]; */ /* B in dense format stored by rows and used only for debugging; normally this array is not allocated */ /*--------------------------------------------------------------*/ /* augmented matrix (B0 F G H) of the order m0+n */ int n_max; /* maximal number of additional rows and columns */ int n; /* current number of additional rows and columns */ /*--------------------------------------------------------------*/ /* m0xn matrix R in column-wise format */ int *R_ptr; /* int R_ptr[1+n_max]; */ /* R_ptr[j], 1 <= j <= n, is a pointer to j-th column */ int *R_len; /* int R_len[1+n_max]; */ /* R_len[j], 1 <= j <= n, is the length of j-th column */ /*--------------------------------------------------------------*/ /* nxm0 matrix S in row-wise format */ int *S_ptr; /* int S_ptr[1+n_max]; */ /* S_ptr[i], 1 <= i <= n, is a pointer to i-th row */ int *S_len; /* int S_len[1+n_max]; */ /* S_len[i], 1 <= i <= n, is the length of i-th row */ /*--------------------------------------------------------------*/ /* Schur complement C of the order n */ SCF *scf; /* SCF scf[1:n_max]; */ /* factorization of the Schur complement */ /*--------------------------------------------------------------*/ /* matrix P of the order m0+n */ int *P_row; /* int P_row[1+m0_max+n_max]; */ /* P_row[i] = j means that P[i,j] = 1 */ int *P_col; /* int P_col[1+m0_max+n_max]; */ /* P_col[j] = i means that P[i,j] = 1 */ /*--------------------------------------------------------------*/ /* matrix Q of the order m0+n */ int *Q_row; /* int Q_row[1+m0_max+n_max]; */ /* Q_row[i] = j means that Q[i,j] = 1 */ int *Q_col; /* int Q_col[1+m0_max+n_max]; */ /* Q_col[j] = i means that Q[i,j] = 1 */ /*--------------------------------------------------------------*/ /* Sparse Vector Area (SVA) is a set of locations intended to store sparse vectors which represent columns of matrix R and rows of matrix S; each location is a doublet (ind, val), where ind is an index, val is a numerical value of a sparse vector element; in the whole each sparse vector is a set of adjacent locations defined by a pointer to its first element and its length, i.e. the number of its elements */ int v_size; /* the SVA size, in locations; locations are numbered by integers 1, 2, ..., v_size, and location 0 is not used */ int v_ptr; /* pointer to the first available location */ int *v_ind; /* int v_ind[1+v_size]; */ /* v_ind[k], 1 <= k <= v_size, is the index field of location k */ double *v_val; /* double v_val[1+v_size]; */ /* v_val[k], 1 <= k <= v_size, is the value field of location k */ /*--------------------------------------------------------------*/ double *work1; /* double work1[1+m0+n_max]; */ /* working array */ double *work2; /* double work2[1+m0+n_max]; */ /* working array */ }; /* return codes: */ #define LPF_ESING 1 /* singular matrix */ #define LPF_ECOND 2 /* ill-conditioned matrix */ #define LPF_ELIMIT 3 /* update limit reached */ #define lpf_create_it _glp_lpf_create_it LPF *lpf_create_it(void); /* create LP basis factorization */ #define lpf_factorize _glp_lpf_factorize int lpf_factorize(LPF *lpf, int m, const int bh[], int (*col) (void *info, int j, int ind[], double val[]), void *info); /* compute LP basis factorization */ #define lpf_ftran _glp_lpf_ftran void lpf_ftran(LPF *lpf, double x[]); /* perform forward transformation (solve system B*x = b) */ #define lpf_btran _glp_lpf_btran void lpf_btran(LPF *lpf, double x[]); /* perform backward transformation (solve system B'*x = b) */ #define lpf_update_it _glp_lpf_update_it int lpf_update_it(LPF *lpf, int j, int bh, int len, const int ind[], const double val[]); /* update LP basis factorization */ #define lpf_delete_it _glp_lpf_delete_it void lpf_delete_it(LPF *lpf); /* delete LP basis factorization */ #endif /* eof */ igraph/src/glpk/glpmat.h0000644000176000001440000001646012325527073014752 0ustar ripleyusers/* glpmat.h (linear algebra routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPMAT_H #define GLPMAT_H /*********************************************************************** * FULL-VECTOR STORAGE * * For a sparse vector x having n elements, ne of which are non-zero, * the full-vector storage format uses two arrays x_ind and x_vec, which * are set up as follows: * * x_ind is an integer array of length [1+ne]. Location x_ind[0] is * not used, and locations x_ind[1], ..., x_ind[ne] contain indices of * non-zero elements in vector x. * * x_vec is a floating-point array of length [1+n]. Location x_vec[0] * is not used, and locations x_vec[1], ..., x_vec[n] contain numeric * values of ALL elements in vector x, including its zero elements. * * Let, for example, the following sparse vector x be given: * * (0, 1, 0, 0, 2, 3, 0, 4) * * Then the arrays are: * * x_ind = { X; 2, 5, 6, 8 } * * x_vec = { X; 0, 1, 0, 0, 2, 3, 0, 4 } * * COMPRESSED-VECTOR STORAGE * * For a sparse vector x having n elements, ne of which are non-zero, * the compressed-vector storage format uses two arrays x_ind and x_vec, * which are set up as follows: * * x_ind is an integer array of length [1+ne]. Location x_ind[0] is * not used, and locations x_ind[1], ..., x_ind[ne] contain indices of * non-zero elements in vector x. * * x_vec is a floating-point array of length [1+ne]. Location x_vec[0] * is not used, and locations x_vec[1], ..., x_vec[ne] contain numeric * values of corresponding non-zero elements in vector x. * * Let, for example, the following sparse vector x be given: * * (0, 1, 0, 0, 2, 3, 0, 4) * * Then the arrays are: * * x_ind = { X; 2, 5, 6, 8 } * * x_vec = { X; 1, 2, 3, 4 } * * STORAGE-BY-ROWS * * For a sparse matrix A, which has m rows, n columns, and ne non-zero * elements the storage-by-rows format uses three arrays A_ptr, A_ind, * and A_val, which are set up as follows: * * A_ptr is an integer array of length [1+m+1] also called "row pointer * array". It contains the relative starting positions of each row of A * in the arrays A_ind and A_val, i.e. element A_ptr[i], 1 <= i <= m, * indicates where row i begins in the arrays A_ind and A_val. If all * elements in row i are zero, then A_ptr[i] = A_ptr[i+1]. Location * A_ptr[0] is not used, location A_ptr[1] must contain 1, and location * A_ptr[m+1] must contain ne+1 that indicates the position after the * last element in the arrays A_ind and A_val. * * A_ind is an integer array of length [1+ne]. Location A_ind[0] is not * used, and locations A_ind[1], ..., A_ind[ne] contain column indices * of (non-zero) elements in matrix A. * * A_val is a floating-point array of length [1+ne]. Location A_val[0] * is not used, and locations A_val[1], ..., A_val[ne] contain numeric * values of non-zero elements in matrix A. * * Non-zero elements of matrix A are stored contiguously, and the rows * of matrix A are stored consecutively from 1 to m in the arrays A_ind * and A_val. The elements in each row of A may be stored in any order * in A_ind and A_val. Note that elements with duplicate column indices * are not allowed. * * Let, for example, the following sparse matrix A be given: * * | 11 . 13 . . . | * | 21 22 . 24 . . | * | . 32 33 . . . | * | . . 43 44 . 46 | * | . . . . . . | * | 61 62 . . . 66 | * * Then the arrays are: * * A_ptr = { X; 1, 3, 6, 8, 11, 11; 14 } * * A_ind = { X; 1, 3; 4, 2, 1; 2, 3; 4, 3, 6; 1, 2, 6 } * * A_val = { X; 11, 13; 24, 22, 21; 32, 33; 44, 43, 46; 61, 62, 66 } * * PERMUTATION MATRICES * * Let P be a permutation matrix of the order n. It is represented as * an integer array P_per of length [1+n+n] as follows: if p[i,j] = 1, * then P_per[i] = j and P_per[n+j] = i. Location P_per[0] is not used. * * Let A' = P*A. If i-th row of A corresponds to i'-th row of A', then * P_per[i'] = i and P_per[n+i] = i'. * * References: * * 1. Gustavson F.G. Some basic techniques for solving sparse systems of * linear equations. In Rose and Willoughby (1972), pp. 41-52. * * 2. Basic Linear Algebra Subprograms Technical (BLAST) Forum Standard. * University of Tennessee (2001). */ #define check_fvs _glp_mat_check_fvs int check_fvs(int n, int nnz, int ind[], double vec[]); /* check sparse vector in full-vector storage format */ #define check_pattern _glp_mat_check_pattern int check_pattern(int m, int n, int A_ptr[], int A_ind[]); /* check pattern of sparse matrix */ #define transpose _glp_mat_transpose void transpose(int m, int n, int A_ptr[], int A_ind[], double A_val[], int AT_ptr[], int AT_ind[], double AT_val[]); /* transpose sparse matrix */ #define adat_symbolic _glp_mat_adat_symbolic int *adat_symbolic(int m, int n, int P_per[], int A_ptr[], int A_ind[], int S_ptr[]); /* compute S = P*A*D*A'*P' (symbolic phase) */ #define adat_numeric _glp_mat_adat_numeric void adat_numeric(int m, int n, int P_per[], int A_ptr[], int A_ind[], double A_val[], double D_diag[], int S_ptr[], int S_ind[], double S_val[], double S_diag[]); /* compute S = P*A*D*A'*P' (numeric phase) */ #define min_degree _glp_mat_min_degree void min_degree(int n, int A_ptr[], int A_ind[], int P_per[]); /* minimum degree ordering */ #define amd_order1 _glp_mat_amd_order1 void amd_order1(int n, int A_ptr[], int A_ind[], int P_per[]); /* approximate minimum degree ordering (AMD) */ #define symamd_ord _glp_mat_symamd_ord void symamd_ord(int n, int A_ptr[], int A_ind[], int P_per[]); /* approximate minimum degree ordering (SYMAMD) */ #define chol_symbolic _glp_mat_chol_symbolic int *chol_symbolic(int n, int A_ptr[], int A_ind[], int U_ptr[]); /* compute Cholesky factorization (symbolic phase) */ #define chol_numeric _glp_mat_chol_numeric int chol_numeric(int n, int A_ptr[], int A_ind[], double A_val[], double A_diag[], int U_ptr[], int U_ind[], double U_val[], double U_diag[]); /* compute Cholesky factorization (numeric phase) */ #define u_solve _glp_mat_u_solve void u_solve(int n, int U_ptr[], int U_ind[], double U_val[], double U_diag[], double x[]); /* solve upper triangular system U*x = b */ #define ut_solve _glp_mat_ut_solve void ut_solve(int n, int U_ptr[], int U_ind[], double U_val[], double U_diag[], double x[]); /* solve lower triangular system U'*x = b */ #endif /* eof */ igraph/src/glpk/glpnpp.h0000644000176000001440000004331612325527073014766 0ustar ripleyusers/* glpnpp.h (LP/MIP preprocessor) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPNPP_H #define GLPNPP_H #include "glpapi.h" typedef struct NPP NPP; typedef struct NPPROW NPPROW; typedef struct NPPCOL NPPCOL; typedef struct NPPAIJ NPPAIJ; typedef struct NPPTSE NPPTSE; typedef struct NPPLFE NPPLFE; struct NPP { /* LP/MIP preprocessor workspace */ /*--------------------------------------------------------------*/ /* original problem segment */ int orig_dir; /* optimization direction flag: GLP_MIN - minimization GLP_MAX - maximization */ int orig_m; /* number of rows */ int orig_n; /* number of columns */ int orig_nnz; /* number of non-zero constraint coefficients */ /*--------------------------------------------------------------*/ /* transformed problem segment (always minimization) */ DMP *pool; /* memory pool to store problem components */ char *name; /* problem name (1 to 255 chars); NULL means no name is assigned to the problem */ char *obj; /* objective function name (1 to 255 chars); NULL means no name is assigned to the objective function */ double c0; /* constant term of the objective function */ int nrows; /* number of rows introduced into the problem; this count increases by one every time a new row is added and never decreases; thus, actual number of rows may be less than nrows due to row deletions */ int ncols; /* number of columns introduced into the problem; this count increases by one every time a new column is added and never decreases; thus, actual number of column may be less than ncols due to column deletions */ NPPROW *r_head; /* pointer to the beginning of the row list */ NPPROW *r_tail; /* pointer to the end of the row list */ NPPCOL *c_head; /* pointer to the beginning of the column list */ NPPCOL *c_tail; /* pointer to the end of the column list */ /*--------------------------------------------------------------*/ /* transformation history */ DMP *stack; /* memory pool to store transformation entries */ NPPTSE *top; /* pointer to most recent transformation entry */ #if 0 /* 16/XII-2009 */ int count[1+25]; /* transformation statistics */ #endif /*--------------------------------------------------------------*/ /* resultant (preprocessed) problem segment */ int m; /* number of rows */ int n; /* number of columns */ int nnz; /* number of non-zero constraint coefficients */ int *row_ref; /* int row_ref[1+m]; */ /* row_ref[i], 1 <= i <= m, is the reference number assigned to a row, which is i-th row of the resultant problem */ int *col_ref; /* int col_ref[1+n]; */ /* col_ref[j], 1 <= j <= n, is the reference number assigned to a column, which is j-th column of the resultant problem */ /*--------------------------------------------------------------*/ /* recovered solution segment */ int sol; /* solution indicator: GLP_SOL - basic solution GLP_IPT - interior-point solution GLP_MIP - mixed integer solution */ int scaling; /* scaling option: GLP_OFF - scaling is disabled GLP_ON - scaling is enabled */ int p_stat; /* status of primal basic solution: GLP_UNDEF - primal solution is undefined GLP_FEAS - primal solution is feasible GLP_INFEAS - primal solution is infeasible GLP_NOFEAS - no primal feasible solution exists */ int d_stat; /* status of dual basic solution: GLP_UNDEF - dual solution is undefined GLP_FEAS - dual solution is feasible GLP_INFEAS - dual solution is infeasible GLP_NOFEAS - no dual feasible solution exists */ int t_stat; /* status of interior-point solution: GLP_UNDEF - interior solution is undefined GLP_OPT - interior solution is optimal */ int i_stat; /* status of mixed integer solution: GLP_UNDEF - integer solution is undefined GLP_OPT - integer solution is optimal GLP_FEAS - integer solution is feasible GLP_NOFEAS - no integer solution exists */ char *r_stat; /* char r_stat[1+nrows]; */ /* r_stat[i], 1 <= i <= nrows, is status of i-th row: GLP_BS - inactive constraint GLP_NL - active constraint on lower bound GLP_NU - active constraint on upper bound GLP_NF - active free row GLP_NS - active equality constraint */ char *c_stat; /* char c_stat[1+nrows]; */ /* c_stat[j], 1 <= j <= nrows, is status of j-th column: GLP_BS - basic variable GLP_NL - non-basic variable on lower bound GLP_NU - non-basic variable on upper bound GLP_NF - non-basic free variable GLP_NS - non-basic fixed variable */ double *r_pi; /* double r_pi[1+nrows]; */ /* r_pi[i], 1 <= i <= nrows, is Lagrange multiplier (dual value) for i-th row (constraint) */ double *c_value; /* double c_value[1+ncols]; */ /* c_value[j], 1 <= j <= ncols, is primal value of j-th column (structural variable) */ }; struct NPPROW { /* row (constraint) */ int i; /* reference number assigned to the row, 1 <= i <= nrows */ char *name; /* row name (1 to 255 chars); NULL means no name is assigned to the row */ double lb; /* lower bound; -DBL_MAX means the row has no lower bound */ double ub; /* upper bound; +DBL_MAX means the row has no upper bound */ NPPAIJ *ptr; /* pointer to the linked list of constraint coefficients */ int temp; /* working field used by preprocessor routines */ NPPROW *prev; /* pointer to previous row in the row list */ NPPROW *next; /* pointer to next row in the row list */ }; struct NPPCOL { /* column (variable) */ int j; /* reference number assigned to the column, 1 <= j <= ncols */ char *name; /* column name (1 to 255 chars); NULL means no name is assigned to the column */ char is_int; /* 0 means continuous variable; 1 means integer variable */ double lb; /* lower bound; -DBL_MAX means the column has no lower bound */ double ub; /* upper bound; +DBL_MAX means the column has no upper bound */ double coef; /* objective coefficient */ NPPAIJ *ptr; /* pointer to the linked list of constraint coefficients */ int temp; /* working field used by preprocessor routines */ #if 1 /* 28/XII-2009 */ union { double ll; /* implied column lower bound */ int pos; /* vertex ordinal number corresponding to this binary column in the conflict graph (0, if the vertex does not exist) */ } ll; union { double uu; /* implied column upper bound */ int neg; /* vertex ordinal number corresponding to complement of this binary column in the conflict graph (0, if the vertex does not exist) */ } uu; #endif NPPCOL *prev; /* pointer to previous column in the column list */ NPPCOL *next; /* pointer to next column in the column list */ }; struct NPPAIJ { /* constraint coefficient */ NPPROW *row; /* pointer to corresponding row */ NPPCOL *col; /* pointer to corresponding column */ double val; /* (non-zero) coefficient value */ NPPAIJ *r_prev; /* pointer to previous coefficient in the same row */ NPPAIJ *r_next; /* pointer to next coefficient in the same row */ NPPAIJ *c_prev; /* pointer to previous coefficient in the same column */ NPPAIJ *c_next; /* pointer to next coefficient in the same column */ }; struct NPPTSE { /* transformation stack entry */ int (*func)(NPP *npp, void *info); /* pointer to routine performing back transformation */ void *info; /* pointer to specific info (depends on the transformation) */ NPPTSE *link; /* pointer to another entry created *before* this entry */ }; struct NPPLFE { /* linear form element */ int ref; /* row/column reference number */ double val; /* (non-zero) coefficient value */ NPPLFE *next; /* pointer to another element */ }; #define npp_create_wksp _glp_npp_create_wksp NPP *npp_create_wksp(void); /* create LP/MIP preprocessor workspace */ #define npp_insert_row _glp_npp_insert_row void npp_insert_row(NPP *npp, NPPROW *row, int where); /* insert row to the row list */ #define npp_remove_row _glp_npp_remove_row void npp_remove_row(NPP *npp, NPPROW *row); /* remove row from the row list */ #define npp_activate_row _glp_npp_activate_row void npp_activate_row(NPP *npp, NPPROW *row); /* make row active */ #define npp_deactivate_row _glp_npp_deactivate_row void npp_deactivate_row(NPP *npp, NPPROW *row); /* make row inactive */ #define npp_insert_col _glp_npp_insert_col void npp_insert_col(NPP *npp, NPPCOL *col, int where); /* insert column to the column list */ #define npp_remove_col _glp_npp_remove_col void npp_remove_col(NPP *npp, NPPCOL *col); /* remove column from the column list */ #define npp_activate_col _glp_npp_activate_col void npp_activate_col(NPP *npp, NPPCOL *col); /* make column active */ #define npp_deactivate_col _glp_npp_deactivate_col void npp_deactivate_col(NPP *npp, NPPCOL *col); /* make column inactive */ #define npp_add_row _glp_npp_add_row NPPROW *npp_add_row(NPP *npp); /* add new row to the current problem */ #define npp_add_col _glp_npp_add_col NPPCOL *npp_add_col(NPP *npp); /* add new column to the current problem */ #define npp_add_aij _glp_npp_add_aij NPPAIJ *npp_add_aij(NPP *npp, NPPROW *row, NPPCOL *col, double val); /* add new element to the constraint matrix */ #define npp_row_nnz _glp_npp_row_nnz int npp_row_nnz(NPP *npp, NPPROW *row); /* count number of non-zero coefficients in row */ #define npp_col_nnz _glp_npp_col_nnz int npp_col_nnz(NPP *npp, NPPCOL *col); /* count number of non-zero coefficients in column */ #define npp_push_tse _glp_npp_push_tse void *npp_push_tse(NPP *npp, int (*func)(NPP *npp, void *info), int size); /* push new entry to the transformation stack */ #define npp_erase_row _glp_npp_erase_row void npp_erase_row(NPP *npp, NPPROW *row); /* erase row content to make it empty */ #define npp_del_row _glp_npp_del_row void npp_del_row(NPP *npp, NPPROW *row); /* remove row from the current problem */ #define npp_del_col _glp_npp_del_col void npp_del_col(NPP *npp, NPPCOL *col); /* remove column from the current problem */ #define npp_del_aij _glp_npp_del_aij void npp_del_aij(NPP *npp, NPPAIJ *aij); /* remove element from the constraint matrix */ #define npp_load_prob _glp_npp_load_prob void npp_load_prob(NPP *npp, glp_prob *orig, int names, int sol, int scaling); /* load original problem into the preprocessor workspace */ #define npp_build_prob _glp_npp_build_prob void npp_build_prob(NPP *npp, glp_prob *prob); /* build resultant (preprocessed) problem */ #define npp_postprocess _glp_npp_postprocess void npp_postprocess(NPP *npp, glp_prob *prob); /* postprocess solution from the resultant problem */ #define npp_unload_sol _glp_npp_unload_sol void npp_unload_sol(NPP *npp, glp_prob *orig); /* store solution to the original problem */ #define npp_delete_wksp _glp_npp_delete_wksp void npp_delete_wksp(NPP *npp); /* delete LP/MIP preprocessor workspace */ #define npp_error() #define npp_free_row _glp_npp_free_row void npp_free_row(NPP *npp, NPPROW *p); /* process free (unbounded) row */ #define npp_geq_row _glp_npp_geq_row void npp_geq_row(NPP *npp, NPPROW *p); /* process row of 'not less than' type */ #define npp_leq_row _glp_npp_leq_row void npp_leq_row(NPP *npp, NPPROW *p); /* process row of 'not greater than' type */ #define npp_free_col _glp_npp_free_col void npp_free_col(NPP *npp, NPPCOL *q); /* process free (unbounded) column */ #define npp_lbnd_col _glp_npp_lbnd_col void npp_lbnd_col(NPP *npp, NPPCOL *q); /* process column with (non-zero) lower bound */ #define npp_ubnd_col _glp_npp_ubnd_col void npp_ubnd_col(NPP *npp, NPPCOL *q); /* process column with upper bound */ #define npp_dbnd_col _glp_npp_dbnd_col void npp_dbnd_col(NPP *npp, NPPCOL *q); /* process non-negative column with upper bound */ #define npp_fixed_col _glp_npp_fixed_col void npp_fixed_col(NPP *npp, NPPCOL *q); /* process fixed column */ #define npp_make_equality _glp_npp_make_equality int npp_make_equality(NPP *npp, NPPROW *p); /* process row with almost identical bounds */ #define npp_make_fixed _glp_npp_make_fixed int npp_make_fixed(NPP *npp, NPPCOL *q); /* process column with almost identical bounds */ #define npp_empty_row _glp_npp_empty_row int npp_empty_row(NPP *npp, NPPROW *p); /* process empty row */ #define npp_empty_col _glp_npp_empty_col int npp_empty_col(NPP *npp, NPPCOL *q); /* process empty column */ #define npp_implied_value _glp_npp_implied_value int npp_implied_value(NPP *npp, NPPCOL *q, double s); /* process implied column value */ #define npp_eq_singlet _glp_npp_eq_singlet int npp_eq_singlet(NPP *npp, NPPROW *p); /* process row singleton (equality constraint) */ #define npp_implied_lower _glp_npp_implied_lower int npp_implied_lower(NPP *npp, NPPCOL *q, double l); /* process implied column lower bound */ #define npp_implied_upper _glp_npp_implied_upper int npp_implied_upper(NPP *npp, NPPCOL *q, double u); /* process implied upper bound of column */ #define npp_ineq_singlet _glp_npp_ineq_singlet int npp_ineq_singlet(NPP *npp, NPPROW *p); /* process row singleton (inequality constraint) */ #define npp_implied_slack _glp_npp_implied_slack void npp_implied_slack(NPP *npp, NPPCOL *q); /* process column singleton (implied slack variable) */ #define npp_implied_free _glp_npp_implied_free int npp_implied_free(NPP *npp, NPPCOL *q); /* process column singleton (implied free variable) */ #define npp_eq_doublet _glp_npp_eq_doublet NPPCOL *npp_eq_doublet(NPP *npp, NPPROW *p); /* process row doubleton (equality constraint) */ #define npp_forcing_row _glp_npp_forcing_row int npp_forcing_row(NPP *npp, NPPROW *p, int at); /* process forcing row */ #define npp_analyze_row _glp_npp_analyze_row int npp_analyze_row(NPP *npp, NPPROW *p); /* perform general row analysis */ #define npp_inactive_bound _glp_npp_inactive_bound void npp_inactive_bound(NPP *npp, NPPROW *p, int which); /* remove row lower/upper inactive bound */ #define npp_implied_bounds _glp_npp_implied_bounds void npp_implied_bounds(NPP *npp, NPPROW *p); /* determine implied column bounds */ #define npp_binarize_prob _glp_npp_binarize_prob int npp_binarize_prob(NPP *npp); /* binarize MIP problem */ #define npp_is_packing _glp_npp_is_packing int npp_is_packing(NPP *npp, NPPROW *row); /* test if constraint is packing inequality */ #define npp_hidden_packing _glp_npp_hidden_packing int npp_hidden_packing(NPP *npp, NPPROW *row); /* identify hidden packing inequality */ #define npp_implied_packing _glp_npp_implied_packing int npp_implied_packing(NPP *npp, NPPROW *row, int which, NPPCOL *var[], char set[]); /* identify implied packing inequality */ #define npp_is_covering _glp_npp_is_covering int npp_is_covering(NPP *npp, NPPROW *row); /* test if constraint is covering inequality */ #define npp_hidden_covering _glp_npp_hidden_covering int npp_hidden_covering(NPP *npp, NPPROW *row); /* identify hidden covering inequality */ #define npp_is_partitioning _glp_npp_is_partitioning int npp_is_partitioning(NPP *npp, NPPROW *row); /* test if constraint is partitioning equality */ #define npp_reduce_ineq_coef _glp_npp_reduce_ineq_coef int npp_reduce_ineq_coef(NPP *npp, NPPROW *row); /* reduce inequality constraint coefficients */ #define npp_clean_prob _glp_npp_clean_prob void npp_clean_prob(NPP *npp); /* perform initial LP/MIP processing */ #define npp_process_row _glp_npp_process_row int npp_process_row(NPP *npp, NPPROW *row, int hard); /* perform basic row processing */ #define npp_improve_bounds _glp_npp_improve_bounds int npp_improve_bounds(NPP *npp, NPPROW *row, int flag); /* improve current column bounds */ #define npp_process_col _glp_npp_process_col int npp_process_col(NPP *npp, NPPCOL *col); /* perform basic column processing */ #define npp_process_prob _glp_npp_process_prob int npp_process_prob(NPP *npp, int hard); /* perform basic LP/MIP processing */ #define npp_simplex _glp_npp_simplex int npp_simplex(NPP *npp, const glp_smcp *parm); /* process LP prior to applying primal/dual simplex method */ #define npp_integer _glp_npp_integer int npp_integer(NPP *npp, const glp_iocp *parm); /* process MIP prior to applying branch-and-bound method */ #endif /* eof */ igraph/src/glpk/glpqmd.h0000644000176000001440000000430512325527073014745 0ustar ripleyusers/* glpqmd.h (quotient minimum degree algorithm) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPQMD_H #define GLPQMD_H #define genqmd _glp_qmd_genqmd void genqmd(int *neqns, int xadj[], int adjncy[], int perm[], int invp[], int deg[], int marker[], int rchset[], int nbrhd[], int qsize[], int qlink[], int *nofsub); /* GENeral Quotient Minimum Degree algorithm */ #define qmdrch _glp_qmd_qmdrch void qmdrch(int *root, int xadj[], int adjncy[], int deg[], int marker[], int *rchsze, int rchset[], int *nhdsze, int nbrhd[]); /* Quotient MD ReaCHable set */ #define qmdqt _glp_qmd_qmdqt void qmdqt(int *root, int xadj[], int adjncy[], int marker[], int *rchsze, int rchset[], int nbrhd[]); /* Quotient MD Quotient graph Transformation */ #define qmdupd _glp_qmd_qmdupd void qmdupd(int xadj[], int adjncy[], int *nlist, int list[], int deg[], int qsize[], int qlink[], int marker[], int rchset[], int nbrhd[]); /* Quotient MD UPDate */ #define qmdmrg _glp_qmd_qmdmrg void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[], int rchset[], int ovrlp[]); /* Quotient MD MeRGe */ #endif /* eof */ igraph/src/glpk/glpstd.h0000644000176000001440000000254612325527073014763 0ustar ripleyusers/* glpstd.h (standard C headers) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPSTD_H #define GLPSTD_H #include #include #include #include #include #include #include #include #include #include #include #include #endif /* eof */ igraph/src/glpk/glpavl.h0000644000176000001440000001035612325527073014751 0ustar ripleyusers/* glpavl.h (binary search tree) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPAVL_H #define GLPAVL_H #include "glpdmp.h" typedef struct AVL AVL; typedef struct AVLNODE AVLNODE; struct AVL { /* AVL tree (Adelson-Velsky & Landis binary search tree) */ DMP *pool; /* memory pool for allocating nodes */ AVLNODE *root; /* pointer to the root node */ int (*fcmp)(void *info, const void *key1, const void *key2); /* application-defined key comparison routine */ void *info; /* transit pointer passed to the routine fcmp */ int size; /* the tree size (the total number of nodes) */ int height; /* the tree height */ }; struct AVLNODE { /* node of AVL tree */ const void *key; /* pointer to the node key (data structure for representing keys is supplied by the application) */ int rank; /* node rank = relative position of the node in its own subtree = the number of nodes in the left subtree plus one */ int type; /* reserved for the application specific information */ void *link; /* reserved for the application specific information */ AVLNODE *up; /* pointer to the parent node */ short int flag; /* node flag: 0 - this node is the left child of its parent (or this node is the root of the tree and has no parent) 1 - this node is the right child of its parent */ short int bal; /* node balance = the difference between heights of the right and left subtrees: -1 - the left subtree is higher than the right one; 0 - the left and right subtrees have the same height; +1 - the left subtree is lower than the right one */ AVLNODE *left; /* pointer to the root of the left subtree */ AVLNODE *right; /* pointer to the root of the right subtree */ }; #define avl_create_tree _glp_avl_create_tree AVL *avl_create_tree(int (*fcmp)(void *info, const void *key1, const void *key2), void *info); /* create AVL tree */ #define avl_strcmp _glp_avl_strcmp int avl_strcmp(void *info, const void *key1, const void *key2); /* compare character string keys */ #define avl_insert_node _glp_avl_insert_node AVLNODE *avl_insert_node(AVL *tree, const void *key); /* insert new node into AVL tree */ #define avl_set_node_type _glp_avl_set_node_type void avl_set_node_type(AVLNODE *node, int type); /* assign the type field of specified node */ #define avl_set_node_link _glp_avl_set_node_link void avl_set_node_link(AVLNODE *node, void *link); /* assign the link field of specified node */ #define avl_find_node _glp_avl_find_node AVLNODE *avl_find_node(AVL *tree, const void *key); /* find node in AVL tree */ #define avl_get_node_type _glp_avl_get_node_type int avl_get_node_type(AVLNODE *node); /* retrieve the type field of specified node */ #define avl_get_node_link _glp_avl_get_node_link void *avl_get_node_link(AVLNODE *node); /* retrieve the link field of specified node */ #define avl_delete_node _glp_avl_delete_node void avl_delete_node(AVL *tree, AVLNODE *node); /* delete specified node from AVL tree */ #define avl_delete_tree _glp_avl_delete_tree void avl_delete_tree(AVL *tree); /* delete AVL tree */ #endif /* eof */ igraph/src/glpk/glprgr.h0000644000176000001440000000244512325527073014761 0ustar ripleyusers/* glprgr.h (raster graphics) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifndef GLPRGR_H #define GLPRGR_H #define rgr_write_bmp16 _glp_rgr_write_bmp16 int rgr_write_bmp16(const char *fname, int m, int n, const char map[]); /* write 16-color raster image in BMP file format */ #endif /* eof */ igraph/src/cs_lusol.c0000644000176000001440000000334712325527073014347 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* x=A\b where A is unsymmetric; b overwritten with solution */ CS_INT cs_lusol (CS_INT order, const cs *A, CS_ENTRY *b, double tol) { CS_ENTRY *x ; css *S ; csn *N ; CS_INT n, ok ; if (!CS_CSC (A) || !b) return (0) ; /* check inputs */ n = A->n ; S = cs_sqr (order, A, 0) ; /* ordering and symbolic analysis */ N = cs_lu (A, S, tol) ; /* numeric LU factorization */ x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get workspace */ ok = (S && N && x) ; if (ok) { cs_ipvec (N->pinv, b, x, n) ; /* x = b(p) */ cs_lsolve (N->L, x) ; /* x = L\x */ cs_usolve (N->U, x) ; /* x = U\x */ cs_ipvec (S->q, x, b, n) ; /* b(q) = x */ } cs_free (x) ; cs_sfree (S) ; cs_nfree (N) ; return (ok) ; } igraph/src/sbm.c0000644000176000001440000001567412325527074013314 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=8 sw=2 sts=2 et: */ /* IGraph R library. Copyright (C) 2003-2013 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_interface.h" #include "igraph_vector.h" #include "igraph_matrix.h" #include "igraph_random.h" #include "igraph_constructors.h" #include "igraph_games.h" /** * \function igraph_sbm_game * Sample from a stochastic block model * * This function samples graphs from a stochastic block * model by (doing the equivalent of) Bernoulli * trials for each potential edge with the probabilities * given by the Bernoulli rate matrix, \p pref_matrix. * See Faust, K., & Wasserman, S. (1992a). Blockmodels: * Interpretation and evaluation. Social Networks, 14, 5-–61. * * * The order of the vertex ids in the generated graph corresponds to * the \p block_sizes argument. * * \param graph The output graph. * \param n Number of vertices. * \param pref_matrix The matrix giving the Bernoulli rates. * This is a KxK matrix, where K is the number of groups. * The probability of creating an edge between vertices from * groups i and j is given by element (i,j). * \param block_sizes An integer vector giving the number of * vertices in each group. * \param directed Boolean, whether to create a directed graph. If * this argument is false, then \p pref_matrix must be symmetric. * \param loops Boolean, whether to create self-loops. * \return Error code. * * Time complexity: O(|V|+|E|+K^2), where |V| is the number of * vertices, |E| is the number of edges, and K is the number of * groups. * * \sa \ref igraph_erdos_renyi_game() for a simple Bernoulli graph. * */ int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, const igraph_matrix_t *pref_matrix, const igraph_vector_int_t *block_sizes, igraph_bool_t directed, igraph_bool_t loops) { int no_blocks=igraph_matrix_nrow(pref_matrix); int from, to, fromoff=0; igraph_real_t minp, maxp; igraph_vector_t edges; /* ------------------------------------------------------------ */ /* Check arguments */ /* ------------------------------------------------------------ */ if (igraph_matrix_ncol(pref_matrix) != no_blocks) { IGRAPH_ERROR("Preference matrix is not square", IGRAPH_NONSQUARE); } igraph_matrix_minmax(pref_matrix, &minp, &maxp); if (minp < 0 || maxp > 1) { IGRAPH_ERROR("Connection probabilities must in [0,1]", IGRAPH_EINVAL); } if (n < 0) { IGRAPH_ERROR("Number of vertices must be non-negative", IGRAPH_EINVAL); } if (!directed && !igraph_matrix_is_symmetric(pref_matrix)) { IGRAPH_ERROR("Preference matrix must be symmetric for undirected graphs", IGRAPH_EINVAL); } if (igraph_vector_int_size(block_sizes) != no_blocks) { IGRAPH_ERROR("Invalid block size vector length", IGRAPH_EINVAL); } if (igraph_vector_int_min(block_sizes) < 0) { IGRAPH_ERROR("Block size must be non-negative", IGRAPH_EINVAL); } if (igraph_vector_int_sum(block_sizes) != n) { IGRAPH_ERROR("Block sizes must sum up to number of vertices", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); RNG_BEGIN(); for (from = 0; from < no_blocks; from++) { int fromsize = VECTOR(*block_sizes)[from]; int start = directed ? 0 : from; int i, tooff=0; for (i=0; in ; S = cs_calloc (1, sizeof (css)) ; /* allocate result S */ if (!S) return (NULL) ; /* out of memory */ P = cs_amd (order, A) ; /* P = amd(A+A'), or natural */ S->pinv = cs_pinv (P, n) ; /* find inverse permutation */ cs_free (P) ; if (order && !S->pinv) return (cs_sfree (S)) ; C = cs_symperm (A, S->pinv, 0) ; /* C = spones(triu(A(P,P))) */ S->parent = cs_etree (C, 0) ; /* find etree of C */ post = cs_post (S->parent, n) ; /* postorder the etree */ c = cs_counts (C, S->parent, post, 0) ; /* find column counts of chol(C) */ cs_free (post) ; cs_spfree (C) ; S->cp = cs_malloc (n+1, sizeof (CS_INT)) ; /* allocate result S->cp */ S->unz = S->lnz = cs_cumsum (S->cp, c, n) ; /* find column pointers for L */ cs_free (c) ; return ((S->lnz >= 0) ? S : cs_sfree (S)) ; } igraph/src/dsortr.f0000644000176000001440000001240412325527073014036 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsortr c c\Description: c Sort the array X1 in the order specified by WHICH and optionally c applies the permutation to the array X2. c c\Usage: c call igraphdsortr c ( WHICH, APPLY, N, X1, X2 ) c c\Arguments c WHICH Character*2. (Input) c 'LM' -> X1 is sorted into increasing order of magnitude. c 'SM' -> X1 is sorted into decreasing order of magnitude. c 'LA' -> X1 is sorted into increasing order of algebraic. c 'SA' -> X1 is sorted into decreasing order of algebraic. c c APPLY Logical. (Input) c APPLY = .TRUE. -> apply the sorted order to X2. c APPLY = .FALSE. -> do not apply the sorted order to X2. c c N Integer. (INPUT) c Size of the arrays. c c X1 Double precision array of length N. (INPUT/OUTPUT) c The array to be sorted. c c X2 Double precision array of length N. (INPUT/OUTPUT) c Only referenced if APPLY = .TRUE. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c 12/16/93: Version ' 2.1'. c Adapted from the sort routine in LANSO. c c\SCCS Information: @(#) c FILE: sortr.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsortr (which, apply, n, x1, x2) c c %------------------% c | Scalar Arguments | c %------------------% c character*2 which logical apply integer n c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & x1(0:n-1), x2(0:n-1) c c %---------------% c | Local Scalars | c %---------------% c integer i, igap, j Double precision & temp c c %-----------------------% c | Executable Statements | c %-----------------------% c igap = n / 2 c if (which .eq. 'SA') then c c X1 is sorted into decreasing order of algebraic. c 10 continue if (igap .eq. 0) go to 9000 do 30 i = igap, n-1 j = i-igap 20 continue c if (j.lt.0) go to 30 c if (x1(j).lt.x1(j+igap)) then temp = x1(j) x1(j) = x1(j+igap) x1(j+igap) = temp if (apply) then temp = x2(j) x2(j) = x2(j+igap) x2(j+igap) = temp end if else go to 30 endif j = j-igap go to 20 30 continue igap = igap / 2 go to 10 c else if (which .eq. 'SM') then c c X1 is sorted into decreasing order of magnitude. c 40 continue if (igap .eq. 0) go to 9000 do 60 i = igap, n-1 j = i-igap 50 continue c if (j.lt.0) go to 60 c if (abs(x1(j)).lt.abs(x1(j+igap))) then temp = x1(j) x1(j) = x1(j+igap) x1(j+igap) = temp if (apply) then temp = x2(j) x2(j) = x2(j+igap) x2(j+igap) = temp end if else go to 60 endif j = j-igap go to 50 60 continue igap = igap / 2 go to 40 c else if (which .eq. 'LA') then c c X1 is sorted into increasing order of algebraic. c 70 continue if (igap .eq. 0) go to 9000 do 90 i = igap, n-1 j = i-igap 80 continue c if (j.lt.0) go to 90 c if (x1(j).gt.x1(j+igap)) then temp = x1(j) x1(j) = x1(j+igap) x1(j+igap) = temp if (apply) then temp = x2(j) x2(j) = x2(j+igap) x2(j+igap) = temp end if else go to 90 endif j = j-igap go to 80 90 continue igap = igap / 2 go to 70 c else if (which .eq. 'LM') then c c X1 is sorted into increasing order of magnitude. c 100 continue if (igap .eq. 0) go to 9000 do 120 i = igap, n-1 j = i-igap 110 continue c if (j.lt.0) go to 120 c if (abs(x1(j)).gt.abs(x1(j+igap))) then temp = x1(j) x1(j) = x1(j+igap) x1(j+igap) = temp if (apply) then temp = x2(j) x2(j) = x2(j+igap) x2(j+igap) = temp end if else go to 120 endif j = j-igap go to 110 120 continue igap = igap / 2 go to 100 end if c 9000 continue return c c %---------------% c | End of igraphdsortr | c %---------------% c end igraph/src/glpapi10.c0000644000176000001440000002304312325527073014134 0ustar ripleyusers/* glpapi10.c (solution checking routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" void _glp_check_kkt(glp_prob *P, int sol, int cond, double *_ae_max, int *_ae_ind, double *_re_max, int *_re_ind) { /* check feasibility and optimality conditions */ int m = P->m; int n = P->n; GLPROW *row; GLPCOL *col; GLPAIJ *aij; int i, j, ae_ind, re_ind; double e, sp, sn, t, ae_max, re_max; if (!(sol == GLP_SOL || sol == GLP_IPT || sol == GLP_MIP)) xerror("glp_check_kkt: sol = %d; invalid solution indicator\n", sol); if (!(cond == GLP_KKT_PE || cond == GLP_KKT_PB || cond == GLP_KKT_DE || cond == GLP_KKT_DB || cond == GLP_KKT_CS)) xerror("glp_check_kkt: cond = %d; invalid condition indicator " "\n", cond); ae_max = re_max = 0.0; ae_ind = re_ind = 0; if (cond == GLP_KKT_PE) { /* xR - A * xS = 0 */ for (i = 1; i <= m; i++) { row = P->row[i]; sp = sn = 0.0; /* t := xR[i] */ if (sol == GLP_SOL) t = row->prim; else if (sol == GLP_IPT) t = row->pval; else if (sol == GLP_MIP) t = row->mipx; else xassert(sol != sol); if (t >= 0.0) sp += t; else sn -= t; for (aij = row->ptr; aij != NULL; aij = aij->r_next) { col = aij->col; /* t := - a[i,j] * xS[j] */ if (sol == GLP_SOL) t = - aij->val * col->prim; else if (sol == GLP_IPT) t = - aij->val * col->pval; else if (sol == GLP_MIP) t = - aij->val * col->mipx; else xassert(sol != sol); if (t >= 0.0) sp += t; else sn -= t; } /* absolute error */ e = fabs(sp - sn); if (ae_max < e) ae_max = e, ae_ind = i; /* relative error */ e /= (1.0 + sp + sn); if (re_max < e) re_max = e, re_ind = i; } } else if (cond == GLP_KKT_PB) { /* lR <= xR <= uR */ for (i = 1; i <= m; i++) { row = P->row[i]; /* t := xR[i] */ if (sol == GLP_SOL) t = row->prim; else if (sol == GLP_IPT) t = row->pval; else if (sol == GLP_MIP) t = row->mipx; else xassert(sol != sol); /* check lower bound */ if (row->type == GLP_LO || row->type == GLP_DB || row->type == GLP_FX) { if (t < row->lb) { /* absolute error */ e = row->lb - t; if (ae_max < e) ae_max = e, ae_ind = i; /* relative error */ e /= (1.0 + fabs(row->lb)); if (re_max < e) re_max = e, re_ind = i; } } /* check upper bound */ if (row->type == GLP_UP || row->type == GLP_DB || row->type == GLP_FX) { if (t > row->ub) { /* absolute error */ e = t - row->ub; if (ae_max < e) ae_max = e, ae_ind = i; /* relative error */ e /= (1.0 + fabs(row->ub)); if (re_max < e) re_max = e, re_ind = i; } } } /* lS <= xS <= uS */ for (j = 1; j <= n; j++) { col = P->col[j]; /* t := xS[j] */ if (sol == GLP_SOL) t = col->prim; else if (sol == GLP_IPT) t = col->pval; else if (sol == GLP_MIP) t = col->mipx; else xassert(sol != sol); /* check lower bound */ if (col->type == GLP_LO || col->type == GLP_DB || col->type == GLP_FX) { if (t < col->lb) { /* absolute error */ e = col->lb - t; if (ae_max < e) ae_max = e, ae_ind = m+j; /* relative error */ e /= (1.0 + fabs(col->lb)); if (re_max < e) re_max = e, re_ind = m+j; } } /* check upper bound */ if (col->type == GLP_UP || col->type == GLP_DB || col->type == GLP_FX) { if (t > col->ub) { /* absolute error */ e = t - col->ub; if (ae_max < e) ae_max = e, ae_ind = m+j; /* relative error */ e /= (1.0 + fabs(col->ub)); if (re_max < e) re_max = e, re_ind = m+j; } } } } else if (cond == GLP_KKT_DE) { /* A' * (lambdaR - cR) + (lambdaS - cS) = 0 */ for (j = 1; j <= n; j++) { col = P->col[j]; sp = sn = 0.0; /* t := lambdaS[j] - cS[j] */ if (sol == GLP_SOL) t = col->dual - col->coef; else if (sol == GLP_IPT) t = col->dval - col->coef; else xassert(sol != sol); if (t >= 0.0) sp += t; else sn -= t; for (aij = col->ptr; aij != NULL; aij = aij->c_next) { row = aij->row; /* t := a[i,j] * (lambdaR[i] - cR[i]) */ if (sol == GLP_SOL) t = aij->val * row->dual; else if (sol == GLP_IPT) t = aij->val * row->dval; else xassert(sol != sol); if (t >= 0.0) sp += t; else sn -= t; } /* absolute error */ e = fabs(sp - sn); if (ae_max < e) ae_max = e, ae_ind = m+j; /* relative error */ e /= (1.0 + sp + sn); if (re_max < e) re_max = e, re_ind = m+j; } } else if (cond == GLP_KKT_DB) { /* check lambdaR */ for (i = 1; i <= m; i++) { row = P->row[i]; /* t := lambdaR[i] */ if (sol == GLP_SOL) t = row->dual; else if (sol == GLP_IPT) t = row->dval; else xassert(sol != sol); /* correct sign */ if (P->dir == GLP_MIN) t = + t; else if (P->dir == GLP_MAX) t = - t; else xassert(P != P); /* check for positivity */ if (row->type == GLP_FR || row->type == GLP_LO) { if (t < 0.0) { e = - t; if (ae_max < e) ae_max = re_max = e, ae_ind = re_ind = i; } } /* check for negativity */ if (row->type == GLP_FR || row->type == GLP_UP) { if (t > 0.0) { e = + t; if (ae_max < e) ae_max = re_max = e, ae_ind = re_ind = i; } } } /* check lambdaS */ for (j = 1; j <= n; j++) { col = P->col[j]; /* t := lambdaS[j] */ if (sol == GLP_SOL) t = col->dual; else if (sol == GLP_IPT) t = col->dval; else xassert(sol != sol); /* correct sign */ if (P->dir == GLP_MIN) t = + t; else if (P->dir == GLP_MAX) t = - t; else xassert(P != P); /* check for positivity */ if (col->type == GLP_FR || col->type == GLP_LO) { if (t < 0.0) { e = - t; if (ae_max < e) ae_max = re_max = e, ae_ind = re_ind = m+j; } } /* check for negativity */ if (col->type == GLP_FR || col->type == GLP_UP) { if (t > 0.0) { e = + t; if (ae_max < e) ae_max = re_max = e, ae_ind = re_ind = m+j; } } } } else xassert(cond != cond); if (_ae_max != NULL) *_ae_max = ae_max; if (_ae_ind != NULL) *_ae_ind = ae_ind; if (_re_max != NULL) *_re_max = re_max; if (_re_ind != NULL) *_re_ind = re_ind; return; } /* eof */ igraph/src/glpspx01.c0000644000176000001440000030147412325527073014204 0ustar ripleyusers/* glpspx01.c (primal simplex method) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpspx.h" struct csa { /* common storage area */ /*--------------------------------------------------------------*/ /* LP data */ int m; /* number of rows (auxiliary variables), m > 0 */ int n; /* number of columns (structural variables), n > 0 */ char *type; /* char type[1+m+n]; */ /* type[0] is not used; type[k], 1 <= k <= m+n, is the type of variable x[k]: GLP_FR - free variable GLP_LO - variable with lower bound GLP_UP - variable with upper bound GLP_DB - double-bounded variable GLP_FX - fixed variable */ double *lb; /* double lb[1+m+n]; */ /* lb[0] is not used; lb[k], 1 <= k <= m+n, is an lower bound of variable x[k]; if x[k] has no lower bound, lb[k] is zero */ double *ub; /* double ub[1+m+n]; */ /* ub[0] is not used; ub[k], 1 <= k <= m+n, is an upper bound of variable x[k]; if x[k] has no upper bound, ub[k] is zero; if x[k] is of fixed type, ub[k] is the same as lb[k] */ double *coef; /* double coef[1+m+n]; */ /* coef[0] is not used; coef[k], 1 <= k <= m+n, is an objective coefficient at variable x[k] (note that on phase I auxiliary variables also may have non-zero objective coefficients) */ /*--------------------------------------------------------------*/ /* original objective function */ double *obj; /* double obj[1+n]; */ /* obj[0] is a constant term of the original objective function; obj[j], 1 <= j <= n, is an original objective coefficient at structural variable x[m+j] */ double zeta; /* factor used to scale original objective coefficients; its sign defines original optimization direction: zeta > 0 means minimization, zeta < 0 means maximization */ /*--------------------------------------------------------------*/ /* constraint matrix A; it has m rows and n columns and is stored by columns */ int *A_ptr; /* int A_ptr[1+n+1]; */ /* A_ptr[0] is not used; A_ptr[j], 1 <= j <= n, is starting position of j-th column in arrays A_ind and A_val; note that A_ptr[1] is always 1; A_ptr[n+1] indicates the position after the last element in arrays A_ind and A_val */ int *A_ind; /* int A_ind[A_ptr[n+1]]; */ /* row indices */ double *A_val; /* double A_val[A_ptr[n+1]]; */ /* non-zero element values */ /*--------------------------------------------------------------*/ /* basis header */ int *head; /* int head[1+m+n]; */ /* head[0] is not used; head[i], 1 <= i <= m, is the ordinal number of basic variable xB[i]; head[i] = k means that xB[i] = x[k] and i-th column of matrix B is k-th column of matrix (I|-A); head[m+j], 1 <= j <= n, is the ordinal number of non-basic variable xN[j]; head[m+j] = k means that xN[j] = x[k] and j-th column of matrix N is k-th column of matrix (I|-A) */ char *stat; /* char stat[1+n]; */ /* stat[0] is not used; stat[j], 1 <= j <= n, is the status of non-basic variable xN[j], which defines its active bound: GLP_NL - lower bound is active GLP_NU - upper bound is active GLP_NF - free variable GLP_NS - fixed variable */ /*--------------------------------------------------------------*/ /* matrix B is the basis matrix; it is composed from columns of the augmented constraint matrix (I|-A) corresponding to basic variables and stored in a factorized (invertable) form */ int valid; /* factorization is valid only if this flag is set */ BFD *bfd; /* BFD bfd[1:m,1:m]; */ /* factorized (invertable) form of the basis matrix */ /*--------------------------------------------------------------*/ /* matrix N is a matrix composed from columns of the augmented constraint matrix (I|-A) corresponding to non-basic variables except fixed ones; it is stored by rows and changes every time the basis changes */ int *N_ptr; /* int N_ptr[1+m+1]; */ /* N_ptr[0] is not used; N_ptr[i], 1 <= i <= m, is starting position of i-th row in arrays N_ind and N_val; note that N_ptr[1] is always 1; N_ptr[m+1] indicates the position after the last element in arrays N_ind and N_val */ int *N_len; /* int N_len[1+m]; */ /* N_len[0] is not used; N_len[i], 1 <= i <= m, is length of i-th row (0 to n) */ int *N_ind; /* int N_ind[N_ptr[m+1]]; */ /* column indices */ double *N_val; /* double N_val[N_ptr[m+1]]; */ /* non-zero element values */ /*--------------------------------------------------------------*/ /* working parameters */ int phase; /* search phase: 0 - not determined yet 1 - search for primal feasible solution 2 - search for optimal solution */ glp_long tm_beg; /* time value at the beginning of the search */ int it_beg; /* simplex iteration count at the beginning of the search */ int it_cnt; /* simplex iteration count; it increases by one every time the basis changes (including the case when a non-basic variable jumps to its opposite bound) */ int it_dpy; /* simplex iteration count at the most recent display output */ /*--------------------------------------------------------------*/ /* basic solution components */ double *bbar; /* double bbar[1+m]; */ /* bbar[0] is not used; bbar[i], 1 <= i <= m, is primal value of basic variable xB[i] (if xB[i] is free, its primal value is not updated) */ double *cbar; /* double cbar[1+n]; */ /* cbar[0] is not used; cbar[j], 1 <= j <= n, is reduced cost of non-basic variable xN[j] (if xN[j] is fixed, its reduced cost is not updated) */ /*--------------------------------------------------------------*/ /* the following pricing technique options may be used: GLP_PT_STD - standard ("textbook") pricing; GLP_PT_PSE - projected steepest edge; GLP_PT_DVX - Devex pricing (not implemented yet); in case of GLP_PT_STD the reference space is not used, and all steepest edge coefficients are set to 1 */ int refct; /* this count is set to an initial value when the reference space is defined and decreases by one every time the basis changes; once this count reaches zero, the reference space is redefined again */ char *refsp; /* char refsp[1+m+n]; */ /* refsp[0] is not used; refsp[k], 1 <= k <= m+n, is the flag which means that variable x[k] belongs to the current reference space */ double *gamma; /* double gamma[1+n]; */ /* gamma[0] is not used; gamma[j], 1 <= j <= n, is the steepest edge coefficient for non-basic variable xN[j]; if xN[j] is fixed, gamma[j] is not used and just set to 1 */ /*--------------------------------------------------------------*/ /* non-basic variable xN[q] chosen to enter the basis */ int q; /* index of the non-basic variable xN[q] chosen, 1 <= q <= n; if the set of eligible non-basic variables is empty and thus no variable has been chosen, q is set to 0 */ /*--------------------------------------------------------------*/ /* pivot column of the simplex table corresponding to non-basic variable xN[q] chosen is the following vector: T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], where B is the current basis matrix, N[q] is a column of the matrix (I|-A) corresponding to xN[q] */ int tcol_nnz; /* number of non-zero components, 0 <= nnz <= m */ int *tcol_ind; /* int tcol_ind[1+m]; */ /* tcol_ind[0] is not used; tcol_ind[t], 1 <= t <= nnz, is an index of non-zero component, i.e. tcol_ind[t] = i means that tcol_vec[i] != 0 */ double *tcol_vec; /* double tcol_vec[1+m]; */ /* tcol_vec[0] is not used; tcol_vec[i], 1 <= i <= m, is a numeric value of i-th component of the column */ double tcol_max; /* infinity (maximum) norm of the column (max |tcol_vec[i]|) */ int tcol_num; /* number of significant non-zero components, which means that: |tcol_vec[i]| >= eps for i in tcol_ind[1,...,num], |tcol_vec[i]| < eps for i in tcol_ind[num+1,...,nnz], where eps is a pivot tolerance */ /*--------------------------------------------------------------*/ /* basic variable xB[p] chosen to leave the basis */ int p; /* index of the basic variable xB[p] chosen, 1 <= p <= m; p = 0 means that no basic variable reaches its bound; p < 0 means that non-basic variable xN[q] reaches its opposite bound before any basic variable */ int p_stat; /* new status (GLP_NL, GLP_NU, or GLP_NS) to be assigned to xB[p] once it has left the basis */ double teta; /* change of non-basic variable xN[q] (see above), on which xB[p] (or, if p < 0, xN[q] itself) reaches its bound */ /*--------------------------------------------------------------*/ /* pivot row of the simplex table corresponding to basic variable xB[p] chosen is the following vector: T' * e[p] = - N' * inv(B') * e[p] = - N' * rho, where B' is a matrix transposed to the current basis matrix, N' is a matrix, whose rows are columns of the matrix (I|-A) corresponding to non-basic non-fixed variables */ int trow_nnz; /* number of non-zero components, 0 <= nnz <= n */ int *trow_ind; /* int trow_ind[1+n]; */ /* trow_ind[0] is not used; trow_ind[t], 1 <= t <= nnz, is an index of non-zero component, i.e. trow_ind[t] = j means that trow_vec[j] != 0 */ double *trow_vec; /* int trow_vec[1+n]; */ /* trow_vec[0] is not used; trow_vec[j], 1 <= j <= n, is a numeric value of j-th component of the row */ /*--------------------------------------------------------------*/ /* working arrays */ double *work1; /* double work1[1+m]; */ double *work2; /* double work2[1+m]; */ double *work3; /* double work3[1+m]; */ double *work4; /* double work4[1+m]; */ }; static const double kappa = 0.10; /*********************************************************************** * alloc_csa - allocate common storage area * * This routine allocates all arrays in the common storage area (CSA) * and returns a pointer to the CSA. */ static struct csa *alloc_csa(glp_prob *lp) { struct csa *csa; int m = lp->m; int n = lp->n; int nnz = lp->nnz; csa = xmalloc(sizeof(struct csa)); xassert(m > 0 && n > 0); csa->m = m; csa->n = n; csa->type = xcalloc(1+m+n, sizeof(char)); csa->lb = xcalloc(1+m+n, sizeof(double)); csa->ub = xcalloc(1+m+n, sizeof(double)); csa->coef = xcalloc(1+m+n, sizeof(double)); csa->obj = xcalloc(1+n, sizeof(double)); csa->A_ptr = xcalloc(1+n+1, sizeof(int)); csa->A_ind = xcalloc(1+nnz, sizeof(int)); csa->A_val = xcalloc(1+nnz, sizeof(double)); csa->head = xcalloc(1+m+n, sizeof(int)); csa->stat = xcalloc(1+n, sizeof(char)); csa->N_ptr = xcalloc(1+m+1, sizeof(int)); csa->N_len = xcalloc(1+m, sizeof(int)); csa->N_ind = NULL; /* will be allocated later */ csa->N_val = NULL; /* will be allocated later */ csa->bbar = xcalloc(1+m, sizeof(double)); csa->cbar = xcalloc(1+n, sizeof(double)); csa->refsp = xcalloc(1+m+n, sizeof(char)); csa->gamma = xcalloc(1+n, sizeof(double)); csa->tcol_ind = xcalloc(1+m, sizeof(int)); csa->tcol_vec = xcalloc(1+m, sizeof(double)); csa->trow_ind = xcalloc(1+n, sizeof(int)); csa->trow_vec = xcalloc(1+n, sizeof(double)); csa->work1 = xcalloc(1+m, sizeof(double)); csa->work2 = xcalloc(1+m, sizeof(double)); csa->work3 = xcalloc(1+m, sizeof(double)); csa->work4 = xcalloc(1+m, sizeof(double)); return csa; } /*********************************************************************** * init_csa - initialize common storage area * * This routine initializes all data structures in the common storage * area (CSA). */ static void alloc_N(struct csa *csa); static void build_N(struct csa *csa); static void init_csa(struct csa *csa, glp_prob *lp) { int m = csa->m; int n = csa->n; char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; double *coef = csa->coef; double *obj = csa->obj; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; char *stat = csa->stat; char *refsp = csa->refsp; double *gamma = csa->gamma; int i, j, k, loc; double cmax; /* auxiliary variables */ for (i = 1; i <= m; i++) { GLPROW *row = lp->row[i]; type[i] = (char)row->type; lb[i] = row->lb * row->rii; ub[i] = row->ub * row->rii; coef[i] = 0.0; } /* structural variables */ for (j = 1; j <= n; j++) { GLPCOL *col = lp->col[j]; type[m+j] = (char)col->type; lb[m+j] = col->lb / col->sjj; ub[m+j] = col->ub / col->sjj; coef[m+j] = col->coef * col->sjj; } /* original objective function */ obj[0] = lp->c0; memcpy(&obj[1], &coef[m+1], n * sizeof(double)); /* factor used to scale original objective coefficients */ cmax = 0.0; for (j = 1; j <= n; j++) if (cmax < fabs(obj[j])) cmax = fabs(obj[j]); if (cmax == 0.0) cmax = 1.0; switch (lp->dir) { case GLP_MIN: csa->zeta = + 1.0 / cmax; break; case GLP_MAX: csa->zeta = - 1.0 / cmax; break; default: xassert(lp != lp); } #if 1 if (fabs(csa->zeta) < 1.0) csa->zeta *= 1000.0; #endif /* matrix A (by columns) */ loc = 1; for (j = 1; j <= n; j++) { GLPAIJ *aij; A_ptr[j] = loc; for (aij = lp->col[j]->ptr; aij != NULL; aij = aij->c_next) { A_ind[loc] = aij->row->i; A_val[loc] = aij->row->rii * aij->val * aij->col->sjj; loc++; } } A_ptr[n+1] = loc; xassert(loc == lp->nnz+1); /* basis header */ xassert(lp->valid); memcpy(&head[1], &lp->head[1], m * sizeof(int)); k = 0; for (i = 1; i <= m; i++) { GLPROW *row = lp->row[i]; if (row->stat != GLP_BS) { k++; xassert(k <= n); head[m+k] = i; stat[k] = (char)row->stat; } } for (j = 1; j <= n; j++) { GLPCOL *col = lp->col[j]; if (col->stat != GLP_BS) { k++; xassert(k <= n); head[m+k] = m + j; stat[k] = (char)col->stat; } } xassert(k == n); /* factorization of matrix B */ csa->valid = 1, lp->valid = 0; csa->bfd = lp->bfd, lp->bfd = NULL; /* matrix N (by rows) */ alloc_N(csa); build_N(csa); /* working parameters */ csa->phase = 0; csa->tm_beg = xtime(); csa->it_beg = csa->it_cnt = lp->it_cnt; csa->it_dpy = -1; /* reference space and steepest edge coefficients */ csa->refct = 0; memset(&refsp[1], 0, (m+n) * sizeof(char)); for (j = 1; j <= n; j++) gamma[j] = 1.0; return; } /*********************************************************************** * invert_B - compute factorization of the basis matrix * * This routine computes factorization of the current basis matrix B. * * If the operation is successful, the routine returns zero, otherwise * non-zero. */ static int inv_col(void *info, int i, int ind[], double val[]) { /* this auxiliary routine returns row indices and numeric values of non-zero elements of i-th column of the basis matrix */ struct csa *csa = info; int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; int k, len, ptr, t; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); #endif k = head[i]; /* B[i] is k-th column of (I|-A) */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* B[i] is k-th column of submatrix I */ len = 1; ind[1] = k; val[1] = 1.0; } else { /* B[i] is (k-m)-th column of submatrix (-A) */ ptr = A_ptr[k-m]; len = A_ptr[k-m+1] - ptr; memcpy(&ind[1], &A_ind[ptr], len * sizeof(int)); memcpy(&val[1], &A_val[ptr], len * sizeof(double)); for (t = 1; t <= len; t++) val[t] = - val[t]; } return len; } static int invert_B(struct csa *csa) { int ret; ret = bfd_factorize(csa->bfd, csa->m, NULL, inv_col, csa); csa->valid = (ret == 0); return ret; } /*********************************************************************** * update_B - update factorization of the basis matrix * * This routine replaces i-th column of the basis matrix B by k-th * column of the augmented constraint matrix (I|-A) and then updates * the factorization of B. * * If the factorization has been successfully updated, the routine * returns zero, otherwise non-zero. */ static int update_B(struct csa *csa, int i, int k) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int ret; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* new i-th column of B is k-th column of I */ int ind[1+1]; double val[1+1]; ind[1] = k; val[1] = 1.0; xassert(csa->valid); ret = bfd_update_it(csa->bfd, i, 0, 1, ind, val); } else { /* new i-th column of B is (k-m)-th column of (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; double *val = csa->work1; int beg, end, ptr, len; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; len = 0; for (ptr = beg; ptr < end; ptr++) val[++len] = - A_val[ptr]; xassert(csa->valid); ret = bfd_update_it(csa->bfd, i, 0, len, &A_ind[beg-1], val); } csa->valid = (ret == 0); return ret; } /*********************************************************************** * error_ftran - compute residual vector r = h - B * x * * This routine computes the residual vector r = h - B * x, where B is * the current basis matrix, h is the vector of right-hand sides, x is * the solution vector. */ static void error_ftran(struct csa *csa, double h[], double x[], double r[]) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; int i, k, beg, end, ptr; double temp; /* compute the residual vector: r = h - B * x = h - B[1] * x[1] - ... - B[m] * x[m], where B[1], ..., B[m] are columns of matrix B */ memcpy(&r[1], &h[1], m * sizeof(double)); for (i = 1; i <= m; i++) { temp = x[i]; if (temp == 0.0) continue; k = head[i]; /* B[i] is k-th column of (I|-A) */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* B[i] is k-th column of submatrix I */ r[k] -= temp; } else { /* B[i] is (k-m)-th column of submatrix (-A) */ beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) r[A_ind[ptr]] += A_val[ptr] * temp; } } return; } /*********************************************************************** * refine_ftran - refine solution of B * x = h * * This routine performs one iteration to refine the solution of * the system B * x = h, where B is the current basis matrix, h is the * vector of right-hand sides, x is the solution vector. */ static void refine_ftran(struct csa *csa, double h[], double x[]) { int m = csa->m; double *r = csa->work1; double *d = csa->work1; int i; /* compute the residual vector r = h - B * x */ error_ftran(csa, h, x, r); /* compute the correction vector d = inv(B) * r */ xassert(csa->valid); bfd_ftran(csa->bfd, d); /* refine the solution vector (new x) = (old x) + d */ for (i = 1; i <= m; i++) x[i] += d[i]; return; } /*********************************************************************** * error_btran - compute residual vector r = h - B'* x * * This routine computes the residual vector r = h - B'* x, where B' * is a matrix transposed to the current basis matrix, h is the vector * of right-hand sides, x is the solution vector. */ static void error_btran(struct csa *csa, double h[], double x[], double r[]) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; int i, k, beg, end, ptr; double temp; /* compute the residual vector r = b - B'* x */ for (i = 1; i <= m; i++) { /* r[i] := b[i] - (i-th column of B)'* x */ k = head[i]; /* B[i] is k-th column of (I|-A) */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif temp = h[i]; if (k <= m) { /* B[i] is k-th column of submatrix I */ temp -= x[k]; } else { /* B[i] is (k-m)-th column of submatrix (-A) */ beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) temp += A_val[ptr] * x[A_ind[ptr]]; } r[i] = temp; } return; } /*********************************************************************** * refine_btran - refine solution of B'* x = h * * This routine performs one iteration to refine the solution of the * system B'* x = h, where B' is a matrix transposed to the current * basis matrix, h is the vector of right-hand sides, x is the solution * vector. */ static void refine_btran(struct csa *csa, double h[], double x[]) { int m = csa->m; double *r = csa->work1; double *d = csa->work1; int i; /* compute the residual vector r = h - B'* x */ error_btran(csa, h, x, r); /* compute the correction vector d = inv(B') * r */ xassert(csa->valid); bfd_btran(csa->bfd, d); /* refine the solution vector (new x) = (old x) + d */ for (i = 1; i <= m; i++) x[i] += d[i]; return; } /*********************************************************************** * alloc_N - allocate matrix N * * This routine determines maximal row lengths of matrix N, sets its * row pointers, and then allocates arrays N_ind and N_val. * * Note that some fixed structural variables may temporarily become * double-bounded, so corresponding columns of matrix A should not be * ignored on calculating maximal row lengths of matrix N. */ static void alloc_N(struct csa *csa) { int m = csa->m; int n = csa->n; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; int *N_ptr = csa->N_ptr; int *N_len = csa->N_len; int i, j, beg, end, ptr; /* determine number of non-zeros in each row of the augmented constraint matrix (I|-A) */ for (i = 1; i <= m; i++) N_len[i] = 1; for (j = 1; j <= n; j++) { beg = A_ptr[j]; end = A_ptr[j+1]; for (ptr = beg; ptr < end; ptr++) N_len[A_ind[ptr]]++; } /* determine maximal row lengths of matrix N and set its row pointers */ N_ptr[1] = 1; for (i = 1; i <= m; i++) { /* row of matrix N cannot have more than n non-zeros */ if (N_len[i] > n) N_len[i] = n; N_ptr[i+1] = N_ptr[i] + N_len[i]; } /* now maximal number of non-zeros in matrix N is known */ csa->N_ind = xcalloc(N_ptr[m+1], sizeof(int)); csa->N_val = xcalloc(N_ptr[m+1], sizeof(double)); return; } /*********************************************************************** * add_N_col - add column of matrix (I|-A) to matrix N * * This routine adds j-th column to matrix N which is k-th column of * the augmented constraint matrix (I|-A). (It is assumed that old j-th * column was previously removed from matrix N.) */ static void add_N_col(struct csa *csa, int j, int k) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *N_ptr = csa->N_ptr; int *N_len = csa->N_len; int *N_ind = csa->N_ind; double *N_val = csa->N_val; int pos; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* N[j] is k-th column of submatrix I */ pos = N_ptr[k] + (N_len[k]++); #ifdef GLP_DEBUG xassert(pos < N_ptr[k+1]); #endif N_ind[pos] = j; N_val[pos] = 1.0; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int i, beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) { i = A_ind[ptr]; /* row number */ pos = N_ptr[i] + (N_len[i]++); #ifdef GLP_DEBUG xassert(pos < N_ptr[i+1]); #endif N_ind[pos] = j; N_val[pos] = - A_val[ptr]; } } return; } /*********************************************************************** * del_N_col - remove column of matrix (I|-A) from matrix N * * This routine removes j-th column from matrix N which is k-th column * of the augmented constraint matrix (I|-A). */ static void del_N_col(struct csa *csa, int j, int k) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *N_ptr = csa->N_ptr; int *N_len = csa->N_len; int *N_ind = csa->N_ind; double *N_val = csa->N_val; int pos, head, tail; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); xassert(1 <= k && k <= m+n); #endif if (k <= m) { /* N[j] is k-th column of submatrix I */ /* find element in k-th row of N */ head = N_ptr[k]; for (pos = head; N_ind[pos] != j; pos++) /* nop */; /* and remove it from the row list */ tail = head + (--N_len[k]); #ifdef GLP_DEBUG xassert(pos <= tail); #endif N_ind[pos] = N_ind[tail]; N_val[pos] = N_val[tail]; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; int i, beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) { i = A_ind[ptr]; /* row number */ /* find element in i-th row of N */ head = N_ptr[i]; for (pos = head; N_ind[pos] != j; pos++) /* nop */; /* and remove it from the row list */ tail = head + (--N_len[i]); #ifdef GLP_DEBUG xassert(pos <= tail); #endif N_ind[pos] = N_ind[tail]; N_val[pos] = N_val[tail]; } } return; } /*********************************************************************** * build_N - build matrix N for current basis * * This routine builds matrix N for the current basis from columns * of the augmented constraint matrix (I|-A) corresponding to non-basic * non-fixed variables. */ static void build_N(struct csa *csa) { int m = csa->m; int n = csa->n; int *head = csa->head; char *stat = csa->stat; int *N_len = csa->N_len; int j, k; /* N := empty matrix */ memset(&N_len[1], 0, m * sizeof(int)); /* go through non-basic columns of matrix (I|-A) */ for (j = 1; j <= n; j++) { if (stat[j] != GLP_NS) { /* xN[j] is non-fixed; add j-th column to matrix N which is k-th column of matrix (I|-A) */ k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif add_N_col(csa, j, k); } } return; } /*********************************************************************** * get_xN - determine current value of non-basic variable xN[j] * * This routine returns the current value of non-basic variable xN[j], * which is a value of its active bound. */ static double get_xN(struct csa *csa, int j) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif double *lb = csa->lb; double *ub = csa->ub; int *head = csa->head; char *stat = csa->stat; int k; double xN; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif switch (stat[j]) { case GLP_NL: /* x[k] is on its lower bound */ xN = lb[k]; break; case GLP_NU: /* x[k] is on its upper bound */ xN = ub[k]; break; case GLP_NF: /* x[k] is free non-basic variable */ xN = 0.0; break; case GLP_NS: /* x[k] is fixed non-basic variable */ xN = lb[k]; break; default: xassert(stat != stat); } return xN; } /*********************************************************************** * eval_beta - compute primal values of basic variables * * This routine computes current primal values of all basic variables: * * beta = - inv(B) * N * xN, * * where B is the current basis matrix, N is a matrix built of columns * of matrix (I|-A) corresponding to non-basic variables, and xN is the * vector of current values of non-basic variables. */ static void eval_beta(struct csa *csa, double beta[]) { int m = csa->m; int n = csa->n; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; double *h = csa->work2; int i, j, k, beg, end, ptr; double xN; /* compute the right-hand side vector: h := - N * xN = - N[1] * xN[1] - ... - N[n] * xN[n], where N[1], ..., N[n] are columns of matrix N */ for (i = 1; i <= m; i++) h[i] = 0.0; for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* determine current value of xN[j] */ xN = get_xN(csa, j); if (xN == 0.0) continue; if (k <= m) { /* N[j] is k-th column of submatrix I */ h[k] -= xN; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] += xN * A_val[ptr]; } } /* solve system B * beta = h */ memcpy(&beta[1], &h[1], m * sizeof(double)); xassert(csa->valid); bfd_ftran(csa->bfd, beta); /* and refine the solution */ refine_ftran(csa, h, beta); return; } /*********************************************************************** * eval_pi - compute vector of simplex multipliers * * This routine computes the vector of current simplex multipliers: * * pi = inv(B') * cB, * * where B' is a matrix transposed to the current basis matrix, cB is * a subvector of objective coefficients at basic variables. */ static void eval_pi(struct csa *csa, double pi[]) { int m = csa->m; double *c = csa->coef; int *head = csa->head; double *cB = csa->work2; int i; /* construct the right-hand side vector cB */ for (i = 1; i <= m; i++) cB[i] = c[head[i]]; /* solve system B'* pi = cB */ memcpy(&pi[1], &cB[1], m * sizeof(double)); xassert(csa->valid); bfd_btran(csa->bfd, pi); /* and refine the solution */ refine_btran(csa, cB, pi); return; } /*********************************************************************** * eval_cost - compute reduced cost of non-basic variable xN[j] * * This routine computes the current reduced cost of non-basic variable * xN[j]: * * d[j] = cN[j] - N'[j] * pi, * * where cN[j] is the objective coefficient at variable xN[j], N[j] is * a column of the augmented constraint matrix (I|-A) corresponding to * xN[j], pi is the vector of simplex multipliers. */ static double eval_cost(struct csa *csa, double pi[], int j) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif double *coef = csa->coef; int *head = csa->head; int k; double dj; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif dj = coef[k]; if (k <= m) { /* N[j] is k-th column of submatrix I */ dj -= pi[k]; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) dj += A_val[ptr] * pi[A_ind[ptr]]; } return dj; } /*********************************************************************** * eval_bbar - compute and store primal values of basic variables * * This routine computes primal values of all basic variables and then * stores them in the solution array. */ static void eval_bbar(struct csa *csa) { eval_beta(csa, csa->bbar); return; } /*********************************************************************** * eval_cbar - compute and store reduced costs of non-basic variables * * This routine computes reduced costs of all non-basic variables and * then stores them in the solution array. */ static void eval_cbar(struct csa *csa) { #ifdef GLP_DEBUG int m = csa->m; #endif int n = csa->n; #ifdef GLP_DEBUG int *head = csa->head; #endif double *cbar = csa->cbar; double *pi = csa->work3; int j; #ifdef GLP_DEBUG int k; #endif /* compute simplex multipliers */ eval_pi(csa, pi); /* compute and store reduced costs */ for (j = 1; j <= n; j++) { #ifdef GLP_DEBUG k = head[m+j]; /* x[k] = xN[j] */ xassert(1 <= k && k <= m+n); #endif cbar[j] = eval_cost(csa, pi, j); } return; } /*********************************************************************** * reset_refsp - reset the reference space * * This routine resets (redefines) the reference space used in the * projected steepest edge pricing algorithm. */ static void reset_refsp(struct csa *csa) { int m = csa->m; int n = csa->n; int *head = csa->head; char *refsp = csa->refsp; double *gamma = csa->gamma; int j, k; xassert(csa->refct == 0); csa->refct = 1000; memset(&refsp[1], 0, (m+n) * sizeof(char)); for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ refsp[k] = 1; gamma[j] = 1.0; } return; } /*********************************************************************** * eval_gamma - compute steepest edge coefficient * * This routine computes the steepest edge coefficient for non-basic * variable xN[j] using its direct definition: * * gamma[j] = delta[j] + sum alfa[i,j]^2, * i in R * * where delta[j] = 1, if xN[j] is in the current reference space, * and 0 otherwise; R is a set of basic variables xB[i], which are in * the current reference space; alfa[i,j] are elements of the current * simplex table. * * NOTE: The routine is intended only for debugginig purposes. */ static double eval_gamma(struct csa *csa, int j) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *head = csa->head; char *refsp = csa->refsp; double *alfa = csa->work3; double *h = csa->work3; int i, k; double gamma; #ifdef GLP_DEBUG xassert(1 <= j && j <= n); #endif k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* construct the right-hand side vector h = - N[j] */ for (i = 1; i <= m; i++) h[i] = 0.0; if (k <= m) { /* N[j] is k-th column of submatrix I */ h[k] = -1.0; } else { /* N[j] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] = A_val[ptr]; } /* solve system B * alfa = h */ xassert(csa->valid); bfd_ftran(csa->bfd, alfa); /* compute gamma */ gamma = (refsp[k] ? 1.0 : 0.0); for (i = 1; i <= m; i++) { k = head[i]; #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (refsp[k]) gamma += alfa[i] * alfa[i]; } return gamma; } /*********************************************************************** * chuzc - choose non-basic variable (column of the simplex table) * * This routine chooses non-basic variable xN[q], which has largest * weighted reduced cost: * * |d[q]| / sqrt(gamma[q]) = max |d[j]| / sqrt(gamma[j]), * j in J * * where J is a subset of eligible non-basic variables xN[j], d[j] is * reduced cost of xN[j], gamma[j] is the steepest edge coefficient. * * The working objective function is always minimized, so the sign of * d[q] determines direction, in which xN[q] has to change: * * if d[q] < 0, xN[q] has to increase; * * if d[q] > 0, xN[q] has to decrease. * * If |d[j]| <= tol_dj, where tol_dj is a specified tolerance, xN[j] * is not included in J and therefore ignored. (It is assumed that the * working objective row is appropriately scaled, i.e. max|c[k]| = 1.) * * If J is empty and no variable has been chosen, q is set to 0. */ static void chuzc(struct csa *csa, double tol_dj) { int n = csa->n; char *stat = csa->stat; double *cbar = csa->cbar; double *gamma = csa->gamma; int j, q; double dj, best, temp; /* nothing is chosen so far */ q = 0, best = 0.0; /* look through the list of non-basic variables */ for (j = 1; j <= n; j++) { dj = cbar[j]; switch (stat[j]) { case GLP_NL: /* xN[j] can increase */ if (dj >= - tol_dj) continue; break; case GLP_NU: /* xN[j] can decrease */ if (dj <= + tol_dj) continue; break; case GLP_NF: /* xN[j] can change in any direction */ if (- tol_dj <= dj && dj <= + tol_dj) continue; break; case GLP_NS: /* xN[j] cannot change at all */ continue; default: xassert(stat != stat); } /* xN[j] is eligible non-basic variable; choose one which has largest weighted reduced cost */ #ifdef GLP_DEBUG xassert(gamma[j] > 0.0); #endif temp = (dj * dj) / gamma[j]; if (best < temp) q = j, best = temp; } /* store the index of non-basic variable xN[q] chosen */ csa->q = q; return; } /*********************************************************************** * eval_tcol - compute pivot column of the simplex table * * This routine computes the pivot column of the simplex table, which * corresponds to non-basic variable xN[q] chosen. * * The pivot column is the following vector: * * tcol = T * e[q] = - inv(B) * N * e[q] = - inv(B) * N[q], * * where B is the current basis matrix, N[q] is a column of the matrix * (I|-A) corresponding to variable xN[q]. */ static void eval_tcol(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *head = csa->head; int q = csa->q; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; double *h = csa->tcol_vec; int i, k, nnz; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif k = head[m+q]; /* x[k] = xN[q] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* construct the right-hand side vector h = - N[q] */ for (i = 1; i <= m; i++) h[i] = 0.0; if (k <= m) { /* N[q] is k-th column of submatrix I */ h[k] = -1.0; } else { /* N[q] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] = A_val[ptr]; } /* solve system B * tcol = h */ xassert(csa->valid); bfd_ftran(csa->bfd, tcol_vec); /* construct sparse pattern of the pivot column */ nnz = 0; for (i = 1; i <= m; i++) { if (tcol_vec[i] != 0.0) tcol_ind[++nnz] = i; } csa->tcol_nnz = nnz; return; } /*********************************************************************** * refine_tcol - refine pivot column of the simplex table * * This routine refines the pivot column of the simplex table assuming * that it was previously computed by the routine eval_tcol. */ static void refine_tcol(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif int *head = csa->head; int q = csa->q; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; double *h = csa->work3; int i, k, nnz; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif k = head[m+q]; /* x[k] = xN[q] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif /* construct the right-hand side vector h = - N[q] */ for (i = 1; i <= m; i++) h[i] = 0.0; if (k <= m) { /* N[q] is k-th column of submatrix I */ h[k] = -1.0; } else { /* N[q] is (k-m)-th column of submatrix (-A) */ int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int beg, end, ptr; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) h[A_ind[ptr]] = A_val[ptr]; } /* refine solution of B * tcol = h */ refine_ftran(csa, h, tcol_vec); /* construct sparse pattern of the pivot column */ nnz = 0; for (i = 1; i <= m; i++) { if (tcol_vec[i] != 0.0) tcol_ind[++nnz] = i; } csa->tcol_nnz = nnz; return; } /*********************************************************************** * sort_tcol - sort pivot column of the simplex table * * This routine reorders the list of non-zero elements of the pivot * column to put significant elements, whose magnitude is not less than * a specified tolerance, in front of the list, and stores the number * of significant elements in tcol_num. */ static void sort_tcol(struct csa *csa, double tol_piv) { #ifdef GLP_DEBUG int m = csa->m; #endif int nnz = csa->tcol_nnz; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; int i, num, pos; double big, eps, temp; /* compute infinity (maximum) norm of the column */ big = 0.0; for (pos = 1; pos <= nnz; pos++) { #ifdef GLP_DEBUG i = tcol_ind[pos]; xassert(1 <= i && i <= m); #endif temp = fabs(tcol_vec[tcol_ind[pos]]); if (big < temp) big = temp; } csa->tcol_max = big; /* determine absolute pivot tolerance */ eps = tol_piv * (1.0 + 0.01 * big); /* move significant column components to front of the list */ for (num = 0; num < nnz; ) { i = tcol_ind[nnz]; if (fabs(tcol_vec[i]) < eps) nnz--; else { num++; tcol_ind[nnz] = tcol_ind[num]; tcol_ind[num] = i; } } csa->tcol_num = num; return; } /*********************************************************************** * chuzr - choose basic variable (row of the simplex table) * * This routine chooses basic variable xB[p], which reaches its bound * first on changing non-basic variable xN[q] in valid direction. * * The parameter rtol is a relative tolerance used to relax bounds of * basic variables. If rtol = 0, the routine implements the standard * ratio test. Otherwise, if rtol > 0, the routine implements Harris' * two-pass ratio test. In the latter case rtol should be about three * times less than a tolerance used to check primal feasibility. */ static void chuzr(struct csa *csa, double rtol) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; double *coef = csa->coef; int *head = csa->head; int phase = csa->phase; double *bbar = csa->bbar; double *cbar = csa->cbar; int q = csa->q; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; int tcol_num = csa->tcol_num; int i, i_stat, k, p, p_stat, pos; double alfa, big, delta, s, t, teta, tmax; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif /* s := - sign(d[q]), where d[q] is reduced cost of xN[q] */ #ifdef GLP_DEBUG xassert(cbar[q] != 0.0); #endif s = (cbar[q] > 0.0 ? -1.0 : +1.0); /*** FIRST PASS ***/ k = head[m+q]; /* x[k] = xN[q] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (type[k] == GLP_DB) { /* xN[q] has both lower and upper bounds */ p = -1, p_stat = 0, teta = ub[k] - lb[k], big = 1.0; } else { /* xN[q] has no opposite bound */ p = 0, p_stat = 0, teta = DBL_MAX, big = 0.0; } /* walk through significant elements of the pivot column */ for (pos = 1; pos <= tcol_num; pos++) { i = tcol_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); #endif k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif alfa = s * tcol_vec[i]; #ifdef GLP_DEBUG xassert(alfa != 0.0); #endif /* xB[i] = ... + alfa * xN[q] + ..., and due to s we need to consider the only case when xN[q] is increasing */ if (alfa > 0.0) { /* xB[i] is increasing */ if (phase == 1 && coef[k] < 0.0) { /* xB[i] violates its lower bound, which plays the role of an upper bound on phase I */ delta = rtol * (1.0 + kappa * fabs(lb[k])); t = ((lb[k] + delta) - bbar[i]) / alfa; i_stat = GLP_NL; } else if (phase == 1 && coef[k] > 0.0) { /* xB[i] violates its upper bound, which plays the role of an lower bound on phase I */ continue; } else if (type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX) { /* xB[i] is within its bounds and has an upper bound */ delta = rtol * (1.0 + kappa * fabs(ub[k])); t = ((ub[k] + delta) - bbar[i]) / alfa; i_stat = GLP_NU; } else { /* xB[i] is within its bounds and has no upper bound */ continue; } } else { /* xB[i] is decreasing */ if (phase == 1 && coef[k] > 0.0) { /* xB[i] violates its upper bound, which plays the role of an lower bound on phase I */ delta = rtol * (1.0 + kappa * fabs(ub[k])); t = ((ub[k] - delta) - bbar[i]) / alfa; i_stat = GLP_NU; } else if (phase == 1 && coef[k] < 0.0) { /* xB[i] violates its lower bound, which plays the role of an upper bound on phase I */ continue; } else if (type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX) { /* xB[i] is within its bounds and has an lower bound */ delta = rtol * (1.0 + kappa * fabs(lb[k])); t = ((lb[k] - delta) - bbar[i]) / alfa; i_stat = GLP_NL; } else { /* xB[i] is within its bounds and has no lower bound */ continue; } } /* t is a change of xN[q], on which xB[i] reaches its bound (possibly relaxed); since the basic solution is assumed to be primal feasible (or pseudo feasible on phase I), t has to be non-negative by definition; however, it may happen that xB[i] slightly (i.e. within a tolerance) violates its bound, that leads to negative t; in the latter case, if xB[i] is chosen, negative t means that xN[q] changes in wrong direction; if pivot alfa[i,q] is close to zero, even small bound violation of xB[i] may lead to a large change of xN[q] in wrong direction; let, for example, xB[i] >= 0 and in the current basis its value be -5e-9; let also xN[q] be on its zero bound and should increase; from the ratio test rule it follows that the pivot alfa[i,q] < 0; however, if alfa[i,q] is, say, -1e-9, the change of xN[q] in wrong direction is 5e-9 / (-1e-9) = -5, and using it for updating values of other basic variables will give absolutely wrong results; therefore, if t is negative, we should replace it by exact zero assuming that xB[i] is exactly on its bound, and the violation appears due to round-off errors */ if (t < 0.0) t = 0.0; /* apply minimal ratio test */ if (teta > t || teta == t && big < fabs(alfa)) p = i, p_stat = i_stat, teta = t, big = fabs(alfa); } /* the second pass is skipped in the following cases: */ /* if the standard ratio test is used */ if (rtol == 0.0) goto done; /* if xN[q] reaches its opposite bound or if no basic variable has been chosen on the first pass */ if (p <= 0) goto done; /* if xB[p] is a blocking variable, i.e. if it prevents xN[q] from any change */ if (teta == 0.0) goto done; /*** SECOND PASS ***/ /* here tmax is a maximal change of xN[q], on which the solution remains primal feasible (or pseudo feasible on phase I) within a tolerance */ #if 0 tmax = (1.0 + 10.0 * DBL_EPSILON) * teta; #else tmax = teta; #endif /* nothing is chosen so far */ p = 0, p_stat = 0, teta = DBL_MAX, big = 0.0; /* walk through significant elements of the pivot column */ for (pos = 1; pos <= tcol_num; pos++) { i = tcol_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); #endif k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif alfa = s * tcol_vec[i]; #ifdef GLP_DEBUG xassert(alfa != 0.0); #endif /* xB[i] = ... + alfa * xN[q] + ..., and due to s we need to consider the only case when xN[q] is increasing */ if (alfa > 0.0) { /* xB[i] is increasing */ if (phase == 1 && coef[k] < 0.0) { /* xB[i] violates its lower bound, which plays the role of an upper bound on phase I */ t = (lb[k] - bbar[i]) / alfa; i_stat = GLP_NL; } else if (phase == 1 && coef[k] > 0.0) { /* xB[i] violates its upper bound, which plays the role of an lower bound on phase I */ continue; } else if (type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX) { /* xB[i] is within its bounds and has an upper bound */ t = (ub[k] - bbar[i]) / alfa; i_stat = GLP_NU; } else { /* xB[i] is within its bounds and has no upper bound */ continue; } } else { /* xB[i] is decreasing */ if (phase == 1 && coef[k] > 0.0) { /* xB[i] violates its upper bound, which plays the role of an lower bound on phase I */ t = (ub[k] - bbar[i]) / alfa; i_stat = GLP_NU; } else if (phase == 1 && coef[k] < 0.0) { /* xB[i] violates its lower bound, which plays the role of an upper bound on phase I */ continue; } else if (type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX) { /* xB[i] is within its bounds and has an lower bound */ t = (lb[k] - bbar[i]) / alfa; i_stat = GLP_NL; } else { /* xB[i] is within its bounds and has no lower bound */ continue; } } /* (see comments for the first pass) */ if (t < 0.0) t = 0.0; /* t is a change of xN[q], on which xB[i] reaches its bound; if t <= tmax, all basic variables can violate their bounds only within relaxation tolerance delta; we can use this freedom and choose basic variable having largest influence coefficient to avoid possible numeric instability */ if (t <= tmax && big < fabs(alfa)) p = i, p_stat = i_stat, teta = t, big = fabs(alfa); } /* something must be chosen on the second pass */ xassert(p != 0); done: /* store the index and status of basic variable xB[p] chosen */ csa->p = p; if (p > 0 && type[head[p]] == GLP_FX) csa->p_stat = GLP_NS; else csa->p_stat = p_stat; /* store corresponding change of non-basic variable xN[q] */ #ifdef GLP_DEBUG xassert(teta >= 0.0); #endif csa->teta = s * teta; return; } /*********************************************************************** * eval_rho - compute pivot row of the inverse * * This routine computes the pivot (p-th) row of the inverse inv(B), * which corresponds to basic variable xB[p] chosen: * * rho = inv(B') * e[p], * * where B' is a matrix transposed to the current basis matrix, e[p] * is unity vector. */ static void eval_rho(struct csa *csa, double rho[]) { int m = csa->m; int p = csa->p; double *e = rho; int i; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); #endif /* construct the right-hand side vector e[p] */ for (i = 1; i <= m; i++) e[i] = 0.0; e[p] = 1.0; /* solve system B'* rho = e[p] */ xassert(csa->valid); bfd_btran(csa->bfd, rho); return; } /*********************************************************************** * refine_rho - refine pivot row of the inverse * * This routine refines the pivot row of the inverse inv(B) assuming * that it was previously computed by the routine eval_rho. */ static void refine_rho(struct csa *csa, double rho[]) { int m = csa->m; int p = csa->p; double *e = csa->work3; int i; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); #endif /* construct the right-hand side vector e[p] */ for (i = 1; i <= m; i++) e[i] = 0.0; e[p] = 1.0; /* refine solution of B'* rho = e[p] */ refine_btran(csa, e, rho); return; } /*********************************************************************** * eval_trow - compute pivot row of the simplex table * * This routine computes the pivot row of the simplex table, which * corresponds to basic variable xB[p] chosen. * * The pivot row is the following vector: * * trow = T'* e[p] = - N'* inv(B') * e[p] = - N' * rho, * * where rho is the pivot row of the inverse inv(B) previously computed * by the routine eval_rho. * * Note that elements of the pivot row corresponding to fixed non-basic * variables are not computed. */ static void eval_trow(struct csa *csa, double rho[]) { int m = csa->m; int n = csa->n; #ifdef GLP_DEBUG char *stat = csa->stat; #endif int *N_ptr = csa->N_ptr; int *N_len = csa->N_len; int *N_ind = csa->N_ind; double *N_val = csa->N_val; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int i, j, beg, end, ptr, nnz; double temp; /* clear the pivot row */ for (j = 1; j <= n; j++) trow_vec[j] = 0.0; /* compute the pivot row as a linear combination of rows of the matrix N: trow = - rho[1] * N'[1] - ... - rho[m] * N'[m] */ for (i = 1; i <= m; i++) { temp = rho[i]; if (temp == 0.0) continue; /* trow := trow - rho[i] * N'[i] */ beg = N_ptr[i]; end = beg + N_len[i]; for (ptr = beg; ptr < end; ptr++) { #ifdef GLP_DEBUG j = N_ind[ptr]; xassert(1 <= j && j <= n); xassert(stat[j] != GLP_NS); #endif trow_vec[N_ind[ptr]] -= temp * N_val[ptr]; } } /* construct sparse pattern of the pivot row */ nnz = 0; for (j = 1; j <= n; j++) { if (trow_vec[j] != 0.0) trow_ind[++nnz] = j; } csa->trow_nnz = nnz; return; } /*********************************************************************** * update_bbar - update values of basic variables * * This routine updates values of all basic variables for the adjacent * basis. */ static void update_bbar(struct csa *csa) { #ifdef GLP_DEBUG int m = csa->m; int n = csa->n; #endif double *bbar = csa->bbar; int q = csa->q; int tcol_nnz = csa->tcol_nnz; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; int p = csa->p; double teta = csa->teta; int i, pos; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); xassert(p < 0 || 1 <= p && p <= m); #endif /* if xN[q] leaves the basis, compute its value in the adjacent basis, where it will replace xB[p] */ if (p > 0) bbar[p] = get_xN(csa, q) + teta; /* update values of other basic variables (except xB[p], because it will be replaced by xN[q]) */ if (teta == 0.0) goto done; for (pos = 1; pos <= tcol_nnz; pos++) { i = tcol_ind[pos]; /* skip xB[p] */ if (i == p) continue; /* (change of xB[i]) = alfa[i,q] * (change of xN[q]) */ bbar[i] += tcol_vec[i] * teta; } done: return; } /*********************************************************************** * reeval_cost - recompute reduced cost of non-basic variable xN[q] * * This routine recomputes reduced cost of non-basic variable xN[q] for * the current basis more accurately using its direct definition: * * d[q] = cN[q] - N'[q] * pi = * * = cN[q] - N'[q] * (inv(B') * cB) = * * = cN[q] - (cB' * inv(B) * N[q]) = * * = cN[q] + cB' * (pivot column). * * It is assumed that the pivot column of the simplex table is already * computed. */ static double reeval_cost(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif double *coef = csa->coef; int *head = csa->head; int q = csa->q; int tcol_nnz = csa->tcol_nnz; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; int i, pos; double dq; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif dq = coef[head[m+q]]; for (pos = 1; pos <= tcol_nnz; pos++) { i = tcol_ind[pos]; #ifdef GLP_DEBUG xassert(1 <= i && i <= m); #endif dq += coef[head[i]] * tcol_vec[i]; } return dq; } /*********************************************************************** * update_cbar - update reduced costs of non-basic variables * * This routine updates reduced costs of all (except fixed) non-basic * variables for the adjacent basis. */ static void update_cbar(struct csa *csa) { #ifdef GLP_DEBUG int n = csa->n; #endif double *cbar = csa->cbar; int q = csa->q; int trow_nnz = csa->trow_nnz; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; int j, pos; double new_dq; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif /* compute reduced cost of xB[p] in the adjacent basis, where it will replace xN[q] */ #ifdef GLP_DEBUG xassert(trow_vec[q] != 0.0); #endif new_dq = (cbar[q] /= trow_vec[q]); /* update reduced costs of other non-basic variables (except xN[q], because it will be replaced by xB[p]) */ for (pos = 1; pos <= trow_nnz; pos++) { j = trow_ind[pos]; /* skip xN[q] */ if (j == q) continue; cbar[j] -= trow_vec[j] * new_dq; } return; } /*********************************************************************** * update_gamma - update steepest edge coefficients * * This routine updates steepest-edge coefficients for the adjacent * basis. */ static void update_gamma(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; int *A_ptr = csa->A_ptr; int *A_ind = csa->A_ind; double *A_val = csa->A_val; int *head = csa->head; char *refsp = csa->refsp; double *gamma = csa->gamma; int q = csa->q; int tcol_nnz = csa->tcol_nnz; int *tcol_ind = csa->tcol_ind; double *tcol_vec = csa->tcol_vec; int p = csa->p; int trow_nnz = csa->trow_nnz; int *trow_ind = csa->trow_ind; double *trow_vec = csa->trow_vec; double *u = csa->work3; int i, j, k, pos, beg, end, ptr; double gamma_q, delta_q, pivot, s, t, t1, t2; #ifdef GLP_DEBUG xassert(1 <= p && p <= m); xassert(1 <= q && q <= n); #endif /* the basis changes, so decrease the count */ xassert(csa->refct > 0); csa->refct--; /* recompute gamma[q] for the current basis more accurately and compute auxiliary vector u */ gamma_q = delta_q = (refsp[head[m+q]] ? 1.0 : 0.0); for (i = 1; i <= m; i++) u[i] = 0.0; for (pos = 1; pos <= tcol_nnz; pos++) { i = tcol_ind[pos]; if (refsp[head[i]]) { u[i] = t = tcol_vec[i]; gamma_q += t * t; } else u[i] = 0.0; } xassert(csa->valid); bfd_btran(csa->bfd, u); /* update gamma[k] for other non-basic variables (except fixed variables and xN[q], because it will be replaced by xB[p]) */ pivot = trow_vec[q]; #ifdef GLP_DEBUG xassert(pivot != 0.0); #endif for (pos = 1; pos <= trow_nnz; pos++) { j = trow_ind[pos]; /* skip xN[q] */ if (j == q) continue; /* compute t */ t = trow_vec[j] / pivot; /* compute inner product s = N'[j] * u */ k = head[m+j]; /* x[k] = xN[j] */ if (k <= m) s = u[k]; else { s = 0.0; beg = A_ptr[k-m]; end = A_ptr[k-m+1]; for (ptr = beg; ptr < end; ptr++) s -= A_val[ptr] * u[A_ind[ptr]]; } /* compute gamma[k] for the adjacent basis */ t1 = gamma[j] + t * t * gamma_q + 2.0 * t * s; t2 = (refsp[k] ? 1.0 : 0.0) + delta_q * t * t; gamma[j] = (t1 >= t2 ? t1 : t2); if (gamma[j] < DBL_EPSILON) gamma[j] = DBL_EPSILON; } /* compute gamma[q] for the adjacent basis */ if (type[head[p]] == GLP_FX) gamma[q] = 1.0; else { gamma[q] = gamma_q / (pivot * pivot); if (gamma[q] < DBL_EPSILON) gamma[q] = DBL_EPSILON; } return; } /*********************************************************************** * err_in_bbar - compute maximal relative error in primal solution * * This routine returns maximal relative error: * * max |beta[i] - bbar[i]| / (1 + |beta[i]|), * * where beta and bbar are, respectively, directly computed and the * current (updated) values of basic variables. * * NOTE: The routine is intended only for debugginig purposes. */ static double err_in_bbar(struct csa *csa) { int m = csa->m; double *bbar = csa->bbar; int i; double e, emax, *beta; beta = xcalloc(1+m, sizeof(double)); eval_beta(csa, beta); emax = 0.0; for (i = 1; i <= m; i++) { e = fabs(beta[i] - bbar[i]) / (1.0 + fabs(beta[i])); if (emax < e) emax = e; } xfree(beta); return emax; } /*********************************************************************** * err_in_cbar - compute maximal relative error in dual solution * * This routine returns maximal relative error: * * max |cost[j] - cbar[j]| / (1 + |cost[j]|), * * where cost and cbar are, respectively, directly computed and the * current (updated) reduced costs of non-basic non-fixed variables. * * NOTE: The routine is intended only for debugginig purposes. */ static double err_in_cbar(struct csa *csa) { int m = csa->m; int n = csa->n; char *stat = csa->stat; double *cbar = csa->cbar; int j; double e, emax, cost, *pi; pi = xcalloc(1+m, sizeof(double)); eval_pi(csa, pi); emax = 0.0; for (j = 1; j <= n; j++) { if (stat[j] == GLP_NS) continue; cost = eval_cost(csa, pi, j); e = fabs(cost - cbar[j]) / (1.0 + fabs(cost)); if (emax < e) emax = e; } xfree(pi); return emax; } /*********************************************************************** * err_in_gamma - compute maximal relative error in steepest edge cff. * * This routine returns maximal relative error: * * max |gamma'[j] - gamma[j]| / (1 + |gamma'[j]), * * where gamma'[j] and gamma[j] are, respectively, directly computed * and the current (updated) steepest edge coefficients for non-basic * non-fixed variable x[j]. * * NOTE: The routine is intended only for debugginig purposes. */ static double err_in_gamma(struct csa *csa) { int n = csa->n; char *stat = csa->stat; double *gamma = csa->gamma; int j; double e, emax, temp; emax = 0.0; for (j = 1; j <= n; j++) { if (stat[j] == GLP_NS) { xassert(gamma[j] == 1.0); continue; } temp = eval_gamma(csa, j); e = fabs(temp - gamma[j]) / (1.0 + fabs(temp)); if (emax < e) emax = e; } return emax; } /*********************************************************************** * change_basis - change basis header * * This routine changes the basis header to make it corresponding to * the adjacent basis. */ static void change_basis(struct csa *csa) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; char *type = csa->type; #endif int *head = csa->head; char *stat = csa->stat; int q = csa->q; int p = csa->p; int p_stat = csa->p_stat; int k; #ifdef GLP_DEBUG xassert(1 <= q && q <= n); #endif if (p < 0) { /* xN[q] goes to its opposite bound */ #ifdef GLP_DEBUG k = head[m+q]; /* x[k] = xN[q] */ xassert(1 <= k && k <= m+n); xassert(type[k] == GLP_DB); #endif switch (stat[q]) { case GLP_NL: /* xN[q] increases */ stat[q] = GLP_NU; break; case GLP_NU: /* xN[q] decreases */ stat[q] = GLP_NL; break; default: xassert(stat != stat); } } else { /* xB[p] leaves the basis, xN[q] enters the basis */ #ifdef GLP_DEBUG xassert(1 <= p && p <= m); k = head[p]; /* x[k] = xB[p] */ switch (p_stat) { case GLP_NL: /* xB[p] goes to its lower bound */ xassert(type[k] == GLP_LO || type[k] == GLP_DB); break; case GLP_NU: /* xB[p] goes to its upper bound */ xassert(type[k] == GLP_UP || type[k] == GLP_DB); break; case GLP_NS: /* xB[p] goes to its fixed value */ xassert(type[k] == GLP_NS); break; default: xassert(p_stat != p_stat); } #endif /* xB[p] <-> xN[q] */ k = head[p], head[p] = head[m+q], head[m+q] = k; stat[q] = (char)p_stat; } return; } /*********************************************************************** * set_aux_obj - construct auxiliary objective function * * The auxiliary objective function is a separable piecewise linear * convex function, which is the sum of primal infeasibilities: * * z = t[1] + ... + t[m+n] -> minimize, * * where: * * / lb[k] - x[k], if x[k] < lb[k] * | * t[k] = < 0, if lb[k] <= x[k] <= ub[k] * | * \ x[k] - ub[k], if x[k] > ub[k] * * This routine computes objective coefficients for the current basis * and returns the number of non-zero terms t[k]. */ static int set_aux_obj(struct csa *csa, double tol_bnd) { int m = csa->m; int n = csa->n; char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; double *coef = csa->coef; int *head = csa->head; double *bbar = csa->bbar; int i, k, cnt = 0; double eps; /* use a bit more restrictive tolerance */ tol_bnd *= 0.90; /* clear all objective coefficients */ for (k = 1; k <= m+n; k++) coef[k] = 0.0; /* walk through the list of basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ if (type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX) { /* x[k] has lower bound */ eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); if (bbar[i] < lb[k] - eps) { /* and violates it */ coef[k] = -1.0; cnt++; } } if (type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX) { /* x[k] has upper bound */ eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); if (bbar[i] > ub[k] + eps) { /* and violates it */ coef[k] = +1.0; cnt++; } } } return cnt; } /*********************************************************************** * set_orig_obj - restore original objective function * * This routine assigns scaled original objective coefficients to the * working objective function. */ static void set_orig_obj(struct csa *csa) { int m = csa->m; int n = csa->n; double *coef = csa->coef; double *obj = csa->obj; double zeta = csa->zeta; int i, j; for (i = 1; i <= m; i++) coef[i] = 0.0; for (j = 1; j <= n; j++) coef[m+j] = zeta * obj[j]; return; } /*********************************************************************** * check_stab - check numerical stability of basic solution * * If the current basic solution is primal feasible (or pseudo feasible * on phase I) within a tolerance, this routine returns zero, otherwise * it returns non-zero. */ static int check_stab(struct csa *csa, double tol_bnd) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; double *coef = csa->coef; int *head = csa->head; int phase = csa->phase; double *bbar = csa->bbar; int i, k; double eps; /* walk through the list of basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (phase == 1 && coef[k] < 0.0) { /* x[k] must not be greater than its lower bound */ #ifdef GLP_DEBUG xassert(type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX); #endif eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); if (bbar[i] > lb[k] + eps) return 1; } else if (phase == 1 && coef[k] > 0.0) { /* x[k] must not be less than its upper bound */ #ifdef GLP_DEBUG xassert(type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX); #endif eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); if (bbar[i] < ub[k] - eps) return 1; } else { /* either phase = 1 and coef[k] = 0, or phase = 2 */ if (type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX) { /* x[k] must not be less than its lower bound */ eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); if (bbar[i] < lb[k] - eps) return 1; } if (type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX) { /* x[k] must not be greater then its upper bound */ eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); if (bbar[i] > ub[k] + eps) return 1; } } } /* basic solution is primal feasible within a tolerance */ return 0; } /*********************************************************************** * check_feas - check primal feasibility of basic solution * * If the current basic solution is primal feasible within a tolerance, * this routine returns zero, otherwise it returns non-zero. */ static int check_feas(struct csa *csa, double tol_bnd) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; char *type = csa->type; #endif double *lb = csa->lb; double *ub = csa->ub; double *coef = csa->coef; int *head = csa->head; double *bbar = csa->bbar; int i, k; double eps; xassert(csa->phase == 1); /* walk through the list of basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (coef[k] < 0.0) { /* check if x[k] still violates its lower bound */ #ifdef GLP_DEBUG xassert(type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX); #endif eps = tol_bnd * (1.0 + kappa * fabs(lb[k])); if (bbar[i] < lb[k] - eps) return 1; } else if (coef[k] > 0.0) { /* check if x[k] still violates its upper bound */ #ifdef GLP_DEBUG xassert(type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX); #endif eps = tol_bnd * (1.0 + kappa * fabs(ub[k])); if (bbar[i] > ub[k] + eps) return 1; } } /* basic solution is primal feasible within a tolerance */ return 0; } /*********************************************************************** * eval_obj - compute original objective function * * This routine computes the current value of the original objective * function. */ static double eval_obj(struct csa *csa) { int m = csa->m; int n = csa->n; double *obj = csa->obj; int *head = csa->head; double *bbar = csa->bbar; int i, j, k; double sum; sum = obj[0]; /* walk through the list of basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k > m) sum += obj[k-m] * bbar[i]; } /* walk through the list of non-basic variables */ for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k > m) sum += obj[k-m] * get_xN(csa, j); } return sum; } /*********************************************************************** * display - display the search progress * * This routine displays some information about the search progress * that includes: * * the search phase; * * the number of simplex iterations performed by the solver; * * the original objective value; * * the sum of (scaled) primal infeasibilities; * * the number of basic fixed variables. */ static void display(struct csa *csa, const glp_smcp *parm, int spec) { int m = csa->m; #ifdef GLP_DEBUG int n = csa->n; #endif char *type = csa->type; double *lb = csa->lb; double *ub = csa->ub; int phase = csa->phase; int *head = csa->head; double *bbar = csa->bbar; int i, k, cnt; double sum; if (parm->msg_lev < GLP_MSG_ON) goto skip; if (parm->out_dly > 0 && 1000.0 * xdifftime(xtime(), csa->tm_beg) < parm->out_dly) goto skip; if (csa->it_cnt == csa->it_dpy) goto skip; if (!spec && csa->it_cnt % parm->out_frq != 0) goto skip; /* compute the sum of primal infeasibilities and determine the number of basic fixed variables */ sum = 0.0, cnt = 0; for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (type[k] == GLP_LO || type[k] == GLP_DB || type[k] == GLP_FX) { /* x[k] has lower bound */ if (bbar[i] < lb[k]) sum += (lb[k] - bbar[i]); } if (type[k] == GLP_UP || type[k] == GLP_DB || type[k] == GLP_FX) { /* x[k] has upper bound */ if (bbar[i] > ub[k]) sum += (bbar[i] - ub[k]); } if (type[k] == GLP_FX) cnt++; } xprintf("%c%6d: obj = %17.9e infeas = %10.3e (%d)\n", phase == 1 ? ' ' : '*', csa->it_cnt, eval_obj(csa), sum, cnt); csa->it_dpy = csa->it_cnt; skip: return; } /*********************************************************************** * store_sol - store basic solution back to the problem object * * This routine stores basic solution components back to the problem * object. */ static void store_sol(struct csa *csa, glp_prob *lp, int p_stat, int d_stat, int ray) { int m = csa->m; int n = csa->n; double zeta = csa->zeta; int *head = csa->head; char *stat = csa->stat; double *bbar = csa->bbar; double *cbar = csa->cbar; int i, j, k; #ifdef GLP_DEBUG xassert(lp->m == m); xassert(lp->n == n); #endif /* basis factorization */ #ifdef GLP_DEBUG xassert(!lp->valid && lp->bfd == NULL); xassert(csa->valid && csa->bfd != NULL); #endif lp->valid = 1, csa->valid = 0; lp->bfd = csa->bfd, csa->bfd = NULL; memcpy(&lp->head[1], &head[1], m * sizeof(int)); /* basic solution status */ lp->pbs_stat = p_stat; lp->dbs_stat = d_stat; /* objective function value */ lp->obj_val = eval_obj(csa); /* simplex iteration count */ lp->it_cnt = csa->it_cnt; /* unbounded ray */ lp->some = ray; /* basic variables */ for (i = 1; i <= m; i++) { k = head[i]; /* x[k] = xB[i] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { GLPROW *row = lp->row[k]; row->stat = GLP_BS; row->bind = i; row->prim = bbar[i] / row->rii; row->dual = 0.0; } else { GLPCOL *col = lp->col[k-m]; col->stat = GLP_BS; col->bind = i; col->prim = bbar[i] * col->sjj; col->dual = 0.0; } } /* non-basic variables */ for (j = 1; j <= n; j++) { k = head[m+j]; /* x[k] = xN[j] */ #ifdef GLP_DEBUG xassert(1 <= k && k <= m+n); #endif if (k <= m) { GLPROW *row = lp->row[k]; row->stat = stat[j]; row->bind = 0; #if 0 row->prim = get_xN(csa, j) / row->rii; #else switch (stat[j]) { case GLP_NL: row->prim = row->lb; break; case GLP_NU: row->prim = row->ub; break; case GLP_NF: row->prim = 0.0; break; case GLP_NS: row->prim = row->lb; break; default: xassert(stat != stat); } #endif row->dual = (cbar[j] * row->rii) / zeta; } else { GLPCOL *col = lp->col[k-m]; col->stat = stat[j]; col->bind = 0; #if 0 col->prim = get_xN(csa, j) * col->sjj; #else switch (stat[j]) { case GLP_NL: col->prim = col->lb; break; case GLP_NU: col->prim = col->ub; break; case GLP_NF: col->prim = 0.0; break; case GLP_NS: col->prim = col->lb; break; default: xassert(stat != stat); } #endif col->dual = (cbar[j] / col->sjj) / zeta; } } return; } /*********************************************************************** * free_csa - deallocate common storage area * * This routine frees all the memory allocated to arrays in the common * storage area (CSA). */ static void free_csa(struct csa *csa) { xfree(csa->type); xfree(csa->lb); xfree(csa->ub); xfree(csa->coef); xfree(csa->obj); xfree(csa->A_ptr); xfree(csa->A_ind); xfree(csa->A_val); xfree(csa->head); xfree(csa->stat); xfree(csa->N_ptr); xfree(csa->N_len); xfree(csa->N_ind); xfree(csa->N_val); xfree(csa->bbar); xfree(csa->cbar); xfree(csa->refsp); xfree(csa->gamma); xfree(csa->tcol_ind); xfree(csa->tcol_vec); xfree(csa->trow_ind); xfree(csa->trow_vec); xfree(csa->work1); xfree(csa->work2); xfree(csa->work3); xfree(csa->work4); xfree(csa); return; } /*********************************************************************** * spx_primal - core LP solver based on the primal simplex method * * SYNOPSIS * * #include "glpspx.h" * int spx_primal(glp_prob *lp, const glp_smcp *parm); * * DESCRIPTION * * The routine spx_primal is a core LP solver based on the two-phase * primal simplex method. * * RETURNS * * 0 LP instance has been successfully solved. * * GLP_EITLIM * Iteration limit has been exhausted. * * GLP_ETMLIM * Time limit has been exhausted. * * GLP_EFAIL * The solver failed to solve LP instance. */ int spx_primal(glp_prob *lp, const glp_smcp *parm) { struct csa *csa; int binv_st = 2; /* status of basis matrix factorization: 0 - invalid; 1 - just computed; 2 - updated */ int bbar_st = 0; /* status of primal values of basic variables: 0 - invalid; 1 - just computed; 2 - updated */ int cbar_st = 0; /* status of reduced costs of non-basic variables: 0 - invalid; 1 - just computed; 2 - updated */ int rigorous = 0; /* rigorous mode flag; this flag is used to enable iterative refinement on computing pivot rows and columns of the simplex table */ int check = 0; int p_stat, d_stat, ret; /* allocate and initialize the common storage area */ csa = alloc_csa(lp); init_csa(csa, lp); if (parm->msg_lev >= GLP_MSG_DBG) xprintf("Objective scale factor = %g\n", csa->zeta); loop: /* main loop starts here */ /* compute factorization of the basis matrix */ if (binv_st == 0) { ret = invert_B(csa); if (ret != 0) { if (parm->msg_lev >= GLP_MSG_ERR) { xprintf("Error: unable to factorize the basis matrix (%d" ")\n", ret); xprintf("Sorry, basis recovery procedure not implemented" " yet\n"); } xassert(!lp->valid && lp->bfd == NULL); lp->bfd = csa->bfd, csa->bfd = NULL; lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; lp->obj_val = 0.0; lp->it_cnt = csa->it_cnt; lp->some = 0; ret = GLP_EFAIL; goto done; } csa->valid = 1; binv_st = 1; /* just computed */ /* invalidate basic solution components */ bbar_st = cbar_st = 0; } /* compute primal values of basic variables */ if (bbar_st == 0) { eval_bbar(csa); bbar_st = 1; /* just computed */ /* determine the search phase, if not determined yet */ if (csa->phase == 0) { if (set_aux_obj(csa, parm->tol_bnd) > 0) { /* current basic solution is primal infeasible */ /* start to minimize the sum of infeasibilities */ csa->phase = 1; } else { /* current basic solution is primal feasible */ /* start to minimize the original objective function */ set_orig_obj(csa); csa->phase = 2; } xassert(check_stab(csa, parm->tol_bnd) == 0); /* working objective coefficients have been changed, so invalidate reduced costs */ cbar_st = 0; display(csa, parm, 1); } /* make sure that the current basic solution remains primal feasible (or pseudo feasible on phase I) */ if (check_stab(csa, parm->tol_bnd)) { /* there are excessive bound violations due to round-off errors */ if (parm->msg_lev >= GLP_MSG_ERR) xprintf("Warning: numerical instability (primal simplex," " phase %s)\n", csa->phase == 1 ? "I" : "II"); /* restart the search */ csa->phase = 0; binv_st = 0; rigorous = 5; goto loop; } } xassert(csa->phase == 1 || csa->phase == 2); /* on phase I we do not need to wait until the current basic solution becomes dual feasible; it is sufficient to make sure that no basic variable violates its bounds */ if (csa->phase == 1 && !check_feas(csa, parm->tol_bnd)) { /* the current basis is primal feasible; switch to phase II */ csa->phase = 2; set_orig_obj(csa); cbar_st = 0; display(csa, parm, 1); } /* compute reduced costs of non-basic variables */ if (cbar_st == 0) { eval_cbar(csa); cbar_st = 1; /* just computed */ } /* redefine the reference space, if required */ switch (parm->pricing) { case GLP_PT_STD: break; case GLP_PT_PSE: if (csa->refct == 0) reset_refsp(csa); break; default: xassert(parm != parm); } /* at this point the basis factorization and all basic solution components are valid */ xassert(binv_st && bbar_st && cbar_st); /* check accuracy of current basic solution components (only for debugging) */ if (check) { double e_bbar = err_in_bbar(csa); double e_cbar = err_in_cbar(csa); double e_gamma = (parm->pricing == GLP_PT_PSE ? err_in_gamma(csa) : 0.0); xprintf("e_bbar = %10.3e; e_cbar = %10.3e; e_gamma = %10.3e\n", e_bbar, e_cbar, e_gamma); xassert(e_bbar <= 1e-5 && e_cbar <= 1e-5 && e_gamma <= 1e-3); } /* check if the iteration limit has been exhausted */ if (parm->it_lim < INT_MAX && csa->it_cnt - csa->it_beg >= parm->it_lim) { if (bbar_st != 1 || csa->phase == 2 && cbar_st != 1) { if (bbar_st != 1) bbar_st = 0; if (csa->phase == 2 && cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); if (parm->msg_lev >= GLP_MSG_ALL) xprintf("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED\n"); switch (csa->phase) { case 1: p_stat = GLP_INFEAS; set_orig_obj(csa); eval_cbar(csa); break; case 2: p_stat = GLP_FEAS; break; default: xassert(csa != csa); } chuzc(csa, parm->tol_dj); d_stat = (csa->q == 0 ? GLP_FEAS : GLP_INFEAS); store_sol(csa, lp, p_stat, d_stat, 0); ret = GLP_EITLIM; goto done; } /* check if the time limit has been exhausted */ if (parm->tm_lim < INT_MAX && 1000.0 * xdifftime(xtime(), csa->tm_beg) >= parm->tm_lim) { if (bbar_st != 1 || csa->phase == 2 && cbar_st != 1) { if (bbar_st != 1) bbar_st = 0; if (csa->phase == 2 && cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); if (parm->msg_lev >= GLP_MSG_ALL) xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); switch (csa->phase) { case 1: p_stat = GLP_INFEAS; set_orig_obj(csa); eval_cbar(csa); break; case 2: p_stat = GLP_FEAS; break; default: xassert(csa != csa); } chuzc(csa, parm->tol_dj); d_stat = (csa->q == 0 ? GLP_FEAS : GLP_INFEAS); store_sol(csa, lp, p_stat, d_stat, 0); ret = GLP_ETMLIM; goto done; } /* display the search progress */ display(csa, parm, 0); /* choose non-basic variable xN[q] */ chuzc(csa, parm->tol_dj); if (csa->q == 0) { if (bbar_st != 1 || cbar_st != 1) { if (bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; goto loop; } display(csa, parm, 1); switch (csa->phase) { case 1: if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); p_stat = GLP_NOFEAS; set_orig_obj(csa); eval_cbar(csa); chuzc(csa, parm->tol_dj); d_stat = (csa->q == 0 ? GLP_FEAS : GLP_INFEAS); break; case 2: if (parm->msg_lev >= GLP_MSG_ALL) xprintf("OPTIMAL SOLUTION FOUND\n"); p_stat = d_stat = GLP_FEAS; break; default: xassert(csa != csa); } store_sol(csa, lp, p_stat, d_stat, 0); ret = 0; goto done; } /* compute pivot column of the simplex table */ eval_tcol(csa); if (rigorous) refine_tcol(csa); sort_tcol(csa, parm->tol_piv); /* check accuracy of the reduced cost of xN[q] */ { double d1 = csa->cbar[csa->q]; /* less accurate */ double d2 = reeval_cost(csa); /* more accurate */ xassert(d1 != 0.0); if (fabs(d1 - d2) > 1e-5 * (1.0 + fabs(d2)) || !(d1 < 0.0 && d2 < 0.0 || d1 > 0.0 && d2 > 0.0)) { if (parm->msg_lev >= GLP_MSG_DBG) xprintf("d1 = %.12g; d2 = %.12g\n", d1, d2); if (cbar_st != 1 || !rigorous) { if (cbar_st != 1) cbar_st = 0; rigorous = 5; goto loop; } } /* replace cbar[q] by more accurate value keeping its sign */ if (d1 > 0.0) csa->cbar[csa->q] = (d2 > 0.0 ? d2 : +DBL_EPSILON); else csa->cbar[csa->q] = (d2 < 0.0 ? d2 : -DBL_EPSILON); } /* choose basic variable xB[p] */ switch (parm->r_test) { case GLP_RT_STD: chuzr(csa, 0.0); break; case GLP_RT_HAR: chuzr(csa, 0.30 * parm->tol_bnd); break; default: xassert(parm != parm); } if (csa->p == 0) { if (bbar_st != 1 || cbar_st != 1 || !rigorous) { if (bbar_st != 1) bbar_st = 0; if (cbar_st != 1) cbar_st = 0; rigorous = 1; goto loop; } display(csa, parm, 1); switch (csa->phase) { case 1: if (parm->msg_lev >= GLP_MSG_ERR) xprintf("Error: unable to choose basic variable on ph" "ase I\n"); xassert(!lp->valid && lp->bfd == NULL); lp->bfd = csa->bfd, csa->bfd = NULL; lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; lp->obj_val = 0.0; lp->it_cnt = csa->it_cnt; lp->some = 0; ret = GLP_EFAIL; break; case 2: if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n"); store_sol(csa, lp, GLP_FEAS, GLP_NOFEAS, csa->head[csa->m+csa->q]); ret = 0; break; default: xassert(csa != csa); } goto done; } /* check if the pivot element is acceptable */ if (csa->p > 0) { double piv = csa->tcol_vec[csa->p]; double eps = 1e-5 * (1.0 + 0.01 * csa->tcol_max); if (fabs(piv) < eps) { if (parm->msg_lev >= GLP_MSG_DBG) xprintf("piv = %.12g; eps = %g\n", piv, eps); if (!rigorous) { rigorous = 5; goto loop; } } } /* now xN[q] and xB[p] have been chosen anyhow */ /* compute pivot row of the simplex table */ if (csa->p > 0) { double *rho = csa->work4; eval_rho(csa, rho); if (rigorous) refine_rho(csa, rho); eval_trow(csa, rho); } /* accuracy check based on the pivot element */ if (csa->p > 0) { double piv1 = csa->tcol_vec[csa->p]; /* more accurate */ double piv2 = csa->trow_vec[csa->q]; /* less accurate */ xassert(piv1 != 0.0); if (fabs(piv1 - piv2) > 1e-8 * (1.0 + fabs(piv1)) || !(piv1 > 0.0 && piv2 > 0.0 || piv1 < 0.0 && piv2 < 0.0)) { if (parm->msg_lev >= GLP_MSG_DBG) xprintf("piv1 = %.12g; piv2 = %.12g\n", piv1, piv2); if (binv_st != 1 || !rigorous) { if (binv_st != 1) binv_st = 0; rigorous = 5; goto loop; } /* use more accurate version in the pivot row */ if (csa->trow_vec[csa->q] == 0.0) { csa->trow_nnz++; xassert(csa->trow_nnz <= csa->n); csa->trow_ind[csa->trow_nnz] = csa->q; } csa->trow_vec[csa->q] = piv1; } } /* update primal values of basic variables */ update_bbar(csa); bbar_st = 2; /* updated */ /* update reduced costs of non-basic variables */ if (csa->p > 0) { update_cbar(csa); cbar_st = 2; /* updated */ /* on phase I objective coefficient of xB[p] in the adjacent basis becomes zero */ if (csa->phase == 1) { int k = csa->head[csa->p]; /* x[k] = xB[p] -> xN[q] */ csa->cbar[csa->q] -= csa->coef[k]; csa->coef[k] = 0.0; } } /* update steepest edge coefficients */ if (csa->p > 0) { switch (parm->pricing) { case GLP_PT_STD: break; case GLP_PT_PSE: if (csa->refct > 0) update_gamma(csa); break; default: xassert(parm != parm); } } /* update factorization of the basis matrix */ if (csa->p > 0) { ret = update_B(csa, csa->p, csa->head[csa->m+csa->q]); if (ret == 0) binv_st = 2; /* updated */ else { csa->valid = 0; binv_st = 0; /* invalid */ } } /* update matrix N */ if (csa->p > 0) { del_N_col(csa, csa->q, csa->head[csa->m+csa->q]); if (csa->type[csa->head[csa->p]] != GLP_FX) add_N_col(csa, csa->q, csa->head[csa->p]); } /* change the basis header */ change_basis(csa); /* iteration complete */ csa->it_cnt++; if (rigorous > 0) rigorous--; goto loop; done: /* deallocate the common storage area */ free_csa(csa); /* return to the calling program */ return ret; } /* eof */ igraph/src/glpapi13.c0000644000176000001440000005445312325527073014150 0ustar ripleyusers/* glpapi13.c (branch-and-bound interface routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpios.h" /*********************************************************************** * NAME * * glp_ios_reason - determine reason for calling the callback routine * * SYNOPSIS * * glp_ios_reason(glp_tree *tree); * * RETURNS * * The routine glp_ios_reason returns a code, which indicates why the * user-defined callback routine is being called. */ int glp_ios_reason(glp_tree *tree) { return tree->reason; } /*********************************************************************** * NAME * * glp_ios_get_prob - access the problem object * * SYNOPSIS * * glp_prob *glp_ios_get_prob(glp_tree *tree); * * DESCRIPTION * * The routine glp_ios_get_prob can be called from the user-defined * callback routine to access the problem object, which is used by the * MIP solver. It is the original problem object passed to the routine * glp_intopt if the MIP presolver is not used; otherwise it is an * internal problem object built by the presolver. If the current * subproblem exists, LP segment of the problem object corresponds to * its LP relaxation. * * RETURNS * * The routine glp_ios_get_prob returns a pointer to the problem object * used by the MIP solver. */ glp_prob *glp_ios_get_prob(glp_tree *tree) { return tree->mip; } /*********************************************************************** * NAME * * glp_ios_tree_size - determine size of the branch-and-bound tree * * SYNOPSIS * * void glp_ios_tree_size(glp_tree *tree, int *a_cnt, int *n_cnt, * int *t_cnt); * * DESCRIPTION * * The routine glp_ios_tree_size stores the following three counts which * characterize the current size of the branch-and-bound tree: * * a_cnt is the current number of active nodes, i.e. the current size of * the active list; * * n_cnt is the current number of all (active and inactive) nodes; * * t_cnt is the total number of nodes including those which have been * already removed from the tree. This count is increased whenever * a new node appears in the tree and never decreased. * * If some of the parameters a_cnt, n_cnt, t_cnt is a null pointer, the * corresponding count is not stored. */ void glp_ios_tree_size(glp_tree *tree, int *a_cnt, int *n_cnt, int *t_cnt) { if (a_cnt != NULL) *a_cnt = tree->a_cnt; if (n_cnt != NULL) *n_cnt = tree->n_cnt; if (t_cnt != NULL) *t_cnt = tree->t_cnt; return; } /*********************************************************************** * NAME * * glp_ios_curr_node - determine current active subproblem * * SYNOPSIS * * int glp_ios_curr_node(glp_tree *tree); * * RETURNS * * The routine glp_ios_curr_node returns the reference number of the * current active subproblem. However, if the current subproblem does * not exist, the routine returns zero. */ int glp_ios_curr_node(glp_tree *tree) { IOSNPD *node; /* obtain pointer to the current subproblem */ node = tree->curr; /* return its reference number */ return node == NULL ? 0 : node->p; } /*********************************************************************** * NAME * * glp_ios_next_node - determine next active subproblem * * SYNOPSIS * * int glp_ios_next_node(glp_tree *tree, int p); * * RETURNS * * If the parameter p is zero, the routine glp_ios_next_node returns * the reference number of the first active subproblem. However, if the * tree is empty, zero is returned. * * If the parameter p is not zero, it must specify the reference number * of some active subproblem, in which case the routine returns the * reference number of the next active subproblem. However, if there is * no next active subproblem in the list, zero is returned. * * All subproblems in the active list are ordered chronologically, i.e. * subproblem A precedes subproblem B if A was created before B. */ int glp_ios_next_node(glp_tree *tree, int p) { IOSNPD *node; if (p == 0) { /* obtain pointer to the first active subproblem */ node = tree->head; } else { /* obtain pointer to the specified subproblem */ if (!(1 <= p && p <= tree->nslots)) err: xerror("glp_ios_next_node: p = %d; invalid subproblem refer" "ence number\n", p); node = tree->slot[p].node; if (node == NULL) goto err; /* the specified subproblem must be active */ if (node->count != 0) xerror("glp_ios_next_node: p = %d; subproblem not in the ac" "tive list\n", p); /* obtain pointer to the next active subproblem */ node = node->next; } /* return the reference number */ return node == NULL ? 0 : node->p; } /*********************************************************************** * NAME * * glp_ios_prev_node - determine previous active subproblem * * SYNOPSIS * * int glp_ios_prev_node(glp_tree *tree, int p); * * RETURNS * * If the parameter p is zero, the routine glp_ios_prev_node returns * the reference number of the last active subproblem. However, if the * tree is empty, zero is returned. * * If the parameter p is not zero, it must specify the reference number * of some active subproblem, in which case the routine returns the * reference number of the previous active subproblem. However, if there * is no previous active subproblem in the list, zero is returned. * * All subproblems in the active list are ordered chronologically, i.e. * subproblem A precedes subproblem B if A was created before B. */ int glp_ios_prev_node(glp_tree *tree, int p) { IOSNPD *node; if (p == 0) { /* obtain pointer to the last active subproblem */ node = tree->tail; } else { /* obtain pointer to the specified subproblem */ if (!(1 <= p && p <= tree->nslots)) err: xerror("glp_ios_prev_node: p = %d; invalid subproblem refer" "ence number\n", p); node = tree->slot[p].node; if (node == NULL) goto err; /* the specified subproblem must be active */ if (node->count != 0) xerror("glp_ios_prev_node: p = %d; subproblem not in the ac" "tive list\n", p); /* obtain pointer to the previous active subproblem */ node = node->prev; } /* return the reference number */ return node == NULL ? 0 : node->p; } /*********************************************************************** * NAME * * glp_ios_up_node - determine parent subproblem * * SYNOPSIS * * int glp_ios_up_node(glp_tree *tree, int p); * * RETURNS * * The parameter p must specify the reference number of some (active or * inactive) subproblem, in which case the routine iet_get_up_node * returns the reference number of its parent subproblem. However, if * the specified subproblem is the root of the tree and, therefore, has * no parent, the routine returns zero. */ int glp_ios_up_node(glp_tree *tree, int p) { IOSNPD *node; /* obtain pointer to the specified subproblem */ if (!(1 <= p && p <= tree->nslots)) err: xerror("glp_ios_up_node: p = %d; invalid subproblem reference " "number\n", p); node = tree->slot[p].node; if (node == NULL) goto err; /* obtain pointer to the parent subproblem */ node = node->up; /* return the reference number */ return node == NULL ? 0 : node->p; } /*********************************************************************** * NAME * * glp_ios_node_level - determine subproblem level * * SYNOPSIS * * int glp_ios_node_level(glp_tree *tree, int p); * * RETURNS * * The routine glp_ios_node_level returns the level of the subproblem, * whose reference number is p, in the branch-and-bound tree. (The root * subproblem has level 0, and the level of any other subproblem is the * level of its parent plus one.) */ int glp_ios_node_level(glp_tree *tree, int p) { IOSNPD *node; /* obtain pointer to the specified subproblem */ if (!(1 <= p && p <= tree->nslots)) err: xerror("glp_ios_node_level: p = %d; invalid subproblem referen" "ce number\n", p); node = tree->slot[p].node; if (node == NULL) goto err; /* return the node level */ return node->level; } /*********************************************************************** * NAME * * glp_ios_node_bound - determine subproblem local bound * * SYNOPSIS * * double glp_ios_node_bound(glp_tree *tree, int p); * * RETURNS * * The routine glp_ios_node_bound returns the local bound for (active or * inactive) subproblem, whose reference number is p. * * COMMENTS * * The local bound for subproblem p is an lower (minimization) or upper * (maximization) bound for integer optimal solution to this subproblem * (not to the original problem). This bound is local in the sense that * only subproblems in the subtree rooted at node p cannot have better * integer feasible solutions. * * On creating a subproblem (due to the branching step) its local bound * is inherited from its parent and then may get only stronger (never * weaker). For the root subproblem its local bound is initially set to * -DBL_MAX (minimization) or +DBL_MAX (maximization) and then improved * as the root LP relaxation has been solved. * * Note that the local bound is not necessarily the optimal objective * value to corresponding LP relaxation; it may be stronger. */ double glp_ios_node_bound(glp_tree *tree, int p) { IOSNPD *node; /* obtain pointer to the specified subproblem */ if (!(1 <= p && p <= tree->nslots)) err: xerror("glp_ios_node_bound: p = %d; invalid subproblem referen" "ce number\n", p); node = tree->slot[p].node; if (node == NULL) goto err; /* return the node local bound */ return node->bound; } /*********************************************************************** * NAME * * glp_ios_best_node - find active subproblem with best local bound * * SYNOPSIS * * int glp_ios_best_node(glp_tree *tree); * * RETURNS * * The routine glp_ios_best_node returns the reference number of the * active subproblem, whose local bound is best (i.e. smallest in case * of minimization or largest in case of maximization). However, if the * tree is empty, the routine returns zero. * * COMMENTS * * The best local bound is an lower (minimization) or upper * (maximization) bound for integer optimal solution to the original * MIP problem. */ int glp_ios_best_node(glp_tree *tree) { return ios_best_node(tree); } /*********************************************************************** * NAME * * glp_ios_mip_gap - compute relative MIP gap * * SYNOPSIS * * double glp_ios_mip_gap(glp_tree *tree); * * DESCRIPTION * * The routine glp_ios_mip_gap computes the relative MIP gap with the * following formula: * * gap = |best_mip - best_bnd| / (|best_mip| + DBL_EPSILON), * * where best_mip is the best integer feasible solution found so far, * best_bnd is the best (global) bound. If no integer feasible solution * has been found yet, gap is set to DBL_MAX. * * RETURNS * * The routine glp_ios_mip_gap returns the relative MIP gap. */ double glp_ios_mip_gap(glp_tree *tree) { return ios_relative_gap(tree); } /*********************************************************************** * NAME * * glp_ios_node_data - access subproblem application-specific data * * SYNOPSIS * * void *glp_ios_node_data(glp_tree *tree, int p); * * DESCRIPTION * * The routine glp_ios_node_data allows the application accessing a * memory block allocated for the subproblem (which may be active or * inactive), whose reference number is p. * * The size of the block is defined by the control parameter cb_size * passed to the routine glp_intopt. The block is initialized by binary * zeros on creating corresponding subproblem, and its contents is kept * until the subproblem will be removed from the tree. * * The application may use these memory blocks to store specific data * for each subproblem. * * RETURNS * * The routine glp_ios_node_data returns a pointer to the memory block * for the specified subproblem. Note that if cb_size = 0, the routine * returns a null pointer. */ void *glp_ios_node_data(glp_tree *tree, int p) { IOSNPD *node; /* obtain pointer to the specified subproblem */ if (!(1 <= p && p <= tree->nslots)) err: xerror("glp_ios_node_level: p = %d; invalid subproblem referen" "ce number\n", p); node = tree->slot[p].node; if (node == NULL) goto err; /* return pointer to the application-specific data */ return node->data; } /*********************************************************************** * NAME * * glp_ios_row_attr - retrieve additional row attributes * * SYNOPSIS * * void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr); * * DESCRIPTION * * The routine glp_ios_row_attr retrieves additional attributes of row * i and stores them in the structure glp_attr. */ void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr) { GLPROW *row; if (!(1 <= i && i <= tree->mip->m)) xerror("glp_ios_row_attr: i = %d; row number out of range\n", i); row = tree->mip->row[i]; attr->level = row->level; attr->origin = row->origin; attr->klass = row->klass; return; } /**********************************************************************/ int glp_ios_pool_size(glp_tree *tree) { /* determine current size of the cut pool */ if (tree->reason != GLP_ICUTGEN) xerror("glp_ios_pool_size: operation not allowed\n"); xassert(tree->local != NULL); return tree->local->size; } /**********************************************************************/ int glp_ios_add_row(glp_tree *tree, const char *name, int klass, int flags, int len, const int ind[], const double val[], int type, double rhs) { /* add row (constraint) to the cut pool */ int num; if (tree->reason != GLP_ICUTGEN) xerror("glp_ios_add_row: operation not allowed\n"); xassert(tree->local != NULL); num = ios_add_row(tree, tree->local, name, klass, flags, len, ind, val, type, rhs); return num; } /**********************************************************************/ void glp_ios_del_row(glp_tree *tree, int i) { /* remove row (constraint) from the cut pool */ if (tree->reason != GLP_ICUTGEN) xerror("glp_ios_del_row: operation not allowed\n"); ios_del_row(tree, tree->local, i); return; } /**********************************************************************/ void glp_ios_clear_pool(glp_tree *tree) { /* remove all rows (constraints) from the cut pool */ if (tree->reason != GLP_ICUTGEN) xerror("glp_ios_clear_pool: operation not allowed\n"); ios_clear_pool(tree, tree->local); return; } /*********************************************************************** * NAME * * glp_ios_can_branch - check if can branch upon specified variable * * SYNOPSIS * * int glp_ios_can_branch(glp_tree *tree, int j); * * RETURNS * * If j-th variable (column) can be used to branch upon, the routine * glp_ios_can_branch returns non-zero, otherwise zero. */ int glp_ios_can_branch(glp_tree *tree, int j) { if (!(1 <= j && j <= tree->mip->n)) xerror("glp_ios_can_branch: j = %d; column number out of range" "\n", j); return tree->non_int[j]; } /*********************************************************************** * NAME * * glp_ios_branch_upon - choose variable to branch upon * * SYNOPSIS * * void glp_ios_branch_upon(glp_tree *tree, int j, int sel); * * DESCRIPTION * * The routine glp_ios_branch_upon can be called from the user-defined * callback routine in response to the reason GLP_IBRANCH to choose a * branching variable, whose ordinal number is j. Should note that only * variables, for which the routine glp_ios_can_branch returns non-zero, * can be used to branch upon. * * The parameter sel is a flag that indicates which branch (subproblem) * should be selected next to continue the search: * * GLP_DN_BRNCH - select down-branch; * GLP_UP_BRNCH - select up-branch; * GLP_NO_BRNCH - use general selection technique. */ void glp_ios_branch_upon(glp_tree *tree, int j, int sel) { if (!(1 <= j && j <= tree->mip->n)) xerror("glp_ios_branch_upon: j = %d; column number out of rang" "e\n", j); if (!(sel == GLP_DN_BRNCH || sel == GLP_UP_BRNCH || sel == GLP_NO_BRNCH)) xerror("glp_ios_branch_upon: sel = %d: invalid branch selectio" "n flag\n", sel); if (!(tree->non_int[j])) xerror("glp_ios_branch_upon: j = %d; variable cannot be used t" "o branch upon\n", j); if (tree->br_var != 0) xerror("glp_ios_branch_upon: branching variable already chosen" "\n"); tree->br_var = j; tree->br_sel = sel; return; } /*********************************************************************** * NAME * * glp_ios_select_node - select subproblem to continue the search * * SYNOPSIS * * void glp_ios_select_node(glp_tree *tree, int p); * * DESCRIPTION * * The routine glp_ios_select_node can be called from the user-defined * callback routine in response to the reason GLP_ISELECT to select an * active subproblem, whose reference number is p. The search will be * continued from the subproblem selected. */ void glp_ios_select_node(glp_tree *tree, int p) { IOSNPD *node; /* obtain pointer to the specified subproblem */ if (!(1 <= p && p <= tree->nslots)) err: xerror("glp_ios_select_node: p = %d; invalid subproblem refere" "nce number\n", p); node = tree->slot[p].node; if (node == NULL) goto err; /* the specified subproblem must be active */ if (node->count != 0) xerror("glp_ios_select_node: p = %d; subproblem not in the act" "ive list\n", p); /* no subproblem must be selected yet */ if (tree->next_p != 0) xerror("glp_ios_select_node: subproblem already selected\n"); /* select the specified subproblem to continue the search */ tree->next_p = p; return; } /*********************************************************************** * NAME * * glp_ios_heur_sol - provide solution found by heuristic * * SYNOPSIS * * int glp_ios_heur_sol(glp_tree *tree, const double x[]); * * DESCRIPTION * * The routine glp_ios_heur_sol can be called from the user-defined * callback routine in response to the reason GLP_IHEUR to provide an * integer feasible solution found by a primal heuristic. * * Primal values of *all* variables (columns) found by the heuristic * should be placed in locations x[1], ..., x[n], where n is the number * of columns in the original problem object. Note that the routine * glp_ios_heur_sol *does not* check primal feasibility of the solution * provided. * * Using the solution passed in the array x the routine computes value * of the objective function. If the objective value is better than the * best known integer feasible solution, the routine computes values of * auxiliary variables (rows) and stores all solution components in the * problem object. * * RETURNS * * If the provided solution is accepted, the routine glp_ios_heur_sol * returns zero. Otherwise, if the provided solution is rejected, the * routine returns non-zero. */ int glp_ios_heur_sol(glp_tree *tree, const double x[]) { glp_prob *mip = tree->mip; int m = tree->orig_m; int n = tree->n; int i, j; double obj; xassert(mip->m >= m); xassert(mip->n == n); /* check values of integer variables and compute value of the objective function */ obj = mip->c0; for (j = 1; j <= n; j++) { GLPCOL *col = mip->col[j]; if (col->kind == GLP_IV) { /* provided value must be integral */ if (x[j] != floor(x[j])) return 1; } obj += col->coef * x[j]; } /* check if the provided solution is better than the best known integer feasible solution */ if (mip->mip_stat == GLP_FEAS) { switch (mip->dir) { case GLP_MIN: if (obj >= tree->mip->mip_obj) return 1; break; case GLP_MAX: if (obj <= tree->mip->mip_obj) return 1; break; default: xassert(mip != mip); } } /* it is better; store it in the problem object */ if (tree->parm->msg_lev >= GLP_MSG_ON) xprintf("Solution found by heuristic: %.12g\n", obj); mip->mip_stat = GLP_FEAS; mip->mip_obj = obj; for (j = 1; j <= n; j++) mip->col[j]->mipx = x[j]; for (i = 1; i <= m; i++) { GLPROW *row = mip->row[i]; GLPAIJ *aij; row->mipx = 0.0; for (aij = row->ptr; aij != NULL; aij = aij->r_next) row->mipx += aij->val * aij->col->mipx; } return 0; } /*********************************************************************** * NAME * * glp_ios_terminate - terminate the solution process. * * SYNOPSIS * * void glp_ios_terminate(glp_tree *tree); * * DESCRIPTION * * The routine glp_ios_terminate sets a flag indicating that the MIP * solver should prematurely terminate the search. */ void glp_ios_terminate(glp_tree *tree) { if (tree->parm->msg_lev >= GLP_MSG_DBG) xprintf("The search is prematurely terminated due to applicati" "on request\n"); tree->stop = 1; return; } /* eof */ igraph/src/gss.c0000644000176000001440000000661712325527073013323 0ustar ripleyusers/* gss.c * * Copyright (C) 2012 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "error.h" #include "gss.h" #include "platform.h" /** * \def PHI * * The golden ratio, i.e. 1+sqrt(5)/2 */ #define PHI 1.618033988749895 /** * \def RESPHI * * Constant defined as 2 - \c PHI */ #define RESPHI 0.3819660112501051 /** * \const _defparam * * Default parameters for the GSS algorithm. */ static const gss_parameter_t _defparam = { /* .epsilon = */ DBL_MIN, /* .on_error = */ GSS_ERROR_STOP }; /** * Stores whether the last optimization run triggered a warning or not. */ static unsigned short int gss_i_warning_flag = 0; void gss_parameter_init(gss_parameter_t *param) { memcpy(param, &_defparam, sizeof(*param)); } unsigned short int gss_get_warning_flag() { return gss_i_warning_flag; } #define TERMINATE { \ if (_min) { \ *(_min) = min; \ } \ if (_fmin) { \ *(_fmin) = fmin; \ } \ } #define EVALUATE(x, fx) { \ fx = proc_evaluate(instance, x); \ if (fmin > fx) { \ min = x; \ fmin = fx; \ } \ if (proc_progress) { \ retval = proc_progress(instance, x, fx, min, fmin, \ (a < b) ? a : b, (a < b) ? b : a, k); \ if (retval) { \ TERMINATE; \ return PLFIT_SUCCESS; \ } \ } \ } int gss(double a, double b, double *_min, double *_fmin, gss_evaluate_t proc_evaluate, gss_progress_t proc_progress, void* instance, const gss_parameter_t *_param) { double c, d, min; double fa, fb, fc, fd, fmin; int k = 0; int retval; unsigned short int successful = 1; gss_parameter_t param = _param ? (*_param) : _defparam; gss_i_warning_flag = 0; if (a > b) { c = a; a = b; b = c; } min = a; fmin = proc_evaluate(instance, a); c = a + RESPHI*(b-a); EVALUATE(a, fa); EVALUATE(b, fb); EVALUATE(c, fc); if (fc >= fa || fc >= fb) { if (param.on_error == GSS_ERROR_STOP) { return PLFIT_FAILURE; } else { gss_i_warning_flag = 1; } } while (fabs(a-b) > param.epsilon) { k++; d = c + RESPHI*(b-c); EVALUATE(d, fd); if (fd >= fa || fd >= fb) { if (param.on_error == GSS_ERROR_STOP) { successful = 0; break; } else { gss_i_warning_flag = 1; } } if (fc <= fd) { b = a; a = d; } else { a = c; c = d; fc = fd; } } if (successful) { c = (a+b) / 2.0; k++; EVALUATE(c, fc); TERMINATE; } return successful ? PLFIT_SUCCESS : PLFIT_FAILURE; } igraph/src/igraph_community.h0000644000176000001440000002130012325527073016074 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_COMMUNITY_H #define IGRAPH_COMMUNITY_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_arpack.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* K-Cores */ /* -------------------------------------------------- */ int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, igraph_neimode_t mode); /* -------------------------------------------------- */ /* Community Structure */ /* -------------------------------------------------- */ /* TODO: cut.community */ /* TODO: edge.type.matrix */ /* TODO: */ int igraph_community_optimal_modularity(const igraph_t *graph, igraph_real_t *modularity, igraph_vector_t *membership, const igraph_vector_t *weights); int igraph_community_spinglass(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, igraph_real_t stoptemp, igraph_real_t coolfact, igraph_spincomm_update_t update_rule, igraph_real_t gamma, /* the rest is for the NegSpin implementation */ igraph_spinglass_implementation_t implementation, /* igraph_matrix_t *adhesion, */ /* igraph_matrix_t *normalised_adhesion, */ /* igraph_real_t *polarization, */ igraph_real_t lambda); int igraph_community_spinglass_single(const igraph_t *graph, const igraph_vector_t *weights, igraph_integer_t vertex, igraph_vector_t *community, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, igraph_integer_t *outer_links, igraph_integer_t spins, igraph_spincomm_update_t update_rule, igraph_real_t gamma); int igraph_community_walktrap(const igraph_t *graph, const igraph_vector_t *weights, int steps, igraph_matrix_t *merges, igraph_vector_t *modularity, igraph_vector_t *membership); int igraph_community_infomap(const igraph_t * graph, const igraph_vector_t *e_weights, const igraph_vector_t *v_weights, int nb_trials, igraph_vector_t *membership, igraph_real_t *codelength); int igraph_community_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, igraph_vector_t *edge_betweenness, igraph_matrix_t *merges, igraph_vector_t *bridges, igraph_vector_t *modularity, igraph_vector_t *membership, igraph_bool_t directed, const igraph_vector_t *weights); int igraph_community_eb_get_merges(const igraph_t *graph, const igraph_vector_t *edges, const igraph_vector_t *weights, igraph_matrix_t *merges, igraph_vector_t *bridges, igraph_vector_t *modularity, igraph_vector_t *membership); int igraph_community_fastgreedy(const igraph_t *graph, const igraph_vector_t *weights, igraph_matrix_t *merges, igraph_vector_t *modularity, igraph_vector_t *membership); int igraph_community_to_membership(const igraph_matrix_t *merges, igraph_integer_t nodes, igraph_integer_t steps, igraph_vector_t *membership, igraph_vector_t *csize); int igraph_le_community_to_membership(const igraph_matrix_t *merges, igraph_integer_t steps, igraph_vector_t *membership, igraph_vector_t *csize); int igraph_modularity(const igraph_t *graph, const igraph_vector_t *membership, igraph_real_t *modularity, const igraph_vector_t *weights); int igraph_modularity_matrix(const igraph_t *graph, const igraph_vector_t *membership, igraph_matrix_t *modmat, const igraph_vector_t *weights); int igraph_reindex_membership(igraph_vector_t *membership, igraph_vector_t *new_to_old); typedef enum { IGRAPH_LEVC_HIST_SPLIT=1, IGRAPH_LEVC_HIST_FAILED, IGRAPH_LEVC_HIST_START_FULL, IGRAPH_LEVC_HIST_START_GIVEN } igraph_leading_eigenvector_community_history_t; /** * \typedef igraph_community_leading_eigenvector_callback_t * Callback for the leading eigenvector community finding method. * * The leading eigenvector community finding implementation in igraph * is able to call a callback function, after each eigenvalue * calculation. This callback function must be of \c * igraph_community_leading_eigenvector_callback_t type. * The following arguments are passed to the callback: * \param membership The actual membership vector, before recording * the potential change implied by the newly found eigenvalue. * \param comm The id of the community that the algorithm tried to * split in the last iteration. The community ids are indexed from * zero here! * \param eigenvalue The eigenvalue the algorithm has just found. * \param eigenvector The eigenvector corresponding to the eigenvalue * the algorithm just found. * \param arpack_multiplier A function that was passed to \ref * igraph_arpack_rssolve() to solve the last eigenproblem. * \param arpack_extra The extra argument that was passed to the * ARPACK solver. * \param extra Extra argument that as passed to \ref * igraph_community_leading_eigenvector(). * * \sa \ref igraph_community_leading_eigenvector(), \ref * igraph_arpack_function_t, \ref igraph_arpack_rssolve(). */ typedef int igraph_community_leading_eigenvector_callback_t( const igraph_vector_t *membership, long int comm, igraph_real_t eigenvalue, const igraph_vector_t *eigenvector, igraph_arpack_function_t *arpack_multiplier, void *arpack_extra, void *extra); int igraph_community_leading_eigenvector(const igraph_t *graph, const igraph_vector_t *weights, igraph_matrix_t *merges, igraph_vector_t *membership, igraph_integer_t steps, igraph_arpack_options_t *options, igraph_real_t *modularity, igraph_bool_t start, igraph_vector_t *eigenvalues, igraph_vector_ptr_t *eigenvectors, igraph_vector_t *history, igraph_community_leading_eigenvector_callback_t *callback, void *callback_extra); int igraph_community_label_propagation(const igraph_t *graph, igraph_vector_t *membership, const igraph_vector_t *weights, const igraph_vector_t *initial, igraph_vector_bool_t *fixed, igraph_real_t *modularity); int igraph_community_multilevel(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *membership, igraph_matrix_t *memberships, igraph_vector_t *modularity); /* -------------------------------------------------- */ /* Community Structure Comparison */ /* -------------------------------------------------- */ int igraph_compare_communities(const igraph_vector_t *comm1, const igraph_vector_t *comm2, igraph_real_t* result, igraph_community_comparison_t method); int igraph_split_join_distance(const igraph_vector_t *comm1, const igraph_vector_t *comm2, igraph_integer_t* distance12, igraph_integer_t* distance21); __END_DECLS #endif igraph/src/blas.c0000644000176000001440000000713012325527072013436 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_blas.h" #include "igraph_blas_internal.h" #include /** * \function igraph_blas_dgemv * \brief Matrix-vector multiplication using BLAS, vector version. * * This function is a somewhat more user-friendly interface to * the \c dgemv function in BLAS. \c dgemv performs the operation * y = alpha*A*x + beta*y, where x and y are vectors and A is an * appropriately sized matrix (symmetric or unsymmetric). * * \param transpose whether to transpose the matrix \p A * \param alpha the constant \p alpha * \param a the matrix \p A * \param x the vector \p x * \param beta the constant \p beta * \param y the vector \p y (which will be modified in-place) * * Time complexity: O(nk) if the matrix is of size n x k * * \sa \ref igraph_blas_dgemv_array if you have arrays instead of * vectors. * * \example examples/simple/blas.c */ void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, const igraph_matrix_t* a, const igraph_vector_t* x, igraph_real_t beta, igraph_vector_t* y) { char trans = transpose ? 'T' : 'N'; int m, n; int inc = 1; m = (int) igraph_matrix_nrow(a); n = (int) igraph_matrix_ncol(a); assert(igraph_vector_size(x) == transpose ? m : n); assert(igraph_vector_size(y) == transpose ? n : m); igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, VECTOR(*x), &inc, &beta, VECTOR(*y), &inc); } /** * \function igraph_blas_dgemv_array * \brief Matrix-vector multiplication using BLAS, array version. * * This function is a somewhat more user-friendly interface to * the \c dgemv function in BLAS. \c dgemv performs the operation * y = alpha*A*x + beta*y, where x and y are vectors and A is an * appropriately sized matrix (symmetric or unsymmetric). * * \param transpose whether to transpose the matrix \p A * \param alpha the constant \p alpha * \param a the matrix \p A * \param x the vector \p x as a regular C array * \param beta the constant \p beta * \param y the vector \p y as a regular C array * (which will be modified in-place) * * Time complexity: O(nk) if the matrix is of size n x k * * \sa \ref igraph_blas_dgemv if you have vectors instead of * arrays. */ void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, const igraph_matrix_t* a, const igraph_real_t* x, igraph_real_t beta, igraph_real_t* y) { char trans = transpose ? 'T' : 'N'; int m, n; int inc = 1; m = (int) igraph_matrix_nrow(a); n = (int) igraph_matrix_ncol(a); igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, (igraph_real_t*)x, &inc, &beta, y, &inc); } igraph/src/bliss_orbit.cc0000644000176000001440000000643312325527072015200 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include #include #include "bliss_defs.hh" #include "bliss_orbit.hh" using namespace std; namespace igraph { Orbit::Orbit() { orbits = 0; in_orbit = 0; nof_elements = 0; } Orbit::~Orbit() { if(orbits) { free(orbits); orbits = 0; } if(in_orbit) { free(in_orbit); in_orbit = 0; } nof_elements = 0; } void Orbit::init(const unsigned int n) { assert(n > 0); if(orbits) free(orbits); orbits = (OrbitEntry*)malloc(n * sizeof(OrbitEntry)); if(in_orbit) free(in_orbit); in_orbit = (OrbitEntry**)malloc(n * sizeof(OrbitEntry*)); nof_elements = n; reset(); } void Orbit::reset() { assert(orbits); assert(in_orbit); for(unsigned int i = 0; i < nof_elements; i++) { orbits[i].element = i; orbits[i].next = 0; orbits[i].size = 1; in_orbit[i] = &orbits[i]; } _nof_orbits = nof_elements; } void Orbit::merge_orbits(OrbitEntry *orbit1, OrbitEntry *orbit2) { DEBUG_ASSERT((orbit1 == orbit2) == (orbit1->element == orbit2->element)); DEBUG_ASSERT(orbit1->element < nof_elements); DEBUG_ASSERT(orbit2->element < nof_elements); if(orbit1 != orbit2) { _nof_orbits--; /* Only update the elements in the smaller orbit */ if(orbit1->size > orbit2->size) { OrbitEntry * const temp = orbit2; orbit2 = orbit1; orbit1 = temp; } /* Link the elements of orbit1 to the almost beginning of orbit2 */ OrbitEntry *e = orbit1; while(e->next) { in_orbit[e->element] = orbit2; e = e->next; } in_orbit[e->element] = orbit2; e->next = orbit2->next; orbit2->next = orbit1; /* Keep the minimal orbit representative in the beginning */ if(orbit1->element < orbit2->element) { const unsigned int temp = orbit1->element; orbit1->element = orbit2->element; orbit2->element = temp; } orbit2->size += orbit1->size; } } void Orbit::merge_orbits(unsigned int e1, unsigned int e2) { DEBUG_ASSERT(e1 < nof_elements); DEBUG_ASSERT(e2 < nof_elements); merge_orbits(in_orbit[e1], in_orbit[e2]); } bool Orbit::is_minimal_representative(unsigned int element) { return(get_minimal_representative(element) == element); } unsigned int Orbit::get_minimal_representative(unsigned int element) { DEBUG_ASSERT(element < nof_elements); OrbitEntry * const orbit = in_orbit[element]; DEBUG_ASSERT(orbit->element <= element); return(orbit->element); } unsigned int Orbit::orbit_size(unsigned int element) { DEBUG_ASSERT(element < nof_elements); return(in_orbit[element]->size); } } igraph/src/structural_properties.c0000644000176000001440000067251712325527074017224 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_structural.h" #include "igraph_transitivity.h" #include "igraph_paths.h" #include "igraph_math.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_adjlist.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_centrality.h" #include "igraph_components.h" #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_types_internal.h" #include "igraph_dqueue.h" #include "igraph_attributes.h" #include "igraph_neighborhood.h" #include "igraph_topology.h" #include "igraph_qsort.h" #include "config.h" #include #include #include /** * \section about_structural * * These functions usually calculate some structural property * of a graph, like its diameter, the degree of the nodes, etc. */ /** * \ingroup structural * \function igraph_diameter * \brief Calculates the diameter of a graph (longest geodesic). * * \param graph The graph object. * \param pres Pointer to an integer, if not \c NULL then it will contain * the diameter (the actual distance). * \param pfrom Pointer to an integer, if not \c NULL it will be set to the * source vertex of the diameter path. * \param pto Pointer to an integer, if not \c NULL it will be set to the * target vertex of the diameter path. * \param path Pointer to an initialized vector. If not \c NULL the actual * longest geodesic path will be stored here. The vector will be * resized as needed. * \param directed Boolean, whether to consider directed * paths. Ignored for undirected graphs. * \param unconn What to do if the graph is not connected. If * \c TRUE the longest geodesic within a component * will be returned, otherwise the number of vertices is * returned. (The rationale behind the latter is that this is * always longer than the longest possible diameter in a * graph.) * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * * Time complexity: O(|V||E|), the * number of vertices times the number of edges. * * \example examples/simple/igraph_diameter.c */ int igraph_diameter(const igraph_t *graph, igraph_integer_t *pres, igraph_integer_t *pfrom, igraph_integer_t *pto, igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t unconn) { long int no_of_nodes=igraph_vcount(graph); long int i, j, n; long int *already_added; long int nodes_reached; long int from=0, to=0; long int res=0; igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; igraph_vector_int_t *neis; igraph_neimode_t dirmode; igraph_adjlist_t allneis; if (directed) { dirmode=IGRAPH_OUT; } else { dirmode=IGRAPH_ALL; } already_added=igraph_Calloc(no_of_nodes, long int); if (already_added==0) { IGRAPH_ERROR("diameter failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, already_added); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode)); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); for (i=0; ires) { res=actdist; from=i; to=actnode; } neis=igraph_adjlist_get(&allneis, actnode); n=igraph_vector_int_size(neis); for (j=0; j ressize) { IGRAPH_CHECK(igraph_vector_resize(res, actdist+1)); for (; ressize * If there is more than one geodesic between two vertices, this * function gives only one of them. * \param graph The graph object. * \param vertices The result, the ids of the vertices along the paths. * This is a pointer vector, each element points to a vector * object. These should be initialized before passing them to * the function, which will properly clear and/or resize them * and fill the ids of the vertices along the geodesics from/to * the vertices. Supply a null pointer here if you don't need * these vectors. * \param edges The result, the ids of the edges along the paths. * This is a pointer vector, each element points to a vector * object. These should be initialized before passing them to * the function, which will properly clear and/or resize them * and fill the ids of the vertices along the geodesics from/to * the vertices. Supply a null pointer here if you don't need * these vectors. * \param from The id of the vertex from/to which the geodesics are * calculated. * \param to Vertex sequence with the ids of the vertices to/from which the * shortest paths will be calculated. A vertex might be given multiple * times. * \param mode The type of shortest paths to be used for the * calculation in directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing paths are calculated. * \cli IGRAPH_IN * the incoming paths are calculated. * \cli IGRAPH_ALL * the directed graph is considered as an * undirected one for the computation. * \endclist * \param predecessors A pointer to an initialized igraph vector or null. * If not null, a vector containing the predecessor of each vertex in * the single source shortest path tree is returned here. The * predecessor of vertex i in the tree is the vertex from which vertex i * was reached. The predecessor of the start vertex (in the \c from * argument) is itself by definition. If the predecessor is -1, it means * that the given vertex was not reached from the source during the * search. Note that the search terminates if all the vertices in * \c to are reached. * \param inbound_edges A pointer to an initialized igraph vector or null. * If not null, a vector containing the inbound edge of each vertex in * the single source shortest path tree is returned here. The * inbound edge of vertex i in the tree is the edge via which vertex i * was reached. The start vertex and vertices that were not reached * during the search will have -1 in the corresponding entry of the * vector. Note that the search terminates if all the vertices in * \c to are reached. * * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * \p from is invalid vertex id, or the length of \p to is * not the same as the length of \p res. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(|V|+|E|), * |V| is the number of vertices, * |E| the number of edges in the * graph. * * \sa \ref igraph_shortest_paths() if you only need the path length but * not the paths themselves. * * \example examples/simple/igraph_get_shortest_paths.c */ int igraph_get_shortest_paths(const igraph_t *graph, igraph_vector_ptr_t *vertices, igraph_vector_ptr_t *edges, igraph_integer_t from, const igraph_vs_t to, igraph_neimode_t mode, igraph_vector_long_t *predecessors, igraph_vector_long_t *inbound_edges) { /* TODO: use inclist_t if to is long (longer than 1?) */ long int no_of_nodes=igraph_vcount(graph); long int *father; igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; long int i, j; igraph_vector_t tmp=IGRAPH_VECTOR_NULL; igraph_vit_t vit; long int to_reach; long int reached=0; if (from<0 || from>=no_of_nodes) { IGRAPH_ERROR("cannot get shortest paths", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); if (vertices && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(vertices)) { IGRAPH_ERROR("Size of the `vertices' and the `to' should match", IGRAPH_EINVAL); } if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) { IGRAPH_ERROR("Size of the `edges' and the `to' should match", IGRAPH_EINVAL); } father=igraph_Calloc(no_of_nodes, long int); if (father==0) { IGRAPH_ERROR("cannot get shortest paths", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, father); IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); /* Mark the vertices we need to reach */ to_reach=IGRAPH_VIT_SIZE(vit); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { if (father[ (long int) IGRAPH_VIT_GET(vit) ] == 0) { father[ (long int) IGRAPH_VIT_GET(vit) ] = -1; } else { to_reach--; /* this node was given multiple times */ } } /* Meaning of father[i]: * * - If father[i] < 0, it means that vertex i has to be reached and has not * been reached yet. * * - If father[i] = 0, it means that vertex i does not have to be reached and * it has not been reached yet. * * - If father[i] = 1, it means that vertex i is the start vertex. * * - Otherwise, father[i] is the ID of the edge from which vertex i was * reached plus 2. */ IGRAPH_CHECK(igraph_dqueue_push(&q, from+1)); if (father[ (long int) from ] < 0) { reached++; } father[ (long int)from ] = 1; while (!igraph_dqueue_empty(&q) && reached < to_reach) { long int act=(long int) igraph_dqueue_pop(&q)-1; IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act, mode)); for (j=0; j 0) { continue; } else if (father[neighbor] < 0) { reached++; } father[neighbor] = edge+2; IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor+1)); } } if (reached < to_reach) { IGRAPH_WARNING("Couldn't reach some vertices"); } /* Create `predecessors' if needed */ if (predecessors) { IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { if (father[i] <= 0) { /* i was not reached */ VECTOR(*predecessors)[i] = -1; } else if (father[i] == 1) { /* i is the start vertex */ VECTOR(*predecessors)[i] = i; } else { /* i was reached via the edge with ID = father[i] - 2 */ VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, father[i]-2, i); } } } /* Create `inbound_edges' if needed */ if (inbound_edges) { IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { if (father[i] <= 1) { /* i was not reached or i is the start vertex */ VECTOR(*inbound_edges)[i] = -1; } else { /* i was reached via the edge with ID = father[i] - 2 */ VECTOR(*inbound_edges)[i] = father[i]-2; } } } /* Create `vertices' and `edges' if needed */ if (vertices || edges) { for (IGRAPH_VIT_RESET(vit), j=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), j++) { long int node=IGRAPH_VIT_GET(vit); igraph_vector_t *vvec=0, *evec=0; if (vertices) { vvec=VECTOR(*vertices)[j]; igraph_vector_clear(vvec); } if (edges) { evec=VECTOR(*edges)[j]; igraph_vector_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); if (father[node]>0) { long int act=node; long int size=0; long int edge; while (father[act]>1) { size++; edge=father[act]-2; act=IGRAPH_OTHER(graph, edge, act); } if (vvec) { IGRAPH_CHECK(igraph_vector_resize(vvec, size+1)); VECTOR(*vvec)[size]=node; } if (evec) { IGRAPH_CHECK(igraph_vector_resize(evec, size)); } act=node; while (father[act]>1) { size--; edge=father[act]-2; act=IGRAPH_OTHER(graph, edge, act); if (vvec) { VECTOR(*vvec)[size]=act; } if (evec) { VECTOR(*evec)[size]=edge; } } } } } /* Clean */ igraph_Free(father); igraph_dqueue_destroy(&q); igraph_vector_destroy(&tmp); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(4); return 0; } /** * \function igraph_get_shortest_path * Shortest path from one vertex to another one. * * Calculates and returns a single unweighted shortest path from a * given vertex to another one. If there are more than one shortest * paths between the two vertices, then an arbitrary one is returned. * * This function is a wrapper to \ref * igraph_get_shortest_paths(), for the special case when only one * target vertex is considered. * \param graph The input graph, it can be directed or * undirected. Directed paths are considered in directed * graphs. * \param vertices Pointer to an initialized vector or a null * pointer. If not a null pointer, then the vertex ids along * the path are stored here, including the source and target * vertices. * \param edges Pointer to an uninitialized vector or a null * pointer. If not a null pointer, then the edge ids along the * path are stored here. * \param from The id of the source vertex. * \param to The id of the target vertex. * \param mode A constant specifying how edge directions are * considered in directed graphs. Valid modes are: * \c IGRAPH_OUT, follows edge directions; * \c IGRAPH_IN, follows the opposite directions; and * \c IGRAPH_ALL, ignores edge directions. This argument is * ignored for undirected graphs. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges in the graph. * * \sa \ref igraph_get_shortest_paths() for the version with more target * vertices. */ int igraph_get_shortest_path(const igraph_t *graph, igraph_vector_t *vertices, igraph_vector_t *edges, igraph_integer_t from, igraph_integer_t to, igraph_neimode_t mode) { igraph_vector_ptr_t vertices2, *vp=&vertices2; igraph_vector_ptr_t edges2, *ep=&edges2; if (vertices) { IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2); VECTOR(vertices2)[0]=vertices; } else { vp=0; } if (edges) { IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); VECTOR(edges2)[0]=edges; } else { ep=0; } IGRAPH_CHECK(igraph_get_shortest_paths(graph, vp, ep, from, igraph_vss_1(to), mode, 0, 0)); if (edges) { igraph_vector_ptr_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { igraph_vector_ptr_destroy(&vertices2); IGRAPH_FINALLY_CLEAN(1); } return 0; } void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v); void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v) { long int i; for (i=0; i * * Time complexity: O(|V|+|E|) for most graphs, O(|V|^2) in the worst * case. */ int igraph_get_all_shortest_paths(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vector_t *nrgeo, igraph_integer_t from, const igraph_vs_t to, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); long int *geodist; igraph_vector_ptr_t paths; igraph_dqueue_t q; igraph_vector_t *vptr; igraph_vector_t neis; igraph_vector_t ptrlist; igraph_vector_t ptrhead; long int n, j, i; long int to_reach, reached=0, maxdist=0; igraph_vit_t vit; if (from<0 || from>=no_of_nodes) { IGRAPH_ERROR("cannot get shortest paths", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); /* paths will store the shortest paths during the search */ IGRAPH_CHECK(igraph_vector_ptr_init(&paths, 0)); IGRAPH_FINALLY(igraph_i_gasp_paths_destroy, &paths); /* neis is a temporary vector holding the neighbors of the * node being examined */ IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); /* ptrlist stores indices into the paths vector, in the order * of how they were found. ptrhead is a second-level index that * will be used to find paths that terminate in a given vertex */ IGRAPH_VECTOR_INIT_FINALLY(&ptrlist, 0); /* ptrhead contains indices into ptrlist. * ptrhead[i] = j means that element #j-1 in ptrlist contains * the shortest path from the root to node i. ptrhead[i] = 0 * means that node i was not reached so far */ IGRAPH_VECTOR_INIT_FINALLY(&ptrhead, no_of_nodes); /* geodist[i] == 0 if i was not reached yet and it is not in the * target vertex sequence, or -1 if i was not reached yet and it * is in the target vertex sequence. Otherwise it is * one larger than the length of the shortest path from the * source */ geodist=igraph_Calloc(no_of_nodes, long int); if (geodist==0) { IGRAPH_ERROR("Cannot calculate shortest paths", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, geodist); /* dequeue to store the BFS queue -- odd elements are the vertex indices, * even elements are the distances from the root */ IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &q); if (nrgeo) { IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes)); igraph_vector_null(nrgeo); } /* use geodist to count how many vertices we have to reach */ to_reach=IGRAPH_VIT_SIZE(vit); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { if (geodist[ (long int) IGRAPH_VIT_GET(vit) ] == 0) { geodist[ (long int) IGRAPH_VIT_GET(vit) ] = -1; } else { to_reach--; /* this node was given multiple times */ } } if (geodist[ (long int) from ] < 0) { reached++; } /* from -> from */ vptr=igraph_Calloc(1, igraph_vector_t); /* TODO: dirty */ IGRAPH_CHECK(igraph_vector_ptr_push_back(&paths, vptr)); IGRAPH_CHECK(igraph_vector_init(vptr, 1)); VECTOR(*vptr)[0]=from; geodist[(long int)from]=1; VECTOR(ptrhead)[(long int)from]=1; IGRAPH_CHECK(igraph_vector_push_back(&ptrlist, 0)); if (nrgeo) { VECTOR(*nrgeo)[(long int)from]=1; } /* Init queue */ IGRAPH_CHECK(igraph_dqueue_push(&q, from)); IGRAPH_CHECK(igraph_dqueue_push(&q, 0.0)); while (!igraph_dqueue_empty(&q)) { long int actnode=(long int) igraph_dqueue_pop(&q); long int actdist=(long int) igraph_dqueue_pop(&q); IGRAPH_ALLOW_INTERRUPTION(); if (reached >= to_reach) { /* all nodes were reached. Since we need all the shortest paths * to all these nodes, we can stop the search only if the distance * of the current node to the root is larger than the distance of * any of the nodes we wanted to reach */ if (actdist > maxdist) { /* safety check, maxdist should have been set when we reached the last node */ if (maxdist < 0) { IGRAPH_ERROR("possible bug in igraph_get_all_shortest_paths, " "maxdist is negative", IGRAPH_EINVAL); } break; } } IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode)); n=igraph_vector_size(&neis); for (j=0; j 0 && geodist[neighbor]-1 < actdist+1) { /* this node was reached via a shorter path before */ continue; } /* yay, found another shortest path to neighbor */ if (nrgeo) { /* the number of geodesics leading to neighbor must be * increased by the number of geodesics leading to actnode */ VECTOR(*nrgeo)[neighbor] += VECTOR(*nrgeo)[actnode]; } if (geodist[neighbor] <= 0) { /* this node was not reached yet, push it into the queue */ IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); IGRAPH_CHECK(igraph_dqueue_push(&q, actdist+1)); if (geodist[neighbor] < 0) { reached++; } if (reached == to_reach) maxdist = actdist; } geodist[neighbor]=actdist+2; /* copy all existing paths to the parent */ fatherptr = (long int) VECTOR(ptrhead)[actnode]; while (fatherptr != 0) { /* allocate a new igraph_vector_t at the end of paths */ vptr=igraph_Calloc(1, igraph_vector_t); IGRAPH_CHECK(igraph_vector_ptr_push_back(&paths, vptr)); IGRAPH_CHECK(igraph_vector_copy(vptr, VECTOR(paths)[fatherptr-1])); IGRAPH_CHECK(igraph_vector_reserve(vptr, actdist+2)); IGRAPH_CHECK(igraph_vector_push_back(vptr, neighbor)); IGRAPH_CHECK(igraph_vector_push_back(&ptrlist, VECTOR(ptrhead)[neighbor])); VECTOR(ptrhead)[neighbor]=igraph_vector_size(&ptrlist); fatherptr=(long int) VECTOR(ptrlist)[fatherptr-1]; } } } igraph_dqueue_destroy(&q); IGRAPH_FINALLY_CLEAN(1); /* mark the nodes for which we need the result */ memset(geodist, 0, sizeof(long int) * (size_t) no_of_nodes); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { geodist[ (long int) IGRAPH_VIT_GET(vit) ] = 1; } /* count the number of paths in the result */ n=0; for (i=0; i 0) { while (fatherptr != 0) { n++; fatherptr=(long int) VECTOR(ptrlist)[fatherptr-1]; } } } IGRAPH_CHECK(igraph_vector_ptr_resize(res, n)); j=0; for (i=0; i 0) { /* yes, copy them to the result vector */ while (fatherptr != 0) { VECTOR(*res)[j++]=VECTOR(paths)[fatherptr-1]; fatherptr=(long int) VECTOR(ptrlist)[fatherptr-1]; } } else { /* no, free them */ while (fatherptr != 0) { igraph_vector_destroy(VECTOR(paths)[fatherptr-1]); igraph_Free(VECTOR(paths)[fatherptr-1]); fatherptr=(long int) VECTOR(ptrlist)[fatherptr-1]; } } } igraph_Free(geodist); igraph_vector_destroy(&ptrlist); igraph_vector_destroy(&ptrhead); igraph_vector_destroy(&neis); igraph_vector_ptr_destroy(&paths); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(6); return 0; } /** * \ingroup structural * \function igraph_subcomponent * \brief The vertices in the same component as a given vertex. * * \param graph The graph object. * \param res The result, vector with the ids of the vertices in the * same component. * \param vertex The id of the vertex of which the component is * searched. * \param mode Type of the component for directed graphs, possible * values: * \clist * \cli IGRAPH_OUT * the set of vertices reachable \em from the * \p vertex, * \cli IGRAPH_IN * the set of vertices from which the * \p vertex is reachable. * \cli IGRAPH_ALL * the graph is considered as an * undirected graph. Note that this is \em not the same * as the union of the previous two. * \endclist * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * \p vertex is an invalid vertex id * \cli IGRAPH_EINVMODE * invalid mode argument passed. * \endclist * * Time complexity: O(|V|+|E|), * |V| and * |E| are the number of vertices and * edges in the graph. * * \sa \ref igraph_subgraph() if you want a graph object consisting only * a given set of vertices and the edges between them. */ int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vertex, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; char *already_added; long int i; igraph_vector_t tmp=IGRAPH_VECTOR_NULL; if (!IGRAPH_FINITE(vertex) || vertex<0 || vertex>=no_of_nodes) { IGRAPH_ERROR("subcomponent failed", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("invalid mode argument", IGRAPH_EINVMODE); } already_added=igraph_Calloc(no_of_nodes, char); if (already_added==0) { IGRAPH_ERROR("subcomponent failed",IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, already_added); /* TODO: hack */ igraph_vector_clear(res); IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_dqueue_push(&q, vertex)); IGRAPH_CHECK(igraph_vector_push_back(res, vertex)); already_added[(long int)vertex]=1; while (!igraph_dqueue_empty(&q)) { long int actnode=(long int) igraph_dqueue_pop(&q); IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) actnode, mode)); for (i=0; iThis is an old implementation, * it is provided for compatibility with igraph versions earlier than * 0.5. Please use the new implementation \ref igraph_pagerank() in * new projects. * * * From version 0.7 this function is deprecated and its use gives a * warning message. * * * Please note that the PageRank of a given vertex depends on the PageRank * of all other vertices, so even if you want to calculate the PageRank for * only some of the vertices, all of them must be calculated. Requesting * the PageRank for only some of the vertices does not result in any * performance increase at all. * * * Since the calculation is an iterative * process, the algorithm is stopped after a given count of iterations * or if the PageRank value differences between iterations are less than * a predefined value. * * * * For the explanation of the PageRank algorithm, see the following * webpage: * http://infolab.stanford.edu/~backrub/google.html , or the * following reference: * * * * Sergey Brin and Larry Page: The Anatomy of a Large-Scale Hypertextual * Web Search Engine. Proceedings of the 7th World-Wide Web Conference, * Brisbane, Australia, April 1998. * * * \param graph The graph object. * \param res The result vector containing the PageRank values for the * given nodes. * \param vids Vector with the vertex ids * \param directed Logical, if true directed paths will be considered * for directed graphs. It is ignored for undirected graphs. * \param niter The maximum number of iterations to perform * \param eps The algorithm will consider the calculation as complete * if the difference of PageRank values between iterations change * less than this value for every node * \param damping The damping factor ("d" in the original paper) * \param old Boolean, whether to use the pre-igraph 0.5 way to * calculate page rank. Not recommended for new applications, * only included for compatibility. If this is non-zero then the damping * factor is not divided by the number of vertices before adding it * to the weighted page rank scores to calculate the * new scores. I.e. the formula in the original PageRank paper * is used. Furthermore, if this is non-zero then the PageRank * vector is renormalized after each iteration. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id in * \p vids. * * Time complexity: O(|V|+|E|) per iteration. A handful iterations * should be enough. Note that if the old-style dumping is used then * the iteration might not converge at all. * * \sa \ref igraph_pagerank() for the new implementation. */ int igraph_pagerank_old(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, igraph_integer_t niter, igraph_real_t eps, igraph_real_t damping, igraph_bool_t old) { long int no_of_nodes=igraph_vcount(graph); long int i, j, n, nodes_to_calc; igraph_real_t *prvec, *prvec_new, *prvec_aux, *prvec_scaled; igraph_vector_int_t *neis; igraph_vector_t outdegree; igraph_neimode_t dirmode; igraph_adjlist_t allneis; igraph_real_t maxdiff=eps; igraph_vit_t vit; IGRAPH_WARNING("igraph_pagerank_old is deprecated from igraph 0.7, " "use igraph_pagerank instead"); if (niter<=0) IGRAPH_ERROR("Invalid iteration count", IGRAPH_EINVAL); if (eps<=0) IGRAPH_ERROR("Invalid epsilon value", IGRAPH_EINVAL); if (damping<=0 || damping>=1) IGRAPH_ERROR("Invalid damping factor", IGRAPH_EINVAL); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc=IGRAPH_VIT_SIZE(vit); IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); igraph_vector_null(res); IGRAPH_VECTOR_INIT_FINALLY(&outdegree, no_of_nodes); prvec=igraph_Calloc(no_of_nodes, igraph_real_t); if (prvec==0) { IGRAPH_ERROR("pagerank failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, prvec); prvec_new=igraph_Calloc(no_of_nodes, igraph_real_t); if (prvec_new==0) { IGRAPH_ERROR("pagerank failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, prvec_new); prvec_scaled=igraph_Calloc(no_of_nodes, igraph_real_t); if (prvec_scaled==0) { IGRAPH_ERROR("pagerank failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, prvec_scaled); if (directed) { dirmode=IGRAPH_IN; } else { dirmode=IGRAPH_ALL; } igraph_adjlist_init(graph, &allneis, dirmode); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); /* Calculate outdegrees for every node */ igraph_degree(graph, &outdegree, igraph_vss_all(), directed?IGRAPH_OUT:IGRAPH_ALL, 0); /* Initialize PageRank values */ for (i=0; i0 && maxdiff >= eps) { igraph_real_t sumfrom=0, sum=0; niter--; maxdiff=0; /* Calculate the quotient of the actual PageRank value and the * outdegree for every node */ sumfrom=0.0; sum=0.0; for (i=0; imaxdiff) maxdiff=prvec_new[i]-prvec[i]; else if (prvec[i]-prvec_new[i]>maxdiff) maxdiff=prvec[i]-prvec_new[i]; } /* Swap the vectors */ prvec_aux=prvec_new; prvec_new=prvec; prvec=prvec_aux; } /* Copy results from prvec to res */ for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int vid=IGRAPH_VIT_GET(vit); VECTOR(*res)[i]=prvec[vid]; } igraph_adjlist_destroy(&allneis); igraph_vit_destroy(&vit); igraph_vector_destroy(&outdegree); igraph_Free(prvec); igraph_Free(prvec_new); igraph_Free(prvec_scaled); IGRAPH_FINALLY_CLEAN(6); return 0; } /** * \ingroup structural * \function igraph_rewire * \brief Randomly rewires a graph while preserving the degree distribution. * * * This function generates a new graph based on the original one by randomly * rewiring edges while preserving the original graph's degree distribution. * Please note that the rewiring is done "in place", so no new graph will * be allocated. If you would like to keep the original graph intact, use * \ref igraph_copy() beforehand. * * \param graph The graph object to be rewired. * \param n Number of rewiring trials to perform. * \param mode The rewiring algorithm to be used. It can be one of the following: * \clist * \cli IGRAPH_REWIRING_SIMPLE * Simple rewiring algorithm which chooses two arbitrary edges * in each step (namely (a,b) and (c,d)) and substitutes them * with (a,d) and (c,b) if they don't exist. The method will * neither destroy nor create self-loops. * \cli IGRAPH_REWIRING_SIMPLE_LOOPS * Same as \c IGRAPH_REWIRING_SIMPLE but allows the creation or * destruction of self-loops. * \endclist * * \return Error code: * \clist * \cli IGRAPH_EINVMODE * Invalid rewiring mode. * \cli IGRAPH_EINVAL * Graph unsuitable for rewiring (e.g. it has * less than 4 nodes in case of \c IGRAPH_REWIRING_SIMPLE) * \cli IGRAPH_ENOMEM * Not enough memory for temporary data. * \endclist * * Time complexity: TODO. * * \example examples/simple/igraph_rewire.c */ int igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); char message[256]; igraph_integer_t a, b, c, d, dummy, num_swaps, num_successful_swaps; igraph_vector_t eids, edgevec; igraph_bool_t directed, loops, ok; igraph_es_t es; if ((mode == IGRAPH_REWIRING_SIMPLE || mode == IGRAPH_REWIRING_SIMPLE_LOOPS) && no_of_nodes<4) IGRAPH_ERROR("graph unsuitable for rewiring", IGRAPH_EINVAL); directed = igraph_is_directed(graph); loops = (mode == IGRAPH_REWIRING_SIMPLE_LOOPS); RNG_BEGIN(); IGRAPH_VECTOR_INIT_FINALLY(&edgevec, 4); IGRAPH_VECTOR_INIT_FINALLY(&eids, 2); es = igraph_ess_vector(&eids); /* We don't want the algorithm to get stuck in an infinite loop when * it can't choose two edges satisfying the conditions. Instead of * this, we choose two arbitrary edges and if they have endpoints * in common, we just decrease the number of trials left and continue * (so unsuccessful rewirings still count as a trial) */ num_swaps = num_successful_swaps = 0; while (num_swaps < n) { IGRAPH_ALLOW_INTERRUPTION(); if (num_swaps % 1000 == 0) { snprintf(message, sizeof(message), "Random rewiring (%.2f%% of the trials were successful)", (100.0 * num_successful_swaps) / num_swaps); IGRAPH_PROGRESS(message, (100.0 * num_swaps) / n, 0); } switch (mode) { case IGRAPH_REWIRING_SIMPLE: case IGRAPH_REWIRING_SIMPLE_LOOPS: ok = 1; /* Choose two edges randomly */ VECTOR(eids)[0]=RNG_INTEGER(0, no_of_edges-1); do { VECTOR(eids)[1]=RNG_INTEGER(0, no_of_edges-1); } while (VECTOR(eids)[0] == VECTOR(eids)[1]); /* Get the endpoints */ IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) VECTOR(eids)[0], &a, &b)); IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) VECTOR(eids)[1], &c, &d)); /* For an undirected graph, we have two "variants" of each edge, i.e. * a -- b and b -- a. Since some rewirings can be performed only when we * "swap" the endpoints, we do it now with probability 0.5 */ if (!directed && RNG_UNIF01() < 0.5) { dummy = c; c = d; d = dummy; } /* If we do not touch loops, check whether a == b or c == d and disallow * the swap if needed */ if (!loops && (a == b || c == d)) { ok = 0; } else { /* Check whether they are suitable for rewiring */ if (a == c || b == d) { /* Swapping would have no effect */ ok = 0; } else { /* a != c && b != d */ /* If a == d or b == c, the swap would generate at least one loop, so * we disallow them unless we want to have loops */ ok = loops || (a != d && b != c); /* Also, if a == b and c == d and we allow loops, doing the swap * would result in a multiple edge if the graph is undirected */ ok = ok && (directed || a != b || c != d); } } /* All good so far. Now check for the existence of a --> d and c --> b to * disallow the creation of multiple edges */ if (ok) { IGRAPH_CHECK(igraph_are_connected(graph, a, d, &ok)); ok = !ok; } if (ok) { IGRAPH_CHECK(igraph_are_connected(graph, c, b, &ok)); ok = !ok; } /* If we are still okay, we can perform the rewiring */ if (ok) { /* printf("Deleting: %ld -> %ld, %ld -> %ld\n", (long)a, (long)b, (long)c, (long)d); */ IGRAPH_CHECK(igraph_delete_edges(graph, es)); VECTOR(edgevec)[0]=a; VECTOR(edgevec)[1]=d; VECTOR(edgevec)[2]=c; VECTOR(edgevec)[3]=b; /* printf("Adding: %ld -> %ld, %ld -> %ld\n", (long)a, (long)d, (long)c, (long)b); */ igraph_add_edges(graph, &edgevec, 0); num_successful_swaps++; } break; default: RNG_END(); IGRAPH_ERROR("unknown rewiring mode", IGRAPH_EINVMODE); } num_swaps++; } IGRAPH_PROGRESS("Random rewiring: ", 100.0, 0); igraph_vector_destroy(&eids); igraph_vector_destroy(&edgevec); IGRAPH_FINALLY_CLEAN(2); RNG_END(); return 0; } int igraph_i_subgraph_copy_and_delete(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_vector_t *map, igraph_vector_t *invmap); int igraph_i_subgraph_create_from_scratch(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_vector_t *map, igraph_vector_t *invmap); /** * Subgraph creation, old version: it copies the graph and then deletes * unneeded vertices. */ int igraph_i_subgraph_copy_and_delete(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_vector_t *map, igraph_vector_t *invmap) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t delete=IGRAPH_VECTOR_NULL; char *remain; long int i; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); IGRAPH_VECTOR_INIT_FINALLY(&delete, 0); remain=igraph_Calloc(no_of_nodes, char); if (remain==0) { IGRAPH_ERROR("subgraph failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, remain); /* TODO: hack */ IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_nodes-IGRAPH_VIT_SIZE(vit))); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { remain[ (long int) IGRAPH_VIT_GET(vit) ] = 1; } for (i=0; iattr to 0 before calling igraph_copy */ res->attr=0; /* Why is this needed? TODO */ IGRAPH_CHECK(igraph_copy(res, graph)); IGRAPH_FINALLY(igraph_destroy, res); IGRAPH_CHECK(igraph_delete_vertices_idx(res, igraph_vss_vector(&delete), map, invmap)); igraph_vector_destroy(&delete); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * Subgraph creation, new version: creates the new graph instead of * copying the old one. */ int igraph_i_subgraph_create_from_scratch(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_vector_t *map, igraph_vector_t *invmap) { igraph_bool_t directed = igraph_is_directed(graph); long int no_of_nodes = igraph_vcount(graph); long int no_of_new_nodes = 0; char* seen_edges = 0; long int i, j, n; igraph_vector_t vids_old2new, vids_new2old; igraph_vector_t eids_new2old; igraph_vector_t nei_edges; igraph_vector_t new_edges; igraph_vit_t vit; igraph_vector_t *my_vids_old2new=&vids_old2new, *my_vids_new2old=&vids_new2old; /* The order of initialization is important here, they will be destroyed in the * opposite order */ IGRAPH_VECTOR_INIT_FINALLY(&eids_new2old, 0); if (invmap) { my_vids_new2old=invmap; igraph_vector_clear(my_vids_new2old); } else { IGRAPH_VECTOR_INIT_FINALLY(&vids_new2old, 0); } IGRAPH_VECTOR_INIT_FINALLY(&new_edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&nei_edges, 0); if (map) { my_vids_old2new=map; IGRAPH_CHECK(igraph_vector_resize(map, no_of_nodes)); igraph_vector_null(map); } else { IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); } IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); /* Calculate the mapping from the old node IDs to the new ones. The other * igraph_simplify implementation in igraph_i_simplify_copy_and_delete * ensures that the order of vertex IDs is kept during remapping (i.e. * if the old ID of vertex A is less than the old ID of vertex B, then * the same will also be true for the new IDs). To ensure compatibility * with the other implementation, we have to fetch the vertex IDs into * a vector first and then sort it. We temporarily use new_edges for that. */ IGRAPH_CHECK(igraph_vit_as_vector(&vit, &nei_edges)); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); igraph_vector_sort(&nei_edges); n = igraph_vector_size(&nei_edges); for (i = 0; i < n; i++) { long int vid = (long int) VECTOR(nei_edges)[i]; if (VECTOR(*my_vids_old2new)[vid] == 0) { VECTOR(*my_vids_old2new)[vid] = ++no_of_new_nodes; } } /* Allocate some memory for the seen_edges array that avoids processing edges * twice for undirected graphs */ if (!directed) { seen_edges = igraph_Calloc(igraph_ecount(graph), char); if (seen_edges == 0) IGRAPH_ERROR("cannot calculate subgraph", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, seen_edges); } /* Calculate the mapping from the new node IDs to the new ones * and also create the new edge list */ IGRAPH_CHECK(igraph_vector_resize(my_vids_new2old, no_of_new_nodes)); for (i = 0; i < no_of_nodes; i++) { long int new_vid = (long int) VECTOR(*my_vids_old2new)[i] - 1; if (new_vid < 0) continue; VECTOR(*my_vids_new2old)[new_vid] = i; IGRAPH_CHECK(igraph_incident(graph, &nei_edges, (igraph_integer_t) i, IGRAPH_OUT)); n = igraph_vector_size(&nei_edges); if (directed) { for (j = 0; j < n; j++) { igraph_integer_t eid = (igraph_integer_t) VECTOR(nei_edges)[j]; long int from, to; from = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_FROM(graph, eid) ]; if (!from) continue; to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid)]; if (!to) continue; IGRAPH_CHECK(igraph_vector_push_back(&new_edges, from-1)); IGRAPH_CHECK(igraph_vector_push_back(&new_edges, to-1)); IGRAPH_CHECK(igraph_vector_push_back(&eids_new2old, eid)); } } else { for (j = 0; j < n; j++) { igraph_integer_t eid = (igraph_integer_t) VECTOR(nei_edges)[j]; long int from, to; from = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_FROM(graph, eid)]; if (!from) continue; to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid)]; if (!to) continue; if (seen_edges[(long int)eid]) continue; seen_edges[(long int)eid] = 1; IGRAPH_CHECK(igraph_vector_push_back(&new_edges, from-1)); IGRAPH_CHECK(igraph_vector_push_back(&new_edges, to-1)); IGRAPH_CHECK(igraph_vector_push_back(&eids_new2old, eid)); } } } /* Get rid of some vectors that are not needed anymore */ if (!directed) { igraph_free(seen_edges); IGRAPH_FINALLY_CLEAN(1); } if (!map) { igraph_vector_destroy(&vids_old2new); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&nei_edges); IGRAPH_FINALLY_CLEAN(1); /* Create the new graph */ IGRAPH_CHECK(igraph_create(res, &new_edges, (igraph_integer_t) no_of_new_nodes, directed)); IGRAPH_I_ATTRIBUTE_DESTROY(res); /* Now we can also get rid of the new_edges vector */ igraph_vector_destroy(&new_edges); IGRAPH_FINALLY_CLEAN(1); /* Make sure that the newly created graph is destroyed if something happens from * now on */ IGRAPH_FINALLY(igraph_destroy, res); /* Copy the graph attributes */ IGRAPH_CHECK(igraph_i_attribute_copy(res, graph, /* ga = */ 1, /* va = */ 0, /* ea = */ 0)); /* Copy the vertex attributes */ IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, my_vids_new2old)); /* Copy the edge attributes */ IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, res, &eids_new2old)); if (!invmap) { igraph_vector_destroy(my_vids_new2old); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&eids_new2old); IGRAPH_FINALLY_CLEAN(2); /* 1 + 1 since we don't need to destroy res */ return 0; } /** * \ingroup structural * \function igraph_subgraph * \brief Creates a subgraph induced by the specified vertices. * * * This function is an alias to \ref igraph_induced_subgraph(), it is * left here to ensure API compatibility with igraph versions prior to 0.6. * * * This function collects the specified vertices and all edges between * them to a new graph. * As the vertex ids in a graph always start with zero, this function * very likely needs to reassign ids to the vertices. * \param graph The graph object. * \param res The subgraph, another graph object will be stored here, * do \em not initialize this object before calling this * function, and call \ref igraph_destroy() on it if you don't need * it any more. * \param vids A vertex selector describing which vertices to keep. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id in * \p vids. * * Time complexity: O(|V|+|E|), * |V| and * |E| are the number of vertices and * edges in the original graph. * * \sa \ref igraph_delete_vertices() to delete the specified set of * vertices from a graph, the opposite of this function. */ int igraph_subgraph(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids) { IGRAPH_WARNING("igraph_subgraph is deprecated from igraph 0.6, " "use igraph_induced_subgraph instead"); return igraph_induced_subgraph(graph, res, vids, IGRAPH_SUBGRAPH_AUTO); } /** * \ingroup structural * \function igraph_induced_subgraph * \brief Creates a subgraph induced by the specified vertices. * * * This function collects the specified vertices and all edges between * them to a new graph. * As the vertex ids in a graph always start with zero, this function * very likely needs to reassign ids to the vertices. * \param graph The graph object. * \param res The subgraph, another graph object will be stored here, * do \em not initialize this object before calling this * function, and call \ref igraph_destroy() on it if you don't need * it any more. * \param vids A vertex selector describing which vertices to keep. * \param impl This parameter selects which implementation should we * use when constructing the new graph. Basically there are two * possibilities: \c IGRAPH_SUBGRAPH_COPY_AND_DELETE copies the * existing graph and deletes the vertices that are not needed * in the new graph, while \c IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH * constructs the new graph from scratch without copying the old * one. The latter is more efficient if you are extracting a * relatively small subpart of a very large graph, while the * former is better if you want to extract a subgraph whose size * is comparable to the size of the whole graph. There is a third * possibility: \c IGRAPH_SUBGRAPH_AUTO will select one of the * two methods automatically based on the ratio of the number * of vertices in the new and the old graph. * * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id in * \p vids. * * Time complexity: O(|V|+|E|), * |V| and * |E| are the number of vertices and * edges in the original graph. * * \sa \ref igraph_delete_vertices() to delete the specified set of * vertices from a graph, the opposite of this function. */ int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl) { return igraph_induced_subgraph_map(graph, res, vids, impl, /* map= */ 0, /* invmap= */ 0); } int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl, igraph_vector_t *map, igraph_vector_t *invmap) { if (impl == IGRAPH_SUBGRAPH_AUTO) { double ratio; if (igraph_vs_is_all(&vids)) ratio = 1.0; else { igraph_integer_t num_vs; IGRAPH_CHECK(igraph_vs_size(graph, &vids, &num_vs)); ratio = (igraph_real_t) num_vs / igraph_vcount(graph); } if (ratio > 0.5) impl = IGRAPH_SUBGRAPH_COPY_AND_DELETE; else impl = IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH; } switch (impl) { case IGRAPH_SUBGRAPH_COPY_AND_DELETE: return igraph_i_subgraph_copy_and_delete(graph, res, vids, map, invmap); case IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH: return igraph_i_subgraph_create_from_scratch(graph, res, vids, map, invmap); default: IGRAPH_ERROR("unknown subgraph implementation type", IGRAPH_EINVAL); } return 0; } /** * \ingroup structural * \function igraph_subgraph_edges * \brief Creates a subgraph with the specified edges and their endpoints. * * * This function collects the specified edges and their endpoints to a new * graph. * As the vertex ids in a graph always start with zero, this function * very likely needs to reassign ids to the vertices. * \param graph The graph object. * \param res The subgraph, another graph object will be stored here, * do \em not initialize this object before calling this * function, and call \ref igraph_destroy() on it if you don't need * it any more. * \param eids An edge selector describing which edges to keep. * \param delete_vertices Whether to delete the vertices not incident on any * of the specified edges as well. If \c FALSE, the number of vertices * in the result graph will always be equal to the number of vertices * in the input graph. * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVEID, invalid edge id in * \p eids. * * Time complexity: O(|V|+|E|), * |V| and * |E| are the number of vertices and * edges in the original graph. * * \sa \ref igraph_delete_edges() to delete the specified set of * edges from a graph, the opposite of this function. */ int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, const igraph_es_t eids, igraph_bool_t delete_vertices) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vector_t delete=IGRAPH_VECTOR_NULL; char *vremain, *eremain; long int i; igraph_eit_t eit; IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); IGRAPH_VECTOR_INIT_FINALLY(&delete, 0); vremain=igraph_Calloc(no_of_nodes, char); if (vremain==0) { IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM); } eremain=igraph_Calloc(no_of_edges, char); if (eremain==0) { IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, vremain); /* TODO: hack */ IGRAPH_FINALLY(free, eremain); /* TODO: hack */ IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_edges-IGRAPH_EIT_SIZE(eit))); /* Collect the vertex and edge IDs that will remain */ for (IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { igraph_integer_t from, to; long int eid = (long int) IGRAPH_EIT_GET(eit); IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) eid, &from, &to)); eremain[eid] = vremain[(long int)from] = vremain[(long int)to] = 1; } /* Collect the edge IDs to be deleted */ for (i=0; iattr to 0 before calling igraph_copy */ res->attr=0; /* Why is this needed? TODO */ IGRAPH_CHECK(igraph_copy(res, graph)); IGRAPH_FINALLY(igraph_destroy, res); IGRAPH_CHECK(igraph_delete_edges(res, igraph_ess_vector(&delete))); if (delete_vertices) { /* Collect the vertex IDs to be deleted */ igraph_vector_clear(&delete); for (i=0; i 0) { IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges))); } igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } if (attr) { IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*2)); IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_FROM)); IGRAPH_FINALLY(igraph_es_destroy, &es); IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); for (actedge=-1; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { edge=IGRAPH_EIT_GET(eit); from=IGRAPH_FROM(graph, edge); to=IGRAPH_TO(graph, edge); if (loops && from==to) { /* Loop edge to be removed */ if (attr) { VECTOR(mergeinto)[edge] = -1; } } else if (multiple && from==pfrom && to==pto) { /* Multiple edge to be contracted */ if (attr) { VECTOR(mergeinto)[edge]=actedge; } } else { /* Edge to be kept */ igraph_vector_push_back(&edges, from); igraph_vector_push_back(&edges, to); if (attr) { actedge++; VECTOR(mergeinto)[edge]=actedge; } } pfrom=from; pto=to; } igraph_eit_destroy(&eit); igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_of_nodes, igraph_is_directed(graph))); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &res); IGRAPH_I_ATTRIBUTE_DESTROY(&res); IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ 1, /*vertex=*/ 1, /*edge=*/ 0); if (attr) { igraph_fixed_vectorlist_t vl; IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, actedge+1)); IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl); IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &res, &vl.v, edge_comb)); igraph_fixed_vectorlist_destroy(&vl); igraph_vector_destroy(&mergeinto); IGRAPH_FINALLY_CLEAN(2); } IGRAPH_FINALLY_CLEAN(1); igraph_destroy(graph); *graph=res; return 0; } /** * \ingroup structural * \function igraph_reciprocity * \brief Calculates the reciprocity of a directed graph. * * * The measure of reciprocity defines the proportion of mutual * connections, in a directed graph. It is most commonly defined as * the probability that the opposite counterpart of a directed edge is * also included in the graph. In adjacency matrix notation: * sum(i, j, (A.*A')ij) / sum(i, j, Aij), where * A.*A' is the element-wise product of matrix * A and its transpose. This measure is * calculated if the \p mode argument is \c * IGRAPH_RECIPROCITY_DEFAULT. * * * Prior to igraph version 0.6, another measure was implemented, * defined as the probability of mutual connection between a vertex * pair if we know that there is a (possibly non-mutual) connection * between them. In other words, (unordered) vertex pairs are * classified into three groups: (1) disconnected, (2) * non-reciprocally connected, (3) reciprocally connected. * The result is the size of group (3), divided by the sum of group * sizes (2)+(3). This measure is calculated if \p mode is \c * IGRAPH_RECIPROCITY_RATIO. * * \param graph The graph object. * \param res Pointer to an \c igraph_real_t which will contain the result. * \param ignore_loops Whether to ignore loop edges. * \param mode Type of reciprocity to calculate, possible values are * \c IGRAPH_RECIPROCITY_DEFAULT and \c IGRAPH_RECIPROCITY_RATIO, * please see their description above. * \return Error code: * \c IGRAPH_EINVAL: graph has no edges * \c IGRAPH_ENOMEM: not enough memory for * temporary data. * * Time complexity: O(|V|+|E|), |V| is the number of vertices, * |E| is the number of edges. * * \example examples/simple/igraph_reciprocity.c */ int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, igraph_bool_t ignore_loops, igraph_reciprocity_t mode) { igraph_integer_t nonrec=0, rec=0, loops=0; igraph_vector_t inneis, outneis; long int i; long int no_of_nodes=igraph_vcount(graph); if (mode != IGRAPH_RECIPROCITY_DEFAULT && mode != IGRAPH_RECIPROCITY_RATIO) { IGRAPH_ERROR("Invalid reciprocity type", IGRAPH_EINVAL); } /* THIS IS AN EXIT HERE !!!!!!!!!!!!!! */ if (!igraph_is_directed(graph)) { *res=1.0; return 0; } IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); for (i=0; i VECTOR(outneis)[op]) { nonrec += 1; op++; } else { /* loop edge? */ if (VECTOR(inneis)[ip]==i) { loops += 1; if (!ignore_loops) { rec += 1; } } else { rec += 1; } ip++; op++; } } nonrec += (igraph_vector_size(&inneis)-ip) + (igraph_vector_size(&outneis)-op); } if (mode==IGRAPH_RECIPROCITY_DEFAULT) { if (ignore_loops) { *res= (igraph_real_t) rec/(igraph_ecount(graph)-loops); } else { *res= (igraph_real_t) rec/(igraph_ecount(graph)); } } else if (mode==IGRAPH_RECIPROCITY_RATIO) { *res= (igraph_real_t) rec/(rec+nonrec); } igraph_vector_destroy(&inneis); igraph_vector_destroy(&outneis); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_constraint * \brief Burt's constraint scores. * * * This function calculates Burt's constraint scores for the given * vertices, also known as structural holes. * * * Burt's constraint is higher if ego has less, or mutually stronger * related (i.e. more redundant) contacts. Burt's measure of * constraint, C[i], of vertex i's ego network V[i], is defined for * directed and valued graphs, *
* C[i] = sum( sum( (p[i,q] p[q,j])^2, q in V[i], q != i,j ), j in * V[], j != i) *
* for a graph of order (ie. number of vertices) N, where proportional * tie strengths are defined as *
* p[i,j]=(a[i,j]+a[j,i]) / sum(a[i,k]+a[k,i], k in V[i], k != i), *
* a[i,j] are elements of A and * the latter being the graph adjacency matrix. For isolated vertices, * constraint is undefined. * *
* Burt, R.S. (2004). Structural holes and good ideas. American * Journal of Sociology 110, 349-399. * * * The first R version of this function was contributed by Jeroen * Bruggeman. * \param graph A graph object. * \param res Pointer to an initialized vector, the result will be * stored here. The vector will be resized to have the * appropriate size for holding the result. * \param vids Vertex selector containing the vertices for which the * constraint should be calculated. * \param weights Vector giving the weights of the edges. If it is * \c NULL then each edge is supposed to have the same weight. * \return Error code. * * Time complexity: O(|V|+E|+n*d^2), n is the number of vertices for * which the constraint is calculated and d is the average degree, |V| * is the number of vertices, |E| the number of edges in the * graph. If the weights argument is \c NULL then the time complexity * is O(|V|+n*d^2). */ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, const igraph_vector_t *weights) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vit_t vit; long int nodes_to_calc; long int a, b, c, i, j, q; igraph_integer_t edge, from, to, edge2, from2, to2; igraph_vector_t contrib; igraph_vector_t degree; igraph_vector_t ineis_in, ineis_out, jneis_in, jneis_out; if (weights != 0 && igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&contrib, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&ineis_in, 0); IGRAPH_VECTOR_INIT_FINALLY(&ineis_out, 0); IGRAPH_VECTOR_INIT_FINALLY(&jneis_in, 0); IGRAPH_VECTOR_INIT_FINALLY(&jneis_out, 0); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc=IGRAPH_VIT_SIZE(vit); if (weights==0) { IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); } else { for (a=0; a * The largest in-, out- or total degree of the specified vertices is * calculated. * \param graph The input graph. * \param res Pointer to an integer (\c igraph_integer_t), the result * will be stored here. * \param vids Vector giving the vertex IDs for which the maximum degree will * be calculated. * \param mode Defines the type of the degree. * \c IGRAPH_OUT, out-degree, * \c IGRAPH_IN, in-degree, * \c IGRAPH_ALL, total degree (sum of the * in- and out-degree). * This parameter is ignored for undirected graphs. * \param loops Boolean, gives whether the self-loops should be * counted. * \return Error code: * \c IGRAPH_EINVVID: invalid vertex id. * \c IGRAPH_EINVMODE: invalid mode argument. * * Time complexity: O(v) if * loops is * TRUE, and * O(v*d) * otherwise. v is the number * vertices for which the degree will be calculated, and * d is their (average) degree. */ int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) { igraph_vector_t tmp; IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); igraph_degree(graph, &tmp, vids, mode, loops); *res=(igraph_integer_t) igraph_vector_max(&tmp); igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_density * Calculate the density of a graph. * * The density of a graph is simply the ratio number of * edges and the number of possible edges. Note that density is * ill-defined for graphs with multiple and/or loop edges, so consider * calling \ref igraph_simplify() on the graph if you know that it * contains multiple or loop edges. * \param graph The input graph object. * \param res Pointer to a real number, the result will be stored * here. * \param loops Logical constant, whether to include loops in the * calculation. If this constant is TRUE then * loop edges are thought to be possible in the graph (this does not * necessarily mean that the graph really contains any loops). If * this is FALSE then the result is only correct if the graph does not * contain loops. * \return Error code. * * Time complexity: O(1). */ int igraph_density(const igraph_t *graph, igraph_real_t *res, igraph_bool_t loops) { igraph_integer_t no_of_nodes=igraph_vcount(graph); igraph_real_t no_of_edges=igraph_ecount(graph); igraph_bool_t directed=igraph_is_directed(graph); if (no_of_nodes == 0) { *res = IGRAPH_NAN; return 0; } if (!loops) { if (no_of_nodes == 1) { *res = IGRAPH_NAN; } else if (directed) { *res = no_of_edges / no_of_nodes / (no_of_nodes-1); } else { *res = no_of_edges / no_of_nodes * 2.0 / (no_of_nodes-1); } } else { if (directed) { *res = no_of_edges / no_of_nodes / no_of_nodes; } else { *res = no_of_edges / no_of_nodes * 2.0 / (no_of_nodes+1); } } return 0; } /** * \function igraph_neighborhood_size * \brief Calculates the size of the neighborhood of a given vertex. * * The neighborhood of a given order of a vertex includes all vertices * which are closer to the vertex than the order. Ie. order 0 is * always the vertex itself, order 1 is the vertex plus its immediate * neighbors, order 2 is order 1 plus the immediate neighbors of the * vertices in order 1, etc. * * This function calculates the size of the neighborhood * of the given order for the given vertices. * \param graph The input graph. * \param res Pointer to an initialized vector, the result will be * stored here. It will be resized as needed. * \param vids The vertices for which the calculation is performed. * \param order Integer giving the order of the neighborhood. * \param mode Specifies how to use the direction of the edges if a * directed graph is analyzed. For \c IGRAPH_OUT only the outgoing * edges are followed, so all vertices reachable from the source * vertex in at most \c order steps are counted. For \c IGRAPH_IN * all vertices from which the source vertex is reachable in at most * \c order steps are counted. \c IGRAPH_ALL ignores the direction * of the edges. This argument is ignored for undirected graphs. * \return Error code. * * \sa \ref igraph_neighborhood() for calculating the actual neighborhood, * \ref igraph_neighborhood_graphs() for creating separate graphs from * the neighborhoods. * * Time complexity: O(n*d*o), where n is the number vertices for which * the calculation is performed, d is the average degree, o is the order. */ int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q; igraph_vit_t vit; long int i, j; long int *added; igraph_vector_t neis; if (order < 0) { IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL); } added=igraph_Calloc(no_of_nodes, long int); if (added==0) { IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); for (i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int node=IGRAPH_VIT_GET(vit); long int size=1; added[node]=i+1; igraph_dqueue_clear(&q); if (order > 0) { igraph_dqueue_push(&q, node); igraph_dqueue_push(&q, 0); } while (!igraph_dqueue_empty(&q)) { long int actnode=(long int) igraph_dqueue_pop(&q); long int actdist=(long int) igraph_dqueue_pop(&q); long int n; igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode); n=igraph_vector_size(&neis); if (actdist This function calculates the vertices within the * neighborhood of the specified vertices. * \param graph The input graph. * \param res An initialized pointer vector. Note that the objects * (pointers) in the vector will \em not be freed, but the pointer * vector will be resized as needed. The result of the calculation * will be stored here in \c vector_t objects. * \param vids The vertices for which the calculation is performed. * \param order Integer giving the order of the neighborhood. * \param mode Specifies how to use the direction of the edges if a * directed graph is analyzed. For \c IGRAPH_OUT only the outgoing * edges are followed, so all vertices reachable from the source * vertex in at most \c order steps are included. For \c IGRAPH_IN * all vertices from which the source vertex is reachable in at most * \c order steps are included. \c IGRAPH_ALL ignores the direction * of the edges. This argument is ignored for undirected graphs. * \return Error code. * * \sa \ref igraph_neighborhood_size() to calculate the size of the * neighborhood, \ref igraph_neighborhood_graphs() for creating * graphs from the neighborhoods. * * Time complexity: O(n*d*o), n is the number of vertices for which * the calculation is performed, d is the average degree, o is the * order. */ int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q; igraph_vit_t vit; long int i, j; long int *added; igraph_vector_t neis; igraph_vector_t tmp; igraph_vector_t *newv; if (order < 0) { IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL); } added=igraph_Calloc(no_of_nodes, long int); if (added==0) { IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_vector_ptr_resize(res, IGRAPH_VIT_SIZE(vit))); for (i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int node=IGRAPH_VIT_GET(vit); added[node]=i+1; igraph_vector_clear(&tmp); IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); if (order > 0) { igraph_dqueue_push(&q, node); igraph_dqueue_push(&q, 0); } while (!igraph_dqueue_empty(&q)) { long int actnode=(long int) igraph_dqueue_pop(&q); long int actdist=(long int) igraph_dqueue_pop(&q); long int n; igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode); n=igraph_vector_size(&neis); if (actdist This function finds every vertex in the neighborhood * of a given parameter vertex and creates a graph from these * vertices. * * The first version of this function was written by * Vincent Matossian, thanks Vincent. * \param graph The input graph. * \param res Pointer to a pointer vector, the result will be stored * here, ie. \c res will contain pointers to \c igraph_t * objects. It will be resized if needed but note that the * objects in the pointer vector will not be freed. * \param vids The vertices for which the calculation is performed. * \param order Integer giving the order of the neighborhood. * \param mode Specifies how to use the direction of the edges if a * directed graph is analyzed. For \c IGRAPH_OUT only the outgoing * edges are followed, so all vertices reachable from the source * vertex in at most \c order steps are counted. For \c IGRAPH_IN * all vertices from which the source vertex is reachable in at most * \c order steps are counted. \c IGRAPH_ALL ignores the direction * of the edges. This argument is ignored for undirected graphs. * \return Error code. * * \sa \ref igraph_neighborhood_size() for calculating the neighborhood * sizes only, \ref igraph_neighborhood() for calculating the * neighborhoods (but not creating graphs). * * Time complexity: O(n*(|V|+|E|)), where n is the number vertices for * which the calculation is performed, |V| and |E| are the number of * vertices and edges in the original input graph. */ int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q; igraph_vit_t vit; long int i, j; long int *added; igraph_vector_t neis; igraph_vector_t tmp; igraph_t *newg; if (order < 0) { IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL); } added=igraph_Calloc(no_of_nodes, long int); if (added==0) { IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_vector_ptr_resize(res, IGRAPH_VIT_SIZE(vit))); for (i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int node=IGRAPH_VIT_GET(vit); added[node]=i+1; igraph_vector_clear(&tmp); IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); if (order > 0) { igraph_dqueue_push(&q, node); igraph_dqueue_push(&q, 0); } while (!igraph_dqueue_empty(&q)) { long int actnode=(long int) igraph_dqueue_pop(&q); long int actdist=(long int) igraph_dqueue_pop(&q); long int n; igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode); n=igraph_vector_size(&neis); if (actdist * A topological sorting of a directed acyclic graph is a linear ordering * of its nodes where each node comes before all nodes to which it has * edges. Every DAG has at least one topological sort, and may have many. * This function returns a possible topological sort among them. If the * graph is not acyclic (it has at least one cycle), a partial topological * sort is returned and a warning is issued. * * \param graph The input graph. * \param res Pointer to a vector, the result will be stored here. * It will be resized if needed. * \param mode Specifies how to use the direction of the edges. * For \c IGRAPH_OUT, the sorting order ensures that each node comes * before all nodes to which it has edges, so nodes with no incoming * edges go first. For \c IGRAPH_IN, it is quite the opposite: each * node comes before all nodes from which it receives edges. Nodes * with no outgoing edges go first. * \return Error code. * * Time complexity: O(|V|+|E|), where |V| and |E| are the number of * vertices and edges in the original input graph. * * \sa \ref igraph_is_dag() if you are only interested in whether a given * graph is a DAG or not, or \ref igraph_feedback_arc_set() to find a * set of edges whose removal makes the graph a DAG. * * \example examples/simple/igraph_topological_sorting.c */ int igraph_topological_sorting(const igraph_t* graph, igraph_vector_t *res, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t degrees, neis; igraph_dqueue_t sources; igraph_neimode_t deg_mode; long int node, i, j; if (mode == IGRAPH_ALL || !igraph_is_directed(graph)) { IGRAPH_ERROR("topological sorting does not make sense for undirected graphs", IGRAPH_EINVAL); } else if (mode == IGRAPH_OUT) { deg_mode = IGRAPH_IN; } else if (mode == IGRAPH_IN) { deg_mode = IGRAPH_OUT; } else { IGRAPH_ERROR("invalid mode", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_dqueue_init(&sources, 0)); IGRAPH_FINALLY(igraph_dqueue_destroy, &sources); IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), deg_mode, 0)); igraph_vector_clear(res); /* Do we have nodes with no incoming vertices? */ for (i=0; i * A directed acyclic graph (DAG) is a directed graph with no cycles. * * \param graph The input graph. * \param res Pointer to a boolean constant, the result * is stored here. * \return Error code. * * Time complexity: O(|V|+|E|), where |V| and |E| are the number of * vertices and edges in the original input graph. * * \sa \ref igraph_topological_sorting() to get a possible topological * sorting of a DAG. */ int igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t degrees, neis; igraph_dqueue_t sources; long int node, i, j, nei, vertices_left; if (!igraph_is_directed(graph)) { *res = 0; return IGRAPH_SUCCESS; } IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_dqueue_init(&sources, 0)); IGRAPH_FINALLY(igraph_dqueue_destroy, &sources); IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_OUT, 1)); vertices_left = no_of_nodes; /* Do we have nodes with no incoming edges? */ for (i=0; i * A graph is a simple graph if it does not contain loop edges and * multiple edges. * * \param graph The input graph. * \param res Pointer to a boolean constant, the result * is stored here. * \return Error code. * * \sa \ref igraph_is_loop() and \ref igraph_is_multiple() to * find the loops and multiple edges, \ref igraph_simplify() to * get rid of them, or \ref igraph_has_multiple() to decide whether * there is at least one multiple edge. * * Time complexity: O(|V|+|E|). */ int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { long int vc=igraph_vcount(graph); long int ec=igraph_ecount(graph); if (vc==0 || ec==0) { *res=1; } else { igraph_vector_t neis; long int i, j, n; igraph_bool_t found = 0; IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); for (i=0; i < vc; i++) { igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT); n=igraph_vector_size(&neis); for (j=0; j < n; j++) { if (VECTOR(neis)[j]==i) { found=1; break; } if (j>0 && VECTOR(neis)[j-1]==VECTOR(neis)[j]) { found=1; break; } } } *res=!found; igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_is_loop * \brief Find the loop edges in a graph. * * * A loop edge is an edge from a vertex to itself. * \param graph The input graph. * \param res Pointer to an initialized boolean vector for storing the result, * it will be resized as needed. * \param es The edges to check, for all edges supply \ref igraph_ess_all() here. * \return Error code. * * \sa \ref igraph_simplify() to get rid of loop edges. * * Time complexity: O(e), the number of edges to check. * * \example examples/simple/igraph_is_loop.c */ int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) { igraph_eit_t eit; long int i; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); for (i=0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { long int e=IGRAPH_EIT_GET(eit); VECTOR(*res)[i] = (IGRAPH_FROM(graph, e)==IGRAPH_TO(graph, e)) ? 1 : 0; } igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_has_multiple * \brief Check whether the graph has at least one multiple edge. * * * An edge is a multiple edge if there is another * edge with the same head and tail vertices in the graph. * * \param graph The input graph. * \param res Pointer to a boolean variable, the result will be stored here. * \return Error code. * * \sa \ref igraph_count_multiple(), \ref igraph_is_multiple() and \ref igraph_simplify(). * * Time complexity: O(e*d), e is the number of edges to check and d is the * average degree (out-degree in directed graphs) of the vertices at the * tail of the edges. * * \example examples/simple/igraph_has_multiple.c */ int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { long int vc=igraph_vcount(graph); long int ec=igraph_ecount(graph); igraph_bool_t directed=igraph_is_directed(graph); if (vc==0 || ec==0) { *res=0; } else { igraph_vector_t neis; long int i, j, n; igraph_bool_t found=0; IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); for (i=0; i < vc && !found; i++) { IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT)); n = igraph_vector_size(&neis); for (j=1; j < n; j++) { if (VECTOR(neis)[j-1] == VECTOR(neis)[j]) { /* If the graph is undirected, loop edges appear twice in the neighbor * list, so check the next item as well */ if (directed) { /* Directed, so this is a real multiple edge */ found=1; break; } else if (VECTOR(neis)[j-1] != i) { /* Undirected, but not a loop edge */ found=1; break; } else if (j < n-1 && VECTOR(neis)[j] == VECTOR(neis)[j+1]) { /* Undirected, loop edge, multiple times */ found=1; break; } } } } *res=found; igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_is_multiple * \brief Find the multiple edges in a graph. * * * An edge is a multiple edge if there is another * edge with the same head and tail vertices in the graph. * * * Note that this function returns true only for the second or more * appearances of the multiple edges. * \param graph The input graph. * \param res Pointer to a boolean vector, the result will be stored * here. It will be resized as needed. * \param es The edges to check. Supply \ref igraph_ess_all() if you want * to check all edges. * \return Error code. * * \sa \ref igraph_count_multiple(), \ref igraph_has_multiple() and \ref igraph_simplify(). * * Time complexity: O(e*d), e is the number of edges to check and d is the * average degree (out-degree in directed graphs) of the vertices at the * tail of the edges. * * \example examples/simple/igraph_is_multiple.c */ int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) { igraph_eit_t eit; long int i; igraph_lazy_inclist_t inclist; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); for (i=0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { long int e=IGRAPH_EIT_GET(eit); long int from=IGRAPH_FROM(graph, e); long int to=IGRAPH_TO(graph, e); igraph_vector_t *neis=igraph_lazy_inclist_get(&inclist, (igraph_integer_t) from); long int j, n=igraph_vector_size(neis); VECTOR(*res)[i]=0; for (j=0; j * If the graph has no multiple edges then the result vector will be * filled with ones. * (An edge is a multiple edge if there is another * edge with the same head and tail vertices in the graph.) * * * \param graph The input graph. * \param res Pointer to a vector, the result will be stored * here. It will be resized as needed. * \param es The edges to check. Supply \ref igraph_ess_all() if you want * to check all edges. * \return Error code. * * \sa \ref igraph_is_multiple() and \ref igraph_simplify(). * * Time complexity: O(e*d), e is the number of edges to check and d is the * average degree (out-degree in directed graphs) of the vertices at the * tail of the edges. */ int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es) { igraph_eit_t eit; long int i; igraph_lazy_inclist_t inclist; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); for (i=0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { long int e=IGRAPH_EIT_GET(eit); long int from=IGRAPH_FROM(graph, e); long int to=IGRAPH_TO(graph, e); igraph_vector_t *neis=igraph_lazy_inclist_get(&inclist, (igraph_integer_t) from); long int j, n=igraph_vector_size(neis); VECTOR(*res)[i] = 0; for (j=0; j * The current implementation works for undirected graphs only, * directed graphs are treated as undirected graphs. Loop edges and * multiple edges are ignored. * * If the graph is a forest (ie. acyclic), then zero is returned. * * This implementation is based on Alon Itai and Michael Rodeh: * Finding a minimum circuit in a graph * \emb Proceedings of the ninth annual ACM symposium on Theory of * computing \eme, 1-10, 1977. The first implementation of this * function was done by Keith Briggs, thanks Keith. * \param graph The input graph. * \param girth Pointer to an integer, if not \c NULL then the result * will be stored here. * \param circle Pointer to an initialized vector, the vertex ids in * the shortest circle will be stored here. If \c NULL then it is * ignored. * \return Error code. * * Time complexity: O((|V|+|E|)^2), |V| is the number of vertices, |E| * is the number of edges in the general case. If the graph has no * circles at all then the function needs O(|V|+|E|) time to realize * this and then it stops. * * \example examples/simple/igraph_girth.c */ int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, igraph_vector_t *circle) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q; igraph_lazy_adjlist_t adjlist; long int mincirc=LONG_MAX, minvertex=0; long int node; igraph_bool_t triangle=0; igraph_vector_t *neis; igraph_vector_long_t level; long int stoplevel=no_of_nodes+1; igraph_bool_t anycircle=0; long int t1=0, t2=0; IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_vector_long_init(&level, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &level); for (node=0; !triangle && node=stoplevel) { break; } neis=igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actnode); n=igraph_vector_size(neis); for (i=0; i * The line graph L(G) of a G directed graph is slightly different, * L(G) has one vertex for each edge in G and two vertices in L(G) are connected * by a directed edge if the target of the first vertex's corresponding edge * is the same as the source of the second vertex's corresponding edge. * * * Edge \em i in the original graph will correspond to vertex \em i * in the line graph. * * * The first version of this function was contributed by Vincent Matossian, * thanks. * \param graph The input graph, may be directed or undirected. * \param linegraph Pointer to an uninitialized graph object, the * result is stored here. * \return Error code. * * Time complexity: O(|V|+|E|), the number of edges plus the number of vertices. */ int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph) { if (igraph_is_directed(graph)) { return igraph_i_linegraph_directed(graph, linegraph); } else { return igraph_i_linegraph_undirected(graph, linegraph); } } /** * \function igraph_add_edge * \brief Adds a single edge to a graph. * * * For directed graphs the edge points from \p from to \p to. * * * Note that if you want to add many edges to a big graph, then it is * inefficient to add them one by one, it is better to collect them into * a vector and add all of them via a single \ref igraph_add_edges() call. * \param igraph The graph. * \param from The id of the first vertex of the edge. * \param to The id of the second vertex of the edge. * \return Error code. * * \sa \ref igraph_add_edges() to add many edges, \ref * igraph_delete_edges() to remove edges and \ref * igraph_add_vertices() to add vertices. * * Time complexity: O(|V|+|E|), the number of edges plus the number of * vertices. */ int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to) { igraph_vector_t edges; int ret; IGRAPH_VECTOR_INIT_FINALLY(&edges, 2); VECTOR(edges)[0]=from; VECTOR(edges)[1]=to; IGRAPH_CHECK(ret=igraph_add_edges(graph, &edges, 0)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return ret; } /* * \example examples/simple/graph_convergence_degree.c */ int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, igraph_vector_t *ins, igraph_vector_t *outs) { long int no_of_nodes = igraph_vcount(graph); long int no_of_edges = igraph_ecount(graph); long int i, j, k, n; long int *geodist; igraph_vector_t *eids, *ins_p, *outs_p, ins_v, outs_v; igraph_dqueue_t q; igraph_inclist_t inclist; igraph_bool_t directed = igraph_is_directed(graph); if (result != 0) IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &q); if (ins == 0) { ins_p = &ins_v; IGRAPH_VECTOR_INIT_FINALLY(ins_p, no_of_edges); } else { ins_p = ins; IGRAPH_CHECK(igraph_vector_resize(ins_p, no_of_edges)); igraph_vector_null(ins_p); } if (outs == 0) { outs_p = &outs_v; IGRAPH_VECTOR_INIT_FINALLY(outs_p, no_of_edges); } else { outs_p = outs; IGRAPH_CHECK(igraph_vector_resize(outs_p, no_of_edges)); igraph_vector_null(outs_p); } geodist=igraph_Calloc(no_of_nodes, long int); if (geodist==0) { IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, geodist); /* Collect shortest paths originating from/to every node to correctly * determine input field sizes */ for (k=0; k<(directed?2:1); k++) { igraph_neimode_t neimode = (k==0)?IGRAPH_OUT:IGRAPH_IN; igraph_real_t *vec; IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, neimode)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); vec = (k==0)?VECTOR(*ins_p):VECTOR(*outs_p); for (i=0; i * If there is more than one path with the smallest weight between two vertices, this * function gives only one of them. * \param graph The graph object. * \param vertices The result, the ids of the vertices along the paths. * This is a pointer vector, each element points to a vector * object. These should be initialized before passing them to * the function, which will properly clear and/or resize them * and fill the ids of the vertices along the geodesics from/to * the vertices. Supply a null pointer here if you don't need * these vectors. Normally, either this argument, or the \c * edges should be non-null, but no error or warning is given * if they are both null pointers. * \param edges The result, the ids of the edges along the paths. * This is a pointer vector, each element points to a vector * object. These should be initialized before passing them to * the function, which will properly clear and/or resize them * and fill the ids of the vertices along the geodesics from/to * the vertices. Supply a null pointer here if you don't need * these vectors. Normally, either this argument, or the \c * vertices should be non-null, but no error or warning is given * if they are both null pointers. * \param from The id of the vertex from/to which the geodesics are * calculated. * \param to Vertex sequence with the ids of the vertices to/from which the * shortest paths will be calculated. A vertex might be given multiple * times. * \param weights a vector holding the edge weights. All weights must be * positive. * \param mode The type of shortest paths to be use for the * calculation in directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing paths are calculated. * \cli IGRAPH_IN * the incoming paths are calculated. * \cli IGRAPH_ALL * the directed graph is considered as an * undirected one for the computation. * \endclist * \param predecessors A pointer to an initialized igraph vector or null. * If not null, a vector containing the predecessor of each vertex in * the single source shortest path tree is returned here. The * predecessor of vertex i in the tree is the vertex from which vertex i * was reached. The predecessor of the start vertex (in the \c from * argument) is itself by definition. If the predecessor is -1, it means * that the given vertex was not reached from the source during the * search. Note that the search terminates if all the vertices in * \c to are reached. * \param inbound_edges A pointer to an initialized igraph vector or null. * If not null, a vector containing the inbound edge of each vertex in * the single source shortest path tree is returned here. The * inbound edge of vertex i in the tree is the edge via which vertex i * was reached. The start vertex and vertices that were not reached * during the search will have -1 in the corresponding entry of the * vector. Note that the search terminates if all the vertices in * \c to are reached. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * \p from is invalid vertex id, or the length of \p to is * not the same as the length of \p res. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(|E|log|E|+|V|), where |V| is the number of * vertices and |E| is the number of edges * * \sa \ref igraph_shortest_paths_dijkstra() if you only need the path length but * not the paths themselves, \ref igraph_get_shortest_paths() if all edge * weights are equal. * * \example examples/simple/igraph_get_shortest_paths_dijkstra.c */ int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, igraph_vector_ptr_t *vertices, igraph_vector_ptr_t *edges, igraph_integer_t from, igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode, igraph_vector_long_t *predecessors, igraph_vector_long_t *inbound_edges) { /* Implementation details. This is the basic Dijkstra algorithm, with a binary heap. The heap is indexed, i.e. it stores not only the distances, but also which vertex they belong to. The other mapping, i.e. getting the distance for a vertex is not in the heap (that would by the double-indexed heap), but in the result matrix. Dirty tricks: - the opposite of the distance is stored in the heap, as it is a maximum heap and we need a minimum heap. - we don't use IGRAPH_INFINITY in the distance vector during the computation, as IGRAPH_FINITE() might involve a function call and we want to spare that. So we store distance+1.0 instead of distance, and zero denotes infinity. - `parents' assigns the inbound edge IDs of all vertices in the shortest path tree to the vertices. In this implementation, the edge ID + 1 is stored, zero means unreachable vertices. */ long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vit_t vit; igraph_2wheap_t Q; igraph_lazy_inclist_t inclist; igraph_vector_t dists; long int *parents; igraph_bool_t *is_target; long int i,to_reach; if (!weights) { return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, predecessors, inbound_edges); } if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); } if (igraph_vector_min(weights) < 0) { IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); if (vertices && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(vertices)) { IGRAPH_ERROR("Size of `vertices' and `to' should match", IGRAPH_EINVAL); } if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) { IGRAPH_ERROR("Size of `edges' and `to' should match", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); igraph_vector_fill(&dists, -1.0); parents = igraph_Calloc(no_of_nodes, long int); if (parents == 0) IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, parents); is_target = igraph_Calloc(no_of_nodes, igraph_bool_t); if (is_target == 0) IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, is_target); /* Mark the vertices we need to reach */ to_reach=IGRAPH_VIT_SIZE(vit); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { if (!is_target[ (long int) IGRAPH_VIT_GET(vit) ]) { is_target[ (long int) IGRAPH_VIT_GET(vit) ] = 1; } else { to_reach--; /* this node was given multiple times */ } } VECTOR(dists)[(long int)from] = 0.0; /* zero distance */ parents[(long int)from] = 0; igraph_2wheap_push_with_index(&Q, from, 0); while (!igraph_2wheap_empty(&Q) && to_reach > 0) { long int nlen, minnei=igraph_2wheap_max_index(&Q); igraph_real_t mindist=-igraph_2wheap_delete_max(&Q); igraph_vector_t *neis; IGRAPH_ALLOW_INTERRUPTION(); if (is_target[minnei]) { is_target[minnei] = 0; to_reach--; } /* Now check all neighbors of 'minnei' for a shorter path */ neis=igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); nlen=igraph_vector_size(neis); for (i=0; i 0) IGRAPH_WARNING("Couldn't reach some vertices"); /* Create `predecessors' if needed */ if (predecessors) { IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { if (i == from) { /* i is the start vertex */ VECTOR(*predecessors)[i] = i; } else if (parents[i] <= 0) { /* i was not reached */ VECTOR(*predecessors)[i] = -1; } else { /* i was reached via the edge with ID = parents[i] - 1 */ VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, parents[i]-1, i); } } } /* Create `inbound_edges' if needed */ if (inbound_edges) { IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { if (parents[i] <= 0) { /* i was not reached */ VECTOR(*inbound_edges)[i] = -1; } else { /* i was reached via the edge with ID = parents[i] - 1 */ VECTOR(*inbound_edges)[i] = parents[i]-1; } } } /* Reconstruct the shortest paths based on vertex and/or edge IDs */ if (vertices || edges) { for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int node=IGRAPH_VIT_GET(vit); igraph_vector_t *vvec=0, *evec=0; if (vertices) { vvec=VECTOR(*vertices)[i]; igraph_vector_clear(vvec); } if (edges) { evec=VECTOR(*edges)[i]; igraph_vector_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); if (parents[node]>0) { long int size=0; long int act=node; long int edge; while (parents[act]) { size++; edge=parents[act]-1; act=IGRAPH_OTHER(graph, edge, act); } if (vvec) { IGRAPH_CHECK(igraph_vector_resize(vvec, size+1)); VECTOR(*vvec)[size]=node; } if (evec) { IGRAPH_CHECK(igraph_vector_resize(evec, size)); } act=node; while (parents[act]) { edge=parents[act]-1; act=IGRAPH_OTHER(graph, edge, act); size--; if (vvec) { VECTOR(*vvec)[size]=act; } if (evec) { VECTOR(*evec)[size]=edge; } } } } } igraph_lazy_inclist_destroy(&inclist); igraph_2wheap_destroy(&Q); igraph_vector_destroy(&dists); igraph_Free(is_target); igraph_Free(parents); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(6); return 0; } /** * \function igraph_get_shortest_path_dijkstra * Weighted shortest path from one vertex to another one. * * Calculates a single (positively) weighted shortest path from * a single vertex to another one, using Dijkstra's algorithm. * * This function is a special case (and a wrapper) to * \ref igraph_get_shortest_paths_dijkstra(). * * \param graph The input graph, it can be directed or undirected. * \param vertices Pointer to an initialized vector or a null * pointer. If not a null pointer, then the vertex ids along * the path are stored here, including the source and target * vertices. * \param edges Pointer to an uninitialized vector or a null * pointer. If not a null pointer, then the edge ids along the * path are stored here. * \param from The id of the source vertex. * \param to The id of the target vertex. * \param weights Vector of edge weights, in the order of edge * ids. They must be non-negative, otherwise the algorithm does * not work. * \param mode A constant specifying how edge directions are * considered in directed graphs. \c IGRAPH_OUT follows edge * directions, \c IGRAPH_IN follows the opposite directions, * and \c IGRAPH_ALL ignores edge directions. This argument is * ignored for undirected graphs. * \return Error code. * * Time complexity: O(|E|log|E|+|V|), |V| is the number of vertices, * |E| is the number of edges in the graph. * * \sa \ref igraph_get_shortest_paths_dijkstra() for the version with * more target vertices. */ int igraph_get_shortest_path_dijkstra(const igraph_t *graph, igraph_vector_t *vertices, igraph_vector_t *edges, igraph_integer_t from, igraph_integer_t to, const igraph_vector_t *weights, igraph_neimode_t mode) { igraph_vector_ptr_t vertices2, *vp=&vertices2; igraph_vector_ptr_t edges2, *ep=&edges2; if (vertices) { IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2); VECTOR(vertices2)[0]=vertices; } else { vp=0; } if (edges) { IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); VECTOR(edges2)[0]=edges; } else { ep=0; } IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph, vp, ep, from, igraph_vss_1(to), weights, mode, 0, 0)); if (edges) { igraph_vector_ptr_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { igraph_vector_ptr_destroy(&vertices2); IGRAPH_FINALLY_CLEAN(1); } return 0; } int igraph_i_vector_tail_cmp(const void* path1, const void* path2); /* Compares two paths based on their last elements. Required by * igraph_get_all_shortest_paths_dijkstra to put the final result * in order. Assumes that both paths are pointers to igraph_vector_t * objects and that they are not empty */ int igraph_i_vector_tail_cmp(const void* path1, const void* path2) { return (int) (igraph_vector_tail(*(const igraph_vector_t**)path1) - igraph_vector_tail(*(const igraph_vector_t**)path2)); } /** * \ingroup structural * \function igraph_get_all_shortest_paths_dijkstra * \brief Finds all shortest paths (geodesics) from a vertex to all other vertices. * * \param graph The graph object. * \param res Pointer to an initialized pointer vector, the result * will be stored here in igraph_vector_t objects. Each vector * object contains the vertices along a shortest path from \p from * to another vertex. The vectors are ordered according to their * target vertex: first the shortest paths to vertex 0, then to * vertex 1, etc. No data is included for unreachable vertices. * \param nrgeo Pointer to an initialized igraph_vector_t object or * NULL. If not NULL the number of shortest paths from \p from are * stored here for every vertex in the graph. Note that the values * will be accurate only for those vertices that are in the target * vertex sequence (see \p to), since the search terminates as soon * as all the target vertices have been found. * \param from The id of the vertex from/to which the geodesics are * calculated. * \param to Vertex sequence with the ids of the vertices to/from which the * shortest paths will be calculated. A vertex might be given multiple * times. * \param weights a vector holding the edge weights. All weights must be * non-negative. * \param mode The type of shortest paths to be use for the * calculation in directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing paths are calculated. * \cli IGRAPH_IN * the incoming paths are calculated. * \cli IGRAPH_ALL * the directed graph is considered as an * undirected one for the computation. * \endclist * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * \p from is invalid vertex id, or the length of \p to is * not the same as the length of \p res. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(|E|log|E|+|V|), where |V| is the number of * vertices and |E| is the number of edges * * \sa \ref igraph_shortest_paths_dijkstra() if you only need the path * length but not the paths themselves, \ref igraph_get_all_shortest_paths() * if all edge weights are equal. * * \example examples/simple/igraph_get_all_shortest_paths_dijkstra.c */ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_vector_t *nrgeo, igraph_integer_t from, igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode) { /* Implementation details: see igraph_get_shortest_paths_dijkstra, it's basically the same. */ long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_vit_t vit; igraph_2wheap_t Q; igraph_lazy_inclist_t inclist; igraph_vector_t dists, order; igraph_vector_ptr_t parents; unsigned char *is_target; long int i, n, to_reach; if (!weights) { return igraph_get_all_shortest_paths(graph, res, nrgeo, from, to, mode); } if (res == 0 && nrgeo == 0) return IGRAPH_SUCCESS; if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); } if (igraph_vector_min(weights) < 0) { IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); } /* parents stores a vector for each vertex, listing the parent vertices * of each vertex in the traversal */ IGRAPH_CHECK(igraph_vector_ptr_init(&parents, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &parents); igraph_vector_ptr_set_item_destructor(&parents, (igraph_finally_func_t*)igraph_vector_destroy); for (i = 0; i < no_of_nodes; i++) { igraph_vector_t* parent_vec; parent_vec = igraph_Calloc(1, igraph_vector_t); if (parent_vec == 0) IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths", IGRAPH_ENOMEM); IGRAPH_CHECK(igraph_vector_init(parent_vec, 0)); VECTOR(parents)[i] = parent_vec; } /* distance of each vertex from the root */ IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); igraph_vector_fill(&dists, -1.0); /* order lists the order of vertices in which they were found during * the traversal */ IGRAPH_VECTOR_INIT_FINALLY(&order, 0); /* boolean array to mark whether a given vertex is a target or not */ is_target = igraph_Calloc(no_of_nodes, unsigned char); if (is_target == 0) IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, is_target); /* two-way heap storing vertices and distances */ IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); /* lazy adjacency edge list to query neighbours efficiently */ IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); /* Mark the vertices we need to reach */ IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); to_reach=IGRAPH_VIT_SIZE(vit); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { if (!is_target[ (long int) IGRAPH_VIT_GET(vit) ]) { is_target[ (long int) IGRAPH_VIT_GET(vit) ] = 1; } else { to_reach--; /* this node was given multiple times */ } } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); VECTOR(dists)[(long int)from] = 0.0; /* zero distance */ igraph_2wheap_push_with_index(&Q, from, 0); while (!igraph_2wheap_empty(&Q) && to_reach > 0) { long int nlen, minnei=igraph_2wheap_max_index(&Q); igraph_real_t mindist=-igraph_2wheap_delete_max(&Q); igraph_vector_t *neis; IGRAPH_ALLOW_INTERRUPTION(); /* printf("Reached vertex %ld, is_target[%ld] = %d, %ld to go\n", minnei, minnei, (int)is_target[minnei], to_reach - is_target[minnei]); */ if (is_target[minnei]) { is_target[minnei] = 0; to_reach--; } /* Mark that we have reached this vertex */ IGRAPH_CHECK(igraph_vector_push_back(&order, minnei)); /* Now check all neighbors of 'minnei' for a shorter path */ neis=igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); nlen=igraph_vector_size(neis); for (i=0; i 0) { /* This is an alternative path with exactly the same length. * Note that we consider this case only if the edge via which we * reached the node has a nonzero weight; otherwise we could create * infinite loops in undirected graphs by traversing zero-weight edges * back-and-forth */ parent_vec = (igraph_vector_t*)VECTOR(parents)[tto]; IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei)); } else if (altdist < curdist) { /* This is a shorter path */ VECTOR(dists)[tto] = altdist; parent_vec = (igraph_vector_t*)VECTOR(parents)[tto]; igraph_vector_clear(parent_vec); IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei)); IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); } } } /* !igraph_2wheap_empty(&Q) */ if (to_reach > 0) IGRAPH_WARNING("Couldn't reach some vertices"); /* we don't need these anymore */ igraph_lazy_inclist_destroy(&inclist); igraph_2wheap_destroy(&Q); IGRAPH_FINALLY_CLEAN(2); /* printf("Order:\n"); igraph_vector_print(&order); printf("Parent vertices:\n"); for (i = 0; i < no_of_nodes; i++) { if (igraph_vector_size(VECTOR(parents)[i]) > 0) { printf("[%ld]: ", (long int)i); igraph_vector_print(VECTOR(parents)[i]); } } */ if (nrgeo) { IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes)); igraph_vector_null(nrgeo); /* Theoretically, we could calculate nrgeo in parallel with the traversal. * However, that way we would have to check whether nrgeo is null or not * every time we want to update some element in nrgeo. Since we need the * order vector anyway for building the final result, we could just as well * build nrgeo here. */ VECTOR(*nrgeo)[(long int)from] = 1; n = igraph_vector_size(&order); for (i = 1; i < n; i++) { long int node, j, k; igraph_vector_t *parent_vec; node = (long int)VECTOR(order)[i]; /* now, take the parent vertices */ parent_vec = (igraph_vector_t*)VECTOR(parents)[node]; k = igraph_vector_size(parent_vec); for (j = 0; j < k; j++) { VECTOR(*nrgeo)[node] += VECTOR(*nrgeo)[(long int)VECTOR(*parent_vec)[j]]; } } } if (res) { igraph_vector_t *path, *paths_index, *parent_vec; igraph_stack_t stack; long int j, node; /* a shortest path from the starting vertex to vertex i can be * obtained by calculating the shortest paths from the "parents" * of vertex i in the traversal. Knowing which of the vertices * are "targets" (see is_target), we can collect for which other * vertices do we need to calculate the shortest paths. We reuse * is_target for that; is_target = 0 means that we don't need the * vertex, is_target = 1 means that the vertex is a target (hence * we need it), is_target = 2 means that the vertex is not a target * but it stands between a shortest path between the root and one * of the targets */ if (igraph_vs_is_all(&to)) { memset(is_target, 1, sizeof(unsigned char) * (size_t) no_of_nodes); } else { memset(is_target, 0, sizeof(unsigned char) * (size_t) no_of_nodes); IGRAPH_CHECK(igraph_stack_init(&stack, 0)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); /* Add the target vertices to the queue */ IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { i = (long int) IGRAPH_VIT_GET(vit); if (!is_target[i]) { is_target[i] = 1; IGRAPH_CHECK(igraph_stack_push(&stack, i)); } } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); while (!igraph_stack_empty(&stack)) { /* For each parent of node i, get its parents */ igraph_real_t el=igraph_stack_pop(&stack); parent_vec = (igraph_vector_t*)VECTOR(parents)[(long int) el]; i = igraph_vector_size(parent_vec); for (j = 0; j < i; j++) { /* For each parent, check if it's already in the stack. * If not, push it and mark it in is_target */ n = (long int) VECTOR(*parent_vec)[j]; if (!is_target[n]) { is_target[n] = 2; IGRAPH_CHECK(igraph_stack_push(&stack, n)); } } } igraph_stack_destroy(&stack); IGRAPH_FINALLY_CLEAN(1); } /* now, reconstruct the shortest paths from the parent list in the * order we've found the nodes during the traversal. * dists is being re-used as a vector where element i tells the * index in res where the shortest paths leading to vertex i * start, plus one (so that zero means that there are no paths * for a given vertex). */ paths_index = &dists; n = igraph_vector_size(&order); igraph_vector_null(paths_index); /* clear the paths vector */ igraph_vector_ptr_clear(res); igraph_vector_ptr_set_item_destructor(res, (igraph_finally_func_t*)igraph_vector_destroy); /* by definition, the shortest path leading to the starting vertex * consists of the vertex itself only */ path = igraph_Calloc(1, igraph_vector_t); if (path == 0) IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths_dijkstra", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, path); IGRAPH_CHECK(igraph_vector_init(path, 1)); IGRAPH_CHECK(igraph_vector_ptr_push_back(res, path)); IGRAPH_FINALLY_CLEAN(1); /* ownership of path passed to res */ VECTOR(*path)[0] = from; VECTOR(*paths_index)[(long int)from] = 1; for (i = 1; i < n; i++) { long int m, path_count; igraph_vector_t *parent_path; node = (long int) VECTOR(order)[i]; /* if we don't need the shortest paths for this node (because * it is not standing in a shortest path between the source * node and any of the target nodes), skip it */ if (!is_target[node]) continue; IGRAPH_ALLOW_INTERRUPTION(); /* we are calculating the shortest paths of node now. */ /* first, we update the paths_index */ path_count = igraph_vector_ptr_size(res); VECTOR(*paths_index)[node] = path_count+1; /* res_end = (igraph_vector_t*)&(VECTOR(*res)[path_count]); */ /* now, take the parent vertices */ parent_vec = (igraph_vector_t*)VECTOR(parents)[node]; m = igraph_vector_size(parent_vec); /* printf("Calculating shortest paths to vertex %ld\n", node); printf("Parents are: "); igraph_vector_print(parent_vec); */ for (j = 0; j < m; j++) { /* for each parent, copy the shortest paths leading to that parent * and add the current vertex in the end */ long int parent_node = (long int) VECTOR(*parent_vec)[j]; long int parent_path_idx = (long int) VECTOR(*paths_index)[parent_node] - 1; /* printf(" Considering parent: %ld\n", parent_node); printf(" Paths to parent start at index %ld in res\n", parent_path_idx); */ assert(parent_path_idx >= 0); for (; parent_path_idx < path_count; parent_path_idx++) { parent_path = (igraph_vector_t*)VECTOR(*res)[parent_path_idx]; if (igraph_vector_tail(parent_path) != parent_node) break; path = igraph_Calloc(1, igraph_vector_t); if (path == 0) IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths_dijkstra", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, path); IGRAPH_CHECK(igraph_vector_copy(path, parent_path)); IGRAPH_CHECK(igraph_vector_ptr_push_back(res, path)); IGRAPH_FINALLY_CLEAN(1); /* ownership of path passed to res */ IGRAPH_CHECK(igraph_vector_push_back(path, node)); } } } /* remove the destructor from the path vector */ igraph_vector_ptr_set_item_destructor(res, 0); /* free those paths from the result vector which we won't need */ n = igraph_vector_ptr_size(res); j = 0; for (i = 0; i < n; i++) { igraph_real_t tmp; path = (igraph_vector_t*)VECTOR(*res)[i]; tmp=igraph_vector_tail(path); if (is_target[(long int)tmp] == 1) { /* we need this path, keep it */ VECTOR(*res)[j] = path; j++; } else { /* we don't need this path, free it */ igraph_vector_destroy(path); free(path); } } IGRAPH_CHECK(igraph_vector_ptr_resize(res, j)); /* sort the paths by the target vertices */ igraph_vector_ptr_sort(res, igraph_i_vector_tail_cmp); } /* free the allocated memory */ igraph_vector_destroy(&order); igraph_Free(is_target); igraph_vector_destroy(&dists); igraph_vector_ptr_destroy_all(&parents); IGRAPH_FINALLY_CLEAN(4); return 0; } /** * \function igraph_shortest_paths_bellman_ford * Weighted shortest paths from some sources allowing negative weights. * * This function is the Bellman-Ford algorithm to find the weighted * shortest paths to all vertices from a single source. (It is run * independently for the given sources.). If there are no negative * weights, you are better off with \ref igraph_shortest_paths_dijkstra() . * * \param graph The input graph, can be directed. * \param res The result, a matrix. A pointer to an initialized matrix * should be passed here, the matrix will be resized if needed. * Each row contains the distances from a single source, to all * vertices in the graph, in the order of vertex ids. For unreachable * vertices the matrix contains \c IGRAPH_INFINITY. * \param from The source vertices. * \param weights The edge weights. There mustn't be any closed loop in * the graph that has a negative total weight (since this would allow * us to decrease the weight of any path containing at least a single * vertex of this loop infinitely). If this is a null pointer, then the * unweighted version, \ref igraph_shortest_paths() is called. * \param mode For directed graphs; whether to follow paths along edge * directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or * ignore edge directions completely (\c IGRAPH_ALL). It is ignored * for undirected graphs. * \return Error code. * * Time complexity: O(s*|E|*|V|), where |V| is the number of * vertices, |E| the number of edges and s the number of sources. * * \sa \ref igraph_shortest_paths() for a faster unweighted version * or \ref igraph_shortest_paths_dijkstra() if you do not have negative * edge weights. * * \example examples/simple/bellman_ford.c */ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t from, const igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_lazy_inclist_t inclist; long int i,j,k; long int no_of_from, no_of_to; igraph_dqueue_t Q; igraph_vector_t clean_vertices; igraph_vector_t num_queued; igraph_vit_t fromvit, tovit; igraph_real_t my_infinity=IGRAPH_INFINITY; igraph_bool_t all_to; igraph_vector_t dist; /* - speedup: a vertex is marked clean if its distance from the source did not change during the last phase. Neighbors of a clean vertex are not relaxed again, since it would mean no change in the shortest path values. Dirty vertices are queued. Negative loops can be detected by checking whether a vertex has been queued at least n times. */ if (!weights) { return igraph_shortest_paths(graph, res, from, to, mode); } if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); no_of_from=IGRAPH_VIT_SIZE(fromvit); IGRAPH_DQUEUE_INIT_FINALLY(&Q, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&clean_vertices, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&num_queued, no_of_nodes); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); if ( (all_to=igraph_vs_is_all(&to)) ) { no_of_to=no_of_nodes; } else { IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); IGRAPH_FINALLY(igraph_vit_destroy, &tovit); no_of_to=IGRAPH_VIT_SIZE(tovit); } IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); for (IGRAPH_VIT_RESET(fromvit), i=0; !IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { long int source=IGRAPH_VIT_GET(fromvit); igraph_vector_fill(&dist, my_infinity); VECTOR(dist)[source] = 0; igraph_vector_null(&clean_vertices); igraph_vector_null(&num_queued); /* Fill the queue with vertices to be checked */ for (j=0; j no_of_nodes) IGRAPH_ERROR("cannot run Bellman-Ford algorithm", IGRAPH_ENEGLOOP); /* If we cannot get to j in finite time yet, there is no need to relax * its edges */ if (!IGRAPH_FINITE(VECTOR(dist)[j])) continue; neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j); nlen = igraph_vector_size(neis); for (k=0; k VECTOR(dist)[j] + VECTOR(*weights)[nei]) { /* relax the edge */ VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei]; if (VECTOR(clean_vertices)[target]) { VECTOR(clean_vertices)[target] = 0; IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); } } } } /* Copy it to the result */ if (all_to) { igraph_matrix_set_row(res, &dist, i); } else { for (IGRAPH_VIT_RESET(tovit), j=0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), j++) { long int v=IGRAPH_VIT_GET(tovit); MATRIX(*res, i, j) = VECTOR(dist)[v]; } } } igraph_vector_destroy(&dist); IGRAPH_FINALLY_CLEAN(1); if (!all_to) { igraph_vit_destroy(&tovit); IGRAPH_FINALLY_CLEAN(1); } igraph_vit_destroy(&fromvit); igraph_dqueue_destroy(&Q); igraph_vector_destroy(&clean_vertices); igraph_vector_destroy(&num_queued); igraph_lazy_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(5); return 0; } /** * \function igraph_shortest_paths_johnson * Calculate shortest paths from some sources using Johnson's algorithm. * * See Wikipedia at http://en.wikipedia.org/wiki/Johnson's_algorithm * for Johnson's algorithm. This algorithm works even if the graph * contains negative edge weights, and it is worth using it if we * calculate the shortest paths from many sources. * * If no edge weights are supplied, then the unweighted * version, \ref igraph_shortest_paths() is called. * * If all the supplied edge weights are non-negative, * then Dijkstra's algorithm is used by calling * \ref igraph_shortest_paths_dijkstra(). * * \param graph The input graph, typically it is directed. * \param res Pointer to an initialized matrix, the result will be * stored here, one line for each source vertex, one column for each * target vertex. * \param from The source vertices. * \param to The target vertices. It is not allowed to include a * vertex twice or more. * \param weights Optional edge weights. If it is a null-pointer, then * the unweighted breadth-first search based \ref * igraph_shortest_paths() will be called. * \return Error code. * * Time complexity: O(s|V|log|V|+|V||E|), |V| and |E| are the number * of vertices and edges, s is the number of source vertices. * * \sa \ref igraph_shortest_paths() for a faster unweighted version * or \ref igraph_shortest_paths_dijkstra() if you do not have negative * edge weights, \ref igraph_shortest_paths_bellman_ford() if you only * need to calculate shortest paths from a couple of sources. */ int igraph_shortest_paths_johnson(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t from, const igraph_vs_t to, const igraph_vector_t *weights) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_t newgraph; igraph_vector_t edges, newweights; igraph_matrix_t bfres; long int i, ptr; long int nr, nc; igraph_vit_t fromvit; /* If no weights, then we can just run the unweighted version */ if (!weights) { return igraph_shortest_paths(graph, res, from, to, IGRAPH_OUT); } if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); } /* If no negative weights, then we can run Dijkstra's algorithm */ if (igraph_vector_min(weights) >= 0) { return igraph_shortest_paths_dijkstra(graph, res, from, to, weights, IGRAPH_OUT); } if (!igraph_is_directed(graph)) { IGRAPH_ERROR("Johnson's shortest path: undirected graph and negative weight", IGRAPH_EINVAL); } /* ------------------------------------------------------------ */ /* -------------------- Otherwise proceed --------------------- */ IGRAPH_MATRIX_INIT_FINALLY(&bfres, 0, 0); IGRAPH_VECTOR_INIT_FINALLY(&newweights, 0); IGRAPH_CHECK(igraph_empty(&newgraph, (igraph_integer_t) no_of_nodes+1, igraph_is_directed(graph))); IGRAPH_FINALLY(igraph_destroy, &newgraph); /* Add a new node to the graph, plus edges from it to all the others. */ IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2 + no_of_nodes*2); igraph_get_edgelist(graph, &edges, /*bycol=*/ 0); igraph_vector_resize(&edges, no_of_edges * 2 + no_of_nodes * 2); for (i=0, ptr=no_of_edges*2; i no_of_edges+1 ? no_of_nodes : no_of_edges+1)); for (i=0; i * * An undirected graph only has mutual edges, by definition. * * * Edge multiplicity is not considered here, e.g. if there are two * (A,B) edges and one (B,A) edge, then all three are considered to be * mutual. * * \param graph The input graph. * \param res Pointer to an initialized vector, the result is stored * here. * \param es The sequence of edges to check. Supply * igraph_ess_all() for all edges, see \ref * igraph_ess_all(). * \return Error code. * * Time complexity: O(n log(d)), n is the number of edges supplied, d * is the maximum in-degree of the vertices that are targets of the * supplied edges. An upper limit of the time complexity is O(n log(|E|)), * |E| is the number of edges in the graph. */ int igraph_is_mutual(igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) { igraph_eit_t eit; igraph_lazy_adjlist_t adjlist; long int i; /* How many edges do we have? */ IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); /* An undirected graph has mutual edges by definition, res is already properly resized */ if (! igraph_is_directed(graph)) { igraph_vector_bool_fill(res, 1); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); return 0; } IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_DONT_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); for (i=0; ! IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { long int edge=IGRAPH_EIT_GET(eit); long int from=IGRAPH_FROM(graph, edge); long int to=IGRAPH_TO(graph, edge); /* Check whether there is a to->from edge, search for from in the out-list of to. We don't search an empty vector, because vector_binsearch seems to have a bug with this. */ igraph_vector_t *neis=igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) to); if (igraph_vector_empty(neis)) { VECTOR(*res)[i]=0; } else { VECTOR(*res)[i]=igraph_vector_binsearch2(neis, from); } } igraph_lazy_adjlist_destroy(&adjlist); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, igraph_vs_t vids, igraph_vector_t *knn, igraph_vector_t *knnk, const igraph_vector_t *weights); int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, igraph_vs_t vids, igraph_vector_t *knn, igraph_vector_t *knnk, const igraph_vector_t *weights) { long int no_of_nodes = igraph_vcount(graph); igraph_vector_t neis; long int i, j, no_vids; igraph_vit_t vit; igraph_vector_t my_knn_v, *my_knn=knn; igraph_vector_t deg; long int maxdeg; igraph_integer_t maxdeg2; igraph_vector_t deghist; igraph_real_t mynan=IGRAPH_NAN; if (igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector size", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); no_vids=IGRAPH_VIT_SIZE(vit); if (!knn) { IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids); my_knn=&my_knn_v; } else { IGRAPH_CHECK(igraph_vector_resize(knn, no_vids)); } IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); IGRAPH_CHECK(igraph_strength(graph, °, igraph_vss_all(), /*mode=*/ IGRAPH_ALL, /*loops=*/ 1, weights)); IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg2, igraph_vss_all(), /*mode=*/ IGRAPH_ALL, /*loops=*/ 1)); maxdeg=maxdeg2; IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); igraph_vector_resize(&neis, 0); if (knnk) { IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg)); igraph_vector_null(knnk); IGRAPH_VECTOR_INIT_FINALLY(°hist, maxdeg); } for (i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { igraph_real_t sum=0.0; long int v=IGRAPH_VIT_GET(vit); long int nv; igraph_real_t str=VECTOR(deg)[v]; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, IGRAPH_ALL)); nv=igraph_vector_size(&neis); for (j=0; jFor isolate vertices \p knn is set to \c * IGRAPH_NAN. The same is done in \p knnk for vertex degrees that * don't appear in the graph. * * \param graph The input graph, it can be directed but the * directedness of the edges is ignored. * \param vids The vertices for which the calculation is performed. * \param knn Pointer to an initialized vector, the result will be * stored here. It will be resized as needed. Supply a NULL pointer * here, if you only want to calculate \c knnk. * \param knnk Pointer to an initialized vector, the average nearest * neighbor degree in the function of vertex degree is stored * here. The first (zeroth) element is for degree one vertices, * etc. Supply a NULL pointer here if you don't want to calculate * this. * \param weights Optional edge weights. Supply a null pointer here * for the non-weighted version. If this is not a null pointer, then * the strength of the vertices is used instead of the normal vertex * degree, see \ref igraph_strength(). * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. * * \example examples/simple/igraph_knn.c */ int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vs_t vids, igraph_vector_t *knn, igraph_vector_t *knnk, const igraph_vector_t *weights) { long int no_of_nodes = igraph_vcount(graph); igraph_vector_t neis; long int i, j, no_vids; igraph_vit_t vit; igraph_vector_t my_knn_v, *my_knn=knn; igraph_vector_t deg; long int maxdeg; igraph_vector_t deghist; igraph_real_t mynan=IGRAPH_NAN; igraph_bool_t simple; IGRAPH_CHECK(igraph_is_simple(graph, &simple)); if (!simple) { IGRAPH_ERROR("Average nearest neighbor degree Works only with " "simple graphs", IGRAPH_EINVAL); } if (weights) { return igraph_i_avg_nearest_neighbor_degree_weighted(graph, vids, knn, knnk, weights); } IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); no_vids=IGRAPH_VIT_SIZE(vit); if (!knn) { IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids); my_knn=&my_knn_v; } else { IGRAPH_CHECK(igraph_vector_resize(knn, no_vids)); } IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), /*mode=*/ IGRAPH_ALL, /*loops*/ 1)); maxdeg=(long int) igraph_vector_max(°); IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); igraph_vector_resize(&neis, 0); if (knnk) { IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg)); igraph_vector_null(knnk); IGRAPH_VECTOR_INIT_FINALLY(°hist, maxdeg); } for (i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { igraph_real_t sum=0.0; long int v=IGRAPH_VIT_GET(vit); long int nv=(long int) VECTOR(deg)[v]; IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, IGRAPH_ALL)); for (j=0; j res) { res=mindist; from=source; to=minnei; } nodes_reached++; neis=igraph_inclist_get(&inclist, minnei); nlen=igraph_vector_size(neis); for (j=0; j 0) last = (long int) igraph_vector_max(mapping); for (e=0; e last) { last = nfrom; } if (nto > last) { last = nto; } } no_new_vertices = last+1; IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_new_vertices, igraph_is_directed(graph))); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &res); IGRAPH_I_ATTRIBUTE_DESTROY(&res); IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ 1, /*vertex=*/ 0, /*edge=*/ 1); if (vattr) { long int i; igraph_vector_ptr_t merges; igraph_vector_t sizes; igraph_vector_t *vecs; vecs=igraph_Calloc(no_new_vertices, igraph_vector_t); if (!vecs) { IGRAPH_ERROR("Cannot combine attributes while contracting" " vertices", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vecs); IGRAPH_CHECK(igraph_vector_ptr_init(&merges, no_new_vertices)); IGRAPH_FINALLY(igraph_i_simplify_free, &merges); IGRAPH_VECTOR_INIT_FINALLY(&sizes, no_new_vertices); for (i=0; i * It is simply the (normalized) Shannon entropy of the * incident edges' weights. D(i)=H(i)/log(k[i]), and * H(i) = -sum(p[i,j] log(p[i,j]), j=1..k[i]), * where p[i,j]=w[i,j]/sum(w[i,l], l=1..k[i]), k[i] is the (total) * degree of vertex i, and w[i,j] is the weight of the edge(s) between * vertex i and j. * \param graph The input graph, edge directions are ignored. * \param weights The edge weights, in the order of the edge ids, must * have appropriate length. * \param res An initialized vector, the results are stored here. * \param vids Vector with the vertex ids for which to calculate the * measure. * \return Error code. * * Time complexity: O(|V|+|E|), linear. * */ int igraph_diversity(igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *res, const igraph_vs_t vids) { int no_of_nodes=igraph_vcount(graph); int no_of_edges=igraph_ecount(graph); igraph_vector_t incident; igraph_vit_t vit; igraph_real_t s, ent, w; int i, j, k; if (!weights) { IGRAPH_ERROR("Edge weights must be given", IGRAPH_EINVAL); } if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&incident, 10); if (igraph_vs_is_all(&vids)) { IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); for (i=0; i * In particular, the function checks whether all the degrees are non-negative. * For undirected graphs, it also checks whether the sum of degrees is even. * For directed graphs, the function checks whether the lengths of the two * degree vectors are equal and whether their sums are also equal. These are * known sufficient and necessary conditions for a degree sequence to be * valid. * * \param out_degrees an integer vector specifying the degree sequence for * undirected graphs or the out-degree sequence for directed graphs. * \param in_degrees an integer vector specifying the in-degrees of the * vertices for directed graphs. For undirected graphs, this must be null. * \param res pointer to a boolean variable, the result will be stored here * \return Error code. * * Time complexity: O(n), where n is the length of the degree sequence. */ int igraph_is_degree_sequence(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { /* degrees must be non-negative */ if (igraph_vector_any_smaller(out_degrees, 0)) FAIL; if (in_degrees && igraph_vector_any_smaller(in_degrees, 0)) FAIL; if (in_degrees == 0) { /* sum of degrees must be even */ if (((long int)igraph_vector_sum(out_degrees) % 2) != 0) FAIL; } else { /* length of the two degree vectors must be equal */ if (igraph_vector_size(out_degrees) != igraph_vector_size(in_degrees)) FAIL; /* sum of in-degrees must be equal to sum of out-degrees */ if (igraph_vector_sum(out_degrees) != igraph_vector_sum(in_degrees)) FAIL; } SUCCEED; return 0; } int igraph_i_is_graphical_degree_sequence_undirected( const igraph_vector_t *degrees, igraph_bool_t *res); int igraph_i_is_graphical_degree_sequence_directed( const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); /** * \function igraph_is_graphical_degree_sequence * Determines whether a sequence of integers can be a degree sequence of some * simple graph. * * * References: * * * Hakimi SL: On the realizability of a set of integers as degrees of the * vertices of a simple graph. J SIAM Appl Math 10:496-506, 1962. * * * PL Erdos, I Miklos and Z Toroczkai: A simple Havel-Hakimi type algorithm * to realize graphical degree sequences of directed graphs. The Electronic * Journal of Combinatorics 17(1):R66, 2010. * * \param out_degrees an integer vector specifying the degree sequence for * undirected graphs or the out-degree sequence for directed graphs. * \param in_degrees an integer vector specifying the in-degrees of the * vertices for directed graphs. For undirected graphs, this must be null. * \param res pointer to a boolean variable, the result will be stored here * \return Error code. * * Time complexity: O(n^2 log n) where n is the length of the degree sequence. */ int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { IGRAPH_CHECK(igraph_is_degree_sequence(out_degrees, in_degrees, res)); if (!*res) FAIL; if (igraph_vector_size(out_degrees) == 0) SUCCEED; if (in_degrees == 0) { return igraph_i_is_graphical_degree_sequence_undirected(out_degrees, res); } else { return igraph_i_is_graphical_degree_sequence_directed(out_degrees, in_degrees, res); } } int igraph_i_is_graphical_degree_sequence_undirected( const igraph_vector_t *degrees, igraph_bool_t *res) { igraph_vector_t work; igraph_integer_t degree; long int i, vcount; IGRAPH_CHECK(igraph_vector_copy(&work, degrees)); IGRAPH_FINALLY(igraph_vector_destroy, &work); vcount = igraph_vector_size(&work); *res = 0; while (vcount) { /* RFE: theoretically, a counting sort would be only O(n) here and not * O(n log n) since the degrees are bounded from above by n. I am not sure * whether it's worth the fuss, though, sort() in the C library is highly * optimized */ igraph_vector_sort(&work); if (VECTOR(work)[0] < 0) break; degree = (igraph_integer_t) igraph_vector_pop_back(&work); vcount--; if (degree == 0) { *res = 1; break; } if (degree > vcount) break; for (i = vcount-degree; i < vcount; i++) { VECTOR(work)[i]--; } } igraph_vector_destroy(&work); IGRAPH_FINALLY_CLEAN(1); return 0; } typedef struct { igraph_vector_t* first; igraph_vector_t* second; } igraph_i_qsort_dual_vector_cmp_data_t; int igraph_i_qsort_dual_vector_cmp_asc(void* data, const void *p1, const void *p2) { igraph_i_qsort_dual_vector_cmp_data_t* sort_data = (igraph_i_qsort_dual_vector_cmp_data_t*)data; long int index1 = *((long int*)p1); long int index2 = *((long int*)p2); if (VECTOR(*sort_data->first)[index1] < VECTOR(*sort_data->first)[index2]) return -1; if (VECTOR(*sort_data->first)[index1] > VECTOR(*sort_data->first)[index2]) return 1; if (VECTOR(*sort_data->second)[index1] < VECTOR(*sort_data->second)[index2]) return -1; if (VECTOR(*sort_data->second)[index1] > VECTOR(*sort_data->second)[index2]) return 1; return 0; } int igraph_i_is_graphical_degree_sequence_directed( const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { igraph_vector_t work_in; igraph_vector_t work_out; igraph_vector_long_t out_vertices; igraph_vector_long_t index_array; long int i, vcount, u, v, degree; long int index_array_unused_prefix_length, nonzero_indegree_count; igraph_i_qsort_dual_vector_cmp_data_t sort_data; IGRAPH_CHECK(igraph_vector_copy(&work_in, in_degrees)); IGRAPH_FINALLY(igraph_vector_destroy, &work_in); IGRAPH_CHECK(igraph_vector_copy(&work_out, out_degrees)); IGRAPH_FINALLY(igraph_vector_destroy, &work_in); IGRAPH_CHECK(igraph_vector_long_init(&out_vertices, 0)); IGRAPH_FINALLY(igraph_vector_long_destroy, &out_vertices); vcount = igraph_vector_size(&work_out); IGRAPH_CHECK(igraph_vector_long_reserve(&out_vertices, vcount)); IGRAPH_CHECK(igraph_vector_long_init(&index_array, vcount)); IGRAPH_FINALLY(igraph_vector_long_destroy, &index_array); /* Set up the auxiliary struct for sorting */ sort_data.first = &work_in; sort_data.second = &work_out; /* Fill the index array. This will contain the indices of the "active" vertices, * i.e. those that have a non-zero in- or out-degree */ nonzero_indegree_count = 0; for (i = 0; i < vcount; i++) { if (VECTOR(work_in)[i] > 0) { VECTOR(index_array)[i] = i; nonzero_indegree_count++; } if (VECTOR(work_out)[i] > 0) { IGRAPH_CHECK(igraph_vector_long_push_back(&out_vertices, i)); } } *res = 0; index_array_unused_prefix_length = 0; while (!igraph_vector_long_empty(&out_vertices)) { /* Find a vertex with non-zero out-degree. */ u = igraph_vector_long_pop_back(&out_vertices); /* printf("Using vertex %ld\n", (long int)u); printf(" Degree vectors:\n "); igraph_vector_print(&work_out); printf(" "); igraph_vector_print(&work_in); */ /* Remember the degree of u and clear the degree itself */ degree = (long int) VECTOR(work_out)[u]; VECTOR(work_out)[u] = 0; /* printf(" Out-degree: %ld\n", (long int)degree); */ /* Is the degree larger than the number of vertices with nonzero in-degree? * (Make sure that u is excluded from the vertices with nonzero in-degree). */ if (degree > nonzero_indegree_count - (VECTOR(work_in)[u] > 0 ? 1 : 0)) break; /* Find the prefix of index_array that consists solely of vertices with * zero indegree. We don't need to sort these */ while (index_array_unused_prefix_length < vcount && VECTOR(work_in)[VECTOR(index_array)[index_array_unused_prefix_length]] == 0) { index_array_unused_prefix_length++; nonzero_indegree_count--; } /* Sort work_in first and then sort work_out for equal indegrees only. This * is done by sorting an index vector first; indexing work_out and work_in by * the sorted index vector would then give the sorted order of these vectors. */ igraph_qsort_r(VECTOR(index_array) + index_array_unused_prefix_length, (size_t) nonzero_indegree_count, sizeof(long int), &sort_data, igraph_i_qsort_dual_vector_cmp_asc); /* printf(" Sorted index array:\n "); igraph_vector_long_print(&index_array); */ /* Create edges from u to the vertices with the largest in-degrees */ i = vcount; while (degree > 0) { v = VECTOR(index_array)[--i]; if (u == v) { /* Avoid creating a loop edge */ continue; } VECTOR(work_in)[v]--; /* printf(" Created edge from %ld to %ld, in-degree is now %ld\n", (long int)u, (long int)v, (long int)VECTOR(work_in)[v]); */ degree--; } } if (igraph_vector_long_empty(&out_vertices)) { /* No more vertices with non-zero outdegree, so we were successful */ *res = 1; } igraph_vector_long_destroy(&index_array); igraph_vector_long_destroy(&out_vertices); igraph_vector_destroy(&work_out); igraph_vector_destroy(&work_in); IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; } #undef SUCCEED #undef FAIL igraph/src/glpnet09.c0000644000176000001440000002241712325527073014165 0ustar ripleyusers/* glpnet09.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpapi.h" #include "glpnet.h" /*********************************************************************** * NAME * * kellerman - cover edges by cliques with Kellerman's heuristic * * SYNOPSIS * * #include "glpnet.h" * int kellerman(int n, int (*func)(void *info, int i, int ind[]), * void *info, glp_graph *H); * * DESCRIPTION * * The routine kellerman implements Kellerman's heuristic algorithm * to find a minimal set of cliques which cover all edges of specified * graph G = (V, E). * * The parameter n specifies the number of vertices |V|, n >= 0. * * Formal routine func specifies the set of edges E in the following * way. Running the routine kellerman calls the routine func and passes * to it parameter i, which is the number of some vertex, 1 <= i <= n. * In response the routine func should store numbers of all vertices * adjacent to vertex i to locations ind[1], ind[2], ..., ind[len] and * return the value of len, which is the number of adjacent vertices, * 0 <= len <= n. Self-loops are allowed, but ignored. Multiple edges * are not allowed. * * The parameter info is a transit pointer (magic cookie) passed to the * formal routine func as its first parameter. * * The result provided by the routine kellerman is the bipartite graph * H = (V union C, F), which defines the covering found. (The program * object of type glp_graph specified by the parameter H should be * previously created with the routine glp_create_graph. On entry the * routine kellerman erases the content of this object with the routine * glp_erase_graph.) Vertices of first part V correspond to vertices of * the graph G and have the same ordinal numbers 1, 2, ..., n. Vertices * of second part C correspond to cliques and have ordinal numbers * n+1, n+2, ..., n+k, where k is the total number of cliques in the * edge covering found. Every edge f in F in the program object H is * represented as arc f = (i->j), where i in V and j in C, which means * that vertex i of the graph G is in clique C[j], 1 <= j <= k. (Thus, * if two vertices of the graph G are in the same clique, these vertices * are adjacent in G, and corresponding edge is covered by that clique.) * * RETURNS * * The routine Kellerman returns k, the total number of cliques in the * edge covering found. * * REFERENCE * * For more details see: glpk/doc/notes/keller.pdf (in Russian). */ struct set { /* set of vertices */ int size; /* size (cardinality) of the set, 0 <= card <= n */ int *list; /* int list[1+n]; */ /* the set contains vertices list[1,...,size] */ int *pos; /* int pos[1+n]; */ /* pos[i] > 0 means that vertex i is in the set and list[pos[i]] = i; pos[i] = 0 means that vertex i is not in the set */ }; int kellerman(int n, int (*func)(void *info, int i, int ind[]), void *info, void /* glp_graph */ *H_) { glp_graph *H = H_; struct set W_, *W = &W_, V_, *V = &V_; glp_arc *a; int i, j, k, m, t, len, card, best; xassert(n >= 0); /* H := (V, 0; 0), where V is the set of vertices of graph G */ glp_erase_graph(H, H->v_size, H->a_size); glp_add_vertices(H, n); /* W := 0 */ W->size = 0; W->list = xcalloc(1+n, sizeof(int)); W->pos = xcalloc(1+n, sizeof(int)); memset(&W->pos[1], 0, sizeof(int) * n); /* V := 0 */ V->size = 0; V->list = xcalloc(1+n, sizeof(int)); V->pos = xcalloc(1+n, sizeof(int)); memset(&V->pos[1], 0, sizeof(int) * n); /* main loop */ for (i = 1; i <= n; i++) { /* W must be empty */ xassert(W->size == 0); /* W := { j : i > j and (i,j) in E } */ len = func(info, i, W->list); xassert(0 <= len && len <= n); for (t = 1; t <= len; t++) { j = W->list[t]; xassert(1 <= j && j <= n); if (j >= i) continue; xassert(W->pos[j] == 0); W->list[++W->size] = j, W->pos[j] = W->size; } /* on i-th iteration we need to cover edges (i,j) for all j in W */ /* if W is empty, it is a special case */ if (W->size == 0) { /* set k := k + 1 and create new clique C[k] = { i } */ k = glp_add_vertices(H, 1) - n; glp_add_arc(H, i, n + k); continue; } /* try to include vertex i into existing cliques */ /* V must be empty */ xassert(V->size == 0); /* k is the number of cliques found so far */ k = H->nv - n; for (m = 1; m <= k; m++) { /* do while V != W; since here V is within W, we can use equivalent condition: do while |V| < |W| */ if (V->size == W->size) break; /* check if C[m] is within W */ for (a = H->v[n + m]->in; a != NULL; a = a->h_next) { j = a->tail->i; if (W->pos[j] == 0) break; } if (a != NULL) continue; /* C[m] is within W, expand clique C[m] with vertex i */ /* C[m] := C[m] union {i} */ glp_add_arc(H, i, n + m); /* V is a set of vertices whose incident edges are already covered by existing cliques */ /* V := V union C[m] */ for (a = H->v[n + m]->in; a != NULL; a = a->h_next) { j = a->tail->i; if (V->pos[j] == 0) V->list[++V->size] = j, V->pos[j] = V->size; } } /* remove from set W the vertices whose incident edges are already covered by existing cliques */ /* W := W \ V, V := 0 */ for (t = 1; t <= V->size; t++) { j = V->list[t], V->pos[j] = 0; if (W->pos[j] != 0) { /* remove vertex j from W */ if (W->pos[j] != W->size) { int jj = W->list[W->size]; W->list[W->pos[j]] = jj; W->pos[jj] = W->pos[j]; } W->size--, W->pos[j] = 0; } } V->size = 0; /* now set W contains only vertices whose incident edges are still not covered by existing cliques; create new cliques to cover remaining edges until set W becomes empty */ while (W->size > 0) { /* find clique C[m], 1 <= m <= k, which shares maximal number of vertices with W; to break ties choose clique having smallest number m */ m = 0, best = -1; k = H->nv - n; for (t = 1; t <= k; t++) { /* compute cardinality of intersection of W and C[t] */ card = 0; for (a = H->v[n + t]->in; a != NULL; a = a->h_next) { j = a->tail->i; if (W->pos[j] != 0) card++; } if (best < card) m = t, best = card; } xassert(m > 0); /* set k := k + 1 and create new clique: C[k] := (W intersect C[m]) union { i }, which covers all edges incident to vertices from (W intersect C[m]) */ k = glp_add_vertices(H, 1) - n; for (a = H->v[n + m]->in; a != NULL; a = a->h_next) { j = a->tail->i; if (W->pos[j] != 0) { /* vertex j is in both W and C[m]; include it in new clique C[k] */ glp_add_arc(H, j, n + k); /* remove vertex j from W, since edge (i,j) will be covered by new clique C[k] */ if (W->pos[j] != W->size) { int jj = W->list[W->size]; W->list[W->pos[j]] = jj; W->pos[jj] = W->pos[j]; } W->size--, W->pos[j] = 0; } } /* include vertex i to new clique C[k] to cover edges (i,j) incident to all vertices j just removed from W */ glp_add_arc(H, i, n + k); } } /* free working arrays */ xfree(W->list); xfree(W->pos); xfree(V->list); xfree(V->pos); /* return the number of cliques in the edge covering found */ return H->nv - n; } /* eof */ igraph/src/cs_transpose.c0000644000176000001440000000363312325527073015225 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* C = A' */ cs *cs_transpose (const cs *A, CS_INT values) { CS_INT p, q, j, *Cp, *Ci, n, m, *Ap, *Ai, *w ; CS_ENTRY *Cx, *Ax ; cs *C ; if (!CS_CSC (A)) return (NULL) ; /* check inputs */ m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; C = cs_spalloc (n, m, Ap [n], values && Ax, 0) ; /* allocate result */ w = cs_calloc (m, sizeof (CS_INT)) ; /* get workspace */ if (!C || !w) return (cs_done (C, w, NULL, 0)) ; /* out of memory */ Cp = C->p ; Ci = C->i ; Cx = C->x ; for (p = 0 ; p < Ap [n] ; p++) w [Ai [p]]++ ; /* row counts */ cs_cumsum (Cp, w, m) ; /* row pointers */ for (j = 0 ; j < n ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { Ci [q = w [Ai [p]]++] = j ; /* place A(i,j) as entry C(j,i) */ if (Cx) Cx [q] = (values > 0) ? CS_CONJ (Ax [p]) : Ax [p] ; } } return (cs_done (C, w, NULL, 1)) ; /* success; free w and return C */ } igraph/src/stack.pmt0000644000176000001440000001600512325372072014177 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include /** * \ingroup stack * \function igraph_stack_init * \brief Initializes a stack. * * The initialized stack is always empty. * \param s Pointer to an uninitialized stack. * \param size The number of elements to allocate memory for. * \return Error code. * * Time complexity: O(\p size). */ int FUNCTION(igraph_stack,init) (TYPE(igraph_stack)* s, long int size) { long int alloc_size= size > 0 ? size : 1; assert (s != NULL); if (size < 0) { size=0; } s->stor_begin=igraph_Calloc(alloc_size, BASE); if (s->stor_begin==0) { IGRAPH_ERROR("stack init failed", IGRAPH_ENOMEM); } s->stor_end=s->stor_begin + alloc_size; s->end=s->stor_begin; return 0; } /** * \ingroup stack * \function igraph_stack_destroy * \brief Destroys a stack object. * * Deallocate the memory used for a stack. * It is possible to reinitialize a destroyed stack again by * \ref igraph_stack_init(). * \param s The stack to destroy. * * Time complexity: O(1). */ void FUNCTION(igraph_stack,destroy) (TYPE(igraph_stack)* s) { assert( s != NULL); if (s->stor_begin != 0) { igraph_Free(s->stor_begin); s->stor_begin=NULL; } } /** * \ingroup stack * \function igraph_stack_reserve * \brief Reserve memory. * * Reverse memory for future use. The actual size of the stack is * unchanged. * \param s The stack object. * \param size The number of elements to reserve memory for. If it is * not bigger than the current size then nothing happens. * \return Error code. * * Time complexity: should be around O(n), the new allocated size of * the stack. */ int FUNCTION(igraph_stack,reserve) (TYPE(igraph_stack)* s, long int size) { long int actual_size=FUNCTION(igraph_stack,size)(s); BASE *tmp; assert(s != NULL); assert(s->stor_begin != NULL); if (size <= actual_size) { return 0; } tmp=igraph_Realloc(s->stor_begin, (size_t) size, BASE); if (tmp==0) { IGRAPH_ERROR("stack reserve failed", IGRAPH_ENOMEM); } s->stor_begin=tmp; s->stor_end=s->stor_begin + size; s->end=s->stor_begin+actual_size; return 0; } /** * \ingroup stack * \function igraph_stack_empty * \brief Decides whether a stack object is empty. * * \param s The stack object. * \return Boolean, \c TRUE if the stack is empty, \c FALSE * otherwise. * * Time complexity: O(1). */ igraph_bool_t FUNCTION(igraph_stack,empty) (TYPE(igraph_stack)* s) { assert (s != NULL); assert (s->stor_begin != NULL); assert (s->end != NULL); return s->stor_begin == s->end; } /** * \ingroup stack * \function igraph_stack_size * \brief Returns the number of elements in a stack. * * \param s The stack object. * \return The number of elements in the stack. * * Time complexity: O(1). */ long int FUNCTION(igraph_stack,size) (const TYPE(igraph_stack)* s) { assert (s != NULL); assert (s->stor_begin != NULL); return s->end - s->stor_begin; } /** * \ingroup stack * \function igraph_stack_clear * \brief Removes all elements from a stack. * * \param s The stack object. * * Time complexity: O(1). */ void FUNCTION(igraph_stack,clear) (TYPE(igraph_stack)* s) { assert (s != NULL); assert (s->stor_begin != NULL); s->end = s->stor_begin; } /** * \ingroup stack * \function igraph_stack_push * \brief Places an element on the top of a stack. * * The capacity of the stack is increased, if needed. * \param s The stack object. * \param elem The element to push. * \return Error code. * * Time complexity: O(1) is no reallocation is needed, O(n) * otherwise, but it is ensured that n push operations are performed * in O(n) time. */ int FUNCTION(igraph_stack,push)(TYPE(igraph_stack)* s, BASE elem) { assert (s != NULL); assert (s->stor_begin != NULL); if (s->end == s->stor_end) { /* full, allocate more storage */ BASE *bigger=NULL, *old=s->stor_begin; bigger = igraph_Calloc(2*FUNCTION(igraph_stack,size)(s)+1, BASE); if (bigger==0) { IGRAPH_ERROR("stack push failed", IGRAPH_ENOMEM); } memcpy(bigger, s->stor_begin, (size_t) FUNCTION(igraph_stack,size)(s)*sizeof(BASE)); s->end = bigger + (s->stor_end - s->stor_begin); s->stor_end = bigger + 2*(s->stor_end - s->stor_begin)+1; s->stor_begin = bigger; *(s->end) = elem; (s->end) += 1; igraph_Free(old); } else { *(s->end) = elem; (s->end) += 1; } return 0; } /** * \ingroup stack * \function igraph_stack_pop * \brief Removes and returns an element from the top of a stack. * * The stack must contain at least one element, call \ref * igraph_stack_empty() to make sure of this. * \param s The stack object. * \return The removed top element. * * Time complexity: O(1). */ BASE FUNCTION(igraph_stack,pop) (TYPE(igraph_stack)* s) { assert (s != NULL); assert (s->stor_begin != NULL); assert (s->end != NULL); assert (s->end != s->stor_begin); (s->end)--; return *(s->end); } /** * \ingroup stack * \function igraph_stack_top * \brief Query top element. * * Returns the top element of the stack, without removing it. * The stack must be non-empty. * \param s The stack. * \return The top element. * * Time complexity: O(1). */ BASE FUNCTION(igraph_stack,top) (const TYPE(igraph_stack)* s) { assert (s != NULL); assert (s->stor_begin != NULL); assert (s->end != NULL); assert (s->end != s->stor_begin); return *(s->end-1); } #if defined (OUT_FORMAT) #ifndef USING_R int FUNCTION(igraph_stack,print)(const TYPE(igraph_stack) *s) { long int i, n=FUNCTION(igraph_stack,size)(s); if (n!=0) { printf(OUT_FORMAT, s->stor_begin[0]); } for (i=1; istor_begin[i]); } printf("\n"); return 0; } #endif int FUNCTION(igraph_stack,fprint)(const TYPE(igraph_stack) *s, FILE *file) { long int i, n=FUNCTION(igraph_stack,size)(s); if (n!=0) { fprintf(file, OUT_FORMAT, s->stor_begin[0]); } for (i=1; istor_begin[i]); } fprintf(file, "\n"); return 0; } #endif igraph/src/dlaqrb.f0000644000176000001440000004405012325527073013770 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdlaqrb c c\Description: c Compute the eigenvalues and the Schur decomposition of an upper c Hessenberg submatrix in rows and columns ILO to IHI. Only the c last component of the Schur vectors are computed. c c This is mostly a modification of the LAPACK routine dlahqr. c c\Usage: c call igraphdlaqrb c ( WANTT, N, ILO, IHI, H, LDH, WR, WI, Z, INFO ) c c\Arguments c WANTT Logical variable. (INPUT) c = .TRUE. : the full Schur form T is required; c = .FALSE.: only eigenvalues are required. c c N Integer. (INPUT) c The order of the matrix H. N >= 0. c c ILO Integer. (INPUT) c IHI Integer. (INPUT) c It is assumed that H is already upper quasi-triangular in c rows and columns IHI+1:N, and that H(ILO,ILO-1) = 0 (unless c ILO = 1). SLAQRB works primarily with the Hessenberg c submatrix in rows and columns ILO to IHI, but applies c transformations to all of H if WANTT is .TRUE.. c 1 <= ILO <= max(1,IHI); IHI <= N. c c H Double precision array, dimension (LDH,N). (INPUT/OUTPUT) c On entry, the upper Hessenberg matrix H. c On exit, if WANTT is .TRUE., H is upper quasi-triangular in c rows and columns ILO:IHI, with any 2-by-2 diagonal blocks in c standard form. If WANTT is .FALSE., the contents of H are c unspecified on exit. c c LDH Integer. (INPUT) c The leading dimension of the array H. LDH >= max(1,N). c c WR Double precision array, dimension (N). (OUTPUT) c WI Double precision array, dimension (N). (OUTPUT) c The real and imaginary parts, respectively, of the computed c eigenvalues ILO to IHI are stored in the corresponding c elements of WR and WI. If two eigenvalues are computed as a c complex conjugate pair, they are stored in consecutive c elements of WR and WI, say the i-th and (i+1)th, with c WI(i) > 0 and WI(i+1) < 0. If WANTT is .TRUE., the c eigenvalues are stored in the same order as on the diagonal c of the Schur form returned in H, with WR(i) = H(i,i), and, if c H(i:i+1,i:i+1) is a 2-by-2 diagonal block, c WI(i) = sqrt(H(i+1,i)*H(i,i+1)) and WI(i+1) = -WI(i). c c Z Double precision array, dimension (N). (OUTPUT) c On exit Z contains the last components of the Schur vectors. c c INFO Integer. (OUPUT) c = 0: successful exit c > 0: SLAQRB failed to compute all the eigenvalues ILO to IHI c in a total of 30*(IHI-ILO+1) iterations; if INFO = i, c elements i+1:ihi of WR and WI contain those eigenvalues c which have been successfully computed. c c\Remarks c 1. None. c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c dlabad LAPACK routine that computes machine constants. c dlamch LAPACK routine that determines machine constants. c dlanhs LAPACK routine that computes various norms of a matrix. c dlanv2 LAPACK routine that computes the Schur factorization of c 2 by 2 nonsymmetric matrix in standard form. c dlarfg LAPACK Householder reflection construction routine. c dcopy Level 1 BLAS that copies one vector to another. c drot Level 1 BLAS that applies a rotation to a 2 by 2 matrix. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.4' c Modified from the LAPACK routine dlahqr so that only the c last component of the Schur vectors are computed. c c\SCCS Information: @(#) c FILE: laqrb.F SID: 2.2 DATE OF SID: 8/27/96 RELEASE: 2 c c\Remarks c 1. None c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdlaqrb ( wantt, n, ilo, ihi, h, ldh, wr, wi, & z, info ) c c %------------------% c | Scalar Arguments | c %------------------% c logical wantt integer ihi, ilo, info, ldh, n c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & h( ldh, * ), wi( * ), wr( * ), z( * ) c c %------------% c | Parameters | c %------------% c Double precision & zero, one, dat1, dat2 parameter (zero = 0.0D+0, one = 1.0D+0, dat1 = 7.5D-1, & dat2 = -4.375D-1) c c %------------------------% c | Local Scalars & Arrays | c %------------------------% c integer i, i1, i2, itn, its, j, k, l, m, nh, nr Double precision & cs, h00, h10, h11, h12, h21, h22, h33, h33s, & h43h34, h44, h44s, ovfl, s, smlnum, sn, sum, & t1, t2, t3, tst1, ulp, unfl, v1, v2, v3 Double precision & v( 3 ), work( 1 ) c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlamch, dlanhs external dlamch, dlanhs c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, dlabad, dlanv2, dlarfg, drot c c %-----------------------% c | Executable Statements | c %-----------------------% c info = 0 c c %--------------------------% c | Quick return if possible | c %--------------------------% c if( n.eq.0 ) & return if( ilo.eq.ihi ) then wr( ilo ) = h( ilo, ilo ) wi( ilo ) = zero return end if c c %---------------------------------------------% c | Initialize the vector of last components of | c | the Schur vectors for accumulation. | c %---------------------------------------------% c do 5 j = 1, n-1 z(j) = zero 5 continue z(n) = one c nh = ihi - ilo + 1 c c %-------------------------------------------------------------% c | Set machine-dependent constants for the stopping criterion. | c | If norm(H) <= sqrt(OVFL), overflow should not occur. | c %-------------------------------------------------------------% c unfl = dlamch( 'safe minimum' ) ovfl = one / unfl call dlabad( unfl, ovfl ) ulp = dlamch( 'precision' ) smlnum = unfl*( nh / ulp ) c c %---------------------------------------------------------------% c | I1 and I2 are the indices of the first row and last column | c | of H to which transformations must be applied. If eigenvalues | c | only are computed, I1 and I2 are set inside the main loop. | c | Zero out H(J+2,J) = ZERO for J=1:N if WANTT = .TRUE. | c | else H(J+2,J) for J=ILO:IHI-ILO-1 if WANTT = .FALSE. | c %---------------------------------------------------------------% c if( wantt ) then i1 = 1 i2 = n do 8 i=1,i2-2 h(i1+i+1,i) = zero 8 continue else do 9 i=1, ihi-ilo-1 h(ilo+i+1,ilo+i-1) = zero 9 continue end if c c %---------------------------------------------------% c | ITN is the total number of QR iterations allowed. | c %---------------------------------------------------% c itn = 30*nh c c ------------------------------------------------------------------ c The main loop begins here. I is the loop index and decreases from c IHI to ILO in steps of 1 or 2. Each iteration of the loop works c with the active submatrix in rows and columns L to I. c Eigenvalues I+1 to IHI have already converged. Either L = ILO or c H(L,L-1) is negligible so that the matrix splits. c ------------------------------------------------------------------ c i = ihi 10 continue l = ilo if( i.lt.ilo ) & go to 150 c %--------------------------------------------------------------% c | Perform QR iterations on rows and columns ILO to I until a | c | submatrix of order 1 or 2 splits off at the bottom because a | c | subdiagonal element has become negligible. | c %--------------------------------------------------------------% do 130 its = 0, itn c c %----------------------------------------------% c | Look for a single small subdiagonal element. | c %----------------------------------------------% c do 20 k = i, l + 1, -1 tst1 = abs( h( k-1, k-1 ) ) + abs( h( k, k ) ) if( tst1.eq.zero ) & tst1 = dlanhs( '1', i-l+1, h( l, l ), ldh, work ) if( abs( h( k, k-1 ) ).le.max( ulp*tst1, smlnum ) ) & go to 30 20 continue 30 continue l = k if( l.gt.ilo ) then c c %------------------------% c | H(L,L-1) is negligible | c %------------------------% c h( l, l-1 ) = zero end if c c %-------------------------------------------------------------% c | Exit from loop if a submatrix of order 1 or 2 has split off | c %-------------------------------------------------------------% c if( l.ge.i-1 ) & go to 140 c c %---------------------------------------------------------% c | Now the active submatrix is in rows and columns L to I. | c | If eigenvalues only are being computed, only the active | c | submatrix need be transformed. | c %---------------------------------------------------------% c if( .not.wantt ) then i1 = l i2 = i end if c if( its.eq.10 .or. its.eq.20 ) then c c %-------------------% c | Exceptional shift | c %-------------------% c s = abs( h( i, i-1 ) ) + abs( h( i-1, i-2 ) ) h44 = dat1*s h33 = h44 h43h34 = dat2*s*s c else c c %-----------------------------------------% c | Prepare to use Wilkinson's double shift | c %-----------------------------------------% c h44 = h( i, i ) h33 = h( i-1, i-1 ) h43h34 = h( i, i-1 )*h( i-1, i ) end if c c %-----------------------------------------------------% c | Look for two consecutive small subdiagonal elements | c %-----------------------------------------------------% c do 40 m = i - 2, l, -1 c c %---------------------------------------------------------% c | Determine the effect of starting the double-shift QR | c | iteration at row M, and see if this would make H(M,M-1) | c | negligible. | c %---------------------------------------------------------% c h11 = h( m, m ) h22 = h( m+1, m+1 ) h21 = h( m+1, m ) h12 = h( m, m+1 ) h44s = h44 - h11 h33s = h33 - h11 v1 = ( h33s*h44s-h43h34 ) / h21 + h12 v2 = h22 - h11 - h33s - h44s v3 = h( m+2, m+1 ) s = abs( v1 ) + abs( v2 ) + abs( v3 ) v1 = v1 / s v2 = v2 / s v3 = v3 / s v( 1 ) = v1 v( 2 ) = v2 v( 3 ) = v3 if( m.eq.l ) & go to 50 h00 = h( m-1, m-1 ) h10 = h( m, m-1 ) tst1 = abs( v1 )*( abs( h00 )+abs( h11 )+abs( h22 ) ) if( abs( h10 )*( abs( v2 )+abs( v3 ) ).le.ulp*tst1 ) & go to 50 40 continue 50 continue c c %----------------------% c | Double-shift QR step | c %----------------------% c do 120 k = m, i - 1 c c ------------------------------------------------------------ c The first iteration of this loop determines a reflection G c from the vector V and applies it from left and right to H, c thus creating a nonzero bulge below the subdiagonal. c c Each subsequent iteration determines a reflection G to c restore the Hessenberg form in the (K-1)th column, and thus c chases the bulge one step toward the bottom of the active c submatrix. NR is the order of G. c ------------------------------------------------------------ c nr = min( 3, i-k+1 ) if( k.gt.m ) & call dcopy( nr, h( k, k-1 ), 1, v, 1 ) call dlarfg( nr, v( 1 ), v( 2 ), 1, t1 ) if( k.gt.m ) then h( k, k-1 ) = v( 1 ) h( k+1, k-1 ) = zero if( k.lt.i-1 ) & h( k+2, k-1 ) = zero else if( m.gt.l ) then h( k, k-1 ) = -h( k, k-1 ) end if v2 = v( 2 ) t2 = t1*v2 if( nr.eq.3 ) then v3 = v( 3 ) t3 = t1*v3 c c %------------------------------------------------% c | Apply G from the left to transform the rows of | c | the matrix in columns K to I2. | c %------------------------------------------------% c do 60 j = k, i2 sum = h( k, j ) + v2*h( k+1, j ) + v3*h( k+2, j ) h( k, j ) = h( k, j ) - sum*t1 h( k+1, j ) = h( k+1, j ) - sum*t2 h( k+2, j ) = h( k+2, j ) - sum*t3 60 continue c c %----------------------------------------------------% c | Apply G from the right to transform the columns of | c | the matrix in rows I1 to min(K+3,I). | c %----------------------------------------------------% c do 70 j = i1, min( k+3, i ) sum = h( j, k ) + v2*h( j, k+1 ) + v3*h( j, k+2 ) h( j, k ) = h( j, k ) - sum*t1 h( j, k+1 ) = h( j, k+1 ) - sum*t2 h( j, k+2 ) = h( j, k+2 ) - sum*t3 70 continue c c %----------------------------------% c | Accumulate transformations for Z | c %----------------------------------% c sum = z( k ) + v2*z( k+1 ) + v3*z( k+2 ) z( k ) = z( k ) - sum*t1 z( k+1 ) = z( k+1 ) - sum*t2 z( k+2 ) = z( k+2 ) - sum*t3 else if( nr.eq.2 ) then c c %------------------------------------------------% c | Apply G from the left to transform the rows of | c | the matrix in columns K to I2. | c %------------------------------------------------% c do 90 j = k, i2 sum = h( k, j ) + v2*h( k+1, j ) h( k, j ) = h( k, j ) - sum*t1 h( k+1, j ) = h( k+1, j ) - sum*t2 90 continue c c %----------------------------------------------------% c | Apply G from the right to transform the columns of | c | the matrix in rows I1 to min(K+3,I). | c %----------------------------------------------------% c do 100 j = i1, i sum = h( j, k ) + v2*h( j, k+1 ) h( j, k ) = h( j, k ) - sum*t1 h( j, k+1 ) = h( j, k+1 ) - sum*t2 100 continue c c %----------------------------------% c | Accumulate transformations for Z | c %----------------------------------% c sum = z( k ) + v2*z( k+1 ) z( k ) = z( k ) - sum*t1 z( k+1 ) = z( k+1 ) - sum*t2 end if 120 continue 130 continue c c %-------------------------------------------------------% c | Failure to converge in remaining number of iterations | c %-------------------------------------------------------% c info = i return 140 continue if( l.eq.i ) then c c %------------------------------------------------------% c | H(I,I-1) is negligible: one eigenvalue has converged | c %------------------------------------------------------% c wr( i ) = h( i, i ) wi( i ) = zero else if( l.eq.i-1 ) then c c %--------------------------------------------------------% c | H(I-1,I-2) is negligible; | c | a pair of eigenvalues have converged. | c | | c | Transform the 2-by-2 submatrix to standard Schur form, | c | and compute and store the eigenvalues. | c %--------------------------------------------------------% c call dlanv2( h( i-1, i-1 ), h( i-1, i ), h( i, i-1 ), & h( i, i ), wr( i-1 ), wi( i-1 ), wr( i ), wi( i ), & cs, sn ) if( wantt ) then c c %-----------------------------------------------------% c | Apply the transformation to the rest of H and to Z, | c | as required. | c %-----------------------------------------------------% c if( i2.gt.i ) & call drot( i2-i, h( i-1, i+1 ), ldh, h( i, i+1 ), ldh, & cs, sn ) call drot( i-i1-1, h( i1, i-1 ), 1, h( i1, i ), 1, cs, sn ) sum = cs*z( i-1 ) + sn*z( i ) z( i ) = cs*z( i ) - sn*z( i-1 ) z( i-1 ) = sum end if end if c c %---------------------------------------------------------% c | Decrement number of remaining iterations, and return to | c | start of the main loop with new value of I. | c %---------------------------------------------------------% c itn = itn - its i = l - 1 go to 10 150 continue return c c %---------------% c | End of igraphdlaqrb | c %---------------% c end igraph/src/glpdmp.c0000644000176000001440000001712712325527073014010 0ustar ripleyusers/* glpdmp.c (dynamic memory pool) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpdmp.h" #if 1 /* 29/VIII-2008 */ /* some processors need data to be properly aligned; the macro align_datasize enlarges the specified size of a data item to provide a proper alignment of immediately following data */ #define align_datasize(size) ((((size) + 7) / 8) * 8) /* 8 bytes is sufficient in both 32- and 64-bit environments */ #endif #ifdef GLP_DEBUG struct info { DMP *pool; int size; }; #endif /*********************************************************************** * NAME * * dmp_create_pool - create dynamic memory pool * * SYNOPSIS * * #include "glpdmp.h" * DMP *dmp_create_pool(void); * * DESCRIPTION * * The routine dmp_create_pool creates a dynamic memory pool. * * RETURNS * * The routine returns a pointer to the memory pool created. */ DMP *dmp_create_pool(void) { DMP *pool; int k; #ifdef GLP_DEBUG xprintf("dmp_create_pool: warning: debug mode enabled\n"); #endif pool = xmalloc(sizeof(DMP)); #if 0 pool->size = 0; #endif for (k = 0; k <= 31; k++) pool->avail[k] = NULL; pool->block = NULL; pool->used = DMP_BLK_SIZE; pool->count.lo = pool->count.hi = 0; return pool; } /*********************************************************************** * NAME * * dmp_get_atom - get free atom from dynamic memory pool * * SYNOPSIS * * #include "glpdmp.h" * void *dmp_get_atom(DMP *pool, int size); * * DESCRIPTION * * The routine dmp_get_atom obtains a free atom (memory block) from the * specified memory pool. * * The parameter size is the atom size, in bytes, 1 <= size <= 256. * * Note that the free atom contains arbitrary data, not binary zeros. * * RETURNS * * The routine returns a pointer to the free atom obtained. */ void *dmp_get_atom(DMP *pool, int size) { void *atom; int k; #ifdef GLP_DEBUG int orig_size = size; #endif if (!(1 <= size && size <= 256)) xerror("dmp_get_atom: size = %d; invalid atom size\n", size); #if 0 if (!(pool->size == 0 || pool->size == size)) xerror("dmp_get_atom: size = %d; wrong atom size\n", size); #endif /* adjust the size to provide the proper data alignment */ size = align_datasize(size); #ifdef GLP_DEBUG size += align_datasize(sizeof(struct info)); #endif /* adjust the size to make it multiple of 8 bytes, if needed */ size = ((size + 7) / 8) * 8; /* determine the corresponding list of free cells */ k = size / 8 - 1; xassert(0 <= k && k <= 31); /* obtain a free atom */ if (pool->avail[k] == NULL) { /* the list of free cells is empty */ if (pool->used + size > DMP_BLK_SIZE) { /* allocate a new memory block */ void *block = xmalloc(DMP_BLK_SIZE); *(void **)block = pool->block; pool->block = block; pool->used = align_datasize(sizeof(void *)); } /* place the atom in the current memory block */ atom = (char *)pool->block + pool->used; pool->used += size; } else { /* obtain the atom from the list of free cells */ atom = pool->avail[k]; pool->avail[k] = *(void **)atom; } memset(atom, '?', size); /* increase the number of atoms which are currently in use */ pool->count.lo++; if (pool->count.lo == 0) pool->count.hi++; #ifdef GLP_DEBUG ((struct info *)atom)->pool = pool; ((struct info *)atom)->size = orig_size; atom = (char *)atom + align_datasize(sizeof(struct info)); #endif return atom; } /*********************************************************************** * NAME * * dmp_free_atom - return atom to dynamic memory pool * * SYNOPSIS * * #include "glpdmp.h" * void dmp_free_atom(DMP *pool, void *atom, int size); * * DESCRIPTION * * The routine dmp_free_atom returns the specified atom (memory block) * to the specified memory pool, making it free. * * The parameter size is the atom size, in bytes, 1 <= size <= 256. * * Note that the atom can be returned only to the pool, from which it * was obtained, and its size must be exactly the same as on obtaining * it from the pool. */ void dmp_free_atom(DMP *pool, void *atom, int size) { int k; if (!(1 <= size && size <= 256)) xerror("dmp_free_atom: size = %d; invalid atom size\n", size); #if 0 if (!(pool->size == 0 || pool->size == size)) xerror("dmp_free_atom: size = %d; wrong atom size\n", size); #endif if (pool->count.lo == 0 && pool->count.hi == 0) xerror("dmp_free_atom: pool allocation error\n"); #ifdef GLP_DEBUG atom = (char *)atom - align_datasize(sizeof(struct info)); xassert(((struct info *)atom)->pool == pool); xassert(((struct info *)atom)->size == size); #endif /* adjust the size to provide the proper data alignment */ size = align_datasize(size); #ifdef GLP_DEBUG size += align_datasize(sizeof(struct info)); #endif /* adjust the size to make it multiple of 8 bytes, if needed */ size = ((size + 7) / 8) * 8; /* determine the corresponding list of free cells */ k = size / 8 - 1; xassert(0 <= k && k <= 31); /* return the atom to the list of free cells */ *(void **)atom = pool->avail[k]; pool->avail[k] = atom; /* decrease the number of atoms which are currently in use */ pool->count.lo--; if (pool->count.lo == 0xFFFFFFFF) pool->count.hi--; return; } /*********************************************************************** * NAME * * dmp_in_use - determine how many atoms are still in use * * SYNOPSIS * * #include "glpdmp.h" * glp_long dmp_in_use(DMP *pool); * * DESCRIPTION * * The routine dmp_in_use determines how many atoms allocated from the * specified memory pool with the routine dmp_get_atom are still in use, * i.e. not returned to the pool with the routine dmp_free_atom. * * RETURNS * * The routine returns the number of atoms which are still in use. */ glp_long dmp_in_use(DMP *pool) { return pool->count; } /*********************************************************************** * NAME * * dmp_delete_pool - delete dynamic memory pool * * SYNOPSIS * * #include "glpdmp.h" * void dmp_delete_pool(DMP *pool); * * DESCRIPTION * * The routine dmp_delete_pool deletes the specified dynamic memory * pool and frees all the memory allocated to this object. */ void dmp_delete_pool(DMP *pool) { while (pool->block != NULL) { void *block = pool->block; pool->block = *(void **)block; xfree(block); } xfree(pool); return; } /* eof */ igraph/src/microscopic_update.c0000644000176000001440000016300212325527073016373 0ustar ripleyusers/* -*- mode: C -*- */ /* Microscopic update rules for dealing with agent-level strategy revision. Copyright (C) 2011 Minh Van Nguyen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_iterators.h" #include "igraph_interface.h" #include "igraph_microscopic_update.h" #include "igraph_nongraph.h" #include "igraph_random.h" #include /* * Internal use only. * Compute the cumulative proportionate values of a vector. The vector is * assumed to hold values associated with edges. * * \param graph The graph object representing the game network. No error * checks will be performed on this graph. You are responsible for * ensuring that this is a valid graph for the particular * microscopic update rule at hand. * \param U A vector of edge values for which we want to compute cumulative * proportionate values. So U[i] is the value of the edge with ID i. * With a local perspective, we would only compute cumulative * proportionate values for some combination of U. This vector could * be, for example, a vector of weights for edges in \p graph. It is * assumed that each value of U is nonnegative; it is your * responsibility to ensure this. Furthermore, this vector must have a * length the same as the number of edges in \p graph; you are * responsible for ensuring this condition holds. * \param V Pointer to an uninitialized vector. The cumulative proportionate * values will be computed and stored here. No error checks will be * performed on this parameter. * \param islocal Boolean; this flag controls which perspective to use. If * true then we use the local perspective; otherwise we use the global * perspective. In the context of this function, the local perspective * for a vertex v consists of all edges incident on v. In contrast, the * global perspective for v consists of all edges in \p graph. * \param vid The vertex to use if we are considering a local perspective, * i.e. if \p islocal is true. This vertex will be ignored if * \p islocal is false. That is, if \p islocal is false then it is safe * pass the value -1 here. On the other hand, if \p islocal is true then * it is assumed that this is indeed a vertex of \p graph. * \param mode Defines the sort of neighbourhood to consider for \p vid. This * is only relevant if we are considering the local perspective, i.e. if * \p islocal is true. If we are considering the global perspective, * then this parameter would be ignored. In other words, if \p islocal * is false then it is safe to pass the value \p IGRAPH_ALL here. If * \p graph is undirected, then we use all the immediate neighbours of * \p vid. Thus if you know that \p graph is undirected, then it is * safe to pass the value \p IGRAPH_ALL here. Supported values are: * \clist * \cli IGRAPH_OUT * Use the out-neighbours of \p vid. This option is only relevant * when \p graph is a digraph and we are considering the local * perspective. * \cli IGRAPH_IN * Use the in-neighbours of \p vid. Again this option is only relevant * when \p graph is a directed graph and we are considering the local * perspective. * \cli IGRAPH_ALL * Use both the in- and out-neighbours of \p vid. This option is only * relevant if \p graph is a digraph and we are considering a local * perspective. Also use this value if \p graph is undirected or we * are considering the global perspective. * \endclist * \return Codes: * \clist * \cli IGRAPH_EINVAL * This error code is returned in the following case: The vector * \p U, or some combination of its values, sums to zero. * \cli IGRAPH_SUCCESS * This signal is returned if the cumulative proportionate values * were successfully computed. * \endclist * * Time complexity: O(2n) where n is the number of edges in the perspective * of \p vid. */ int igraph_ecumulative_proportionate_values(const igraph_t *graph, const igraph_vector_t *U, igraph_vector_t *V, igraph_bool_t islocal, igraph_integer_t vid, igraph_neimode_t mode) { igraph_eit_t A; /* all edges in v's perspective */ igraph_es_t es; igraph_integer_t e; igraph_real_t C; /* cumulative probability */ igraph_real_t P; /* probability */ igraph_real_t S; /* sum of values */ long int i; /* Set the perspective. Let v be the vertex under consideration. The local */ /* perspective for v consists of edges incident on it. In contrast, the */ /* global perspective for v are all edges in the given graph. Hence in the */ /* global perspective, we will ignore the given vertex and the given */ /* neighbourhood type, but instead consider all edges in the given graph. */ if (islocal) IGRAPH_CHECK(igraph_es_incident(&es, vid, mode)); else IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID)); IGRAPH_FINALLY(igraph_es_destroy, &es); /* Sum up all the values of vector U in the perspective for v. This sum */ /* will be used in normalizing each value. */ /* NOTE: Here we assume that each value to be summed is nonnegative, */ /* and at least one of the values is nonzero. The behaviour resulting */ /* from all values being zero would be division by zero later on when */ /* we normalize each value. We check to see that the values sum to zero. */ /* NOTE: In this function, the order in which we iterate through the */ /* edges of interest should be the same as the order in which we do so */ /* in the caller function. If the caller function doesn't care about the */ /* order of values in the resulting vector V, then there's no need to take */ /* special notice of that order. But in some cases the order of values in */ /* V is taken into account, for example, in the Moran process. */ S = 0.0; IGRAPH_CHECK(igraph_eit_create(graph, es, &A)); IGRAPH_FINALLY(igraph_eit_destroy, &A); while (!IGRAPH_EIT_END(A)) { e = (igraph_integer_t)IGRAPH_EIT_GET(A); S += (igraph_real_t)VECTOR(*U)[e]; IGRAPH_EIT_NEXT(A); } /* avoid division by zero later on */ if (S == (igraph_real_t)0.0) { igraph_eit_destroy(&A); igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); IGRAPH_ERROR("Vector of values sums to zero", IGRAPH_EINVAL); } /* Get cumulative probability and relative value for each edge in the */ /* perspective of v. The vector V holds the cumulative proportionate */ /* values of all edges in v's perspective. The value V[0] is the */ /* cumulative proportionate value of the first edge in the edge iterator */ /* A. The value V[1] is the cumulative proportionate value of the second */ /* edge in the iterator A. And so on. */ C = 0.0; i = 0; IGRAPH_EIT_RESET(A); IGRAPH_VECTOR_INIT_FINALLY(V, IGRAPH_EIT_SIZE(A)); while (!IGRAPH_EIT_END(A)) { e = (igraph_integer_t)IGRAPH_EIT_GET(A); /* NOTE: Beware of division by zero here. This can happen if the vector */ /* of values, or the combination of interest, sums to zero. */ P = (igraph_real_t)VECTOR(*U)[e] / S; C += P; VECTOR(*V)[i] = C; i++; IGRAPH_EIT_NEXT(A); } igraph_eit_destroy(&A); igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } /* * Internal use only. * Compute the cumulative proportionate values of a vector. The vector is * assumed to hold values associated with vertices. * * \param graph The graph object representing the game network. No error * checks will be performed on this graph. You are responsible for * ensuring that this is a valid graph for the particular * microscopic update rule at hand. * \param U A vector of vertex values for which we want to compute cumulative * proportionate values. The vector could be, for example, a vector of * fitness for vertices of \p graph. It is assumed that each value of U * is nonnegative; it is your responsibility to ensure this. Also U, or * a combination of interest, is assumed to sum to a positive value; * this condition will be checked. * \param V Pointer to an uninitialized vector. The cumulative proportionate * values will be computed and stored here. No error checks will be * performed on this parameter. * \param islocal Boolean; this flag controls which perspective to use. If * true then we use the local perspective; otherwise we use the global * perspective. The local perspective for a vertex v is the set of all * immediate neighbours of v. In contrast, the global perspective * for v is the vertex set of \p graph. * \param vid The vertex to use if we are considering a local perspective, * i.e. if \p islocal is true. This vertex will be ignored if * \p islocal is false. That is, if \p islocal is false then it is safe * pass the value -1 here. On the other hand, if \p islocal is true then * it is assumed that this is indeed a vertex of \p graph. * \param mode Defines the sort of neighbourhood to consider for \p vid. This * is only relevant if we are considering the local perspective, i.e. if * \p islocal is true. If we are considering the global perspective, * then this parameter would be ignored. In other words, if \p islocal * is false then it is safe to pass the value \p IGRAPH_ALL here. If * \p graph is undirected, then we use all the immediate neighbours of * \p vid. Thus if you know that \p graph is undirected, then it is * safe to pass the value \p IGRAPH_ALL here. Supported values are: * \clist * \cli IGRAPH_OUT * Use the out-neighbours of \p vid. This option is only relevant * when \p graph is a digraph and we are considering the local * perspective. * \cli IGRAPH_IN * Use the in-neighbours of \p vid. Again this option is only relevant * when \p graph is a directed graph and we are considering the local * perspective. * \cli IGRAPH_ALL * Use both the in- and out-neighbours of \p vid. This option is only * relevant if \p graph is a digraph and we are considering a local * perspective. Also use this value if \p graph is undirected or we * are considering the global perspective. * \endclist * \return Codes: * \clist * \cli IGRAPH_EINVAL * This error code is returned in the following case: The vector * \p U, or some combination of its values, sums to zero. * \cli IGRAPH_SUCCESS * This signal is returned if the cumulative proportionate values * were successfully computed. * \endclist * * Time complexity: O(2n) where n is the number of vertices in the * perspective of vid. */ int igraph_vcumulative_proportionate_values(const igraph_t *graph, const igraph_vector_t *U, igraph_vector_t *V, igraph_bool_t islocal, igraph_integer_t vid, igraph_neimode_t mode) { igraph_integer_t v; igraph_real_t C; /* cumulative probability */ igraph_real_t P; /* probability */ igraph_real_t S; /* sum of values */ igraph_vit_t A; /* all vertices in v's perspective */ igraph_vs_t vs; long int i; /* Set the perspective. Let v be the vertex under consideration; it might */ /* be that we want to update v's strategy. The local perspective for v */ /* consists of its immediate neighbours. In contrast, the global */ /* perspective for v are all the vertices in the given graph. Hence in the */ /* global perspective, we will ignore the given vertex and the given */ /* neighbourhood type, but instead consider all vertices in the given */ /* graph. */ if (islocal) IGRAPH_CHECK(igraph_vs_adj(&vs, vid, mode)); else IGRAPH_CHECK(igraph_vs_all(&vs)); IGRAPH_FINALLY(igraph_vs_destroy, &vs); /* Sum up all the values of vector U in the perspective for v. This */ /* sum will be used in normalizing each value. If we are using a local */ /* perspective, then we also need to consider the quantity of v in */ /* computing the sum. */ /* NOTE: Here we assume that each value to be summed is nonnegative, */ /* and at least one of the values is nonzero. The behaviour resulting */ /* from all values being zero would be division by zero later on when */ /* we normalize each value. We check to see that the values sum to zero. */ /* NOTE: In this function, the order in which we iterate through the */ /* vertices of interest should be the same as the order in which we do so */ /* in the caller function. If the caller function doesn't care about the */ /* order of values in the resulting vector V, then there's no need to take */ /* special notice of that order. But in some cases the order of values in */ /* V is taken into account, for example, in roulette wheel selection. */ S = 0.0; IGRAPH_CHECK(igraph_vit_create(graph, vs, &A)); IGRAPH_FINALLY(igraph_vit_destroy, &A); while (!IGRAPH_VIT_END(A)) { v = (igraph_integer_t)IGRAPH_VIT_GET(A); S += (igraph_real_t)VECTOR(*U)[v]; IGRAPH_VIT_NEXT(A); } if (islocal) S += (igraph_real_t)VECTOR(*U)[vid]; /* avoid division by zero later on */ if (S == (igraph_real_t)0.0) { igraph_vit_destroy(&A); igraph_vs_destroy(&vs); IGRAPH_FINALLY_CLEAN(2); IGRAPH_ERROR("Vector of values sums to zero", IGRAPH_EINVAL); } /* Get cumulative probability and relative value for each vertex in the */ /* perspective of v. The vector V holds the cumulative proportionate */ /* values of all vertices in v's perspective. The value V[0] is the */ /* cumulative proportionate value of the first vertex in the vertex */ /* iterator A. The value V[1] is the cumulative proportionate value of */ /* the second vertex in the iterator A. And so on. If we are using the */ /* local perspective, then we also need to consider the cumulative */ /* proportionate value of v. In the case of the local perspective, we */ /* don't need to compute and store v's cumulative proportionate value, */ /* but we pretend that such value is appended to the vector V. */ C = 0.0; i = 0; IGRAPH_VIT_RESET(A); IGRAPH_VECTOR_INIT_FINALLY(V, IGRAPH_VIT_SIZE(A)); while (!IGRAPH_VIT_END(A)) { v = (igraph_integer_t)IGRAPH_VIT_GET(A); /* NOTE: Beware of division by zero here. This can happen if the vector */ /* of values, or a combination of interest, sums to zero. */ P = (igraph_real_t)VECTOR(*U)[v] / S; C += P; VECTOR(*V)[i] = C; i++; IGRAPH_VIT_NEXT(A); } igraph_vit_destroy(&A); igraph_vs_destroy(&vs); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } /* * Internal use only. * A set of standard tests to be performed prior to strategy updates. The * tests contained in this function are common to many strategy revision * functions in this file. This function is meant to be invoked from within * a specific strategy update function in order to perform certain common * tests, including sanity checks and conditions under which no strategy * updates are necessary. * * \param graph The graph object representing the game network. This cannot * be the empty or trivial graph, but must have at least two vertices * and one edge. If \p graph has one vertex, then no strategy update * would take place. Furthermore, if \p graph has at least two vertices * but zero edges, then strategy update would also not take place. * \param vid The vertex whose strategy is to be updated. It is assumed that * \p vid represents a vertex in \p graph. No checking is performed and * it is your responsibility to ensure that \p vid is indeed a vertex * of \p graph. If an isolated vertex is provided, i.e. the input * vertex has degree 0, then no strategy update would take place and * \p vid would retain its current strategy. Strategy update would also * not take place if the local neighbourhood of \p vid are its * in-neighbours (respectively out-neighbours), but \p vid has zero * in-neighbours (respectively out-neighbours). Loops are ignored in * computing the degree (in, out, all) of \p vid. * \param quantities A vector of quantities providing the quantity of each * vertex in \p graph. Think of each entry of the vector as being * generated by a function such as the fitness function for the game. * So if the vector represents fitness quantities, then each vector * entry is the fitness of some vertex. The length of this vector must * be the same as the number of vertices in the vertex set of \p graph. * \param strategies A vector of the current strategies for the vertex * population. Each strategy is identified with a nonnegative integer, * whose interpretation depends on the payoff matrix of the game. * Generally we use the strategy ID as a row or column index of the * payoff matrix. The length of this vector must be the same as the * number of vertices in the vertex set of \p graph. * \param mode Defines the sort of neighbourhood to consider for \p vid. If * \p graph is undirected, then we use all the immediate neighbours of * \p vid. Thus if you know that \p graph is undirected, then it is safe * to pass the value \p IGRAPH_ALL here. Supported values are: * \clist * \cli IGRAPH_OUT * Use the out-neighbours of \p vid. This option is only relevant * when \p graph is a directed graph. * \cli IGRAPH_IN * Use the in-neighbours of \p vid. Again this option is only relevant * when \p graph is a directed graph. * \cli IGRAPH_ALL * Use both the in- and out-neighbours of \p vid. This option is only * relevant if \p graph is a digraph. Also use this value if * \p graph is undirected. * \endclist * \param updates Boolean; at the end of this test suite, this flag * indicates whether to proceed with strategy revision. If true then * strategy revision should proceed; otherwise there is no need to * continue with revising a vertex's strategy. A caller function that * invokes this function would use the value of \p updates to * determine whether to proceed with strategy revision. * \param islocal Boolean; this flag controls which perspective to use. If * true then we use the local perspective; otherwise we use the global * perspective. The local perspective for \p vid is the set of all * immediate neighbours of \p vid. In contrast, the global perspective * for \p vid is the vertex set of \p graph. * \return Codes: * \clist * \cli IGRAPH_EINVAL * This error code is returned in each of the following cases: * (1) Any of the parameters \p graph, \p quantities, or * \p strategies is a null pointer. (2) The vector \p quantities * or \p strategies has a length different from the number of * vertices in \p graph. (3) The parameter \p graph is the empty * or null graph, i.e. the graph with zero vertices and edges. * \cli IGRAPH_SUCCESS * This signal is returned if no errors were raised. You should use * the value of the boolean \p updates to decide whether to go * ahead with updating a vertex's strategy. * \endclist */ int igraph_microscopic_standard_tests(const igraph_t *graph, igraph_integer_t vid, const igraph_vector_t *quantities, const igraph_vector_t *strategies, igraph_neimode_t mode, igraph_bool_t *updates, igraph_bool_t islocal) { igraph_integer_t nvert; igraph_vector_t degv; *updates=1; /* sanity checks */ if (graph == NULL) { IGRAPH_ERROR("Graph is a null pointer", IGRAPH_EINVAL); } if (quantities == NULL) { IGRAPH_ERROR("Quantities vector is a null pointer", IGRAPH_EINVAL); } if (strategies == NULL) { IGRAPH_ERROR("Strategies vector is a null pointer", IGRAPH_EINVAL); } /* the empty graph */ nvert=igraph_vcount(graph); if (nvert < 1) { IGRAPH_ERROR("Graph cannot be the empty graph", IGRAPH_EINVAL); } /* invalid vector length */ if (nvert != (igraph_integer_t)igraph_vector_size(quantities)) { IGRAPH_ERROR("Size of quantities vector different from number of vertices", IGRAPH_EINVAL); } if (nvert != (igraph_integer_t)igraph_vector_size(strategies)) { IGRAPH_ERROR("Size of strategies vector different from number of vertices", IGRAPH_EINVAL); } /* Various conditions under which no strategy updates will take place. That * is, the vertex retains its current strategy. */ /* given graph has < 2 vertices */ if (nvert < 2) { *updates=0; } /* graph has >= 2 vertices, but no edges */ if (igraph_ecount(graph) < 1) { *updates=0; } /* Test for vertex isolation, depending on the perspective given. For * undirected graphs, a given vertex v is isolated if its degree is zero. * If we are considering in-neighbours (respectively out-neighbours), then * we say that v is isolated if its in-degree (respectively out-degree) is * zero. In general, this vertex isolation test is only relevant if we are * using a local perspective, i.e. if we only consider the immediate * neighbours (local perspective) of v as opposed to all vertices in the * vertex set of the graph (global perspective). */ if (islocal) { /* Moving on ahead with vertex isolation test, since local perspective */ /* is requested. */ IGRAPH_VECTOR_INIT_FINALLY(°v, 1); IGRAPH_CHECK(igraph_degree(graph, °v, igraph_vss_1(vid), mode, IGRAPH_NO_LOOPS)); if (VECTOR(degv)[0] < 1) *updates = 0; igraph_vector_destroy(°v); IGRAPH_FINALLY_CLEAN(1); } return IGRAPH_SUCCESS; } /** * \ingroup spatialgames * \function igraph_deterministic_optimal_imitation * \brief Adopt a strategy via deterministic optimal imitation. * * A simple deterministic imitation strategy where a vertex revises its * strategy to that which yields a local optimal. Here "local" is with * respect to the immediate neighbours of the vertex. The vertex retains its * current strategy where this strategy yields a locally optimal quantity. * The quantity in this case could be a measure such as fitness. * * \param graph The graph object representing the game network. This cannot * be the empty or trivial graph, but must have at least two vertices * and one edge. If \p graph has one vertex, then no strategy update * would take place. Furthermore, if \p graph has at least two vertices * but zero edges, then strategy update would also not take place. * \param vid The vertex whose strategy is to be updated. It is assumed that * \p vid represents a vertex in \p graph. No checking is performed and * it is your responsibility to ensure that \p vid is indeed a vertex * of \p graph. If an isolated vertex is provided, i.e. the input * vertex has degree 0, then no strategy update would take place and * \p vid would retain its current strategy. Strategy update would also * not take place if the local neighbourhood of \p vid are its * in-neighbours (respectively out-neighbours), but \p vid has zero * in-neighbours (respectively out-neighbours). Loops are ignored in * computing the degree (in, out, all) of \p vid. * \param optimality Logical; controls the type of optimality to be used. * Supported values are: * \clist * \cli IGRAPH_MAXIMUM * Use maximum deterministic imitation, where the strategy of the * vertex with maximum quantity (e.g. fitness) would be adopted. We * update the strategy of \p vid to that which yields a local * maximum. * \cli IGRAPH_MINIMUM * Use minimum deterministic imitation. That is, the strategy of the * vertex with minimum quantity would be imitated. In other words, * update to the strategy that yields a local minimum. * \endclist * \param quantities A vector of quantities providing the quantity of each * vertex in \p graph. Think of each entry of the vector as being * generated by a function such as the fitness function for the game. * So if the vector represents fitness quantities, then each vector * entry is the fitness of some vertex. The length of this vector must * be the same as the number of vertices in the vertex set of \p graph. * \param strategies A vector of the current strategies for the vertex * population. The updated strategy for \p vid would be stored here. * Each strategy is identified with a nonnegative integer, whose * interpretation depends on the payoff matrix of the game. Generally * we use the strategy ID as a row or column index of the payoff * matrix. The length of this vector must be the same as the number of * vertices in the vertex set of \p graph. * \param mode Defines the sort of neighbourhood to consider for \p vid. If * \p graph is undirected, then we use all the immediate neighbours of * \p vid. Thus if you know that \p graph is undirected, then it is safe * to pass the value \p IGRAPH_ALL here. Supported values are: * \clist * \cli IGRAPH_OUT * Use the out-neighbours of \p vid. This option is only relevant * when \p graph is a directed graph. * \cli IGRAPH_IN * Use the in-neighbours of \p vid. Again this option is only relevant * when \p graph is a directed graph. * \cli IGRAPH_ALL * Use both the in- and out-neighbours of \p vid. This option is only * relevant if \p graph is a digraph. Also use this value if * \p graph is undirected. * \endclist * \return The error code \p IGRAPH_EINVAL is returned in each of the * following cases: (1) Any of the parameters \p graph, \p quantities, * or \p strategies is a null pointer. (2) The vector \p quantities * or \p strategies has a length different from the number of vertices * in \p graph. (3) The parameter \p graph is the empty or null graph, * i.e. the graph with zero vertices and edges. * * Time complexity: O(2d), where d is the degree of the vertex \p vid. * * \example examples/simple/igraph_deterministic_optimal_imitation.c */ int igraph_deterministic_optimal_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_optimal_t optimality, const igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode) { igraph_integer_t i, k, v; igraph_real_t q; igraph_vector_t adj; igraph_bool_t updates; IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, vid, quantities, strategies, mode, &updates, /*is local?*/ 1)); if (!updates) { return IGRAPH_SUCCESS; } /* Nothing to do */ /* Choose a locally optimal strategy to imitate. This can be either maximum * or minimum deterministic imitation. By now we know that the given vertex v * has degree >= 1 and at least 1 edge. Then within its immediate * neighbourhood adj(v) and including v itself, there exists a vertex whose * strategy yields a local optimal quantity. */ /* Random permutation of adj(v). This ensures that if there are multiple */ /* candidates with an optimal strategy, then we choose one such candidate */ /* at random. */ IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode)); IGRAPH_CHECK(igraph_vector_shuffle(&adj)); /* maximum deterministic imitation */ i = vid; q = (igraph_real_t)VECTOR(*quantities)[vid]; if (optimality == IGRAPH_MAXIMUM) { for (k = 0; k < igraph_vector_size(&adj); k++) { v = (igraph_integer_t) VECTOR(adj)[k]; if ((igraph_real_t)VECTOR(*quantities)[v] > q) { i = v; q = (igraph_real_t)VECTOR(*quantities)[v]; } } } else { /* minimum deterministic imitation */ for (k = 0; k < igraph_vector_size(&adj); k++) { v = (igraph_integer_t) VECTOR(adj)[k]; if ((igraph_real_t)VECTOR(*quantities)[v] < q) { i = v; q = (igraph_real_t)VECTOR(*quantities)[v]; } } } /* Now i is a vertex with a locally optimal quantity, the value of which */ /* is q. Update the strategy of vid to that of i. */ VECTOR(*strategies)[vid] = VECTOR(*strategies)[i]; igraph_vector_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * \ingroup spatialgames * \function igraph_moran_process * \brief The Moran process in a network setting. * * This is an extension of the classic Moran process to a network setting. * The Moran process is a model of haploid (asexual) reproduction within a * population having a fixed size. In the network setting, the Moran process * operates on a weighted graph. At each time step a vertex a is chosen for * reproduction and another vertex b is chosen for death. Vertex a gives birth * to an identical clone c, which replaces b. Vertex c is a clone of a in that * c inherits both the current quantity (e.g. fitness) and current strategy * of a. * * * The graph G representing the game network is assumed to be simple, * i.e. free of loops and without multiple edges. If, on the other hand, G has * a loop incident on some vertex v, then it is possible that when v is chosen * for reproduction it would forgo this opportunity. In particular, when v is * chosen for reproduction and v is also chosen for death, the clone of v * would be v itself with its current vertex ID. In effect v forgoes its * chance for reproduction. * * \param graph The graph object representing the game network. This cannot * be the empty or trivial graph, but must have at least two vertices * and one edge. The Moran process will not take place in each of the * following cases: (1) If \p graph has one vertex. (2) If \p graph has * at least two vertices but zero edges. * \param weights A vector of all edge weights for \p graph. Thus weights[i] * means the weight of the edge with edge ID i. For the purpose of the * Moran process, each weight is assumed to be positive; it is your * responsibility to ensure this condition holds. The length of this * vector must be the same as the number of edges in \p graph. * \param quantities A vector of quantities providing the quantity of each * vertex in \p graph. The quantity of the new clone will be stored * here. Think of each entry of the vector as being generated by a * function such as the fitness function for the game. So if the vector * represents fitness quantities, then each vector entry is the fitness * of some vertex. The length of this vector must be the same as the * number of vertices in the vertex set of \p graph. For the purpose of * the Moran process, each vector entry is assumed to be nonnegative; * no checks will be performed for this. It is your responsibility to * ensure that at least one entry is positive. Furthermore, this vector * cannot be a vector of zeros; this condition will be checked. * \param strategies A vector of the current strategies for the vertex * population. The strategy of the new clone will be stored here. Each * strategy is identified with a nonnegative integer, whose * interpretation depends on the payoff matrix of the game. Generally * we use the strategy ID as a row or column index of the payoff * matrix. The length of this vector must be the same as the number of * vertices in the vertex set of \p graph. * \param mode Defines the sort of neighbourhood to consider for the vertex a * chosen for reproduction. This is only relevant if \p graph is * directed. If \p graph is undirected, then it is safe to pass the * value \p IGRAPH_ALL here. Supported values are: * \clist * \cli IGRAPH_OUT * Use the out-neighbours of a. This option is only relevant when * \p graph is directed. * \cli IGRAPH_IN * Use the in-neighbours of a. Again this option is only relevant * when \p graph is directed. * \cli IGRAPH_ALL * Use both the in- and out-neighbours of a. This option is only * relevant if \p graph is directed. Also use this value if * \p graph is undirected. * \endclist * \return The error code \p IGRAPH_EINVAL is returned in each of the following * cases: (1) Any of the parameters \p graph, \p weights, * \p quantities or \p strategies is a null pointer. (2) The vector * \p quantities or \p strategies has a length different from the * number of vertices in \p graph. (3) The vector \p weights has a * length different from the number of edges in \p graph. (4) The * parameter \p graph is the empty or null graph, i.e. the graph with * zero vertices and edges. (5) The vector \p weights, or the * combination of interest, sums to zero. (6) The vector \p quantities, * or the combination of interest, sums to zero. * * Time complexity: depends on the random number generator, but is usually * O(n) where n is the number of vertices in \p graph. * * * References: * \clist * \cli (Lieberman et al. 2005) * E. Lieberman, C. Hauert, and M. A. Nowak. Evolutionary dynamics on * graphs. \emb Nature, \eme 433(7023):312--316, 2005. * \cli (Moran 1958) * P. A. P. Moran. Random processes in genetics. \emb Mathematical * Proceedings of the Cambridge Philosophical Society, \eme 54(1):60--71, * 1958. * \endclist * * \example examples/simple/igraph_moran_process.c */ int igraph_moran_process(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode) { igraph_bool_t updates; igraph_integer_t a = -1; /* vertex chosen for reproduction */ igraph_integer_t b = -1; /* vertex chosen for death */ igraph_integer_t e, nedge, u, v; igraph_real_t r; /* random number */ igraph_vector_t deg; igraph_vector_t V; /* vector of cumulative proportionate values */ igraph_vit_t vA; /* vertex list */ igraph_eit_t eA; /* edge list */ igraph_vs_t vs; igraph_es_t es; long int i; /* don't test for vertex isolation, hence vid = -1 and islocal = 0 */ IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, /*vid*/ -1, quantities, strategies, mode, &updates, /*is local?*/ 0)); if (!updates) return IGRAPH_SUCCESS; /* nothing more to do */ if (weights == NULL) IGRAPH_ERROR("Weights vector is a null pointer", IGRAPH_EINVAL); nedge = igraph_ecount(graph); if (nedge != (igraph_integer_t)igraph_vector_size(weights)) { IGRAPH_ERROR("Size of weights vector different from number of edges", IGRAPH_EINVAL); } /* Cumulative proportionate quantities. We are using the global */ /* perspective, hence islocal = 0, vid = -1 and mode = IGRAPH_ALL. */ IGRAPH_CHECK(igraph_vcumulative_proportionate_values(graph, quantities, &V, /*is local?*/ 0, /*vid*/ -1, /*mode*/ IGRAPH_ALL)); /* Choose a vertex for reproduction from among all vertices in the graph. */ /* The vertex is chosen proportionate to its quantity and such that its */ /* degree is >= 1. In case we are considering in-neighbours (respectively */ /* out-neighbours), the chosen vertex must have in-degree (respectively */ /* out-degree) >= 1. All loops will be ignored. At this point, we know */ /* that the graph has at least one edge, which may be directed or not. */ /* Furthermore the quantities of all vertices sum to a positive value. */ /* Hence at least one vertex will be chosen for reproduction. */ IGRAPH_CHECK(igraph_vs_all(&vs)); IGRAPH_FINALLY(igraph_vs_destroy, &vs); IGRAPH_CHECK(igraph_vit_create(graph, vs, &vA)); IGRAPH_FINALLY(igraph_vit_destroy, &vA); RNG_BEGIN(); r = RNG_UNIF01(); RNG_END(); i = 0; IGRAPH_VECTOR_INIT_FINALLY(°, 1); while (!IGRAPH_VIT_END(vA)) { u = (igraph_integer_t)IGRAPH_VIT_GET(vA); IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_1(u), mode, IGRAPH_NO_LOOPS)); if (VECTOR(deg)[0] < 1) { i++; IGRAPH_VIT_NEXT(vA); continue; } if (r <= VECTOR(V)[i]) { /* we have found our candidate vertex for reproduction */ a = u; break; } i++; IGRAPH_VIT_NEXT(vA); } /* By now we should have chosen a vertex for reproduction. Check this. */ assert(a >= 0); /* Cumulative proportionate weights. We are using the local perspective */ /* with respect to vertex a, which has been chosen for reproduction. */ /* The degree of a is deg(a) >= 1 with respect to the mode "mode", which */ /* can flag either the in-degree, out-degree or all degree of a. But it */ /* still might happen that the edge weights of interest would sum to zero. */ /* An error would be raised in that case. */ igraph_vector_destroy(&V); IGRAPH_CHECK(igraph_ecumulative_proportionate_values(graph, weights, &V, /*is local?*/ 1, /*vertex*/ a, mode)); /* Choose a vertex for death from among all vertices in a's perspective. */ /* Let E be all the edges in the perspective of a. If (u,v) \in E is any */ /* such edge, then we have a = u or a = v. That is, any edge in E has a */ /* for one of its endpoints. As G is assumed to be a simple graph, then */ /* exactly one of u or v is the vertex a. Without loss of generality, we */ /* assume that each edge in E has the form (a, v_i). Then the vertex v_j */ /* chosen for death is chosen proportionate to the weight of the edge */ /* (a, v_j). */ IGRAPH_CHECK(igraph_es_incident(&es, a, mode)); IGRAPH_FINALLY(igraph_es_destroy, &es); IGRAPH_CHECK(igraph_eit_create(graph, es, &eA)); IGRAPH_FINALLY(igraph_eit_destroy, &eA); RNG_BEGIN(); r = RNG_UNIF01(); RNG_END(); i = 0; while (!IGRAPH_EIT_END(eA)) { e = (igraph_integer_t)IGRAPH_EIT_GET(eA); if (r <= VECTOR(V)[i]) { /* We have found our candidate vertex for death; call this vertex b. */ /* As G is simple, then a =/= b. Check the latter condition. */ IGRAPH_CHECK(igraph_edge(graph, /*edge ID*/ e, /*tail vertex*/ &u, /*head vertex*/ &v)); if (a == u) b = v; else b = u; assert(a != b); /* always true if G is simple */ break; } i++; IGRAPH_EIT_NEXT(eA); } /* By now a vertex a is chosen for reproduction and a vertex b is chosen */ /* for death. Check that b has indeed been chosen. Clone vertex a and kill */ /* vertex b. Let the clone c have the vertex ID of b, and the strategy and */ /* quantity of a. */ assert(b >= 0); VECTOR(*quantities)[b] = VECTOR(*quantities)[a]; VECTOR(*strategies)[b] = VECTOR(*strategies)[a]; igraph_vector_destroy(°); igraph_vector_destroy(&V); igraph_vit_destroy(&vA); igraph_eit_destroy(&eA); igraph_vs_destroy(&vs); igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(6); return IGRAPH_SUCCESS; } /** * \ingroup spatialgames * \function igraph_roulette_wheel_imitation * \brief Adopt a strategy via roulette wheel selection. * * A simple stochastic imitation strategy where a vertex revises its * strategy to that of a vertex u chosen proportionate to u's quantity * (e.g. fitness). This is a special case of stochastic imitation, where a * candidate is not chosen uniformly at random but proportionate to its * quantity. * * \param graph The graph object representing the game network. This cannot * be the empty or trivial graph, but must have at least two vertices * and one edge. If \p graph has one vertex, then no strategy update * would take place. Furthermore, if \p graph has at least two vertices * but zero edges, then strategy update would also not take place. * \param vid The vertex whose strategy is to be updated. It is assumed that * \p vid represents a vertex in \p graph. No checking is performed and * it is your responsibility to ensure that \p vid is indeed a vertex * of \p graph. If an isolated vertex is provided, i.e. the input * vertex has degree 0, then no strategy update would take place and * \p vid would retain its current strategy. Strategy update would also * not take place if the local neighbourhood of \p vid are its * in-neighbours (respectively out-neighbours), but \p vid has zero * in-neighbours (respectively out-neighbours). Loops are ignored in * computing the degree (in, out, all) of \p vid. * \param islocal Boolean; this flag controls which perspective to use in * computing the relative quantity. If true then we use the local * perspective; otherwise we use the global perspective. The local * perspective for \p vid is the set of all immediate neighbours of * \p vid. In contrast, the global perspective for \p vid is the * vertex set of \p graph. * \param quantities A vector of quantities providing the quantity of each * vertex in \p graph. Think of each entry of the vector as being * generated by a function such as the fitness function for the game. * So if the vector represents fitness quantities, then each vector * entry is the fitness of some vertex. The length of this vector must * be the same as the number of vertices in the vertex set of \p graph. * For the purpose of roulette wheel selection, each vector entry is * assumed to be nonnegative; no checks will be performed for this. It * is your responsibility to ensure that at least one entry is nonzero. * Furthermore, this vector cannot be a vector of zeros; this condition * will be checked. * \param strategies A vector of the current strategies for the vertex * population. The updated strategy for \p vid would be stored here. * Each strategy is identified with a nonnegative integer, whose * interpretation depends on the payoff matrix of the game. Generally * we use the strategy ID as a row or column index of the payoff * matrix. The length of this vector must be the same as the number of * vertices in the vertex set of \p graph. * \param mode Defines the sort of neighbourhood to consider for \p vid. This * is only relevant if we are considering the local perspective, i.e. if * \p islocal is true. If we are considering the global perspective, * then it is safe to pass the value \p IGRAPH_ALL here. If \p graph is * undirected, then we use all the immediate neighbours of \p vid. Thus * if you know that \p graph is undirected, then it is safe to pass the * value \p IGRAPH_ALL here. Supported values are: * \clist * \cli IGRAPH_OUT * Use the out-neighbours of \p vid. This option is only relevant * when \p graph is a digraph and we are considering the local * perspective. * \cli IGRAPH_IN * Use the in-neighbours of \p vid. Again this option is only relevant * when \p graph is a directed graph and we are considering the local * perspective. * \cli IGRAPH_ALL * Use both the in- and out-neighbours of \p vid. This option is only * relevant if \p graph is a digraph. Also use this value if * \p graph is undirected or we are considering the global * perspective. * \endclist * \return The error code \p IGRAPH_EINVAL is returned in each of the following * cases: (1) Any of the parameters \p graph, \p quantities, or * \p strategies is a null pointer. (2) The vector \p quantities or * \p strategies has a length different from the number of vertices * in \p graph. (3) The parameter \p graph is the empty or null graph, * i.e. the graph with zero vertices and edges. (4) The vector * \p quantities sums to zero. * * Time complexity: O(n) where n is the number of vertices in the perspective * to consider. If we consider the global perspective, then n is the number * of vertices in the vertex set of \p graph. On the other hand, for the local * perspective n is the degree of \p vid, excluding loops. * * * Reference: * \clist * \cli (Yu & Gen 2010) * X. Yu and M. Gen. \emb Introduction to Evolutionary Algorithms. \eme * Springer, 2010, pages 18--20. * \endclist * * \example examples/simple/igraph_roulette_wheel_imitation.c */ int igraph_roulette_wheel_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_bool_t islocal, const igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode) { igraph_bool_t updates; igraph_integer_t u; igraph_real_t r; /* random number */ igraph_vector_t V; /* vector of cumulative proportionate quantities */ igraph_vit_t A; /* all vertices in v's perspective */ igraph_vs_t vs; long int i; IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, vid, quantities, strategies, mode, &updates, islocal)); if (!updates) return IGRAPH_SUCCESS; /* nothing further to do */ /* set the perspective */ if (islocal) IGRAPH_CHECK(igraph_vs_adj(&vs, vid, mode)); else IGRAPH_CHECK(igraph_vs_all(&vs)); IGRAPH_FINALLY(igraph_vs_destroy, &vs); IGRAPH_CHECK(igraph_vit_create(graph, vs, &A)); IGRAPH_FINALLY(igraph_vit_destroy, &A); IGRAPH_CHECK(igraph_vcumulative_proportionate_values(graph, quantities, &V, islocal, vid, mode)); /* Finally, choose a vertex u to imitate. The vertex u is chosen */ /* proportionate to its quantity. In the case of a local perspective, we */ /* pretend that v's cumulative proportionate quantity has been appended to */ /* the vector V. Let V be of length n so that V[n-1] is the last element */ /* of V, and let r be a real number chosen uniformly at random from the */ /* unit interval [0,1]. If r > V[i] for all i < n, then v defaults to */ /* retaining its current strategy. Similarly in the case of the global */ /* perspective, if r > V[i] for all i < n - 1 then v would adopt the */ /* strategy of the vertex whose cumulative proportionate quantity is */ /* V[n-1]. */ /* NOTE: Here we assume that the order in which we iterate through the */ /* vertices in A is the same as the order in which we do so in the */ /* invoked function igraph_vcumulative_proportionate_values(). */ /* Otherwise we would incorrectly associate each V[i] with a vertex in A. */ RNG_BEGIN(); r = RNG_UNIF01(); RNG_END(); i = 0; while (!IGRAPH_VIT_END(A)) { if (r <= VECTOR(V)[i]) { /* We have found our candidate vertex for imitation. Update strategy */ /* of v to that of u, and exit the selection loop. */ u = (igraph_integer_t)IGRAPH_VIT_GET(A); VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; break; } i++; IGRAPH_VIT_NEXT(A); } /* By now, vertex v should either retain its current strategy or it has */ /* adopted the strategy of a vertex in its perspective. Nothing else to */ /* do, but clean up. */ igraph_vector_destroy(&V); igraph_vit_destroy(&A); igraph_vs_destroy(&vs); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } /** * \ingroup spatialgames * \function igraph_stochastic_imitation * \brief Adopt a strategy via stochastic imitation with uniform selection. * * A simple stochastic imitation strategy where a vertex revises its * strategy to that of a vertex chosen uniformly at random from its local * neighbourhood. This is called stochastic imitation via uniform selection, * where the strategy to imitate is chosen via some random process. For the * purposes of this function, we use uniform selection from a pool of * candidates. * * \param graph The graph object representing the game network. This cannot * be the empty or trivial graph, but must have at least two vertices * and one edge. If \p graph has one vertex, then no strategy update * would take place. Furthermore, if \p graph has at least two vertices * but zero edges, then strategy update would also not take place. * \param vid The vertex whose strategy is to be updated. It is assumed that * \p vid represents a vertex in \p graph. No checking is performed and * it is your responsibility to ensure that \p vid is indeed a vertex * of \p graph. If an isolated vertex is provided, i.e. the input * vertex has degree 0, then no strategy update would take place and * \p vid would retain its current strategy. Strategy update would also * not take place if the local neighbourhood of \p vid are its * in-neighbours (respectively out-neighbours), but \p vid has zero * in-neighbours (respectively out-neighbours). Loops are ignored in * computing the degree (in, out, all) of \p vid. * \param algo This flag controls which algorithm to use in stochastic * imitation. Supported values are: * \clist * \cli IGRAPH_IMITATE_AUGMENTED * Augmented imitation. Vertex \p vid imitates the strategy of the * chosen vertex u provided that doing so would increase the * quantity (e.g. fitness) of \p vid. Augmented imitation can be * thought of as "imitate if better". * \cli IGRAPH_IMITATE_BLIND * Blind imitation. Vertex \p vid blindly imitates the strategy of * the chosen vertex u, regardless of whether doing so would * increase or decrease the quantity of \p vid. * \cli IGRAPH_IMITATE_CONTRACTED * Contracted imitation. Here vertex \p vid imitates the strategy of * the chosen vertex u if doing so would decrease the quantity of * \p vid. Think of contracted imitation as "imitate if worse". * \endclist * \param quantities A vector of quantities providing the quantity of each * vertex in \p graph. Think of each entry of the vector as being * generated by a function such as the fitness function for the game. * So if the vector represents fitness quantities, then each vector * entry is the fitness of some vertex. The length of this vector must * be the same as the number of vertices in the vertex set of \p graph. * \param strategies A vector of the current strategies for the vertex * population. The updated strategy for \p vid would be stored here. * Each strategy is identified with a nonnegative integer, whose * interpretation depends on the payoff matrix of the game. Generally * we use the strategy ID as a row or column index of the payoff * matrix. The length of this vector must be the same as the number of * vertices in the vertex set of \p graph. * \param mode Defines the sort of neighbourhood to consider for \p vid. If * \p graph is undirected, then we use all the immediate neighbours of * \p vid. Thus if you know that \p graph is undirected, then it is safe * to pass the value \p IGRAPH_ALL here. Supported values are: * \clist * \cli IGRAPH_OUT * Use the out-neighbours of \p vid. This option is only relevant * when \p graph is a directed graph. * \cli IGRAPH_IN * Use the in-neighbours of \p vid. Again this option is only relevant * when \p graph is a directed graph. * \cli IGRAPH_ALL * Use both the in- and out-neighbours of \p vid. This option is only * relevant if \p graph is a digraph. Also use this value if * \p graph is undirected. * \endclist * \return The error code \p IGRAPH_EINVAL is returned in each of the following * cases: (1) Any of the parameters \p graph, \p quantities, or * \p strategies is a null pointer. (2) The vector \p quantities or * \p strategies has a length different from the number of vertices * in \p graph. (3) The parameter \p graph is the empty or null graph, * i.e. the graph with zero vertices and edges. (4) The parameter * \p algo refers to an unsupported stochastic imitation algorithm. * * Time complexity: depends on the uniform random number generator, but should * usually be O(1). * * \example examples/simple/igraph_stochastic_imitation.c */ int igraph_stochastic_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_imitate_algorithm_t algo, const igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode) { igraph_bool_t updates; igraph_integer_t u; igraph_vector_t adj; int i; /* sanity checks */ if (algo != IGRAPH_IMITATE_AUGMENTED && algo != IGRAPH_IMITATE_BLIND && algo != IGRAPH_IMITATE_CONTRACTED) { IGRAPH_ERROR("Unsupported stochastic imitation algorithm", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, vid, quantities, strategies, mode, &updates, /*is local?*/ 1)); if (!updates) return IGRAPH_SUCCESS; /* nothing more to do */ /* immediate neighbours of v */ IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode)); /* Blind imitation. Let v be the vertex whose strategy we want to revise. */ /* Choose a vertex u uniformly at random from the immediate neighbours of */ /* v, including v itself. Then blindly update the strategy of v to that of */ /* u, irrespective of whether doing so would increase or decrease the */ /* quantity (e.g. fitness) of v. Here v retains its current strategy if */ /* the chosen vertex u is indeed v itself. */ if (algo == IGRAPH_IMITATE_BLIND) { IGRAPH_CHECK(igraph_vector_push_back(&adj, vid)); RNG_BEGIN(); i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); RNG_END(); u = (igraph_integer_t) VECTOR(adj)[i]; VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } /* Augmented imitation. Let v be the vertex whose strategy we want to */ /* revise. Let f be the quantity function for the game. Choose a vertex u */ /* uniformly at random from the immediate neighbours of v; do not include */ /* v. Then v imitates the strategy of u if f(u) > f(v). Otherwise v */ /* retains its current strategy. */ else if (algo == IGRAPH_IMITATE_AUGMENTED) { RNG_BEGIN(); i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); RNG_END(); u = (igraph_integer_t) VECTOR(adj)[i]; if (VECTOR(*quantities)[u] > VECTOR(*quantities)[vid]) VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } /* Contracted imitation. Let v be the vertex whose strategy we want to */ /* update and let f be the quantity function for the game. Choose a vertex */ /* u uniformly at random from the immediate neighbours of v, excluding v */ /* itself. Then v imitates the strategy of u provided that f(u) < f(v). */ /* Otherwise v retains its current strategy. */ else if (algo == IGRAPH_IMITATE_CONTRACTED) { RNG_BEGIN(); i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); RNG_END(); u = (igraph_integer_t) VECTOR(adj)[i]; if (VECTOR(*quantities)[u] < VECTOR(*quantities)[vid]) VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } /* clean up */ igraph_vector_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } igraph/src/igraph_trie.c0000644000176000001440000002506012325527073015015 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include /** * \ingroup igraphtrie * \brief Creates a trie node (not to be called directly) * \return Error code: errors by igraph_strvector_init(), * igraph_vector_ptr_init() and igraph_vector_init() might be returned. */ int igraph_i_trie_init_node(igraph_trie_node_t *t) { IGRAPH_STRVECTOR_INIT_FINALLY(&t->strs, 0); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 0); IGRAPH_VECTOR_INIT_FINALLY(&t->values, 0); IGRAPH_FINALLY_CLEAN(3); return 0; } void igraph_i_trie_destroy_node(igraph_trie_node_t *t, igraph_bool_t sfree); /** * \ingroup igraphtrie * \brief Creates a trie. * \return Error code: errors by igraph_strvector_init(), * igraph_vector_ptr_init() and igraph_vector_init() might be returned. */ int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys) { t->maxvalue=-1; t->storekeys=storekeys; IGRAPH_CHECK(igraph_i_trie_init_node( (igraph_trie_node_t *)t )); IGRAPH_FINALLY(igraph_i_trie_destroy_node, t); if (storekeys) { IGRAPH_CHECK(igraph_strvector_init(&t->keys, 0)); } IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup igraphtrie * \brief Destroys a node of a trie (not to be called directly). */ void igraph_i_trie_destroy_node(igraph_trie_node_t *t, igraph_bool_t sfree) { long int i; igraph_strvector_destroy(&t->strs); for (i=0; ichildren); i++) { igraph_trie_node_t *child=VECTOR(t->children)[i]; if (child != 0) { igraph_i_trie_destroy_node(child, 1); } } igraph_vector_ptr_destroy(&t->children); igraph_vector_destroy(&t->values); if (sfree) { igraph_Free(t); } } /** * \ingroup igraphtrie * \brief Destroys a trie (frees allocated memory). */ void igraph_trie_destroy(igraph_trie_t *t) { if (t->storekeys) { igraph_strvector_destroy(&t->keys); } igraph_i_trie_destroy_node( (igraph_trie_node_t*) t, 0); } /** * \ingroup igraphtrie * \brief Internal helping function for igraph_trie_t */ long int igraph_i_strdiff(const char *str, const char *key) { long int diff=0; while (key[diff] != '\0' && str[diff] != '\0' && str[diff]==key[diff]) { diff++; } return diff; } /** * \ingroup igraphtrie * \brief Search/insert in a trie (not to be called directly). * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, igraph_real_t newvalue, long int *id) { char *str; long int i; igraph_bool_t add; /* If newvalue is negative, we don't add the node if nonexistent, only check * for its existence */ add = (newvalue>=0); for (i=0; istrs); i++) { long int diff; igraph_strvector_get(&t->strs, i, &str); diff=igraph_i_strdiff(str, key); if (diff == 0) { /* ------------------------------------ */ /* No match, next */ } else if (str[diff]=='\0' && key[diff]=='\0') { /* ------------------------------------ */ /* They are exactly the same */ if (VECTOR(t->values)[i] != -1) { *id=(long int) VECTOR(t->values)[i]; return 0; } else { VECTOR(t->values)[i]=newvalue; *id=(long int) newvalue; return 0; } } else if (str[diff]=='\0') { /* ------------------------------------ */ /* str is prefix of key, follow its link if there is one */ igraph_trie_node_t *node=VECTOR(t->children)[i]; if (node != 0) { return igraph_trie_get_node(node, key+diff, newvalue, id); } else if (add) { igraph_trie_node_t *node=igraph_Calloc(1, igraph_trie_node_t); if (node==0) { IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1); IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, key+diff)); VECTOR(node->children)[0]=0; VECTOR(node->values)[0]=newvalue; VECTOR(t->children)[i]=node; *id=(long int) newvalue; IGRAPH_FINALLY_CLEAN(3); return 0; } else { *id=-1; return 0; } } else if (key[diff]=='\0' && add) { /* ------------------------------------ */ /* key is prefix of str, the node has to be cut */ char *str2; igraph_trie_node_t *node=igraph_Calloc(1, igraph_trie_node_t); if (node==0) { IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1); IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, str+diff)); VECTOR(node->children)[0]=VECTOR(t->children)[i]; VECTOR(node->values)[0]=VECTOR(t->values)[i]; str2=strdup(str); if (str2 == 0) { IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } str2[diff]='\0'; IGRAPH_FINALLY(free, str2); IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); free(str2); IGRAPH_FINALLY_CLEAN(4); VECTOR(t->values)[i]=newvalue; VECTOR(t->children)[i]=node; *id=(long int) newvalue; return 0; } else if (add) { /* ------------------------------------ */ /* the first diff characters match */ char *str2; igraph_trie_node_t *node=igraph_Calloc(1, igraph_trie_node_t); if (node==0) { IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 2); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 2); IGRAPH_VECTOR_INIT_FINALLY(&node->values, 2); IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, str+diff)); IGRAPH_CHECK(igraph_strvector_set(&node->strs, 1, key+diff)); VECTOR(node->children)[0]=VECTOR(t->children)[i]; VECTOR(node->children)[1]=0; VECTOR(node->values)[0]=VECTOR(t->values)[i]; VECTOR(node->values)[1]=newvalue; str2=strdup(str); if (str2 == 0) { IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } str2[diff]='\0'; IGRAPH_FINALLY(free, str2); IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); free(str2); IGRAPH_FINALLY_CLEAN(4); VECTOR(t->values)[i]=-1; VECTOR(t->children)[i]=node; *id=(long int) newvalue; return 0; } else { /* ------------------------------------------------- */ /* No match, but we requested not to add the new key */ *id=-1; return 0; } } /* ------------------------------------ */ /* Nothing matches */ if (add) { IGRAPH_CHECK(igraph_vector_ptr_reserve(&t->children, igraph_vector_ptr_size(&t->children)+1)); IGRAPH_CHECK(igraph_vector_reserve(&t->values, igraph_vector_size(&t->values)+1)); IGRAPH_CHECK(igraph_strvector_add(&t->strs, key)); igraph_vector_ptr_push_back(&t->children, 0); /* allocated */ igraph_vector_push_back(&t->values, newvalue); /* allocated */ *id=(long int) newvalue; } else { *id=-1; } return 0; } /** * \ingroup igraphtrie * \brief Search/insert in a trie. */ int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id) { if (!t->storekeys) { IGRAPH_CHECK(igraph_trie_get_node( (igraph_trie_node_t*) t, key, t->maxvalue+1, id)); if (*id > t->maxvalue) { t->maxvalue=*id; } return 0; } else { int ret; igraph_error_handler_t *oldhandler; oldhandler=igraph_set_error_handler(igraph_error_handler_ignore); /* Add it to the string vector first, we can undo this later */ ret=igraph_strvector_add(&t->keys, key); if (ret != 0) { igraph_set_error_handler(oldhandler); IGRAPH_ERROR("cannot get element from trie", ret); } ret = igraph_trie_get_node( (igraph_trie_node_t*) t, key, t->maxvalue+1, id); if (ret != 0) { igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys)-1); igraph_set_error_handler(oldhandler); IGRAPH_ERROR("cannot get element from trie", ret); } /* everything is fine */ if (*id > t->maxvalue) { t->maxvalue=*id; } else { igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys)-1); } igraph_set_error_handler(oldhandler); } return 0; } /** * \ingroup igraphtrie * \brief Search/insert in a trie (for internal use). * * @return Error code: * - IGRAPH_ENOMEM: out of memory */ int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length, long int *id) { char *tmp=igraph_Calloc(length+1, char); if (tmp==0) { IGRAPH_ERROR("Cannot get from trie", IGRAPH_ENOMEM); } strncpy(tmp, key, length); tmp[length]='\0'; IGRAPH_FINALLY(free, tmp); IGRAPH_CHECK(igraph_trie_get(t, tmp, id)); igraph_Free(tmp); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup igraphtrie * \brief Search in a trie. * This variant does not add \c key to the trie if it does not exist. * In this case, a negative id is returned. */ int igraph_trie_check(igraph_trie_t *t, const char *key, long int *id) { IGRAPH_CHECK(igraph_trie_get_node( (igraph_trie_node_t*) t, key, -1, id)); return 0; } /** * \ingroup igraphtrie * \brief Get an element of a trie based on its index. */ void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str) { igraph_strvector_get(&t->keys, idx, str); } /** * \ingroup igraphtrie * \brief Returns the size of a trie. */ long int igraph_trie_size(igraph_trie_t *t) { return t->maxvalue+1; } /* Hmmm, very dirty.... */ int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv) { *strv=&t->keys; return 0; } igraph/src/cs_spsolve.c0000644000176000001440000000435012325527073014677 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* solve Gx=b(:,k), where G is either upper (lo=0) or lower (lo=1) triangular */ CS_INT cs_spsolve (cs *G, const cs *B, CS_INT k, CS_INT *xi, CS_ENTRY *x, const CS_INT *pinv, CS_INT lo) { CS_INT j, J, p, q, px, top, n, *Gp, *Gi, *Bp, *Bi ; CS_ENTRY *Gx, *Bx ; if (!CS_CSC (G) || !CS_CSC (B) || !xi || !x) return (-1) ; Gp = G->p ; Gi = G->i ; Gx = G->x ; n = G->n ; Bp = B->p ; Bi = B->i ; Bx = B->x ; top = cs_reach (G, B, k, xi, pinv) ; /* xi[top..n-1]=Reach(B(:,k)) */ for (p = top ; p < n ; p++) x [xi [p]] = 0 ; /* clear x */ for (p = Bp [k] ; p < Bp [k+1] ; p++) x [Bi [p]] = Bx [p] ; /* scatter B */ for (px = top ; px < n ; px++) { j = xi [px] ; /* x(j) is nonzero */ J = pinv ? (pinv [j]) : j ; /* j maps to col J of G */ if (J < 0) continue ; /* column J is empty */ x [j] /= Gx [lo ? (Gp [J]) : (Gp [J+1]-1)] ;/* x(j) /= G(j,j) */ p = lo ? (Gp [J]+1) : (Gp [J]) ; /* lo: L(j,j) 1st entry */ q = lo ? (Gp [J+1]) : (Gp [J+1]-1) ; /* up: U(j,j) last entry */ for ( ; p < q ; p++) { x [Gi [p]] -= Gx [p] * x [j] ; /* x(i) -= G(i,j) * x(j) */ } } return (top) ; /* return top of stack */ } igraph/src/dsaupd.f0000644000176000001440000006463512325527073014016 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsaupd c c\Description: c c Reverse communication interface for the Implicitly Restarted Arnoldi c Iteration. For symmetric problems this reduces to a variant of the Lanczos c method. This method has been designed to compute approximations to a c few eigenpairs of a linear operator OP that is real and symmetric c with respect to a real positive semi-definite symmetric matrix B, c i.e. c c B*OP = (OP')*B. c c Another way to express this condition is c c < x,OPy > = < OPx,y > where < z,w > = z'Bw . c c In the standard eigenproblem B is the identity matrix. c ( A' denotes transpose of A) c c The computed approximate eigenvalues are called Ritz values and c the corresponding approximate eigenvectors are called Ritz vectors. c c igraphdsaupd is usually called iteratively to solve one of the c following problems: c c Mode 1: A*x = lambda*x, A symmetric c ===> OP = A and B = I. c c Mode 2: A*x = lambda*M*x, A symmetric, M symmetric positive definite c ===> OP = inv[M]*A and B = M. c ===> (If M can be factored see remark 3 below) c c Mode 3: K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite c ===> OP = (inv[K - sigma*M])*M and B = M. c ===> Shift-and-Invert mode c c Mode 4: K*x = lambda*KG*x, K symmetric positive semi-definite, c KG symmetric indefinite c ===> OP = (inv[K - sigma*KG])*K and B = K. c ===> Buckling mode c c Mode 5: A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite c ===> OP = inv[A - sigma*M]*[A + sigma*M] and B = M. c ===> Cayley transformed mode c c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v c should be accomplished either by a direct method c using a sparse matrix factorization and solving c c [A - sigma*M]*w = v or M*w = v, c c or through an iterative method for solving these c systems. If an iterative method is used, the c convergence test must be more stringent than c the accuracy requirements for the eigenvalue c approximations. c c\Usage: c call igraphdsaupd c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, c IPNTR, WORKD, WORKL, LWORKL, INFO ) c c\Arguments c IDO Integer. (INPUT/OUTPUT) c Reverse communication flag. IDO must be zero on the first c call to igraphdsaupd. IDO will be set internally to c indicate the type of operation to be performed. Control is c then given back to the calling routine which has the c responsibility to carry out the requested operation and call c igraphdsaupd with the result. The operand is given in c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). c (If Mode = 2 see remark 5 below) c ------------------------------------------------------------- c IDO = 0: first call to the reverse communication interface c IDO = -1: compute Y = OP * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c This is for the initialization phase to force the c starting vector into the range of OP. c IDO = 1: compute Y = OP * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c In mode 3,4 and 5, the vector B * X is already c available in WORKD(ipntr(3)). It does not c need to be recomputed in forming OP * X. c IDO = 2: compute Y = B * X where c IPNTR(1) is the pointer into WORKD for X, c IPNTR(2) is the pointer into WORKD for Y. c IDO = 3: compute the IPARAM(8) shifts where c IPNTR(11) is the pointer into WORKL for c placing the shifts. See remark 6 below. c IDO = 99: done c ------------------------------------------------------------- c c BMAT Character*1. (INPUT) c BMAT specifies the type of the matrix B that defines the c semi-inner product for the operator OP. c B = 'I' -> standard eigenvalue problem A*x = lambda*x c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x c c N Integer. (INPUT) c Dimension of the eigenproblem. c c WHICH Character*2. (INPUT) c Specify which of the Ritz values of OP to compute. c c 'LA' - compute the NEV largest (algebraic) eigenvalues. c 'SA' - compute the NEV smallest (algebraic) eigenvalues. c 'LM' - compute the NEV largest (in magnitude) eigenvalues. c 'SM' - compute the NEV smallest (in magnitude) eigenvalues. c 'BE' - compute NEV eigenvalues, half from each end of the c spectrum. When NEV is odd, compute one more from the c high end than from the low end. c (see remark 1 below) c c NEV Integer. (INPUT) c Number of eigenvalues of OP to be computed. 0 < NEV < N. c c TOL Double precision scalar. (INPUT) c Stopping criterion: the relative accuracy of the Ritz value c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)). c If TOL .LE. 0. is passed a default is set: c DEFAULT = DLAMCH('EPS') (machine precision as computed c by the LAPACK auxiliary subroutine DLAMCH). c c RESID Double precision array of length N. (INPUT/OUTPUT) c On INPUT: c If INFO .EQ. 0, a random initial residual vector is used. c If INFO .NE. 0, RESID contains the initial residual vector, c possibly from a previous run. c On OUTPUT: c RESID contains the final residual vector. c c NCV Integer. (INPUT) c Number of columns of the matrix V (less than or equal to N). c This will indicate how many Lanczos vectors are generated c at each iteration. After the startup phase in which NEV c Lanczos vectors are generated, the algorithm generates c NCV-NEV Lanczos vectors at each subsequent update iteration. c Most of the cost in generating each Lanczos vector is in the c matrix-vector product OP*x. (See remark 4 below). c c V Double precision N by NCV array. (OUTPUT) c The NCV columns of V contain the Lanczos basis vectors. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c IPARAM Integer array of length 11. (INPUT/OUTPUT) c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. c The shifts selected at each iteration are used to restart c the Arnoldi iteration in an implicit fashion. c ------------------------------------------------------------- c ISHIFT = 0: the shifts are provided by the user via c reverse communication. The NCV eigenvalues of c the current tridiagonal matrix T are returned in c the part of WORKL array corresponding to RITZ. c See remark 6 below. c ISHIFT = 1: exact shifts with respect to the reduced c tridiagonal matrix T. This is equivalent to c restarting the iteration with a starting vector c that is a linear combination of Ritz vectors c associated with the "wanted" Ritz values. c ------------------------------------------------------------- c c IPARAM(2) = LEVEC c No longer referenced. See remark 2 below. c c IPARAM(3) = MXITER c On INPUT: maximum number of Arnoldi update iterations allowed. c On OUTPUT: actual number of Arnoldi update iterations taken. c c IPARAM(4) = NB: blocksize to be used in the recurrence. c The code currently works only for NB = 1. c c IPARAM(5) = NCONV: number of "converged" Ritz values. c This represents the number of Ritz values that satisfy c the convergence criterion. c c IPARAM(6) = IUPD c No longer referenced. Implicit restarting is ALWAYS used. c c IPARAM(7) = MODE c On INPUT determines what type of eigenproblem is being solved. c Must be 1,2,3,4,5; See under \Description of igraphdsaupd for the c five modes available. c c IPARAM(8) = NP c When ido = 3 and the user provides shifts through reverse c communication (IPARAM(1)=0), igraphdsaupd returns NP, the number c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark c 6 below. c c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, c OUTPUT: NUMOP = total number of OP*x operations, c NUMOPB = total number of B*x operations if BMAT='G', c NUMREO = total number of steps of re-orthogonalization. c c IPNTR Integer array of length 11. (OUTPUT) c Pointer to mark the starting locations in the WORKD and WORKL c arrays for matrices/vectors used by the Lanczos iteration. c ------------------------------------------------------------- c IPNTR(1): pointer to the current operand vector X in WORKD. c IPNTR(2): pointer to the current result vector Y in WORKD. c IPNTR(3): pointer to the vector B * X in WORKD when used in c the shift-and-invert mode. c IPNTR(4): pointer to the next available location in WORKL c that is untouched by the program. c IPNTR(5): pointer to the NCV by 2 tridiagonal matrix T in WORKL. c IPNTR(6): pointer to the NCV RITZ values array in WORKL. c IPNTR(7): pointer to the Ritz estimates in array WORKL associated c with the Ritz values located in RITZ in WORKL. c IPNTR(11): pointer to the NP shifts in WORKL. See Remark 6 below. c c Note: IPNTR(8:10) is only referenced by igraphdseupd. See Remark 2. c IPNTR(8): pointer to the NCV RITZ values of the original system. c IPNTR(9): pointer to the NCV corresponding error bounds. c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors c of the tridiagonal matrix T. Only referenced by c igraphdseupd if RVEC = .TRUE. See Remarks. c ------------------------------------------------------------- c c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) c Distributed array to be used in the basic Arnoldi iteration c for reverse communication. The user should not use WORKD c as temporary workspace during the iteration. Upon termination c WORKD(1:N) contains B*RESID(1:N). If the Ritz vectors are desired c subroutine igraphdseupd uses this output. c See Data Distribution Note below. c c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) c Private (replicated) array on each PE or array allocated on c the front end. See Data Distribution Note below. c c LWORKL Integer. (INPUT) c LWORKL must be at least NCV**2 + 8*NCV . c c INFO Integer. (INPUT/OUTPUT) c If INFO .EQ. 0, a randomly initial residual vector is used. c If INFO .NE. 0, RESID contains the initial residual vector, c possibly from a previous run. c Error flag on output. c = 0: Normal exit. c = 1: Maximum number of iterations taken. c All possible eigenvalues of OP has been found. IPARAM(5) c returns the number of wanted converged Ritz values. c = 2: No longer an informational error. Deprecated starting c with release 2 of ARPACK. c = 3: No shifts could be applied during a cycle of the c Implicitly restarted Arnoldi iteration. One possibility c is to increase the size of NCV relative to NEV. c See remark 4 below. c = -1: N must be positive. c = -2: NEV must be positive. c = -3: NCV must be greater than NEV and less than or equal to N. c = -4: The maximum number of Arnoldi update iterations allowed c must be greater than zero. c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. c = -6: BMAT must be one of 'I' or 'G'. c = -7: Length of private work array WORKL is not sufficient. c = -8: Error return from trid. eigenvalue calculation; c Informatinal error from LAPACK routine dsteqr. c = -9: Starting vector is zero. c = -10: IPARAM(7) must be 1,2,3,4,5. c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. c = -12: IPARAM(1) must be equal to 0 or 1. c = -13: NEV and WHICH = 'BE' are incompatable. c = -9999: Could not build an Arnoldi factorization. c IPARAM(5) returns the size of the current Arnoldi c factorization. The user is advised to check that c enough workspace and array storage has been allocated. c c c\Remarks c 1. The converged Ritz values are always returned in ascending c algebraic order. The computed Ritz values are approximate c eigenvalues of OP. The selection of WHICH should be made c with this in mind when Mode = 3,4,5. After convergence, c approximate eigenvalues of the original problem may be obtained c with the ARPACK subroutine igraphdseupd. c c 2. If the Ritz vectors corresponding to the converged Ritz values c are needed, the user must call igraphdseupd immediately following completion c of igraphdsaupd. This is new starting with version 2.1 of ARPACK. c c 3. If M can be factored into a Cholesky factorization M = LL' c then Mode = 2 should not be selected. Instead one should use c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular c linear systems should be solved with L and L' rather c than computing inverses. After convergence, an approximate c eigenvector z of the original problem is recovered by solving c L'z = x where x is a Ritz vector of OP. c c 4. At present there is no a-priori analysis to guide the selection c of NCV relative to NEV. The only formal requrement is that NCV > NEV. c However, it is recommended that NCV .ge. 2*NEV. If many problems of c the same type are to be solved, one should experiment with increasing c NCV while keeping NEV fixed for a given test problem. This will c usually decrease the required number of OP*x operations but it c also increases the work and storage required to maintain the orthogonal c basis vectors. The optimal "cross-over" with respect to CPU time c is problem dependent and must be determined empirically. c c 5. If IPARAM(7) = 2 then in the Reverse commuication interface the user c must do the following. When IDO = 1, Y = OP * X is to be computed. c When IPARAM(7) = 2 OP = inv(B)*A. After computing A*X the user c must overwrite X with A*X. Y is then the solution to the linear set c of equations B*Y = A*X. c c 6. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the c NP = IPARAM(8) shifts in locations: c 1 WORKL(IPNTR(11)) c 2 WORKL(IPNTR(11)+1) c . c . c . c NP WORKL(IPNTR(11)+NP-1). c c The eigenvalues of the current tridiagonal matrix are located in c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1). They are in the c order defined by WHICH. The associated Ritz estimates are located in c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). c c----------------------------------------------------------------------- c c\Data Distribution Note: c c Fortran-D syntax: c ================ c REAL RESID(N), V(LDV,NCV), WORKD(3*N), WORKL(LWORKL) c DECOMPOSE D1(N), D2(N,NCV) c ALIGN RESID(I) with D1(I) c ALIGN V(I,J) with D2(I,J) c ALIGN WORKD(I) with D1(I) range (1:N) c ALIGN WORKD(I) with D1(I-N) range (N+1:2*N) c ALIGN WORKD(I) with D1(I-2*N) range (2*N+1:3*N) c DISTRIBUTE D1(BLOCK), D2(BLOCK,:) c REPLICATED WORKL(LWORKL) c c Cray MPP syntax: c =============== c REAL RESID(N), V(LDV,NCV), WORKD(N,3), WORKL(LWORKL) c SHARED RESID(BLOCK), V(BLOCK,:), WORKD(BLOCK,:) c REPLICATED WORKL(LWORKL) c c c\BeginLib c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, c 1980. c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", c Computer Physics Communications, 53 (1989), pp 169-179. c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to c Implement the Spectral Transformation", Math. Comp., 48 (1987), c pp 663-673. c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", c SIAM J. Matr. Anal. Apps., January (1993). c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines c for Updating the QR decomposition", ACM TOMS, December 1990, c Volume 16 Number 4, pp 369-377. c 8. R.B. Lehoucq, D.C. Sorensen, "Implementation of Some Spectral c Transformations in a k-Step Arnoldi Method". In Preparation. c c\Routines called: c igraphdsaup2 ARPACK routine that implements the Implicitly Restarted c Arnoldi Iteration. c igraphdstats ARPACK routine that initialize timing and other statistics c variables. c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdvout ARPACK utility routine that prints vectors. c dlamch LAPACK routine that determines machine constants. c c\Authors c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c 12/15/93: Version ' 2.4' c c\SCCS Information: @(#) c FILE: saupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 c c\Remarks c 1. None c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsaupd & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, & ipntr, workd, workl, lworkl, info ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat*1, which*2 integer ido, info, ldv, lworkl, n, ncv, nev Double precision & tol c c %-----------------% c | Array Arguments | c %-----------------% c integer iparam(11), ipntr(11) Double precision & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c integer bounds, ierr, ih, iq, ishift, iupd, iw, & ldh, ldq, msglvl, mxiter, mode, nb, & nev0, next, np, ritz, j save bounds, ierr, ih, iq, ishift, iupd, iw, & ldh, ldq, msglvl, mxiter, mode, nb, & nev0, next, np, ritz c c %----------------------% c | External Subroutines | c %----------------------% c external igraphdsaup2, igraphdvout, igraphivout, & igraphsecond, igraphdstats c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlamch external dlamch c c %-----------------------% c | Executable Statements | c %-----------------------% c if (ido .eq. 0) then c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphdstats call igraphsecond (t0) msglvl = msaupd c ierr = 0 ishift = iparam(1) mxiter = iparam(3) nb = iparam(4) c c %--------------------------------------------% c | Revision 2 performs only implicit restart. | c %--------------------------------------------% c iupd = 1 mode = iparam(7) c c %----------------% c | Error checking | c %----------------% c if (n .le. 0) then ierr = -1 else if (nev .le. 0) then ierr = -2 else if (ncv .le. nev .or. ncv .gt. n) then ierr = -3 end if c c %----------------------------------------------% c | NP is the number of additional steps to | c | extend the length NEV Lanczos factorization. | c %----------------------------------------------% c np = ncv - nev c if (mxiter .le. 0) ierr = -4 if (which .ne. 'LM' .and. & which .ne. 'SM' .and. & which .ne. 'LA' .and. & which .ne. 'SA' .and. & which .ne. 'BE') ierr = -5 if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 c if (lworkl .lt. ncv**2 + 8*ncv) ierr = -7 if (mode .lt. 1 .or. mode .gt. 5) then ierr = -10 else if (mode .eq. 1 .and. bmat .eq. 'G') then ierr = -11 else if (ishift .lt. 0 .or. ishift .gt. 1) then ierr = -12 else if (nev .eq. 1 .and. which .eq. 'BE') then ierr = -13 end if c c %------------% c | Error Exit | c %------------% c if (ierr .ne. 0) then info = ierr ido = 99 go to 9000 end if c c %------------------------% c | Set default parameters | c %------------------------% c if (nb .le. 0) nb = 1 if (tol .le. zero) tol = dlamch('EpsMach') c c %----------------------------------------------% c | NP is the number of additional steps to | c | extend the length NEV Lanczos factorization. | c | NEV0 is the local variable designating the | c | size of the invariant subspace desired. | c %----------------------------------------------% c np = ncv - nev nev0 = nev c c %-----------------------------% c | Zero out internal workspace | c %-----------------------------% c do 10 j = 1, ncv**2 + 8*ncv workl(j) = zero 10 continue c c %-------------------------------------------------------% c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | c | etc... and the remaining workspace. | c | Also update pointer to be used on output. | c | Memory is laid out as follows: | c | workl(1:2*ncv) := generated tridiagonal matrix | c | workl(2*ncv+1:2*ncv+ncv) := ritz values | c | workl(3*ncv+1:3*ncv+ncv) := computed error bounds | c | workl(4*ncv+1:4*ncv+ncv*ncv) := rotation matrix Q | c | workl(4*ncv+ncv*ncv+1:7*ncv+ncv*ncv) := workspace | c %-------------------------------------------------------% c ldh = ncv ldq = ncv ih = 1 ritz = ih + 2*ldh bounds = ritz + ncv iq = bounds + ncv iw = iq + ncv**2 next = iw + 3*ncv c ipntr(4) = next ipntr(5) = ih ipntr(6) = ritz ipntr(7) = bounds ipntr(11) = iw end if c c %-------------------------------------------------------% c | Carry out the Implicitly restarted Lanczos Iteration. | c %-------------------------------------------------------% c call igraphdsaup2 & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritz), & workl(bounds), workl(iq), ldq, workl(iw), ipntr, workd, & info ) c c %--------------------------------------------------% c | ido .ne. 99 implies use of reverse communication | c | to compute operations involving OP or shifts. | c %--------------------------------------------------% c if (ido .eq. 3) iparam(8) = np if (ido .ne. 99) go to 9000 c iparam(3) = mxiter iparam(5) = np iparam(9) = nopx iparam(10) = nbx iparam(11) = nrorth c c %------------------------------------% c | Exit if there was an informational | c | error within igraphdsaup2. | c %------------------------------------% c if (info .lt. 0) go to 9000 if (info .eq. 2) info = 3 c if (msglvl .gt. 0) then call igraphivout (logfil, 1, mxiter, ndigit, & '_saupd: number of update iterations taken') call igraphivout (logfil, 1, np, ndigit, & '_saupd: number of "converged" Ritz values') call igraphdvout (logfil, np, workl(Ritz), ndigit, & '_saupd: final Ritz values') call igraphdvout (logfil, np, workl(Bounds), ndigit, & '_saupd: corresponding error bounds') end if c call igraphsecond (t1) tsaupd = t1 - t0 c c 9000 continue c return c c %---------------% c | End of igraphdsaupd | c %---------------% c end igraph/src/bliss.cc0000644000176000001440000002270412325527072014000 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include "bliss_timer.hh" #include "bliss_graph.hh" #include "bliss_kqueue.hh" #include "bliss_utils.hh" #include "igraph_types.h" #include "igraph_topology.h" #include using namespace igraph; using namespace std; /** * \function igraph_canonical_permutation * Canonical permutation using BLISS * * This function computes the canonical permutation which transforms * the graph into a canonical form by using the BLISS algorithm. * * \param graph The input graph, it is treated as undirected and the * multiple edges are ignored. * \param labeling Pointer to a vector, the result is stored here. The * permutation takes vertex 0 to the first element of the vector, * vertex 1 to the second, etc. The vector will be resized as * needed. * \param sh The split heuristics to be used in BLISS. See \ref * igraph_bliss_sh_t. * \param info If not \c NULL then information on BLISS internals is * stored here. See \ref igraph_bliss_info_t. * \return Error code. * * Time complexity: exponential, in practice it is fast for many graphs. */ int igraph_canonical_permutation(const igraph_t *graph, igraph_vector_t *labeling, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { Graph *g = Graph::from_igraph(graph); Stats stats; const unsigned int N=g->get_nof_vertices(); unsigned int gsh=Graph::sh_flm; switch (sh) { case IGRAPH_BLISS_F: gsh= Graph::sh_f; break; case IGRAPH_BLISS_FL: gsh= Graph::sh_fl; break; case IGRAPH_BLISS_FS: gsh= Graph::sh_fs; break; case IGRAPH_BLISS_FM: gsh= Graph::sh_fm; break; case IGRAPH_BLISS_FLM: gsh= Graph::sh_flm; break; case IGRAPH_BLISS_FSM: gsh= Graph::sh_fsm; break; } g->set_splitting_heuristics(gsh); const unsigned int *cl = g->canonical_form(stats); IGRAPH_CHECK(igraph_vector_resize(labeling, N)); for (unsigned int i=0; inof_nodes = stats.nof_nodes; info->nof_leaf_nodes = stats.nof_leaf_nodes; info->nof_bad_nodes = stats.nof_bad_nodes; info->nof_canupdates = stats.nof_canupdates; info->max_level = stats.max_level; stats.group_size.tostring(&info->group_size); } return 0; } /** * \function igraph_automorphisms * Number of automorphisms using BLISS * * The number of automorphisms of a graph is computed using BLISS. The * result is returned as part of the \p info structure, in tag \c * group_size. It is returned as a string, as it can be very high even * for relatively small graphs. If the GNU MP library is used then * this number is exact, otherwise a long double is used * and it is only approximate. See also \ref igraph_bliss_info_t. * * \param graph The input graph, it is treated as undirected and the * multiple edges are ignored. * \param sh The split heuristics to be used in BLISS. See \ref * igraph_bliss_sh_t. * \param info The result is stored here, in particular in the \c * group_size tag of \p info. * \return Error code. * * Time complexity: exponential, in practice it is fast for many graphs. */ int igraph_automorphisms(const igraph_t *graph, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { Graph *g = Graph::from_igraph(graph); Stats stats; unsigned int gsh=Graph::sh_flm; switch (sh) { case IGRAPH_BLISS_F: gsh= Graph::sh_f; break; case IGRAPH_BLISS_FL: gsh= Graph::sh_fl; break; case IGRAPH_BLISS_FS: gsh= Graph::sh_fs; break; case IGRAPH_BLISS_FM: gsh= Graph::sh_fm; break; case IGRAPH_BLISS_FLM: gsh= Graph::sh_flm; break; case IGRAPH_BLISS_FSM: gsh= Graph::sh_fsm; break; } g->set_splitting_heuristics(gsh); g->find_automorphisms(stats); if (info) { info->nof_nodes = stats.nof_nodes; info->nof_leaf_nodes = stats.nof_leaf_nodes; info->nof_bad_nodes = stats.nof_bad_nodes; info->nof_canupdates = stats.nof_canupdates; info->max_level = stats.max_level; stats.group_size.tostring(&info->group_size); } delete g; return 0; } bool bliss_verbose = false; // FILE *bliss_verbstr = stdout; namespace igraph { typedef enum {FORMAT_BIN = 0, FORMAT_ADJ} Format; // static Format input_format; // static char *infilename = 0; // static bool opt_canonize = false; // static char *opt_output_can_file = 0; // static unsigned int sh = Graph::sh_fm; // static void usage(FILE *fp, char *argv0) // { // char *program_name; // program_name = strrchr(argv0, '/'); // if(program_name) program_name++; // else program_name = argv0; // if(!*program_name) program_name = "bliss"; // fprintf(fp, "bliss, version 0.35, compiled " __DATE__ "\n"); // fprintf(fp, "Copyright 2003-2006 Tommi Junttila\n"); // fprintf(fp, // "%s []\n" // "\n" // " -can compute canonical form\n" // " -ocan=f compute canonical form and output it in file f\n" // //" -v switch verbose mode on\n" // " -sh=x select splitting heuristics, where x is\n" // " f first non-singleton cell\n" // " fl first largest non-singleton cell\n" // " fs first smallest non-singleton cell\n" // " fm first maximally non-trivially connected non-singleton cell [default]\n" // " flm first largest maximally non-trivially connected non-singleton cell\n" // " fsm first smallest maximally non-trivially connected non-singleton cell\n" // ,program_name); // } // static void parse_options(int argc, char ** argv) // { // for(int i = 1; i < argc; i++) // { // //if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "-verbose") == 0) // //bliss_verbose = true; // /* // if(strcmp(argv[i], "-bin") == 0) // input_format = FORMAT_BIN; // else if(strcmp(argv[i], "-adj") == 0) // input_format = FORMAT_ADJ; // */ // if(strcmp(argv[i], "-can") == 0) // opt_canonize = true; // else if((strncmp(argv[i], "-ocan=", 6) == 0) && (strlen(argv[i]) > 6)) // { // opt_canonize = true; // opt_output_can_file = argv[i]+6; // } // else if(strcmp(argv[i], "-sh=f") == 0) // sh = Graph::sh_f; // else if(strcmp(argv[i], "-sh=fs") == 0) // sh = Graph::sh_fs; // else if(strcmp(argv[i], "-sh=fl") == 0) // sh = Graph::sh_fl; // else if(strcmp(argv[i], "-sh=fm") == 0) // sh = Graph::sh_fm; // else if(strcmp(argv[i], "-sh=fsm") == 0) // sh = Graph::sh_fsm; // else if(strcmp(argv[i], "-sh=flm") == 0) // sh = Graph::sh_flm; // else if(argv[i][0] == '-') { // fprintf(stderr, "unknown command line argument `%s'\n", argv[i]); // usage(stderr, argv[0]); // exit(1); // } // else { // if(infilename) { // fprintf(stderr, "too many file arguments\n"); // usage(stderr, argv[0]); // exit(1); // } // else { // infilename = argv[i]; // } // } // } // } } // using namespace igraph; // int main(int argc, char **argv) // { // Timer t; // t.start(); // parse_options(argc, argv); // Graph *g = 0; // FILE *infile = stdin; // if(infilename) { // if(input_format == FORMAT_BIN) // infile = fopen(infilename, "rb"); // else // infile = fopen(infilename, "r"); // if(!infile) { // fprintf(stderr, "cannot not open `%s' for input\n", infilename); // exit(1); } // } // g = Graph::read_dimacs(infile); // if(infile != stdin) // fclose(infile); // if(!g) // return 0; // fprintf(stdout, "Graph read in %.2fs\n", t.get_intermediate()); // #ifdef DEBUG_PRINT_DOT // g->to_dot("debug_graph.dot"); // #endif // Stats stats; // g->set_splitting_heuristics(sh); // if(opt_canonize) // { // const unsigned int *cl = g->canonical_form(stats); // //fprintf(stdout, "Canonical labeling: "); // //print_permutation(stdout, g->get_nof_vertices(), cl); // //fprintf(stdout, "\n"); // if(opt_output_can_file) // { // Graph *cf = g->permute(cl); // FILE *fp = fopen(opt_output_can_file, "w"); // if(!fp) // { // fprintf(stderr, "Can not open '%s' for outputting the canonical form", opt_output_can_file); // exit(1); // } // cf->print_dimacs(fp); // fclose(fp); // delete cf; // } // } // else // { // g->find_automorphisms(stats); // } // printf("Nodes:\t\t%lu\n", stats.nof_nodes); // printf("Leaf nodes:\t%lu\n", stats.nof_leaf_nodes); // printf("Bad nodes:\t%lu\n", stats.nof_bad_nodes); // printf("Canrep updates:\t%lu\n", stats.nof_canupdates); // printf("Generators:\t%lu\n", stats.nof_generators); // printf("Max level:\t%lu\n", stats.max_level); // printf("|Aut|:\t\t"); stats.group_size.print(stdout); printf("\n"); // t.stop(); // printf("Total time:\t%.2fs\n", t.get_duration()); // delete g; // return 0; // } igraph/src/qsort_r.c0000644000176000001440000000033112325527074014204 0ustar ripleyusers/* * This file is in the public domain. Originally written by Garrett * A. Wollman. * * $FreeBSD: src/lib/libc/stdlib/qsort_r.c,v 1.1 2002/09/10 02:04:49 wollman Exp $ */ #define I_AM_QSORT_R #include "qsort.c" igraph/src/revolver_grow.c0000644000176000001440000011650012325527074015423 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_revolver.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_structural.h" #include "config.h" #include /* This file contains tools for non-citation evolving networks */ /* Citation networks are in evolver.c */ /***********************************************/ /* degree + degree */ /***********************************************/ int igraph_revolver_d_d(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *vtime, const igraph_vector_t *etime, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres) { igraph_integer_t no_of_events, vnoev, enoev; igraph_vector_t st; long int i; igraph_integer_t maxdegree; igraph_vector_t vtimeidx, etimeidx; igraph_lazy_inclist_t inclist; if (igraph_vector_size(vtime) != igraph_vcount(graph)) { IGRAPH_ERROR("Invalid vtime length", IGRAPH_EINVAL); } if (igraph_vector_size(etime) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid etime length", IGRAPH_EINVAL); } vnoev=(igraph_integer_t) igraph_vector_max(vtime)+1; enoev=(igraph_integer_t) igraph_vector_max(etime)+1; no_of_events= vnoev > enoev ? vnoev : enoev; IGRAPH_VECTOR_INIT_FINALLY(&st, no_of_events); for (i=0; i= 2) { MATRIX(ch, 0, 0) = eptr; } for (i=1; i 0 && after==0) { MATRIX(*normfact, xidx, i) += eptr_new-MATRIX(ch, xidx, i); MATRIX(*normfact, i, xidx) = MATRIX(*normfact, xidx, i); } } VECTOR(ntk)[xidx]--; for (i=0; i 0 && after==0) { MATRIX(*normfact, yidx, i) += eptr_new-MATRIX(ch, yidx, i); MATRIX(*normfact, i, yidx) = MATRIX(*normfact, yidx, i); } } VECTOR(ntk)[yidx]--; for (i=0; i 0) { MATRIX(ch, xidx+1, i) = eptr_new; MATRIX(ch, i, xidx+1) = MATRIX(ch, xidx+1, i); } } VECTOR(ntk)[xidx+1]++; for (i=0; i maxpapers) { maxpapers=(igraph_integer_t) VECTOR(papers)[author]; } } igraph_vector_long_destroy(&papers); IGRAPH_FINALLY_CLEAN(1); IGRAPH_VECTOR_INIT_FINALLY(&vtimeidx, 0); IGRAPH_VECTOR_INIT_FINALLY(&etimeidx, 0); IGRAPH_CHECK(igraph_vector_order1(vtime, &vtimeidx, no_of_events)); IGRAPH_CHECK(igraph_vector_order1(etime, &etimeidx, no_of_events)); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_PROGRESS("Revolver p-p", 0, NULL); for (i=0; i= 2) { MATRIX(ch, 0, 0) = eptr; } for (i=1; i 0 && after==0) { MATRIX(*normfact, pap, j) += eptr_new-MATRIX(ch, pap, j); MATRIX(*normfact, j, pap) = MATRIX(*normfact, pap, j); } } VECTOR(ntk)[pap]-=1; for (j=0; j 0) { MATRIX(ch, pap+1, j) = eptr_new; MATRIX(ch, j, pap+1) = MATRIX(ch, pap+1, j); } } VECTOR(ntk)[pap+1]+=1; VECTOR(papers)[aut] += 1; } aptr += VECTOR(*eventsizes)[timestep]; /* For every new edge, we lose one connection possibility, also add the edges*/ eptr=eptr_save; while (eptr < no_of_edges && VECTOR(*etime)[(long int) VECTOR(*etimeidx)[eptr] ] == timestep) { long int edge=(long int) VECTOR(*etimeidx)[eptr]; long int from=IGRAPH_FROM(graph, edge), to=IGRAPH_TO(graph, edge); long int xidx=VECTOR(papers)[from]; long int yidx=VECTOR(papers)[to]; MATRIX(ntkk, xidx, yidx) += 1; MATRIX(ntkk, yidx, xidx) = MATRIX(ntkk, xidx, yidx); if (NTKK(xidx, yidx)==0) { MATRIX(*normfact, xidx, yidx) += eptr_new-MATRIX(ch, xidx, yidx); MATRIX(*normfact, yidx, xidx) = MATRIX(*normfact, xidx, yidx); } VECTOR(added)[edge]=1; eptr++; } } for (i=0; i #include #include #include #include using namespace std; #include "drl_layout.h" #include "drl_parse.h" namespace drl { // void parse::print_syntax( const char *error_string ) // { // cout << endl << "Error: " << error_string << endl; // cout << endl << "Layout" << endl // << "------" << endl // << "S. Martin" << endl // << "Version " << DRL_VERSION << endl << endl // << "This program provides a parallel adaptation of a force directed" << endl // << "graph layout algorithm for use with large datasets." << endl << endl // << "Usage: layout [options] root_file" << endl << endl // << "root_file -- the root name of the file being processed." << endl << endl // << "INPUT" << endl // << "-----" << endl // << "root_file.int -- the input file containing the graph to draw using layout." << endl // << " The .int file must have the suffix \".int\" and each line of .int file" << endl // << " should have the form" << endl // << "\tnode_id node_id weight" << endl // << " where node_id's are integers in sequence starting from 0, and" << endl // << " weight is a float > 0." << endl << endl // << "OUTPUT" << endl // << "------" << endl // << "root_file.icoord -- the resulting output file, containing an ordination" << endl // << " of the graph. The .icoord file will have the suffix \".icoord\" and" << endl // << " each line of the .icoord file will be of the form" << endl // << "\tnode_id x-coord y-coord" << endl << endl // << "Options:" << endl << endl // << "\t-s {int>=0} random seed (default value is 0)" << endl // << "\t-c {real[0,1]} edge cutting (default 32/40 = .8)" << endl // << "\t (old max was 39/40 = .975)" << endl // << "\t-p input parameters from .parms file" << endl // << "\t-r {real[0,1]} input coordinates from .real file" << endl // << "\t (hold fixed until fraction of optimization schedule reached)" << endl // << "\t-i {int>=0} intermediate output interval (default 0: no output)" << endl // << "\t-e output .iedges file (same prefix as .coord file)" << endl << endl; // #ifdef MUSE_MPI // MPI_Abort ( MPI_COMM_WORLD, 1 ); // #else // exit (1); // #endif // } // parse::parse ( int argc, char** argv) // { // map m; // // make sure there is at least one argument // if ( argc < 2) // print_syntax ( "not enough arguments!" ); // // make sure coord_file ends in ".coord" // parms_file = real_file = sim_file = coord_file = argv[argc-1]; // parms_file = parms_file + ".parms"; // real_file = real_file + ".real"; // sim_file = sim_file + ".int"; // coord_file = coord_file + ".icoord"; // char error_string[200]; // sprintf ( error_string, "%s %d %s", "root file name cannot be longer than", MAX_FILE_NAME-7, // "characters."); // if ( coord_file.length() > MAX_FILE_NAME ) // print_syntax ( error_string ); // // echo sim_file and coord_file // cout << "Using " << sim_file << " for .int file, and " << coord_file << " for .icoord file." << endl; // // set defaults // rand_seed = 0; // //edge_cut = 32.0/39.0; // (old default) // edge_cut = 32.0/40.0; // int_out = 0; // edges_out = 0; // parms_in = 0; // real_in = -1.0; // // now check for optional arguments // string arg; // for( int i = 1; i= (argc-1) ) // print_syntax ( "-s flag has no argument." ); // else // { // rand_seed = atoi ( argv[i] ); // if ( rand_seed < 0 ) // print_syntax ( "random seed must be >= 0." ); // } // } // // check for edge cutting // else if ( arg == "-c" ) // { // i++; // if ( i >= (argc-1) ) // print_syntax ( "-c flag has no argument." ); // else // { // edge_cut = atof ( argv[i] ); // if ( (edge_cut < 0) || (edge_cut > 1) ) // print_syntax ( "edge cut must be between 0 and 1." ); // } // } // // check for intermediate output // else if ( arg == "-i" ) // { // i++; // if ( i >= (argc-1) ) // print_syntax ( "-i flag has no argument." ); // else // { // int_out = atoi ( argv[i] ); // if ( int_out < 0 ) // print_syntax ( "intermediate output must be >= 0." ); // } // } // // check for .real input // else if ( arg == "-r" ) // { // i++; // if ( i >= (argc-1) ) // print_syntax ( "-r flag has no argument." ); // else // { // real_in = atof ( argv[i] ); // if ( (real_in < 0) || (real_in > 1) ) // print_syntax ( "real iteration fraction must be from 0 to 1." ); // } // } // else if ( arg == "-e" ) // edges_out = 1; // else if ( arg == "-p" ) // parms_in = 1; // else // print_syntax ( "unrecongized option!" ); // } // if ( parms_in ) // cout << "Using " << parms_file << " for .parms file." << endl; // if ( real_in >= 0 ) // cout << "Using " << real_file << " for .real file." << endl; // // echo arguments input or default // cout << "Using random seed = " << rand_seed << endl // << " edge_cutting = " << edge_cut << endl // << " intermediate output = " << int_out << endl // << " output .iedges file = " << edges_out << endl; // if ( real_in >= 0 ) // cout << " holding .real fixed until iterations = " << real_in << endl; // } } // namespace drl igraph/src/array.c0000644000176000001440000000261412325527072013635 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_array.h" #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "array.pmt" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "array.pmt" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "array.pmt" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "array.pmt" #include "igraph_pmt_off.h" #undef BASE_BOOL igraph/src/igraph_matching.h0000644000176000001440000000416512325527073015654 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_MATCHING_H #define IGRAPH_MATCHING_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_vector.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Matchings in graphs */ /* -------------------------------------------------- */ int igraph_is_matching(const igraph_t* graph, const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, igraph_bool_t* result); int igraph_is_maximal_matching(const igraph_t* graph, const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, igraph_bool_t* result); int igraph_maximum_bipartite_matching(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_integer_t* matching_size, igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights, igraph_real_t eps); int igraph_maximum_matching(const igraph_t* graph, igraph_integer_t* matching_size, igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights); __END_DECLS #endif igraph/src/glpapi19.c0000644000176000001440000013254112325527073014151 0ustar ripleyusers/* glpapi19.c (stand-alone LP/MIP solver) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpapi.h" #include "glpgmp.h" struct csa { /* common storage area */ glp_prob *prob; /* LP/MIP problem object */ glp_bfcp bfcp; /* basis factorization control parameters */ glp_smcp smcp; /* simplex method control parameters */ glp_iptcp iptcp; /* interior-point method control parameters */ glp_iocp iocp; /* integer optimizer control parameters */ glp_tran *tran; /* model translator workspace */ glp_graph *graph; /* network problem object */ int format; /* problem file format: */ #define FMT_MPS_DECK 1 /* fixed MPS */ #define FMT_MPS_FILE 2 /* free MPS */ #define FMT_LP 3 /* CPLEX LP */ #define FMT_GLP 4 /* GLPK LP/MIP */ #define FMT_MATHPROG 5 /* MathProg */ #define FMT_MIN_COST 6 /* DIMACS min-cost flow */ #define FMT_MAX_FLOW 7 /* DIMACS maximum flow */ const char *in_file; /* name of input problem file */ #define DATA_MAX 10 /* maximal number of input data files */ int ndf; /* number of input data files specified */ const char *in_data[1+DATA_MAX]; /* name(s) of input data file(s) */ const char *out_dpy; /* name of output file to send display output; NULL means the display output is sent to the terminal */ int seed; /* seed value to be passed to the MathProg translator; initially set to 1; 0x80000000 means the value is omitted */ int solution; /* solution type flag: */ #define SOL_BASIC 1 /* basic */ #define SOL_INTERIOR 2 /* interior-point */ #define SOL_INTEGER 3 /* mixed integer */ const char *in_res; /* name of input solution file in raw format */ int dir; /* optimization direction flag: 0 - not specified GLP_MIN - minimization GLP_MAX - maximization */ int scale; /* automatic problem scaling flag */ const char *out_sol; /* name of output solution file in printable format */ const char *out_res; /* name of output solution file in raw format */ const char *out_ranges; /* name of output file to write sensitivity analysis report */ int check; /* input data checking flag; no solution is performed */ const char *new_name; /* new name to be assigned to the problem */ const char *out_mps; /* name of output problem file in fixed MPS format */ const char *out_freemps; /* name of output problem file in free MPS format */ const char *out_cpxlp; /* name of output problem file in CPLEX LP format */ const char *out_glp; /* name of output problem file in GLPK format */ const char *out_pb; /* name of output problem file in OPB format */ const char *out_npb; /* name of output problem file in normalized OPB format */ const char *log_file; /* name of output file to hardcopy terminal output */ int crash; /* initial basis option: */ #define USE_STD_BASIS 1 /* use standard basis */ #define USE_ADV_BASIS 2 /* use advanced basis */ #define USE_CPX_BASIS 3 /* use Bixby's basis */ #define USE_INI_BASIS 4 /* use initial basis from ini_file */ const char *ini_file; /* name of input file containing initial basis */ int exact; /* flag to use glp_exact rather than glp_simplex */ int xcheck; /* flag to check final basis with glp_exact */ int nomip; /* flag to consider MIP as pure LP */ }; static void print_help(const char *my_name) { /* print help information */ xprintf("Usage: %s [options...] filename\n", my_name); xprintf("\n"); xprintf("General options:\n"); xprintf(" --mps read LP/MIP problem in fixed MPS fo" "rmat\n"); xprintf(" --freemps read LP/MIP problem in free MPS for" "mat (default)\n"); xprintf(" --lp read LP/MIP problem in CPLEX LP for" "mat\n"); xprintf(" --glp read LP/MIP problem in GLPK format " "\n"); xprintf(" --math read LP/MIP model written in GNU Ma" "thProg modeling\n"); xprintf(" language\n"); xprintf(" -m filename, --model filename\n"); xprintf(" read model section and optional dat" "a section from\n"); xprintf(" filename (same as --math)\n"); xprintf(" -d filename, --data filename\n"); xprintf(" read data section from filename (fo" "r --math only);\n"); xprintf(" if model file also has data section" ", it is ignored\n"); xprintf(" -y filename, --display filename\n"); xprintf(" send display output to filename (fo" "r --math only);\n"); xprintf(" by default the output is sent to te" "rminal\n"); xprintf(" --seed value initialize pseudo-random number gen" "erator used in\n"); xprintf(" MathProg model with specified seed " "(any integer);\n"); xprintf(" if seed value is ?, some random see" "d will be used\n"); xprintf(" --mincost read min-cost flow problem in DIMAC" "S format\n"); xprintf(" --maxflow read maximum flow problem in DIMACS" " format\n"); xprintf(" --simplex use simplex method (default)\n"); xprintf(" --interior use interior point method (LP only)" "\n"); xprintf(" -r filename, --read filename\n"); xprintf(" read solution from filename rather " "to find it with\n"); xprintf(" the solver\n"); xprintf(" --min minimization\n"); xprintf(" --max maximization\n"); xprintf(" --scale scale problem (default)\n"); xprintf(" --noscale do not scale problem\n"); xprintf(" -o filename, --output filename\n"); xprintf(" write solution to filename in print" "able format\n"); xprintf(" -w filename, --write filename\n"); xprintf(" write solution to filename in plain" " text format\n"); xprintf(" --ranges filename\n"); xprintf(" write sensitivity analysis report t" "o filename in\n"); xprintf(" printable format (simplex only)\n"); xprintf(" --tmlim nnn limit solution time to nnn seconds " "\n"); xprintf(" --memlim nnn limit available memory to nnn megab" "ytes\n"); xprintf(" --check do not solve problem, check input d" "ata only\n"); xprintf(" --name probname change problem name to probname\n"); xprintf(" --wmps filename write problem to filename in fixed " "MPS format\n"); xprintf(" --wfreemps filename\n"); xprintf(" write problem to filename in free M" "PS format\n"); xprintf(" --wlp filename write problem to filename in CPLEX " "LP format\n"); xprintf(" --wglp filename write problem to filename in GLPK f" "ormat\n"); #if 0 xprintf(" --wpb filename write problem to filename in OPB fo" "rmat\n"); xprintf(" --wnpb filename write problem to filename in normal" "ized OPB format\n"); #endif xprintf(" --log filename write copy of terminal output to fi" "lename\n"); xprintf(" -h, --help display this help information and e" "xit\n"); xprintf(" -v, --version display program version and exit\n") ; xprintf("\n"); xprintf("LP basis factorization options:\n"); xprintf(" --luf LU + Forrest-Tomlin update\n"); xprintf(" (faster, less stable; default)\n"); xprintf(" --cbg LU + Schur complement + Bartels-Gol" "ub update\n"); xprintf(" (slower, more stable)\n"); xprintf(" --cgr LU + Schur complement + Givens rota" "tion update\n"); xprintf(" (slower, more stable)\n"); xprintf("\n"); xprintf("Options specific to simplex solver:\n"); xprintf(" --primal use primal simplex (default)\n"); xprintf(" --dual use dual simplex\n"); xprintf(" --std use standard initial basis of all s" "lacks\n"); xprintf(" --adv use advanced initial basis (default" ")\n"); xprintf(" --bib use Bixby's initial basis\n"); xprintf(" --ini filename use as initial basis previously sav" "ed with -w\n"); xprintf(" (disables LP presolver)\n"); xprintf(" --steep use steepest edge technique (defaul" "t)\n"); xprintf(" --nosteep use standard \"textbook\" pricing\n" ); xprintf(" --relax use Harris' two-pass ratio test (de" "fault)\n"); xprintf(" --norelax use standard \"textbook\" ratio tes" "t\n"); xprintf(" --presol use presolver (default; assumes --s" "cale and --adv)\n"); xprintf(" --nopresol do not use presolver\n"); xprintf(" --exact use simplex method based on exact a" "rithmetic\n"); xprintf(" --xcheck check final basis using exact arith" "metic\n"); xprintf("\n"); xprintf("Options specific to interior-point solver:\n"); xprintf(" --nord use natural (original) ordering\n"); xprintf(" --qmd use quotient minimum degree orderin" "g\n"); xprintf(" --amd use approximate minimum degree orde" "ring (default)\n"); xprintf(" --symamd use approximate minimum degree orde" "ring\n"); xprintf("\n"); xprintf("Options specific to MIP solver:\n"); xprintf(" --nomip consider all integer variables as c" "ontinuous\n"); xprintf(" (allows solving MIP as pure LP)\n"); xprintf(" --first branch on first integer variable\n") ; xprintf(" --last branch on last integer variable\n"); xprintf(" --mostf branch on most fractional variable " "\n"); xprintf(" --drtom branch using heuristic by Driebeck " "and Tomlin\n"); xprintf(" (default)\n"); xprintf(" --pcost branch using hybrid pseudocost heur" "istic (may be\n"); xprintf(" useful for hard instances)\n"); xprintf(" --dfs backtrack using depth first search " "\n"); xprintf(" --bfs backtrack using breadth first searc" "h\n"); xprintf(" --bestp backtrack using the best projection" " heuristic\n"); xprintf(" --bestb backtrack using node with best loca" "l bound\n"); xprintf(" (default)\n"); xprintf(" --intopt use MIP presolver (default)\n"); xprintf(" --nointopt do not use MIP presolver\n"); xprintf(" --binarize replace general integer variables b" "y binary ones\n"); xprintf(" (assumes --intopt)\n"); xprintf(" --fpump apply feasibility pump heuristic\n") ; xprintf(" --gomory generate Gomory's mixed integer cut" "s\n"); xprintf(" --mir generate MIR (mixed integer roundin" "g) cuts\n"); xprintf(" --cover generate mixed cover cuts\n"); xprintf(" --clique generate clique cuts\n"); xprintf(" --cuts generate all cuts above\n"); xprintf(" --mipgap tol set relative mip gap tolerance to t" "ol\n"); xprintf("\n"); xprintf("For description of the MPS and CPLEX LP formats see Refe" "rence Manual.\n"); xprintf("For description of the modeling language see \"GLPK: Mod" "eling Language\n"); xprintf("GNU MathProg\". Both documents are included in the GLPK " "distribution.\n"); xprintf("\n"); xprintf("See GLPK web page at .\n"); xprintf("\n"); xprintf("Please report bugs to .\n"); return; } static void print_version(int briefly) { /* print version information */ xprintf("GLPSOL: GLPK LP/MIP Solver, v%s\n", glp_version()); if (briefly) goto done; xprintf("\n"); xprintf("Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, " "2007, 2008,\n"); xprintf("2009, 2010 Andrew Makhorin, Department for Applied Infor" "matics, Moscow\n"); xprintf("Aviation Institute, Moscow, Russia. All rights reserved." "\n"); xprintf("\n"); xprintf("This program has ABSOLUTELY NO WARRANTY.\n"); xprintf("\n"); xprintf("This program is free software; you may re-distribute it " "under the terms\n"); xprintf("of the GNU General Public License version 3 or later.\n") ; done: return; } static int parse_cmdline(struct csa *csa, int argc, const char *argv[]) { /* parse command-line parameters */ int k; #define p(str) (strcmp(argv[k], str) == 0) for (k = 1; k < argc; k++) { if (p("--mps")) csa->format = FMT_MPS_DECK; else if (p("--freemps")) csa->format = FMT_MPS_FILE; else if (p("--lp") || p("--cpxlp")) csa->format = FMT_LP; else if (p("--glp")) csa->format = FMT_GLP; else if (p("--math") || p("-m") || p("--model")) csa->format = FMT_MATHPROG; else if (p("-d") || p("--data")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No input data file specified\n"); return 1; } if (csa->ndf == DATA_MAX) { xprintf("Too many input data files\n"); return 1; } csa->in_data[++(csa->ndf)] = argv[k]; } else if (p("-y") || p("--display")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No display output file specified\n"); return 1; } if (csa->out_dpy != NULL) { xprintf("Only one display output file allowed\n"); return 1; } csa->out_dpy = argv[k]; } else if (p("--seed")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-' && !isdigit((unsigned char)argv[k][1])) { xprintf("No seed value specified\n"); return 1; } if (strcmp(argv[k], "?") == 0) csa->seed = 0x80000000; else if (str2int(argv[k], &csa->seed)) { xprintf("Invalid seed value `%s'\n", argv[k]); return 1; } } else if (p("--mincost")) csa->format = FMT_MIN_COST; else if (p("--maxflow")) csa->format = FMT_MAX_FLOW; else if (p("--simplex")) csa->solution = SOL_BASIC; else if (p("--interior")) csa->solution = SOL_INTERIOR; #if 1 /* 28/V-2010 */ else if (p("--alien")) csa->iocp.alien = GLP_ON; #endif else if (p("-r") || p("--read")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No input solution file specified\n"); return 1; } if (csa->in_res != NULL) { xprintf("Only one input solution file allowed\n"); return 1; } csa->in_res = argv[k]; } else if (p("--min")) csa->dir = GLP_MIN; else if (p("--max")) csa->dir = GLP_MAX; else if (p("--scale")) csa->scale = 1; else if (p("--noscale")) csa->scale = 0; else if (p("-o") || p("--output")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No output solution file specified\n"); return 1; } if (csa->out_sol != NULL) { xprintf("Only one output solution file allowed\n"); return 1; } csa->out_sol = argv[k]; } else if (p("-w") || p("--write")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No output solution file specified\n"); return 1; } if (csa->out_res != NULL) { xprintf("Only one output solution file allowed\n"); return 1; } csa->out_res = argv[k]; } else if (p("--ranges") || p("--bounds")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No output file specified to write sensitivity a" "nalysis report\n"); return 1; } if (csa->out_ranges != NULL) { xprintf("Only one output file allowed to write sensitivi" "ty analysis report\n"); return 1; } csa->out_ranges = argv[k]; } else if (p("--tmlim")) { int tm_lim; k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No time limit specified\n"); return 1; } if (str2int(argv[k], &tm_lim) || tm_lim < 0) { xprintf("Invalid time limit `%s'\n", argv[k]); return 1; } if (tm_lim <= INT_MAX / 1000) csa->smcp.tm_lim = csa->iocp.tm_lim = 1000 * tm_lim; else csa->smcp.tm_lim = csa->iocp.tm_lim = INT_MAX; } else if (p("--memlim")) { int mem_lim; k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No memory limit specified\n"); return 1; } if (str2int(argv[k], &mem_lim) || mem_lim < 1) { xprintf("Invalid memory limit `%s'\n", argv[k]); return 1; } glp_mem_limit(mem_lim); } else if (p("--check")) csa->check = 1; else if (p("--name")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No problem name specified\n"); return 1; } if (csa->new_name != NULL) { xprintf("Only one problem name allowed\n"); return 1; } csa->new_name = argv[k]; } else if (p("--wmps")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No fixed MPS output file specified\n"); return 1; } if (csa->out_mps != NULL) { xprintf("Only one fixed MPS output file allowed\n"); return 1; } csa->out_mps = argv[k]; } else if (p("--wfreemps")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No free MPS output file specified\n"); return 1; } if (csa->out_freemps != NULL) { xprintf("Only one free MPS output file allowed\n"); return 1; } csa->out_freemps = argv[k]; } else if (p("--wlp") || p("--wcpxlp") || p("--wlpt")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No CPLEX LP output file specified\n"); return 1; } if (csa->out_cpxlp != NULL) { xprintf("Only one CPLEX LP output file allowed\n"); return 1; } csa->out_cpxlp = argv[k]; } else if (p("--wglp")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No GLPK LP/MIP output file specified\n"); return 1; } if (csa->out_glp != NULL) { xprintf("Only one GLPK LP/MIP output file allowed\n"); return 1; } csa->out_glp = argv[k]; } else if (p("--wpb")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No problem output file specified\n"); return 1; } if (csa->out_pb != NULL) { xprintf("Only one OPB output file allowed\n"); return 1; } csa->out_pb = argv[k]; } else if (p("--wnpb")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No problem output file specified\n"); return 1; } if (csa->out_npb != NULL) { xprintf("Only one normalized OPB output file allowed\n"); return 1; } csa->out_npb = argv[k]; } else if (p("--log")) { k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No log file specified\n"); return 1; } if (csa->log_file != NULL) { xprintf("Only one log file allowed\n"); return 1; } csa->log_file = argv[k]; } else if (p("-h") || p("--help")) { print_help(argv[0]); return -1; } else if (p("-v") || p("--version")) { print_version(0); return -1; } else if (p("--luf")) csa->bfcp.type = GLP_BF_FT; else if (p("--cbg")) csa->bfcp.type = GLP_BF_BG; else if (p("--cgr")) csa->bfcp.type = GLP_BF_GR; else if (p("--primal")) csa->smcp.meth = GLP_PRIMAL; else if (p("--dual")) csa->smcp.meth = GLP_DUAL; else if (p("--std")) csa->crash = USE_STD_BASIS; else if (p("--adv")) csa->crash = USE_ADV_BASIS; else if (p("--bib")) csa->crash = USE_CPX_BASIS; else if (p("--ini")) { csa->crash = USE_INI_BASIS; csa->smcp.presolve = GLP_OFF; k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No initial basis file specified\n"); return 1; } if (csa->ini_file != NULL) { xprintf("Only one initial basis file allowed\n"); return 1; } csa->ini_file = argv[k]; } else if (p("--steep")) csa->smcp.pricing = GLP_PT_PSE; else if (p("--nosteep")) csa->smcp.pricing = GLP_PT_STD; else if (p("--relax")) csa->smcp.r_test = GLP_RT_HAR; else if (p("--norelax")) csa->smcp.r_test = GLP_RT_STD; else if (p("--presol")) csa->smcp.presolve = GLP_ON; else if (p("--nopresol")) csa->smcp.presolve = GLP_OFF; else if (p("--exact")) csa->exact = 1; else if (p("--xcheck")) csa->xcheck = 1; else if (p("--nord")) csa->iptcp.ord_alg = GLP_ORD_NONE; else if (p("--qmd")) csa->iptcp.ord_alg = GLP_ORD_QMD; else if (p("--amd")) csa->iptcp.ord_alg = GLP_ORD_AMD; else if (p("--symamd")) csa->iptcp.ord_alg = GLP_ORD_SYMAMD; else if (p("--nomip")) csa->nomip = 1; else if (p("--first")) csa->iocp.br_tech = GLP_BR_FFV; else if (p("--last")) csa->iocp.br_tech = GLP_BR_LFV; else if (p("--drtom")) csa->iocp.br_tech = GLP_BR_DTH; else if (p("--mostf")) csa->iocp.br_tech = GLP_BR_MFV; else if (p("--pcost")) csa->iocp.br_tech = GLP_BR_PCH; else if (p("--dfs")) csa->iocp.bt_tech = GLP_BT_DFS; else if (p("--bfs")) csa->iocp.bt_tech = GLP_BT_BFS; else if (p("--bestp")) csa->iocp.bt_tech = GLP_BT_BPH; else if (p("--bestb")) csa->iocp.bt_tech = GLP_BT_BLB; else if (p("--intopt")) csa->iocp.presolve = GLP_ON; else if (p("--nointopt")) csa->iocp.presolve = GLP_OFF; else if (p("--binarize")) csa->iocp.presolve = csa->iocp.binarize = GLP_ON; else if (p("--fpump")) csa->iocp.fp_heur = GLP_ON; else if (p("--gomory")) csa->iocp.gmi_cuts = GLP_ON; else if (p("--mir")) csa->iocp.mir_cuts = GLP_ON; else if (p("--cover")) csa->iocp.cov_cuts = GLP_ON; else if (p("--clique")) csa->iocp.clq_cuts = GLP_ON; else if (p("--cuts")) csa->iocp.gmi_cuts = csa->iocp.mir_cuts = csa->iocp.cov_cuts = csa->iocp.clq_cuts = GLP_ON; else if (p("--mipgap")) { double mip_gap; k++; if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-') { xprintf("No relative gap tolerance specified\n"); return 1; } if (str2num(argv[k], &mip_gap) || mip_gap < 0.0) { xprintf("Invalid relative mip gap tolerance `%s'\n", argv[k]); return 1; } csa->iocp.mip_gap = mip_gap; } else if (argv[k][0] == '-' || (argv[k][0] == '-' && argv[k][1] == '-')) { xprintf("Invalid option `%s'; try %s --help\n", argv[k], argv[0]); return 1; } else { if (csa->in_file != NULL) { xprintf("Only one input problem file allowed\n"); return 1; } csa->in_file = argv[k]; } } #undef p return 0; } typedef struct { double rhs, pi; } v_data; typedef struct { double low, cap, cost, x; } a_data; int glp_main(int argc, const char *argv[]) { /* stand-alone LP/MIP solver */ struct csa _csa, *csa = &_csa; int ret; glp_long start; /* perform initialization */ csa->prob = glp_create_prob(); glp_get_bfcp(csa->prob, &csa->bfcp); glp_init_smcp(&csa->smcp); csa->smcp.presolve = GLP_ON; glp_init_iptcp(&csa->iptcp); glp_init_iocp(&csa->iocp); csa->iocp.presolve = GLP_ON; csa->tran = NULL; csa->graph = NULL; csa->format = FMT_MPS_FILE; csa->in_file = NULL; csa->ndf = 0; csa->out_dpy = NULL; csa->seed = 1; csa->solution = SOL_BASIC; csa->in_res = NULL; csa->dir = 0; csa->scale = 1; csa->out_sol = NULL; csa->out_res = NULL; csa->out_ranges = NULL; csa->check = 0; csa->new_name = NULL; csa->out_mps = NULL; csa->out_freemps = NULL; csa->out_cpxlp = NULL; csa->out_glp = NULL; csa->out_pb = NULL; csa->out_npb = NULL; csa->log_file = NULL; csa->crash = USE_ADV_BASIS; csa->ini_file = NULL; csa->exact = 0; csa->xcheck = 0; csa->nomip = 0; /* parse command-line parameters */ ret = parse_cmdline(csa, argc, argv); if (ret < 0) { ret = EXIT_SUCCESS; goto done; } if (ret > 0) { ret = EXIT_FAILURE; goto done; } /*--------------------------------------------------------------*/ /* remove all output files specified in the command line */ if (csa->out_dpy != NULL) remove(csa->out_dpy); if (csa->out_sol != NULL) remove(csa->out_sol); if (csa->out_res != NULL) remove(csa->out_res); if (csa->out_ranges != NULL) remove(csa->out_ranges); if (csa->out_mps != NULL) remove(csa->out_mps); if (csa->out_freemps != NULL) remove(csa->out_freemps); if (csa->out_cpxlp != NULL) remove(csa->out_cpxlp); if (csa->out_glp != NULL) remove(csa->out_glp); if (csa->out_pb != NULL) remove(csa->out_pb); if (csa->out_npb != NULL) remove(csa->out_npb); if (csa->log_file != NULL) remove(csa->log_file); /*--------------------------------------------------------------*/ /* open log file, if required */ if (csa->log_file != NULL) { if (glp_open_tee(csa->log_file)) { xprintf("Unable to create log file\n"); ret = EXIT_FAILURE; goto done; } } /*--------------------------------------------------------------*/ /* print version information */ print_version(1); /*--------------------------------------------------------------*/ /* print parameters specified in the command line */ if (argc > 1) { int k, len = INT_MAX; xprintf("Parameter(s) specified in the command line:"); for (k = 1; k < argc; k++) { if (len > 72) xprintf("\n"), len = 0; xprintf(" %s", argv[k]); len += 1 + strlen(argv[k]); } xprintf("\n"); } /*--------------------------------------------------------------*/ /* read problem data from the input file */ if (csa->in_file == NULL) { xprintf("No input problem file specified; try %s --help\n", argv[0]); ret = EXIT_FAILURE; goto done; } if (csa->format == FMT_MPS_DECK) { ret = glp_read_mps(csa->prob, GLP_MPS_DECK, NULL, csa->in_file); if (ret != 0) err1: { xprintf("MPS file processing error\n"); ret = EXIT_FAILURE; goto done; } } else if (csa->format == FMT_MPS_FILE) { ret = glp_read_mps(csa->prob, GLP_MPS_FILE, NULL, csa->in_file); if (ret != 0) goto err1; } else if (csa->format == FMT_LP) { ret = glp_read_lp(csa->prob, NULL, csa->in_file); if (ret != 0) { xprintf("CPLEX LP file processing error\n"); ret = EXIT_FAILURE; goto done; } } else if (csa->format == FMT_GLP) { ret = glp_read_prob(csa->prob, 0, csa->in_file); if (ret != 0) { xprintf("GLPK LP/MIP file processing error\n"); ret = EXIT_FAILURE; goto done; } } else if (csa->format == FMT_MATHPROG) { int k; /* allocate the translator workspace */ csa->tran = glp_mpl_alloc_wksp(); /* set seed value */ if (csa->seed == 0x80000000) { csa->seed = glp_time().lo; xprintf("Seed value %d will be used\n", csa->seed); } _glp_mpl_init_rand(csa->tran, csa->seed); /* read model section and optional data section */ if (glp_mpl_read_model(csa->tran, csa->in_file, csa->ndf > 0)) err2: { xprintf("MathProg model processing error\n"); ret = EXIT_FAILURE; goto done; } /* read optional data section(s), if necessary */ for (k = 1; k <= csa->ndf; k++) { if (glp_mpl_read_data(csa->tran, csa->in_data[k])) goto err2; } /* generate the model */ if (glp_mpl_generate(csa->tran, csa->out_dpy)) goto err2; /* build the problem instance from the model */ glp_mpl_build_prob(csa->tran, csa->prob); } else if (csa->format == FMT_MIN_COST) { csa->graph = glp_create_graph(sizeof(v_data), sizeof(a_data)); ret = glp_read_mincost(csa->graph, offsetof(v_data, rhs), offsetof(a_data, low), offsetof(a_data, cap), offsetof(a_data, cost), csa->in_file); if (ret != 0) { xprintf("DIMACS file processing error\n"); ret = EXIT_FAILURE; goto done; } glp_mincost_lp(csa->prob, csa->graph, GLP_ON, offsetof(v_data, rhs), offsetof(a_data, low), offsetof(a_data, cap), offsetof(a_data, cost)); glp_set_prob_name(csa->prob, csa->in_file); } else if (csa->format == FMT_MAX_FLOW) { int s, t; csa->graph = glp_create_graph(sizeof(v_data), sizeof(a_data)); ret = glp_read_maxflow(csa->graph, &s, &t, offsetof(a_data, cap), csa->in_file); if (ret != 0) { xprintf("DIMACS file processing error\n"); ret = EXIT_FAILURE; goto done; } glp_maxflow_lp(csa->prob, csa->graph, GLP_ON, s, t, offsetof(a_data, cap)); glp_set_prob_name(csa->prob, csa->in_file); } else xassert(csa != csa); /*--------------------------------------------------------------*/ /* change problem name, if required */ if (csa->new_name != NULL) glp_set_prob_name(csa->prob, csa->new_name); /* change optimization direction, if required */ if (csa->dir != 0) glp_set_obj_dir(csa->prob, csa->dir); /* sort elements of the constraint matrix */ glp_sort_matrix(csa->prob); /*--------------------------------------------------------------*/ /* write problem data in fixed MPS format, if required */ if (csa->out_mps != NULL) { ret = glp_write_mps(csa->prob, GLP_MPS_DECK, NULL, csa->out_mps); if (ret != 0) { xprintf("Unable to write problem in fixed MPS format\n"); ret = EXIT_FAILURE; goto done; } } /* write problem data in free MPS format, if required */ if (csa->out_freemps != NULL) { ret = glp_write_mps(csa->prob, GLP_MPS_FILE, NULL, csa->out_freemps); if (ret != 0) { xprintf("Unable to write problem in free MPS format\n"); ret = EXIT_FAILURE; goto done; } } /* write problem data in CPLEX LP format, if required */ if (csa->out_cpxlp != NULL) { ret = glp_write_lp(csa->prob, NULL, csa->out_cpxlp); if (ret != 0) { xprintf("Unable to write problem in CPLEX LP format\n"); ret = EXIT_FAILURE; goto done; } } /* write problem data in GLPK format, if required */ if (csa->out_glp != NULL) { ret = glp_write_prob(csa->prob, 0, csa->out_glp); if (ret != 0) { xprintf("Unable to write problem in GLPK format\n"); ret = EXIT_FAILURE; goto done; } } /* write problem data in OPB format, if required */ if (csa->out_pb != NULL) { ret = lpx_write_pb(csa->prob, csa->out_pb, 0, 0); if (ret != 0) { xprintf("Unable to write problem in OPB format\n"); ret = EXIT_FAILURE; goto done; } } /* write problem data in normalized OPB format, if required */ if (csa->out_npb != NULL) { ret = lpx_write_pb(csa->prob, csa->out_npb, 1, 1); if (ret != 0) { xprintf( "Unable to write problem in normalized OPB format\n"); ret = EXIT_FAILURE; goto done; } } /*--------------------------------------------------------------*/ /* if only problem data check is required, skip computations */ if (csa->check) { ret = EXIT_SUCCESS; goto done; } /*--------------------------------------------------------------*/ /* determine the solution type */ if (!csa->nomip && glp_get_num_int(csa->prob) + glp_get_num_bin(csa->prob) > 0) { if (csa->solution == SOL_INTERIOR) { xprintf("Interior-point method is not able to solve MIP pro" "blem; use --simplex\n"); ret = EXIT_FAILURE; goto done; } csa->solution = SOL_INTEGER; } /*--------------------------------------------------------------*/ /* if solution is provided, read it and skip computations */ if (csa->in_res != NULL) { if (csa->solution == SOL_BASIC) ret = glp_read_sol(csa->prob, csa->in_res); else if (csa->solution == SOL_INTERIOR) ret = glp_read_ipt(csa->prob, csa->in_res); else if (csa->solution == SOL_INTEGER) ret = glp_read_mip(csa->prob, csa->in_res); else xassert(csa != csa); if (ret != 0) { xprintf("Unable to read problem solution\n"); ret = EXIT_FAILURE; goto done; } goto skip; } /*--------------------------------------------------------------*/ /* scale the problem data, if required */ if (csa->scale) { if (csa->solution == SOL_BASIC && !csa->smcp.presolve || csa->solution == SOL_INTERIOR || csa->solution == SOL_INTEGER && !csa->iocp.presolve) glp_scale_prob(csa->prob, GLP_SF_AUTO); } /*--------------------------------------------------------------*/ /* construct starting LP basis */ if (csa->solution == SOL_BASIC && !csa->smcp.presolve || csa->solution == SOL_INTEGER && !csa->iocp.presolve) { if (csa->crash == USE_STD_BASIS) glp_std_basis(csa->prob); else if (csa->crash == USE_ADV_BASIS) glp_adv_basis(csa->prob, 0); else if (csa->crash == USE_CPX_BASIS) glp_cpx_basis(csa->prob); else if (csa->crash == USE_INI_BASIS) { ret = glp_read_sol(csa->prob, csa->ini_file); if (ret != 0) { xprintf("Unable to read initial basis\n"); ret = EXIT_FAILURE; goto done; } } else xassert(csa != csa); } /*--------------------------------------------------------------*/ /* solve the problem */ start = xtime(); if (csa->solution == SOL_BASIC) { if (!csa->exact) { glp_set_bfcp(csa->prob, &csa->bfcp); glp_simplex(csa->prob, &csa->smcp); if (csa->xcheck) { if (csa->smcp.presolve && glp_get_status(csa->prob) != GLP_OPT) xprintf("If you need to check final basis for non-opt" "imal solution, use --nopresol\n"); else glp_exact(csa->prob, &csa->smcp); } if (csa->out_sol != NULL || csa->out_res != NULL) { if (csa->smcp.presolve && glp_get_status(csa->prob) != GLP_OPT) xprintf("If you need actual output for non-optimal solut" "ion, use --nopresol\n"); } } else glp_exact(csa->prob, &csa->smcp); } else if (csa->solution == SOL_INTERIOR) glp_interior(csa->prob, &csa->iptcp); else if (csa->solution == SOL_INTEGER) { if (!csa->iocp.presolve) { glp_set_bfcp(csa->prob, &csa->bfcp); glp_simplex(csa->prob, &csa->smcp); } #if 0 csa->iocp.msg_lev = GLP_MSG_DBG; csa->iocp.pp_tech = GLP_PP_NONE; #endif glp_intopt(csa->prob, &csa->iocp); } else xassert(csa != csa); /*--------------------------------------------------------------*/ /* display statistics */ xprintf("Time used: %.1f secs\n", xdifftime(xtime(), start)); { glp_long tpeak; char buf[50]; glp_mem_usage(NULL, NULL, NULL, &tpeak); xprintf("Memory used: %.1f Mb (%s bytes)\n", xltod(tpeak) / 1048576.0, xltoa(tpeak, buf)); } /*--------------------------------------------------------------*/ skip: /* postsolve the model, if necessary */ if (csa->tran != NULL) { if (csa->solution == SOL_BASIC) ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_SOL); else if (csa->solution == SOL_INTERIOR) ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_IPT); else if (csa->solution == SOL_INTEGER) ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_MIP); else xassert(csa != csa); if (ret != 0) { xprintf("Model postsolving error\n"); ret = EXIT_FAILURE; goto done; } } /*--------------------------------------------------------------*/ /* write problem solution in printable format, if required */ if (csa->out_sol != NULL) { if (csa->solution == SOL_BASIC) ret = lpx_print_sol(csa->prob, csa->out_sol); else if (csa->solution == SOL_INTERIOR) ret = lpx_print_ips(csa->prob, csa->out_sol); else if (csa->solution == SOL_INTEGER) ret = lpx_print_mip(csa->prob, csa->out_sol); else xassert(csa != csa); if (ret != 0) { xprintf("Unable to write problem solution\n"); ret = EXIT_FAILURE; goto done; } } /* write problem solution in printable format, if required */ if (csa->out_res != NULL) { if (csa->solution == SOL_BASIC) ret = glp_write_sol(csa->prob, csa->out_res); else if (csa->solution == SOL_INTERIOR) ret = glp_write_ipt(csa->prob, csa->out_res); else if (csa->solution == SOL_INTEGER) ret = glp_write_mip(csa->prob, csa->out_res); else xassert(csa != csa); if (ret != 0) { xprintf("Unable to write problem solution\n"); ret = EXIT_FAILURE; goto done; } } /* write sensitivity analysis report, if required */ if (csa->out_ranges != NULL) { if (csa->solution == SOL_BASIC) { if (glp_get_status(csa->prob) == GLP_OPT) { if (glp_bf_exists(csa->prob)) ranges: { ret = glp_print_ranges(csa->prob, 0, NULL, 0, csa->out_ranges); if (ret != 0) { xprintf("Unable to write sensitivity analysis repo" "rt\n"); ret = EXIT_FAILURE; goto done; } } else { ret = glp_factorize(csa->prob); if (ret == 0) goto ranges; xprintf("Cannot produce sensitivity analysis report d" "ue to error in basis factorization (glp_factorize" " returned %d); try --nopresol\n", ret); } } else xprintf("Cannot produce sensitivity analysis report for " "non-optimal basic solution\n"); } else xprintf("Cannot produce sensitivity analysis report for int" "erior-point or MIP solution\n"); } /*--------------------------------------------------------------*/ /* all seems to be ok */ ret = EXIT_SUCCESS; /*--------------------------------------------------------------*/ done: /* delete the LP/MIP problem object */ if (csa->prob != NULL) glp_delete_prob(csa->prob); /* free the translator workspace, if necessary */ if (csa->tran != NULL) glp_mpl_free_wksp(csa->tran); /* delete the network problem object, if necessary */ if (csa->graph != NULL) glp_delete_graph(csa->graph); xassert(gmp_pool_count() == 0); gmp_free_mem(); /* close log file, if necessary */ if (csa->log_file != NULL) glp_close_tee(); /* check that no memory blocks are still allocated */ { int count; glp_long total; glp_mem_usage(&count, NULL, &total, NULL); if (count != 0) xerror("Error: %d memory block(s) were lost\n", count); xassert(count == 0); xassert(total.lo == 0 && total.hi == 0); } /* free the GLPK environment */ glp_free_env(); /* return to the control program */ return ret; } /* eof */ igraph/src/glpssx02.c0000644000176000001440000004070312325527073014203 0ustar ripleyusers/* glpssx02.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpenv.h" #include "glpssx.h" static void show_progress(SSX *ssx, int phase) { /* this auxiliary routine displays information about progress of the search */ int i, def = 0; for (i = 1; i <= ssx->m; i++) if (ssx->type[ssx->Q_col[i]] == SSX_FX) def++; xprintf("%s%6d: %s = %22.15g (%d)\n", phase == 1 ? " " : "*", ssx->it_cnt, phase == 1 ? "infsum" : "objval", mpq_get_d(ssx->bbar[0]), def); #if 0 ssx->tm_lag = utime(); #else ssx->tm_lag = xtime(); #endif return; } /*---------------------------------------------------------------------- // ssx_phase_I - find primal feasible solution. // // This routine implements phase I of the primal simplex method. // // On exit the routine returns one of the following codes: // // 0 - feasible solution found; // 1 - problem has no feasible solution; // 2 - iterations limit exceeded; // 3 - time limit exceeded. ----------------------------------------------------------------------*/ int ssx_phase_I(SSX *ssx) { int m = ssx->m; int n = ssx->n; int *type = ssx->type; mpq_t *lb = ssx->lb; mpq_t *ub = ssx->ub; mpq_t *coef = ssx->coef; int *A_ptr = ssx->A_ptr; int *A_ind = ssx->A_ind; mpq_t *A_val = ssx->A_val; int *Q_col = ssx->Q_col; mpq_t *bbar = ssx->bbar; mpq_t *pi = ssx->pi; mpq_t *cbar = ssx->cbar; int *orig_type, orig_dir; mpq_t *orig_lb, *orig_ub, *orig_coef; int i, k, ret; /* save components of the original LP problem, which are changed by the routine */ orig_type = xcalloc(1+m+n, sizeof(int)); orig_lb = xcalloc(1+m+n, sizeof(mpq_t)); orig_ub = xcalloc(1+m+n, sizeof(mpq_t)); orig_coef = xcalloc(1+m+n, sizeof(mpq_t)); for (k = 1; k <= m+n; k++) { orig_type[k] = type[k]; mpq_init(orig_lb[k]); mpq_set(orig_lb[k], lb[k]); mpq_init(orig_ub[k]); mpq_set(orig_ub[k], ub[k]); } orig_dir = ssx->dir; for (k = 0; k <= m+n; k++) { mpq_init(orig_coef[k]); mpq_set(orig_coef[k], coef[k]); } /* build an artificial basic solution, which is primal feasible, and also build an auxiliary objective function to minimize the sum of infeasibilities for the original problem */ ssx->dir = SSX_MIN; for (k = 0; k <= m+n; k++) mpq_set_si(coef[k], 0, 1); mpq_set_si(bbar[0], 0, 1); for (i = 1; i <= m; i++) { int t; k = Q_col[i]; /* x[k] = xB[i] */ t = type[k]; if (t == SSX_LO || t == SSX_DB || t == SSX_FX) { /* in the original problem x[k] has lower bound */ if (mpq_cmp(bbar[i], lb[k]) < 0) { /* which is violated */ type[k] = SSX_UP; mpq_set(ub[k], lb[k]); mpq_set_si(lb[k], 0, 1); mpq_set_si(coef[k], -1, 1); mpq_add(bbar[0], bbar[0], ub[k]); mpq_sub(bbar[0], bbar[0], bbar[i]); } } if (t == SSX_UP || t == SSX_DB || t == SSX_FX) { /* in the original problem x[k] has upper bound */ if (mpq_cmp(bbar[i], ub[k]) > 0) { /* which is violated */ type[k] = SSX_LO; mpq_set(lb[k], ub[k]); mpq_set_si(ub[k], 0, 1); mpq_set_si(coef[k], +1, 1); mpq_add(bbar[0], bbar[0], bbar[i]); mpq_sub(bbar[0], bbar[0], lb[k]); } } } /* now the initial basic solution should be primal feasible due to changes of bounds of some basic variables, which turned to implicit artifical variables */ /* compute simplex multipliers and reduced costs */ ssx_eval_pi(ssx); ssx_eval_cbar(ssx); /* display initial progress of the search */ show_progress(ssx, 1); /* main loop starts here */ for (;;) { /* display current progress of the search */ #if 0 if (utime() - ssx->tm_lag >= ssx->out_frq - 0.001) #else if (xdifftime(xtime(), ssx->tm_lag) >= ssx->out_frq - 0.001) #endif show_progress(ssx, 1); /* we do not need to wait until all artificial variables have left the basis */ if (mpq_sgn(bbar[0]) == 0) { /* the sum of infeasibilities is zero, therefore the current solution is primal feasible for the original problem */ ret = 0; break; } /* check if the iterations limit has been exhausted */ if (ssx->it_lim == 0) { ret = 2; break; } /* check if the time limit has been exhausted */ #if 0 if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= utime() - ssx->tm_beg) #else if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= xdifftime(xtime(), ssx->tm_beg)) #endif { ret = 3; break; } /* choose non-basic variable xN[q] */ ssx_chuzc(ssx); /* if xN[q] cannot be chosen, the sum of infeasibilities is minimal but non-zero; therefore the original problem has no primal feasible solution */ if (ssx->q == 0) { ret = 1; break; } /* compute q-th column of the simplex table */ ssx_eval_col(ssx); /* choose basic variable xB[p] */ ssx_chuzr(ssx); /* the sum of infeasibilities cannot be negative, therefore the auxiliary lp problem cannot have unbounded solution */ xassert(ssx->p != 0); /* update values of basic variables */ ssx_update_bbar(ssx); if (ssx->p > 0) { /* compute p-th row of the inverse inv(B) */ ssx_eval_rho(ssx); /* compute p-th row of the simplex table */ ssx_eval_row(ssx); xassert(mpq_cmp(ssx->aq[ssx->p], ssx->ap[ssx->q]) == 0); /* update simplex multipliers */ ssx_update_pi(ssx); /* update reduced costs of non-basic variables */ ssx_update_cbar(ssx); } /* xB[p] is leaving the basis; if it is implicit artificial variable, the corresponding residual vanishes; therefore bounds of this variable should be restored to the original values */ if (ssx->p > 0) { k = Q_col[ssx->p]; /* x[k] = xB[p] */ if (type[k] != orig_type[k]) { /* x[k] is implicit artificial variable */ type[k] = orig_type[k]; mpq_set(lb[k], orig_lb[k]); mpq_set(ub[k], orig_ub[k]); xassert(ssx->p_stat == SSX_NL || ssx->p_stat == SSX_NU); ssx->p_stat = (ssx->p_stat == SSX_NL ? SSX_NU : SSX_NL); if (type[k] == SSX_FX) ssx->p_stat = SSX_NS; /* nullify the objective coefficient at x[k] */ mpq_set_si(coef[k], 0, 1); /* since coef[k] has been changed, we need to compute new reduced cost of x[k], which it will have in the adjacent basis */ /* the formula d[j] = cN[j] - pi' * N[j] is used (note that the vector pi is not changed, because it depends on objective coefficients at basic variables, but in the adjacent basis, for which the vector pi has been just recomputed, x[k] is non-basic) */ if (k <= m) { /* x[k] is auxiliary variable */ mpq_neg(cbar[ssx->q], pi[k]); } else { /* x[k] is structural variable */ int ptr; mpq_t temp; mpq_init(temp); mpq_set_si(cbar[ssx->q], 0, 1); for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) { mpq_mul(temp, pi[A_ind[ptr]], A_val[ptr]); mpq_add(cbar[ssx->q], cbar[ssx->q], temp); } mpq_clear(temp); } } } /* jump to the adjacent vertex of the polyhedron */ ssx_change_basis(ssx); /* one simplex iteration has been performed */ if (ssx->it_lim > 0) ssx->it_lim--; ssx->it_cnt++; } /* display final progress of the search */ show_progress(ssx, 1); /* restore components of the original problem, which were changed by the routine */ for (k = 1; k <= m+n; k++) { type[k] = orig_type[k]; mpq_set(lb[k], orig_lb[k]); mpq_clear(orig_lb[k]); mpq_set(ub[k], orig_ub[k]); mpq_clear(orig_ub[k]); } ssx->dir = orig_dir; for (k = 0; k <= m+n; k++) { mpq_set(coef[k], orig_coef[k]); mpq_clear(orig_coef[k]); } xfree(orig_type); xfree(orig_lb); xfree(orig_ub); xfree(orig_coef); /* return to the calling program */ return ret; } /*---------------------------------------------------------------------- // ssx_phase_II - find optimal solution. // // This routine implements phase II of the primal simplex method. // // On exit the routine returns one of the following codes: // // 0 - optimal solution found; // 1 - problem has unbounded solution; // 2 - iterations limit exceeded; // 3 - time limit exceeded. ----------------------------------------------------------------------*/ int ssx_phase_II(SSX *ssx) { int ret; /* display initial progress of the search */ show_progress(ssx, 2); /* main loop starts here */ for (;;) { /* display current progress of the search */ #if 0 if (utime() - ssx->tm_lag >= ssx->out_frq - 0.001) #else if (xdifftime(xtime(), ssx->tm_lag) >= ssx->out_frq - 0.001) #endif show_progress(ssx, 2); /* check if the iterations limit has been exhausted */ if (ssx->it_lim == 0) { ret = 2; break; } /* check if the time limit has been exhausted */ #if 0 if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= utime() - ssx->tm_beg) #else if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= xdifftime(xtime(), ssx->tm_beg)) #endif { ret = 3; break; } /* choose non-basic variable xN[q] */ ssx_chuzc(ssx); /* if xN[q] cannot be chosen, the current basic solution is dual feasible and therefore optimal */ if (ssx->q == 0) { ret = 0; break; } /* compute q-th column of the simplex table */ ssx_eval_col(ssx); /* choose basic variable xB[p] */ ssx_chuzr(ssx); /* if xB[p] cannot be chosen, the problem has no dual feasible solution (i.e. unbounded) */ if (ssx->p == 0) { ret = 1; break; } /* update values of basic variables */ ssx_update_bbar(ssx); if (ssx->p > 0) { /* compute p-th row of the inverse inv(B) */ ssx_eval_rho(ssx); /* compute p-th row of the simplex table */ ssx_eval_row(ssx); xassert(mpq_cmp(ssx->aq[ssx->p], ssx->ap[ssx->q]) == 0); #if 0 /* update simplex multipliers */ ssx_update_pi(ssx); #endif /* update reduced costs of non-basic variables */ ssx_update_cbar(ssx); } /* jump to the adjacent vertex of the polyhedron */ ssx_change_basis(ssx); /* one simplex iteration has been performed */ if (ssx->it_lim > 0) ssx->it_lim--; ssx->it_cnt++; } /* display final progress of the search */ show_progress(ssx, 2); /* return to the calling program */ return ret; } /*---------------------------------------------------------------------- // ssx_driver - base driver to exact simplex method. // // This routine is a base driver to a version of the primal simplex // method using exact (bignum) arithmetic. // // On exit the routine returns one of the following codes: // // 0 - optimal solution found; // 1 - problem has no feasible solution; // 2 - problem has unbounded solution; // 3 - iterations limit exceeded (phase I); // 4 - iterations limit exceeded (phase II); // 5 - time limit exceeded (phase I); // 6 - time limit exceeded (phase II); // 7 - initial basis matrix is exactly singular. ----------------------------------------------------------------------*/ int ssx_driver(SSX *ssx) { int m = ssx->m; int *type = ssx->type; mpq_t *lb = ssx->lb; mpq_t *ub = ssx->ub; int *Q_col = ssx->Q_col; mpq_t *bbar = ssx->bbar; int i, k, ret; ssx->tm_beg = xtime(); /* factorize the initial basis matrix */ if (ssx_factorize(ssx)) { xprintf("Initial basis matrix is singular\n"); ret = 7; goto done; } /* compute values of basic variables */ ssx_eval_bbar(ssx); /* check if the initial basic solution is primal feasible */ for (i = 1; i <= m; i++) { int t; k = Q_col[i]; /* x[k] = xB[i] */ t = type[k]; if (t == SSX_LO || t == SSX_DB || t == SSX_FX) { /* x[k] has lower bound */ if (mpq_cmp(bbar[i], lb[k]) < 0) { /* which is violated */ break; } } if (t == SSX_UP || t == SSX_DB || t == SSX_FX) { /* x[k] has upper bound */ if (mpq_cmp(bbar[i], ub[k]) > 0) { /* which is violated */ break; } } } if (i > m) { /* no basic variable violates its bounds */ ret = 0; goto skip; } /* phase I: find primal feasible solution */ ret = ssx_phase_I(ssx); switch (ret) { case 0: ret = 0; break; case 1: xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n"); ret = 1; break; case 2: xprintf("ITERATIONS LIMIT EXCEEDED; SEARCH TERMINATED\n"); ret = 3; break; case 3: xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); ret = 5; break; default: xassert(ret != ret); } /* compute values of basic variables (actually only the objective value needs to be computed) */ ssx_eval_bbar(ssx); skip: /* compute simplex multipliers */ ssx_eval_pi(ssx); /* compute reduced costs of non-basic variables */ ssx_eval_cbar(ssx); /* if phase I failed, do not start phase II */ if (ret != 0) goto done; /* phase II: find optimal solution */ ret = ssx_phase_II(ssx); switch (ret) { case 0: xprintf("OPTIMAL SOLUTION FOUND\n"); ret = 0; break; case 1: xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n"); ret = 2; break; case 2: xprintf("ITERATIONS LIMIT EXCEEDED; SEARCH TERMINATED\n"); ret = 4; break; case 3: xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); ret = 6; break; default: xassert(ret != ret); } done: /* decrease the time limit by the spent amount of time */ if (ssx->tm_lim >= 0.0) #if 0 { ssx->tm_lim -= utime() - ssx->tm_beg; #else { ssx->tm_lim -= xdifftime(xtime(), ssx->tm_beg); #endif if (ssx->tm_lim < 0.0) ssx->tm_lim = 0.0; } return ret; } /* eof */ igraph/src/cocitation.c0000644000176000001440000006613012325527072014656 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph R package. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_cocitation.h" #include "igraph_memory.h" #include "igraph_adjlist.h" #include "igraph_interrupt_internal.h" #include "igraph_interface.h" #include "config.h" #include int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_vector_t *weights); /** * \ingroup structural * \function igraph_cocitation * \brief Cocitation coupling. * * * Two vertices are cocited if there is another vertex citing both of * them. \ref igraph_cocitation() simply counts how many times two vertices are * cocited. * The cocitation score for each given vertex and all other vertices * in the graph will be calculated. * \param graph The graph object to analyze. * \param res Pointer to a matrix, the result of the calculation will * be stored here. The number of its rows is the same as the * number of vertex ids in \p vids, the number of * columns is the number of vertices in the graph. * \param vids The vertex ids of the vertices for which the * calculation will be done. * \return Error code: * \c IGRAPH_EINVVID: invalid vertex id. * * Time complexity: O(|V|d^2), |V| is * the number of vertices in the graph, * d is the (maximum) degree of * the vertices in the graph. * * \sa \ref igraph_bibcoupling() * * \example examples/simple/igraph_cocitation.c */ int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids) { return igraph_cocitation_real(graph, res, vids, IGRAPH_OUT, 0); } /** * \ingroup structural * \function igraph_bibcoupling * \brief Bibliographic coupling. * * * The bibliographic coupling of two vertices is the number * of other vertices they both cite, \ref igraph_bibcoupling() calculates * this. * The bibliographic coupling score for each given vertex and all * other vertices in the graph will be calculated. * \param graph The graph object to analyze. * \param res Pointer to a matrix, the result of the calculation will * be stored here. The number of its rows is the same as the * number of vertex ids in \p vids, the number of * columns is the number of vertices in the graph. * \param vids The vertex ids of the vertices for which the * calculation will be done. * \return Error code: * \c IGRAPH_EINVVID: invalid vertex id. * * Time complexity: O(|V|d^2), * |V| is the number of vertices in * the graph, d is the (maximum) * degree of the vertices in the graph. * * \sa \ref igraph_cocitation() */ int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids) { return igraph_cocitation_real(graph, res, vids, IGRAPH_IN, 0); } /** * \ingroup structural * \function igraph_similarity_inverse_log_weighted * \brief Vertex similarity based on the inverse logarithm of vertex degrees. * * * The inverse log-weighted similarity of two vertices is the number of * their common neighbors, weighted by the inverse logarithm of their degrees. * It is based on the assumption that two vertices should be considered * more similar if they share a low-degree common neighbor, since high-degree * common neighbors are more likely to appear even by pure chance. * * * Isolated vertices will have zero similarity to any other vertex. * Self-similarities are not calculated. * * * See the following paper for more details: Lada A. Adamic and Eytan Adar: * Friends and neighbors on the Web. Social Networks, 25(3):211-230, 2003. * * \param graph The graph object to analyze. * \param res Pointer to a matrix, the result of the calculation will * be stored here. The number of its rows is the same as the * number of vertex ids in \p vids, the number of * columns is the number of vertices in the graph. * \param vids The vertex ids of the vertices for which the * calculation will be done. * \param mode The type of neighbors to be used for the calculation in * directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing edges will be considered for each node. Nodes * will be weighted according to their in-degree. * \cli IGRAPH_IN * the incoming edges will be considered for each node. Nodes * will be weighted according to their out-degree. * \cli IGRAPH_ALL * the directed graph is considered as an undirected one for the * computation. Every node is weighted according to its undirected * degree. * \endclist * \return Error code: * \c IGRAPH_EINVVID: invalid vertex id. * * Time complexity: O(|V|d^2), * |V| is the number of vertices in * the graph, d is the (maximum) * degree of the vertices in the graph. * * \example examples/simple/igraph_similarity.c */ int igraph_similarity_inverse_log_weighted(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode) { igraph_vector_t weights; igraph_neimode_t mode0; long int i, no_of_nodes; switch (mode) { case IGRAPH_OUT: mode0 = IGRAPH_IN; break; case IGRAPH_IN: mode0 = IGRAPH_OUT; break; default: mode0 = IGRAPH_ALL; } no_of_nodes = igraph_vcount(graph); IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_nodes); IGRAPH_CHECK(igraph_degree(graph, &weights, igraph_vss_all(), mode0, 1)); for (i=0; i < no_of_nodes; i++) { if (VECTOR(weights)[i] > 1) VECTOR(weights)[i] = 1.0 / log(VECTOR(weights)[i]); } IGRAPH_CHECK(igraph_cocitation_real(graph, res, vids, mode0, &weights)); igraph_vector_destroy(&weights); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_vector_t *weights) { long int no_of_nodes=igraph_vcount(graph); long int no_of_vids; long int from, i, j, k, l, u, v; igraph_vector_t neis=IGRAPH_VECTOR_NULL; igraph_vector_t vid_reverse_index; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); no_of_vids = IGRAPH_VIT_SIZE(vit); /* Create a mapping from vertex IDs to the row of the matrix where * the result for this vertex will appear */ IGRAPH_VECTOR_INIT_FINALLY(&vid_reverse_index, no_of_nodes); igraph_vector_fill(&vid_reverse_index, -1); for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { v = IGRAPH_VIT_GET(vit); if (v < 0 || v >= no_of_nodes) IGRAPH_ERROR("invalid vertex ID in vertex selector", IGRAPH_EINVAL); VECTOR(vid_reverse_index)[v] = i; } IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_vids, no_of_nodes)); igraph_matrix_null(res); /* The result */ for (from=0; from * The Jaccard similarity coefficient of two vertices is the number of common * neighbors divided by the number of vertices that are neighbors of at * least one of the two vertices being considered. This function calculates * the pairwise Jaccard similarities for some (or all) of the vertices. * * \param graph The graph object to analyze * \param res Pointer to a matrix, the result of the calculation will * be stored here. The number of its rows and columns is the same * as the number of vertex ids in \p vids. * \param vids The vertex ids of the vertices for which the * calculation will be done. * \param mode The type of neighbors to be used for the calculation in * directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing edges will be considered for each node. * \cli IGRAPH_IN * the incoming edges will be considered for each node. * \cli IGRAPH_ALL * the directed graph is considered as an undirected one for the * computation. * \endclist * \param loops Whether to include the vertices themselves in the neighbor * sets. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(|V|^2 d), * |V| is the number of vertices in the vertex iterator given, d is the * (maximum) degree of the vertices in the graph. * * \sa \ref igraph_similarity_dice(), a measure very similar to the Jaccard * coefficient * * \example examples/simple/igraph_similarity.c */ int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) { igraph_lazy_adjlist_t al; igraph_vit_t vit, vit2; long int i, j, k; long int len_union, len_intersection; igraph_vector_t *v1, *v2; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit2)); IGRAPH_FINALLY(igraph_vit_destroy, &vit2); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, mode, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); IGRAPH_CHECK(igraph_matrix_resize(res, IGRAPH_VIT_SIZE(vit), IGRAPH_VIT_SIZE(vit))); if (loops) { for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { i=IGRAPH_VIT_GET(vit); v1=igraph_lazy_adjlist_get(&al, (igraph_integer_t) i); if (!igraph_vector_binsearch(v1, i, &k)) igraph_vector_insert(v1, k, i); } } for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { MATRIX(*res, i, i) = 1.0; for (IGRAPH_VIT_RESET(vit2), j=0; !IGRAPH_VIT_END(vit2); IGRAPH_VIT_NEXT(vit2), j++) { if (j <= i) continue; v1=igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit)); v2=igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit2)); igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection); if (len_union > 0) MATRIX(*res, i, j) = ((igraph_real_t)len_intersection)/len_union; else MATRIX(*res, i, j) = 0.0; MATRIX(*res, j, i) = MATRIX(*res, i, j); } } igraph_lazy_adjlist_destroy(&al); igraph_vit_destroy(&vit); igraph_vit_destroy(&vit2); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \ingroup structural * \function igraph_similarity_jaccard_pairs * \brief Jaccard similarity coefficient for given vertex pairs. * * * The Jaccard similarity coefficient of two vertices is the number of common * neighbors divided by the number of vertices that are neighbors of at * least one of the two vertices being considered. This function calculates * the pairwise Jaccard similarities for a list of vertex pairs. * * \param graph The graph object to analyze * \param res Pointer to a vector, the result of the calculation will * be stored here. The number of elements is the same as the number * of pairs in \p pairs. * \param pairs A vector that contains the pairs for which the similarity * will be calculated. Each pair is defined by two consecutive elements, * i.e. the first and second element of the vector specifies the first * pair, the third and fourth element specifies the second pair and so on. * \param mode The type of neighbors to be used for the calculation in * directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing edges will be considered for each node. * \cli IGRAPH_IN * the incoming edges will be considered for each node. * \cli IGRAPH_ALL * the directed graph is considered as an undirected one for the * computation. * \endclist * \param loops Whether to include the vertices themselves in the neighbor * sets. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(nd), n is the number of pairs in the given vector, d is * the (maximum) degree of the vertices in the graph. * * \sa \ref igraph_similarity_jaccard() to calculate the Jaccard similarity * between all pairs of a vertex set, or \ref igraph_similarity_dice() and * \ref igraph_similarity_dice_pairs() for a measure very similar to the * Jaccard coefficient * * \example examples/simple/igraph_similarity.c */ int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { igraph_lazy_adjlist_t al; long int i, j, k, u, v; long int len_union, len_intersection; igraph_vector_t *v1, *v2; igraph_bool_t *seen; k = igraph_vector_size(pairs); if (k % 2 != 0) IGRAPH_ERROR("number of elements in `pairs' must be even", IGRAPH_EINVAL); IGRAPH_CHECK(igraph_vector_resize(res, k/2)); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, mode, IGRAPH_SIMPLIFY)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); if (loops) { /* Add the loop edges */ i = igraph_vcount(graph); seen = igraph_Calloc(i, igraph_bool_t); if (seen == 0) IGRAPH_ERROR("cannot calculate Jaccard similarity", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, seen); for (i = 0; i < k; i++) { j = (long int) VECTOR(*pairs)[i]; if (seen[j]) continue; seen[j] = 1; v1=igraph_lazy_adjlist_get(&al, (igraph_integer_t) j); if (!igraph_vector_binsearch(v1, j, &u)) igraph_vector_insert(v1, u, j); } free(seen); IGRAPH_FINALLY_CLEAN(1); } for (i = 0, j = 0; i < k; i += 2, j++) { u = (long int) VECTOR(*pairs)[i]; v = (long int) VECTOR(*pairs)[i+1]; if (u == v) { VECTOR(*res)[j] = 1.0; continue; } v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) u); v2 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) v); igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection); if (len_union > 0) VECTOR(*res)[j] = ((igraph_real_t)len_intersection) / len_union; else VECTOR(*res)[j] = 0.0; } igraph_lazy_adjlist_destroy(&al); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup structural * \function igraph_similarity_jaccard_es * \brief Jaccard similarity coefficient for a given edge selector. * * * The Jaccard similarity coefficient of two vertices is the number of common * neighbors divided by the number of vertices that are neighbors of at * least one of the two vertices being considered. This function calculates * the pairwise Jaccard similarities for the endpoints of edges in a given edge * selector. * * \param graph The graph object to analyze * \param res Pointer to a vector, the result of the calculation will * be stored here. The number of elements is the same as the number * of edges in \p es. * \param es An edge selector that specifies the edges to be included in the * result. * \param mode The type of neighbors to be used for the calculation in * directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing edges will be considered for each node. * \cli IGRAPH_IN * the incoming edges will be considered for each node. * \cli IGRAPH_ALL * the directed graph is considered as an undirected one for the * computation. * \endclist * \param loops Whether to include the vertices themselves in the neighbor * sets. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(nd), n is the number of edges in the edge selector, d is * the (maximum) degree of the vertices in the graph. * * \sa \ref igraph_similarity_jaccard() and \ref igraph_similarity_jaccard_pairs() * to calculate the Jaccard similarity between all pairs of a vertex set or * some selected vertex pairs, or \ref igraph_similarity_dice(), * \ref igraph_similarity_dice_pairs() and \ref igraph_similarity_dice_es() for a * measure very similar to the Jaccard coefficient * * \example examples/simple/igraph_similarity.c */ int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops) { igraph_vector_t v; igraph_eit_t eit; IGRAPH_VECTOR_INIT_FINALLY(&v, 0); IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); while (!IGRAPH_EIT_END(eit)) { long int eid = IGRAPH_EIT_GET(eit); igraph_vector_push_back(&v, IGRAPH_FROM(graph, eid)); igraph_vector_push_back(&v, IGRAPH_TO(graph, eid)); IGRAPH_EIT_NEXT(eit); } igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, &v, mode, loops)); igraph_vector_destroy(&v); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * \ingroup structural * \function igraph_similarity_dice * \brief Dice similarity coefficient. * * * The Dice similarity coefficient of two vertices is twice the number of common * neighbors divided by the sum of the degrees of the vertices. This function * calculates the pairwise Dice similarities for some (or all) of the vertices. * * \param graph The graph object to analyze * \param res Pointer to a matrix, the result of the calculation will * be stored here. The number of its rows and columns is the same * as the number of vertex ids in \p vids. * \param vids The vertex ids of the vertices for which the * calculation will be done. * \param mode The type of neighbors to be used for the calculation in * directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing edges will be considered for each node. * \cli IGRAPH_IN * the incoming edges will be considered for each node. * \cli IGRAPH_ALL * the directed graph is considered as an undirected one for the * computation. * \endclist * \param loops Whether to include the vertices themselves as their own * neighbors. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(|V|^2 d), * |V| is the number of vertices in the vertex iterator given, d is the * (maximum) degree of the vertices in the graph. * * \sa \ref igraph_similarity_jaccard(), a measure very similar to the Dice * coefficient * * \example examples/simple/igraph_similarity.c */ int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) { long int i, j, nr, nc; IGRAPH_CHECK(igraph_similarity_jaccard(graph, res, vids, mode, loops)); nr = igraph_matrix_nrow(res); nc = igraph_matrix_ncol(res); for (i = 0; i < nr; i++) { for (j = 0; j < nc; j++) { igraph_real_t x = MATRIX(*res, i, j); MATRIX(*res, i, j) = 2*x / (1+x); } } return IGRAPH_SUCCESS; } /** * \ingroup structural * \function igraph_similarity_dice_pairs * \brief Dice similarity coefficient for given vertex pairs. * * * The Dice similarity coefficient of two vertices is twice the number of common * neighbors divided by the sum of the degrees of the vertices. This function * calculates the pairwise Dice similarities for a list of vertex pairs. * * \param graph The graph object to analyze * \param res Pointer to a vector, the result of the calculation will * be stored here. The number of elements is the same as the number * of pairs in \p pairs. * \param pairs A vector that contains the pairs for which the similarity * will be calculated. Each pair is defined by two consecutive elements, * i.e. the first and second element of the vector specifies the first * pair, the third and fourth element specifies the second pair and so on. * \param mode The type of neighbors to be used for the calculation in * directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing edges will be considered for each node. * \cli IGRAPH_IN * the incoming edges will be considered for each node. * \cli IGRAPH_ALL * the directed graph is considered as an undirected one for the * computation. * \endclist * \param loops Whether to include the vertices themselves as their own * neighbors. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(nd), n is the number of pairs in the given vector, d is * the (maximum) degree of the vertices in the graph. * * \sa \ref igraph_similarity_dice() to calculate the Dice similarity * between all pairs of a vertex set, or \ref igraph_similarity_jaccard(), * \ref igraph_similarity_jaccard_pairs() and \ref igraph_similarity_jaccard_es() * for a measure very similar to the Dice coefficient * * \example examples/simple/igraph_similarity.c */ int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { long int i, n; IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, pairs, mode, loops)); n = igraph_vector_size(res); for (i = 0; i < n; i++) { igraph_real_t x = VECTOR(*res)[i]; VECTOR(*res)[i] = 2*x / (1+x); } return IGRAPH_SUCCESS; } /** * \ingroup structural * \function igraph_similarity_dice_es * \brief Dice similarity coefficient for a given edge selector. * * * The Dice similarity coefficient of two vertices is twice the number of common * neighbors divided by the sum of the degrees of the vertices. This function * calculates the pairwise Dice similarities for the endpoints of edges in a given * edge selector. * * \param graph The graph object to analyze * \param res Pointer to a vector, the result of the calculation will * be stored here. The number of elements is the same as the number * of edges in \p es. * \param es An edge selector that specifies the edges to be included in the * result. * \param mode The type of neighbors to be used for the calculation in * directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the outgoing edges will be considered for each node. * \cli IGRAPH_IN * the incoming edges will be considered for each node. * \cli IGRAPH_ALL * the directed graph is considered as an undirected one for the * computation. * \endclist * \param loops Whether to include the vertices themselves as their own * neighbors. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(nd), n is the number of pairs in the given vector, d is * the (maximum) degree of the vertices in the graph. * * \sa \ref igraph_similarity_dice() and \ref igraph_similarity_dice_pairs() * to calculate the Dice similarity between all pairs of a vertex set or * some selected vertex pairs, or \ref igraph_similarity_jaccard(), * \ref igraph_similarity_jaccard_pairs() and \ref igraph_similarity_jaccard_es() * for a measure very similar to the Dice coefficient * * \example examples/simple/igraph_similarity.c */ int igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res, const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops) { long int i, n; IGRAPH_CHECK(igraph_similarity_jaccard_es(graph, res, es, mode, loops)); n = igraph_vector_size(res); for (i = 0; i < n; i++) { igraph_real_t x = VECTOR(*res)[i]; VECTOR(*res)[i] = 2*x / (1+x); } return IGRAPH_SUCCESS; } igraph/src/gengraph_hash.h0000644000176000001440000001763112325527073015330 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef HASH_H #define HASH_H #include #include "gengraph_definitions.h" //_________________________________________________________________________ // Hash table profiling... Active only if definition below is uncommented //_________________________________________________________________________ //#define _HASH_PROFILE namespace gengraph { #ifdef _HASH_PROFILE void _hash_add_iter(); void _hash_add_call(); void _hash_put_iter(); void _hash_put_call(); void _hash_rm_iter(); void _hash_rm_call(); void _hash_find_iter(); void _hash_find_call(); void _hash_rand_iter(); void _hash_rand_call(); void _hash_expand_call(); void _hash_prof(); #define _HASH_ADD_ITER() _hash_add_iter() #define _HASH_ADD_CALL() _hash_add_call() #define _HASH_PUT_ITER() _hash_put_iter() #define _HASH_PUT_CALL() _hash_put_call() #define _HASH_RM_ITER() _hash_rm_iter() #define _HASH_RM_CALL() _hash_rm_call() #define _HASH_FIND_ITER() _hash_find_iter() #define _HASH_FIND_CALL() _hash_find_call() #define _HASH_RAND_ITER() _hash_rand_iter() #define _HASH_RAND_CALL() _hash_rand_call() #define _HASH_EXP_CALL() _hash_expand_call() #else #define _HASH_ADD_ITER() {} #define _HASH_ADD_CALL() {} #define _HASH_PUT_ITER() {} #define _HASH_PUT_CALL() {} #define _HASH_RM_ITER() {} #define _HASH_RM_CALL() {} #define _HASH_FIND_ITER() {} #define _HASH_FIND_CALL() {} #define _HASH_RAND_ITER() {} #define _HASH_RAND_CALL() {} #define _HASH_EXP_CALL() {} #endif //_________________________________________________________________________ // Hash Table properties. Works best when HASH_SIZE_IS_POWER2 is uncommented // but takes 2.25 times the needed space, in average (from 1.5 to 3) // If you have memory issues, Try to comment it: tables will take 1.5 times // the minimal space //_________________________________________________________________________ #define HASH_SIZE_IS_POWER2 #define MACRO_RATHER_THAN_INLINE // under HASH_MIN_SIZE, vectors are not hash table (just a simle array) #define HASH_MIN_SIZE 100 #define IS_HASH(x) ((x)>HASH_MIN_SIZE) #define HASH_NONE (-1) #ifdef HASH_SIZE_IS_POWER2 inline int HASH_EXPAND(int x) { _HASH_EXP_CALL(); x+=x; x |= x>>1; x |= x>>2; x |= x>>4; x |= x>>8; x |= x>>16; return x+1; } #define HASH_KEY(x,size) ((x*2198737)&((size)-1)) #endif //HASH_SIZE_IS_POWER2 #ifdef MACRO_RATHER_THAN_INLINE #ifndef HASH_SIZE_IS_POWER2 #define HASH_EXPAND(x) ((x)+((x)>>1)) #define HASH_UNEXPAND(x) ((((x)<<1)+1)/3) #define HASH_KEY(x,size) ((x)%(size)) #endif //HASH_SIZE_IS_POWER2 #define HASH_SIZE(x) (IS_HASH(x) ? HASH_EXPAND(x) : (x) ) #define HASH_REKEY(k,size) ((k)==0 ? (size)-1 : (k)-1) #else //MACRO_RATHER_THAN_INLINE #ifndef HASH_SIZE_IS_POWER2 inline int HASH_KEY(const int x, const int size) { assert(x>=0); return x%size; }; inline int HASH_EXPAND(const int x) { _HASH_EXP_CALL(); return x+(x>>1); }; inline int HASH_UNEXPAND(const int x) { return ((x<<1)+1)/3; }; #endif //HASH_SIZE_IS_POWER2 inline int HASH_REKEY(const int k, const int s) { assert(k>=0); if(k==0) return s-1; else return k-1; }; inline int HASH_SIZE(const int x) { if(IS_HASH(x)) return HASH_EXPAND(x); else return x; }; #endif //MACRO_RATHER_THAN_INLINE inline int HASH_PAIR_KEY(const int x, const int y, const int size) { return HASH_KEY(x*1434879443+y, size); } //_________________________________________________________________________ // Hash-only functions : table must NOT be Raw. // the argument 'size' is the total size of the hash table //_________________________________________________________________________ // copy hash table into raw vector inline void H_copy(int *mem, int *h, int size) { for(int i=HASH_EXPAND(size); i--; h++) if(*h != HASH_NONE) *(mem++)=*h; } // Look for the place to add an element. Return NULL if element is already here. inline int* H_add(int* h, const int size, int a) { _HASH_ADD_CALL(); _HASH_ADD_ITER(); int k = HASH_KEY(a, size); if(h[k]==HASH_NONE) return h+k; while(h[k]!=a) { _HASH_ADD_ITER(); k=HASH_REKEY(k,size); if(h[k]==HASH_NONE) return h+k; } return NULL; } // would element be well placed in newk ? inline bool H_better(const int a, const int size, const int currentk, const int newk) { int k = HASH_KEY(a, size); if(newk=newk); else return (k=newk); } // removes h[k] inline void H_rm(int* h, const int size, int k) { _HASH_RM_CALL(); int lasthole = k; do { _HASH_RM_ITER(); k = HASH_REKEY(k,size); int next = h[k]; if(next==HASH_NONE) break; if(H_better(next,size,k,lasthole)) { h[lasthole] = next; lasthole = k; } } while(true); h[lasthole] = HASH_NONE; } //put a inline int* H_put(int* h, const int size, const int a) { assert(H_add(h,size,a)!=NULL); _HASH_PUT_CALL(); _HASH_PUT_ITER(); int k = HASH_KEY(a, size); while(h[k]!=HASH_NONE) { k=HASH_REKEY(k,size); _HASH_PUT_ITER(); } h[k]=a; assert(H_add(h,size,a)==NULL); return h+k; } // find A inline int H_find(int *h, int size, const int a) { assert(H_add(h,size,a)==NULL); _HASH_FIND_CALL(); _HASH_FIND_ITER(); int k = HASH_KEY(a, size); while(h[k]!=a) { k=HASH_REKEY(k,size); _HASH_FIND_ITER(); } return k; } // Look for the place to add an element. Return NULL if element is already here. inline bool H_pair_insert(int* h, const int size, int a, int b) { _HASH_ADD_CALL(); _HASH_ADD_ITER(); int k = HASH_PAIR_KEY(a, b, size); if(h[2*k]==HASH_NONE) { h[2*k]=a; h[2*k+1]=b; return true; } while(h[2*k]!=a || h[2*k+1]!=b) { _HASH_ADD_ITER(); k=HASH_REKEY(k,size); if(h[2*k]==HASH_NONE) { h[2*k]=a; h[2*k+1]=b; return true; } } return false; } //_________________________________________________________________________ // Generic functions : table can be either Hash or Raw. // the argument 'size' is the number of elements //_________________________________________________________________________ // Look for an element inline bool H_is(int *mem, const int size, const int elem) { if(IS_HASH(size)) return (H_add(mem, HASH_EXPAND(size), elem)==NULL); else return fast_search(mem, size, elem)!=NULL; } //pick random location (containing an element) inline int* H_random(int* mem, int size) { if(!IS_HASH(size)) return mem+(my_random()%size); _HASH_RAND_CALL(); size = HASH_EXPAND(size); int* yo; do { yo = mem + HASH_KEY(my_random(),size); _HASH_RAND_ITER(); } while(*yo==HASH_NONE); return yo; } // replace *k by b inline int* H_rpl(int *mem, int size, int* k, const int b) { assert(!H_is(mem,size,b)); if(!IS_HASH(size)) { *k=b; return k; } else { size = HASH_EXPAND(size); assert(mem + int(k-mem) == k); H_rm(mem, size, int(k-mem)); return H_put(mem, size, b); } } // replace a by b inline int* H_rpl(int *mem, int size, const int a, const int b) { assert(H_is(mem,size,a)); assert(!H_is(mem,size,b)); if(!IS_HASH(size)) return fast_rpl(mem,a,b); else { size = HASH_EXPAND(size); H_rm(mem, size, H_find(mem, size, a)); return H_put(mem, size, b); } } } // namespace gengraph #endif //HASH_H igraph/src/glpapi15.c0000644000176000001440000004470312325527073014147 0ustar ripleyusers/* glpapi15.c (basic graph and network routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpapi.h" /* CAUTION: DO NOT CHANGE THE LIMITS BELOW */ #define NV_MAX 100000000 /* = 100*10^6 */ /* maximal number of vertices in the graph */ #define NA_MAX 500000000 /* = 500*10^6 */ /* maximal number of arcs in the graph */ /*********************************************************************** * NAME * * glp_create_graph - create graph * * SYNOPSIS * * glp_graph *glp_create_graph(int v_size, int a_size); * * DESCRIPTION * * The routine creates a new graph, which initially is empty, i.e. has * no vertices and arcs. * * The parameter v_size specifies the size of data associated with each * vertex of the graph (0 to 256 bytes). * * The parameter a_size specifies the size of data associated with each * arc of the graph (0 to 256 bytes). * * RETURNS * * The routine returns a pointer to the graph created. */ static void create_graph(glp_graph *G, int v_size, int a_size) { G->pool = dmp_create_pool(); G->name = NULL; G->nv_max = 50; G->nv = G->na = 0; G->v = xcalloc(1+G->nv_max, sizeof(glp_vertex *)); G->index = NULL; G->v_size = v_size; G->a_size = a_size; return; } glp_graph *glp_create_graph(int v_size, int a_size) { glp_graph *G; if (!(0 <= v_size && v_size <= 256)) xerror("glp_create_graph: v_size = %d; invalid size of vertex " "data\n", v_size); if (!(0 <= a_size && a_size <= 256)) xerror("glp_create_graph: a_size = %d; invalid size of arc dat" "a\n", a_size); G = xmalloc(sizeof(glp_graph)); create_graph(G, v_size, a_size); return G; } /*********************************************************************** * NAME * * glp_set_graph_name - assign (change) graph name * * SYNOPSIS * * void glp_set_graph_name(glp_graph *G, const char *name); * * DESCRIPTION * * The routine glp_set_graph_name assigns a symbolic name specified by * the character string name (1 to 255 chars) to the graph. * * If the parameter name is NULL or an empty string, the routine erases * the existing symbolic name of the graph. */ void glp_set_graph_name(glp_graph *G, const char *name) { if (G->name != NULL) { dmp_free_atom(G->pool, G->name, strlen(G->name)+1); G->name = NULL; } if (!(name == NULL || name[0] == '\0')) { int j; for (j = 0; name[j] != '\0'; j++) { if (j == 256) xerror("glp_set_graph_name: graph name too long\n"); if (iscntrl((unsigned char)name[j])) xerror("glp_set_graph_name: graph name contains invalid " "character(s)\n"); } G->name = dmp_get_atom(G->pool, strlen(name)+1); strcpy(G->name, name); } return; } /*********************************************************************** * NAME * * glp_add_vertices - add new vertices to graph * * SYNOPSIS * * int glp_add_vertices(glp_graph *G, int nadd); * * DESCRIPTION * * The routine glp_add_vertices adds nadd vertices to the specified * graph. New vertices are always added to the end of the vertex list, * so ordinal numbers of existing vertices remain unchanged. * * Being added each new vertex is isolated (has no incident arcs). * * RETURNS * * The routine glp_add_vertices returns an ordinal number of the first * new vertex added to the graph. */ int glp_add_vertices(glp_graph *G, int nadd) { int i, nv_new; if (nadd < 1) xerror("glp_add_vertices: nadd = %d; invalid number of vertice" "s\n", nadd); if (nadd > NV_MAX - G->nv) xerror("glp_add_vertices: nadd = %d; too many vertices\n", nadd); /* determine new number of vertices */ nv_new = G->nv + nadd; /* increase the room, if necessary */ if (G->nv_max < nv_new) { glp_vertex **save = G->v; while (G->nv_max < nv_new) { G->nv_max += G->nv_max; xassert(G->nv_max > 0); } G->v = xcalloc(1+G->nv_max, sizeof(glp_vertex *)); memcpy(&G->v[1], &save[1], G->nv * sizeof(glp_vertex *)); xfree(save); } /* add new vertices to the end of the vertex list */ for (i = G->nv+1; i <= nv_new; i++) { glp_vertex *v; G->v[i] = v = dmp_get_atom(G->pool, sizeof(glp_vertex)); v->i = i; v->name = NULL; v->entry = NULL; if (G->v_size == 0) v->data = NULL; else { v->data = dmp_get_atom(G->pool, G->v_size); memset(v->data, 0, G->v_size); } v->temp = NULL; v->in = v->out = NULL; } /* set new number of vertices */ G->nv = nv_new; /* return the ordinal number of the first vertex added */ return nv_new - nadd + 1; } /**********************************************************************/ void glp_set_vertex_name(glp_graph *G, int i, const char *name) { /* assign (change) vertex name */ glp_vertex *v; if (!(1 <= i && i <= G->nv)) xerror("glp_set_vertex_name: i = %d; vertex number out of rang" "e\n", i); v = G->v[i]; if (v->name != NULL) { if (v->entry != NULL) { xassert(G->index != NULL); avl_delete_node(G->index, v->entry); v->entry = NULL; } dmp_free_atom(G->pool, v->name, strlen(v->name)+1); v->name = NULL; } if (!(name == NULL || name[0] == '\0')) { int k; for (k = 0; name[k] != '\0'; k++) { if (k == 256) xerror("glp_set_vertex_name: i = %d; vertex name too lon" "g\n", i); if (iscntrl((unsigned char)name[k])) xerror("glp_set_vertex_name: i = %d; vertex name contain" "s invalid character(s)\n", i); } v->name = dmp_get_atom(G->pool, strlen(name)+1); strcpy(v->name, name); if (G->index != NULL) { xassert(v->entry == NULL); v->entry = avl_insert_node(G->index, v->name); avl_set_node_link(v->entry, v); } } return; } /*********************************************************************** * NAME * * glp_add_arc - add new arc to graph * * SYNOPSIS * * glp_arc *glp_add_arc(glp_graph *G, int i, int j); * * DESCRIPTION * * The routine glp_add_arc adds a new arc to the specified graph. * * The parameters i and j specify the ordinal numbers of, resp., tail * and head vertices of the arc. Note that self-loops and multiple arcs * are allowed. * * RETURNS * * The routine glp_add_arc returns a pointer to the arc added. */ glp_arc *glp_add_arc(glp_graph *G, int i, int j) { glp_arc *a; if (!(1 <= i && i <= G->nv)) xerror("glp_add_arc: i = %d; tail vertex number out of range\n" , i); if (!(1 <= j && j <= G->nv)) xerror("glp_add_arc: j = %d; head vertex number out of range\n" , j); if (G->na == NA_MAX) xerror("glp_add_arc: too many arcs\n"); a = dmp_get_atom(G->pool, sizeof(glp_arc)); a->tail = G->v[i]; a->head = G->v[j]; if (G->a_size == 0) a->data = NULL; else { a->data = dmp_get_atom(G->pool, G->a_size); memset(a->data, 0, G->a_size); } a->temp = NULL; a->t_prev = NULL; a->t_next = G->v[i]->out; if (a->t_next != NULL) a->t_next->t_prev = a; a->h_prev = NULL; a->h_next = G->v[j]->in; if (a->h_next != NULL) a->h_next->h_prev = a; G->v[i]->out = G->v[j]->in = a; G->na++; return a; } /*********************************************************************** * NAME * * glp_del_vertices - delete vertices from graph * * SYNOPSIS * * void glp_del_vertices(glp_graph *G, int ndel, const int num[]); * * DESCRIPTION * * The routine glp_del_vertices deletes vertices along with all * incident arcs from the specified graph. Ordinal numbers of vertices * to be deleted should be placed in locations num[1], ..., num[ndel], * ndel > 0. * * Note that deleting vertices involves changing ordinal numbers of * other vertices remaining in the graph. New ordinal numbers of the * remaining vertices are assigned under the assumption that the * original order of vertices is not changed. */ void glp_del_vertices(glp_graph *G, int ndel, const int num[]) { glp_vertex *v; int i, k, nv_new; /* scan the list of vertices to be deleted */ if (!(1 <= ndel && ndel <= G->nv)) xerror("glp_del_vertices: ndel = %d; invalid number of vertice" "s\n", ndel); for (k = 1; k <= ndel; k++) { /* take the number of vertex to be deleted */ i = num[k]; /* obtain pointer to i-th vertex */ if (!(1 <= i && i <= G->nv)) xerror("glp_del_vertices: num[%d] = %d; vertex number out o" "f range\n", k, i); v = G->v[i]; /* check that the vertex is not marked yet */ if (v->i == 0) xerror("glp_del_vertices: num[%d] = %d; duplicate vertex nu" "mbers not allowed\n", k, i); /* erase symbolic name assigned to the vertex */ glp_set_vertex_name(G, i, NULL); xassert(v->name == NULL); xassert(v->entry == NULL); /* free vertex data, if allocated */ if (v->data != NULL) dmp_free_atom(G->pool, v->data, G->v_size); /* delete all incoming arcs */ while (v->in != NULL) glp_del_arc(G, v->in); /* delete all outgoing arcs */ while (v->out != NULL) glp_del_arc(G, v->out); /* mark the vertex to be deleted */ v->i = 0; } /* delete all marked vertices from the vertex list */ nv_new = 0; for (i = 1; i <= G->nv; i++) { /* obtain pointer to i-th vertex */ v = G->v[i]; /* check if the vertex is marked */ if (v->i == 0) { /* it is marked, delete it */ dmp_free_atom(G->pool, v, sizeof(glp_vertex)); } else { /* it is not marked, keep it */ v->i = ++nv_new; G->v[v->i] = v; } } /* set new number of vertices in the graph */ G->nv = nv_new; return; } /*********************************************************************** * NAME * * glp_del_arc - delete arc from graph * * SYNOPSIS * * void glp_del_arc(glp_graph *G, glp_arc *a); * * DESCRIPTION * * The routine glp_del_arc deletes an arc from the specified graph. * The arc to be deleted must exist. */ void glp_del_arc(glp_graph *G, glp_arc *a) { /* some sanity checks */ xassert(G->na > 0); xassert(1 <= a->tail->i && a->tail->i <= G->nv); xassert(a->tail == G->v[a->tail->i]); xassert(1 <= a->head->i && a->head->i <= G->nv); xassert(a->head == G->v[a->head->i]); /* remove the arc from the list of incoming arcs */ if (a->h_prev == NULL) a->head->in = a->h_next; else a->h_prev->h_next = a->h_next; if (a->h_next == NULL) ; else a->h_next->h_prev = a->h_prev; /* remove the arc from the list of outgoing arcs */ if (a->t_prev == NULL) a->tail->out = a->t_next; else a->t_prev->t_next = a->t_next; if (a->t_next == NULL) ; else a->t_next->t_prev = a->t_prev; /* free arc data, if allocated */ if (a->data != NULL) dmp_free_atom(G->pool, a->data, G->a_size); /* delete the arc from the graph */ dmp_free_atom(G->pool, a, sizeof(glp_arc)); G->na--; return; } /*********************************************************************** * NAME * * glp_erase_graph - erase graph content * * SYNOPSIS * * void glp_erase_graph(glp_graph *G, int v_size, int a_size); * * DESCRIPTION * * The routine glp_erase_graph erases the content of the specified * graph. The effect of this operation is the same as if the graph * would be deleted with the routine glp_delete_graph and then created * anew with the routine glp_create_graph, with exception that the * handle (pointer) to the graph remains valid. */ static void delete_graph(glp_graph *G) { dmp_delete_pool(G->pool); xfree(G->v); if (G->index != NULL) avl_delete_tree(G->index); return; } void glp_erase_graph(glp_graph *G, int v_size, int a_size) { if (!(0 <= v_size && v_size <= 256)) xerror("glp_erase_graph: v_size = %d; invalid size of vertex d" "ata\n", v_size); if (!(0 <= a_size && a_size <= 256)) xerror("glp_erase_graph: a_size = %d; invalid size of arc data" "\n", a_size); delete_graph(G); create_graph(G, v_size, a_size); return; } /*********************************************************************** * NAME * * glp_delete_graph - delete graph * * SYNOPSIS * * void glp_delete_graph(glp_graph *G); * * DESCRIPTION * * The routine glp_delete_graph deletes the specified graph and frees * all the memory allocated to this program object. */ void glp_delete_graph(glp_graph *G) { delete_graph(G); xfree(G); return; } /**********************************************************************/ void glp_create_v_index(glp_graph *G) { /* create vertex name index */ glp_vertex *v; int i; if (G->index == NULL) { G->index = avl_create_tree(avl_strcmp, NULL); for (i = 1; i <= G->nv; i++) { v = G->v[i]; xassert(v->entry == NULL); if (v->name != NULL) { v->entry = avl_insert_node(G->index, v->name); avl_set_node_link(v->entry, v); } } } return; } int glp_find_vertex(glp_graph *G, const char *name) { /* find vertex by its name */ AVLNODE *node; int i = 0; if (G->index == NULL) xerror("glp_find_vertex: vertex name index does not exist\n"); if (!(name == NULL || name[0] == '\0' || strlen(name) > 255)) { node = avl_find_node(G->index, name); if (node != NULL) i = ((glp_vertex *)avl_get_node_link(node))->i; } return i; } void glp_delete_v_index(glp_graph *G) { /* delete vertex name index */ int i; if (G->index != NULL) { avl_delete_tree(G->index), G->index = NULL; for (i = 1; i <= G->nv; i++) G->v[i]->entry = NULL; } return; } /*********************************************************************** * NAME * * glp_read_graph - read graph from plain text file * * SYNOPSIS * * int glp_read_graph(glp_graph *G, const char *fname); * * DESCRIPTION * * The routine glp_read_graph reads a graph from a plain text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_read_graph(glp_graph *G, const char *fname) { glp_data *data; jmp_buf jump; int nv, na, i, j, k, ret; glp_erase_graph(G, G->v_size, G->a_size); xprintf("Reading graph from `%s'...\n", fname); data = glp_sdf_open_file(fname); if (data == NULL) { ret = 1; goto done; } if (setjmp(jump)) { ret = 1; goto done; } glp_sdf_set_jump(data, jump); nv = glp_sdf_read_int(data); if (nv < 0) glp_sdf_error(data, "invalid number of vertices\n"); na = glp_sdf_read_int(data); if (na < 0) glp_sdf_error(data, "invalid number of arcs\n"); xprintf("Graph has %d vert%s and %d arc%s\n", nv, nv == 1 ? "ex" : "ices", na, na == 1 ? "" : "s"); if (nv > 0) glp_add_vertices(G, nv); for (k = 1; k <= na; k++) { i = glp_sdf_read_int(data); if (!(1 <= i && i <= nv)) glp_sdf_error(data, "tail vertex number out of range\n"); j = glp_sdf_read_int(data); if (!(1 <= j && j <= nv)) glp_sdf_error(data, "head vertex number out of range\n"); glp_add_arc(G, i, j); } xprintf("%d lines were read\n", glp_sdf_line(data)); ret = 0; done: if (data != NULL) glp_sdf_close_file(data); return ret; } /*********************************************************************** * NAME * * glp_write_graph - write graph to plain text file * * SYNOPSIS * * int glp_write_graph(glp_graph *G, const char *fname). * * DESCRIPTION * * The routine glp_write_graph writes the specified graph to a plain * text file. * * RETURNS * * If the operation was successful, the routine returns zero. Otherwise * it prints an error message and returns non-zero. */ int glp_write_graph(glp_graph *G, const char *fname) { XFILE *fp; glp_vertex *v; glp_arc *a; int i, count, ret; xprintf("Writing graph to `%s'...\n", fname); fp = xfopen(fname, "w"), count = 0; if (fp == NULL) { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xfprintf(fp, "%d %d\n", G->nv, G->na), count++; for (i = 1; i <= G->nv; i++) { v = G->v[i]; for (a = v->out; a != NULL; a = a->t_next) xfprintf(fp, "%d %d\n", a->tail->i, a->head->i), count++; } xfflush(fp); if (xferror(fp)) { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); ret = 1; goto done; } xprintf("%d lines were written\n", count); ret = 0; done: if (fp != NULL) xfclose(fp); return ret; } /* eof */ igraph/src/zeroin.c0000644000176000001440000001522412325527074014030 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* from GNU R's zeroin.c, minor modifications by Gabor Csardi */ /* from NETLIB c/brent.shar with max.iter, add'l info and convergence details hacked in by Peter Dalgaard */ /************************************************************************* * C math library * function ZEROIN - obtain a function zero within the given range * * Input * double zeroin(ax,bx,f,info,Tol,Maxit) * double ax; Root will be seeked for within * double bx; a range [ax,bx] * double (*f)(double x, void *info); Name of the function whose zero * will be seeked for * void *info; Add'l info passed to f * double *Tol; Acceptable tolerance for the root * value. * May be specified as 0.0 to cause * the program to find the root as * accurate as possible * * int *Maxit; Max. iterations * * * Output * Zeroin returns an estimate for the root with accuracy * 4*EPSILON*abs(x) + tol * *Tol returns estimated precision * *Maxit returns actual # of iterations, or -1 if maxit was * reached without convergence. * * Algorithm * G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical * computations. M., Mir, 1980, p.180 of the Russian edition * * The function makes use of the bisection procedure combined with * the linear or quadric inverse interpolation. * At every step program operates on three abscissae - a, b, and c. * b - the last and the best approximation to the root * a - the last but one approximation * c - the last but one or even earlier approximation than a that * 1) |f(b)| <= |f(c)| * 2) f(b) and f(c) have opposite signs, i.e. b and c confine * the root * At every step Zeroin selects one of the two new approximations, the * former being obtained by the bisection procedure and the latter * resulting in the interpolation (if a,b, and c are all different * the quadric interpolation is utilized, otherwise the linear one). * If the latter (i.e. obtained by the interpolation) point is * reasonable (i.e. lies within the current interval [b,c] not being * too close to the boundaries) it is accepted. The bisection result * is used in the other case. Therefore, the range of uncertainty is * ensured to be reduced at least by the factor 1.6 * ************************************************************************ */ #include "igraph_types.h" #include "igraph_interrupt_internal.h" #include #include #define EPSILON DBL_EPSILON int igraph_zeroin( /* An estimate of the root */ igraph_real_t *ax, /* Left border | of the range */ igraph_real_t *bx, /* Right border| the root is seeked*/ igraph_real_t (*f)(igraph_real_t x, void *info), /* Function under investigation */ void *info, /* Add'l info passed on to f */ igraph_real_t *Tol, /* Acceptable tolerance */ int *Maxit, /* Max # of iterations */ igraph_real_t *res) /* Result is stored here */ { igraph_real_t a,b,c, /* Abscissae, descr. see above */ fa, fb, fc; /* f(a), f(b), f(c) */ igraph_real_t tol; int maxit; a = *ax; b = *bx; fa = (*f)(a, info); fb = (*f)(b, info); c = a; fc = fa; maxit = *Maxit + 1; tol = * Tol; /* First test if we have found a root at an endpoint */ if(fa == 0.0) { *Tol = 0.0; *Maxit = 0; *res=a; return 0; } if(fb == 0.0) { *Tol = 0.0; *Maxit = 0; *res=b; return 0; } while(maxit--) /* Main iteration loop */ { igraph_real_t prev_step = b-a; /* Distance from the last but one to the last approximation */ igraph_real_t tol_act; /* Actual tolerance */ igraph_real_t p; /* Interpolation step is calcu- */ igraph_real_t q; /* lated in the form p/q; divi- * sion operations is delayed * until the last moment */ igraph_real_t new_step; /* Step at this iteration */ IGRAPH_ALLOW_INTERRUPTION(); if( fabs(fc) < fabs(fb) ) { /* Swap data for b to be the */ a = b; b = c; c = a; /* best approximation */ fa=fb; fb=fc; fc=fa; } tol_act = 2*EPSILON*fabs(b) + tol/2; new_step = (c-b)/2; if( fabs(new_step) <= tol_act || fb == (igraph_real_t)0 ) { *Maxit -= maxit; *Tol = fabs(c-b); *res=b; return 0; /* Acceptable approx. is found */ } /* Decide if the interpolation can be tried */ if( fabs(prev_step) >= tol_act /* If prev_step was large enough*/ && fabs(fa) > fabs(fb) ) { /* and was in true direction, * Interpolation may be tried */ register igraph_real_t t1,cb,t2; cb = c-b; if( a==c ) { /* If we have only two distinct */ /* points linear interpolation */ t1 = fb/fa; /* can only be applied */ p = cb*t1; q = 1.0 - t1; } else { /* Quadric inverse interpolation*/ q = fa/fc; t1 = fb/fc; t2 = fb/fa; p = t2 * ( cb*q*(q-t1) - (b-a)*(t1-1.0) ); q = (q-1.0) * (t1-1.0) * (t2-1.0); } if( p>(igraph_real_t)0 ) /* p was calculated with the */ q = -q; /* opposite sign; make p positive */ else /* and assign possible minus to */ p = -p; /* q */ if( p < (0.75*cb*q-fabs(tol_act*q)/2) /* If b+p/q falls in [b,c]*/ && p < fabs(prev_step*q/2) ) /* and isn't too large */ new_step = p/q; /* it is accepted * If p/q is too large then the * bisection procedure can * reduce [b,c] range to more * extent */ } if( fabs(new_step) < tol_act) { /* Adjust the step to be not less*/ if( new_step > (igraph_real_t)0 ) /* than tolerance */ new_step = tol_act; else new_step = -tol_act; } a = b; fa = fb; /* Save the previous approx. */ b += new_step; fb = (*f)(b, info); /* Do step to a new approxim. */ if( (fb > 0 && fc > 0) || (fb < 0 && fc < 0) ) { /* Adjust c for it to have a sign opposite to that of b */ c = a; fc = fa; } } /* failed! */ *Tol = fabs(c-b); *Maxit = -1; *res=b; return IGRAPH_DIVERGED; } igraph/src/foreign-lgl-lexer.l0000644000176000001440000000623312325372071016050 0ustar ripleyusers/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include "foreign-lgl-header.h" #include "foreign-lgl-parser.h" #define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); %} %option noyywrap %option prefix="igraph_lgl_yy" %option outfile="lex.yy.c" %option nounput %option noinput %option reentrant %option bison-bridge %option bison-locations alnum [^ \t\r\n#] %% /* --------------------------------------------------hashmark------*/ # { return HASH; } /* ------------------------------------------------whitespace------*/ [ \t]* { } /* ---------------------------------------------------newline------*/ \n\r|\r\n|\n|\r { return NEWLINE; } /* ----------------------------------------------alphanumeric------*/ {alnum}+ { return ALNUM; } <> { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; return NEWLINE; } } %% igraph/src/igraph_separators.h0000644000176000001440000000336612325527073016247 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_SEPARATORS_H #define IGRAPH_SEPARATORS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_datatype.h" #include "igraph_iterators.h" __BEGIN_DECLS int igraph_is_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res); int igraph_all_minimal_st_separators(const igraph_t *graph, igraph_vector_ptr_t *separators); int igraph_is_minimal_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res); int igraph_minimum_size_separators(const igraph_t *graph, igraph_vector_ptr_t *separators); __END_DECLS #endif igraph/src/hrg_graph.h0000644000176000001440000001344012325527073014465 0ustar ripleyusers/* -*- mode: C++ -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // **************************************************************************************************** // *** COPYRIGHT NOTICE ******************************************************************************* // graph.h - graph data structure for hierarchical random graphs // Copyright (C) 2005-2008 Aaron Clauset // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.gnu.org/licenses/gpl.txt for more details. // // **************************************************************************************************** // Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) // Collaborators: Cristopher Moore and Mark E.J. Newman // Project : Hierarchical Random Graphs // Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute // Created : 8 November 2005 // Modified : 23 December 2007 (cleaned up for public consumption) // // **************************************************************************************************** // // Graph data structure for hierarchical random graphs. The basic structure is an adjacency list of // edges; however, many additional pieces of metadata are stored as well. Each node stores its // external name, its degree and (if assigned) its group index. // // **************************************************************************************************** #ifndef IGRAPH_HRG_GRAPH #define IGRAPH_HRG_GRAPH #include #include #include #include "hrg_rbtree.h" using namespace std; namespace fitHRG { // ******** Basic Structures ********************************************* #ifndef IGRAPH_HRG_EDGE #define IGRAPH_HRG_EDGE class edge { public: int x; // stored integer value (edge terminator) double* h; // (histogram) weights of edge existence double total_weight; // (histogram) total weight observed int obs_count; // number of observations in histogram edge* next; // pointer to next elementd edge(): x(-1), h(0), total_weight(0.0), obs_count(0), next(0) { } ~edge() { if (h != NULL) { delete [] h; } h = NULL; } }; #endif #ifndef IGRAPH_HRG_VERT #define IGRAPH_HRG_VERT class vert { public: string name; // (external) name of vertex int degree; // degree of this vertex vert(): name(""), degree(0) { } ~vert() { } }; #endif // ******** Graph Class with Edge Statistics ***************************** class graph { public: graph(const int, bool predict=false); ~graph(); // add (i,j) to graph bool addLink(const int, const int); // add weight to (i,j)'s histogram bool addAdjacencyObs(const int, const int, const double, const double); // add to obs_count and total_weight void addAdjacencyEnd(); // true if (i,j) is already in graph bool doesLinkExist(const int, const int); // returns degree of vertex i int getDegree(const int); // returns name of vertex i string getName(const int); // returns edge list of vertex i edge* getNeighborList(const int); // return ptr to histogram of edge (i,j) double* getAdjacencyHist(const int, const int); // return average value of adjacency A(i,j) double getAdjacencyAverage(const int, const int); // returns bin_resolution double getBinResolution(); // returns num_bins int getNumBins(); // returns m int numLinks(); // returns n int numNodes(); // returns total_weight double getTotalWeight(); // reset edge (i,j)'s histogram void resetAdjacencyHistogram(const int, const int); // reset all edge histograms void resetAllAdjacencies(); // clear all links from graph void resetLinks(); // allocate edge histograms void setAdjacencyHistograms(const int); // set name of vertex i bool setName(const int, const string); private: bool predict; // do we need prediction? vert* nodes; // list of nodes edge** nodeLink; // linked list of neighbors to vertex edge** nodeLinkTail; // pointers to tail of neighbor list double*** A; // stochastic adjacency matrix for this graph int obs_count; // number of observations in A double total_weight; // total weight added to A int n; // number of vertices int m; // number of directed edges int num_bins; // number of bins in edge histograms double bin_resolution; // width of histogram bin }; } // namespace fitHRG #endif igraph/src/prpack_preprocessed_gs_graph.cpp0000644000176000001440000000461612325527074020775 0ustar ripleyusers#include "prpack_preprocessed_gs_graph.h" #include using namespace prpack; using namespace std; void prpack_preprocessed_gs_graph::initialize() { heads = NULL; tails = NULL; vals = NULL; ii = NULL; d = NULL; num_outlinks = NULL; } void prpack_preprocessed_gs_graph::initialize_weighted(const prpack_base_graph* bg) { vals = new double[num_es]; d = new double[num_vs]; fill(d, d + num_vs, 1); for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { tails[tails_i] = heads_i; ii[tails_i] = 0; const int start_j = bg->tails[tails_i]; const int end_j = (tails_i + 1 != num_vs) ? bg->tails[tails_i + 1]: bg->num_es; for (int j = start_j; j < end_j; ++j) { if (tails_i == bg->heads[j]) ii[tails_i] += bg->vals[j]; else { heads[heads_i] = bg->heads[j]; vals[heads_i] = bg->vals[j]; ++heads_i; } d[bg->heads[j]] -= bg->vals[j]; } } } void prpack_preprocessed_gs_graph::initialize_unweighted(const prpack_base_graph* bg) { num_outlinks = new double[num_vs]; fill(num_outlinks, num_outlinks + num_vs, 0); for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { tails[tails_i] = heads_i; ii[tails_i] = 0; const int start_j = bg->tails[tails_i]; const int end_j = (tails_i + 1 != num_vs) ? bg->tails[tails_i + 1]: bg->num_es; for (int j = start_j; j < end_j; ++j) { if (tails_i == bg->heads[j]) ++ii[tails_i]; else heads[heads_i++] = bg->heads[j]; ++num_outlinks[bg->heads[j]]; } } for (int i = 0; i < num_vs; ++i) { if (num_outlinks[i] == 0) num_outlinks[i] = -1; ii[i] /= num_outlinks[i]; } } prpack_preprocessed_gs_graph::prpack_preprocessed_gs_graph(const prpack_base_graph* bg) { initialize(); num_vs = bg->num_vs; num_es = bg->num_es - bg->num_self_es; heads = new int[num_es]; tails = new int[num_vs]; ii = new double[num_vs]; if (bg->vals != NULL) initialize_weighted(bg); else initialize_unweighted(bg); } prpack_preprocessed_gs_graph::~prpack_preprocessed_gs_graph() { delete[] heads; delete[] tails; delete[] vals; delete[] ii; delete[] d; delete[] num_outlinks; } igraph/src/bliss_heap.cc0000644000176000001440000000401312325527072014766 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include #include #include #include "bliss_defs.hh" #include "bliss_heap.hh" using namespace std; namespace igraph { Heap::~Heap() { if(array) { free(array); array = 0; } } void Heap::upheap(unsigned int index) { assert(n >= 1); assert(index >= 1 && index <= n); const unsigned int v = array[index]; array[0] = UINT_MAX; while(array[index/2] <= v) { array[index] = array[index/2]; index = index/2; } array[index] = v; } void Heap::downheap(unsigned int index) { const unsigned int v = array[index]; while(index <= n/2) { unsigned int new_index = index + index; if(new_index < n && array[new_index] < array[new_index+1]) new_index++; if(v >= array[new_index]) break; array[index] = array[new_index]; index = new_index; } array[index] = v; } void Heap::init(unsigned int size) { array = (unsigned int*)malloc((size + 1) * sizeof(unsigned int)); n = 0; #if defined(CONSISTENCY_CHECKS) assert(size > 0); N = size; #endif } void Heap::insert(unsigned int v) { DEBUG_ASSERT(n < N); array[++n] = v; upheap(n); } unsigned int Heap::remove() { DEBUG_ASSERT(n >= 1 && n <= N); const unsigned int v = array[1]; array[1] = array[n--]; downheap(1); return v; } } igraph/src/prpack/0000755000176000001440000000000012325555115013627 5ustar ripleyusersigraph/src/prpack/prpack_edge_list.h0000644000176000001440000000034712325527074017306 0ustar ripleyusers#ifndef PRPACK_EDGE_LIST #define PRPACK_EDGE_LIST namespace prpack { class prpack_edge_list { public: int num_vs; int num_es; int* heads; int* tails; }; }; #endif igraph/src/prpack/prpack_utils.h0000644000176000001440000000171612325527074016510 0ustar ripleyusers#ifndef PRPACK_UTILS #define PRPACK_UTILS #ifdef MATLAB_MEX_FILE #include "mex.h" #endif #include // Computes the time taken to do X and stores it in T. #define TIME(T, X) \ (T) = prpack_utils::get_time(); \ (X); \ (T) = prpack_utils::get_time() - (T) // Computes S += A using C as a carry-over. // This is a macro over a function as it is faster this way. #define COMPENSATED_SUM(S, A, C) \ double compensated_sum_y = (A) - (C); \ double compensated_sum_t = (S) + compensated_sum_y; \ (C) = compensated_sum_t - (S) - compensated_sum_y; \ (S) = compensated_sum_t namespace prpack { class prpack_utils { public: static double get_time(); static void validate(const bool condition, const std::string& msg); static double* permute(const int length, const double* a, const int* coding); }; }; #endif igraph/src/prpack/prpack_preprocessed_schur_graph.h0000644000176000001440000000167412325527074022436 0ustar ripleyusers#ifndef PRPACK_PREPROCESSED_SCHUR_GRAPH #define PRPACK_PREPROCESSED_SCHUR_GRAPH #include "prpack_preprocessed_graph.h" #include "prpack_base_graph.h" namespace prpack { class prpack_preprocessed_schur_graph : public prpack_preprocessed_graph { private: // helper methods void initialize(); void initialize_weighted(const prpack_base_graph* bg); void initialize_unweighted(const prpack_base_graph* bg); public: // instance variables int num_no_in_vs; int num_no_out_vs; int* heads; int* tails; double* vals; double* ii; double* num_outlinks; int* encoding; int* decoding; // constructors prpack_preprocessed_schur_graph(const prpack_base_graph* bg); // destructor ~prpack_preprocessed_schur_graph(); }; }; #endif igraph/src/prpack/prpack_solver.h0000644000176000001440000001547212325527074016666 0ustar ripleyusers#ifndef PRPACK_SOLVER #define PRPACK_SOLVER #include "prpack_base_graph.h" #include "prpack_csc.h" #include "prpack_csr.h" #include "prpack_edge_list.h" #include "prpack_preprocessed_ge_graph.h" #include "prpack_preprocessed_gs_graph.h" #include "prpack_preprocessed_scc_graph.h" #include "prpack_preprocessed_schur_graph.h" #include "prpack_result.h" // TODO Make this a user configurable variable #define PRPACK_SOLVER_MAX_ITERS 1000000 namespace prpack { // Solver class. class prpack_solver { private: // instance variables double read_time; prpack_base_graph* bg; prpack_preprocessed_ge_graph* geg; prpack_preprocessed_gs_graph* gsg; prpack_preprocessed_schur_graph* sg; prpack_preprocessed_scc_graph* sccg; bool owns_bg; // methods void initialize(); static prpack_result* solve_via_ge( const double alpha, const double tol, const int num_vs, const double* matrix, const double* uv); static prpack_result* solve_via_ge_uv( const double alpha, const double tol, const int num_vs, const double* matrix, const double* d, const double* u, const double* v); static prpack_result* solve_via_gs( const double alpha, const double tol, const int num_vs, const int num_es, const int* heads, const int* tails, const double* vals, const double* ii, const double* d, const double* num_outlinks, const double* u, const double* v); static prpack_result* solve_via_gs_err( const double alpha, const double tol, const int num_vs, const int num_es, const int* heads, const int* tails, const double* ii, const double* num_outlinks, const double* u, const double* v); static prpack_result* solve_via_schur_gs( const double alpha, const double tol, const int num_vs, const int num_no_in_vs, const int num_no_out_vs, const int num_es, const int* heads, const int* tails, const double* vals, const double* ii, const double* d, const double* num_outlinks, const double* uv, const int* encoding, const int* decoding, const bool should_normalize = true); static prpack_result* solve_via_schur_gs_uv( const double alpha, const double tol, const int num_vs, const int num_no_in_vs, const int num_no_out_vs, const int num_es, const int* heads, const int* tails, const double* vals, const double* ii, const double* d, const double* num_outlinks, const double* u, const double* v, const int* encoding, const int* decoding); static prpack_result* solve_via_scc_gs( const double alpha, const double tol, const int num_vs, const int num_es_inside, const int* heads_inside, const int* tails_inside, const double* vals_inside, const int num_es_outside, const int* heads_outside, const int* tails_outside, const double* vals_outside, const double* ii, const double* d, const double* num_outlinks, const double* uv, const int num_comps, const int* divisions, const int* encoding, const int* decoding, const bool should_normalize = true); static prpack_result* solve_via_scc_gs_uv( const double alpha, const double tol, const int num_vs, const int num_es_inside, const int* heads_inside, const int* tails_inside, const double* vals_inside, const int num_es_outside, const int* heads_outside, const int* tails_outside, const double* vals_outside, const double* ii, const double* d, const double* num_outlinks, const double* u, const double* v, const int num_comps, const int* divisions, const int* encoding, const int* decoding); static void ge(const int sz, double* A, double* b); static void normalize(const int length, double* x); static prpack_result* combine_uv( const int num_vs, const double* d, const double* num_outlinks, const int* encoding, const double alpha, const prpack_result* ret_u, const prpack_result* ret_v); public: // constructors prpack_solver(const prpack_csc* g); prpack_solver(const prpack_int64_csc* g); prpack_solver(const prpack_csr* g); prpack_solver(const prpack_edge_list* g); prpack_solver(prpack_base_graph* g, bool owns_bg=true); prpack_solver(const char* filename, const char* format, const bool weighted); // destructor ~prpack_solver(); // methods int get_num_vs(); prpack_result* solve(const double alpha, const double tol, const char* method); prpack_result* solve( const double alpha, const double tol, const double* u, const double* v, const char* method); }; }; #endif igraph/src/prpack/prpack.h0000644000176000001440000000031212325527074015257 0ustar ripleyusers#ifndef PRPACK #define PRPACK #include "prpack_csc.h" #include "prpack_csr.h" #include "prpack_edge_list.h" #include "prpack_base_graph.h" #include "prpack_solver.h" #include "prpack_result.h" #endif igraph/src/prpack/prpack_preprocessed_graph.h0000644000176000001440000000051612325527074021224 0ustar ripleyusers#ifndef PRPACK_PREPROCESSED_GRAPH #define PRPACK_PREPROCESSED_GRAPH namespace prpack { // TODO: this class should not be seeable by the users of the library. // Super graph class. class prpack_preprocessed_graph { public: int num_vs; int num_es; double* d; }; }; #endif igraph/src/prpack/prpack_csr.h0000644000176000001440000000032512325527074016132 0ustar ripleyusers#ifndef PRPACK_CSR #define PRPACK_CSR namespace prpack { class prpack_csr { public: int num_vs; int num_es; int* heads; int* tails; }; }; #endif igraph/src/prpack/prpack_csc.h0000644000176000001440000000061612325527074016116 0ustar ripleyusers#ifndef PRPACK_CSC #define PRPACK_CSC #include namespace prpack { class prpack_csc { public: int num_vs; int num_es; int* heads; int* tails; }; class prpack_int64_csc { public: int64_t num_vs; int64_t num_es; int64_t* heads; int64_t* tails; }; }; #endif igraph/src/prpack/prpack_preprocessed_scc_graph.h0000644000176000001440000000220312325527074022047 0ustar ripleyusers#ifndef PRPACK_PREPROCESSED_SCC_GRAPH #define PRPACK_PREPROCESSED_SCC_GRAPH #include "prpack_preprocessed_graph.h" #include "prpack_base_graph.h" namespace prpack { // Pre-processed graph class class prpack_preprocessed_scc_graph : public prpack_preprocessed_graph { private: // helper methods void initialize(); void initialize_weighted(const prpack_base_graph* bg); void initialize_unweighted(const prpack_base_graph* bg); public: // instance variables int num_es_inside; int* heads_inside; int* tails_inside; double* vals_inside; int num_es_outside; int* heads_outside; int* tails_outside; double* vals_outside; double* ii; double* num_outlinks; int num_comps; int* divisions; int* encoding; int* decoding; // constructors prpack_preprocessed_scc_graph(const prpack_base_graph* bg); // destructor ~prpack_preprocessed_scc_graph(); }; }; #endif igraph/src/prpack/prpack_igraph_graph.h0000644000176000001440000000075012325527074020000 0ustar ripleyusers#ifndef PRPACK_IGRAPH_GRAPH #define PRPACK_IGRAPH_GRAPH #ifdef PRPACK_IGRAPH_SUPPORT #include "igraph_interface.h" #include "prpack_base_graph.h" namespace prpack { class prpack_igraph_graph : public prpack_base_graph { public: // constructors explicit prpack_igraph_graph(const igraph_t* g, const igraph_vector_t* weights = 0, igraph_bool_t directed = true); }; }; // PRPACK_IGRAPH_SUPPORT #endif // PRPACK_IGRAPH_GRAPH #endif igraph/src/prpack/prpack_preprocessed_ge_graph.h0000644000176000001440000000136312325527074021700 0ustar ripleyusers#ifndef PRPACK_PREPROCESSED_GE_GRAPH #define PRPACK_PREPROCESSED_GE_GRAPH #include "prpack_preprocessed_graph.h" #include "prpack_base_graph.h" namespace prpack { // Pre-processed graph class class prpack_preprocessed_ge_graph : public prpack_preprocessed_graph { private: // helper methods void initialize(); void initialize_weighted(const prpack_base_graph* bg); void initialize_unweighted(const prpack_base_graph* bg); public: // instance variables double* matrix; // constructors prpack_preprocessed_ge_graph(const prpack_base_graph* bg); // destructor ~prpack_preprocessed_ge_graph(); }; }; #endif igraph/src/prpack/prpack_base_graph.h0000644000176000001440000000241012325527074017433 0ustar ripleyusers#ifndef PRPACK_ADJACENCY_LIST #define PRPACK_ADJACENCY_LIST #include "prpack_csc.h" #include "prpack_csr.h" #include "prpack_edge_list.h" #include #include namespace prpack { class prpack_base_graph { private: // helper methods void initialize(); void read_smat(std::FILE* f, const bool weighted); void read_edges(std::FILE* f); void read_ascii(std::FILE* f); public: // instance variables int num_vs; int num_es; int num_self_es; int* heads; int* tails; double* vals; // constructors prpack_base_graph(); // only to support inheritance prpack_base_graph(const prpack_csc* g); prpack_base_graph(const prpack_int64_csc* g); prpack_base_graph(const prpack_csr* g); prpack_base_graph(const prpack_edge_list* g); prpack_base_graph(const char* filename, const char* format, const bool weighted); prpack_base_graph(int nverts, int nedges, std::pair* edges); // destructor ~prpack_base_graph(); // operations void normalize_weights(); }; }; #endif igraph/src/prpack/prpack_result.h0000644000176000001440000000105312325527074016660 0ustar ripleyusers#ifndef PRPACK_RESULT #define PRPACK_RESULT namespace prpack { // Result class. class prpack_result { public: // instance variables int num_vs; int num_es; double* x; double read_time; double preprocess_time; double compute_time; long num_es_touched; const char* method; int converged; // constructor prpack_result(); // destructor ~prpack_result(); }; }; #endif igraph/src/prpack/prpack_preprocessed_gs_graph.h0000644000176000001440000000153312325527074021715 0ustar ripleyusers#ifndef PRPACK_PREPROCESSED_GS_GRAPH #define PRPACK_PREPROCESSED_GS_GRAPH #include "prpack_preprocessed_graph.h" #include "prpack_base_graph.h" namespace prpack { // Pre-processed graph class class prpack_preprocessed_gs_graph : public prpack_preprocessed_graph { private: // helper methods void initialize(); void initialize_weighted(const prpack_base_graph* bg); void initialize_unweighted(const prpack_base_graph* bg); public: // instance variables int* heads; int* tails; double* vals; double* ii; double* num_outlinks; // constructors prpack_preprocessed_gs_graph(const prpack_base_graph* bg); // destructor ~prpack_preprocessed_gs_graph(); }; }; #endif igraph/src/triangles.c0000644000176000001440000007361612325527074014523 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_transitivity.h" #include "igraph_interface.h" #include "igraph_adjlist.h" #include "igraph_memory.h" #include "igraph_interrupt_internal.h" #include "igraph_centrality.h" #include "igraph_motifs.h" /** * \function igraph_transitivity_avglocal_undirected * \brief Average local transitivity (clustering coefficient). * * The transitivity measures the probability that two neighbors of a * vertex are connected. In case of the average local transitivity, * this probability is calculated for each vertex and then the average * is taken. Vertices with less than two neighbors require special treatment, * they will either be left out from the calculation or they will be considered * as having zero transitivity, depending on the \c mode argument. * * * Note that this measure is different from the global transitivity measure * (see \ref igraph_transitivity_undirected() ) as it simply takes the * average local transitivity across the whole network. See the following * reference for more details: * * * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks. * Nature 393(6684):440-442 (1998). * * * Clustering coefficient is an alternative name for transitivity. * * \param graph The input graph, directed graphs are considered as * undirected ones. * \param res Pointer to a real variable, the result will be stored here. * \param mode Defines how to treat vertices with degree less than two. * \c IGRAPH_TRANSITIVITY_NAN leaves them out from averaging, * \c IGRAPH_TRANSITIVITY_ZERO includes them with zero transitivity. * The result will be \c NaN if the mode is \c IGRAPH_TRANSITIVITY_NAN * and there are no vertices with more than one neighbor. * * \return Error code. * * \sa \ref igraph_transitivity_undirected(), \ref * igraph_transitivity_local_undirected(). * * Time complexity: O(|V|*d^2), |V| is the number of vertices in the * graph and d is the average degree. */ int igraph_transitivity_avglocal_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_real_t sum=0.0; igraph_integer_t count=0; long int node, i, j, nn; igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; long int neilen1, neilen2; igraph_integer_t triples; long int *neis; long int maxdegree; igraph_vector_t order; igraph_vector_t rank; igraph_vector_t degree; igraph_vector_t triangles; IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); maxdegree=(long int) igraph_vector_max(°ree)+1; igraph_vector_order1(°ree, &order, maxdegree); igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes); for (i=0; i= 0; nn--) { node=(long int) VECTOR(order)[nn]; IGRAPH_ALLOW_INTERRUPTION(); neis1=igraph_adjlist_get(&allneis, node); neilen1=igraph_vector_int_size(neis1); triples = (igraph_integer_t) ((double)neilen1 * (neilen1-1) / 2); /* Mark the neighbors of 'node' */ for (i=0; i VECTOR(rank)[node]) { neis2=igraph_adjlist_get(&allneis, nei); neilen2=igraph_vector_int_size(neis2); for (j=0; j maxdegree) { maxdegree = deg; } } igraph_vector_order1(°ree, &order, maxdegree+1); igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); IGRAPH_VECTOR_INIT_FINALLY(&rank, affected_nodes); for (i=0; i=0; nn--) { long int node=(long int) VECTOR(avids) [ (long int) VECTOR(order)[nn] ]; igraph_vector_t *neis1, *neis2; long int neilen1, neilen2; long int nodeindex=(long int) VECTOR(indexv)[node]; long int noderank=(long int) VECTOR(rank) [nodeindex-1]; /* fprintf(stderr, "node %li (indexv %li, rank %li)\n", node, */ /* (long int)VECTOR(indexv)[node]-1, noderank); */ IGRAPH_ALLOW_INTERRUPTION(); neis1=igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); neilen1=igraph_vector_size(neis1); for (i=0; i noderank) { neis2=igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) nei); neilen2=igraph_vector_size(neis2); for (j=0; j nei2) { */ /* l2++; */ /* } else { */ /* triangles+=1; */ /* l1++; l2++; */ /* } */ /* } */ /* } */ /* /\* We're done with 'node' *\/ */ /* VECTOR(*res)[i] = triangles / triples; */ /* } */ /* igraph_lazy_adjlist_destroy(&adjlist); */ /* igraph_vit_destroy(&vit); */ /* IGRAPH_FINALLY_CLEAN(2); */ /* return 0; */ /* } */ /* This removes loop, multiple edges and edges that point "backwards" according to the rank vector. */ int igraph_i_trans4_al_simplify(igraph_adjlist_t *al, const igraph_vector_int_t *rank) { long int i; long int n=al->length; igraph_vector_int_t mark; igraph_vector_int_init(&mark, n); IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); for (i=0; iadjs[i]; int j, l=igraph_vector_int_size(v); int irank=VECTOR(*rank)[i]; VECTOR(mark)[i] = i+1; for (j=0; j irank && VECTOR(mark)[e] != i+1) { VECTOR(mark)[e]=i+1; j++; } else { VECTOR(*v)[j] = igraph_vector_int_tail(v); igraph_vector_int_pop_back(v); l--; } } } igraph_vector_int_destroy(&mark); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_transitivity_local_undirected4(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode) { #define TRIPLES 1 #define TRANSIT 1 #include "triangles_template.h" #undef TRIPLES #undef TRANSIT return 0; } /** * \function igraph_transitivity_local_undirected * \brief Calculates the local transitivity (clustering coefficient) of a graph. * * The transitivity measures the probability that two neighbors of a * vertex are connected. In case of the local transitivity, this * probability is calculated separately for each vertex. * * * Note that this measure is different from the global transitivity measure * (see \ref igraph_transitivity_undirected() ) as it calculates a transitivity * value for each vertex individually. See the following reference for more * details: * * * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks. * Nature 393(6684):440-442 (1998). * * * Clustering coefficient is an alternative name for transitivity. * * \param graph The input graph, it can be directed but direction of * the edges will be ignored. * \param res Pointer to an initialized vector, the result will be * stored here. It will be resized as needed. * \param vids Vertex set, the vertices for which the local * transitivity will be calculated. * \param mode Defines how to treat vertices with degree less than two. * \c IGRAPH_TRANSITIVITY_NAN returns \c NaN for these vertices, * \c IGRAPH_TRANSITIVITY_ZERO returns zero. * \return Error code. * * \sa \ref igraph_transitivity_undirected(), \ref * igraph_transitivity_avglocal_undirected(). * * Time complexity: O(n*d^2), n is the number of vertices for which * the transitivity is calculated, d is the average vertex degree. */ int igraph_transitivity_local_undirected(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode) { if (igraph_vs_is_all(&vids)) { return igraph_transitivity_local_undirected4(graph, res, vids, mode); } else { igraph_vit_t vit; long int size; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); size=IGRAPH_VIT_SIZE(vit); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); if (size < 100) { return igraph_transitivity_local_undirected1(graph, res, vids, mode); } else { return igraph_transitivity_local_undirected2(graph, res, vids, mode); } } return 0; } int igraph_adjacent_triangles1(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids) { # include "triangles_template1.h" return 0; } int igraph_adjacent_triangles4(const igraph_t *graph, igraph_vector_t *res) { # include "triangles_template.h" return 0; } int igraph_adjacent_triangles(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids) { if (igraph_vs_is_all(&vids)) { return igraph_adjacent_triangles4(graph, res); } else { return igraph_adjacent_triangles1(graph, res, vids); } return 0; } /** * \ingroup structural * \function igraph_transitivity_undirected * \brief Calculates the transitivity (clustering coefficient) of a graph. * * * The transitivity measures the probability that two neighbors of a * vertex are connected. More precisely, this is the ratio of the * triangles and connected triples in the graph, the result is a * single real number. Directed graphs are considered as undirected ones. * * * Note that this measure is different from the local transitivity measure * (see \ref igraph_transitivity_local_undirected() ) as it calculates a single * value for the whole graph. See the following reference for more details: * * * S. Wasserman and K. Faust: Social Network Analysis: Methods and * Applications. Cambridge: Cambridge University Press, 1994. * * * Clustering coefficient is an alternative name for transitivity. * * \param graph The graph object. * \param res Pointer to a real variable, the result will be stored here. * \param mode Defines how to treat graphs with no connected triples. * \c IGRAPH_TRANSITIVITY_NAN returns \c NaN in this case, * \c IGRAPH_TRANSITIVITY_ZERO returns zero. * \return Error code: * \c IGRAPH_ENOMEM: not enough memory for * temporary data. * * \sa \ref igraph_transitivity_local_undirected(), * \ref igraph_transitivity_avglocal_undirected(). * * Time complexity: O(|V|*d^2), |V| is the number of vertices in * the graph, d is the average node degree. * * \example examples/simple/igraph_transitivity.c */ int igraph_transitivity_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode) { long int no_of_nodes=igraph_vcount(graph); igraph_real_t triples=0, triangles=0; long int node, nn; long int maxdegree; long int *neis; igraph_vector_t order; igraph_vector_t rank; igraph_vector_t degree; igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; long int i, j, neilen1, neilen2; IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); maxdegree=(long int) igraph_vector_max(°ree)+1; igraph_vector_order1(°ree, &order, maxdegree); igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes); for (i=0; i=0; nn--) { node=(long int) VECTOR(order)[nn]; IGRAPH_ALLOW_INTERRUPTION(); neis1=igraph_adjlist_get(&allneis, node); neilen1=igraph_vector_int_size(neis1); triples += (double)neilen1 * (neilen1-1); /* Mark the neighbors of 'node' */ for (i=0; i VECTOR(rank)[node]) { neis2=igraph_adjlist_get(&allneis, nei); neilen2=igraph_vector_int_size(neis2); for (j=0; j=0; nn--) { long int adjlen1, adjlen2; igraph_real_t triples; long int node=(long int) VECTOR(order)[nn]; IGRAPH_ALLOW_INTERRUPTION(); adj1=igraph_inclist_get(&incident, node); adjlen1=igraph_vector_size(adj1); triples = VECTOR(degree)[node] * (adjlen1-1) / 2.0; /* Mark the neighbors of the node */ for (i=0; i VECTOR(rank)[node]) { adj2=igraph_inclist_get(&incident, nei); adjlen2=igraph_vector_size(adj2); for (j=0; j This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_MICROSCOPIC_UPDATE_H #define IGRAPH_MICROSCOPIC_UPDATE_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_datatype.h" #include "igraph_iterators.h" #include "igraph_types.h" #include "igraph_vector.h" __BEGIN_DECLS int igraph_deterministic_optimal_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_optimal_t optimality, const igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode); int igraph_moran_process(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode); int igraph_roulette_wheel_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_bool_t islocal, const igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode); int igraph_stochastic_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_imitate_algorithm_t algo, const igraph_vector_t *quantities, igraph_vector_t *strategies, igraph_neimode_t mode); __END_DECLS #endif igraph/src/cs_happly.c0000644000176000001440000000273312325527073014504 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* apply the ith Householder vector to x */ CS_INT cs_happly (const cs *V, CS_INT i, double beta, CS_ENTRY *x) { CS_INT p, *Vp, *Vi ; CS_ENTRY *Vx, tau = 0 ; if (!CS_CSC (V) || !x) return (0) ; /* check inputs */ Vp = V->p ; Vi = V->i ; Vx = V->x ; for (p = Vp [i] ; p < Vp [i+1] ; p++) /* tau = v'*x */ { tau += CS_CONJ (Vx [p]) * x [Vi [p]] ; } tau *= beta ; /* tau = beta*(v'*x) */ for (p = Vp [i] ; p < Vp [i+1] ; p++) /* x = x - v*tau */ { x [Vi [p]] -= Vx [p] * tau ; } return (1) ; } igraph/src/Light.cpp0000755000176000001440000000126412325527072014131 0ustar ripleyusers#include "Light.h" #include "unit_limiter.h" namespace igraph { Light::Light() : mLightPoint(0,0,0) { mIntensity = 0.1; } Light::Light(const Point& rLightPoint) : mLightPoint(rLightPoint) { mIntensity = 0.1; } Light::~Light() {} const Point& Light::LightPoint() const { return mLightPoint; } void Light::LightPoint(const Point& rLightPoint) { mLightPoint = rLightPoint; } double Light::Intensity() const { return mIntensity; } void Light::Intensity(double vIntensity) { mIntensity = unit_limiter(vIntensity); } const Color& Light::LightColor() const { return mLightColor; } void Light::LightColor(const Color& rLightColor) { mLightColor = rLightColor; } } // namespace igraph igraph/src/gengraph_random.h0000644000176000001440000001643012325527073015661 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef RNG_H #define RNG_H #include "igraph_random.h" #include using namespace std; namespace KW_RNG { typedef signed int sint; typedef unsigned int uint; typedef signed long slong; typedef unsigned long ulong; class RNG { public: RNG() { } RNG(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) { IGRAPH_UNUSED(z_); IGRAPH_UNUSED(w_); IGRAPH_UNUSED(jsr_); IGRAPH_UNUSED(jcong_); }; ~RNG() { } void init(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) { IGRAPH_UNUSED(z_); IGRAPH_UNUSED(w_); IGRAPH_UNUSED(jsr_); IGRAPH_UNUSED(jcong_); } long rand_int31() { return RNG_INT31(); } double rand_halfopen01() // (0,1] { return RNG_UNIF01(); } int binomial(double pp, int n) { return RNG_BINOM(n,pp); } }; } // namespace KW_RNG /* This was the original RNG, but now we use the igraph version */ // __________________________________________________________________________ // random.h - a Random Number Generator Class // random.cpp - contains the non-inline class methods // __________________________________________________________________________ // This C++ code uses the simple, very fast "KISS" (Keep It Simple // Stupid) random number generator suggested by George Marsaglia in a // Usenet posting from 1999. He describes it as "one of my favorite // generators". It generates high-quality random numbers that // apparently pass all commonly used tests for randomness. In fact, it // generates random numbers by combining the results of three other good // random number generators that have different periods and are // constructed from completely different algorithms. It does not have // the ultra-long period of some other generators - a "problem" that can // be fixed fairly easily - but that seems to be its only potential // problem. The period is about 2^123. // The ziggurat method of Marsaglia is used to generate exponential and // normal variates. The method as well as source code can be found in // the article "The Ziggurat Method for Generating Random Variables" by // Marsaglia and Tsang, Journal of Statistical Software 5, 2000. // The method for generating gamma variables appears in "A Simple Method // for Generating Gamma Variables" by Marsaglia and Tsang, ACM // Transactions on Mathematical Software, Vol. 26, No 3, Sep 2000, pages // 363-372. // The code for Poisson and Binomial random numbers comes from // Numerical Recipes in C. // Some of this code is unlikely to work correctly as is on 64 bit // machines. // #include // #include // #ifdef _WIN32 // #include // #define getpid _getpid // #else // #include // #endif // //#ifdef _WIN32 // static const double PI = 3.1415926535897932; // static const double AD_l = 0.6931471805599453; // static const double AD_a = 5.7133631526454228; // static const double AD_b = 3.4142135623730950; // static const double AD_c = -1.6734053240284925; // static const double AD_p = 0.9802581434685472; // static const double AD_A = 5.6005707569738080; // static const double AD_B = 3.3468106480569850; // static const double AD_H = 0.0026106723602095; // static const double AD_D = 0.0857864376269050; // //#endif //_WIN32 // namespace KW_RNG { // class RNG // { // private: // ulong z, w, jsr, jcong; // Seeds // ulong kn[128], ke[256]; // double wn[128],fn[128], we[256],fe[256]; // /* // #ifndef _WIN32 // static const double PI = 3.1415926535897932; // static const double AD_l = 0.6931471805599453; // static const double AD_a = 5.7133631526454228; // static const double AD_b = 3.4142135623730950; // static const double AD_c = -1.6734053240284925; // static const double AD_p = 0.9802581434685472; // static const double AD_A = 5.6005707569738080; // static const double AD_B = 3.3468106480569850; // static const double AD_H = 0.0026106723602095; // static const double AD_D = 0.0857864376269050; // #endif //_WIN32 // */ // public: // RNG() { init(); zigset(); } // RNG(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) : // z(z_), w(w_), jsr(jsr_), jcong(jcong_) { zigset(); } // ~RNG() { } // inline ulong znew() // { return (z = 36969 * (z & 65535) + (z >> 16)); } // inline ulong wnew() // { return (w = 18000 * (w & 65535) + (w >> 16)); } // inline ulong MWC() // { return (((znew() & 65535) << 16) + wnew()); } // inline ulong SHR3() // { jsr ^= ((jsr & 32767) << 17); jsr ^= (jsr >> 13); return (jsr ^= ((jsr << 5) & 0xFFFFFFFF)); } // inline ulong CONG() // { return (jcong = (69069 * jcong + 1234567) & 0xFFFFFFFF); } // inline double RNOR() { // slong h = rand_int32(); // ulong i = h & 127; // return (((ulong) abs((sint) h) < kn[i]) ? h * wn[i] : nfix(h, i)); // } // inline double REXP() { // ulong j = rand_int32(); // ulong i = j & 255; // return ((j < ke[i]) ? j * we[i] : efix(j, i)); // } // double nfix(slong h, ulong i); // double efix(ulong j, ulong i); // void zigset(); // inline void init() // { ulong yo = time(0) + getpid(); // z = w = jsr = jcong = yo; } // inline void init(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) // { z = z_; w = w_; jsr = jsr_; jcong = jcong_; } // inline ulong rand_int32() // [0,2^32-1] // { return ((MWC() ^ CONG()) + SHR3()) & 0xFFFFFFFF; } // inline long rand_int31() // [0,2^31-1] // { return long(rand_int32() >> 1);} // inline double rand_closed01() // [0,1] // { return ((double) rand_int32() / 4294967295.0); } // inline double rand_open01() // (0,1) // { return (((double) rand_int32() + 0.5) / 4294967296.0); } // inline double rand_halfclosed01() // [0,1) // { return ((double) rand_int32() / 4294967296.0); } // inline double rand_halfopen01() // (0,1] // { return (((double) rand_int32() + 0.5) / 4294967295.5); } // // Continuous Distributions // inline double uniform(double x = 0.0, double y = 1.0) // { return rand_closed01() * (y - x) + x; } // inline double normal(double mu = 0.0, double sd = 1.0) // { return RNOR() * sd + mu; } // inline double exponential(double lambda = 1) // { return REXP() / lambda; } // double gamma(double shape = 1, double scale = 1); // double chi_square(double df) // { return gamma(df / 2.0, 0.5); } // double beta(double a1, double a2) // { double x1 = gamma(a1, 1); return (x1 / (x1 + gamma(a2, 1))); } // // Discrete Distributions // double poisson(double lambda); // int binomial(double pp, int n); // }; // class RNG // } // namespace #endif // RNG_H igraph/src/Triangle.cpp0000755000176000001440000000515112325527072014626 0ustar ripleyusers#include "Triangle.h" #include namespace igraph { Triangle::Triangle() {} Triangle::Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3) { Type("Triangle"); mPoint1 = rPoint1; mPoint2 = rPoint2; mPoint3 = rPoint3; } Triangle::~Triangle() { } bool Triangle::Intersect(const Ray& vRay, Point& rIntersectPoint) const { Vector pointb_minus_pointa (mPoint1, mPoint2); Vector pointb_minus_pointc (mPoint1, mPoint3); /* Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc); // get the plane normal facing the right way: Vector plane_normal_normalized = plane_normal.Normalize(); Vector triangle_to_ray_origin = Vector(mPoint1, vRay.Origin() ); triangle_to_ray_origin.NormalizeThis(); if ( plane_normal_normalized.Dot(triangle_to_ray_origin) < 0.0 ) { plane_normal = plane_normal * -1.0; plane_normal_normalized = plane_normal_normalized * -1.0; } // check that the ray is actually facing the triangle Vector ray_direction_normalized = vRay.Direction().Normalize(); if ( plane_normal_normalized.Dot(ray_direction_normalized) > 0.0 ) return false; */ Vector plane_normal = this->Normal(mPoint1, vRay.Origin()); Vector ray_direction_normalized = vRay.Direction().Normalize(); if ( plane_normal.IsSameDirection(ray_direction_normalized) ) return false; Vector b_minus_u (vRay.Origin(), mPoint2); double t = plane_normal.Dot(b_minus_u) / plane_normal.Dot(vRay.Direction()); Point p = (vRay.Direction() * t) + vRay.Origin(); Vector p_minus_a (mPoint1, p); Vector p_minus_b (mPoint2, p); Vector p_minus_c (mPoint3, p); Vector pointc_minus_pointb (mPoint2, mPoint3); Vector pointa_minus_pointc (mPoint3, mPoint1); double test1 = (pointb_minus_pointa.Cross(p_minus_a)).Dot(plane_normal); double test2 = (pointc_minus_pointb.Cross(p_minus_b)).Dot(plane_normal); double test3 = (pointa_minus_pointc.Cross(p_minus_c)).Dot(plane_normal); if ((test1 > 0 && test2 > 0 && test3 > 0) || (test1 < 0 && test2 < 0 && test3 < 0)) { rIntersectPoint = p; return true; } else return false; } Vector Triangle::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const { Vector pointb_minus_pointa (mPoint1, mPoint2); Vector pointb_minus_pointc (mPoint1, mPoint3); Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc).Normalize(); // get the plane normal facing the right way: Vector triangle_to_off_surface_point = Vector(mPoint1, rOffSurface ); triangle_to_off_surface_point.NormalizeThis(); if ( !plane_normal.IsSameDirection(triangle_to_off_surface_point) ) { plane_normal.ReverseDirection(); } return plane_normal; } } // namespace igraph igraph/src/scg_kmeans.c0000644000176000001440000000567412325527074014644 0ustar ripleyusers/* * SCGlib : A C library for the spectral coarse graining of matrices * as described in the paper: Shrinking Matrices while preserving their * eigenpairs with Application to the Spectral Coarse Graining of Graphs. * Preprint available at * * Copyright (C) 2008 David Morton de Lachapelle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * DESCRIPTION * ----------- * The kmeans_Lloyd function is adapted from the R-stats package. * It perfoms Lloyd's k-means clustering on a p x n data matrix * stored row-wise in a vector 'x'. 'cen' contains k initial centers. * The group label to which each object belongs is stored in 'cl'. * Labels are positive consecutive integers starting from 0. * See also Section 5.3.3 of the above reference. */ #include "igraph_memory.h" #include "scg_headers.h" int igraph_i_kmeans_Lloyd(const igraph_vector_t *x, int n, int p, igraph_vector_t *cen, int k, int *cl, int maxiter) { int iter, i, j, c, it, inew = 0; igraph_real_t best, dd, tmp; int updated; igraph_vector_int_t nc; IGRAPH_CHECK(igraph_vector_int_init(&nc, k)); IGRAPH_FINALLY(igraph_vector_int_destroy, &nc); for (i = 0; i < n; i++) { cl[i] = -1; } for (iter = 0; iter < maxiter; iter++) { updated = 0; for (i = 0; i < n; i++) { /* find nearest centre for each point */ best = IGRAPH_INFINITY; for (j = 0; j < k; j++) { dd = 0.0; for (c = 0; c < p; c++) { tmp = VECTOR(*x)[i+n*c] - VECTOR(*cen)[j+k*c]; dd += tmp * tmp; } if (dd < best) { best = dd; inew = j+1; } } if (cl[i] != inew) { updated = 1; cl[i] = inew; } } if (!updated) { break; } /* update each centre */ for (j = 0; j < k*p; j++) { VECTOR(*cen)[j] = 0.0; } for (j = 0; j < k; j++) { VECTOR(nc)[j] = 0; } for (i = 0; i < n; i++) { it = cl[i] - 1; VECTOR(nc)[it]++; for (c = 0; c < p; c++) { VECTOR(*cen)[it+c*k] += VECTOR(*x)[i+c*n]; } } for (j = 0; j < k*p; j++) { VECTOR(*cen)[j] /= VECTOR(nc)[j % k]; } } igraph_vector_int_destroy(&nc); IGRAPH_FINALLY_CLEAN(1); /* convervenge check */ if (iter >= maxiter-1) { IGRAPH_ERROR("Lloyd k-means did not converge", IGRAPH_FAILURE); } return 0; } igraph/src/unit_limiter.cpp0000755000176000001440000000035312325527074015566 0ustar ripleyusers#include "unit_limiter.h" namespace igraph { double unit_limiter(double vUnitDouble) { double result = vUnitDouble; if (result < 0.0) result = 0.0; else if (result > 1.0) result = 1.0; return result; } } // namespace igraph igraph/src/igraph_constructors.h0000644000176000001440000000644612325527073016636 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_CONSTRUCTORS_H #define IGRAPH_CONSTRUCTORS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_datatype.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Constructors, deterministic */ /* -------------------------------------------------- */ int igraph_create(igraph_t *graph, const igraph_vector_t *edges, igraph_integer_t n, igraph_bool_t directed); int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, ...); int igraph_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix, igraph_adjacency_t mode); int igraph_weighted_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, const char* attr, igraph_bool_t loops); int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, igraph_integer_t center); int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector, igraph_integer_t nei, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular); int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular); int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, igraph_tree_mode_t type); int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); int igraph_full_citation(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); int igraph_atlas(igraph_t *graph, int number); int igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_t *W); int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, igraph_neimode_t mode); int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); int igraph_famous(igraph_t *graph, const char *name); int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, const igraph_vector_t *shifts, igraph_integer_t repeats); int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); __END_DECLS #endif igraph/src/gengraph_graph_molloy_optimized.cpp0000644000176000001440000015713312325527073021522 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gengraph_definitions.h" #include #include #include #include #include "gengraph_qsort.h" #include "gengraph_box_list.h" #include "gengraph_vertex_cover.h" #include "gengraph_degree_sequence.h" #include "gengraph_graph_molloy_optimized.h" #include "igraph_error.h" #include "igraph_statusbar.h" #include "igraph_progress.h" using namespace std; namespace gengraph { void graph_molloy_opt::breadth_search(int *dist, int v0, int *buff) { bool tmpbuff = (buff==NULL); if(tmpbuff) buff = new int[n]; for(int i=0; im) m=deg[k]; return m; } void graph_molloy_opt::compute_neigh() { int *p = links; for(int i=0; in) n=i; // n++; // // degrees ? // if(VERBOSE()) fprintf(stderr,"%d, #edges=",n); // int *degs = new int[n]; // for(i=0; i=i) *(c++)=*p; } } assert(c==b+(a/2)); return b; } int *graph_molloy_opt::hard_copy() { int *hc = new int[2+n+a/2]; // to store n,a,deg[] and links[] hc[0] = n; hc[1] = a; memcpy(hc+2,deg,sizeof(int)*n); int *c = hc+2+n; for(int i=0; i=i) *(c++)=*p; } } assert(c==hc+2+n+a/2); return hc; } void graph_molloy_opt::restore(int* b) { int i; for(i=0; i=0; i--) a+=(deg[i]=int(neigh[i+1]-neigh[i])); refresh_nbarcs(); } void graph_molloy_opt::clean() { int *b = hard_copy(); replace(b); delete[] b; } void graph_molloy_opt::replace(int *_hardcopy) { delete[] deg; n = *(_hardcopy++); a = *(_hardcopy++); deg = new int[a+n]; memcpy(deg,_hardcopy,sizeof(int)*n); links = deg+n; compute_neigh(); restore(_hardcopy+n); } int* graph_molloy_opt::components(int *comp) { int i; // breadth-first search buffer int *buff=new int[n]; // comp[i] will contain the index of the component that contains vertex i if(comp==NULL) comp=new int[n]; memset(comp,0,sizeof(int)*n); // current component index int curr_comp = 0; // loop over all non-visited vertices... for(int v0=0; v0 nb_comp) nb_comp=comp[i]; // box-sort sizes int offset = 0; int *box = pre_boxsort(buff,nb_comp,offset); for(i=nb_comp-1; i>=0; i--) buff[i] = --box[buff[i]-offset]; delete[] box; // reassign component indexes for(int *c=comp+n; comp!=c--; *c=buff[*c-1]) { } // clean.. at last! delete[] buff; return comp; } void graph_molloy_opt::giant_comp() { int *comp = components(); // Clear edges of all vertices that do not belong to comp 0 for(int i=0; i=0; i--) { c+=nb[i]; nb[i]=-nb[i]+c; } // sort for(i=0; i0; ) { // pick a vertex. we could pick any, but here we pick the one with biggest degree int v = sorted[first]; // look for current degree of v while(nb[d]<=first) d--; // store it in dv int dv = d; // bind it ! c -= dv; int dc = d; // residual degree of vertices we bind to int fc = ++first; // position of the first vertex with degree dc while(dv>0 && dc>0) { int lc = nb[dc]; if(lc!=fc) { while(dv>0 && lc>fc) { // binds v with sorted[--lc] dv--; int w = sorted[--lc]; *(neigh[v]++) = w; *(neigh[w]++) = v; } fc = nb[dc]; nb[dc] = lc; } dc--; } if(dv != 0) { // We couldn't bind entirely v delete[] nb; delete[] sorted; compute_neigh(); igraph_errorf("Error in graph_molloy_opt::havelhakimi():" " Couldn't bind vertex %d entirely " "(%d edges remaining)", __FILE__, __LINE__, IGRAPH_EINTERNAL, v, dv); return false; } } assert(c==0); compute_neigh(); delete[] nb; delete[] sorted; return true; } bool graph_molloy_opt::is_connected() { bool *visited = new bool[n]; for(int i=n; i>0; visited[--i]=false) { } int *to_visit = new int[n]; int *stop = to_visit; int left = n-1; *(to_visit++) = 0; visited[0] = true; while(left>0 && to_visit != stop) { int v = *(--to_visit); int *w = neigh[v]; for(int k = deg[v]; k--; w++) if(!visited[*w]) { visited[*w] = true; left--; *(to_visit++) = *w; } } delete[] visited; delete[] stop; assert(left>=0); return (left == 0); } bool graph_molloy_opt::make_connected() { //assert(verify()); if(a/2 < n-1) { // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n"); return false; } int i; // Data struct for the visit : // - buff[] contains vertices to visit // - dist[V] is V's distance modulo 4 to the root of its comp, or -1 if it hasn't been visited yet #define MC_BUFF_SIZE (n+2) int *buff = new int[MC_BUFF_SIZE]; unsigned char * dist = new unsigned char[n]; #define NOT_VISITED 255 #define FORBIDDEN 254 for(i=n; i>0; dist[--i]=NOT_VISITED) { } // Data struct to store components : either surplus trees or surplus edges are stored at buff[]'s end // - A Tree is coded by one of its vertices // - An edge (a,b) is coded by the TWO ints a and b int *ffub = buff+MC_BUFF_SIZE; edge *edges = (edge *) ffub; int *trees = ffub; int *min_ffub = buff+1+(MC_BUFF_SIZE%2 ? 0 : 1); // There will be only one "fatty" component, and trees. edge fatty_edge = { -1, -1 }; bool enough_edges = false; // start main loop for(int v0=0; v0min_ffub) min_ffub+=2; // update limit of ffub's storage //assert(verify()); } else if(dist[w]==next_dist || (w>=v && dist[w]==current_dist)) { // we found a removable edge if(trees!=ffub) { // some trees still.. Let's merge with them! assert(trees>=min_ffub); assert(edges==(edge *)ffub); swap_edges(v,w,*trees,neigh[*trees][0]); trees++; //assert(verify()); } else if(is_a_tree) { // we must merge with the fatty component is_a_tree = false; if(fatty_edge.from < 0) { // we ARE the first component! fatty is us fatty_edge.from = v; fatty_edge.to = w; } else { // we connect to fatty swap_edges(fatty_edge.from, fatty_edge.to, v, w); fatty_edge.to = w; //assert(verify()); } } else if(!enough_edges) { // Store the removable edge for future use if(edges<=(edge *)min_ffub+1) enough_edges = true; else { edges--; edges->from = v; edges->to = w; } } } } } // Mark component while(to_visit!=buff) dist[*(--to_visit)] = FORBIDDEN; // Check if it is a tree if(is_a_tree ) { assert(deg[v0]!=0); if(edges!=(edge *)ffub) { // let's bind the tree we found with a removable edge in stock assert(trees == ffub); if(edges<(edge *)min_ffub) edges=(edge *)min_ffub; swap_edges(v0,neigh[v0][0],edges->from,edges->to); edges++; assert(verify()); } else if(fatty_edge.from>=0) { // if there is a fatty component, let's merge with it ! and discard fatty :-/ assert(trees == ffub); swap_edges(v0,neigh[v0][0],fatty_edge.from,fatty_edge.to); fatty_edge.from = -1; fatty_edge.to = -1; assert(verify()); } else { // add the tree to the list of trees assert(trees>min_ffub); *(--trees) = v0; assert(verify()); } } } delete[] buff; delete[] dist; // Should ALWAYS return true : either we have no tree left, or we are a unique, big tree return(trees == ffub || ((trees+1)==ffub && fatty_edge.from<0)); } bool graph_molloy_opt::swap_edges_simple(int from1, int to1, int from2, int to2) { if(from1==to1 || from1==from2 || from1==to2 || to1==from2 || to1==to2 || from2==to2) return false; if (is_edge(from1,to2) || is_edge(from2,to1)) return false; swap_edges(from1, to1, from2, to2); return true; } long graph_molloy_opt::fab_connected_shuffle(long times) { //assert(verify()); long nb_swaps = 0; double T = double(min(a,times))/10.0; double q1 = 1.131; double q2 = 0.9237; while(times>0) { long iperiod = max(1,long(T)); // Backup graph int *save = backup(); //assert(verify()); // Swaps long swaps = 0; for(long i=iperiod; i>0; i--) { // Pick two random vertices int f1 = links[my_random()%a]; int f2 = links[my_random()%a]; if(f1==f2) continue; // Pick two random neighbours int *f1t1 = neigh[f1]+my_random()%deg[f1]; int *f2t2 = neigh[f2]+my_random()%deg[f2]; int t1 = *f1t1; int t2 = *f2t2; // test simplicity if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) { // swap *f1t1 = t2; *f2t2 = t1; fast_rpl(neigh[t1],f1,f2); fast_rpl(neigh[t2],f2,f1); swaps++; } } //assert(verify()); // test connectivity if(is_connected()) { nb_swaps += swaps; times -= iperiod; // adjust T T*=q1; } else { restore(save); //assert(verify()); T*=q2; } delete[] save; } return nb_swaps; } long graph_molloy_opt::opt_fab_connected_shuffle(long times) { //assert(verify()); long nb_swaps = 0; double T = double(min(a,times))/10.0; double q1 = 1.131; double q2 = 0.9237; while(times>0) { long iperiod = max(1,long(T)); // Backup graph int *save = backup(); //assert(verify()); // Swaps long swaps = 0; for(long i=iperiod; i>0; i--) { // Pick two random vertices int f1 = links[my_random()%a]; int f2 = links[my_random()%a]; if(f1==f2) continue; // Pick two random neighbours int *f1t1 = neigh[f1]+my_random()%deg[f1]; int *f2t2 = neigh[f2]+my_random()%deg[f2]; int t1 = *f1t1; int t2 = *f2t2; if( // test simplicity t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1) && // test isolated pair (deg[f1]>1 || deg[t2]>1) && (deg[f2]>1 || deg[t1]>1) ) { // swap *f1t1 = t2; *f2t2 = t1; fast_rpl(neigh[t1],f1,f2); fast_rpl(neigh[t2],f2,f1); swaps++; } } //assert(verify()); // test connectivity if(is_connected()) { nb_swaps += swaps; times -= iperiod; // adjust T T*=q1; } else { restore(save); //assert(verify()); T*=q2; } delete[] save; } return nb_swaps; } long graph_molloy_opt::gkantsidis_connected_shuffle(long times) { //assert(verify()); long nb_swaps = 0; long T = min(a,times)/10; while(times>0) { // Backup graph int *save = backup(); //assert(verify()); // Swaps long swaps = 0; for(int i=T; i>0; i--) { // Pick two random vertices int f1 = links[my_random()%a]; int f2 = links[my_random()%a]; if(f1==f2) continue; // Pick two random neighbours int *f1t1 = neigh[f1]+my_random()%deg[f1]; int *f2t2 = neigh[f2]+my_random()%deg[f2]; int t1 = *f1t1; int t2 = *f2t2; // test simplicity if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) { // swap *f1t1 = t2; *f2t2 = t1; fast_rpl(neigh[t1],f1,f2); fast_rpl(neigh[t2],f2,f1); swaps++; } } //assert(verify()); // test connectivity if(is_connected()) { nb_swaps += swaps; times -= T; // adjust T T++; } else { restore(save); //assert(verify()); T/=2; if(T==0) T=1; } delete[] save; } return nb_swaps; } long graph_molloy_opt::slow_connected_shuffle(long times) { //assert(verify()); long nb_swaps = 0; while(times--) { // Pick two random vertices int f1 = links[my_random()%a]; int f2 = links[my_random()%a]; if(f1==f2) continue; // Pick two random neighbours int *f1t1 = neigh[f1]+my_random()%deg[f1]; int *f2t2 = neigh[f2]+my_random()%deg[f2]; int t1 = *f1t1; int t2 = *f2t2; // test simplicity if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) { // swap *f1t1 = t2; *f2t2 = t1; int *t1f1 = fast_rpl(neigh[t1],f1,f2); int *t2f2 = fast_rpl(neigh[t2],f2,f1); // test connectivity if(is_connected()) nb_swaps++; else { // undo swap *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; } } } return nb_swaps; } void graph_molloy_opt::print(FILE *f, bool NOZERO) { int i,j; for(i=0; i0) { fprintf(f,"%d",i); for(j=0; j=dmax) { left_to_explore = 0; return; } *(Kbuff++) = v; visited[v] = true; calls++; int *w = neigh[v]; qsort(deg, w, deg[v]); w+=deg[v]; for(int i=deg[v]; i--; ) { if(visited[*--w]) calls++; else depth_isolated(*w, calls, left_to_explore, dmax, Kbuff, visited); if(left_to_explore==0) break; } } int graph_molloy_opt::depth_search(bool *visited, int *buff, int v0) { for(int i=0; i=0) for(int i=0; i=newdeg[v]) { int *p = neigh[v]+(newdeg[v]++); *ww = *p; *p = w; // Now, add the dual edge ww = neigh[w]; p = ww+(newdeg[w]); while(ww!=p && *ww != v) { ww++; k2++; } if(ww==p) { // dual edge was not discovered.. search it and add it. while(*ww != v) { ww++; k2++; } *ww = *p; *p = v; newdeg[w]++; } } // if edge redudancy is asked, look for dual edge else if(edge_redudancy!=NULL) for(int *ww = neigh[w]; *(ww++)!=v; k2++) { } // add edge redudancy if(edge_redudancy!=NULL) { edge_redudancy[v][k] += red; edge_redudancy[w][k2] += red; } assert(newdeg[v]<=deg[v]); } // dist[] MUST be full of zeros !!!! int graph_molloy_opt::breadth_path_search(int src, int *buff, double *paths, unsigned char *dist) { unsigned char last_dist = 0; unsigned char curr_dist = 1; int *to_visit = buff; int *visited = buff; *(to_visit++) = src; paths[src] = 1.0; dist[src] = curr_dist; int nb_visited = 1; while(visited != to_visit) { int v = *(visited++); if(last_dist==(curr_dist=dist[v])) break; unsigned char nd = next_dist(curr_dist); int *ww = neigh[v]; double p = paths[v]; for(int k=deg[v]; k--;) { int w=*(ww++); unsigned char d = dist[w]; if(d==0) { // not visited yet ! *(to_visit++) = w; dist[w] = nd; paths[w]= p; // is it the last one ? if(++nb_visited==n) last_dist=nd; } else if(d==nd) if((paths[w]+=p)==numeric_limits::infinity()) { IGRAPH_ERROR("Fatal error : too many (>MAX_DOUBLE) possible" " paths in graph", IGRAPH_EOVERFLOW); } } } assert(to_visit == buff+nb_visited); return nb_visited; } // dist[] MUST be full of zeros !!!! void graph_molloy_opt::explore_usp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg, double **edge_redudancy) { while(--nb_vertices) { int v = buff[nb_vertices]; if(target[v]>0.0) { unsigned char pd = prev_dist(dist[v]); int *ww = neigh[v]; int k=0; // pick ONE father at random double father_index = my_random01()*paths[v]; double f = 0.0; int father = -1; while(f0.0) { unsigned char pd = prev_dist(dist[v]); int *ww = neigh[v]; int dv = deg[v]; double f=target[v]/paths[v]; // pick ALL fathers register int father; for(int k=0; k0.0) { unsigned char pd = prev_dist(dist[v]); int *ww = neigh[v]; // for all fathers : do we take it ? int paths_left = int(target[v]); double father_index = paths[v]; int father; for(int k=0; k0) { paths_left -= to_add_to_father; // increase target[] of father target[father] += to_add_to_father; // add edge, if necessary if(newdeg!=NULL) add_traceroute_edge(v,k,newdeg,edge_redudancy,target[v]); } } } // clear dist[] dist[v] = 0; } dist[buff[0]] = 0; } double *graph_molloy_opt::vertex_betweenness(int mode, bool trivial_paths) { char MODES[3] = {'U','A','R'}; igraph_statusf("Computing vertex betweenness %cSP...", 0, MODES[mode]); // breadth-first search vertex fifo int *buff = new int[n]; // breadth-first search path count double *paths = new double[n]; // breadth-first search distance vector unsigned char *dist = new unsigned char[n]; // global betweenness double *b = new double[n]; // local betweenness (for one source) double *target = new double[n]; // init all int progress = 0; memset(dist,0,sizeof(unsigned char)*n); for(double *yo = target+n; (yo--)!=target; *yo=1.0) { } for(double *yo = b+n; (yo--)!=b; *yo=0.0) { } int progress_steps = max(1000,n/10); // Main loop for(int v0 = 0; v0(progress*n) / progress_steps) { progress++; igraph_progressf("Computing vertex betweenness %cSP", 100.0*double(progress)/double(progress_steps), 0, MODES[mode]); } // Breadth-first search int nb_vertices = breadth_path_search(v0, buff, paths, dist); // initialize target[vertices in component] to 1 //for(int *yo = buff+nb_vertices; (yo--)!=buff; target[*yo]=1.0); // backwards-cumulative exploration switch(mode) { case MODE_USP: explore_usp(target, nb_vertices, buff, paths, dist); break; case MODE_ASP: explore_asp(target, nb_vertices, buff, paths, dist); break; case MODE_RSP: explore_rsp(target, nb_vertices, buff, paths, dist); break; default: IGRAPH_WARNING("graph_molloy_opt::vertex_betweenness() " "called with Invalid Mode"); } // add targets[vertices in component] to global betweenness and reset targets[] if(nb_vertices==n) { // cache optimization if all vertices are in component double *bb=b; double *tt_end=target+n; if(trivial_paths) for(double *yo=target; yo!=tt_end; *(bb++)+=*(yo++)){} else { for(double *yo=target; yo!=tt_end; *(bb++)+=(*(yo++)-1.0)) { } b[*buff]-=(target[*buff]-1.0); } for(double *yo = target; yo!=tt_end; *(yo++)=1.0) { } } else { if(trivial_paths) for(int *yo = buff+nb_vertices; (yo--)!=buff; b[*yo]+=target[*yo]) { } else for(int *yo = buff+nb_vertices; (--yo)!=buff; b[*yo]+=(target[*yo]-1.0)) { } for(int *yo = buff+nb_vertices; (yo--)!=buff; target[*yo]=1.0) { } } } // Clean all & return delete[] target; delete[] dist; delete[] buff; delete[] paths; igraph_status("Done\n", 0); return b; } double graph_molloy_opt::traceroute_sample(int mode, int nb_src, int *src, int nb_dst, int* dst, double *redudancy, double **edge_redudancy) { // verify & verbose assert(verify()); char MODES[3] = {'U','A','R'}; igraph_statusf("traceroute %cSP on G(N=%d,M=%d) with %d src and %d dst...", 0, MODES[mode], nbvertices_real(), nbarcs(), nb_src,nb_dst); // create dst[] buffer if necessary bool newdist = dst==NULL; if(newdist) dst = new int[n]; // breadth-first search vertex fifo int *buff = new int[n]; // breadth-first search path count double *paths = new double[n]; // breadth-first search distance vector unsigned char *dist = new unsigned char[n]; // newdeg[] allows to tag discovered edges int *newdeg = new int[n]; // target[v] is > 0 if v is a destination double *target = new double[n]; // init all int i; memset(dist,0,sizeof(unsigned char)*n); memset(newdeg,0,sizeof(int)*n); for(double *yo = target+n; (yo--)!=target; *yo=0.0) { } if(redudancy!=NULL) for(double *yo = redudancy+n; (yo--)!=redudancy; *yo=0.0) { } // src_0 counts the number of sources having degree 0 int src_0 = 0; // nopath counts the number of pairs (src,dst) having no possible path int nopath = 0; // nb_paths & total_dist are for the average distance estimator int nb_paths = 0; double total_dist = 0; // s will be the current source int s; while(nb_src--) if(deg[s = *(src++)]==0) src_0++; else { // breadth-first search int nb_vertices = breadth_path_search(s,buff,paths,dist); // do we have to pick new destinations ? if(newdist) pick_random_dst(double(nb_dst),NULL,dst); // mark reachable destinations as "targets" for(i=0; i0.0) { total_dist += double(current_dist); nb_paths++; } } // substract target[] to redudancy if needed if(redudancy!=NULL) for(i=1; i0) { if(deg[s]==0) src_0++; else { if(s>next_step) { next_step = s+(n/1000)+1; igraph_progress("Sampling paths", double(s)/double(n), 0); } int v; // breadth-first search int *to_visit=buff; int *visited=buff; *(to_visit++)=s; dist[s]=1; nb_pos[s]=1; while(visited!=to_visit) { v=*(visited++); unsigned char n_dist = next_dist(dist[v]); int *w0 = neigh[v]; for(int *w = w0+deg[v]; w--!=w0; ) { unsigned char d2 = dist[*w]; if(d2==0) { dist[*w]=d2=n_dist; *(to_visit++) = *w; } if(d2==n_dist) nb_pos[*w] += nb_pos[v]; } } // for every target, pick a random path. int t_index = nb_dst[s]; // create dst[] if necessary if(NOMEM) pick_random_src(double(t_index),NULL,dst); while(t_index--) if(dist[v = *(dst++)]==0) nopath++; else { #ifdef _DEBUG igraph_statusf("Sampling path %d -> %d\n", 0, s, v); #endif //_DEBUG nb_paths++; // while we haven't reached the source.. while(v!=s) { // pick a random father int index = my_random()%nb_pos[v]; unsigned char p_dist = prev_dist(dist[v]); int *w = neigh[v]; int k=0; int new_father; while(dist[new_father=w[k]]!=p_dist || (index-=nb_pos[new_father])>=0) k++; // add edge add_traceroute_edge(v,k,newdeg,edge_redudancies,1.0); if(redudancies!=NULL && new_father!=s) redudancies[new_father]+=1.0; // step down to father v = new_father; // increase total distance total_dist++; if(total_dist==0) total_dist64++; } } // reset (int *)dst if necessary if(NOMEM) dst -= nb_dst[s]; // clear breadth-first search buffers while(visited!=buff) { v=*(--visited); dist[v]=0; nb_pos[v]=0; } } } // update degrees for(i=0; i0) tdist *= 4294967296.0; tdist += double(total_dist); return tdist / double(nb_paths); } int *graph_molloy_opt::vertices_real(int &nb_v) { int *yo; if(nb_v<0) { nb_v=0; for(yo=deg; yo!=deg+n; ) if(*(yo++)>0) nb_v++; } if(nb_v==0) { IGRAPH_WARNING("graph is empty"); return NULL; } int *buff=new int[nb_v]; yo=buff; for(int i=0; i0) *(yo++)=i; if(yo!=buff+nb_v){ igraph_warningf("wrong #vertices in graph_molloy_opt::vertices_real(%d)", __FILE__, __LINE__, -1, nb_v); delete[] buff; return NULL; } else return buff; } int *graph_molloy_opt::pick_random_vertices(int &k, int *output, int nb_v, int *among) { int i; bool CREATED_AMONG = false; if(among==NULL && k>0) { among=vertices_real(nb_v); CREATED_AMONG=true; } if(k>nb_v) { igraph_warningf("Warning : tried to pick %d among %d vertices. " "Picked only %d", __FILE__, __LINE__, -1, k, nb_v, nb_v); k = nb_v; } if(k>0) { if(output==NULL) output=new int[k]; for(i=0; i=1.0 ? k : k*double(nb_v)))); if(kk==0) kk=1; int *yo=pick_random_vertices(kk,buff,nb_v,among); if(nb!=NULL) *nb=kk; if(AMONG_CREATED) delete[] among; return yo; } int *graph_molloy_opt::pick_random_dst(double k, int *nb, int* buff, int nb_v, int* among) { bool AMONG_CREATED=false; if(among==NULL || nb_v<0) { AMONG_CREATED=true; among=vertices_real(nb_v); } int kk = int(floor(0.5 + (k>1.0 ? k : k*double(nb_v)))); if(kk==0) kk=1; int *yo=pick_random_vertices(kk,buff,nb_v,among); if(nb!=NULL) *nb=kk; if(AMONG_CREATED) delete[] among; return yo; } int graph_molloy_opt::core() { box_list b(n,deg); int v; int removed = 0; while((v=b.get_one())>=0) { b.pop_vertex(v,neigh); deg[v]=0; removed++; } refresh_nbarcs(); return removed; } int graph_molloy_opt::try_disconnect(int K, int max_tries) { bool *visited = new bool[n]; for(bool *p = visited+n; p!=visited; *(--p)=false) { } int *Kbuff = new int[K]; int tries = 0; int next_step = -1; if(VERBOSE()) next_step = 0; bool yo = true; while(yo && tries 0 if v is a destination double *target = new double[n]; // times_seen count the times we saw each vertex int *times_seen = new int[n]; // init all int i; memset(dist,0,sizeof(unsigned char)*n); memset(times_seen,0,sizeof(int)*n); for(double *yo = target+n; (yo--)!=target; *yo=0.0) { } // src_0 counts the number of sources having degree 0 int src_0 = 0; // nopath counts the number of pairs (src,dst) having no possible path int nopath = 0; // s will be the current source int s; for(int nsrc=0; nsrc=0 && links[i]0); } return true; } /*___________________________________________________________________________________ Not to use anymore : use graph_molloy_hash class instead void graph_molloy_opt::shuffle(long times) { while(times) { int f1 = links[my_random()%a]; int f2 = links[my_random()%a]; int t1 = neigh[f1][my_random()%deg[f1]]; int t2 = neigh[f2][my_random()%deg[f2]]; if(swap_edges_simple(f1,t1,f2,t2)) times--; } } long graph_molloy_opt::connected_shuffle(long times) { //assert(verify()); #ifdef PERFORMANCE_MONITOR long failures = 0; long successes = 0; double avg_K = 0.0; long avg_T = 0; #endif //PERFORMANCE_MONITOR long nb_swaps = 0; long T = min(a,times)/10; double double_K = 1.0; int K = int(double_K); double Q1 = 1.35; double Q2 = 1.01; int *Kbuff = new int[K]; bool *visited = new bool[n]; for(int i=0; inb_swaps) { // Backup graph #ifdef PERFORMANCE_MONITOR avg_K+=double_K; avg_T+=T; #endif //PERFORMANCE_MONITOR int *save = backup(); //assert(verify()); // Swaps long swaps = 0; for(int i=T; i>0; i--) { // Pick two random vertices int f1 = pick_random_vertex(); int f2 = pick_random_vertex(); if(f1==f2) continue; // Pick two random neighbours int *f1t1 = random_neighbour(f1); int t1 = *f1t1; int *f2t2 = random_neighbour(f2); int t2 = *f2t2; // test simplicity if(t1!=t2 && f1!=t2 && f2!=t1 && !is_edge(f1,t2) && !is_edge(f2,t1)) { // swap *f1t1 = t2; *f2t2 = t1; int *t1f1 = fast_rpl(neigh[t1],f1,f2); int *t2f2 = fast_rpl(neigh[t2],f2,f1); // isolation test if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) { // undo swap *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; } else swaps++; } } //assert(verify()); // test connectivity bool ok = is_connected(); #ifdef PERFORMANCE_MONITOR if(ok) successes++; else failures++; #endif //PERFORMANCE_MONITOR if(ok) { nb_swaps += swaps; // adjust K and T if((K+10)*T>5*a) { double_K/=Q2; K = int(double_K); } else T*=2; } else { restore(save); //assert(verify()); double_K*=Q1; K = int(double_K); delete[] Kbuff; Kbuff = new int[K]; } delete[] save; } #ifdef PERFORMANCE_MONITOR fprintf(stderr,"\n*** Performance Monitor ***\n"); fprintf(stderr," - Connectivity test successes : %ld\n",successes); fprintf(stderr," - Connectivity test failures : %ld\n",failures); fprintf(stderr," - Average window : %ld\n",avg_T/long(successes+failures)); fprintf(stderr," - Average isolation test width : %f\n",avg_K/double(successes+failures)); #endif //PERFORMANCE_MONITOR return nb_swaps; } bool graph_molloy_opt::try_shuffle(int T, int K) { int i; int *Kbuff = NULL; if(K>0) Kbuff = new int[K]; bool *visited = new bool[n]; for(i=0; i0; i--) { // Pick two random vertices int f1 = pick_random_vertex(); int f2 = pick_random_vertex(); if(f1==f2) continue; // Pick two random neighbours int *f1t1 = random_neighbour(f1); int t1 = *f1t1; int *f2t2 = random_neighbour(f2); int t2 = *f2t2; // test simplicity if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) { // swap *f1t1 = t2; *f2t2 = t1; int *t1f1 = fast_rpl(neigh[t1],f1,f2); int *t2f2 = fast_rpl(neigh[t2],f2,f1); // isolation test if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) { // undo swap *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; } } } delete[] visited; if(Kbuff != NULL) delete[] Kbuff; bool yo = is_connected(); restore(back); delete[] back; return yo; } double graph_molloy_opt::window(int K, double ratio) { int steps = 100; double T = double(a*10); double q2 = 0.1; double q1 = pow(q2,(ratio-1.0)/ratio); int failures = 0; int successes = 0; int *Kbuff = new int[K]; bool *visited = new bool[n]; while(successes<10*steps) { int *back=backup(); for(int i=int(T); i>0; i--) { // Pick two random vertices int f1 = links[my_random()%a]; int f2 = links[my_random()%a]; if(f1==f2) continue; // Pick two random neighbours int *f1t1 = neigh[f1]+my_random()%deg[f1]; int *f2t2 = neigh[f2]+my_random()%deg[f2]; int t1 = *f1t1; int t2 = *f2t2; // test simplicity if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) { // swap *f1t1 = t2; *f2t2 = t1; int *t1f1 = fast_rpl(neigh[t1],f1,f2); int *t2f2 = fast_rpl(neigh[t2],f2,f1); // isolation test if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) { // undo swap *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; } } } if(is_connected()) { T *= q1; if(T>double(5*a)) T=double(5*a); successes++; if((successes%steps)==0) { q2 = sqrt(q2); q1 = sqrt(q1); } } else { T*=q2; failures++; } if(VERBOSE()) fprintf(stderr,"."); restore(back); delete[] back; } delete[] Kbuff; delete[] visited; if(VERBOSE()) fprintf(stderr,"Failures:%d Successes:%d\n",failures, successes); return T; } double graph_molloy_opt::eval_K(int quality) { double K = 5.0; double avg_K = 1.0; for(int i=quality; i--; ) { int int_K = int(floor(K+0.5)); if(try_shuffle(a/(int_K+1),int_K)) { K*=0.8; fprintf(stderr,"+"); } else { K*=1.25; fprintf(stderr,"-"); } if(ideg[t2] ? f1 : t2, K, Kbuff, visited); sum_K += effective_isolated(deg[f2]>deg[t1] ? f2 : t1, K, Kbuff, visited); // undo swap swap_edges(f1,t2,f2,t1); // assert(verify()); } delete[] Kbuff; delete[] visited; return double(sum_K)/double(2*quality); } //___________________________________________________________________________________ //*/ /***** NOT USED ANYMORE (Modif 22/04/2005) ****** long long *graph_molloy_opt::vertex_betweenness_usp(bool trivial_paths) { if(VERBOSE()) fprintf(stderr,"Computing vertex betweenness USP..."); int i; unsigned char *dist = new unsigned char[n]; int *buff = new int[n]; long long *b = new long long[n]; int *bb = new int[n]; int *dd = new int[max_degree()]; for(i=0; i(progress*n)/1000) { progress++; fprintf(stderr,"\rComputing vertex betweenness USP : %d.%d%% ",progress/10,progress%10); } int nb_vertices = width_search(dist, buff, v0); int nv = nb_vertices; for(i=0; i(progress*n)/1000) { progress++; fprintf(stderr,"\rComputing vertex betweenness RSP : %d.%d%% ",progress/10,progress%10); } int nb_vertices = width_search(dist, buff, v0); int nv = nb_vertices; for(i=0; i1 && to_give>2*n_father) { int o = rng.binomial(1.0/n_father,to_give); to_give -= o; bb[dd[--n_father]]+=o; } if(n_father==1) bb[dd[0]]+=to_give; else { while(to_give--) bb[dd[my_random()%n_father]]++; } } if(trivial_paths) bb[v]++; } for(i=0; i0) { if(VERBOSE()==VERBOSE_LOTS && v0>(progress*n)/1000) { progress++; fprintf(stderr,"\rComputing vertex betweenness ASP : %d.%d%% ",progress/10,progress%10); } int nb_vertices = width_search(dist, buff, v0); if(!trivial_paths) dist[v0]=2; int nv = nb_vertices; for(i=0; i. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpapi.h" /*********************************************************************** * NAME * * glp_bf_exists - check if the basis factorization exists * * SYNOPSIS * * int glp_bf_exists(glp_prob *lp); * * RETURNS * * If the basis factorization for the current basis associated with * the specified problem object exists and therefore is available for * computations, the routine glp_bf_exists returns non-zero. Otherwise * the routine returns zero. */ int glp_bf_exists(glp_prob *lp) { int ret; ret = (lp->m == 0 || lp->valid); return ret; } /*********************************************************************** * NAME * * glp_factorize - compute the basis factorization * * SYNOPSIS * * int glp_factorize(glp_prob *lp); * * DESCRIPTION * * The routine glp_factorize computes the basis factorization for the * current basis associated with the specified problem object. * * RETURNS * * 0 The basis factorization has been successfully computed. * * GLP_EBADB * The basis matrix is invalid, i.e. the number of basic (auxiliary * and structural) variables differs from the number of rows in the * problem object. * * GLP_ESING * The basis matrix is singular within the working precision. * * GLP_ECOND * The basis matrix is ill-conditioned. */ static int b_col(void *info, int j, int ind[], double val[]) { glp_prob *lp = info; int m = lp->m; GLPAIJ *aij; int k, len; xassert(1 <= j && j <= m); /* determine the ordinal number of basic auxiliary or structural variable x[k] corresponding to basic variable xB[j] */ k = lp->head[j]; /* build j-th column of the basic matrix, which is k-th column of the scaled augmented matrix (I | -R*A*S) */ if (k <= m) { /* x[k] is auxiliary variable */ len = 1; ind[1] = k; val[1] = 1.0; } else { /* x[k] is structural variable */ len = 0; for (aij = lp->col[k-m]->ptr; aij != NULL; aij = aij->c_next) { len++; ind[len] = aij->row->i; val[len] = - aij->row->rii * aij->val * aij->col->sjj; } } return len; } static void copy_bfcp(glp_prob *lp); int glp_factorize(glp_prob *lp) { int m = lp->m; int n = lp->n; GLPROW **row = lp->row; GLPCOL **col = lp->col; int *head = lp->head; int j, k, stat, ret; /* invalidate the basis factorization */ lp->valid = 0; /* build the basis header */ j = 0; for (k = 1; k <= m+n; k++) { if (k <= m) { stat = row[k]->stat; row[k]->bind = 0; } else { stat = col[k-m]->stat; col[k-m]->bind = 0; } if (stat == GLP_BS) { j++; if (j > m) { /* too many basic variables */ ret = GLP_EBADB; goto fini; } head[j] = k; if (k <= m) row[k]->bind = j; else col[k-m]->bind = j; } } if (j < m) { /* too few basic variables */ ret = GLP_EBADB; goto fini; } /* try to factorize the basis matrix */ if (m > 0) { if (lp->bfd == NULL) { lp->bfd = bfd_create_it(); copy_bfcp(lp); } switch (bfd_factorize(lp->bfd, m, lp->head, b_col, lp)) { case 0: /* ok */ break; case BFD_ESING: /* singular matrix */ ret = GLP_ESING; goto fini; case BFD_ECOND: /* ill-conditioned matrix */ ret = GLP_ECOND; goto fini; default: xassert(lp != lp); } lp->valid = 1; } /* factorization successful */ ret = 0; fini: /* bring the return code to the calling program */ return ret; } /*********************************************************************** * NAME * * glp_bf_updated - check if the basis factorization has been updated * * SYNOPSIS * * int glp_bf_updated(glp_prob *lp); * * RETURNS * * If the basis factorization has been just computed from scratch, the * routine glp_bf_updated returns zero. Otherwise, if the factorization * has been updated one or more times, the routine returns non-zero. */ int glp_bf_updated(glp_prob *lp) { int cnt; if (!(lp->m == 0 || lp->valid)) xerror("glp_bf_update: basis factorization does not exist\n"); #if 0 /* 15/XI-2009 */ cnt = (lp->m == 0 ? 0 : lp->bfd->upd_cnt); #else cnt = (lp->m == 0 ? 0 : bfd_get_count(lp->bfd)); #endif return cnt; } /*********************************************************************** * NAME * * glp_get_bfcp - retrieve basis factorization control parameters * * SYNOPSIS * * void glp_get_bfcp(glp_prob *lp, glp_bfcp *parm); * * DESCRIPTION * * The routine glp_get_bfcp retrieves control parameters, which are * used on computing and updating the basis factorization associated * with the specified problem object. * * Current values of control parameters are stored by the routine in * a glp_bfcp structure, which the parameter parm points to. */ void glp_get_bfcp(glp_prob *lp, glp_bfcp *parm) { glp_bfcp *bfcp = lp->bfcp; if (bfcp == NULL) { parm->type = GLP_BF_FT; parm->lu_size = 0; parm->piv_tol = 0.10; parm->piv_lim = 4; parm->suhl = GLP_ON; parm->eps_tol = 1e-15; parm->max_gro = 1e+10; parm->nfs_max = 100; parm->upd_tol = 1e-6; parm->nrs_max = 100; parm->rs_size = 0; } else memcpy(parm, bfcp, sizeof(glp_bfcp)); return; } /*********************************************************************** * NAME * * glp_set_bfcp - change basis factorization control parameters * * SYNOPSIS * * void glp_set_bfcp(glp_prob *lp, const glp_bfcp *parm); * * DESCRIPTION * * The routine glp_set_bfcp changes control parameters, which are used * by internal GLPK routines in computing and updating the basis * factorization associated with the specified problem object. * * New values of the control parameters should be passed in a structure * glp_bfcp, which the parameter parm points to. * * The parameter parm can be specified as NULL, in which case all * control parameters are reset to their default values. */ #if 0 /* 15/XI-2009 */ static void copy_bfcp(glp_prob *lp) { glp_bfcp _parm, *parm = &_parm; BFD *bfd = lp->bfd; glp_get_bfcp(lp, parm); xassert(bfd != NULL); bfd->type = parm->type; bfd->lu_size = parm->lu_size; bfd->piv_tol = parm->piv_tol; bfd->piv_lim = parm->piv_lim; bfd->suhl = parm->suhl; bfd->eps_tol = parm->eps_tol; bfd->max_gro = parm->max_gro; bfd->nfs_max = parm->nfs_max; bfd->upd_tol = parm->upd_tol; bfd->nrs_max = parm->nrs_max; bfd->rs_size = parm->rs_size; return; } #else static void copy_bfcp(glp_prob *lp) { glp_bfcp _parm, *parm = &_parm; glp_get_bfcp(lp, parm); bfd_set_parm(lp->bfd, parm); return; } #endif void glp_set_bfcp(glp_prob *lp, const glp_bfcp *parm) { glp_bfcp *bfcp = lp->bfcp; if (parm == NULL) { /* reset to default values */ if (bfcp != NULL) xfree(bfcp), lp->bfcp = NULL; } else { /* set to specified values */ if (bfcp == NULL) bfcp = lp->bfcp = xmalloc(sizeof(glp_bfcp)); memcpy(bfcp, parm, sizeof(glp_bfcp)); if (!(bfcp->type == GLP_BF_FT || bfcp->type == GLP_BF_BG || bfcp->type == GLP_BF_GR)) xerror("glp_set_bfcp: type = %d; invalid parameter\n", bfcp->type); if (bfcp->lu_size < 0) xerror("glp_set_bfcp: lu_size = %d; invalid parameter\n", bfcp->lu_size); if (!(0.0 < bfcp->piv_tol && bfcp->piv_tol < 1.0)) xerror("glp_set_bfcp: piv_tol = %g; invalid parameter\n", bfcp->piv_tol); if (bfcp->piv_lim < 1) xerror("glp_set_bfcp: piv_lim = %d; invalid parameter\n", bfcp->piv_lim); if (!(bfcp->suhl == GLP_ON || bfcp->suhl == GLP_OFF)) xerror("glp_set_bfcp: suhl = %d; invalid parameter\n", bfcp->suhl); if (!(0.0 <= bfcp->eps_tol && bfcp->eps_tol <= 1e-6)) xerror("glp_set_bfcp: eps_tol = %g; invalid parameter\n", bfcp->eps_tol); if (bfcp->max_gro < 1.0) xerror("glp_set_bfcp: max_gro = %g; invalid parameter\n", bfcp->max_gro); if (!(1 <= bfcp->nfs_max && bfcp->nfs_max <= 32767)) xerror("glp_set_bfcp: nfs_max = %d; invalid parameter\n", bfcp->nfs_max); if (!(0.0 < bfcp->upd_tol && bfcp->upd_tol < 1.0)) xerror("glp_set_bfcp: upd_tol = %g; invalid parameter\n", bfcp->upd_tol); if (!(1 <= bfcp->nrs_max && bfcp->nrs_max <= 32767)) xerror("glp_set_bfcp: nrs_max = %d; invalid parameter\n", bfcp->nrs_max); if (bfcp->rs_size < 0) xerror("glp_set_bfcp: rs_size = %d; invalid parameter\n", bfcp->nrs_max); if (bfcp->rs_size == 0) bfcp->rs_size = 20 * bfcp->nrs_max; } if (lp->bfd != NULL) copy_bfcp(lp); return; } /*********************************************************************** * NAME * * glp_get_bhead - retrieve the basis header information * * SYNOPSIS * * int glp_get_bhead(glp_prob *lp, int k); * * DESCRIPTION * * The routine glp_get_bhead returns the basis header information for * the current basis associated with the specified problem object. * * RETURNS * * If xB[k], 1 <= k <= m, is i-th auxiliary variable (1 <= i <= m), the * routine returns i. Otherwise, if xB[k] is j-th structural variable * (1 <= j <= n), the routine returns m+j. Here m is the number of rows * and n is the number of columns in the problem object. */ int glp_get_bhead(glp_prob *lp, int k) { if (!(lp->m == 0 || lp->valid)) xerror("glp_get_bhead: basis factorization does not exist\n"); if (!(1 <= k && k <= lp->m)) xerror("glp_get_bhead: k = %d; index out of range\n", k); return lp->head[k]; } /*********************************************************************** * NAME * * glp_get_row_bind - retrieve row index in the basis header * * SYNOPSIS * * int glp_get_row_bind(glp_prob *lp, int i); * * RETURNS * * The routine glp_get_row_bind returns the index k of basic variable * xB[k], 1 <= k <= m, which is i-th auxiliary variable, 1 <= i <= m, * in the current basis associated with the specified problem object, * where m is the number of rows. However, if i-th auxiliary variable * is non-basic, the routine returns zero. */ int glp_get_row_bind(glp_prob *lp, int i) { if (!(lp->m == 0 || lp->valid)) xerror("glp_get_row_bind: basis factorization does not exist\n" ); if (!(1 <= i && i <= lp->m)) xerror("glp_get_row_bind: i = %d; row number out of range\n", i); return lp->row[i]->bind; } /*********************************************************************** * NAME * * glp_get_col_bind - retrieve column index in the basis header * * SYNOPSIS * * int glp_get_col_bind(glp_prob *lp, int j); * * RETURNS * * The routine glp_get_col_bind returns the index k of basic variable * xB[k], 1 <= k <= m, which is j-th structural variable, 1 <= j <= n, * in the current basis associated with the specified problem object, * where m is the number of rows, n is the number of columns. However, * if j-th structural variable is non-basic, the routine returns zero.*/ int glp_get_col_bind(glp_prob *lp, int j) { if (!(lp->m == 0 || lp->valid)) xerror("glp_get_col_bind: basis factorization does not exist\n" ); if (!(1 <= j && j <= lp->n)) xerror("glp_get_col_bind: j = %d; column number out of range\n" , j); return lp->col[j]->bind; } /*********************************************************************** * NAME * * glp_ftran - perform forward transformation (solve system B*x = b) * * SYNOPSIS * * void glp_ftran(glp_prob *lp, double x[]); * * DESCRIPTION * * The routine glp_ftran performs forward transformation, i.e. solves * the system B*x = b, where B is the basis matrix corresponding to the * current basis for the specified problem object, x is the vector of * unknowns to be computed, b is the vector of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. * * SCALING/UNSCALING * * Let A~ = (I | -A) is the augmented constraint matrix of the original * (unscaled) problem. In the scaled LP problem instead the matrix A the * scaled matrix A" = R*A*S is actually used, so * * A~" = (I | A") = (I | R*A*S) = (R*I*inv(R) | R*A*S) = * (1) * = R*(I | A)*S~ = R*A~*S~, * * is the scaled augmented constraint matrix, where R and S are diagonal * scaling matrices used to scale rows and columns of the matrix A, and * * S~ = diag(inv(R) | S) (2) * * is an augmented diagonal scaling matrix. * * By definition: * * A~ = (B | N), (3) * * where B is the basic matrix, which consists of basic columns of the * augmented constraint matrix A~, and N is a matrix, which consists of * non-basic columns of A~. From (1) it follows that: * * A~" = (B" | N") = (R*B*SB | R*N*SN), (4) * * where SB and SN are parts of the augmented scaling matrix S~, which * correspond to basic and non-basic variables, respectively. Therefore * * B" = R*B*SB, (5) * * which is the scaled basis matrix. */ void glp_ftran(glp_prob *lp, double x[]) { int m = lp->m; GLPROW **row = lp->row; GLPCOL **col = lp->col; int i, k; /* B*x = b ===> (R*B*SB)*(inv(SB)*x) = R*b ===> B"*x" = b", where b" = R*b, x = SB*x" */ if (!(m == 0 || lp->valid)) xerror("glp_ftran: basis factorization does not exist\n"); /* b" := R*b */ for (i = 1; i <= m; i++) x[i] *= row[i]->rii; /* x" := inv(B")*b" */ if (m > 0) bfd_ftran(lp->bfd, x); /* x := SB*x" */ for (i = 1; i <= m; i++) { k = lp->head[i]; if (k <= m) x[i] /= row[k]->rii; else x[i] *= col[k-m]->sjj; } return; } /*********************************************************************** * NAME * * glp_btran - perform backward transformation (solve system B'*x = b) * * SYNOPSIS * * void glp_btran(glp_prob *lp, double x[]); * * DESCRIPTION * * The routine glp_btran performs backward transformation, i.e. solves * the system B'*x = b, where B' is a matrix transposed to the basis * matrix corresponding to the current basis for the specified problem * problem object, x is the vector of unknowns to be computed, b is the * vector of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. * * SCALING/UNSCALING * * See comments to the routine glp_ftran. */ void glp_btran(glp_prob *lp, double x[]) { int m = lp->m; GLPROW **row = lp->row; GLPCOL **col = lp->col; int i, k; /* B'*x = b ===> (SB*B'*R)*(inv(R)*x) = SB*b ===> (B")'*x" = b", where b" = SB*b, x = R*x" */ if (!(m == 0 || lp->valid)) xerror("glp_btran: basis factorization does not exist\n"); /* b" := SB*b */ for (i = 1; i <= m; i++) { k = lp->head[i]; if (k <= m) x[i] /= row[k]->rii; else x[i] *= col[k-m]->sjj; } /* x" := inv[(B")']*b" */ if (m > 0) bfd_btran(lp->bfd, x); /* x := R*x" */ for (i = 1; i <= m; i++) x[i] *= row[i]->rii; return; } /*********************************************************************** * NAME * * glp_warm_up - "warm up" LP basis * * SYNOPSIS * * int glp_warm_up(glp_prob *P); * * DESCRIPTION * * The routine glp_warm_up "warms up" the LP basis for the specified * problem object using current statuses assigned to rows and columns * (that is, to auxiliary and structural variables). * * This operation includes computing factorization of the basis matrix * (if it does not exist), computing primal and dual components of basic * solution, and determining the solution status. * * RETURNS * * 0 The operation has been successfully performed. * * GLP_EBADB * The basis matrix is invalid, i.e. the number of basic (auxiliary * and structural) variables differs from the number of rows in the * problem object. * * GLP_ESING * The basis matrix is singular within the working precision. * * GLP_ECOND * The basis matrix is ill-conditioned. */ int glp_warm_up(glp_prob *P) { GLPROW *row; GLPCOL *col; GLPAIJ *aij; int i, j, type, ret; double eps, temp, *work; /* invalidate basic solution */ P->pbs_stat = P->dbs_stat = GLP_UNDEF; P->obj_val = 0.0; P->some = 0; for (i = 1; i <= P->m; i++) { row = P->row[i]; row->prim = row->dual = 0.0; } for (j = 1; j <= P->n; j++) { col = P->col[j]; col->prim = col->dual = 0.0; } /* compute the basis factorization, if necessary */ if (!glp_bf_exists(P)) { ret = glp_factorize(P); if (ret != 0) goto done; } /* allocate working array */ work = xcalloc(1+P->m, sizeof(double)); /* determine and store values of non-basic variables, compute vector (- N * xN) */ for (i = 1; i <= P->m; i++) work[i] = 0.0; for (i = 1; i <= P->m; i++) { row = P->row[i]; if (row->stat == GLP_BS) continue; else if (row->stat == GLP_NL) row->prim = row->lb; else if (row->stat == GLP_NU) row->prim = row->ub; else if (row->stat == GLP_NF) row->prim = 0.0; else if (row->stat == GLP_NS) row->prim = row->lb; else xassert(row != row); /* N[j] is i-th column of matrix (I|-A) */ work[i] -= row->prim; } for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->stat == GLP_BS) continue; else if (col->stat == GLP_NL) col->prim = col->lb; else if (col->stat == GLP_NU) col->prim = col->ub; else if (col->stat == GLP_NF) col->prim = 0.0; else if (col->stat == GLP_NS) col->prim = col->lb; else xassert(col != col); /* N[j] is (m+j)-th column of matrix (I|-A) */ if (col->prim != 0.0) { for (aij = col->ptr; aij != NULL; aij = aij->c_next) work[aij->row->i] += aij->val * col->prim; } } /* compute vector of basic variables xB = - inv(B) * N * xN */ glp_ftran(P, work); /* store values of basic variables, check primal feasibility */ P->pbs_stat = GLP_FEAS; for (i = 1; i <= P->m; i++) { row = P->row[i]; if (row->stat != GLP_BS) continue; row->prim = work[row->bind]; type = row->type; if (type == GLP_LO || type == GLP_DB || type == GLP_FX) { eps = 1e-6 + 1e-9 * fabs(row->lb); if (row->prim < row->lb - eps) P->pbs_stat = GLP_INFEAS; } if (type == GLP_UP || type == GLP_DB || type == GLP_FX) { eps = 1e-6 + 1e-9 * fabs(row->ub); if (row->prim > row->ub + eps) P->pbs_stat = GLP_INFEAS; } } for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->stat != GLP_BS) continue; col->prim = work[col->bind]; type = col->type; if (type == GLP_LO || type == GLP_DB || type == GLP_FX) { eps = 1e-6 + 1e-9 * fabs(col->lb); if (col->prim < col->lb - eps) P->pbs_stat = GLP_INFEAS; } if (type == GLP_UP || type == GLP_DB || type == GLP_FX) { eps = 1e-6 + 1e-9 * fabs(col->ub); if (col->prim > col->ub + eps) P->pbs_stat = GLP_INFEAS; } } /* compute value of the objective function */ P->obj_val = P->c0; for (j = 1; j <= P->n; j++) { col = P->col[j]; P->obj_val += col->coef * col->prim; } /* build vector cB of objective coefficients at basic variables */ for (i = 1; i <= P->m; i++) work[i] = 0.0; for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->stat == GLP_BS) work[col->bind] = col->coef; } /* compute vector of simplex multipliers pi = inv(B') * cB */ glp_btran(P, work); /* compute and store reduced costs of non-basic variables d[j] = c[j] - N'[j] * pi, check dual feasibility */ P->dbs_stat = GLP_FEAS; for (i = 1; i <= P->m; i++) { row = P->row[i]; if (row->stat == GLP_BS) { row->dual = 0.0; continue; } /* N[j] is i-th column of matrix (I|-A) */ row->dual = - work[i]; type = row->type; temp = (P->dir == GLP_MIN ? + row->dual : - row->dual); if ((type == GLP_FR || type == GLP_LO) && temp < -1e-5 || (type == GLP_FR || type == GLP_UP) && temp > +1e-5) P->dbs_stat = GLP_INFEAS; } for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->stat == GLP_BS) { col->dual = 0.0; continue; } /* N[j] is (m+j)-th column of matrix (I|-A) */ col->dual = col->coef; for (aij = col->ptr; aij != NULL; aij = aij->c_next) col->dual += aij->val * work[aij->row->i]; type = col->type; temp = (P->dir == GLP_MIN ? + col->dual : - col->dual); if ((type == GLP_FR || type == GLP_LO) && temp < -1e-5 || (type == GLP_FR || type == GLP_UP) && temp > +1e-5) P->dbs_stat = GLP_INFEAS; } /* free working array */ xfree(work); ret = 0; done: return ret; } /*********************************************************************** * NAME * * glp_eval_tab_row - compute row of the simplex tableau * * SYNOPSIS * * int glp_eval_tab_row(glp_prob *lp, int k, int ind[], double val[]); * * DESCRIPTION * * The routine glp_eval_tab_row computes a row of the current simplex * tableau for the basic variable, which is specified by the number k: * if 1 <= k <= m, x[k] is k-th auxiliary variable; if m+1 <= k <= m+n, * x[k] is (k-m)-th structural variable, where m is number of rows, and * n is number of columns. The current basis must be available. * * The routine stores column indices and numerical values of non-zero * elements of the computed row using sparse format to the locations * ind[1], ..., ind[len] and val[1], ..., val[len], respectively, where * 0 <= len <= n is number of non-zeros returned on exit. * * Element indices stored in the array ind have the same sense as the * index k, i.e. indices 1 to m denote auxiliary variables and indices * m+1 to m+n denote structural ones (all these variables are obviously * non-basic by definition). * * The computed row shows how the specified basic variable x[k] = xB[i] * depends on non-basic variables: * * xB[i] = alfa[i,1]*xN[1] + alfa[i,2]*xN[2] + ... + alfa[i,n]*xN[n], * * where alfa[i,j] are elements of the simplex table row, xN[j] are * non-basic (auxiliary and structural) variables. * * RETURNS * * The routine returns number of non-zero elements in the simplex table * row stored in the arrays ind and val. * * BACKGROUND * * The system of equality constraints of the LP problem is: * * xR = A * xS, (1) * * where xR is the vector of auxliary variables, xS is the vector of * structural variables, A is the matrix of constraint coefficients. * * The system (1) can be written in homogenous form as follows: * * A~ * x = 0, (2) * * where A~ = (I | -A) is the augmented constraint matrix (has m rows * and m+n columns), x = (xR | xS) is the vector of all (auxiliary and * structural) variables. * * By definition for the current basis we have: * * A~ = (B | N), (3) * * where B is the basis matrix. Thus, the system (2) can be written as: * * B * xB + N * xN = 0. (4) * * From (4) it follows that: * * xB = A^ * xN, (5) * * where the matrix * * A^ = - inv(B) * N (6) * * is called the simplex table. * * It is understood that i-th row of the simplex table is: * * e * A^ = - e * inv(B) * N, (7) * * where e is a unity vector with e[i] = 1. * * To compute i-th row of the simplex table the routine first computes * i-th row of the inverse: * * rho = inv(B') * e, (8) * * where B' is a matrix transposed to B, and then computes elements of * i-th row of the simplex table as scalar products: * * alfa[i,j] = - rho * N[j] for all j, (9) * * where N[j] is a column of the augmented constraint matrix A~, which * corresponds to some non-basic auxiliary or structural variable. */ int glp_eval_tab_row(glp_prob *lp, int k, int ind[], double val[]) { int m = lp->m; int n = lp->n; int i, t, len, lll, *iii; double alfa, *rho, *vvv; if (!(m == 0 || lp->valid)) xerror("glp_eval_tab_row: basis factorization does not exist\n" ); if (!(1 <= k && k <= m+n)) xerror("glp_eval_tab_row: k = %d; variable number out of range" , k); /* determine xB[i] which corresponds to x[k] */ if (k <= m) i = glp_get_row_bind(lp, k); else i = glp_get_col_bind(lp, k-m); if (i == 0) xerror("glp_eval_tab_row: k = %d; variable must be basic", k); xassert(1 <= i && i <= m); /* allocate working arrays */ rho = xcalloc(1+m, sizeof(double)); iii = xcalloc(1+m, sizeof(int)); vvv = xcalloc(1+m, sizeof(double)); /* compute i-th row of the inverse; see (8) */ for (t = 1; t <= m; t++) rho[t] = 0.0; rho[i] = 1.0; glp_btran(lp, rho); /* compute i-th row of the simplex table */ len = 0; for (k = 1; k <= m+n; k++) { if (k <= m) { /* x[k] is auxiliary variable, so N[k] is a unity column */ if (glp_get_row_stat(lp, k) == GLP_BS) continue; /* compute alfa[i,j]; see (9) */ alfa = - rho[k]; } else { /* x[k] is structural variable, so N[k] is a column of the original constraint matrix A with negative sign */ if (glp_get_col_stat(lp, k-m) == GLP_BS) continue; /* compute alfa[i,j]; see (9) */ lll = glp_get_mat_col(lp, k-m, iii, vvv); alfa = 0.0; for (t = 1; t <= lll; t++) alfa += rho[iii[t]] * vvv[t]; } /* store alfa[i,j] */ if (alfa != 0.0) len++, ind[len] = k, val[len] = alfa; } xassert(len <= n); /* free working arrays */ xfree(rho); xfree(iii); xfree(vvv); /* return to the calling program */ return len; } /*********************************************************************** * NAME * * glp_eval_tab_col - compute column of the simplex tableau * * SYNOPSIS * * int glp_eval_tab_col(glp_prob *lp, int k, int ind[], double val[]); * * DESCRIPTION * * The routine glp_eval_tab_col computes a column of the current simplex * table for the non-basic variable, which is specified by the number k: * if 1 <= k <= m, x[k] is k-th auxiliary variable; if m+1 <= k <= m+n, * x[k] is (k-m)-th structural variable, where m is number of rows, and * n is number of columns. The current basis must be available. * * The routine stores row indices and numerical values of non-zero * elements of the computed column using sparse format to the locations * ind[1], ..., ind[len] and val[1], ..., val[len] respectively, where * 0 <= len <= m is number of non-zeros returned on exit. * * Element indices stored in the array ind have the same sense as the * index k, i.e. indices 1 to m denote auxiliary variables and indices * m+1 to m+n denote structural ones (all these variables are obviously * basic by the definition). * * The computed column shows how basic variables depend on the specified * non-basic variable x[k] = xN[j]: * * xB[1] = ... + alfa[1,j]*xN[j] + ... * xB[2] = ... + alfa[2,j]*xN[j] + ... * . . . . . . * xB[m] = ... + alfa[m,j]*xN[j] + ... * * where alfa[i,j] are elements of the simplex table column, xB[i] are * basic (auxiliary and structural) variables. * * RETURNS * * The routine returns number of non-zero elements in the simplex table * column stored in the arrays ind and val. * * BACKGROUND * * As it was explained in comments to the routine glp_eval_tab_row (see * above) the simplex table is the following matrix: * * A^ = - inv(B) * N. (1) * * Therefore j-th column of the simplex table is: * * A^ * e = - inv(B) * N * e = - inv(B) * N[j], (2) * * where e is a unity vector with e[j] = 1, B is the basis matrix, N[j] * is a column of the augmented constraint matrix A~, which corresponds * to the given non-basic auxiliary or structural variable. */ int glp_eval_tab_col(glp_prob *lp, int k, int ind[], double val[]) { int m = lp->m; int n = lp->n; int t, len, stat; double *col; if (!(m == 0 || lp->valid)) xerror("glp_eval_tab_col: basis factorization does not exist\n" ); if (!(1 <= k && k <= m+n)) xerror("glp_eval_tab_col: k = %d; variable number out of range" , k); if (k <= m) stat = glp_get_row_stat(lp, k); else stat = glp_get_col_stat(lp, k-m); if (stat == GLP_BS) xerror("glp_eval_tab_col: k = %d; variable must be non-basic", k); /* obtain column N[k] with negative sign */ col = xcalloc(1+m, sizeof(double)); for (t = 1; t <= m; t++) col[t] = 0.0; if (k <= m) { /* x[k] is auxiliary variable, so N[k] is a unity column */ col[k] = -1.0; } else { /* x[k] is structural variable, so N[k] is a column of the original constraint matrix A with negative sign */ len = glp_get_mat_col(lp, k-m, ind, val); for (t = 1; t <= len; t++) col[ind[t]] = val[t]; } /* compute column of the simplex table, which corresponds to the specified non-basic variable x[k] */ glp_ftran(lp, col); len = 0; for (t = 1; t <= m; t++) { if (col[t] != 0.0) { len++; ind[len] = glp_get_bhead(lp, t); val[len] = col[t]; } } xfree(col); /* return to the calling program */ return len; } /*********************************************************************** * NAME * * glp_transform_row - transform explicitly specified row * * SYNOPSIS * * int glp_transform_row(glp_prob *P, int len, int ind[], double val[]); * * DESCRIPTION * * The routine glp_transform_row performs the same operation as the * routine glp_eval_tab_row with exception that the row to be * transformed is specified explicitly as a sparse vector. * * The explicitly specified row may be thought as a linear form: * * x = a[1]*x[m+1] + a[2]*x[m+2] + ... + a[n]*x[m+n], (1) * * where x is an auxiliary variable for this row, a[j] are coefficients * of the linear form, x[m+j] are structural variables. * * On entry column indices and numerical values of non-zero elements of * the row should be stored in locations ind[1], ..., ind[len] and * val[1], ..., val[len], where len is the number of non-zero elements. * * This routine uses the system of equality constraints and the current * basis in order to express the auxiliary variable x in (1) through the * current non-basic variables (as if the transformed row were added to * the problem object and its auxiliary variable were basic), i.e. the * resultant row has the form: * * x = alfa[1]*xN[1] + alfa[2]*xN[2] + ... + alfa[n]*xN[n], (2) * * where xN[j] are non-basic (auxiliary or structural) variables, n is * the number of columns in the LP problem object. * * On exit the routine stores indices and numerical values of non-zero * elements of the resultant row (2) in locations ind[1], ..., ind[len'] * and val[1], ..., val[len'], where 0 <= len' <= n is the number of * non-zero elements in the resultant row returned by the routine. Note * that indices (numbers) of non-basic variables stored in the array ind * correspond to original ordinal numbers of variables: indices 1 to m * mean auxiliary variables and indices m+1 to m+n mean structural ones. * * RETURNS * * The routine returns len', which is the number of non-zero elements in * the resultant row stored in the arrays ind and val. * * BACKGROUND * * The explicitly specified row (1) is transformed in the same way as it * were the objective function row. * * From (1) it follows that: * * x = aB * xB + aN * xN, (3) * * where xB is the vector of basic variables, xN is the vector of * non-basic variables. * * The simplex table, which corresponds to the current basis, is: * * xB = [-inv(B) * N] * xN. (4) * * Therefore substituting xB from (4) to (3) we have: * * x = aB * [-inv(B) * N] * xN + aN * xN = * (5) * = rho * (-N) * xN + aN * xN = alfa * xN, * * where: * * rho = inv(B') * aB, (6) * * and * * alfa = aN + rho * (-N) (7) * * is the resultant row computed by the routine. */ int glp_transform_row(glp_prob *P, int len, int ind[], double val[]) { int i, j, k, m, n, t, lll, *iii; double alfa, *a, *aB, *rho, *vvv; if (!glp_bf_exists(P)) xerror("glp_transform_row: basis factorization does not exist " "\n"); m = glp_get_num_rows(P); n = glp_get_num_cols(P); /* unpack the row to be transformed to the array a */ a = xcalloc(1+n, sizeof(double)); for (j = 1; j <= n; j++) a[j] = 0.0; if (!(0 <= len && len <= n)) xerror("glp_transform_row: len = %d; invalid row length\n", len); for (t = 1; t <= len; t++) { j = ind[t]; if (!(1 <= j && j <= n)) xerror("glp_transform_row: ind[%d] = %d; column index out o" "f range\n", t, j); if (val[t] == 0.0) xerror("glp_transform_row: val[%d] = 0; zero coefficient no" "t allowed\n", t); if (a[j] != 0.0) xerror("glp_transform_row: ind[%d] = %d; duplicate column i" "ndices not allowed\n", t, j); a[j] = val[t]; } /* construct the vector aB */ aB = xcalloc(1+m, sizeof(double)); for (i = 1; i <= m; i++) { k = glp_get_bhead(P, i); /* xB[i] is k-th original variable */ xassert(1 <= k && k <= m+n); aB[i] = (k <= m ? 0.0 : a[k-m]); } /* solve the system B'*rho = aB to compute the vector rho */ rho = aB, glp_btran(P, rho); /* compute coefficients at non-basic auxiliary variables */ len = 0; for (i = 1; i <= m; i++) { if (glp_get_row_stat(P, i) != GLP_BS) { alfa = - rho[i]; if (alfa != 0.0) { len++; ind[len] = i; val[len] = alfa; } } } /* compute coefficients at non-basic structural variables */ iii = xcalloc(1+m, sizeof(int)); vvv = xcalloc(1+m, sizeof(double)); for (j = 1; j <= n; j++) { if (glp_get_col_stat(P, j) != GLP_BS) { alfa = a[j]; lll = glp_get_mat_col(P, j, iii, vvv); for (t = 1; t <= lll; t++) alfa += vvv[t] * rho[iii[t]]; if (alfa != 0.0) { len++; ind[len] = m+j; val[len] = alfa; } } } xassert(len <= n); xfree(iii); xfree(vvv); xfree(aB); xfree(a); return len; } /*********************************************************************** * NAME * * glp_transform_col - transform explicitly specified column * * SYNOPSIS * * int glp_transform_col(glp_prob *P, int len, int ind[], double val[]); * * DESCRIPTION * * The routine glp_transform_col performs the same operation as the * routine glp_eval_tab_col with exception that the column to be * transformed is specified explicitly as a sparse vector. * * The explicitly specified column may be thought as if it were added * to the original system of equality constraints: * * x[1] = a[1,1]*x[m+1] + ... + a[1,n]*x[m+n] + a[1]*x * x[2] = a[2,1]*x[m+1] + ... + a[2,n]*x[m+n] + a[2]*x (1) * . . . . . . . . . . . . . . . * x[m] = a[m,1]*x[m+1] + ... + a[m,n]*x[m+n] + a[m]*x * * where x[i] are auxiliary variables, x[m+j] are structural variables, * x is a structural variable for the explicitly specified column, a[i] * are constraint coefficients for x. * * On entry row indices and numerical values of non-zero elements of * the column should be stored in locations ind[1], ..., ind[len] and * val[1], ..., val[len], where len is the number of non-zero elements. * * This routine uses the system of equality constraints and the current * basis in order to express the current basic variables through the * structural variable x in (1) (as if the transformed column were added * to the problem object and the variable x were non-basic), i.e. the * resultant column has the form: * * xB[1] = ... + alfa[1]*x * xB[2] = ... + alfa[2]*x (2) * . . . . . . * xB[m] = ... + alfa[m]*x * * where xB are basic (auxiliary and structural) variables, m is the * number of rows in the problem object. * * On exit the routine stores indices and numerical values of non-zero * elements of the resultant column (2) in locations ind[1], ..., * ind[len'] and val[1], ..., val[len'], where 0 <= len' <= m is the * number of non-zero element in the resultant column returned by the * routine. Note that indices (numbers) of basic variables stored in * the array ind correspond to original ordinal numbers of variables: * indices 1 to m mean auxiliary variables and indices m+1 to m+n mean * structural ones. * * RETURNS * * The routine returns len', which is the number of non-zero elements * in the resultant column stored in the arrays ind and val. * * BACKGROUND * * The explicitly specified column (1) is transformed in the same way * as any other column of the constraint matrix using the formula: * * alfa = inv(B) * a, (3) * * where alfa is the resultant column computed by the routine. */ int glp_transform_col(glp_prob *P, int len, int ind[], double val[]) { int i, m, t; double *a, *alfa; if (!glp_bf_exists(P)) xerror("glp_transform_col: basis factorization does not exist " "\n"); m = glp_get_num_rows(P); /* unpack the column to be transformed to the array a */ a = xcalloc(1+m, sizeof(double)); for (i = 1; i <= m; i++) a[i] = 0.0; if (!(0 <= len && len <= m)) xerror("glp_transform_col: len = %d; invalid column length\n", len); for (t = 1; t <= len; t++) { i = ind[t]; if (!(1 <= i && i <= m)) xerror("glp_transform_col: ind[%d] = %d; row index out of r" "ange\n", t, i); if (val[t] == 0.0) xerror("glp_transform_col: val[%d] = 0; zero coefficient no" "t allowed\n", t); if (a[i] != 0.0) xerror("glp_transform_col: ind[%d] = %d; duplicate row indi" "ces not allowed\n", t, i); a[i] = val[t]; } /* solve the system B*a = alfa to compute the vector alfa */ alfa = a, glp_ftran(P, alfa); /* store resultant coefficients */ len = 0; for (i = 1; i <= m; i++) { if (alfa[i] != 0.0) { len++; ind[len] = glp_get_bhead(P, i); val[len] = alfa[i]; } } xfree(a); return len; } /*********************************************************************** * NAME * * glp_prim_rtest - perform primal ratio test * * SYNOPSIS * * int glp_prim_rtest(glp_prob *P, int len, const int ind[], * const double val[], int dir, double eps); * * DESCRIPTION * * The routine glp_prim_rtest performs the primal ratio test using an * explicitly specified column of the simplex table. * * The current basic solution associated with the LP problem object * must be primal feasible. * * The explicitly specified column of the simplex table shows how the * basic variables xB depend on some non-basic variable x (which is not * necessarily presented in the problem object): * * xB[1] = ... + alfa[1] * x + ... * xB[2] = ... + alfa[2] * x + ... (*) * . . . . . . . . * xB[m] = ... + alfa[m] * x + ... * * The column (*) is specifed on entry to the routine using the sparse * format. Ordinal numbers of basic variables xB[i] should be placed in * locations ind[1], ..., ind[len], where ordinal number 1 to m denote * auxiliary variables, and ordinal numbers m+1 to m+n denote structural * variables. The corresponding non-zero coefficients alfa[i] should be * placed in locations val[1], ..., val[len]. The arrays ind and val are * not changed on exit. * * The parameter dir specifies direction in which the variable x changes * on entering the basis: +1 means increasing, -1 means decreasing. * * The parameter eps is an absolute tolerance (small positive number) * used by the routine to skip small alfa[j] of the row (*). * * The routine determines which basic variable (among specified in * ind[1], ..., ind[len]) should leave the basis in order to keep primal * feasibility. * * RETURNS * * The routine glp_prim_rtest returns the index piv in the arrays ind * and val corresponding to the pivot element chosen, 1 <= piv <= len. * If the adjacent basic solution is primal unbounded and therefore the * choice cannot be made, the routine returns zero. * * COMMENTS * * If the non-basic variable x is presented in the LP problem object, * the column (*) can be computed with the routine glp_eval_tab_col; * otherwise it can be computed with the routine glp_transform_col. */ int glp_prim_rtest(glp_prob *P, int len, const int ind[], const double val[], int dir, double eps) { int k, m, n, piv, t, type, stat; double alfa, big, beta, lb, ub, temp, teta; if (glp_get_prim_stat(P) != GLP_FEAS) xerror("glp_prim_rtest: basic solution is not primal feasible " "\n"); if (!(dir == +1 || dir == -1)) xerror("glp_prim_rtest: dir = %d; invalid parameter\n", dir); if (!(0.0 < eps && eps < 1.0)) xerror("glp_prim_rtest: eps = %g; invalid parameter\n", eps); m = glp_get_num_rows(P); n = glp_get_num_cols(P); /* initial settings */ piv = 0, teta = DBL_MAX, big = 0.0; /* walk through the entries of the specified column */ for (t = 1; t <= len; t++) { /* get the ordinal number of basic variable */ k = ind[t]; if (!(1 <= k && k <= m+n)) xerror("glp_prim_rtest: ind[%d] = %d; variable number out o" "f range\n", t, k); /* determine type, bounds, status and primal value of basic variable xB[i] = x[k] in the current basic solution */ if (k <= m) { type = glp_get_row_type(P, k); lb = glp_get_row_lb(P, k); ub = glp_get_row_ub(P, k); stat = glp_get_row_stat(P, k); beta = glp_get_row_prim(P, k); } else { type = glp_get_col_type(P, k-m); lb = glp_get_col_lb(P, k-m); ub = glp_get_col_ub(P, k-m); stat = glp_get_col_stat(P, k-m); beta = glp_get_col_prim(P, k-m); } if (stat != GLP_BS) xerror("glp_prim_rtest: ind[%d] = %d; non-basic variable no" "t allowed\n", t, k); /* determine influence coefficient at basic variable xB[i] in the explicitly specified column and turn to the case of increasing the variable x in order to simplify the program logic */ alfa = (dir > 0 ? + val[t] : - val[t]); /* analyze main cases */ if (type == GLP_FR) { /* xB[i] is free variable */ continue; } else if (type == GLP_LO) lo: { /* xB[i] has an lower bound */ if (alfa > - eps) continue; temp = (lb - beta) / alfa; } else if (type == GLP_UP) up: { /* xB[i] has an upper bound */ if (alfa < + eps) continue; temp = (ub - beta) / alfa; } else if (type == GLP_DB) { /* xB[i] has both lower and upper bounds */ if (alfa < 0.0) goto lo; else goto up; } else if (type == GLP_FX) { /* xB[i] is fixed variable */ if (- eps < alfa && alfa < + eps) continue; temp = 0.0; } else xassert(type != type); /* if the value of the variable xB[i] violates its lower or upper bound (slightly, because the current basis is assumed to be primal feasible), temp is negative; we can think this happens due to round-off errors and the value is exactly on the bound; this allows replacing temp by zero */ if (temp < 0.0) temp = 0.0; /* apply the minimal ratio test */ if (teta > temp || teta == temp && big < fabs(alfa)) piv = t, teta = temp, big = fabs(alfa); } /* return index of the pivot element chosen */ return piv; } /*********************************************************************** * NAME * * glp_dual_rtest - perform dual ratio test * * SYNOPSIS * * int glp_dual_rtest(glp_prob *P, int len, const int ind[], * const double val[], int dir, double eps); * * DESCRIPTION * * The routine glp_dual_rtest performs the dual ratio test using an * explicitly specified row of the simplex table. * * The current basic solution associated with the LP problem object * must be dual feasible. * * The explicitly specified row of the simplex table is a linear form * that shows how some basic variable x (which is not necessarily * presented in the problem object) depends on non-basic variables xN: * * x = alfa[1] * xN[1] + alfa[2] * xN[2] + ... + alfa[n] * xN[n]. (*) * * The row (*) is specified on entry to the routine using the sparse * format. Ordinal numbers of non-basic variables xN[j] should be placed * in locations ind[1], ..., ind[len], where ordinal numbers 1 to m * denote auxiliary variables, and ordinal numbers m+1 to m+n denote * structural variables. The corresponding non-zero coefficients alfa[j] * should be placed in locations val[1], ..., val[len]. The arrays ind * and val are not changed on exit. * * The parameter dir specifies direction in which the variable x changes * on leaving the basis: +1 means that x goes to its lower bound, and -1 * means that x goes to its upper bound. * * The parameter eps is an absolute tolerance (small positive number) * used by the routine to skip small alfa[j] of the row (*). * * The routine determines which non-basic variable (among specified in * ind[1], ..., ind[len]) should enter the basis in order to keep dual * feasibility. * * RETURNS * * The routine glp_dual_rtest returns the index piv in the arrays ind * and val corresponding to the pivot element chosen, 1 <= piv <= len. * If the adjacent basic solution is dual unbounded and therefore the * choice cannot be made, the routine returns zero. * * COMMENTS * * If the basic variable x is presented in the LP problem object, the * row (*) can be computed with the routine glp_eval_tab_row; otherwise * it can be computed with the routine glp_transform_row. */ int glp_dual_rtest(glp_prob *P, int len, const int ind[], const double val[], int dir, double eps) { int k, m, n, piv, t, stat; double alfa, big, cost, obj, temp, teta; if (glp_get_dual_stat(P) != GLP_FEAS) xerror("glp_dual_rtest: basic solution is not dual feasible\n") ; if (!(dir == +1 || dir == -1)) xerror("glp_dual_rtest: dir = %d; invalid parameter\n", dir); if (!(0.0 < eps && eps < 1.0)) xerror("glp_dual_rtest: eps = %g; invalid parameter\n", eps); m = glp_get_num_rows(P); n = glp_get_num_cols(P); /* take into account optimization direction */ obj = (glp_get_obj_dir(P) == GLP_MIN ? +1.0 : -1.0); /* initial settings */ piv = 0, teta = DBL_MAX, big = 0.0; /* walk through the entries of the specified row */ for (t = 1; t <= len; t++) { /* get ordinal number of non-basic variable */ k = ind[t]; if (!(1 <= k && k <= m+n)) xerror("glp_dual_rtest: ind[%d] = %d; variable number out o" "f range\n", t, k); /* determine status and reduced cost of non-basic variable x[k] = xN[j] in the current basic solution */ if (k <= m) { stat = glp_get_row_stat(P, k); cost = glp_get_row_dual(P, k); } else { stat = glp_get_col_stat(P, k-m); cost = glp_get_col_dual(P, k-m); } if (stat == GLP_BS) xerror("glp_dual_rtest: ind[%d] = %d; basic variable not al" "lowed\n", t, k); /* determine influence coefficient at non-basic variable xN[j] in the explicitly specified row and turn to the case of increasing the variable x in order to simplify the program logic */ alfa = (dir > 0 ? + val[t] : - val[t]); /* analyze main cases */ if (stat == GLP_NL) { /* xN[j] is on its lower bound */ if (alfa < + eps) continue; temp = (obj * cost) / alfa; } else if (stat == GLP_NU) { /* xN[j] is on its upper bound */ if (alfa > - eps) continue; temp = (obj * cost) / alfa; } else if (stat == GLP_NF) { /* xN[j] is non-basic free variable */ if (- eps < alfa && alfa < + eps) continue; temp = 0.0; } else if (stat == GLP_NS) { /* xN[j] is non-basic fixed variable */ continue; } else xassert(stat != stat); /* if the reduced cost of the variable xN[j] violates its zero bound (slightly, because the current basis is assumed to be dual feasible), temp is negative; we can think this happens due to round-off errors and the reduced cost is exact zero; this allows replacing temp by zero */ if (temp < 0.0) temp = 0.0; /* apply the minimal ratio test */ if (teta > temp || teta == temp && big < fabs(alfa)) piv = t, teta = temp, big = fabs(alfa); } /* return index of the pivot element chosen */ return piv; } /*********************************************************************** * NAME * * glp_analyze_row - simulate one iteration of dual simplex method * * SYNOPSIS * * int glp_analyze_row(glp_prob *P, int len, const int ind[], * const double val[], int type, double rhs, double eps, int *piv, * double *x, double *dx, double *y, double *dy, double *dz); * * DESCRIPTION * * Let the current basis be optimal or dual feasible, and there be * specified a row (constraint), which is violated by the current basic * solution. The routine glp_analyze_row simulates one iteration of the * dual simplex method to determine some information on the adjacent * basis (see below), where the specified row becomes active constraint * (i.e. its auxiliary variable becomes non-basic). * * The current basic solution associated with the problem object passed * to the routine must be dual feasible, and its primal components must * be defined. * * The row to be analyzed must be previously transformed either with * the routine glp_eval_tab_row (if the row is in the problem object) * or with the routine glp_transform_row (if the row is external, i.e. * not in the problem object). This is needed to express the row only * through (auxiliary and structural) variables, which are non-basic in * the current basis: * * y = alfa[1] * xN[1] + alfa[2] * xN[2] + ... + alfa[n] * xN[n], * * where y is an auxiliary variable of the row, alfa[j] is an influence * coefficient, xN[j] is a non-basic variable. * * The row is passed to the routine in sparse format. Ordinal numbers * of non-basic variables are stored in locations ind[1], ..., ind[len], * where numbers 1 to m denote auxiliary variables while numbers m+1 to * m+n denote structural variables. Corresponding non-zero coefficients * alfa[j] are stored in locations val[1], ..., val[len]. The arrays * ind and val are ot changed on exit. * * The parameters type and rhs specify the row type and its right-hand * side as follows: * * type = GLP_LO: y = sum alfa[j] * xN[j] >= rhs * * type = GLP_UP: y = sum alfa[j] * xN[j] <= rhs * * The parameter eps is an absolute tolerance (small positive number) * used by the routine to skip small coefficients alfa[j] on performing * the dual ratio test. * * If the operation was successful, the routine stores the following * information to corresponding location (if some parameter is NULL, * its value is not stored): * * piv index in the array ind and val, 1 <= piv <= len, determining * the non-basic variable, which would enter the adjacent basis; * * x value of the non-basic variable in the current basis; * * dx difference between values of the non-basic variable in the * adjacent and current bases, dx = x.new - x.old; * * y value of the row (i.e. of its auxiliary variable) in the * current basis; * * dy difference between values of the row in the adjacent and * current bases, dy = y.new - y.old; * * dz difference between values of the objective function in the * adjacent and current bases, dz = z.new - z.old. Note that in * case of minimization dz >= 0, and in case of maximization * dz <= 0, i.e. in the adjacent basis the objective function * always gets worse (degrades). */ int _glp_analyze_row(glp_prob *P, int len, const int ind[], const double val[], int type, double rhs, double eps, int *_piv, double *_x, double *_dx, double *_y, double *_dy, double *_dz) { int t, k, dir, piv, ret = 0; double x, dx, y, dy, dz; if (P->pbs_stat == GLP_UNDEF) xerror("glp_analyze_row: primal basic solution components are " "undefined\n"); if (P->dbs_stat != GLP_FEAS) xerror("glp_analyze_row: basic solution is not dual feasible\n" ); /* compute the row value y = sum alfa[j] * xN[j] in the current basis */ if (!(0 <= len && len <= P->n)) xerror("glp_analyze_row: len = %d; invalid row length\n", len); y = 0.0; for (t = 1; t <= len; t++) { /* determine value of x[k] = xN[j] in the current basis */ k = ind[t]; if (!(1 <= k && k <= P->m+P->n)) xerror("glp_analyze_row: ind[%d] = %d; row/column index out" " of range\n", t, k); if (k <= P->m) { /* x[k] is auxiliary variable */ if (P->row[k]->stat == GLP_BS) xerror("glp_analyze_row: ind[%d] = %d; basic auxiliary v" "ariable is not allowed\n", t, k); x = P->row[k]->prim; } else { /* x[k] is structural variable */ if (P->col[k-P->m]->stat == GLP_BS) xerror("glp_analyze_row: ind[%d] = %d; basic structural " "variable is not allowed\n", t, k); x = P->col[k-P->m]->prim; } y += val[t] * x; } /* check if the row is primal infeasible in the current basis, i.e. the constraint is violated at the current point */ if (type == GLP_LO) { if (y >= rhs) { /* the constraint is not violated */ ret = 1; goto done; } /* in the adjacent basis y goes to its lower bound */ dir = +1; } else if (type == GLP_UP) { if (y <= rhs) { /* the constraint is not violated */ ret = 1; goto done; } /* in the adjacent basis y goes to its upper bound */ dir = -1; } else xerror("glp_analyze_row: type = %d; invalid parameter\n", type); /* compute dy = y.new - y.old */ dy = rhs - y; /* perform dual ratio test to determine which non-basic variable should enter the adjacent basis to keep it dual feasible */ piv = glp_dual_rtest(P, len, ind, val, dir, eps); if (piv == 0) { /* no dual feasible adjacent basis exists */ ret = 2; goto done; } /* non-basic variable x[k] = xN[j] should enter the basis */ k = ind[piv]; xassert(1 <= k && k <= P->m+P->n); /* determine its value in the current basis */ if (k <= P->m) x = P->row[k]->prim; else x = P->col[k-P->m]->prim; /* compute dx = x.new - x.old = dy / alfa[j] */ xassert(val[piv] != 0.0); dx = dy / val[piv]; /* compute dz = z.new - z.old = d[j] * dx, where d[j] is reduced cost of xN[j] in the current basis */ if (k <= P->m) dz = P->row[k]->dual * dx; else dz = P->col[k-P->m]->dual * dx; /* store the analysis results */ if (_piv != NULL) *_piv = piv; if (_x != NULL) *_x = x; if (_dx != NULL) *_dx = dx; if (_y != NULL) *_y = y; if (_dy != NULL) *_dy = dy; if (_dz != NULL) *_dz = dz; done: return ret; } #if 0 int main(void) { /* example program for the routine glp_analyze_row */ glp_prob *P; glp_smcp parm; int i, k, len, piv, ret, ind[1+100]; double rhs, x, dx, y, dy, dz, val[1+100]; P = glp_create_prob(); /* read plan.mps (see glpk/examples) */ ret = glp_read_mps(P, GLP_MPS_DECK, NULL, "plan.mps"); glp_assert(ret == 0); /* and solve it to optimality */ ret = glp_simplex(P, NULL); glp_assert(ret == 0); glp_assert(glp_get_status(P) == GLP_OPT); /* the optimal objective value is 296.217 */ /* we would like to know what happens if we would add a new row (constraint) to plan.mps: .01 * bin1 + .01 * bin2 + .02 * bin4 + .02 * bin5 <= 12 */ /* first, we specify this new row */ glp_create_index(P); len = 0; ind[++len] = glp_find_col(P, "BIN1"), val[len] = .01; ind[++len] = glp_find_col(P, "BIN2"), val[len] = .01; ind[++len] = glp_find_col(P, "BIN4"), val[len] = .02; ind[++len] = glp_find_col(P, "BIN5"), val[len] = .02; rhs = 12; /* then we can compute value of the row (i.e. of its auxiliary variable) in the current basis to see if the constraint is violated */ y = 0.0; for (k = 1; k <= len; k++) y += val[k] * glp_get_col_prim(P, ind[k]); glp_printf("y = %g\n", y); /* this prints y = 15.1372, so the constraint is violated, since we require that y <= rhs = 12 */ /* now we transform the row to express it only through non-basic (auxiliary and artificial) variables */ len = glp_transform_row(P, len, ind, val); /* finally, we simulate one step of the dual simplex method to obtain necessary information for the adjacent basis */ ret = _glp_analyze_row(P, len, ind, val, GLP_UP, rhs, 1e-9, &piv, &x, &dx, &y, &dy, &dz); glp_assert(ret == 0); glp_printf("k = %d, x = %g; dx = %g; y = %g; dy = %g; dz = %g\n", ind[piv], x, dx, y, dy, dz); /* this prints dz = 5.64418 and means that in the adjacent basis the objective function would be 296.217 + 5.64418 = 301.861 */ /* now we actually include the row into the problem object; note that the arrays ind and val are clobbered, so we need to build them once again */ len = 0; ind[++len] = glp_find_col(P, "BIN1"), val[len] = .01; ind[++len] = glp_find_col(P, "BIN2"), val[len] = .01; ind[++len] = glp_find_col(P, "BIN4"), val[len] = .02; ind[++len] = glp_find_col(P, "BIN5"), val[len] = .02; rhs = 12; i = glp_add_rows(P, 1); glp_set_row_bnds(P, i, GLP_UP, 0, rhs); glp_set_mat_row(P, i, len, ind, val); /* and perform one dual simplex iteration */ glp_init_smcp(&parm); parm.meth = GLP_DUAL; parm.it_lim = 1; glp_simplex(P, &parm); /* the current objective value is 301.861 */ return 0; } #endif /*********************************************************************** * NAME * * glp_analyze_bound - analyze active bound of non-basic variable * * SYNOPSIS * * void glp_analyze_bound(glp_prob *P, int k, double *limit1, int *var1, * double *limit2, int *var2); * * DESCRIPTION * * The routine glp_analyze_bound analyzes the effect of varying the * active bound of specified non-basic variable. * * The non-basic variable is specified by the parameter k, where * 1 <= k <= m means auxiliary variable of corresponding row while * m+1 <= k <= m+n means structural variable (column). * * Note that the current basic solution must be optimal, and the basis * factorization must exist. * * Results of the analysis have the following meaning. * * value1 is the minimal value of the active bound, at which the basis * still remains primal feasible and thus optimal. -DBL_MAX means that * the active bound has no lower limit. * * var1 is the ordinal number of an auxiliary (1 to m) or structural * (m+1 to n) basic variable, which reaches its bound first and thereby * limits further decreasing the active bound being analyzed. * if value1 = -DBL_MAX, var1 is set to 0. * * value2 is the maximal value of the active bound, at which the basis * still remains primal feasible and thus optimal. +DBL_MAX means that * the active bound has no upper limit. * * var2 is the ordinal number of an auxiliary (1 to m) or structural * (m+1 to n) basic variable, which reaches its bound first and thereby * limits further increasing the active bound being analyzed. * if value2 = +DBL_MAX, var2 is set to 0. */ void glp_analyze_bound(glp_prob *P, int k, double *value1, int *var1, double *value2, int *var2) { GLPROW *row; GLPCOL *col; int m, n, stat, kase, p, len, piv, *ind; double x, new_x, ll, uu, xx, delta, *val; /* sanity checks */ if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_analyze_bound: P = %p; invalid problem object\n", P); m = P->m, n = P->n; if (!(P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS)) xerror("glp_analyze_bound: optimal basic solution required\n"); if (!(m == 0 || P->valid)) xerror("glp_analyze_bound: basis factorization required\n"); if (!(1 <= k && k <= m+n)) xerror("glp_analyze_bound: k = %d; variable number out of rang" "e\n", k); /* retrieve information about the specified non-basic variable x[k] whose active bound is to be analyzed */ if (k <= m) { row = P->row[k]; stat = row->stat; x = row->prim; } else { col = P->col[k-m]; stat = col->stat; x = col->prim; } if (stat == GLP_BS) xerror("glp_analyze_bound: k = %d; basic variable not allowed " "\n", k); /* allocate working arrays */ ind = xcalloc(1+m, sizeof(int)); val = xcalloc(1+m, sizeof(double)); /* compute column of the simplex table corresponding to the non-basic variable x[k] */ len = glp_eval_tab_col(P, k, ind, val); xassert(0 <= len && len <= m); /* perform analysis */ for (kase = -1; kase <= +1; kase += 2) { /* kase < 0 means active bound of x[k] is decreasing; kase > 0 means active bound of x[k] is increasing */ /* use the primal ratio test to determine some basic variable x[p] which reaches its bound first */ piv = glp_prim_rtest(P, len, ind, val, kase, 1e-9); if (piv == 0) { /* nothing limits changing the active bound of x[k] */ p = 0; new_x = (kase < 0 ? -DBL_MAX : +DBL_MAX); goto store; } /* basic variable x[p] limits changing the active bound of x[k]; determine its value in the current basis */ xassert(1 <= piv && piv <= len); p = ind[piv]; if (p <= m) { row = P->row[p]; ll = glp_get_row_lb(P, row->i); uu = glp_get_row_ub(P, row->i); stat = row->stat; xx = row->prim; } else { col = P->col[p-m]; ll = glp_get_col_lb(P, col->j); uu = glp_get_col_ub(P, col->j); stat = col->stat; xx = col->prim; } xassert(stat == GLP_BS); /* determine delta x[p] = bound of x[p] - value of x[p] */ if (kase < 0 && val[piv] > 0.0 || kase > 0 && val[piv] < 0.0) { /* delta x[p] < 0, so x[p] goes toward its lower bound */ xassert(ll != -DBL_MAX); delta = ll - xx; } else { /* delta x[p] > 0, so x[p] goes toward its upper bound */ xassert(uu != +DBL_MAX); delta = uu - xx; } /* delta x[p] = alfa[p,k] * delta x[k], so new x[k] = x[k] + delta x[k] = x[k] + delta x[p] / alfa[p,k] is the value of x[k] in the adjacent basis */ xassert(val[piv] != 0.0); new_x = x + delta / val[piv]; store: /* store analysis results */ if (kase < 0) { if (value1 != NULL) *value1 = new_x; if (var1 != NULL) *var1 = p; } else { if (value2 != NULL) *value2 = new_x; if (var2 != NULL) *var2 = p; } } /* free working arrays */ xfree(ind); xfree(val); return; } /*********************************************************************** * NAME * * glp_analyze_coef - analyze objective coefficient at basic variable * * SYNOPSIS * * void glp_analyze_coef(glp_prob *P, int k, double *coef1, int *var1, * double *value1, double *coef2, int *var2, double *value2); * * DESCRIPTION * * The routine glp_analyze_coef analyzes the effect of varying the * objective coefficient at specified basic variable. * * The basic variable is specified by the parameter k, where * 1 <= k <= m means auxiliary variable of corresponding row while * m+1 <= k <= m+n means structural variable (column). * * Note that the current basic solution must be optimal, and the basis * factorization must exist. * * Results of the analysis have the following meaning. * * coef1 is the minimal value of the objective coefficient, at which * the basis still remains dual feasible and thus optimal. -DBL_MAX * means that the objective coefficient has no lower limit. * * var1 is the ordinal number of an auxiliary (1 to m) or structural * (m+1 to n) non-basic variable, whose reduced cost reaches its zero * bound first and thereby limits further decreasing the objective * coefficient being analyzed. If coef1 = -DBL_MAX, var1 is set to 0. * * value1 is value of the basic variable being analyzed in an adjacent * basis, which is defined as follows. Let the objective coefficient * reaches its minimal value (coef1) and continues decreasing. Then the * reduced cost of the limiting non-basic variable (var1) becomes dual * infeasible and the current basis becomes non-optimal that forces the * limiting non-basic variable to enter the basis replacing there some * basic variable that leaves the basis to keep primal feasibility. * Should note that on determining the adjacent basis current bounds * of the basic variable being analyzed are ignored as if it were free * (unbounded) variable, so it cannot leave the basis. It may happen * that no dual feasible adjacent basis exists, in which case value1 is * set to -DBL_MAX or +DBL_MAX. * * coef2 is the maximal value of the objective coefficient, at which * the basis still remains dual feasible and thus optimal. +DBL_MAX * means that the objective coefficient has no upper limit. * * var2 is the ordinal number of an auxiliary (1 to m) or structural * (m+1 to n) non-basic variable, whose reduced cost reaches its zero * bound first and thereby limits further increasing the objective * coefficient being analyzed. If coef2 = +DBL_MAX, var2 is set to 0. * * value2 is value of the basic variable being analyzed in an adjacent * basis, which is defined exactly in the same way as value1 above with * exception that now the objective coefficient is increasing. */ void glp_analyze_coef(glp_prob *P, int k, double *coef1, int *var1, double *value1, double *coef2, int *var2, double *value2) { GLPROW *row; GLPCOL *col; int m, n, type, stat, kase, p, q, dir, clen, cpiv, rlen, rpiv, *cind, *rind; double lb, ub, coef, x, lim_coef, new_x, d, delta, ll, uu, xx, *rval, *cval; /* sanity checks */ if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_analyze_coef: P = %p; invalid problem object\n", P); m = P->m, n = P->n; if (!(P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS)) xerror("glp_analyze_coef: optimal basic solution required\n"); if (!(m == 0 || P->valid)) xerror("glp_analyze_coef: basis factorization required\n"); if (!(1 <= k && k <= m+n)) xerror("glp_analyze_coef: k = %d; variable number out of range" "\n", k); /* retrieve information about the specified basic variable x[k] whose objective coefficient c[k] is to be analyzed */ if (k <= m) { row = P->row[k]; type = row->type; lb = row->lb; ub = row->ub; coef = 0.0; stat = row->stat; x = row->prim; } else { col = P->col[k-m]; type = col->type; lb = col->lb; ub = col->ub; coef = col->coef; stat = col->stat; x = col->prim; } if (stat != GLP_BS) xerror("glp_analyze_coef: k = %d; non-basic variable not allow" "ed\n", k); /* allocate working arrays */ cind = xcalloc(1+m, sizeof(int)); cval = xcalloc(1+m, sizeof(double)); rind = xcalloc(1+n, sizeof(int)); rval = xcalloc(1+n, sizeof(double)); /* compute row of the simplex table corresponding to the basic variable x[k] */ rlen = glp_eval_tab_row(P, k, rind, rval); xassert(0 <= rlen && rlen <= n); /* perform analysis */ for (kase = -1; kase <= +1; kase += 2) { /* kase < 0 means objective coefficient c[k] is decreasing; kase > 0 means objective coefficient c[k] is increasing */ /* note that decreasing c[k] is equivalent to increasing dual variable lambda[k] and vice versa; we need to correctly set the dir flag as required by the routine glp_dual_rtest */ if (P->dir == GLP_MIN) dir = - kase; else if (P->dir == GLP_MAX) dir = + kase; else xassert(P != P); /* use the dual ratio test to determine non-basic variable x[q] whose reduced cost d[q] reaches zero bound first */ rpiv = glp_dual_rtest(P, rlen, rind, rval, dir, 1e-9); if (rpiv == 0) { /* nothing limits changing c[k] */ lim_coef = (kase < 0 ? -DBL_MAX : +DBL_MAX); q = 0; /* x[k] keeps its current value */ new_x = x; goto store; } /* non-basic variable x[q] limits changing coefficient c[k]; determine its status and reduced cost d[k] in the current basis */ xassert(1 <= rpiv && rpiv <= rlen); q = rind[rpiv]; xassert(1 <= q && q <= m+n); if (q <= m) { row = P->row[q]; stat = row->stat; d = row->dual; } else { col = P->col[q-m]; stat = col->stat; d = col->dual; } /* note that delta d[q] = new d[q] - d[q] = - d[q], because new d[q] = 0; delta d[q] = alfa[k,q] * delta c[k], so delta c[k] = delta d[q] / alfa[k,q] = - d[q] / alfa[k,q] */ xassert(rval[rpiv] != 0.0); delta = - d / rval[rpiv]; /* compute new c[k] = c[k] + delta c[k], which is the limiting value of the objective coefficient c[k] */ lim_coef = coef + delta; /* let c[k] continue decreasing/increasing that makes d[q] dual infeasible and forces x[q] to enter the basis; to perform the primal ratio test we need to know in which direction x[q] changes on entering the basis; we determine that analyzing the sign of delta d[q] (see above), since d[q] may be close to zero having wrong sign */ /* let, for simplicity, the problem is minimization */ if (kase < 0 && rval[rpiv] > 0.0 || kase > 0 && rval[rpiv] < 0.0) { /* delta d[q] < 0, so d[q] being non-negative will become negative, so x[q] will increase */ dir = +1; } else { /* delta d[q] > 0, so d[q] being non-positive will become positive, so x[q] will decrease */ dir = -1; } /* if the problem is maximization, correct the direction */ if (P->dir == GLP_MAX) dir = - dir; /* check that we didn't make a silly mistake */ if (dir > 0) xassert(stat == GLP_NL || stat == GLP_NF); else xassert(stat == GLP_NU || stat == GLP_NF); /* compute column of the simplex table corresponding to the non-basic variable x[q] */ clen = glp_eval_tab_col(P, q, cind, cval); /* make x[k] temporarily free (unbounded) */ if (k <= m) { row = P->row[k]; row->type = GLP_FR; row->lb = row->ub = 0.0; } else { col = P->col[k-m]; col->type = GLP_FR; col->lb = col->ub = 0.0; } /* use the primal ratio test to determine some basic variable which leaves the basis */ cpiv = glp_prim_rtest(P, clen, cind, cval, dir, 1e-9); /* restore original bounds of the basic variable x[k] */ if (k <= m) { row = P->row[k]; row->type = type; row->lb = lb, row->ub = ub; } else { col = P->col[k-m]; col->type = type; col->lb = lb, col->ub = ub; } if (cpiv == 0) { /* non-basic variable x[q] can change unlimitedly */ if (dir < 0 && rval[rpiv] > 0.0 || dir > 0 && rval[rpiv] < 0.0) { /* delta x[k] = alfa[k,q] * delta x[q] < 0 */ new_x = -DBL_MAX; } else { /* delta x[k] = alfa[k,q] * delta x[q] > 0 */ new_x = +DBL_MAX; } goto store; } /* some basic variable x[p] limits changing non-basic variable x[q] in the adjacent basis */ xassert(1 <= cpiv && cpiv <= clen); p = cind[cpiv]; xassert(1 <= p && p <= m+n); xassert(p != k); if (p <= m) { row = P->row[p]; xassert(row->stat == GLP_BS); ll = glp_get_row_lb(P, row->i); uu = glp_get_row_ub(P, row->i); xx = row->prim; } else { col = P->col[p-m]; xassert(col->stat == GLP_BS); ll = glp_get_col_lb(P, col->j); uu = glp_get_col_ub(P, col->j); xx = col->prim; } /* determine delta x[p] = new x[p] - x[p] */ if (dir < 0 && cval[cpiv] > 0.0 || dir > 0 && cval[cpiv] < 0.0) { /* delta x[p] < 0, so x[p] goes toward its lower bound */ xassert(ll != -DBL_MAX); delta = ll - xx; } else { /* delta x[p] > 0, so x[p] goes toward its upper bound */ xassert(uu != +DBL_MAX); delta = uu - xx; } /* compute new x[k] = x[k] + alfa[k,q] * delta x[q], where delta x[q] = delta x[p] / alfa[p,q] */ xassert(cval[cpiv] != 0.0); new_x = x + (rval[rpiv] / cval[cpiv]) * delta; store: /* store analysis results */ if (kase < 0) { if (coef1 != NULL) *coef1 = lim_coef; if (var1 != NULL) *var1 = q; if (value1 != NULL) *value1 = new_x; } else { if (coef2 != NULL) *coef2 = lim_coef; if (var2 != NULL) *var2 = q; if (value2 != NULL) *value2 = new_x; } } /* free working arrays */ xfree(cind); xfree(cval); xfree(rind); xfree(rval); return; } /* eof */ igraph/src/glpios10.c0000644000176000001440000002766712325527073014175 0ustar ripleyusers/* glpios10.c (feasibility pump heuristic) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpios.h" #include "glprng.h" /*********************************************************************** * NAME * * ios_feas_pump - feasibility pump heuristic * * SYNOPSIS * * #include "glpios.h" * void ios_feas_pump(glp_tree *T); * * DESCRIPTION * * The routine ios_feas_pump is a simple implementation of the Feasi- * bility Pump heuristic. * * REFERENCES * * M.Fischetti, F.Glover, and A.Lodi. "The feasibility pump." Math. * Program., Ser. A 104, pp. 91-104 (2005). */ struct VAR { /* binary variable */ int j; /* ordinal number */ int x; /* value in the rounded solution (0 or 1) */ double d; /* sorting key */ }; static int fcmp(const void *x, const void *y) { /* comparison routine */ const struct VAR *vx = x, *vy = y; if (vx->d > vy->d) return -1; else if (vx->d < vy->d) return +1; else return 0; } void ios_feas_pump(glp_tree *T) { glp_prob *P = T->mip; int n = P->n; glp_prob *lp = NULL; struct VAR *var = NULL; RNG *rand = NULL; GLPCOL *col; glp_smcp parm; int j, k, new_x, nfail, npass, nv, ret, stalling; double dist, tol; xassert(glp_get_status(P) == GLP_OPT); /* this heuristic is applied only once on the root level */ if (!(T->curr->level == 0 && T->curr->solved == 1)) goto done; /* determine number of binary variables */ nv = 0; for (j = 1; j <= n; j++) { col = P->col[j]; /* if x[j] is continuous, skip it */ if (col->kind == GLP_CV) continue; /* if x[j] is fixed, skip it */ if (col->type == GLP_FX) continue; /* x[j] is non-fixed integer */ xassert(col->kind == GLP_IV); if (col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0) { /* x[j] is binary */ nv++; } else { /* x[j] is general integer */ if (T->parm->msg_lev >= GLP_MSG_ALL) xprintf("FPUMP heuristic cannot be applied due to genera" "l integer variables\n"); goto done; } } /* there must be at least one binary variable */ if (nv == 0) goto done; if (T->parm->msg_lev >= GLP_MSG_ALL) xprintf("Applying FPUMP heuristic...\n"); /* build the list of binary variables */ var = xcalloc(1+nv, sizeof(struct VAR)); k = 0; for (j = 1; j <= n; j++) { col = P->col[j]; if (col->kind == GLP_IV && col->type == GLP_DB) var[++k].j = j; } xassert(k == nv); /* create working problem object */ lp = glp_create_prob(); more: /* copy the original problem object to keep it intact */ glp_copy_prob(lp, P, GLP_OFF); /* we are interested to find an integer feasible solution, which is better than the best known one */ if (P->mip_stat == GLP_FEAS) { int *ind; double *val, bnd; /* add a row and make it identical to the objective row */ glp_add_rows(lp, 1); ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); for (j = 1; j <= n; j++) { ind[j] = j; val[j] = P->col[j]->coef; } glp_set_mat_row(lp, lp->m, n, ind, val); xfree(ind); xfree(val); /* introduce upper (minimization) or lower (maximization) bound to the original objective function; note that this additional constraint is not violated at the optimal point to LP relaxation */ #if 0 /* modified by xypron */ if (P->dir == GLP_MIN) { bnd = P->mip_obj - 0.10 * (1.0 + fabs(P->mip_obj)); if (bnd < P->obj_val) bnd = P->obj_val; glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0); } else if (P->dir == GLP_MAX) { bnd = P->mip_obj + 0.10 * (1.0 + fabs(P->mip_obj)); if (bnd > P->obj_val) bnd = P->obj_val; glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0); } else xassert(P != P); #else bnd = 0.1 * P->obj_val + 0.9 * P->mip_obj; /* xprintf("bnd = %f\n", bnd); */ if (P->dir == GLP_MIN) glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0); else if (P->dir == GLP_MAX) glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0); else xassert(P != P); #endif } /* reset pass count */ npass = 0; /* invalidate the rounded point */ for (k = 1; k <= nv; k++) var[k].x = -1; pass: /* next pass starts here */ npass++; if (T->parm->msg_lev >= GLP_MSG_ALL) xprintf("Pass %d\n", npass); /* initialize minimal distance between the basic point and the rounded one obtained during this pass */ dist = DBL_MAX; /* reset failure count (the number of succeeded iterations failed to improve the distance) */ nfail = 0; /* if it is not the first pass, perturb the last rounded point rather than construct it from the basic solution */ if (npass > 1) { double rho, temp; if (rand == NULL) rand = rng_create_rand(); for (k = 1; k <= nv; k++) { j = var[k].j; col = lp->col[j]; rho = rng_uniform(rand, -0.3, 0.7); if (rho < 0.0) rho = 0.0; temp = fabs((double)var[k].x - col->prim); if (temp + rho > 0.5) var[k].x = 1 - var[k].x; } goto skip; } loop: /* innermost loop begins here */ /* round basic solution (which is assumed primal feasible) */ stalling = 1; for (k = 1; k <= nv; k++) { col = lp->col[var[k].j]; if (col->prim < 0.5) { /* rounded value is 0 */ new_x = 0; } else { /* rounded value is 1 */ new_x = 1; } if (var[k].x != new_x) { stalling = 0; var[k].x = new_x; } } /* if the rounded point has not changed (stalling), choose and flip some its entries heuristically */ if (stalling) { /* compute d[j] = |x[j] - round(x[j])| */ for (k = 1; k <= nv; k++) { col = lp->col[var[k].j]; var[k].d = fabs(col->prim - (double)var[k].x); } /* sort the list of binary variables by descending d[j] */ qsort(&var[1], nv, sizeof(struct VAR), fcmp); /* choose and flip some rounded components */ for (k = 1; k <= nv; k++) { if (k >= 5 && var[k].d < 0.35 || k >= 10) break; var[k].x = 1 - var[k].x; } } skip: /* check if the time limit has been exhausted */ if (T->parm->tm_lim < INT_MAX && (double)(T->parm->tm_lim - 1) <= 1000.0 * xdifftime(xtime(), T->tm_beg)) goto done; /* build the objective, which is the distance between the current (basic) point and the rounded one */ lp->dir = GLP_MIN; lp->c0 = 0.0; for (j = 1; j <= n; j++) lp->col[j]->coef = 0.0; for (k = 1; k <= nv; k++) { j = var[k].j; if (var[k].x == 0) lp->col[j]->coef = +1.0; else { lp->col[j]->coef = -1.0; lp->c0 += 1.0; } } /* minimize the distance with the simplex method */ glp_init_smcp(&parm); if (T->parm->msg_lev <= GLP_MSG_ERR) parm.msg_lev = T->parm->msg_lev; else if (T->parm->msg_lev <= GLP_MSG_ALL) { parm.msg_lev = GLP_MSG_ON; parm.out_dly = 10000; } ret = glp_simplex(lp, &parm); if (ret != 0) { if (T->parm->msg_lev >= GLP_MSG_ERR) xprintf("Warning: glp_simplex returned %d\n", ret); goto done; } ret = glp_get_status(lp); if (ret != GLP_OPT) { if (T->parm->msg_lev >= GLP_MSG_ERR) xprintf("Warning: glp_get_status returned %d\n", ret); goto done; } if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("delta = %g\n", lp->obj_val); /* check if the basic solution is integer feasible; note that it may be so even if the minimial distance is positive */ tol = 0.3 * T->parm->tol_int; for (k = 1; k <= nv; k++) { col = lp->col[var[k].j]; if (tol < col->prim && col->prim < 1.0 - tol) break; } if (k > nv) { /* okay; the basic solution seems to be integer feasible */ double *x = xcalloc(1+n, sizeof(double)); for (j = 1; j <= n; j++) { x[j] = lp->col[j]->prim; if (P->col[j]->kind == GLP_IV) x[j] = floor(x[j] + 0.5); } #if 1 /* modified by xypron */ /* reset direction and right-hand side of objective */ lp->c0 = P->c0; lp->dir = P->dir; /* fix integer variables */ for (k = 1; k <= nv; k++) { lp->col[var[k].j]->lb = x[var[k].j]; lp->col[var[k].j]->ub = x[var[k].j]; lp->col[var[k].j]->type = GLP_FX; } /* copy original objective function */ for (j = 1; j <= n; j++) lp->col[j]->coef = P->col[j]->coef; /* solve original LP and copy result */ ret = glp_simplex(lp, &parm); if (ret != 0) { if (T->parm->msg_lev >= GLP_MSG_ERR) xprintf("Warning: glp_simplex returned %d\n", ret); goto done; } ret = glp_get_status(lp); if (ret != GLP_OPT) { if (T->parm->msg_lev >= GLP_MSG_ERR) xprintf("Warning: glp_get_status returned %d\n", ret); goto done; } for (j = 1; j <= n; j++) if (P->col[j]->kind != GLP_IV) x[j] = lp->col[j]->prim; #endif ret = glp_ios_heur_sol(T, x); xfree(x); if (ret == 0) { /* the integer solution is accepted */ if (ios_is_hopeful(T, T->curr->bound)) { /* it is reasonable to apply the heuristic once again */ goto more; } else { /* the best known integer feasible solution just found is close to optimal solution to LP relaxation */ goto done; } } } /* the basic solution is fractional */ if (dist == DBL_MAX || lp->obj_val <= dist - 1e-6 * (1.0 + dist)) { /* the distance is reducing */ nfail = 0, dist = lp->obj_val; } else { /* improving the distance failed */ nfail++; } if (nfail < 3) goto loop; if (npass < 5) goto pass; done: /* delete working objects */ if (lp != NULL) glp_delete_prob(lp); if (var != NULL) xfree(var); if (rand != NULL) rng_delete_rand(rand); return; } /* eof */ igraph/src/statusbar.c0000644000176000001440000001052412325527074014530 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "igraph_types.h" #include "igraph_statusbar.h" #include "igraph_error.h" #include #include static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler=0; /** * \function igraph_status * Report status from an igraph function. * * It calls the installed status handler function, if there is * one. Otherwise it does nothing. Note that the standard way to * report the status from an igraph function is the * \ref IGRAPH_STATUS or \ref IGRAPH_STATUSF macro, as these * take care of the termination of the calling function if the * status handler returns with \c IGRAPH_INTERRUPTED. * \param message The status message. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. * \return Error code. If a status handler function was called * and it did not return with \c IGRAPH_SUCCESS, then * \c IGRAPH_INTERRUPTED is returned by \c igraph_status(). * * Time complexity: O(1). */ int igraph_status(const char *message, void *data) { if (igraph_i_status_handler) { if (igraph_i_status_handler(message, data) != IGRAPH_SUCCESS) { return IGRAPH_INTERRUPTED; } } return IGRAPH_SUCCESS; } /** * \function igraph_statusf * Report status, more flexible printf-like version. * * This is the more flexible version of \ref igraph_status(), * that has a syntax similar to the \c printf standard C library function. * It substitutes the values of the additional arguments into the * \p message template string and calls \ref igraph_status(). * \param message Status message template string, the syntax is the same * as for the \c printf function. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. * \param ... The additional arguments to fill the template given in the * \p message argument. * \return Error code. If a status handler function was called * and it did not return with \c IGRAPH_SUCCESS, then * \c IGRAPH_INTERRUPTED is returned by \c igraph_status(). */ int igraph_statusf(const char *message, void *data, ...) { char buffer[300]; va_list ap; va_start(ap, data); vsnprintf(buffer, sizeof(buffer)-1, message, ap); return igraph_status(buffer, data); } #ifndef USING_R /** * \function igraph_status_handler_stderr * A simple predefined status handler function. * * A simple status handler function, that writes the status * message to the standard errror. * \param message The status message. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. * \return Error code. * * Time complexity: O(1). */ int igraph_status_handler_stderr(const char *message, void *data) { IGRAPH_UNUSED(data); fputs(message, stderr); return 0; } #endif /** * \function igraph_set_status_handler * Install of uninstall a status handler function. * * To uninstall the currently installed status handler, call * this function with a null pointer. * \param new_handler The status handler function to install. * \return The previously installed status handler function. * * Time complexity: O(1). */ igraph_status_handler_t * igraph_set_status_handler(igraph_status_handler_t new_handler) { igraph_status_handler_t *previous_handler=igraph_i_status_handler; igraph_i_status_handler = new_handler; return previous_handler; } igraph/src/igraph_layout.h0000644000176000001440000002531012325527073015372 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_LAYOUT_H #define IGRAPH_LAYOUT_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_matrix.h" #include "igraph_datatype.h" #include "igraph_arpack.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Layouts */ /* -------------------------------------------------- */ int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res); int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t center, const igraph_vector_t *order); int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width); int igraph_layout_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_bool_t use_seed, const igraph_vector_t *weight, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy); int igraph_layout_grid_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_bool_t use_seed, const igraph_vector_t *weight); int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t sigma, igraph_real_t initemp, igraph_real_t coolexp, igraph_real_t kkconst, igraph_bool_t use_seed, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy); int igraph_layout_springs(const igraph_t *graph, igraph_matrix_t *res, igraph_real_t mass, igraph_real_t equil, igraph_real_t k, igraph_real_t repeqdis, igraph_real_t kfr, igraph_bool_t repulse); int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t maxiter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_integer_t root); int igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, const igraph_vector_t *roots, const igraph_vector_t *rootlevel); int igraph_layout_reingold_tilford_circular(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, const igraph_vector_t *roots, const igraph_vector_t *rootlevel); int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, igraph_t *extd_graph, igraph_vector_t *extd_to_orig_eids, const igraph_vector_t* layers, igraph_real_t hgap, igraph_real_t vgap, long int maxiter, const igraph_vector_t *weights); int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, long int width, long int height); int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t volume, igraph_real_t coolexp, igraph_real_t repulserad, igraph_bool_t use_seed, const igraph_vector_t *weight, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, const igraph_vector_t *minz, const igraph_vector_t *maxz); int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t sigma, igraph_real_t initemp, igraph_real_t coolexp, igraph_real_t kkconst, igraph_bool_t use_seed, igraph_bool_t fixz, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, const igraph_vector_t *minz, const igraph_vector_t *maxz); int igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t node_charge, igraph_real_t node_mass, igraph_real_t spring_length, igraph_real_t spring_constant, igraph_real_t max_sa_movement, igraph_bool_t use_seed); int igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res, const igraph_matrix_t *dist, long int dim, igraph_arpack_options_t *options); int igraph_layout_bipartite(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, igraph_real_t hgap, igraph_real_t vgap, long int maxiter); /** * \struct igraph_layout_drl_options_t * Parameters for the DrL layout generator * * \member edge_cut The edge cutting parameter. * Edge cutting is done in the late stages of the * algorithm in order to achieve less dense layouts. Edges are cut * if there is a lot of stress on them (a large value in the * objective function sum). The edge cutting parameter is a value * between 0 and 1 with 0 representing no edge cutting and 1 * representing maximal edge cutting. The default value is 32/40. * \member init_iterations Number of iterations, initial phase. * \member init_temperature Start temperature, initial phase. * \member init_attraction Attraction, initial phase. * \member init_damping_mult Damping factor, initial phase. * \member liquid_iterations Number of iterations in the liquid phase. * \member liquid_temperature Start temperature in the liquid phase. * \member liquid_attraction Attraction in the liquid phase. * \member liquid_damping_mult Multiplicatie damping factor, liquid phase. * \member expansion_iterations Number of iterations in the expansion phase. * \member expansion_temperature Start temperature in the expansion phase. * \member expansion_attraction Attraction, expansion phase. * \member expansion_damping_mult Damping factor, expansion phase. * \member cooldown_iterations Number of iterations in the cooldown phase. * \member cooldown_temperature Start temperature in the cooldown phase. * \member cooldown_attraction Attraction in the cooldown phase. * \member cooldown_damping_mult Damping fact int the cooldown phase. * \member crunch_iterations Number of iterations in the crunch phase. * \member crunch_temperature Start temperature in the crunch phase. * \member crunch_attraction Attraction in the crunch phase. * \member crunch_damping_mult Damping factor in the crunch phase. * \member simmer_iterations Number of iterations in the simmer phase. * \member simmer_temperature Start temperature in te simmer phase. * \member simmer_attraction Attraction in the simmer phase. * \member simmer_damping_mult Multiplicative damping factor in the simmer phase. */ typedef struct igraph_layout_drl_options_t { igraph_real_t edge_cut; igraph_integer_t init_iterations; igraph_real_t init_temperature; igraph_real_t init_attraction; igraph_real_t init_damping_mult; igraph_integer_t liquid_iterations; igraph_real_t liquid_temperature; igraph_real_t liquid_attraction; igraph_real_t liquid_damping_mult; igraph_integer_t expansion_iterations; igraph_real_t expansion_temperature; igraph_real_t expansion_attraction; igraph_real_t expansion_damping_mult; igraph_integer_t cooldown_iterations; igraph_real_t cooldown_temperature; igraph_real_t cooldown_attraction; igraph_real_t cooldown_damping_mult; igraph_integer_t crunch_iterations; igraph_real_t crunch_temperature; igraph_real_t crunch_attraction; igraph_real_t crunch_damping_mult; igraph_integer_t simmer_iterations; igraph_real_t simmer_temperature; igraph_real_t simmer_attraction; igraph_real_t simmer_damping_mult; } igraph_layout_drl_options_t; /** * \typedef igraph_layout_drl_default_t * Predefined parameter templates for the DrL layout generator * * These constants can be used to initialize a set of DrL parameters. * These can then be modified according to the user's needs. * \enumval IGRAPH_LAYOUT_DRL_DEFAULT The deafult parameters. * \enumval IGRAPH_LAYOUT_DRL_COARSEN Slightly modified parameters to * get a coarser layout. * \enumval IGRAPH_LAYOUT_DRL_COARSEST An even coarser layout. * \enumval IGRAPH_LAYOUT_DRL_REFINE Refine an already calculated layout. * \enumval IGRAPH_LAYOUT_DRL_FINAL Finalize an already refined layout. */ typedef enum { IGRAPH_LAYOUT_DRL_DEFAULT=0, IGRAPH_LAYOUT_DRL_COARSEN, IGRAPH_LAYOUT_DRL_COARSEST, IGRAPH_LAYOUT_DRL_REFINE, IGRAPH_LAYOUT_DRL_FINAL } igraph_layout_drl_default_t; int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, igraph_layout_drl_default_t templ); int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t use_seed, igraph_layout_drl_options_t *options, const igraph_vector_t *weights, const igraph_vector_bool_t *fixed); int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t use_seed, igraph_layout_drl_options_t *options, const igraph_vector_t *weights, const igraph_vector_bool_t *fixed); int igraph_layout_merge_dla(igraph_vector_ptr_t *graphs, igraph_vector_ptr_t *coords, igraph_matrix_t *res); __END_DECLS #endif igraph/src/foreign-pajek-parser.c0000644000176000001440000025307312325527073016542 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Using locations. */ #define YYLSP_NEEDED 1 /* Substitute the variable and function names. */ #define yyparse igraph_pajek_yyparse #define yylex igraph_pajek_yylex #define yyerror igraph_pajek_yyerror #define yylval igraph_pajek_yylval #define yychar igraph_pajek_yychar #define yydebug igraph_pajek_yydebug #define yynerrs igraph_pajek_yynerrs #define yylloc igraph_pajek_yylloc /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { NEWLINE = 258, NUM = 259, ALNUM = 260, QSTR = 261, PSTR = 262, NETWORKLINE = 263, VERTICESLINE = 264, ARCSLINE = 265, EDGESLINE = 266, ARCSLISTLINE = 267, EDGESLISTLINE = 268, MATRIXLINE = 269, VP_X_FACT = 270, VP_Y_FACT = 271, VP_IC = 272, VP_BC = 273, VP_LC = 274, VP_LR = 275, VP_LPHI = 276, VP_BW = 277, VP_FOS = 278, VP_PHI = 279, VP_R = 280, VP_Q = 281, VP_LA = 282, VP_FONT = 283, VP_URL = 284, VP_SIZE = 285, EP_C = 286, EP_S = 287, EP_A = 288, EP_W = 289, EP_H1 = 290, EP_H2 = 291, EP_A1 = 292, EP_A2 = 293, EP_K1 = 294, EP_K2 = 295, EP_AP = 296, EP_P = 297, EP_L = 298, EP_LP = 299, EP_LR = 300, EP_LPHI = 301, EP_LC = 302, EP_LA = 303, EP_SIZE = 304, EP_FOS = 305 }; #endif /* Tokens. */ #define NEWLINE 258 #define NUM 259 #define ALNUM 260 #define QSTR 261 #define PSTR 262 #define NETWORKLINE 263 #define VERTICESLINE 264 #define ARCSLINE 265 #define EDGESLINE 266 #define ARCSLISTLINE 267 #define EDGESLISTLINE 268 #define MATRIXLINE 269 #define VP_X_FACT 270 #define VP_Y_FACT 271 #define VP_IC 272 #define VP_BC 273 #define VP_LC 274 #define VP_LR 275 #define VP_LPHI 276 #define VP_BW 277 #define VP_FOS 278 #define VP_PHI 279 #define VP_R 280 #define VP_Q 281 #define VP_LA 282 #define VP_FONT 283 #define VP_URL 284 #define VP_SIZE 285 #define EP_C 286 #define EP_S 287 #define EP_A 288 #define EP_W 289 #define EP_H1 290 #define EP_H2 291 #define EP_A1 292 #define EP_A2 293 #define EP_K1 294 #define EP_K2 295 #define EP_AP 296 #define EP_P 297 #define EP_L 298 #define EP_LP 299 #define EP_LR 300 #define EP_LPHI 301 #define EP_LC 302 #define EP_LA 303 #define EP_SIZE 304 #define EP_FOS 305 /* Copy the first part of user declarations. */ #line 23 "igraph/src/foreign-pajek-parser.y" /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include "igraph_hacks_internal.h" #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_memory.h" #include "igraph_error.h" #include "igraph_attributes.h" #include "config.h" #include "igraph_math.h" #include #include "foreign-pajek-header.h" #include "foreign-pajek-parser.h" #define yyscan_t void* int igraph_pajek_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_pajek_yyerror(YYLTYPE* locp, igraph_i_pajek_parsedata_t *context, char *s); char *igraph_pajek_yyget_text (yyscan_t yyscanner ); int igraph_pajek_yyget_leng (yyscan_t yyscanner ); int igraph_i_pajek_add_string_vertex_attribute(const char *name, const char *value, int len, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, int len, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_numeric_edge_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, long int count, const char *attrname, igraph_integer_t vid, igraph_real_t number); int igraph_i_pajek_add_string_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, long int count, const char *attrname, igraph_integer_t vid, const char *str); int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context); int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context); extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); extern long int igraph_i_pajek_actvertex; extern long int igraph_i_pajek_actedge; #define scanner context->scanner /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 123 "igraph/src/foreign-pajek-parser.y" { long int intnum; double realnum; struct { char *str; int len; } string; } /* Line 193 of yacc.c. */ #line 304 "y.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ /* Line 216 of yacc.c. */ #line 329 "y.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int i) #else static int YYID (i) int i; #endif { return i; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss; YYSTYPE yyvs; YYLTYPE yyls; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 5 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 250 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 51 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 66 /* YYNRULES -- Number of rules. */ #define YYNRULES 137 /* YYNRULES -- Number of states. */ #define YYNSTATES 207 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 305 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 7, 8, 12, 16, 19, 23, 24, 27, 29, 32, 33, 41, 43, 45, 46, 49, 53, 54, 56, 57, 60, 62, 65, 68, 73, 78, 83, 86, 89, 92, 95, 98, 101, 104, 107, 110, 111, 115, 116, 120, 121, 125, 126, 130, 131, 135, 137, 138, 141, 144, 147, 150, 153, 157, 162, 163, 166, 168, 169, 176, 178, 180, 184, 189, 190, 193, 195, 196, 203, 205, 207, 208, 210, 211, 214, 216, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, 260, 263, 266, 267, 271, 272, 276, 277, 281, 282, 286, 287, 291, 293, 297, 298, 301, 303, 307, 308, 311, 313, 315, 319, 320, 323, 325, 329, 330, 333, 335, 337, 341, 343, 344, 347, 350, 351, 354, 356, 358, 360, 361, 364, 366, 368 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 52, 0, -1, 53, 54, 72, -1, -1, 8, 115, 3, -1, 55, 3, 56, -1, 9, 113, -1, 9, 113, 113, -1, -1, 56, 57, -1, 3, -1, 59, 3, -1, -1, 59, 58, 60, 61, 62, 63, 3, -1, 113, -1, 116, -1, -1, 114, 114, -1, 114, 114, 114, -1, -1, 116, -1, -1, 63, 64, -1, 65, -1, 15, 114, -1, 16, 114, -1, 17, 114, 114, 114, -1, 18, 114, 114, 114, -1, 19, 114, 114, 114, -1, 20, 114, -1, 21, 114, -1, 22, 114, -1, 23, 114, -1, 24, 114, -1, 25, 114, -1, 26, 114, -1, 27, 114, -1, 30, 114, -1, -1, 28, 66, 71, -1, -1, 29, 67, 71, -1, -1, 17, 68, 71, -1, -1, 18, 69, 71, -1, -1, 19, 70, 71, -1, 116, -1, -1, 72, 73, -1, 72, 79, -1, 72, 95, -1, 72, 101, -1, 72, 107, -1, 10, 3, 74, -1, 10, 114, 3, 74, -1, -1, 74, 75, -1, 3, -1, -1, 77, 78, 76, 85, 86, 3, -1, 113, -1, 113, -1, 11, 3, 80, -1, 11, 114, 3, 80, -1, -1, 80, 81, -1, 3, -1, -1, 83, 84, 82, 85, 86, 3, -1, 113, -1, 113, -1, -1, 114, -1, -1, 86, 87, -1, 88, -1, 31, 114, 114, 114, -1, 32, 114, -1, 34, 114, -1, 35, 114, -1, 36, 114, -1, 37, 114, -1, 38, 114, -1, 39, 114, -1, 40, 114, -1, 41, 114, -1, 44, 114, -1, 45, 114, -1, 46, 114, -1, 48, 114, -1, 49, 114, -1, 50, 114, -1, -1, 33, 89, 94, -1, -1, 42, 90, 94, -1, -1, 43, 91, 94, -1, -1, 47, 92, 94, -1, -1, 31, 93, 94, -1, 116, -1, 12, 3, 96, -1, -1, 96, 97, -1, 3, -1, 99, 98, 3, -1, -1, 98, 100, -1, 113, -1, 113, -1, 13, 3, 102, -1, -1, 102, 103, -1, 3, -1, 105, 104, 3, -1, -1, 104, 106, -1, 113, -1, 113, -1, 108, 3, 109, -1, 14, -1, -1, 109, 110, -1, 111, 3, -1, -1, 112, 111, -1, 114, -1, 4, -1, 4, -1, -1, 115, 116, -1, 5, -1, 4, -1, 6, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 196, 196, 200, 200, 202, 204, 208, 214, 214, 216, 217, 218, 218, 221, 223, 227, 228, 232, 238, 238, 242, 242, 245, 246, 249, 252, 257, 262, 267, 270, 273, 276, 279, 282, 285, 288, 291, 296, 296, 300, 300, 304, 304, 308, 308, 313, 313, 320, 322, 322, 322, 322, 322, 322, 324, 325, 327, 327, 329, 330, 330, 336, 338, 340, 341, 343, 343, 345, 346, 346, 352, 354, 356, 356, 360, 360, 363, 364, 369, 372, 375, 378, 381, 384, 387, 390, 393, 396, 399, 402, 405, 408, 411, 416, 416, 420, 420, 424, 424, 428, 428, 432, 432, 438, 440, 442, 442, 444, 444, 446, 446, 448, 450, 455, 457, 457, 459, 459, 461, 461, 463, 465, 472, 474, 479, 479, 481, 483, 483, 485, 505, 508, 511, 511, 513, 515, 517 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "NEWLINE", "NUM", "ALNUM", "QSTR", "PSTR", "NETWORKLINE", "VERTICESLINE", "ARCSLINE", "EDGESLINE", "ARCSLISTLINE", "EDGESLISTLINE", "MATRIXLINE", "VP_X_FACT", "VP_Y_FACT", "VP_IC", "VP_BC", "VP_LC", "VP_LR", "VP_LPHI", "VP_BW", "VP_FOS", "VP_PHI", "VP_R", "VP_Q", "VP_LA", "VP_FONT", "VP_URL", "VP_SIZE", "EP_C", "EP_S", "EP_A", "EP_W", "EP_H1", "EP_H2", "EP_A1", "EP_A2", "EP_K1", "EP_K2", "EP_AP", "EP_P", "EP_L", "EP_LP", "EP_LR", "EP_LPHI", "EP_LC", "EP_LA", "EP_SIZE", "EP_FOS", "$accept", "input", "nethead", "vertices", "verticeshead", "vertdefs", "vertexline", "@1", "vertex", "vertexid", "vertexcoords", "shape", "params", "param", "vpword", "@2", "@3", "@4", "@5", "@6", "vpwordpar", "edgeblock", "arcs", "arcsdefs", "arcsline", "@7", "arcfrom", "arcto", "edges", "edgesdefs", "edgesline", "@8", "edgefrom", "edgeto", "weight", "edgeparams", "edgeparam", "epword", "@9", "@10", "@11", "@12", "@13", "epwordpar", "arcslist", "arcslistlines", "arclistline", "arctolist", "arclistfrom", "arclistto", "edgeslist", "edgelistlines", "edgelistline", "edgetolist", "edgelistfrom", "edgelistto", "adjmatrix", "matrixline", "adjmatrixlines", "adjmatrixline", "adjmatrixnumbers", "adjmatrixentry", "longint", "number", "words", "word", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 51, 52, 53, 53, 54, 55, 55, 56, 56, 57, 57, 58, 57, 59, 60, 61, 61, 61, 62, 62, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 66, 65, 67, 65, 68, 65, 69, 65, 70, 65, 71, 72, 72, 72, 72, 72, 72, 73, 73, 74, 74, 75, 76, 75, 77, 78, 79, 79, 80, 80, 81, 82, 81, 83, 84, 85, 85, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 89, 88, 90, 88, 91, 88, 92, 88, 93, 88, 94, 95, 96, 96, 97, 97, 98, 98, 99, 100, 101, 102, 102, 103, 103, 104, 104, 105, 106, 107, 108, 109, 109, 110, 111, 111, 112, 113, 114, 115, 115, 116, 116, 116 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 3, 0, 3, 3, 2, 3, 0, 2, 1, 2, 0, 7, 1, 1, 0, 2, 3, 0, 1, 0, 2, 1, 2, 2, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 1, 0, 2, 2, 2, 2, 2, 3, 4, 0, 2, 1, 0, 6, 1, 1, 3, 4, 0, 2, 1, 0, 6, 1, 1, 0, 1, 0, 2, 1, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 1, 3, 0, 2, 1, 3, 0, 2, 1, 1, 3, 0, 2, 1, 3, 0, 2, 1, 1, 3, 1, 0, 2, 2, 0, 2, 1, 1, 1, 0, 2, 1, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 3, 133, 0, 0, 0, 1, 0, 49, 0, 4, 136, 135, 137, 134, 131, 6, 2, 8, 7, 0, 0, 0, 0, 124, 50, 51, 52, 53, 54, 0, 5, 57, 132, 0, 66, 0, 106, 115, 125, 10, 9, 12, 14, 55, 57, 64, 66, 105, 114, 123, 11, 0, 59, 58, 0, 62, 56, 68, 67, 0, 71, 65, 108, 107, 110, 112, 117, 116, 119, 121, 126, 0, 128, 130, 16, 15, 60, 63, 69, 72, 0, 0, 127, 129, 19, 0, 73, 73, 109, 111, 113, 118, 120, 122, 21, 20, 17, 75, 74, 75, 0, 18, 0, 0, 13, 0, 0, 42, 44, 46, 0, 0, 0, 0, 0, 0, 0, 0, 38, 40, 0, 22, 23, 61, 102, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 96, 98, 0, 0, 0, 100, 0, 0, 0, 76, 77, 70, 24, 25, 0, 0, 0, 0, 0, 0, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 37, 0, 0, 79, 0, 80, 81, 82, 83, 84, 85, 86, 87, 0, 0, 88, 89, 90, 0, 91, 92, 93, 43, 48, 0, 45, 0, 47, 0, 39, 41, 103, 104, 0, 95, 97, 99, 101, 26, 27, 28, 78 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 2, 3, 7, 8, 30, 40, 51, 41, 74, 84, 94, 100, 121, 122, 163, 164, 149, 151, 153, 187, 16, 24, 43, 53, 86, 54, 76, 25, 45, 58, 87, 59, 78, 97, 102, 144, 145, 169, 178, 179, 183, 166, 196, 26, 47, 63, 80, 64, 89, 27, 48, 67, 81, 68, 92, 28, 29, 49, 70, 71, 72, 55, 73, 4, 188 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -167 static const yytype_int16 yypact[] = { -4, -167, 7, 36, 22, -167, 44, -167, 49, -167, -167, -167, -167, -167, -167, 44, 10, -167, -167, 12, 27, 51, 56, -167, -167, -167, -167, -167, -167, 58, 29, -167, -167, 59, -167, 60, -167, -167, -167, -167, -167, 61, -167, 31, -167, 33, -167, 35, 37, 39, -167, 5, -167, -167, 44, -167, 31, -167, -167, 44, -167, 33, -167, -167, -167, -167, -167, -167, -167, -167, -167, 62, 65, -167, 65, -167, -167, -167, -167, -167, 47, 53, -167, -167, 5, 65, 65, 65, -167, -167, -167, -167, -167, -167, -167, -167, 65, -167, -167, -167, 220, -167, 151, 172, -167, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, -167, -167, 65, -167, -167, -167, 65, 65, -167, 65, 65, 65, 65, 65, 65, 65, 65, -167, -167, 65, 65, 65, -167, 65, 65, 65, -167, -167, -167, -167, -167, 5, 65, 5, 65, 5, 65, -167, -167, -167, -167, -167, -167, -167, -167, 5, 5, -167, 5, 65, -167, 5, -167, -167, -167, -167, -167, -167, -167, -167, 5, 5, -167, -167, -167, 5, -167, -167, -167, -167, -167, 65, -167, 65, -167, 65, -167, -167, -167, -167, 65, -167, -167, -167, -167, -167, -167, -167, -167 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -145, -167, -167, 26, -167, -167, -167, -167, -167, 25, -167, -167, -167, -167, -15, -26, -167, -167, -167, -167, -167, -167, -167, -166, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, 2, -167, -1, -19, -167, -2 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -129 static const yytype_int16 yytable[] = { 33, 35, 13, 199, 1, 15, 190, 5, 192, 10, 11, 12, 200, 201, 18, 31, 32, 202, 194, 195, 19, 20, 21, 22, 23, 9, 10, 11, 12, 42, 34, 32, 39, 14, 52, 14, 57, 14, 62, 14, 66, 14, -128, 32, 60, 6, 65, 69, 14, 75, 88, 14, 17, 77, 36, 85, 91, 14, 79, 37, 60, 38, 44, 46, 50, 82, 96, 98, 98, 32, 56, 61, 99, 103, 83, 0, 0, 101, 0, 90, 93, 0, 95, 0, 0, 0, 147, 148, 150, 152, 154, 155, 156, 157, 158, 159, 160, 161, 162, 0, 0, 165, 0, 0, 0, 167, 168, 0, 170, 171, 172, 173, 174, 175, 176, 177, 0, 0, 180, 181, 182, 0, 184, 185, 186, 0, 0, 0, 0, 0, 0, 189, 0, 191, 0, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 197, 0, 0, 197, 0, 0, 203, 0, 204, 0, 205, 146, 197, 197, 0, 206, 0, 197, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 0, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120 }; static const yytype_int16 yycheck[] = { 19, 20, 4, 169, 8, 6, 151, 0, 153, 4, 5, 6, 178, 179, 15, 3, 4, 183, 163, 164, 10, 11, 12, 13, 14, 3, 4, 5, 6, 30, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 45, 9, 47, 48, 4, 51, 3, 4, 3, 54, 3, 74, 3, 4, 59, 3, 61, 3, 3, 3, 3, 3, 85, 86, 87, 4, 44, 46, 87, 99, 72, -1, -1, 96, -1, 80, 81, -1, 84, -1, -1, -1, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, -1, 120, -1, -1, -1, 124, 125, -1, 127, 128, 129, 130, 131, 132, 133, 134, -1, -1, 137, 138, 139, -1, 141, 142, 143, -1, -1, -1, -1, -1, -1, 150, -1, 152, -1, 154, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 167, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, 166, -1, -1, 169, -1, -1, 189, -1, 191, -1, 193, 3, 178, 179, -1, 198, -1, 183, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 8, 52, 53, 115, 0, 9, 54, 55, 3, 4, 5, 6, 116, 4, 113, 72, 3, 113, 10, 11, 12, 13, 14, 73, 79, 95, 101, 107, 108, 56, 3, 4, 114, 3, 114, 3, 3, 3, 3, 57, 59, 113, 74, 3, 80, 3, 96, 102, 109, 3, 58, 3, 75, 77, 113, 74, 3, 81, 83, 113, 80, 3, 97, 99, 113, 3, 103, 105, 113, 110, 111, 112, 114, 60, 116, 78, 113, 84, 113, 98, 104, 3, 111, 61, 114, 76, 82, 3, 100, 113, 3, 106, 113, 62, 116, 114, 85, 114, 85, 63, 114, 86, 86, 3, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 64, 65, 3, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 87, 88, 3, 114, 114, 68, 114, 69, 114, 70, 114, 114, 114, 114, 114, 114, 114, 114, 114, 66, 67, 114, 93, 114, 114, 89, 114, 114, 114, 114, 114, 114, 114, 114, 90, 91, 114, 114, 114, 92, 114, 114, 114, 71, 116, 114, 71, 114, 71, 114, 71, 71, 94, 116, 114, 94, 94, 94, 94, 114, 114, 114, 114 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) #else # define YYLEX yylex (&yylval, &yylloc, scanner) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location, context); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_pajek_parsedata_t* context; #endif { if (!yyvaluep) return; YYUSE (yylocationp); YYUSE (context); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_pajek_parsedata_t* context; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); YY_LOCATION_PRINT (yyoutput, *yylocationp); YYFPRINTF (yyoutput, ": "); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void yy_stack_print (bottom, top) yytype_int16 *bottom; yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); for (; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_pajek_parsedata_t* context) #else static void yy_reduce_print (yyvsp, yylsp, yyrule, context) YYSTYPE *yyvsp; YYLTYPE *yylsp; int yyrule; igraph_i_pajek_parsedata_t* context; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , context); fprintf (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, yylsp, Rule, context); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_pajek_parsedata_t* context) #else static void yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) const char *yymsg; int yytype; YYSTYPE *yyvaluep; YYLTYPE *yylocationp; igraph_i_pajek_parsedata_t* context; #endif { YYUSE (yyvaluep); YYUSE (yylocationp); YYUSE (context); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (igraph_i_pajek_parsedata_t* context); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (igraph_i_pajek_parsedata_t* context) #else int yyparse (context) igraph_i_pajek_parsedata_t* context; #endif #endif { /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /* Location data for the look-ahead symbol. */ YYLTYPE yylloc; int yystate; int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss = yyssa; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[2]; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; yylsp = yyls; #if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Initialize the default location before parsing starts. */ yylloc.first_line = yylloc.last_line = 1; yylloc.first_column = yylloc.last_column = 0; #endif goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyls = yyls1; yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); YYSTACK_RELOCATE (yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a look-ahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 196 "igraph/src/foreign-pajek-parser.y" { if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } ;} break; case 6: #line 204 "igraph/src/foreign-pajek-parser.y" { context->vcount=(yyvsp[(2) - (2)].intnum); context->vcount2=0; ;} break; case 7: #line 208 "igraph/src/foreign-pajek-parser.y" { context->vcount=(yyvsp[(2) - (3)].intnum); context->vcount2=(yyvsp[(3) - (3)].intnum); igraph_i_pajek_add_bipartite_type(context); ;} break; case 12: #line 218 "igraph/src/foreign-pajek-parser.y" { context->actvertex=(yyvsp[(1) - (1)].intnum); ;} break; case 13: #line 218 "igraph/src/foreign-pajek-parser.y" { ;} break; case 14: #line 221 "igraph/src/foreign-pajek-parser.y" { (yyval.intnum)=(yyvsp[(1) - (1)].intnum); context->mode=1; ;} break; case 15: #line 223 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_string_vertex_attribute("id", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context); ;} break; case 17: #line 228 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[(1) - (2)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[(2) - (2)].realnum), context); ;} break; case 18: #line 232 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[(1) - (3)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[(2) - (3)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("z", (yyvsp[(3) - (3)].realnum), context); ;} break; case 20: #line 238 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_string_vertex_attribute("shape", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context); ;} break; case 24: #line 246 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("xfact", (yyvsp[(2) - (2)].realnum), context); ;} break; case 25: #line 249 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("yfact", (yyvsp[(2) - (2)].realnum), context); ;} break; case 26: #line 252 "igraph/src/foreign-pajek-parser.y" { /* RGB color */ igraph_i_pajek_add_numeric_vertex_attribute("color-red", (yyvsp[(2) - (4)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("color-green", (yyvsp[(3) - (4)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("color-blue", (yyvsp[(4) - (4)].realnum), context); ;} break; case 27: #line 257 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", (yyvsp[(2) - (4)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", (yyvsp[(3) - (4)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", (yyvsp[(4) - (4)].realnum), context); ;} break; case 28: #line 262 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", (yyvsp[(2) - (4)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", (yyvsp[(3) - (4)].realnum), context); igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", (yyvsp[(4) - (4)].realnum), context); ;} break; case 29: #line 267 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("labeldist", (yyvsp[(2) - (2)].realnum), context); ;} break; case 30: #line 270 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", (yyvsp[(2) - (2)].realnum), context); ;} break; case 31: #line 273 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("framewidth", (yyvsp[(2) - (2)].realnum), context); ;} break; case 32: #line 276 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("fontsize", (yyvsp[(2) - (2)].realnum), context); ;} break; case 33: #line 279 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("rotation", (yyvsp[(2) - (2)].realnum), context); ;} break; case 34: #line 282 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("radius", (yyvsp[(2) - (2)].realnum), context); ;} break; case 35: #line 285 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", (yyvsp[(2) - (2)].realnum), context); ;} break; case 36: #line 288 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", (yyvsp[(2) - (2)].realnum), context); ;} break; case 37: #line 291 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", (yyvsp[(2) - (2)].realnum), context); ;} break; case 38: #line 296 "igraph/src/foreign-pajek-parser.y" { context->mode=3; ;} break; case 39: #line 296 "igraph/src/foreign-pajek-parser.y" { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("font", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 40: #line 300 "igraph/src/foreign-pajek-parser.y" { context->mode=3; ;} break; case 41: #line 300 "igraph/src/foreign-pajek-parser.y" { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("url", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 42: #line 304 "igraph/src/foreign-pajek-parser.y" { context->mode=3; ;} break; case 43: #line 304 "igraph/src/foreign-pajek-parser.y" { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("color", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 44: #line 308 "igraph/src/foreign-pajek-parser.y" { context->mode=3; ;} break; case 45: #line 308 "igraph/src/foreign-pajek-parser.y" { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("framecolor", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 46: #line 313 "igraph/src/foreign-pajek-parser.y" { context->mode=3; ;} break; case 47: #line 313 "igraph/src/foreign-pajek-parser.y" { context->mode=1; igraph_i_pajek_add_string_vertex_attribute("labelcolor", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 48: #line 320 "igraph/src/foreign-pajek-parser.y" { (yyval.string)=(yyvsp[(1) - (1)].string); ;} break; case 55: #line 324 "igraph/src/foreign-pajek-parser.y" { context->directed=1; ;} break; case 56: #line 325 "igraph/src/foreign-pajek-parser.y" { context->directed=1; ;} break; case 60: #line 330 "igraph/src/foreign-pajek-parser.y" { context->actedge++; context->mode=2; ;} break; case 61: #line 331 "igraph/src/foreign-pajek-parser.y" { igraph_vector_push_back(context->vector, (yyvsp[(1) - (6)].intnum)-1); igraph_vector_push_back(context->vector, (yyvsp[(2) - (6)].intnum)-1); ;} break; case 64: #line 340 "igraph/src/foreign-pajek-parser.y" { context->directed=0; ;} break; case 65: #line 341 "igraph/src/foreign-pajek-parser.y" { context->directed=0; ;} break; case 69: #line 346 "igraph/src/foreign-pajek-parser.y" { context->actedge++; context->mode=2; ;} break; case 70: #line 347 "igraph/src/foreign-pajek-parser.y" { igraph_vector_push_back(context->vector, (yyvsp[(1) - (6)].intnum)-1); igraph_vector_push_back(context->vector, (yyvsp[(2) - (6)].intnum)-1); ;} break; case 74: #line 356 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context); ;} break; case 78: #line 364 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("color-red", (yyvsp[(2) - (4)].realnum), context); igraph_i_pajek_add_numeric_edge_attribute("color-green", (yyvsp[(3) - (4)].realnum), context); igraph_i_pajek_add_numeric_edge_attribute("color-blue", (yyvsp[(4) - (4)].realnum), context); ;} break; case 79: #line 369 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[(2) - (2)].realnum), context); ;} break; case 80: #line 372 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("edgewidth", (yyvsp[(2) - (2)].realnum), context); ;} break; case 81: #line 375 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("hook1", (yyvsp[(2) - (2)].realnum), context); ;} break; case 82: #line 378 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("hook2", (yyvsp[(2) - (2)].realnum), context); ;} break; case 83: #line 381 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("angle1", (yyvsp[(2) - (2)].realnum), context); ;} break; case 84: #line 384 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("angle2", (yyvsp[(2) - (2)].realnum), context); ;} break; case 85: #line 387 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("velocity1", (yyvsp[(2) - (2)].realnum), context); ;} break; case 86: #line 390 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("velocity2", (yyvsp[(2) - (2)].realnum), context); ;} break; case 87: #line 393 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("arrowpos", (yyvsp[(2) - (2)].realnum), context); ;} break; case 88: #line 396 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("labelpos", (yyvsp[(2) - (2)].realnum), context); ;} break; case 89: #line 399 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("labelangle", (yyvsp[(2) - (2)].realnum), context); ;} break; case 90: #line 402 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("labelangle2", (yyvsp[(2) - (2)].realnum), context); ;} break; case 91: #line 405 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("labeldegree", (yyvsp[(2) - (2)].realnum), context); ;} break; case 92: #line 408 "igraph/src/foreign-pajek-parser.y" { /* what is this??? */ igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[(2) - (2)].realnum), context); ;} break; case 93: #line 411 "igraph/src/foreign-pajek-parser.y" { igraph_i_pajek_add_numeric_edge_attribute("fontsize", (yyvsp[(2) - (2)].realnum), context); ;} break; case 94: #line 416 "igraph/src/foreign-pajek-parser.y" { context->mode=4; ;} break; case 95: #line 416 "igraph/src/foreign-pajek-parser.y" { context->mode=2; igraph_i_pajek_add_string_edge_attribute("arrowtype", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 96: #line 420 "igraph/src/foreign-pajek-parser.y" { context->mode=4; ;} break; case 97: #line 420 "igraph/src/foreign-pajek-parser.y" { context->mode=2; igraph_i_pajek_add_string_edge_attribute("linepattern", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 98: #line 424 "igraph/src/foreign-pajek-parser.y" { context->mode=4; ;} break; case 99: #line 424 "igraph/src/foreign-pajek-parser.y" { context->mode=2; igraph_i_pajek_add_string_edge_attribute("label", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 100: #line 428 "igraph/src/foreign-pajek-parser.y" { context->mode=4; ;} break; case 101: #line 428 "igraph/src/foreign-pajek-parser.y" { context->mode=2; igraph_i_pajek_add_string_edge_attribute("labelcolor", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 102: #line 432 "igraph/src/foreign-pajek-parser.y" { context->mode=4; ;} break; case 103: #line 432 "igraph/src/foreign-pajek-parser.y" { context->mode=2; igraph_i_pajek_add_string_edge_attribute("color", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context); ;} break; case 104: #line 438 "igraph/src/foreign-pajek-parser.y" { context->mode=2; (yyval.string)=(yyvsp[(1) - (1)].string); ;} break; case 105: #line 440 "igraph/src/foreign-pajek-parser.y" { context->directed=1; ;} break; case 112: #line 448 "igraph/src/foreign-pajek-parser.y" { context->mode=0; context->actfrom=fabs((yyvsp[(1) - (1)].intnum))-1; ;} break; case 113: #line 450 "igraph/src/foreign-pajek-parser.y" { igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, fabs((yyvsp[(1) - (1)].intnum))-1); ;} break; case 114: #line 455 "igraph/src/foreign-pajek-parser.y" { context->directed=0; ;} break; case 121: #line 463 "igraph/src/foreign-pajek-parser.y" { context->mode=0; context->actfrom=fabs((yyvsp[(1) - (1)].intnum))-1; ;} break; case 122: #line 465 "igraph/src/foreign-pajek-parser.y" { igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, fabs((yyvsp[(1) - (1)].intnum))-1); ;} break; case 124: #line 474 "igraph/src/foreign-pajek-parser.y" { context->actfrom=0; context->actto=0; context->directed=(context->vcount2==0); ;} break; case 127: #line 481 "igraph/src/foreign-pajek-parser.y" { context->actfrom++; context->actto=0; ;} break; case 130: #line 485 "igraph/src/foreign-pajek-parser.y" { if ((yyvsp[(1) - (1)].realnum) != 0) { if (context->vcount2==0) { context->actedge++; igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context); igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, context->actto); } else if (context->vcount2 + context->actto < context->vcount) { context->actedge++; igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context); igraph_vector_push_back(context->vector, context->actfrom); igraph_vector_push_back(context->vector, context->vcount2+context->actto); } } context->actto++; ;} break; case 131: #line 505 "igraph/src/foreign-pajek-parser.y" { (yyval.intnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), igraph_pajek_yyget_leng(scanner)); ;} break; case 132: #line 508 "igraph/src/foreign-pajek-parser.y" { (yyval.realnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), igraph_pajek_yyget_leng(scanner)); ;} break; case 135: #line 513 "igraph/src/foreign-pajek-parser.y" { (yyval.string).str=igraph_pajek_yyget_text(scanner); (yyval.string).len=igraph_pajek_yyget_leng(scanner); ;} break; case 136: #line 515 "igraph/src/foreign-pajek-parser.y" { (yyval.string).str=igraph_pajek_yyget_text(scanner); (yyval.string).len=igraph_pajek_yyget_leng(scanner); ;} break; case 137: #line 517 "igraph/src/foreign-pajek-parser.y" { (yyval.string).str=igraph_pajek_yyget_text(scanner)+1; (yyval.string).len=igraph_pajek_yyget_leng(scanner)-2; ;} break; /* Line 1267 of yacc.c. */ #line 2358 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (&yylloc, context, YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (&yylloc, context, yymsg); } else { yyerror (&yylloc, context, YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } yyerror_range[0] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, context); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; yyerror_range[0] = yylsp[1-yylen]; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[0] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp, context); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; yyerror_range[1] = yylloc; /* Using YYLLOC is tempting, but would change the location of the look-ahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, context, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, context); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp, context); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } #line 520 "igraph/src/foreign-pajek-parser.y" int igraph_pajek_yyerror(YYLTYPE* locp, igraph_i_pajek_parsedata_t *context, char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "Parse error in Pajek file, line %i (%s)", locp->first_line, s); return 0; } igraph_real_t igraph_pajek_get_number(const char *str, long int length) { igraph_real_t num; char *tmp=igraph_Calloc(length+1, char); strncpy(tmp, str, length); tmp[length]='\0'; sscanf(tmp, "%lf", &num); igraph_Free(tmp); return num; } /* TODO: NA's */ int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, long int count, const char *attrname, igraph_integer_t vid, igraph_real_t number) { long int attrsize=igraph_trie_size(names); long int id; igraph_vector_t *na; igraph_attribute_record_t *rec; igraph_trie_get(names, attrname, &id); if (id == attrsize) { /* add a new attribute */ rec=igraph_Calloc(1, igraph_attribute_record_t); na=igraph_Calloc(1, igraph_vector_t); igraph_vector_init(na, count); rec->name=strdup(attrname); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->value=na; igraph_vector_ptr_push_back(attrs, rec); } rec=VECTOR(*attrs)[id]; na=(igraph_vector_t*)rec->value; if (igraph_vector_size(na) == vid) { IGRAPH_CHECK(igraph_vector_push_back(na, number)); } else if (igraph_vector_size(na) < vid) { long int origsize=igraph_vector_size(na); IGRAPH_CHECK(igraph_vector_resize(na, (long int)vid+1)); for (;origsizename=strdup(attrname); rec->type=IGRAPH_ATTRIBUTE_STRING; rec->value=na; igraph_vector_ptr_push_back(attrs, rec); } rec=VECTOR(*attrs)[id]; na=(igraph_strvector_t*)rec->value; if (igraph_strvector_size(na) <= vid) { long int origsize=igraph_strvector_size(na); IGRAPH_CHECK(igraph_strvector_resize(na, vid+1)); for (;origsizevertex_attribute_names, context->vertex_attributes, context->vcount, name, context->actvertex-1, tmp); igraph_Free(tmp); IGRAPH_FINALLY_CLEAN(1); return ret; } int igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, int len, igraph_i_pajek_parsedata_t *context) { char *tmp; int ret; tmp=igraph_Calloc(len+1, char); if (tmp==0) { IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, tmp); strncpy(tmp, value, len); tmp[len]='\0'; ret=igraph_i_pajek_add_string_attribute(context->edge_attribute_names, context->edge_attributes, context->actedge, name, context->actedge-1, tmp); igraph_Free(tmp); IGRAPH_FINALLY_CLEAN(1); return ret; } int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context) { return igraph_i_pajek_add_numeric_attribute(context->vertex_attribute_names, context->vertex_attributes, context->vcount, name, context->actvertex-1, value); } int igraph_i_pajek_add_numeric_edge_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context) { return igraph_i_pajek_add_numeric_attribute(context->edge_attribute_names, context->edge_attributes, context->actedge, name, context->actedge-1, value); } int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context) { const char *attrname="type"; igraph_trie_t *names=context->vertex_attribute_names; igraph_vector_ptr_t *attrs=context->vertex_attributes; int i, n=context->vcount, n1=context->vcount2; long int attrid, attrsize=igraph_trie_size(names); igraph_attribute_record_t *rec; igraph_vector_t *na; if (n1 > n) { IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file", IGRAPH_PARSEERROR); } igraph_trie_get(names, attrname, &attrid); if (attrid != attrsize) { IGRAPH_ERROR("Duplicate 'type' attribute in Pajek file, " "this should not happen", IGRAPH_EINTERNAL); } /* add a new attribute */ rec=igraph_Calloc(1, igraph_attribute_record_t); na=igraph_Calloc(1, igraph_vector_t); igraph_vector_init(na, n); rec->name=strdup(attrname); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->value=na; igraph_vector_ptr_push_back(attrs, rec); for (i=0; ivector; int i, n1=context->vcount2; int ne=igraph_vector_size(edges); for (i=0; i n1 && v2 > n1) ) { IGRAPH_WARNING("Invalid edge in bipartite graph"); } } return 0; } igraph/src/igraph_lapack.h0000644000176000001440000000744712325527073015323 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LAPACK_H #define LAPACK_H #include "igraph_vector.h" #include "igraph_matrix.h" #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /** * \section about_lapack LAPACK interface in igraph * * * LAPACK is written in Fortran90 and provides routines for solving * systems of simultaneous linear equations, least-squares solutions * of linear systems of equations, eigenvalue problems, and singular * value problems. The associated matrix factorizations (LU, Cholesky, * QR, SVD, Schur, generalized Schur) are also provided, as are * related computations such as reordering of the Schur factorizations * and estimating condition numbers. Dense and banded matrices are * handled, but not general sparse matrices. In all areas, similar * functionality is provided for real and complex matrices, in both * single and double precision. * * * * igraph provides an interface to a very limited set of LAPACK * functions, using the regular igraph data structures. * * * * See more about LAPACK at http://www.netlib.org/lapack/ * */ int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, int *info); int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, igraph_vector_int_t *ipiv, igraph_matrix_t *b); int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, igraph_matrix_t *b, int *info); typedef enum { IGRAPH_LAPACK_DSYEV_ALL, IGRAPH_LAPACK_DSYEV_INTERVAL, IGRAPH_LAPACK_DSYEV_SELECT } igraph_lapack_dsyev_which_t; int igraph_lapack_dsyevr(const igraph_matrix_t *A, igraph_lapack_dsyev_which_t which, igraph_real_t vl, igraph_real_t vu, int vestimate, int il, int iu, igraph_real_t abstol, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_int_t *support); /* TODO: should we use complex vectors/matrices? */ int igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, igraph_matrix_t *vectorsright, int *info); typedef enum { IGRAPH_LAPACK_DGEEVX_BALANCE_NONE=0, IGRAPH_LAPACK_DGEEVX_BALANCE_PERM, IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE, IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH } igraph_lapack_dgeevx_balance_t; int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, igraph_matrix_t *vectorsright, int *ilo, int *ihi, igraph_vector_t *scale, igraph_real_t *abnrm, igraph_vector_t *rconde, igraph_vector_t *rcondv, int *info); int igraph_lapack_dgehrd(const igraph_matrix_t *A, int ilo, int ihi, igraph_matrix_t *result); __END_DECLS #endif igraph/src/cs_compress.c0000644000176000001440000000355312325527073015043 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* C = compressed-column form of a triplet matrix T */ cs *cs_compress (const cs *T) { CS_INT m, n, nz, p, k, *Cp, *Ci, *w, *Ti, *Tj ; CS_ENTRY *Cx, *Tx ; cs *C ; if (!CS_TRIPLET (T)) return (NULL) ; /* check inputs */ m = T->m ; n = T->n ; Ti = T->i ; Tj = T->p ; Tx = T->x ; nz = T->nz ; C = cs_spalloc (m, n, nz, Tx != NULL, 0) ; /* allocate result */ w = cs_calloc (n, sizeof (CS_INT)) ; /* get workspace */ if (!C || !w) return (cs_done (C, w, NULL, 0)) ; /* out of memory */ Cp = C->p ; Ci = C->i ; Cx = C->x ; for (k = 0 ; k < nz ; k++) w [Tj [k]]++ ; /* column counts */ cs_cumsum (Cp, w, n) ; /* column pointers */ for (k = 0 ; k < nz ; k++) { Ci [p = w [Tj [k]]++] = Ti [k] ; /* A(i,j) is the pth entry in C */ if (Cx) Cx [p] = Tx [k] ; } return (cs_done (C, w, NULL, 1)) ; /* success; free w and return C */ } igraph/src/drl_graph.cpp0000644000176000001440000011144712325527073015027 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file contains the member definitions of the master class #include #include #include #include #include #include #include using namespace std; #include "drl_graph.h" #include "igraph_random.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #ifdef MUSE_MPI #include #endif namespace drl { // constructor -- initializes the schedule variables (as in // graph constructor) // graph::graph ( int proc_id, int tot_procs, char *int_file ) // { // // MPI parameters // myid = proc_id; // num_procs = tot_procs; // // initial annealing parameters // STAGE = 0; // iterations = 0; // temperature = 2000; // attraction = 10; // damping_mult = 1.0; // min_edges = 20; // first_add = fine_first_add = true; // fineDensity = false; // // Brian's original Vx schedule // liquid.iterations = 200; // liquid.temperature = 2000; // liquid.attraction = 2; // liquid.damping_mult = 1.0; // liquid.time_elapsed = 0; // expansion.iterations = 200; // expansion.temperature = 2000; // expansion.attraction = 10; // expansion.damping_mult = 1.0; // expansion.time_elapsed = 0; // cooldown.iterations = 200; // cooldown.temperature = 2000; // cooldown.attraction = 1; // cooldown.damping_mult = .1; // cooldown.time_elapsed = 0; // crunch.iterations = 50; // crunch.temperature = 250; // crunch.attraction = 1; // crunch. damping_mult = .25; // crunch.time_elapsed = 0; // simmer.iterations = 100; // simmer.temperature = 250; // simmer.attraction = .5; // simmer.damping_mult = 0.0; // simmer.time_elapsed = 0; // // scan .int file for node info // scan_int ( int_file ); // // populate node positions and ids // positions.reserve ( num_nodes ); // map < int, int >::iterator cat_iter; // for ( cat_iter = id_catalog.begin(); // cat_iter != id_catalog.end(); // cat_iter++ ) // positions.push_back ( Node( cat_iter->first ) ); // /* // // output positions .ids for debugging // for ( int id = 0; id < num_nodes; id++ ) // cout << positions[id].id << endl; // */ // // read .int file for graph info // read_int ( int_file ); // // initialize density server // density_server.Init(); // } graph::graph(const igraph_t *igraph, const igraph_layout_drl_options_t *options, const igraph_vector_t *weights) { myid = 0; num_procs = 1; STAGE = 0; iterations = options->init_iterations; temperature = options->init_temperature; attraction = options->init_attraction; damping_mult = options->init_damping_mult; min_edges = 20; first_add = fine_first_add = true; fineDensity = false; // Brian's original Vx schedule liquid.iterations = options->liquid_iterations; liquid.temperature = options->liquid_temperature; liquid.attraction = options->liquid_attraction; liquid.damping_mult = options->liquid_damping_mult; liquid.time_elapsed = 0; expansion.iterations = options->expansion_iterations; expansion.temperature = options->expansion_temperature; expansion.attraction = options->expansion_attraction; expansion.damping_mult = options->expansion_damping_mult; expansion.time_elapsed = 0; cooldown.iterations = options->cooldown_iterations; cooldown.temperature = options->cooldown_temperature; cooldown.attraction = options->cooldown_attraction; cooldown.damping_mult = options->cooldown_damping_mult; cooldown.time_elapsed = 0; crunch.iterations = options->crunch_iterations; crunch.temperature = options->crunch_temperature; crunch.attraction = options->crunch_attraction; crunch.damping_mult = options->crunch_damping_mult; crunch.time_elapsed = 0; simmer.iterations = options->simmer_iterations; simmer.temperature = options->simmer_temperature; simmer.attraction = options->simmer_attraction; simmer.damping_mult = options->simmer_damping_mult; simmer.time_elapsed = 0; // scan .int file for node info highest_sim = 1.0; num_nodes=igraph_vcount(igraph); long int no_of_edges=igraph_ecount(igraph); for (long int i=0; i::iterator cat_iter; for ( cat_iter = id_catalog.begin(); cat_iter != id_catalog.end(); cat_iter++) { cat_iter->second = cat_iter->first; } // populate node positions and ids positions.reserve ( num_nodes ); for ( cat_iter = id_catalog.begin(); cat_iter != id_catalog.end(); cat_iter++ ) { positions.push_back ( Node( cat_iter->first ) ); } // read .int file for graph info long int node_1, node_2; double weight; for (long int i=0; i> id1 >> id2 >> edge_weight; // // ignore negative weights! // if ( edge_weight <= 0 ) // { // cout << "Error: found negative edge weight in " << filename << ". Program stopped." << endl; // #ifdef MUSE_MPI // MPI_Abort ( MPI_COMM_WORLD, 1 ); // #else // exit (1); // #endif // } // if ( highest_sim < edge_weight ) // highest_sim = edge_weight; // id_catalog[id1] = 1; // id_catalog[id2] = 1; // } // fp.close(); // if ( id_catalog.size() == 0 ) // { // cout << "Error: Proc. " << myid << ": " << filename << " is empty. Program terminated." << endl; // #ifdef MUSE_MPI // MPI_Abort ( MPI_COMM_WORLD, 1 ); // #else // exit (1); // #endif // } // // label nodes with sequential integers starting at 0 // map< int, int>::iterator cat_iter; // int id_label; // for ( cat_iter = id_catalog.begin(), id_label = 0; // cat_iter != id_catalog.end(); cat_iter++, id_label++ ) // cat_iter->second = id_label; // /* // // output id_catalog for debugging: // for ( cat_iter = id_catalog.begin(); // cat_iter != id_catalog.end(); // cat_iter++ ) // cout << cat_iter->first << "\t" << cat_iter->second << endl; // */ // num_nodes = id_catalog.size(); // } // read in .parms file, if present /* void graph::read_parms ( char *parms_file ) { // read from .parms file ifstream parms_in ( parms_file ); if ( !parms_in ) { cout << "Error: could not open .parms file! Program stopped." << endl; #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else exit (1); #endif } cout << "Processor " << myid << " reading .parms file." << endl; // read in stage parameters string parm_label; // this is ignored in the .parms file // initial parameters parms_in >> parm_label >> iterations; parms_in >> parm_label >> temperature; parms_in >> parm_label >> attraction; parms_in >> parm_label >> damping_mult; // liquid stage parms_in >> parm_label >> liquid.iterations; parms_in >> parm_label >> liquid.temperature; parms_in >> parm_label >> liquid.attraction; parms_in >> parm_label >> liquid.damping_mult; // expansion stage parms_in >> parm_label >> expansion.iterations; parms_in >> parm_label >> expansion.temperature; parms_in >> parm_label >> expansion.attraction; parms_in >> parm_label >> expansion.damping_mult; // cooldown stage parms_in >> parm_label >> cooldown.iterations; parms_in >> parm_label >> cooldown.temperature; parms_in >> parm_label >> cooldown.attraction; parms_in >> parm_label >> cooldown.damping_mult; // crunch stage parms_in >> parm_label >> crunch.iterations; parms_in >> parm_label >> crunch.temperature; parms_in >> parm_label >> crunch.attraction; parms_in >> parm_label >> crunch.damping_mult; // simmer stage parms_in >> parm_label >> simmer.iterations; parms_in >> parm_label >> simmer.temperature; parms_in >> parm_label >> simmer.attraction; parms_in >> parm_label >> simmer.damping_mult; parms_in.close(); // print out parameters for double checking if ( myid == 0 ) { cout << "Processor 0 reports the following inputs:" << endl; cout << "inital.iterations = " << iterations << endl; cout << "initial.temperature = " << temperature << endl; cout << "initial.attraction = " << attraction << endl; cout << "initial.damping_mult = " << damping_mult << endl; cout << " ..." << endl; cout << "liquid.iterations = " << liquid.iterations << endl; cout << "liquid.temperature = " << liquid.temperature << endl; cout << "liquid.attraction = " << liquid.attraction << endl; cout << "liquid.damping_mult = " << liquid.damping_mult << endl; cout << " ..." << endl; cout << "simmer.iterations = " << simmer.iterations << endl; cout << "simmer.temperature = " << simmer.temperature << endl; cout << "simmer.attraction = " << simmer.attraction << endl; cout << "simmer.damping_mult = " << simmer.damping_mult << endl; } } */ // init_parms -- this subroutine initializes the edge_cut variables // used in the original VxOrd starting with the edge_cut parameter. // In our version, edge_cut = 0 means no cutting, 1 = maximum cut. // We also set the random seed here. void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) { IGRAPH_UNUSED(rand_seed); // first we translate edge_cut the former tcl sliding scale //CUT_END = cut_length_end = 39000.0 * (1.0 - edge_cut) + 1000.0; CUT_END = cut_length_end = 40000.0 * (1.0 - edge_cut); // cut_length_end cannot actually be 0 if ( cut_length_end <= 1.0 ) cut_length_end = 1.0; float cut_length_start = 4.0 * cut_length_end; // now we set the parameters used by ReCompute cut_off_length = cut_length_start; cut_rate = ( cut_length_start - cut_length_end ) / 400.0; // finally set the number of iterations to leave .real coords fixed int full_comp_iters; full_comp_iters = liquid.iterations + expansion.iterations + cooldown.iterations + crunch.iterations + 3; // adjust real parm to iterations (do not enter simmer halfway) if ( real_parm < 0 ) real_iterations = (int)real_parm; else if ( real_parm == 1) real_iterations = full_comp_iters + simmer.iterations + 100; else real_iterations = (int)(real_parm*full_comp_iters); tot_iterations = 0; if ( real_iterations > 0 ) real_fixed = true; else real_fixed = false; // calculate total expected iterations (for progress bar display) tot_expected_iterations = liquid.iterations + expansion.iterations + cooldown.iterations + crunch.iterations + simmer.iterations; /* // output edge_cutting parms (for debugging) cout << "Processor " << myid << ": " << "cut_length_end = CUT_END = " << cut_length_end << ", cut_length_start = " << cut_length_start << ", cut_rate = " << cut_rate << endl; */ // set random seed // srand ( rand_seed ); // Don't need this in igraph } void graph::init_parms(const igraph_layout_drl_options_t *options) { double rand_seed = 0.0; double real_in = -1.0; init_parms(rand_seed, options->edge_cut, real_in); } // The following subroutine reads a .real file to obtain initial // coordinates. If a node is missing coordinates the coordinates // are computed // void graph::read_real ( char *real_file ) // { // cout << "Processor " << myid << " reading .real file ..." << endl; // // read in .real file and mark as fixed // ifstream real_in ( real_file ); // if ( !real_in ) // { // cout << "Error: proc. " << myid << " could not open .real file." << endl; // #ifdef MUSE_MPI // MPI_Abort ( MPI_COMM_WORLD, 1 ); // #else // exit (1); // #endif // } // int real_id; // float real_x, real_y; // while ( !real_in.eof () ) // { // real_id = -1; // real_in >> real_id >> real_x >> real_y; // if ( real_id >= 0 ) // { // positions[id_catalog[real_id]].x = real_x; // positions[id_catalog[real_id]].y = real_y; // positions[id_catalog[real_id]].fixed = true; // /* // // output positions read (for debugging) // cout << id_catalog[real_id] << " (" << positions[id_catalog[real_id]].x // << ", " << positions[id_catalog[real_id]].y << ") " // << positions[id_catalog[real_id]].fixed << endl; // */ // // add node to density grid // if ( real_iterations > 0 ) // density_server.Add ( positions[id_catalog[real_id]], fineDensity ); // } // } // real_in.close(); // } int graph::read_real ( const igraph_matrix_t *real_mat, const igraph_vector_bool_t *fixed) { long int n=igraph_matrix_nrow(real_mat); for (long int i=0; i 0 ) { density_server.Add ( positions[id_catalog[i]], fineDensity ); } } return 0; } // The read_part_int subroutine reads the .int // file produced by convert_sim and gathers the nodes and their // neighbors in the range start_ind to end_ind. // void graph::read_int ( char *file_name ) // { // ifstream int_file; // int_file.open ( file_name ); // if ( !int_file ) // { // cout << "Error (worker process " << myid << "): could not open .int file." << endl; // #ifdef MUSE_MPI // MPI_Abort ( MPI_COMM_WORLD, 1 ); // #else // exit (1); // #endif // } // cout << "Processor " << myid << " reading .int file ..." << endl; // int node_1, node_2; // float weight; // while ( !int_file.eof() ) // { // weight = 0; // all weights should be >= 0 // int_file >> node_1 >> node_2 >> weight; // if ( weight ) // otherwise we are at end of file // // or it is a self-connected node // { // // normalization from original vxord // weight /= highest_sim; // weight = weight*fabs(weight); // // initialize graph // if ( ( node_1 % num_procs ) == myid ) // (neighbors[id_catalog[node_1]])[id_catalog[node_2]] = weight; // if ( ( node_2 % num_procs ) == myid ) // (neighbors[id_catalog[node_2]])[id_catalog[node_1]] = weight; // } // } // int_file.close(); // /* // // the following code outputs the contents of the neighbors structure // // (to be used for debugging) // map >::iterator i; // map::iterator j; // for ( i = neighbors.begin(); i != neighbors.end(); i++ ) { // cout << myid << ": " << i->first << " "; // for (j = (i->second).begin(); j != (i->second).end(); j++ ) // cout << j->first << " (" << j->second << ") "; // cout << endl; // } // */ // } /********************************************* * Function: ReCompute * * Description: Compute the graph locations * * Modified from original code by B. Wylie * ********************************************/ int graph::ReCompute( ) { // carryover from original VxOrd int MIN = 1; /* // output parameters (for debugging) cout << "ReCompute is using the following parameters: "<< endl; cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature << ", attract = " << attraction << ", damping_mult = " << damping_mult << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length << ", fineDensity = " << fineDensity << endl; */ /* igraph progress report */ float progress = (tot_iterations * 100.0 / tot_expected_iterations); switch (STAGE) { case 0: if (iterations == 0) IGRAPH_PROGRESS("DrL layout (initialization stage)", progress, 0); else IGRAPH_PROGRESS("DrL layout (liquid stage)", progress, 0); break; case 1: IGRAPH_PROGRESS("DrL layout (expansion stage)", progress, 0); break; case 2: IGRAPH_PROGRESS("DrL layout (cooldown and cluster phase)", progress, 0); break; case 3: IGRAPH_PROGRESS("DrL layout (crunch phase)", progress, 0); break; case 5: IGRAPH_PROGRESS("DrL layout (simmer phase)", progress, 0); break; case 6: IGRAPH_PROGRESS("DrL layout (final phase)", 100.0, 0); break; default: IGRAPH_PROGRESS("DrL layout (unknown phase)", 0.0, 0); break; } /* Compute Energies for individual nodes */ update_nodes (); // check to see if we need to free fixed nodes tot_iterations++; if ( tot_iterations >= real_iterations ) real_fixed = false; // **************************************** // AUTOMATIC CONTROL SECTION // **************************************** // STAGE 0: LIQUID if (STAGE == 0) { if ( iterations == 0 ) { start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering liquid stage ..."; } if (iterations < liquid.iterations) { temperature = liquid.temperature; attraction = liquid.attraction; damping_mult = liquid.damping_mult; iterations++; // if ( myid == 0 ) // cout << "." << flush; } else { stop_time = time( NULL ); liquid.time_elapsed = liquid.time_elapsed + (stop_time - start_time); temperature = expansion.temperature; attraction = expansion.attraction; damping_mult = expansion.damping_mult; iterations = 0; // go to next stage STAGE = 1; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering expansion stage ..."; } } // STAGE 1: EXPANSION if (STAGE == 1) { if (iterations < expansion.iterations) { // Play with vars if (attraction > 1) attraction -= .05; if (min_edges > 12) min_edges -= .05; cut_off_length -= cut_rate; if (damping_mult > .1) damping_mult -= .005; iterations++; // if ( myid == 0 ) cout << "." << flush; } else { stop_time = time( NULL ); expansion.time_elapsed = expansion.time_elapsed + (stop_time - start_time); min_edges = 12; damping_mult = cooldown.damping_mult; STAGE = 2; attraction = cooldown.attraction; temperature = cooldown.temperature; iterations = 0; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering cool-down stage ..."; } } // STAGE 2: Cool down and cluster else if(STAGE==2) { if (iterations < cooldown.iterations) { // Reduce temperature if (temperature > 50) temperature -= 10; // Reduce cut length if (cut_off_length > cut_length_end) cut_off_length -= cut_rate*2; if (min_edges > MIN) min_edges -= .2; //min_edges = 99; iterations++; // if ( myid == 0 ) // cout << "." << flush; } else { stop_time = time( NULL ); cooldown.time_elapsed = cooldown.time_elapsed + (stop_time - start_time); cut_off_length = cut_length_end; temperature = crunch.temperature; damping_mult = crunch.damping_mult; min_edges = MIN; //min_edges = 99; // In other words: no more cutting STAGE = 3; iterations = 0; attraction = crunch.attraction; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering crunch stage ..."; } } // STAGE 3: Crunch else if(STAGE==3) { if (iterations < crunch.iterations) { iterations++; // if ( myid == 0 ) cout << "." << flush; } else { stop_time = time( NULL ); crunch.time_elapsed = crunch.time_elapsed + (stop_time - start_time); iterations = 0; temperature = simmer.temperature; attraction = simmer.attraction; damping_mult = simmer.damping_mult; min_edges = 99; fineDensity = true; STAGE = 5; start_time = time( NULL ); // if ( myid == 0 ) // cout << "Entering simmer stage ..."; } } // STAGE 5: Simmer else if( STAGE==5 ) { if (iterations < simmer.iterations) { if (temperature > 50) temperature -= 2; iterations++; // if ( myid == 0 ) cout << "." << flush; } else { stop_time = time( NULL ); simmer.time_elapsed = simmer.time_elapsed + (stop_time - start_time); STAGE = 6; // if ( myid == 0 ) // cout << "Layout calculation completed in " << // ( liquid.time_elapsed + expansion.time_elapsed + // cooldown.time_elapsed + crunch.time_elapsed + // simmer.time_elapsed ) // << " seconds (not including I/O)." // << endl; } } // STAGE 6: All Done! else if ( STAGE == 6) { /* // output parameters (for debugging) cout << "ReCompute is using the following parameters: "<< endl; cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature << ", attract = " << attraction << ", damping_mult = " << damping_mult << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length << ", fineDensity = " << fineDensity << endl; */ return 0; } // **************************************** // END AUTOMATIC CONTROL SECTION // **************************************** // Still need more recomputation return 1; } // update_nodes -- this function will complete the primary node update // loop in layout's recompute routine. It follows exactly the same // sequence to ensure similarity of parallel layout to the standard layout void graph::update_nodes ( ) { vector node_indices; // node list of nodes currently being updated float old_positions[2*MAX_PROCS]; // positions before update float new_positions[2*MAX_PROCS]; // positions after update bool all_fixed; // check if all nodes are fixed // initial node list consists of 0,1,...,num_procs for ( int i = 0; i < num_procs; i++ ) node_indices.push_back( i ); // next we calculate the number of nodes there would be if the // num_nodes by num_procs schedule grid were perfectly square int square_num_nodes = (int)(num_procs + num_procs*floor ((float)(num_nodes-1)/(float)num_procs )); for ( int i = myid; i < square_num_nodes; i += num_procs ) { // get old positions get_positions ( node_indices, old_positions ); // default new position is old position get_positions ( node_indices, new_positions ); if ( i < num_nodes ) { // advance random sequence according to myid for ( int j = 0; j < 2*myid; j++ ) RNG_UNIF01(); // rand(); // calculate node energy possibilities if ( !(positions[i].fixed && real_fixed) ) update_node_pos ( i, old_positions, new_positions ); // advance random sequence for next iteration for ( unsigned int j = 2*myid; j < 2*(node_indices.size()-1); j++ ) RNG_UNIF01(); // rand(); } else { // advance random sequence according to use by // the other processors for ( unsigned int j = 0; j < 2*(node_indices.size()); j++ ) RNG_UNIF01(); //rand(); } // check if anything was actually updated (e.g. everything was fixed) all_fixed = true; for ( unsigned int j = 0; j < node_indices.size (); j++ ) if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) all_fixed = false; // update positions across processors (if not all fixed) if ( !all_fixed ) { #ifdef MUSE_MPI MPI_Allgather ( &new_positions[2*myid], 2, MPI_FLOAT, new_positions, 2, MPI_FLOAT, MPI_COMM_WORLD ); #endif // update positions (old to new) update_density ( node_indices, old_positions, new_positions ); } /* if ( myid == 0 ) { // output node list (for debugging) for ( unsigned int j = 0; j < node_indices.size(); j++ ) cout << node_indices[j] << " "; cout << endl; } */ // compute node list for next update for ( unsigned int j = 0; j < node_indices.size(); j++ ) node_indices [j] += num_procs; while ( !node_indices.empty() && node_indices.back() >= num_nodes ) node_indices.pop_back ( ); } // update first_add and fine_first_add first_add = false; if ( fineDensity ) fine_first_add = false; } // The get_positions function takes the node_indices list // and returns the corresponding positions in an array. void graph::get_positions ( vector &node_indices, float return_positions[2*MAX_PROCS] ) { // fill positions for(unsigned int i=0; i < node_indices.size(); i++) { return_positions[2*i] = positions[ node_indices[i] ].x; return_positions[2*i+1] = positions[ node_indices[i] ].y; } } // update_node_pos -- this subroutine does the actual work of computing // the new position of a given node. num_act_proc gives the number // of active processes at this level for use by the random number // generators. void graph::update_node_pos ( int node_ind, float old_positions[2*MAX_PROCS], float new_positions[2*MAX_PROCS] ) { float energies[2]; // node energies for possible positions float updated_pos[2][2]; // possible positions float pos_x, pos_y; // old VxOrd parameter float jump_length = .010 * temperature; // subtract old node density_server.Subtract ( positions[node_ind], first_add, fine_first_add, fineDensity ); // compute node energy for old solution energies[0] = Compute_Node_Energy ( node_ind ); // move node to centroid position Solve_Analytic ( node_ind, pos_x, pos_y ); positions[node_ind].x = updated_pos[0][0] = pos_x; positions[node_ind].y = updated_pos[0][1] = pos_y; /* // ouput random numbers (for debugging) int rand_0, rand_1; rand_0 = rand(); rand_1 = rand(); cout << myid << ": " << rand_0 << ", " << rand_1 << endl; */ // Do random method (RAND_MAX is C++ maximum random number) updated_pos[1][0] = updated_pos[0][0] + (.5 - RNG_UNIF01()) * jump_length; updated_pos[1][1] = updated_pos[0][1] + (.5 - RNG_UNIF01()) * jump_length; // compute node energy for random position positions[node_ind].x = updated_pos[1][0]; positions[node_ind].y = updated_pos[1][1]; energies[1] = Compute_Node_Energy ( node_ind ); /* // output update possiblities (debugging): cout << node_ind << ": (" << updated_pos[0][0] << "," << updated_pos[0][1] << "), " << energies[0] << "; (" << updated_pos[1][0] << "," << updated_pos[1][1] << "), " << energies[1] << endl; */ // add back old position positions[node_ind].x = old_positions[2*myid]; positions[node_ind].y = old_positions[2*myid+1]; if ( !fineDensity && !first_add ) density_server.Add ( positions[node_ind], fineDensity ); else if ( !fine_first_add ) density_server.Add ( positions[node_ind], fineDensity ); // choose updated node position with lowest energy if ( energies[0] < energies[1] ) { new_positions[2*myid] = updated_pos[0][0]; new_positions[2*myid+1] = updated_pos[0][1]; positions[node_ind].energy = energies[0]; } else { new_positions[2*myid] = updated_pos[1][0]; new_positions[2*myid+1] = updated_pos[1][1]; positions[node_ind].energy = energies[1]; } } // update_density takes a sequence of node_indices and their positions and // updates the positions by subtracting the old positions and adding the // new positions to the density grid. void graph::update_density ( vector &node_indices, float old_positions[2*MAX_PROCS], float new_positions[2*MAX_PROCS] ) { // go through each node and subtract old position from // density grid before adding new position for ( unsigned int i = 0; i < node_indices.size(); i++ ) { positions[node_indices[i]].x = old_positions[2*i]; positions[node_indices[i]].y = old_positions[2*i+1]; density_server.Subtract ( positions[node_indices[i]], first_add, fine_first_add, fineDensity ); positions[node_indices[i]].x = new_positions[2*i]; positions[node_indices[i]].y = new_positions[2*i+1]; density_server.Add ( positions[node_indices[i]], fineDensity ); } } /******************************************** * Function: Compute_Node_Energy * * Description: Compute the node energy * * This code has been modified from the * * original code by B. Wylie. * *********************************************/ float graph::Compute_Node_Energy( int node_ind ) { /* Want to expand 4th power range of attraction */ float attraction_factor = attraction*attraction* attraction*attraction*2e-2; map ::iterator EI; float x_dis,y_dis; float energy_distance, weight; float node_energy=0; // Add up all connection energies for(EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { // Get edge weight weight = EI->second; // Compute x,y distance x_dis = positions[ node_ind ].x - positions[ EI->first ].x; y_dis = positions[ node_ind ].y - positions[ EI->first ].y; // Energy Distance energy_distance = x_dis*x_dis + y_dis*y_dis; if (STAGE<2) energy_distance *= energy_distance; // In the liquid phase we want to discourage long link distances if (STAGE==0) energy_distance *= energy_distance; node_energy += weight * attraction_factor * energy_distance; } // output effect of density (debugging) //cout << "[before: " << node_energy; // add density node_energy += density_server.GetDensity ( positions[ node_ind ].x, positions[ node_ind ].y, fineDensity ); // after calling density server (debugging) //cout << ", after: " << node_energy << "]" << endl; // return computated energy return node_energy; } /********************************************* * Function: Solve_Analytic * * Description: Compute the node position * * This is a modified version of the function * * originally written by B. Wylie * *********************************************/ void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y ) { map ::iterator EI; float total_weight = 0; float x_dis, y_dis,x_cen=0, y_cen=0; float x=0,y=0,dis; float damping,weight; // Sum up all connections for(EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { weight = EI->second; total_weight += weight; x += weight * positions[ EI->first ].x; y += weight * positions[ EI->first ].y; } // Now set node position if (total_weight > 0) { // Compute centriod x_cen = x/total_weight; y_cen = y/total_weight; damping = 1.0 - damping_mult; pos_x = damping*positions[ node_ind ].x + (1.0-damping) * x_cen; pos_y = damping*positions[ node_ind ].y + (1.0-damping) * y_cen; } else { pos_x = positions[ node_ind ].x; pos_y = positions[ node_ind ].y; } // No cut edge flag (?) if (min_edges == 99) return; // Don't cut at end of scale if ( CUT_END >= 39500 ) return; float num_connections = sqrt((double)neighbors[node_ind].size()); float maxLength = 0; map::iterator maxIndex; // Go through nodes edges... cutting if necessary for(EI = maxIndex = neighbors[node_ind].begin(); EI !=neighbors[node_ind].end(); ++EI) { // Check for at least min edges if (neighbors[node_ind].size() < min_edges) continue; x_dis = x_cen - positions[ EI->first ].x; y_dis = y_cen - positions[ EI->first ].y; dis = x_dis*x_dis+y_dis*y_dis; dis *= num_connections; // Store maximum edge if (dis > maxLength) {maxLength = dis; maxIndex=EI;} } // If max length greater than cut_length then cut if (maxLength > cut_off_length) neighbors[ node_ind ].erase( maxIndex ); } // write_coord writes out the coordinate file of the final solutions // void graph::write_coord( const char *file_name ) // { // ofstream coordOUT( file_name ); // if ( !coordOUT ) // { // cout << "Could not open " << file_name << ". Program terminated." << endl; // #ifdef MUSE_MPI // MPI_Abort ( MPI_COMM_WORLD, 1 ); // #else // exit (1); // #endif // } // cout << "Writing out solution to " << file_name << " ..." << endl; // for (unsigned int i = 0; i < positions.size(); i++) { // coordOUT << positions[i].id << "\t" << positions[i].x << "\t" << positions[i].y < >::iterator i; map::iterator j; for ( i = neighbors.begin(); i != neighbors.end(); i++ ) for (j = (i->second).begin(); j != (i->second).end(); j++ ) simOUT << positions[i->first].id << "\t" << positions[j->first].id << "\t" << j->second << endl; simOUT.close(); } */ // get_tot_energy adds up the energy for each node to give an estimate of the // quality of the minimization. float graph::get_tot_energy ( ) { float my_tot_energy, tot_energy; my_tot_energy = 0; for ( int i = myid; i < num_nodes; i += num_procs ) my_tot_energy += positions[i].energy; //vector::iterator i; //for ( i = positions.begin(); i != positions.end(); i++ ) // tot_energy += i->energy; #ifdef MUSE_MPI MPI_Reduce ( &my_tot_energy, &tot_energy, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD ); #else tot_energy = my_tot_energy; #endif return tot_energy; } // The following subroutine draws the graph with possible intermediate // output (int_out is set to 0 if not proc. 0). int_out is the parameter // passed by the user, and coord_file is the .coord file. // void graph::draw_graph ( int int_out, char *coord_file ) // { // // layout graph (with possible intermediate output) // int count_iter = 0, count_file = 1; // char int_coord_file [MAX_FILE_NAME + MAX_INT_LENGTH]; // while ( ReCompute( ) ) // if ( (int_out > 0) && (count_iter == int_out) ) // { // // output intermediate solution // sprintf ( int_coord_file, "%s.%d", coord_file, count_file ); // write_coord ( int_coord_file ); // count_iter = 0; // count_file++; // } // else // count_iter++; // } int graph::draw_graph(igraph_matrix_t *res) { int count_iter=0; while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); count_iter++; } long int n=positions.size(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 2)); for (long int i=0; i. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpapi.h" /*********************************************************************** * NAME * * lpx_put_solution - store basic solution components * * SYNOPSIS * * void lpx_put_solution(glp_prob *lp, int inval, const int *p_stat, * const int *d_stat, const double *obj_val, const int r_stat[], * const double r_prim[], const double r_dual[], const int c_stat[], * const double c_prim[], const double c_dual[]) * * DESCRIPTION * * The routine lpx_put_solution stores basic solution components to the * specified problem object. * * The parameter inval is the basis factorization invalidity flag. * If this flag is clear, the current status of the basis factorization * remains unchanged. If this flag is set, the routine invalidates the * basis factorization. * * The parameter p_stat is a pointer to the status of primal basic * solution, which should be specified as follows: * * GLP_UNDEF - primal solution is undefined; * GLP_FEAS - primal solution is feasible; * GLP_INFEAS - primal solution is infeasible; * GLP_NOFEAS - no primal feasible solution exists. * * If the parameter p_stat is NULL, the current status of primal basic * solution remains unchanged. * * The parameter d_stat is a pointer to the status of dual basic * solution, which should be specified as follows: * * GLP_UNDEF - dual solution is undefined; * GLP_FEAS - dual solution is feasible; * GLP_INFEAS - dual solution is infeasible; * GLP_NOFEAS - no dual feasible solution exists. * * If the parameter d_stat is NULL, the current status of dual basic * solution remains unchanged. * * The parameter obj_val is a pointer to the objective function value. * If it is NULL, the current value of the objective function remains * unchanged. * * The array element r_stat[i], 1 <= i <= m (where m is the number of * rows in the problem object), specifies the status of i-th auxiliary * variable, which should be specified as follows: * * GLP_BS - basic variable; * GLP_NL - non-basic variable on lower bound; * GLP_NU - non-basic variable on upper bound; * GLP_NF - non-basic free variable; * GLP_NS - non-basic fixed variable. * * If the parameter r_stat is NULL, the current statuses of auxiliary * variables remain unchanged. * * The array element r_prim[i], 1 <= i <= m (where m is the number of * rows in the problem object), specifies a primal value of i-th * auxiliary variable. If the parameter r_prim is NULL, the current * primal values of auxiliary variables remain unchanged. * * The array element r_dual[i], 1 <= i <= m (where m is the number of * rows in the problem object), specifies a dual value (reduced cost) * of i-th auxiliary variable. If the parameter r_dual is NULL, the * current dual values of auxiliary variables remain unchanged. * * The array element c_stat[j], 1 <= j <= n (where n is the number of * columns in the problem object), specifies the status of j-th * structural variable, which should be specified as follows: * * GLP_BS - basic variable; * GLP_NL - non-basic variable on lower bound; * GLP_NU - non-basic variable on upper bound; * GLP_NF - non-basic free variable; * GLP_NS - non-basic fixed variable. * * If the parameter c_stat is NULL, the current statuses of structural * variables remain unchanged. * * The array element c_prim[j], 1 <= j <= n (where n is the number of * columns in the problem object), specifies a primal value of j-th * structural variable. If the parameter c_prim is NULL, the current * primal values of structural variables remain unchanged. * * The array element c_dual[j], 1 <= j <= n (where n is the number of * columns in the problem object), specifies a dual value (reduced cost) * of j-th structural variable. If the parameter c_dual is NULL, the * current dual values of structural variables remain unchanged. */ void lpx_put_solution(glp_prob *lp, int inval, const int *p_stat, const int *d_stat, const double *obj_val, const int r_stat[], const double r_prim[], const double r_dual[], const int c_stat[], const double c_prim[], const double c_dual[]) { GLPROW *row; GLPCOL *col; int i, j; /* invalidate the basis factorization, if required */ if (inval) lp->valid = 0; /* store primal status */ if (p_stat != NULL) { if (!(*p_stat == GLP_UNDEF || *p_stat == GLP_FEAS || *p_stat == GLP_INFEAS || *p_stat == GLP_NOFEAS)) xerror("lpx_put_solution: p_stat = %d; invalid primal statu" "s\n", *p_stat); lp->pbs_stat = *p_stat; } /* store dual status */ if (d_stat != NULL) { if (!(*d_stat == GLP_UNDEF || *d_stat == GLP_FEAS || *d_stat == GLP_INFEAS || *d_stat == GLP_NOFEAS)) xerror("lpx_put_solution: d_stat = %d; invalid dual status " "\n", *d_stat); lp->dbs_stat = *d_stat; } /* store objective function value */ if (obj_val != NULL) lp->obj_val = *obj_val; /* store row solution components */ for (i = 1; i <= lp->m; i++) { row = lp->row[i]; if (r_stat != NULL) { if (!(r_stat[i] == GLP_BS || row->type == GLP_FR && r_stat[i] == GLP_NF || row->type == GLP_LO && r_stat[i] == GLP_NL || row->type == GLP_UP && r_stat[i] == GLP_NU || row->type == GLP_DB && r_stat[i] == GLP_NL || row->type == GLP_DB && r_stat[i] == GLP_NU || row->type == GLP_FX && r_stat[i] == GLP_NS)) xerror("lpx_put_solution: r_stat[%d] = %d; invalid row s" "tatus\n", i, r_stat[i]); row->stat = r_stat[i]; } if (r_prim != NULL) row->prim = r_prim[i]; if (r_dual != NULL) row->dual = r_dual[i]; } /* store column solution components */ for (j = 1; j <= lp->n; j++) { col = lp->col[j]; if (c_stat != NULL) { if (!(c_stat[j] == GLP_BS || col->type == GLP_FR && c_stat[j] == GLP_NF || col->type == GLP_LO && c_stat[j] == GLP_NL || col->type == GLP_UP && c_stat[j] == GLP_NU || col->type == GLP_DB && c_stat[j] == GLP_NL || col->type == GLP_DB && c_stat[j] == GLP_NU || col->type == GLP_FX && c_stat[j] == GLP_NS)) xerror("lpx_put_solution: c_stat[%d] = %d; invalid colum" "n status\n", j, c_stat[j]); col->stat = c_stat[j]; } if (c_prim != NULL) col->prim = c_prim[j]; if (c_dual != NULL) col->dual = c_dual[j]; } return; } /*---------------------------------------------------------------------- -- lpx_put_mip_soln - store mixed integer solution components. -- -- *Synopsis* -- -- #include "glplpx.h" -- void lpx_put_mip_soln(glp_prob *lp, int i_stat, double row_mipx[], -- double col_mipx[]); -- -- *Description* -- -- The routine lpx_put_mip_soln stores solution components obtained by -- branch-and-bound solver into the specified problem object. -- -- NOTE: This routine is intended for internal use only. */ void lpx_put_mip_soln(glp_prob *lp, int i_stat, double row_mipx[], double col_mipx[]) { GLPROW *row; GLPCOL *col; int i, j; double sum; /* store mixed integer status */ #if 0 if (!(i_stat == LPX_I_UNDEF || i_stat == LPX_I_OPT || i_stat == LPX_I_FEAS || i_stat == LPX_I_NOFEAS)) fault("lpx_put_mip_soln: i_stat = %d; invalid mixed integer st" "atus", i_stat); lp->i_stat = i_stat; #else switch (i_stat) { case LPX_I_UNDEF: lp->mip_stat = GLP_UNDEF; break; case LPX_I_OPT: lp->mip_stat = GLP_OPT; break; case LPX_I_FEAS: lp->mip_stat = GLP_FEAS; break; case LPX_I_NOFEAS: lp->mip_stat = GLP_NOFEAS; break; default: xerror("lpx_put_mip_soln: i_stat = %d; invalid mixed intege" "r status\n", i_stat); } #endif /* store row solution components */ if (row_mipx != NULL) { for (i = 1; i <= lp->m; i++) { row = lp->row[i]; row->mipx = row_mipx[i]; } } /* store column solution components */ if (col_mipx != NULL) { for (j = 1; j <= lp->n; j++) { col = lp->col[j]; col->mipx = col_mipx[j]; } } /* if the solution is claimed to be integer feasible, check it */ if (lp->mip_stat == GLP_OPT || lp->mip_stat == GLP_FEAS) { for (j = 1; j <= lp->n; j++) { col = lp->col[j]; if (col->kind == GLP_IV && col->mipx != floor(col->mipx)) xerror("lpx_put_mip_soln: col_mipx[%d] = %.*g; must be i" "ntegral\n", j, DBL_DIG, col->mipx); } } /* compute the objective function value */ sum = lp->c0; for (j = 1; j <= lp->n; j++) { col = lp->col[j]; sum += col->coef * col->mipx; } lp->mip_obj = sum; return; } /* eof */ igraph/src/dseupd.f0000644000176000001440000011030612325527073014005 0ustar ripleyusersc\BeginDoc c c\Name: igraphdseupd c c\Description: c c This subroutine returns the converged approximations to eigenvalues c of A*z = lambda*B*z and (optionally): c c (1) the corresponding approximate eigenvectors, c c (2) an orthonormal (Lanczos) basis for the associated approximate c invariant subspace, c c (3) Both. c c There is negligible additional cost to obtain eigenvectors. An orthonormal c (Lanczos) basis is always computed. There is an additional storage cost c of n*nev if both are requested (in this case a separate array Z must be c supplied). c c These quantities are obtained from the Lanczos factorization computed c by DSAUPD for the linear operator OP prescribed by the MODE selection c (see IPARAM(7) in DSAUPD documentation.) DSAUPD must be called before c this routine is called. These approximate eigenvalues and vectors are c commonly called Ritz values and Ritz vectors respectively. They are c referred to as such in the comments that follow. The computed orthonormal c basis for the invariant subspace corresponding to these Ritz values is c referred to as a Lanczos basis. c c See documentation in the header of the subroutine DSAUPD for a definition c of OP as well as other terms and the relation of computed Ritz values c and vectors of OP with respect to the given problem A*z = lambda*B*z. c c The approximate eigenvalues of the original problem are returned in c ascending algebraic order. The user may elect to call this routine c once for each desired Ritz vector and store it peripherally if desired. c There is also the option of computing a selected set of these vectors c with a single call. c c\Usage: c call igraphdseupd c ( RVEC, HOWMNY, SELECT, D, Z, LDZ, SIGMA, BMAT, N, WHICH, NEV, TOL, c RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, LWORKL, INFO ) c c RVEC LOGICAL (INPUT) c Specifies whether Ritz vectors corresponding to the Ritz value c approximations to the eigenproblem A*z = lambda*B*z are computed. c c RVEC = .FALSE. Compute Ritz values only. c c RVEC = .TRUE. Compute Ritz vectors. c c HOWMNY Character*1 (INPUT) c Specifies how many Ritz vectors are wanted and the form of Z c the matrix of Ritz vectors. See remark 1 below. c = 'A': compute NEV Ritz vectors; c = 'S': compute some of the Ritz vectors, specified c by the logical array SELECT. c c SELECT Logical array of dimension NEV. (INPUT) c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be c computed. To select the Ritz vector corresponding to a c Ritz value D(j), SELECT(j) must be set to .TRUE.. c If HOWMNY = 'A' , SELECT is not referenced. c c D Double precision array of dimension NEV. (OUTPUT) c On exit, D contains the Ritz value approximations to the c eigenvalues of A*z = lambda*B*z. The values are returned c in ascending order. If IPARAM(7) = 3,4,5 then D represents c the Ritz values of OP computed by igraphdsaupd transformed to c those of the original eigensystem A*z = lambda*B*z. If c IPARAM(7) = 1,2 then the Ritz values of OP are the same c as the those of A*z = lambda*B*z. c c Z Double precision N by NEV array if HOWMNY = 'A'. (OUTPUT) c On exit, Z contains the B-orthonormal Ritz vectors of the c eigensystem A*z = lambda*B*z corresponding to the Ritz c value approximations. c If RVEC = .FALSE. then Z is not referenced. c NOTE: The array Z may be set equal to first NEV columns of the c Arnoldi/Lanczos basis array V computed by DSAUPD. c c LDZ Integer. (INPUT) c The leading dimension of the array Z. If Ritz vectors are c desired, then LDZ .ge. max( 1, N ). In any case, LDZ .ge. 1. c c SIGMA Double precision (INPUT) c If IPARAM(7) = 3,4,5 represents the shift. Not referenced if c IPARAM(7) = 1 or 2. c c c **** The remaining arguments MUST be the same as for the **** c **** call to DNAUPD that was just completed. **** c c NOTE: The remaining arguments c c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, c WORKD, WORKL, LWORKL, INFO c c must be passed directly to DSEUPD following the last call c to DSAUPD. These arguments MUST NOT BE MODIFIED between c the the last call to DSAUPD and the call to DSEUPD. c c Two of these parameters (WORKL, INFO) are also output parameters: c c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) c WORKL(1:4*ncv) contains information obtained in c igraphdsaupd. They are not changed by igraphdseupd. c WORKL(4*ncv+1:ncv*ncv+8*ncv) holds the c untransformed Ritz values, the computed error estimates, c and the associated eigenvector matrix of H. c c Note: IPNTR(8:10) contains the pointer into WORKL for addresses c of the above information computed by igraphdseupd. c ------------------------------------------------------------- c IPNTR(8): pointer to the NCV RITZ values of the original system. c IPNTR(9): pointer to the NCV corresponding error bounds. c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors c of the tridiagonal matrix T. Only referenced by c igraphdseupd if RVEC = .TRUE. See Remarks. c ------------------------------------------------------------- c c INFO Integer. (OUTPUT) c Error flag on output. c = 0: Normal exit. c = -1: N must be positive. c = -2: NEV must be positive. c = -3: NCV must be greater than NEV and less than or equal to N. c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. c = -6: BMAT must be one of 'I' or 'G'. c = -7: Length of private work WORKL array is not sufficient. c = -8: Error return from trid. eigenvalue calculation; c Information error from LAPACK routine dsteqr. c = -9: Starting vector is zero. c = -10: IPARAM(7) must be 1,2,3,4,5. c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. c = -12: NEV and WHICH = 'BE' are incompatible. c = -14: DSAUPD did not find any eigenvalues to sufficient c accuracy. c = -15: HOWMNY must be one of 'A' or 'S' if RVEC = .true. c = -16: HOWMNY = 'S' not yet implemented c c\BeginLib c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, c 1980. c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", c Computer Physics Communications, 53 (1989), pp 169-179. c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to c Implement the Spectral Transformation", Math. Comp., 48 (1987), c pp 663-673. c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", c SIAM J. Matr. Anal. Apps., January (1993). c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines c for Updating the QR decomposition", ACM TOMS, December 1990, c Volume 16 Number 4, pp 369-377. c c\Remarks c 1. The converged Ritz values are always returned in increasing c (algebraic) order. c c 2. Currently only HOWMNY = 'A' is implemented. It is included at this c stage for the user who wants to incorporate it. c c\Routines called: c igraphdsesrt ARPACK routine that sorts an array X, and applies the c corresponding permutation to a matrix A. c igraphdsortr igraphdsortr ARPACK sorting routine. c igraphivout ARPACK utility routine that prints integers. c igraphdvout ARPACK utility routine that prints vectors. c dgeqr2 LAPACK routine that computes the QR factorization of c a matrix. c dlacpy LAPACK matrix copy routine. c dlamch LAPACK routine that determines machine constants. c dorm2r LAPACK routine that applies an orthogonal matrix in c factored form. c dsteqr LAPACK routine that computes eigenvalues and eigenvectors c of a tridiagonal matrix. c dger Level 2 BLAS rank one update to a matrix. c dcopy Level 1 BLAS that copies one vector to another . c dnrm2 Level 1 BLAS that computes the norm of a vector. c dscal Level 1 BLAS that scales a vector. c dswap Level 1 BLAS that swaps the contents of two vectors. c\Authors c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Chao Yang Houston, Texas c Dept. of Computational & c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c 12/15/93: Version ' 2.1' c c\SCCS Information: @(#) c FILE: seupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 c c\EndLib c c----------------------------------------------------------------------- subroutine igraphdseupd (rvec, howmny, select, d, z, ldz, & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, & ipntr, workd, workl, lworkl, info ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat, howmny, which*2 logical rvec, select(ncv) integer info, ldz, ldv, lworkl, n, ncv, nev Double precision & sigma, tol c c %-----------------% c | Array Arguments | c %-----------------% c integer iparam(7), ipntr(11) Double precision & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), & workd(2*n), workl(lworkl) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c character type*6 integer bounds, ierr, ih, ihb, ihd, iq, iw, j, k, & ldh, ldq, mode, msglvl, nconv, next, ritz, & irz, ibd, ktrord, leftptr, rghtptr, ism, ilg Double precision & bnorm2, rnorm, temp, thres1, thres2, tempbnd, eps23 logical reord c c %--------------% c | Local Arrays | c %--------------% c Double precision & kv(2) c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, dger, dgeqr2, dlacpy, dorm2r, dscal, & igraphdsesrt, dsteqr, dswap, igraphdvout, & igraphivout, igraphdsortr c c %--------------------% c | External Functions | c %--------------------% c Double precision & dnrm2, dlamch external dnrm2, dlamch c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic min c c %-----------------------% c | Executable Statements | c %-----------------------% c c %------------------------% c | Set default parameters | c %------------------------% c msglvl = mseupd mode = iparam(7) nconv = iparam(5) info = 0 c c %--------------% c | Quick return | c %--------------% c if (nconv .eq. 0) go to 9000 ierr = 0 c if (nconv .le. 0) ierr = -14 if (n .le. 0) ierr = -1 if (nev .le. 0) ierr = -2 if (ncv .le. nev .or. ncv .gt. n) ierr = -3 if (which .ne. 'LM' .and. & which .ne. 'SM' .and. & which .ne. 'LA' .and. & which .ne. 'SA' .and. & which .ne. 'BE') ierr = -5 if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 if ( (howmny .ne. 'A' .and. & howmny .ne. 'P' .and. & howmny .ne. 'S') .and. rvec ) & ierr = -15 if (rvec .and. howmny .eq. 'S') ierr = -16 c if (rvec .and. lworkl .lt. ncv**2+8*ncv) ierr = -7 c if (mode .eq. 1 .or. mode .eq. 2) then type = 'REGULR' else if (mode .eq. 3 ) then type = 'SHIFTI' else if (mode .eq. 4 ) then type = 'BUCKLE' else if (mode .eq. 5 ) then type = 'CAYLEY' else ierr = -10 end if if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 if (nev .eq. 1 .and. which .eq. 'BE') ierr = -12 c c %------------% c | Error Exit | c %------------% c if (ierr .ne. 0) then info = ierr go to 9000 end if c c %-------------------------------------------------------% c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | c | etc... and the remaining workspace. | c | Also update pointer to be used on output. | c | Memory is laid out as follows: | c | workl(1:2*ncv) := generated tridiagonal matrix H | c | The subdiagonal is stored in workl(2:ncv). | c | The dead spot is workl(1) but upon exiting | c | igraphdsaupd stores the B-norm of the last residual | c | vector in workl(1). We use this !!! | c | workl(2*ncv+1:2*ncv+ncv) := ritz values | c | The wanted values are in the first NCONV spots. | c | workl(3*ncv+1:3*ncv+ncv) := computed Ritz estimates | c | The wanted values are in the first NCONV spots. | c | NOTE: workl(1:4*ncv) is set by igraphdsaupd and is not | c | modified by igraphdseupd. | c %-------------------------------------------------------% c c %-------------------------------------------------------% c | The following is used and set by igraphdseupd. | c | workl(4*ncv+1:4*ncv+ncv) := used as workspace during | c | computation of the eigenvectors of H. Stores | c | the diagonal of H. Upon EXIT contains the NCV | c | Ritz values of the original system. The first | c | NCONV spots have the wanted values. If MODE = | c | 1 or 2 then will equal workl(2*ncv+1:3*ncv). | c | workl(5*ncv+1:5*ncv+ncv) := used as workspace during | c | computation of the eigenvectors of H. Stores | c | the subdiagonal of H. Upon EXIT contains the | c | NCV corresponding Ritz estimates of the | c | original system. The first NCONV spots have the | c | wanted values. If MODE = 1,2 then will equal | c | workl(3*ncv+1:4*ncv). | c | workl(6*ncv+1:6*ncv+ncv*ncv) := orthogonal Q that is | c | the eigenvector matrix for H as returned by | c | dsteqr. Not referenced if RVEC = .False. | c | Ordering follows that of workl(4*ncv+1:5*ncv) | c | workl(6*ncv+ncv*ncv+1:6*ncv+ncv*ncv+2*ncv) := | c | Workspace. Needed by dsteqr and by igraphdseupd. | c | GRAND total of NCV*(NCV+8) locations. | c %-------------------------------------------------------% c c ih = ipntr(5) ritz = ipntr(6) bounds = ipntr(7) ldh = ncv ldq = ncv ihd = bounds + ldh ihb = ihd + ldh iq = ihb + ldh iw = iq + ldh*ncv next = iw + 2*ncv ipntr(4) = next ipntr(8) = ihd ipntr(9) = ihb ipntr(10) = iq c c %----------------------------------------% c | irz points to the Ritz values computed | c | by _seigt before exiting _saup2. | c | ibd points to the Ritz estimates | c | computed by _seigt before exiting | c | _saup2. | c %----------------------------------------% c irz = ipntr(11)+ncv ibd = irz+ncv c c c %---------------------------------% c | Set machine dependent constant. | c %---------------------------------% c eps23 = dlamch('Epsilon-Machine') eps23 = eps23**(2.0D+0 / 3.0D+0) c c %---------------------------------------% c | RNORM is B-norm of the RESID(1:N). | c | BNORM2 is the 2 norm of B*RESID(1:N). | c | Upon exit of igraphdsaupd WORKD(1:N) has | c | B*RESID(1:N). | c %---------------------------------------% c rnorm = workl(ih) if (bmat .eq. 'I') then bnorm2 = rnorm else if (bmat .eq. 'G') then bnorm2 = dnrm2(n, workd, 1) end if c if (rvec) then c c %------------------------------------------------% c | Get the converged Ritz value on the boundary. | c | This value will be used to dermine whether we | c | need to reorder the eigenvalues and | c | eigenvectors comupted by _steqr, and is | c | referred to as the "threshold" value. | c | | c | A Ritz value gamma is said to be a wanted | c | one, if | c | abs(gamma) .ge. threshold, when WHICH = 'LM'; | c | abs(gamma) .le. threshold, when WHICH = 'SM'; | c | gamma .ge. threshold, when WHICH = 'LA'; | c | gamma .le. threshold, when WHICH = 'SA'; | c | gamma .le. thres1 .or. gamma .ge. thres2 | c | when WHICH = 'BE'; | c | | c | Note: converged Ritz values and associated | c | Ritz estimates have been placed in the first | c | NCONV locations in workl(ritz) and | c | workl(bounds) respectively. They have been | c | sorted (in _saup2) according to the WHICH | c | selection criterion. (Except in the case | c | WHICH = 'BE', they are sorted in an increasing | c | order.) | c %------------------------------------------------% c if (which .eq. 'LM' .or. which .eq. 'SM' & .or. which .eq. 'LA' .or. which .eq. 'SA' ) then c thres1 = workl(ritz) c if (msglvl .gt. 2) then call igraphdvout(logfil, 1, thres1, ndigit, & '_seupd: Threshold eigenvalue used for re-ordering') end if c else if (which .eq. 'BE') then c c %------------------------------------------------% c | Ritz values returned from _saup2 have been | c | sorted in increasing order. Thus two | c | "threshold" values (one for the small end, one | c | for the large end) are in the middle. | c %------------------------------------------------% c ism = max(nev,nconv) / 2 ilg = ism + 1 thres1 = workl(ism) thres2 = workl(ilg) c if (msglvl .gt. 2) then kv(1) = thres1 kv(2) = thres2 call igraphdvout(logfil, 2, kv, ndigit, & '_seupd: Threshold eigenvalues used for re-ordering') end if c end if c c %----------------------------------------------------------% c | Check to see if all converged Ritz values appear within | c | the first NCONV diagonal elements returned from _seigt. | c | This is done in the following way: | c | | c | 1) For each Ritz value obtained from _seigt, compare it | c | with the threshold Ritz value computed above to | c | determine whether it is a wanted one. | c | | c | 2) If it is wanted, then check the corresponding Ritz | c | estimate to see if it has converged. If it has, set | c | correponding entry in the logical array SELECT to | c | .TRUE.. | c | | c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | c | converged Ritz value that does not appear at the top of | c | the diagonal matrix computed by _seigt in _saup2. | c | Reordering is needed. | c %----------------------------------------------------------% c reord = .false. ktrord = 0 do 10 j = 0, ncv-1 select(j+1) = .false. if (which .eq. 'LM') then if (abs(workl(irz+j)) .ge. abs(thres1)) then tempbnd = max( eps23, abs(workl(irz+j)) ) if (workl(ibd+j) .le. tol*tempbnd) then select(j+1) = .true. end if end if else if (which .eq. 'SM') then if (abs(workl(irz+j)) .le. abs(thres1)) then tempbnd = max( eps23, abs(workl(irz+j)) ) if (workl(ibd+j) .le. tol*tempbnd) then select(j+1) = .true. end if end if else if (which .eq. 'LA') then if (workl(irz+j) .ge. thres1) then tempbnd = max( eps23, abs(workl(irz+j)) ) if (workl(ibd+j) .le. tol*tempbnd) then select(j+1) = .true. end if end if else if (which .eq. 'SA') then if (workl(irz+j) .le. thres1) then tempbnd = max( eps23, abs(workl(irz+j)) ) if (workl(ibd+j) .le. tol*tempbnd) then select(j+1) = .true. end if end if else if (which .eq. 'BE') then if ( workl(irz+j) .le. thres1 .or. & workl(irz+j) .ge. thres2 ) then tempbnd = max( eps23, abs(workl(irz+j)) ) if (workl(ibd+j) .le. tol*tempbnd) then select(j+1) = .true. end if end if end if if (j+1 .gt. nconv ) reord = select(j+1) .or. reord if (select(j+1)) ktrord = ktrord + 1 10 continue c %-------------------------------------------% c | If KTRORD .ne. NCONV, something is wrong. | c %-------------------------------------------% c if (msglvl .gt. 2) then call igraphivout(logfil, 1, ktrord, ndigit, & '_seupd: Number of specified eigenvalues') call igraphivout(logfil, 1, nconv, ndigit, & '_seupd: Number of "converged" eigenvalues') end if c c %-----------------------------------------------------------% c | Call LAPACK routine _steqr to compute the eigenvalues and | c | eigenvectors of the final symmetric tridiagonal matrix H. | c | Initialize the eigenvector matrix Q to the identity. | c %-----------------------------------------------------------% c call dcopy (ncv-1, workl(ih+1), 1, workl(ihb), 1) call dcopy (ncv, workl(ih+ldh), 1, workl(ihd), 1) c call dsteqr ('Identity', ncv, workl(ihd), workl(ihb), & workl(iq), ldq, workl(iw), ierr) c if (ierr .ne. 0) then info = -8 go to 9000 end if c if (msglvl .gt. 1) then call dcopy (ncv, workl(iq+ncv-1), ldq, workl(iw), 1) call igraphdvout (logfil, ncv, workl(ihd), ndigit, & '_seupd: NCV Ritz values of the final H matrix') call igraphdvout (logfil, ncv, workl(iw), ndigit, & '_seupd: last row of the eigenvector matrix for H') end if c if (reord) then c c %---------------------------------------------% c | Reordered the eigenvalues and eigenvectors | c | computed by _steqr so that the "converged" | c | eigenvalues appear in the first NCONV | c | positions of workl(ihd), and the associated | c | eigenvectors appear in the first NCONV | c | columns. | c %---------------------------------------------% c leftptr = 1 rghtptr = ncv c if (ncv .eq. 1) go to 30 c 20 if (select(leftptr)) then c c %-------------------------------------------% c | Search, from the left, for the first Ritz | c | value that has not converged. | c %-------------------------------------------% c leftptr = leftptr + 1 c else if ( .not. select(rghtptr)) then c c %----------------------------------------------% c | Search, from the right, the first Ritz value | c | that has converged. | c %----------------------------------------------% c rghtptr = rghtptr - 1 c else c c %----------------------------------------------% c | Swap the Ritz value on the left that has not | c | converged with the Ritz value on the right | c | that has converged. Swap the associated | c | eigenvector of the tridiagonal matrix H as | c | well. | c %----------------------------------------------% c temp = workl(ihd+leftptr-1) workl(ihd+leftptr-1) = workl(ihd+rghtptr-1) workl(ihd+rghtptr-1) = temp call dcopy(ncv, workl(iq+ncv*(leftptr-1)), 1, & workl(iw), 1) call dcopy(ncv, workl(iq+ncv*(rghtptr-1)), 1, & workl(iq+ncv*(leftptr-1)), 1) call dcopy(ncv, workl(iw), 1, & workl(iq+ncv*(rghtptr-1)), 1) leftptr = leftptr + 1 rghtptr = rghtptr - 1 c end if c if (leftptr .lt. rghtptr) go to 20 c 30 end if c if (msglvl .gt. 2) then call igraphdvout (logfil, ncv, workl(ihd), ndigit, & '_seupd: The eigenvalues of H--reordered') end if c c %----------------------------------------% c | Load the converged Ritz values into D. | c %----------------------------------------% c call dcopy(nconv, workl(ihd), 1, d, 1) c else c c %-----------------------------------------------------% c | Ritz vectors not required. Load Ritz values into D. | c %-----------------------------------------------------% c call dcopy (nconv, workl(ritz), 1, d, 1) call dcopy (ncv, workl(ritz), 1, workl(ihd), 1) c end if c c %------------------------------------------------------------------% c | Transform the Ritz values and possibly vectors and corresponding | c | Ritz estimates of OP to those of A*x=lambda*B*x. The Ritz values | c | (and corresponding data) are returned in ascending order. | c %------------------------------------------------------------------% c if (type .eq. 'REGULR') then c c %---------------------------------------------------------% c | Ascending sort of wanted Ritz values, vectors and error | c | bounds. Not necessary if only Ritz values are desired. | c %---------------------------------------------------------% c if (rvec) then call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), & ldq) else call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) end if c else c c %-------------------------------------------------------------% c | * Make a copy of all the Ritz values. | c | * Transform the Ritz values back to the original system. | c | For TYPE = 'SHIFTI' the transformation is | c | lambda = 1/theta + sigma | c | For TYPE = 'BUCKLE' the transformation is | c | lambda = sigma * theta / ( theta - 1 ) | c | For TYPE = 'CAYLEY' the transformation is | c | lambda = sigma * (theta + 1) / (theta - 1 ) | c | where the theta are the Ritz values returned by igraphdsaupd. | c | NOTES: | c | *The Ritz vectors are not affected by the transformation. | c | They are only reordered. | c %-------------------------------------------------------------% c call dcopy (ncv, workl(ihd), 1, workl(iw), 1) if (type .eq. 'SHIFTI') then do 40 k=1, ncv workl(ihd+k-1) = one / workl(ihd+k-1) + sigma 40 continue else if (type .eq. 'BUCKLE') then do 50 k=1, ncv workl(ihd+k-1) = sigma * workl(ihd+k-1) / & (workl(ihd+k-1) - one) 50 continue else if (type .eq. 'CAYLEY') then do 60 k=1, ncv workl(ihd+k-1) = sigma * (workl(ihd+k-1) + one) / & (workl(ihd+k-1) - one) 60 continue end if c c %-------------------------------------------------------------% c | * Store the wanted NCONV lambda values into D. | c | * Sort the NCONV wanted lambda in WORKL(IHD:IHD+NCONV-1) | c | into ascending order and apply sort to the NCONV theta | c | values in the transformed system. We'll need this to | c | compute Ritz estimates in the original system. | c | * Finally sort the lambda's into ascending order and apply | c | to Ritz vectors if wanted. Else just sort lambda's into | c | ascending order. | c | NOTES: | c | *workl(iw:iw+ncv-1) contain the theta ordered so that they | c | match the ordering of the lambda. We'll use them again for | c | Ritz vector purification. | c %-------------------------------------------------------------% c call dcopy (nconv, workl(ihd), 1, d, 1) call igraphdsortr ('LA', .true., nconv, workl(ihd), workl(iw)) if (rvec) then call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), & ldq) else call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) call dscal (ncv, bnorm2/rnorm, workl(ihb), 1) call igraphdsortr ('LA', .true., nconv, d, workl(ihb)) end if c end if c c %------------------------------------------------% c | Compute the Ritz vectors. Transform the wanted | c | eigenvectors of the symmetric tridiagonal H by | c | the Lanczos basis matrix V. | c %------------------------------------------------% c if (rvec .and. howmny .eq. 'A') then c c %----------------------------------------------------------% c | Compute the QR factorization of the matrix representing | c | the wanted invariant subspace located in the first NCONV | c | columns of workl(iq,ldq). | c %----------------------------------------------------------% c call dgeqr2 (ncv, nconv, workl(iq), ldq, workl(iw+ncv), & workl(ihb), ierr) c c c %--------------------------------------------------------% c | * Postmultiply V by Q. | c | * Copy the first NCONV columns of VQ into Z. | c | The N by NCONV matrix Z is now a matrix representation | c | of the approximate invariant subspace associated with | c | the Ritz values in workl(ihd). | c %--------------------------------------------------------% c call dorm2r ('Right', 'Notranspose', n, ncv, nconv, workl(iq), & ldq, workl(iw+ncv), v, ldv, workd(n+1), ierr) call dlacpy ('All', n, nconv, v, ldv, z, ldz) c c %-----------------------------------------------------% c | In order to compute the Ritz estimates for the Ritz | c | values in both systems, need the last row of the | c | eigenvector matrix. Remember, it's in factored form | c %-----------------------------------------------------% c do 65 j = 1, ncv-1 workl(ihb+j-1) = zero 65 continue workl(ihb+ncv-1) = one call dorm2r ('Left', 'Transpose', ncv, 1, nconv, workl(iq), & ldq, workl(iw+ncv), workl(ihb), ncv, temp, ierr) c else if (rvec .and. howmny .eq. 'S') then c c Not yet implemented. See remark 2 above. c end if c if (type .eq. 'REGULR' .and. rvec) then c do 70 j=1, ncv workl(ihb+j-1) = rnorm * abs( workl(ihb+j-1) ) 70 continue c else if (type .ne. 'REGULR' .and. rvec) then c c %-------------------------------------------------% c | * Determine Ritz estimates of the theta. | c | If RVEC = .true. then compute Ritz estimates | c | of the theta. | c | If RVEC = .false. then copy Ritz estimates | c | as computed by igraphdsaupd. | c | * Determine Ritz estimates of the lambda. | c %-------------------------------------------------% c call dscal (ncv, bnorm2, workl(ihb), 1) if (type .eq. 'SHIFTI') then c do 80 k=1, ncv workl(ihb+k-1) = abs( workl(ihb+k-1) ) / workl(iw+k-1)**2 80 continue c else if (type .eq. 'BUCKLE') then c do 90 k=1, ncv workl(ihb+k-1) = sigma * abs( workl(ihb+k-1) ) / & ( workl(iw+k-1)-one )**2 90 continue c else if (type .eq. 'CAYLEY') then c do 100 k=1, ncv workl(ihb+k-1) = abs( workl(ihb+k-1) / & workl(iw+k-1)*(workl(iw+k-1)-one) ) 100 continue c end if c end if c if (type .ne. 'REGULR' .and. msglvl .gt. 1) then call igraphdvout (logfil, nconv, d, ndigit, & '_seupd: Untransformed converged Ritz values') call igraphdvout (logfil, nconv, workl(ihb), ndigit, & '_seupd: Ritz estimates of the untransformed Ritz values') else if (msglvl .gt. 1) then call igraphdvout (logfil, nconv, d, ndigit, & '_seupd: Converged Ritz values') call igraphdvout (logfil, nconv, workl(ihb), ndigit, & '_seupd: Associated Ritz estimates') end if c c %-------------------------------------------------% c | Ritz vector purification step. Formally perform | c | one of inverse subspace iteration. Only used | c | for MODE = 3,4,5. See reference 7 | c %-------------------------------------------------% c if (rvec .and. (type .eq. 'SHIFTI' .or. type .eq. 'CAYLEY')) then c do 110 k=0, nconv-1 workl(iw+k) = workl(iq+k*ldq+ncv-1) / workl(iw+k) 110 continue c else if (rvec .and. type .eq. 'BUCKLE') then c do 120 k=0, nconv-1 workl(iw+k) = workl(iq+k*ldq+ncv-1) / (workl(iw+k)-one) 120 continue c end if c if (type .ne. 'REGULR') & call dger (n, nconv, one, resid, 1, workl(iw), 1, z, ldz) c 9000 continue c return c c %---------------% c | End of igraphdseupd | c %---------------% c end igraph/src/cs_reach.c0000644000176000001440000000306712325527073014272 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* xi [top...n-1] = nodes reachable from graph of G*P' via nodes in B(:,k). * xi [n...2n-1] used as workspace */ CS_INT cs_reach (cs *G, const cs *B, CS_INT k, CS_INT *xi, const CS_INT *pinv) { CS_INT p, n, top, *Bp, *Bi, *Gp ; if (!CS_CSC (G) || !CS_CSC (B) || !xi) return (-1) ; /* check inputs */ n = G->n ; Bp = B->p ; Bi = B->i ; Gp = G->p ; top = n ; for (p = Bp [k] ; p < Bp [k+1] ; p++) { if (!CS_MARKED (Gp, Bi [p])) /* start a dfs at unmarked node i */ { top = cs_dfs (Bi [p], G, top, xi, xi+n, pinv) ; } } for (p = top ; p < n ; p++) CS_MARK (Gp, xi [p]) ; /* restore G */ return (top) ; } igraph/src/iterators.c0000644000176000001440000015536312325527073014546 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_iterators.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_interface.h" #include "config.h" #include #include /** * \section about_iterators About selectors, iterators * * Everything about vertices and vertex selectors also applies * to edges and edge selectors unless explicitly noted otherwise. * * The vertex (and edge) selector notion was introduced in igraph 0.2. * It is a way to reference a sequence of vertices or edges * independently of the graph. * * While this might sound quite mysterious, it is actually very * simple. For example, all vertices of a graph can be selected by * \ref igraph_vs_all() and the graph independence means that * \ref igraph_vs_all() is not parametrized by a graph object. That is, * \ref igraph_vs_all() is the general \em concept of selecting all vertices * of a graph. A vertex selector is then a way to specify the class of vertices * to be visited. The selector might specify that all vertices of a graph or * all the neighbours of a vertex are to be visited. A vertex selector is a * way of saying that you want to visit a bunch of vertices, as opposed to a * vertex iterator which is a concrete plan for visiting each of the * chosen vertices of a specific graph. * * To determine the actual vertex IDs implied by a vertex selector, you * need to apply the concept of selecting vertices to a specific graph object. * This can be accomplished by instantiating a vertex iterator using a * specific vertex selection concept and a specific graph object. The notion * of vertex iterators can be thought of in the following way. Given a * specific graph object and the class of vertices to be visited, a vertex * iterator is a road map, plan or route for how to visit the chosen * vertices. * * Some vertex selectors have \em immediate versions. These have the * prefix \c igraph_vss instead of \c igraph_vs, e.g. \ref igraph_vss_all() * instead of \ref igraph_vs_all(). The immediate versions are to be used in * the parameter list of the igraph functions, such as \ref igraph_degree(). * These functions are not associated with any \type igraph_vs_t object, so * they have no separate constructors and destructors * (destroy functions). */ /** * \section about_vertex_selectors * * Vertex selectors are created by vertex selector constructors, * can be instantiated with \ref igraph_vit_create(), and are * destroyed with \ref igraph_vs_destroy(). */ /** * \function igraph_vs_all * \brief Vertex set, all vertices of a graph. * * \param vs Pointer to an uninitialized \type igraph_vs_t object. * \return Error code. * \sa \ref igraph_vss_all(), \ref igraph_vs_destroy() * * This selector includes all vertices of a given graph in * increasing vertex id order. * * * Time complexity: O(1). */ int igraph_vs_all(igraph_vs_t *vs) { vs->type=IGRAPH_VS_ALL; return 0; } /** * \function igraph_vss_all * \brief All vertices of a graph (immediate version). * * Immediate vertex selector for all vertices in a graph. It can * be used conveniently when some vertex property (eg. betweenness, * degree, etc.) should be calculated for all vertices. * * \return A vertex selector for all vertices in a graph. * \sa \ref igraph_vs_all() * * Time complexity: O(1). */ igraph_vs_t igraph_vss_all(void) { igraph_vs_t allvs; allvs.type=IGRAPH_VS_ALL; return allvs; } /** * \function igraph_vs_adj * \brief Adjacent vertices of a vertex. * * All neighboring vertices of a given vertex are selected by this * selector. The \c mode argument controls the type of the neighboring * vertices to be selected. The vertices are visited in increasing vertex * ID order, as of igraph version 0.4. * * \param vs Pointer to an uninitialized vertex selector object. * \param vid Vertex ID, the center of the neighborhood. * \param mode Decides the type of the neighborhood for directed * graphs. This parameter is ignored for undirected graphs. * Possible values: * \clist * \cli IGRAPH_OUT * All vertices to which there is a directed edge from \c vid. That * is, all the out-neighbors of \c vid. * \cli IGRAPH_IN * All vertices from which there is a directed edge to \c vid. In * other words, all the in-neighbors of \c vid. * \cli IGRAPH_ALL * All vertices to which or from which there is a directed edge * from/to \c vid. That is, all the neighbors of \c vid considered * as if the graph is undirected. * \endclist * \return Error code. * \sa \ref igraph_vs_destroy() * * Time complexity: O(1). */ int igraph_vs_adj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode) { vs->type=IGRAPH_VS_ADJ; vs->data.adj.vid=vid; vs->data.adj.mode=mode; return 0; } /** * \function igraph_vs_nonadj * \brief Non-adjacent vertices of a vertex. * * All non-neighboring vertices of a given vertex. The \p mode * argument controls the type of neighboring vertices \em not to * select. Instead of selecting immediate neighbors of \c vid as is done by * \ref igraph_vs_adj(), the current function selects vertices that are \em not * immediate neighbors of \c vid. * * \param vs Pointer to an uninitialized vertex selector object. * \param vid Vertex ID, the \quote center \endquote of the * non-neighborhood. * \param mode The type of neighborhood not to select in directed * graphs. Possible values: * \clist * \cli IGRAPH_OUT * All vertices will be selected except those to which there is a * directed edge from \c vid. That is, we select all vertices * excluding the out-neighbors of \c vid. * \cli IGRAPH_IN * All vertices will be selected except those from which there is a * directed edge to \c vid. In other words, we select all vertices * but the in-neighbors of \c vid. * \cli IGRAPH_ALL * All vertices will be selected except those from or to which there * is a directed edge to or from \c vid. That is, we select all * vertices of \c vid except for its immediate neighbors. * \endclist * \return Error code. * \sa \ref igraph_vs_destroy() * * Time complexity: O(1). * * \example examples/simple/igraph_vs_nonadj.c */ int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode) { vs->type=IGRAPH_VS_NONADJ; vs->data.adj.vid=vid; vs->data.adj.mode=mode; return 0; } /** * \function igraph_vs_none * \brief Empty vertex set. * * Creates an empty vertex selector. * * \param vs Pointer to an uninitialized vertex selector object. * \return Error code. * \sa \ref igraph_vss_none(), \ref igraph_vs_destroy() * * Time complexity: O(1). */ int igraph_vs_none(igraph_vs_t *vs) { vs->type=IGRAPH_VS_NONE; return 0; } /** * \function igraph_vss_none * \brief Empty vertex set (immediate version). * * The immediate version of the empty vertex selector. * * \return An empty vertex selector. * \sa \ref igraph_vs_none() * * Time complexity: O(1). */ igraph_vs_t igraph_vss_none(void) { igraph_vs_t nonevs; nonevs.type=IGRAPH_VS_NONE; return nonevs; } /** * \function igraph_vs_1 * \brief Vertex set with a single vertex. * * This vertex selector selects a single vertex. * * \param vs Pointer to an uninitialized vertex selector object. * \param vid The vertex id to be selected. * \return Error Code. * \sa \ref igraph_vss_1(), \ref igraph_vs_destroy() * * Time complexity: O(1). */ int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) { vs->type=IGRAPH_VS_1; vs->data.vid=vid; return 0; } /** * \function igraph_vss_1 * \brief Vertex set with a single vertex (immediate version). * * The immediate version of the single-vertex selector. * * \param vid The vertex to be selected. * \return A vertex selector containing a single vertex. * \sa \ref igraph_vs_1() * * Time complexity: O(1). */ igraph_vs_t igraph_vss_1(igraph_integer_t vid) { igraph_vs_t onevs; onevs.type=IGRAPH_VS_1; onevs.data.vid=vid; return onevs; } /** * \function igraph_vs_vector * \brief Vertex set based on a vector. * * This function makes it possible to handle a \type vector_t * temporarily as a vertex selector. The vertex selector should be * thought of like a \em view to the vector. If you make changes to * the vector that also affects the vertex selector. Destroying the * vertex selector does not destroy the vector. (Of course.) Do not * destroy the vector before destroying the vertex selector, or you * might get strange behavior. * * \param vs Pointer to an uninitialized vertex selector. * \param v Pointer to a \type igraph_vector_t object. * \return Error code. * \sa \ref igraph_vss_vector(), \ref igraph_vs_destroy() * * Time complexity: O(1). * * \example examples/simple/igraph_vs_vector.c */ int igraph_vs_vector(igraph_vs_t *vs, const igraph_vector_t *v) { vs->type=IGRAPH_VS_VECTORPTR; vs->data.vecptr=v; return 0; } /** * \function igraph_vss_vector * \brief Vertex set based on a vector (immediate version). * * This is the immediate version of \ref igraph_vs_vector. * * \param v Pointer to a \type igraph_vector_t object. * \return A vertex selector object containing the vertices in the * vector. * \sa \ref igraph_vs_vector() * * Time complexity: O(1). */ igraph_vs_t igraph_vss_vector(const igraph_vector_t *v) { igraph_vs_t vecvs; vecvs.type=IGRAPH_VS_VECTORPTR; vecvs.data.vecptr=v; return vecvs; } /** * \function igraph_vs_vector_small * \brief Create a vertex set by giving its elements. * * This function can be used to create a vertex selector with a couple * of vertices. Do not forget to include a -1 after the * last vertex id. The behavior of the function is undefined if you * don't use a -1 properly. * * * Note that the vertex ids supplied will be parsed as * int's so you cannot supply arbitrarily large (too * large for int) vertex ids here. * * \param vs Pointer to an uninitialized vertex selector object. * \param ... Additional parameters, these will be the vertex ids to * be included in the vertex selector. Supply a -1 * after the last vertex id. * \return Error code. * \sa \ref igraph_vs_destroy() * * Time complexity: O(n), the number of vertex ids supplied. */ int igraph_vs_vector_small(igraph_vs_t *vs, ...) { va_list ap; long int i, n=0; vs->type=IGRAPH_VS_VECTOR; vs->data.vecptr=igraph_Calloc(1, igraph_vector_t); if (vs->data.vecptr==0) { IGRAPH_ERROR("Cannot create vertex selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)vs->data.vecptr); va_start(ap, vs); while (1) { int num = va_arg(ap, int); if (num == -1) { break; } n++; } va_end(ap); IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vs->data.vecptr, n); va_start(ap, vs); for (i=0; idata.vecptr)[i]=(igraph_real_t) va_arg(ap, int); } va_end(ap); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_vs_vector_copy * \brief Vertex set based on a vector, with copying. * * This function makes it possible to handle a \type vector_t * permanently as a vertex selector. The vertex selector creates a * copy of the original vector, so the vector can safely be destroyed * after creating the vertex selector. Changing the original vector * will not affect the vertex selector. The vertex selector is * responsible for deleting the copy made by itself. * * \param vs Pointer to an uninitialized vertex selector. * \param v Pointer to a \type igraph_vector_t object. * \return Error code. * \sa \ref igraph_vs_destroy() * * Time complexity: O(1). */ int igraph_vs_vector_copy(igraph_vs_t *vs, const igraph_vector_t *v) { vs->type=IGRAPH_VS_VECTOR; vs->data.vecptr=igraph_Calloc(1, igraph_vector_t); if (vs->data.vecptr==0) { IGRAPH_ERROR("Cannot create vertex selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)vs->data.vecptr); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)vs->data.vecptr, v)); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_vs_seq * \brief Vertex set, an interval of vertices. * * Creates a vertex selector containing all vertices with vertex id * equal to or bigger than \c from and equal to or smaller than \c * to. * * \param vs Pointer to an uninitialized vertex selector object. * \param from The first vertex id to be included in the vertex * selector. * \param to The last vertex id to be included in the vertex * selector. * \return Error code. * \sa \ref igraph_vss_seq(), \ref igraph_vs_destroy() * * Time complexity: O(1). * * \example examples/simple/igraph_vs_seq.c */ int igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to) { vs->type=IGRAPH_VS_SEQ; vs->data.seq.from=from; vs->data.seq.to=to+1; return 0; } /** * \function igraph_vss_seq * \brief An interval of vertices (immediate version). * * The immediate version of \ref igraph_vs_seq(). * * \param from The first vertex id to be included in the vertex * selector. * \param to The last vertex id to be included in the vertex * selector. * \return Error code. * \sa \ref igraph_vs_seq() * * Time complexity: O(1). */ igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to) { igraph_vs_t vs; vs.type=IGRAPH_VS_SEQ; vs.data.seq.from=from; vs.data.seq.to=to+1; return vs; } /** * \function igraph_vs_destroy * \brief Destroy a vertex set. * * This function should be called for all vertex selectors when they * are not needed. The memory allocated for the vertex selector will * be deallocated. Do not call this function on vertex selectors * created with the immediate versions of the vertex selector * constructors (starting with igraph_vss). * * \param vs Pointer to a vertex selector object. * * Time complexity: operating system dependent, usually O(1). */ void igraph_vs_destroy(igraph_vs_t *vs) { switch (vs->type) { case IGRAPH_VS_ALL: case IGRAPH_VS_ADJ: case IGRAPH_VS_NONE: case IGRAPH_VS_1: case IGRAPH_VS_VECTORPTR: case IGRAPH_VS_SEQ: case IGRAPH_VS_NONADJ: break; case IGRAPH_VS_VECTOR: igraph_vector_destroy((igraph_vector_t*)vs->data.vecptr); igraph_Free(vs->data.vecptr); break; default: break; } } /** * \function igraph_vs_is_all * \brief Check whether all vertices are included. * * This function checks whether the vertex selector object was created * by \ref igraph_vs_all() or \ref igraph_vss_all(). Note that the * vertex selector might contain all vertices in a given graph but if * it wasn't created by the two constructors mentioned here the return * value will be FALSE. * * \param vs Pointer to a vertex selector object. * \return TRUE (1) if the vertex selector contains all vertices and * FALSE (0) otherwise. * * Time complexity: O(1). */ igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs) { return vs->type == IGRAPH_VS_ALL; } int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, igraph_vector_t *v) { igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vs, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); IGRAPH_CHECK(igraph_vit_as_vector(&vit, v)); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_vs_copy * \brief Creates a copy of a vertex selector. * \param src The selector being copied. * \param dest An uninitialized selector that will contain the copy. */ int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src) { memcpy(dest, src, sizeof(igraph_vs_t)); switch (dest->type) { case IGRAPH_VS_VECTOR: dest->data.vecptr = igraph_Calloc(1,igraph_vector_t); if (!dest->data.vecptr) IGRAPH_ERROR("Cannot copy vertex selector", IGRAPH_ENOMEM); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.vecptr, (igraph_vector_t*)src->data.vecptr)); break; } return 0; } /** * \function igraph_vs_type * \brief Returns the type of the vertex selector. */ int igraph_vs_type(const igraph_vs_t *vs) { return vs->type; } /** * \function igraph_vs_size * \brief Returns the size of the vertex selector. * * The size of the vertex selector is the number of vertices it will * yield when it is iterated over. * * \param graph The graph over which we will iterate. * \param result The result will be returned here. */ int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, igraph_integer_t *result) { igraph_vector_t vec; igraph_bool_t *seen; long i; switch (vs->type) { case IGRAPH_VS_NONE: *result = 0; return 0; case IGRAPH_VS_1: *result = 0; if (vs->data.vid < igraph_vcount(graph) && vs->data.vid >= 0) *result=1; return 0; case IGRAPH_VS_SEQ: *result = vs->data.seq.to - vs->data.seq.from; return 0; case IGRAPH_VS_ALL: *result = igraph_vcount(graph); return 0; case IGRAPH_VS_ADJ: IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); IGRAPH_CHECK(igraph_neighbors(graph,&vec,vs->data.adj.vid,vs->data.adj.mode)); *result=(igraph_integer_t) igraph_vector_size(&vec); igraph_vector_destroy(&vec); IGRAPH_FINALLY_CLEAN(1); return 0; case IGRAPH_VS_NONADJ: IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); IGRAPH_CHECK(igraph_neighbors(graph,&vec,vs->data.adj.vid,vs->data.adj.mode)); *result=igraph_vcount(graph); seen=igraph_Calloc(*result, igraph_bool_t); if (seen==0) { IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, seen); for (i=0; idata.vecptr); return 0; } IGRAPH_ERROR("Cannot calculate selector length, invalid selector type", IGRAPH_EINVAL); } /***************************************************/ /** * \function igraph_vit_create * \brief Creates a vertex iterator from a vertex selector. * * This function instantiates a vertex selector object with a given * graph. This is the step when the actual vertex ids are created from * the \em logical notion of the vertex selector based on the graph. * Eg. a vertex selector created with \ref igraph_vs_all() contains * knowledge that \em all vertices are included in a (yet indefinite) * graph. When instantiating it a vertex iterator object is created, * this contains the actual vertex ids in the graph supplied as a * parameter. * * * The same vertex selector object can be used to instantiate any * number vertex iterators. * * \param graph An \type igraph_t object, a graph. * \param vs A vertex selector object. * \param vit Pointer to an uninitialized vertex iterator object. * \return Error code. * \sa \ref igraph_vit_destroy(). * * Time complexity: it depends on the vertex selector type. O(1) for * vertex selectors created with \ref igraph_vs_all(), \ref * igraph_vs_none(), \ref igraph_vs_1, \ref igraph_vs_vector, \ref * igraph_vs_seq(), \ref igraph_vs_vector(), \ref * igraph_vs_vector_small(). O(d) for \ref igraph_vs_adj(), d is the * number of vertex ids to be included in the iterator. O(|V|) for * \ref igraph_vs_nonadj(), |V| is the number of vertices in the graph. */ int igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_vit_t *vit) { igraph_vector_t vec; igraph_bool_t *seen; long int i, j, n; switch (vs.type) { case IGRAPH_VS_ALL: vit->type=IGRAPH_VIT_SEQ; vit->pos=0; vit->start=0; vit->end=igraph_vcount(graph); break; case IGRAPH_VS_ADJ: vit->type=IGRAPH_VIT_VECTOR; vit->pos=0; vit->start=0; vit->vec=igraph_Calloc(1, igraph_vector_t); if (vit->vec == 0) { IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) vit->vec); IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vit->vec, 0); IGRAPH_CHECK(igraph_neighbors(graph, (igraph_vector_t*)vit->vec, vs.data.adj.vid, vs.data.adj.mode)); vit->end=igraph_vector_size(vit->vec); IGRAPH_FINALLY_CLEAN(2); break; case IGRAPH_VS_NONADJ: vit->type=IGRAPH_VIT_VECTOR; vit->pos=0; vit->start=0; vit->vec=igraph_Calloc(1, igraph_vector_t); if (vit->vec == 0) { IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) vit->vec); IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *) vit->vec, 0); IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs.data.adj.vid, vs.data.adj.mode)); n=igraph_vcount(graph); seen=igraph_Calloc(n, igraph_bool_t); if (seen==0) { IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, seen); for (i=0; ivec, n)); for (i=0, j=0; jvec)[j++] = i; } } igraph_Free(seen); igraph_vector_destroy(&vec); vit->end=n; IGRAPH_FINALLY_CLEAN(4); break; case IGRAPH_VS_NONE: vit->type=IGRAPH_VIT_SEQ; vit->pos=0; vit->start=0; vit->end=0; break; case IGRAPH_VS_1: vit->type=IGRAPH_VIT_SEQ; vit->pos=vs.data.vid; vit->start=vs.data.vid; vit->end=vs.data.vid+1; if (vit->pos >= igraph_vcount(graph)) { IGRAPH_ERROR("Cannot create iterator, invalid vertex id",IGRAPH_EINVVID); } break; case IGRAPH_VS_VECTORPTR: case IGRAPH_VS_VECTOR: vit->type=IGRAPH_VIT_VECTORPTR; vit->pos=0; vit->start=0; vit->vec=vs.data.vecptr; vit->end=igraph_vector_size(vit->vec); if (!igraph_vector_isininterval(vit->vec, 0, igraph_vcount(graph)-1)) { IGRAPH_ERROR("Cannot create iterator, invalid vertex id",IGRAPH_EINVVID); } break; case IGRAPH_VS_SEQ: vit->type=IGRAPH_VIT_SEQ; vit->pos=vs.data.seq.from; vit->start=vs.data.seq.from; vit->end=vs.data.seq.to; break; default: IGRAPH_ERROR("Cannot create iterator, invalid selector", IGRAPH_EINVAL); break; } return 0; } /** * \function igraph_vit_destroy * \brief Destroys a vertex iterator. * * * Deallocates memory allocated for a vertex iterator. * * \param vit Pointer to an initialized vertex iterator object. * \sa \ref igraph_vit_create() * * Time complexity: operating system dependent, usually O(1). */ void igraph_vit_destroy(const igraph_vit_t *vit) { switch (vit->type) { case IGRAPH_VIT_SEQ: case IGRAPH_VIT_VECTORPTR: break; case IGRAPH_VIT_VECTOR: igraph_vector_destroy((igraph_vector_t*)vit->vec); igraph_free((igraph_vector_t*)vit->vec); break; default: /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ break; } } int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) { long int i; IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_VIT_SIZE(*vit))); switch (vit->type) { case IGRAPH_VIT_SEQ: for (i=0; istart+i; } break; case IGRAPH_VIT_VECTOR: case IGRAPH_VIT_VECTORPTR: for (i=0; ivec)[i]; } break; default: IGRAPH_ERROR("Cannot convert to vector, unknown iterator type", IGRAPH_EINVAL); break; } return 0; } /*******************************************************/ /** * \function igraph_es_all * \brief Edge set, all edges. * * \param es Pointer to an uninitialized edge selector object. * \param order Constant giving the order in which the edges will be * included in the selector. Possible values: * \c IGRAPH_EDGEORDER_ID, edge id order. * \c IGRAPH_EDGEORDER_FROM, vertex id order, the id of the * \em source vertex counts for directed graphs. The order * of the incident edges of a given vertex is arbitrary. * \c IGRAPH_EDGEORDER_TO, vertex id order, the id of the \em * target vertex counts for directed graphs. The order * of the incident edges of a given vertex is arbitrary. * For undirected graph the latter two is the same. * \return Error code. * \sa \ref igraph_ess_all(), \ref igraph_es_destroy() * * Time complexity: O(1). */ int igraph_es_all(igraph_es_t *es, igraph_edgeorder_type_t order) { switch (order) { case IGRAPH_EDGEORDER_ID: es->type=IGRAPH_ES_ALL; break; case IGRAPH_EDGEORDER_FROM: es->type=IGRAPH_ES_ALLFROM; break; case IGRAPH_EDGEORDER_TO: es->type=IGRAPH_ES_ALLTO; break; default: IGRAPH_ERROR("Invalid edge order, cannot create selector", IGRAPH_EINVAL); break; } return 0; } /** * \function igraph_ess_all * \brief Edge set, all edges (immediate version) * * The immediate version of the all-vertices selector. * * \param order Constant giving the order of the edges in the edge * selector. See \ref igraph_es_all() for the possible values. * \return The edge selector. * \sa \ref igraph_es_all() * * Time complexity: O(1). */ igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order) { igraph_es_t es; igraph_es_all(&es, order); /* cannot fail */ return es; } /** * \function igraph_es_adj * \brief Adjacent edges of a vertex. * * This function was superseded by \ref igraph_es_incident() in igraph 0.6. * Please use \ref igraph_es_incident() instead of this function. * * * Deprecated in version 0.6. */ int igraph_es_adj(igraph_es_t *es, igraph_integer_t vid, igraph_neimode_t mode) { IGRAPH_WARNING("igraph_es_adj is deprecated, use igraph_es_incident"); return igraph_es_incident(es, vid, mode); } /** * \function igraph_es_incident * \brief Edges incident on a given vertex. * * \param es Pointer to an uninitialized edge selector object. * \param vid Vertex id, of which the incident edges will be * selected. * \param mode Constant giving the type of the incident edges to * select. This is ignored for undirected graphs. Possible values: * \c IGRAPH_OUT, outgoing edges; * \c IGRAPH_IN, incoming edges; * \c IGRAPH_ALL, all edges. * \return Error code. * \sa \ref igraph_es_destroy() * * Time complexity: O(1). * * \example examples/simple/igraph_es_adj.c */ int igraph_es_incident(igraph_es_t *es, igraph_integer_t vid, igraph_neimode_t mode) { es->type=IGRAPH_ES_INCIDENT; es->data.incident.vid=vid; es->data.incident.mode=mode; return 0; } /** * \function igraph_es_none * \brief Empty edge selector. * * \param es Pointer to an uninitialized edge selector object to * initialize. * \return Error code. * \sa \ref igraph_ess_none(), \ref igraph_es_destroy() * * Time complexity: O(1). */ int igraph_es_none(igraph_es_t *es) { es->type=IGRAPH_ES_NONE; return 0; } /** * \function igraph_ess_none * \brief Immediate empty edge selector. * * * Immediate version of the empty edge selector. * * \return Initialized empty edge selector. * \sa \ref igraph_es_none() * * Time complexity: O(1). */ igraph_es_t igraph_ess_none(void) { igraph_es_t es; es.type=IGRAPH_ES_NONE; return es; } /** * \function igraph_es_1 * \brief Edge selector containing a single edge. * * \param es Pointer to an uninitialized edge selector object. * \param eid Edge id of the edge to select. * \return Error code. * \sa \ref igraph_ess_1(), \ref igraph_es_destroy() * * Time complexity: O(1). */ int igraph_es_1(igraph_es_t *es, igraph_integer_t eid) { es->type=IGRAPH_ES_1; es->data.eid=eid; return 0; } /** * \function igraph_ess_1 * \brief Immediate version of the single edge edge selector. * * \param eid The id of the edge. * \return The edge selector. * \sa \ref igraph_es_1() * * Time complexity: O(1). */ igraph_es_t igraph_ess_1(igraph_integer_t eid) { igraph_es_t es; es.type=IGRAPH_ES_1; es.data.eid=eid; return es; } /** * \function igraph_es_vector * \brief Handle a vector as an edge selector. * * * Creates an edge selector which serves as a view to a vector * containing edge ids. Do not destroy the vector before destroying * the view. * * Many views can be created to the same vector. * * \param es Pointer to an uninitialized edge selector. * \param v Vector containing edge ids. * \return Error code. * \sa \ref igraph_ess_vector(), \ref igraph_es_destroy() * * Time complexity: O(1). */ int igraph_es_vector(igraph_es_t *es, const igraph_vector_t *v) { es->type=IGRAPH_ES_VECTORPTR; es->data.vecptr=v; return 0; } /** * \function igraph_es_vector_copy * \brief Edge set, based on a vector, with copying. * * * This function makes it possible to handle a \type vector_t * permanently as an edge selector. The edge selector creates a * copy of the original vector, so the vector can safely be destroyed * after creating the edge selector. Changing the original vector * will not affect the edge selector. The edge selector is * responsible for deleting the copy made by itself. * * \param es Pointer to an uninitialized edge selector. * \param v Pointer to a \type igraph_vector_t object. * \return Error code. * \sa \ref igraph_es_destroy() * * Time complexity: O(1). */ int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v) { es->type=IGRAPH_ES_VECTOR; es->data.vecptr=igraph_Calloc(1, igraph_vector_t); if (es->data.vecptr==0) { IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.vecptr); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)es->data.vecptr, v)); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_ess_vector * \brief Immediate vector view edge selector. * * * This is the immediate version of the vector of edge ids edge * selector. * * \param v The vector of edge ids. * \return Edge selector, initialized. * \sa \ref igraph_es_vector() * * Time complexity: O(1). */ igraph_es_t igraph_ess_vector(const igraph_vector_t *v) { igraph_es_t es; es.type=IGRAPH_ES_VECTORPTR; es.data.vecptr=v; return es; } /** * \function igraph_es_fromto * \brief Edge selector, all edges between two vertex sets. * * * This function is not implemented yet. * * \param es Pointer to an uninitialized edge selector. * \param from Vertex selector, their outgoing edges will be * selected. * \param to Vertex selector, their incoming edges will be selected * from the previous selection. * \return Error code. * \sa \ref igraph_es_destroy() * * Time complexity: O(1). * * \example examples/simple/igraph_es_fromto.c */ int igraph_es_fromto(igraph_es_t *es, igraph_vs_t from, igraph_vs_t to) { IGRAPH_UNUSED(es); IGRAPH_UNUSED(from); IGRAPH_UNUSED(to); IGRAPH_ERROR("igraph_es_fromto not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ return 0; } /** * \function igraph_es_seq * \brief Edge selector, a sequence of edge ids. * * All edge ids between from and to will be * included in the edge selection. * * \param es Pointer to an uninitialized edge selector object. * \param from The first edge id to be included. * \param to The last edge id to be included. * \return Error code. * \sa \ref igraph_ess_seq(), \ref igraph_es_destroy() * * Time complexity: O(1). */ int igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to) { es->type=IGRAPH_ES_SEQ; es->data.seq.from=from; es->data.seq.to=to; return 0; } /** * \function igraph_ess_seq * \brief Immediate version of the sequence edge selector. * * \param from The first edge id to include. * \param to The last edge id to include. * \return The initialized edge selector. * \sa \ref igraph_es_seq() * * Time complexity: O(1). */ igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to) { igraph_es_t es; es.type=IGRAPH_ES_SEQ; es.data.seq.from=from; es.data.seq.to=to; return es; } /** * \function igraph_es_pairs * \brief Edge selector, multiple edges defined by their endpoints in a vector. * * The edges between the given pairs of vertices will be included in the * edge selection. The vertex pairs must be defined in the vector v, * the first element of the vector is the first vertex of the first edge * to be selected, the second element is the second vertex of the first * edge, the third element is the first vertex of the second edge and * so on. * * \param es Pointer to an uninitialized edge selector object. * \param v The vector containing the endpoints of the edges. * \param directed Whether the graph is directed or not. * \return Error code. * \sa \ref igraph_es_pairs_small(), \ref igraph_es_destroy() * * Time complexity: O(n), the number of edges being selected. * * \example examples/simple/igraph_es_pairs.c */ int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed) { es->type=IGRAPH_ES_PAIRS; es->data.path.mode=directed; es->data.path.ptr=igraph_Calloc(1, igraph_vector_t); if (es->data.path.ptr==0) { IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v)); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_es_pairs_small * \brief Edge selector, multiple edges defined by their endpoints as arguments. * * The edges between the given pairs of vertices will be included in the * edge selection. The vertex pairs must be given as the arguments of the * function call, the third argument is the first vertex of the first edge, * the fourth argument is the second vertex of the first edge, the fifth * is the first vertex of the second edge and so on. The last element of the * argument list must be -1 to denote the end of the argument list. * * \param es Pointer to an uninitialized edge selector object. * \param directed Whether the graph is directed or not. * \return Error code. * \sa \ref igraph_es_pairs(), \ref igraph_es_destroy() * * Time complexity: O(n), the number of edges being selected. */ int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...) { va_list ap; long int i, n=0; es->type=IGRAPH_ES_PAIRS; es->data.path.mode=directed; es->data.path.ptr=igraph_Calloc(1, igraph_vector_t); if (es->data.path.ptr==0) { IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.path.ptr); va_start(ap, directed); while (1) { int num = va_arg(ap, int); if (num == -1) { break; } n++; } va_end(ap); IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); va_start(ap, directed); for (i=0; idata.path.ptr)[i]=(igraph_real_t) va_arg(ap, int); } va_end(ap); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed) { es->type=IGRAPH_ES_MULTIPAIRS; es->data.path.mode=directed; es->data.path.ptr=igraph_Calloc(1, igraph_vector_t); if (es->data.path.ptr==0) { IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v)); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \example examples/simple/igraph_es_path.c */ int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed) { es->type=IGRAPH_ES_PATH; es->data.path.mode=directed; es->data.path.ptr=igraph_Calloc(1, igraph_vector_t); if (es->data.path.ptr==0) { IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v)); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...) { va_list ap; long int i, n=0; es->type=IGRAPH_ES_PATH; es->data.path.mode=directed; es->data.path.ptr=igraph_Calloc(1, igraph_vector_t); if (es->data.path.ptr==0) { IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.path.ptr); va_start(ap, directed); while (1) { int num = va_arg(ap, int); if (num == -1) { break; } n++; } va_end(ap); IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); va_start(ap, directed); for (i=0; idata.path.ptr)[i]=(igraph_real_t) va_arg(ap, int); } va_end(ap); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_es_destroy * \brief Destroys an edge selector object. * * * Call this function on an edge selector when it is not needed any * more. Do \em not call this function on edge selectors created by * immediate constructors, those don't need to be destroyed. * * \param es Pointer to an edge selector object. * * Time complexity: operating system dependent, usually O(1). */ void igraph_es_destroy(igraph_es_t *es) { switch (es->type) { case IGRAPH_ES_ALL: case IGRAPH_ES_ALLFROM: case IGRAPH_ES_ALLTO: case IGRAPH_ES_INCIDENT: case IGRAPH_ES_NONE: case IGRAPH_ES_1: case IGRAPH_ES_VECTORPTR: case IGRAPH_ES_SEQ: break; case IGRAPH_ES_VECTOR: igraph_vector_destroy((igraph_vector_t*)es->data.vecptr); igraph_Free(es->data.vecptr); break; case IGRAPH_ES_PAIRS: case IGRAPH_ES_PATH: case IGRAPH_ES_MULTIPAIRS: igraph_vector_destroy((igraph_vector_t*)es->data.path.ptr); igraph_Free(es->data.path.ptr); break; default: break; } } /** * \function igraph_es_is_all * \brief Check whether an edge selector includes all edges. * * \param es Pointer to an edge selector object. * \return TRUE (1) if es was created with \ref * igraph_es_all() or \ref igraph_ess_all(), and FALSE (0) otherwise. * * Time complexity: O(1). */ igraph_bool_t igraph_es_is_all(const igraph_es_t *es) { return es->type == IGRAPH_ES_ALL; } /** * \function igraph_es_copy * \brief Creates a copy of an edge selector. * \param src The selector being copied. * \param dest An uninitialized selector that will contain the copy. * \sa \ref igraph_es_destroy() */ int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) { memcpy(dest, src, sizeof(igraph_es_t)); switch (dest->type) { case IGRAPH_ES_VECTOR: dest->data.vecptr = igraph_Calloc(1,igraph_vector_t); if (!dest->data.vecptr) IGRAPH_ERROR("Cannot copy edge selector", IGRAPH_ENOMEM); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.vecptr, (igraph_vector_t*)src->data.vecptr)); break; case IGRAPH_ES_PATH: case IGRAPH_ES_PAIRS: case IGRAPH_ES_MULTIPAIRS: dest->data.path.ptr = igraph_Calloc(1,igraph_vector_t); if (!dest->data.path.ptr) IGRAPH_ERROR("Cannot copy edge selector", IGRAPH_ENOMEM); IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.path.ptr, (igraph_vector_t*)src->data.path.ptr)); break; } return 0; } int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, igraph_vector_t *v) { igraph_eit_t eit; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); IGRAPH_CHECK(igraph_eit_as_vector(&eit, v)); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_es_type * \brief Returns the type of the edge selector. */ int igraph_es_type(const igraph_es_t *es) { return es->type; } int igraph_i_es_pairs_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result); int igraph_i_es_path_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result); int igraph_i_es_multipairs_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result); /** * \function igraph_es_size * \brief Returns the size of the edge selector. * * The size of the edge selector is the number of edges it will * yield when it is iterated over. * * \param graph The graph over which we will iterate. * \param result The result will be returned here. */ int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { igraph_vector_t v; switch (es->type) { case IGRAPH_ES_ALL: *result = igraph_ecount(graph); return 0; case IGRAPH_ES_ALLFROM: *result = igraph_ecount(graph); return 0; case IGRAPH_ES_ALLTO: *result = igraph_ecount(graph); return 0; case IGRAPH_ES_INCIDENT: IGRAPH_VECTOR_INIT_FINALLY(&v, 0); IGRAPH_CHECK(igraph_incident(graph, &v, es->data.incident.vid, es->data.incident.mode)); *result = (igraph_integer_t) igraph_vector_size(&v); igraph_vector_destroy(&v); IGRAPH_FINALLY_CLEAN(1); return 0; case IGRAPH_ES_NONE: *result = 0; return 0; case IGRAPH_ES_1: if (es->data.eid < igraph_ecount(graph) && es->data.eid >= 0) *result = 1; else *result = 0; return 0; case IGRAPH_ES_VECTOR: case IGRAPH_ES_VECTORPTR: *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)es->data.vecptr); return 0; case IGRAPH_ES_SEQ: *result = es->data.seq.to - es->data.seq.from; return 0; case IGRAPH_ES_PAIRS: IGRAPH_CHECK(igraph_i_es_pairs_size(graph, es, result)); return 0; case IGRAPH_ES_PATH: IGRAPH_CHECK(igraph_i_es_path_size(graph, es, result)); return 0; case IGRAPH_ES_MULTIPAIRS: IGRAPH_CHECK(igraph_i_es_multipairs_size(graph, es, result)); return 0; default: IGRAPH_ERROR("Cannot calculate selector length, invalid selector type", IGRAPH_EINVAL); } return 0; } int igraph_i_es_pairs_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { long int n=igraph_vector_size(es->data.path.ptr); long int no_of_nodes=igraph_vcount(graph); long int i; if (n % 2 != 0) { IGRAPH_ERROR("Cannot calculate edge selector length from odd number of vertices", IGRAPH_EINVAL); } if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot calculate edge selector length", IGRAPH_EINVVID); } *result = (igraph_integer_t) (n/2); /* Check for the existence of all edges */ for (i=0; i<*result; i++) { long int from=(long int) VECTOR(*es->data.path.ptr)[2*i]; long int to=(long int) VECTOR(*es->data.path.ptr)[2*i+1]; igraph_integer_t eid; IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, (igraph_integer_t) to, es->data.path.mode, /*error=*/ 1)); } return 0; } int igraph_i_es_path_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { long int n=igraph_vector_size(es->data.path.ptr); long int no_of_nodes=igraph_vcount(graph); long int i; if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_EINVVID); } if (n<=1) *result=0; else *result=(igraph_integer_t) (n-1); for (i=0; i<*result; i++) { long int from=(long int) VECTOR(*es->data.path.ptr)[i]; long int to=(long int) VECTOR(*es->data.path.ptr)[i+1]; igraph_integer_t eid; IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, (igraph_integer_t) to, es->data.path.mode, /*error=*/ 1)); } return 0; } int igraph_i_es_multipairs_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { IGRAPH_UNUSED(graph); IGRAPH_UNUSED(es); IGRAPH_UNUSED(result); IGRAPH_ERROR("Cannot calculate edge selector length", IGRAPH_UNIMPLEMENTED); } /**************************************************/ int igraph_i_eit_create_allfromto(const igraph_t *graph, igraph_eit_t *eit, igraph_neimode_t mode); int igraph_i_eit_pairs(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit); int igraph_i_eit_multipairs(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit); int igraph_i_eit_path(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit); int igraph_i_eit_create_allfromto(const igraph_t *graph, igraph_eit_t *eit, igraph_neimode_t mode) { igraph_vector_t *vec; long int no_of_nodes=igraph_vcount(graph); long int i; vec=igraph_Calloc(1, igraph_vector_t); if (vec==0) { IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vec); IGRAPH_VECTOR_INIT_FINALLY(vec, 0); IGRAPH_CHECK(igraph_vector_reserve(vec, igraph_ecount(graph))); if (igraph_is_directed(graph)) { igraph_vector_t adj; IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); for (i=0; itype=IGRAPH_EIT_VECTOR; eit->pos=0; eit->start=0; eit->vec=vec; eit->end=igraph_vector_size(eit->vec); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_eit_pairs(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit) { long int n=igraph_vector_size(es.data.path.ptr); long int no_of_nodes=igraph_vcount(graph); long int i; if (n % 2 != 0) { IGRAPH_ERROR("Cannot create edge iterator from odd number of vertices", IGRAPH_EINVAL); } if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); } eit->type=IGRAPH_EIT_VECTOR; eit->pos=0; eit->start=0; eit->end=n/2; eit->vec=igraph_Calloc(1, igraph_vector_t); if (eit->vec==0) { IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec); IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, n/2); for (i=0; ivec); i++) { long int from=(long int) VECTOR(*es.data.path.ptr)[2*i]; long int to=(long int) VECTOR(*es.data.path.ptr)[2*i+1]; igraph_integer_t eid; IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, (igraph_integer_t) to, es.data.path.mode, /*error=*/ 1)); VECTOR(*eit->vec)[i]=eid; } IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_eit_multipairs(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit) { long int n=igraph_vector_size(es.data.path.ptr); long int no_of_nodes=igraph_vcount(graph); if (n % 2 != 0) { IGRAPH_ERROR("Cannot create edge iterator from odd number of vertices", IGRAPH_EINVAL); } if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); } eit->type=IGRAPH_EIT_VECTOR; eit->pos=0; eit->start=0; eit->end=n/2; eit->vec=igraph_Calloc(1, igraph_vector_t); if (eit->vec==0) { IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec); IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, n/2); IGRAPH_CHECK(igraph_get_eids_multi(graph, (igraph_vector_t *) eit->vec, /*pairs=*/ es.data.path.ptr, /*path=*/ 0, es.data.path.mode, /*error=*/ 1)); IGRAPH_FINALLY_CLEAN(2); return 0; } int igraph_i_eit_path(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit) { long int n=igraph_vector_size(es.data.path.ptr); long int no_of_nodes=igraph_vcount(graph); long int i, len; if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); } if (n<=1) { len=0; } else { len=n-1; } eit->type=IGRAPH_EIT_VECTOR; eit->pos=0; eit->start=0; eit->end=len; eit->vec=igraph_Calloc(1, igraph_vector_t); if (eit->vec==0) { IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec); IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *)eit->vec, len); for (i=0; ivec)[i]=eid; } IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_eit_create * \brief Creates an edge iterator from an edge selector. * * * This function creates an edge iterator based on an edge selector * and a graph. * * * The same edge selector can be used to create many edge iterators, * also for different graphs. * * \param graph An \type igraph_t object for which the edge selector * will be instantiated. * \param es The edge selector to instantiate. * \param eit Pointer to an uninitialized edge iterator. * \return Error code. * \sa \ref igraph_eit_destroy() * * Time complexity: depends on the type of the edge selector. For edge * selectors created by \ref igraph_es_all(), \ref igraph_es_none(), * \ref igraph_es_1(), igraph_es_vector(), igraph_es_seq() it is * O(1). For \ref igraph_es_incident() it is O(d) where d is the number of * incident edges of the vertex. */ int igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit) { switch (es.type) { case IGRAPH_ES_ALL: eit->type=IGRAPH_EIT_SEQ; eit->pos=0; eit->start=0; eit->end=igraph_ecount(graph); break; case IGRAPH_ES_ALLFROM: IGRAPH_CHECK(igraph_i_eit_create_allfromto(graph, eit, IGRAPH_OUT)); break; case IGRAPH_ES_ALLTO: IGRAPH_CHECK(igraph_i_eit_create_allfromto(graph, eit, IGRAPH_IN)); break; case IGRAPH_ES_INCIDENT: eit->type=IGRAPH_EIT_VECTOR; eit->pos=0; eit->start=0; eit->vec=igraph_Calloc(1, igraph_vector_t); if (eit->vec == 0) { IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) eit->vec); IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, 0); IGRAPH_CHECK(igraph_incident(graph, (igraph_vector_t*)eit->vec, es.data.incident.vid, es.data.incident.mode)); eit->end=igraph_vector_size(eit->vec); IGRAPH_FINALLY_CLEAN(2); break; case IGRAPH_ES_NONE: eit->type=IGRAPH_EIT_SEQ; eit->pos=0; eit->start=0; eit->end=0; break; case IGRAPH_ES_1: eit->type=IGRAPH_EIT_SEQ; eit->pos=es.data.eid; eit->start=es.data.eid; eit->end=es.data.eid+1; if (eit->pos >= igraph_ecount(graph)) { IGRAPH_ERROR("Cannot create iterator, invalid edge id", IGRAPH_EINVVID); } break; case IGRAPH_ES_VECTOR: case IGRAPH_ES_VECTORPTR: eit->type=IGRAPH_EIT_VECTORPTR; eit->pos=0; eit->start=0; eit->vec=es.data.vecptr; eit->end=igraph_vector_size(eit->vec); if (!igraph_vector_isininterval(eit->vec, 0, igraph_ecount(graph)-1)) { IGRAPH_ERROR("Cannot create iterator, invalid edge id",IGRAPH_EINVVID); } break; case IGRAPH_ES_SEQ: eit->type=IGRAPH_EIT_SEQ; eit->pos=es.data.seq.from; eit->start=es.data.seq.from; eit->end=es.data.seq.to; break; case IGRAPH_ES_PAIRS: IGRAPH_CHECK(igraph_i_eit_pairs(graph, es, eit)); break; case IGRAPH_ES_MULTIPAIRS: IGRAPH_CHECK(igraph_i_eit_multipairs(graph, es, eit)); break; case IGRAPH_ES_PATH: IGRAPH_CHECK(igraph_i_eit_path(graph, es, eit)); break; default: IGRAPH_ERROR("Cannot create iterator, invalid selector", IGRAPH_EINVAL); break; } return 0; } /** * \function igraph_eit_destroy * \brief Destroys an edge iterator. * * \param eit Pointer to an edge iterator to destroy. * \sa \ref igraph_eit_create() * * Time complexity: operating system dependent, usually O(1). */ void igraph_eit_destroy(const igraph_eit_t *eit) { switch (eit->type) { case IGRAPH_EIT_SEQ: case IGRAPH_EIT_VECTORPTR: break; case IGRAPH_EIT_VECTOR: igraph_vector_destroy((igraph_vector_t*)eit->vec); igraph_free((igraph_vector_t*)eit->vec); break; default: /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ break; } } int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v) { long int i; IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_EIT_SIZE(*eit))); switch (eit->type) { case IGRAPH_EIT_SEQ: for (i=0; istart+i; } break; case IGRAPH_EIT_VECTOR: case IGRAPH_EIT_VECTORPTR: for (i=0; ivec)[i]; } break; default: IGRAPH_ERROR("Cannot convert to vector, unknown iterator type", IGRAPH_EINVAL); break; } return 0; } igraph/src/igraph_fixed_vectorlist.c0000644000176000001440000000446012325527073017430 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types_internal.h" #include "igraph_memory.h" void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l) { long int i, n=igraph_vector_ptr_size(&l->v); for (i=0; iv)[i]; if (v) { igraph_vector_destroy(v); } } igraph_vector_ptr_destroy(&l->v); igraph_free(l->vecs); } int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, const igraph_vector_t *from, long int size) { igraph_vector_t sizes; long int i, no=igraph_vector_size(from); l->vecs=igraph_Calloc(size, igraph_vector_t); if (!l->vecs) { IGRAPH_ERROR("Cannot merge attributes for simplify", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, l->vecs); IGRAPH_CHECK(igraph_vector_ptr_init(&l->v, size)); IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &l->v); IGRAPH_VECTOR_INIT_FINALLY(&sizes, size); for (i=0; i= 0) { VECTOR(sizes)[to] += 1; } } for (i=0; ivecs[i]); IGRAPH_CHECK(igraph_vector_init(v, (long int) VECTOR(sizes)[i])); igraph_vector_clear(v); VECTOR(l->v)[i]=v; } for (i=0; i= 0) { igraph_vector_t *v=&(l->vecs[to]); igraph_vector_push_back(v, i); } } igraph_vector_destroy(&sizes); IGRAPH_FINALLY_CLEAN(3); return 0; } igraph/src/igraph_psumtree.c0000644000176000001440000000521312325527073015714 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA Copyright (C) 2006 Elliot Paquette Kalamazoo College, 1200 Academy st, Kalamazoo, MI This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_psumtree.h" #include "igraph_error.h" #include "config.h" #include #include double igraph_i_log2(double f) { return log(f) / log(2.0); } int igraph_psumtree_init(igraph_psumtree_t *t, long int size) { t->size=size; t->offset=(long int) (pow(2, ceil(igraph_i_log2(size)))-1); IGRAPH_CHECK(igraph_vector_init((igraph_vector_t *)t, t->offset+t->size)); return 0; } void igraph_psumtree_destroy(igraph_psumtree_t *t) { igraph_vector_destroy((igraph_vector_t *)t); } igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx) { const igraph_vector_t *tree=&t->v; return VECTOR(*tree)[t->offset+idx]; } int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, igraph_real_t search) { const igraph_vector_t *tree=&t->v; long int i = 1; long int size = igraph_vector_size(tree); while( 2*i+1 <= size) { if( search <= VECTOR(*tree)[i*2-1] ) { i <<= 1; } else { search -= VECTOR(*tree)[i*2-1]; i <<= 1; i += 1; } } if (2*i <= size) { i=2*i; } *idx = i-t->offset-1; return IGRAPH_SUCCESS; } int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, igraph_real_t new_value) { const igraph_vector_t *tree=&t->v; igraph_real_t difference; idx = idx + t->offset+1; difference = new_value - VECTOR(*tree)[idx-1]; while( idx >= 1 ) { VECTOR(*tree)[idx-1] += difference; idx >>= 1; } return IGRAPH_SUCCESS; } long int igraph_psumtree_size(const igraph_psumtree_t *t) { return t->size; } igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t) { return VECTOR(t->v)[0]; } igraph/src/cs_pvec.c0000644000176000001440000000231112325527073014134 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* x = b(p), for dense vectors x and b; p=NULL denotes identity */ CS_INT cs_pvec (const CS_INT *p, const CS_ENTRY *b, CS_ENTRY *x, CS_INT n) { CS_INT k ; if (!x || !b) return (0) ; /* check inputs */ for (k = 0 ; k < n ; k++) x [k] = b [p ? p [k] : k] ; return (1) ; } igraph/src/triangles_template.h0000644000176000001440000000661412325527074016415 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ long int no_of_nodes=igraph_vcount(graph); long int node, i, j, nn; igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; long int neilen1, neilen2, deg1; #ifdef TRIPLES igraph_integer_t triples; #endif long int *neis; long int maxdegree; igraph_vector_int_t order; igraph_vector_int_t rank; igraph_vector_t degree; igraph_vector_int_init(&order, no_of_nodes); IGRAPH_FINALLY(igraph_vector_int_destroy, &order); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); maxdegree=(long int) igraph_vector_max(°ree)+1; igraph_vector_order1_int(°ree, &order, maxdegree); igraph_vector_int_init(&rank, no_of_nodes); IGRAPH_FINALLY(igraph_vector_int_destroy, &rank); for (i=0; i=0; nn--) { node=VECTOR(order)[nn]; IGRAPH_ALLOW_INTERRUPTION(); neis1=igraph_adjlist_get(&allneis, node); neilen1=igraph_vector_int_size(neis1); deg1=(long int) VECTOR(degree)[node]; #ifdef TRIPLES triples=(igraph_integer_t) ((double)deg1*(deg1-1)/2); #endif /* Mark the neighbors of the node */ for (i=0; ip ; Gi = G->i ; xi [0] = j ; /* initialize the recursion stack */ while (head >= 0) { j = xi [head] ; /* get j from the top of the recursion stack */ jnew = pinv ? (pinv [j]) : j ; if (!CS_MARKED (Gp, j)) { CS_MARK (Gp, j) ; /* mark node j as visited */ pstack [head] = (jnew < 0) ? 0 : CS_UNFLIP (Gp [jnew]) ; } done = 1 ; /* node j done if no unvisited neighbors */ p2 = (jnew < 0) ? 0 : CS_UNFLIP (Gp [jnew+1]) ; for (p = pstack [head] ; p < p2 ; p++) /* examine all neighbors of j */ { i = Gi [p] ; /* consider neighbor node i */ if (CS_MARKED (Gp, i)) continue ; /* skip visited node i */ pstack [head] = p ; /* pause depth-first search of node j */ xi [++head] = i ; /* start dfs at node i */ done = 0 ; /* node j is not done */ break ; /* break, to start dfs (i) */ } if (done) /* depth-first search at node j is done */ { head-- ; /* remove j from the recursion stack */ xi [--top] = j ; /* and place in the output stack */ } } return (top) ; } igraph/src/gengraph_box_list.cpp0000644000176000001440000000410312325527073016551 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gengraph_box_list.h" #include namespace gengraph { void box_list::insert(int v) { register int d = deg[v]; if(d<1) return; if(d>dmax) dmax=d; int yo = list[d-1]; list[d-1] = v; prev[v] = -1; next[v] = yo; if(yo>=0) prev[yo]=v; } void box_list::pop(int v) { register int p = prev[v]; register int n = next[v]; if(p<0) { register int d = deg[v]; assert(list[d-1]==v); list[d-1] = n; if(d==dmax && n<0) do dmax--; while(dmax>0 && list[dmax-1]<0); } else next[p] = n; if(n>=0) prev[n] = p; } box_list::box_list(int n0, int *deg0) : n(n0), deg(deg0) { next = new int[n]; prev = new int[n]; dmax = -1; int i; for(i=0; idmax) dmax=deg[i]; list = new int[dmax]; for(i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph.h" #include "igraph_error.h" #include "RayTracer.h" #include "Sphere.h" #include "config.h" #define USE_RINTERNALS #include #include #include using namespace igraph; extern "C" { SEXP R_igraph_getsphere(SEXP pos, SEXP radius, SEXP color, SEXP bgcolor, SEXP lightpos, SEXP lightcolor, SEXP width, SEXP height) { /* All error checking is done at the R level */ int i; double *spos=REAL(pos); double *scolor=REAL(color); double *svgcolor=REAL(bgcolor); int no_lights=GET_LENGTH(lightpos); RayTracer* p_ray_tracer; Sphere * sphere; int swidth=INTEGER(width)[0]; int sheight=INTEGER(height)[0]; int nopixels=swidth * sheight; SEXP result, dim; Image image; p_ray_tracer = new RayTracer(); p_ray_tracer->EyePoint(Point(0,0,0)); for (i=0; iIntensity(1); light->LightColor(Color(lcol[0], lcol[1], lcol[2])); p_ray_tracer->AddLight(light); } sphere = new Sphere(Point(spos[0], spos[1], spos[2]), REAL(radius)[0]); sphere->ShapeColor(Color(scolor[0], scolor[1], scolor[2])); p_ray_tracer->AddShape(sphere); PROTECT(result=NEW_NUMERIC(nopixels * 4)); PROTECT(dim=NEW_INTEGER(3)); INTEGER(dim)[0]=swidth; INTEGER(dim)[1]=sheight; INTEGER(dim)[2]=4; SET_DIM(result, dim); image.width=swidth; image.height=sheight; image.red=REAL(result); image.green=image.red + nopixels; image.blue=image.green + nopixels; image.trans=image.blue + nopixels; p_ray_tracer->RayTrace(image); delete p_ray_tracer; UNPROTECT(2); return result; } } // extern C igraph/src/igraph_array_pmt.h0000644000176000001440000000406212325527073016054 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ typedef struct TYPE(igraph_array3) { TYPE(igraph_vector) data; long int n1, n2, n3, n1n2; } TYPE(igraph_array3); #ifndef IGRAPH_ARRAY3_INIT_FINALLY #define IGRAPH_ARRAY3_INIT_FINALLY(a, n1, n2, n3) \ do { IGRAPH_CHECK(igraph_array3_init(a, n1, n2, n3)); \ IGRAPH_FINALLY(igraph_array3_destroy, a); } while (0) #endif #ifndef ARRAY3 #define ARRAY3(m,i,j,k) ((m).data.stor_begin[(m).n1n2*(k)+(m).n1*(j)+(i)]) #endif int FUNCTION(igraph_array3,init)(TYPE(igraph_array3) *a, long int n1, long int n2, long int n3); void FUNCTION(igraph_array3,destroy)(TYPE(igraph_array3) *a); long int FUNCTION(igraph_array3,size)(const TYPE(igraph_array3) *a); long int FUNCTION(igraph_array3,n)(const TYPE(igraph_array3) *a, long int idx); int FUNCTION(igraph_array3,resize)(TYPE(igraph_array3) *a, long int n1, long int n2, long int n3); void FUNCTION(igraph_array3,null)(TYPE(igraph_array3) *a); BASE FUNCTION(igraph_array3,sum)(const TYPE(igraph_array3) *a); void FUNCTION(igraph_array3,scale)(TYPE(igraph_array3) *a, BASE by); void FUNCTION(igraph_array3,fill)(TYPE(igraph_array3) *a, BASE e); int FUNCTION(igraph_array3,update)(TYPE(igraph_array3) *to, const TYPE(igraph_array3) *from); igraph/src/igraph_blas.h0000644000176000001440000000477312325527073015010 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef BLAS_H #define BLAS_H #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /** * \section about_blas BLAS interface in igraph * * * BLAS is a highly optimized library for basic linear algebra operations * such as vector-vector, matrix-vector and matrix-matrix product. * Please see http://www.netlib.org/blas/ for details and a reference * implementation in Fortran. igraph contains some wrapper functions * that can be used to call BLAS routines in a somewhat more * user-friendly way. Not all BLAS routines are included in igraph, * and even those which are included might not have wrappers; * the extension of the set of wrapped functions will probably be driven * by igraph's internal requirements. The wrapper functions usually * substitute double-precision floating point arrays used by BLAS with * \type igraph_vector_t and \type igraph_matrix_t instances and also * remove those parameters (such as the number of rows/columns) that * can be inferred from the passed arguments directly. * */ void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, const igraph_matrix_t* a, const igraph_vector_t* x, igraph_real_t beta, igraph_vector_t* y); void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, const igraph_matrix_t* a, const igraph_real_t* x, igraph_real_t beta, igraph_real_t* y); __END_DECLS #endif igraph/src/igraph_types.h0000644000176000001440000000561112325527073015223 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef REST_TYPES_H #define REST_TYPES_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif #include "igraph_error.h" #include #include #include /* This is to eliminate gcc warnings about unused parameters */ #define IGRAPH_UNUSED(x) (void)(x) typedef int igraph_integer_t; typedef double igraph_real_t; typedef int igraph_bool_t; /* Replacements for printf that print doubles in the same way on all platforms * (even for NaN and infinities) */ int igraph_real_printf(igraph_real_t val); int igraph_real_fprintf(FILE *file, igraph_real_t val); int igraph_real_snprintf(char* str, size_t size, igraph_real_t val); /* Replacements for printf that print doubles in the same way on all platforms * (even for NaN and infinities) with the largest possible precision */ int igraph_real_printf_precise(igraph_real_t val); int igraph_real_fprintf_precise(FILE *file, igraph_real_t val); int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val); /* igraph_i_fdiv is needed here instead of in igraph_math.h because * some constants use it */ double igraph_i_fdiv(const double a, const double b); #if defined(INFINITY) # define IGRAPH_INFINITY INFINITY # define IGRAPH_POSINFINITY INFINITY # define IGRAPH_NEGINFINITY (-INFINITY) #else # define IGRAPH_INFINITY (igraph_i_fdiv(1.0, 0.0)) # define IGRAPH_POSINFINITY (igraph_i_fdiv(1.0, 0.0)) # define IGRAPH_NEGINFINITY (igraph_i_fdiv(-1.0, 0.0)) #endif int igraph_finite(double x); #define IGRAPH_FINITE(x) igraph_finite(x) int igraph_is_nan(double x); int igraph_is_inf(double x); int igraph_is_posinf(double x); int igraph_is_neginf(double x); #if defined(NAN) # define IGRAPH_NAN NAN #elif defined(INFINITY) # define IGRAPH_NAN (INFINITY/INFINITY) #else # define IGRAPH_NAN (igraph_i_fdiv(0.0, 0.0)) #endif __END_DECLS #endif igraph/src/Makevars.win0000644000176000001440000000075212325372070014640 0ustar ripleyusers PKG_CPPFLAGS= -I${LIB_XML}/include/libxml2 -I${LIB_XML}/include -DLIBXML_STATIC -DUSING_R -DHAVE_FMEMOPEN=0 -DHAVE_OPEN_MEMSTREAM=0 -DHAVE_RINTF -DWin32 -DHAVE_LIBXML -Wall -DPACKAGE_VERSION=\"0.7.1\" -DHAVE_FMIN=1 -DHAVE_LOG2=1 -DHAVE_SNPRINTF -Ics -Iglpk -DHAVE_GLPK=1 -Iglpk/amd -Iglpk/colamd -Iplfit -Iprpack -DIGRAPH_THREAD_LOCAL=/**/ -DPRPACK_IGRAPH_SUPPORT -I. PKG_CFLAGS = -DINTERNAL_ARPACK -I. PKG_LIBS = -L${LIB_XML}/lib -lxml2 -liconv -lz -lws2_32 $(BLAS_LIBS) $(LAPACK_LIBS) igraph/src/gengraph_box_list.h0000644000176000001440000000474412325527073016231 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // This class allows to maintain a list of vertices, // sorted by degree (largest degrees first) // Operations allowed : // - get the vertex having max degree -> Cost = O(1) // - remove any vertex from the graph -> Cost = Sum(degrees of neighbours) // [ could be O(degree) if optimized ] #ifndef _BOX_LIST_H #define _BOX_LIST_H namespace gengraph { class box_list { private: int n; // INITIAL number of vertices int dmax; // CURRENT Maximum degree int *deg; // CURRENT Degrees (points directly to the deg[] of the graph // Vertices are grouped by degree: one double-chained lists for each degree int *list; // list[d-1] is the head of list of vertices of degree d int *next; // next[v]/prev[v] are the vertices next/previous to v int *prev; // in the list where v belongs void pop(int); // pop(v) just removes v from its list void insert(int); // insert(v) insert v at the head of its list public: // Ctor. Takes O(n) time. box_list(int n0, int *deg0); // Dtor ~box_list(); // Self-explaining inline routines inline bool is_empty() { return dmax<1; }; inline int get_max() { return list[dmax-1]; }; inline int get_one() { return list[0]; }; inline int get_min() { int i=0; while(list[i]<0) i++; return list[i]; }; // Remove v from box_list // Also, semi-remove vertex v from graph: all neighbours of v will swap // their last neighbour wit hv, and then decrease their degree, so // that any arc w->v virtually disappear // Actually, adjacency lists are just permuted, and deg[] is changed void pop_vertex(int v, int **neigh); }; } // namespace gengraph #endif //_BOX_LIST_H igraph/src/scg_utils.c0000644000176000001440000000556212325527074014522 0ustar ripleyusers/* * SCGlib : A C library for the spectral coarse graining of matrices * as described in the paper: Shrinking Matrices while preserving their * eigenpairs with Application to the Spectral Coarse Graining of Graphs. * Preprint available at * * Copyright (C) 2008 David Morton de Lachapelle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * DESCRIPTION * ----------- * This files contains the data structures and error handing * functions used throughout the SCGlib. */ #include "igraph_error.h" #include "igraph_memory.h" #include "scg_headers.h" /*to be used with qsort and struct ind_val arrays */ int igraph_i_compare_ind_val(const void *a, const void *b) { igraph_i_scg_indval_t *arg1 = (igraph_i_scg_indval_t *) a; igraph_i_scg_indval_t *arg2 = (igraph_i_scg_indval_t *) b; if ( arg1->val < arg2->val ) { return -1; } else if ( arg1->val == arg2->val ) { return 0; } else { return 1; } } /*to be used with qsort and struct groups*/ int igraph_i_compare_groups(const void *a, const void *b) { igraph_i_scg_groups_t *arg1 = (igraph_i_scg_groups_t *) a; igraph_i_scg_groups_t *arg2 = (igraph_i_scg_groups_t *) b; int i; for (i=0; in; i++) { if (arg1->gr[i]>arg2->gr[i]) return 1; else if (arg1->gr[i]gr[i]) return -1; } return 0; } /*to be used with qsort and real_vectors */ int igraph_i_compare_real(const void *a, const void *b) { igraph_real_t arg1 = * (igraph_real_t *) a; igraph_real_t arg2 = * (igraph_real_t *) b; if (arg1 < arg2) { return -1; } else if (arg1 == arg2) { return 0; } else { return 1; } } /*to be used with qsort and integer vectors */ int igraph_i_compare_int(const void *a, const void *b) { int arg1 = * (int *) a; int arg2 = * (int *) b; return (arg1 -arg2); } /* allocate a igraph_real_t symmetrix matrix with dimension size x size in vector format*/ igraph_real_t *igraph_i_real_sym_matrix(const int size) { igraph_real_t *S = igraph_Calloc(size*(size+1)/2, igraph_real_t); if (!S) { igraph_error("allocation failure in real_sym_matrix()", __FILE__, __LINE__, IGRAPH_ENOMEM); } return S; } igraph/src/glpbfd.c0000644000176000001440000003467212325527073013767 0ustar ripleyusers/* glpbfd.c (LP basis factorization driver) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif typedef struct BFD BFD; #define GLPBFD_PRIVATE #include "glpapi.h" #include "glpfhv.h" #include "glplpf.h" /* CAUTION: DO NOT CHANGE THE LIMIT BELOW */ #define M_MAX 100000000 /* = 100*10^6 */ /* maximal order of the basis matrix */ struct BFD { /* LP basis factorization */ int valid; /* factorization is valid only if this flag is set */ int type; /* factorization type: GLP_BF_FT - LUF + Forrest-Tomlin GLP_BF_BG - LUF + Schur compl. + Bartels-Golub GLP_BF_GR - LUF + Schur compl. + Givens rotation */ FHV *fhv; /* LP basis factorization (GLP_BF_FT) */ LPF *lpf; /* LP basis factorization (GLP_BF_BG, GLP_BF_GR) */ int lu_size; /* luf.sv_size */ double piv_tol; /* luf.piv_tol */ int piv_lim; /* luf.piv_lim */ int suhl; /* luf.suhl */ double eps_tol; /* luf.eps_tol */ double max_gro; /* luf.max_gro */ int nfs_max; /* fhv.hh_max */ double upd_tol; /* fhv.upd_tol */ int nrs_max; /* lpf.n_max */ int rs_size; /* lpf.v_size */ /* internal control parameters */ int upd_lim; /* the factorization update limit */ int upd_cnt; /* the factorization update count */ }; /*********************************************************************** * NAME * * bfd_create_it - create LP basis factorization * * SYNOPSIS * * #include "glpbfd.h" * BFD *bfd_create_it(void); * * DESCRIPTION * * The routine bfd_create_it creates a program object, which represents * a factorization of LP basis. * * RETURNS * * The routine bfd_create_it returns a pointer to the object created. */ BFD *bfd_create_it(void) { BFD *bfd; bfd = xmalloc(sizeof(BFD)); bfd->valid = 0; bfd->type = GLP_BF_FT; bfd->fhv = NULL; bfd->lpf = NULL; bfd->lu_size = 0; bfd->piv_tol = 0.10; bfd->piv_lim = 4; bfd->suhl = 1; bfd->eps_tol = 1e-15; bfd->max_gro = 1e+10; bfd->nfs_max = 100; bfd->upd_tol = 1e-6; bfd->nrs_max = 100; bfd->rs_size = 1000; bfd->upd_lim = -1; bfd->upd_cnt = 0; return bfd; } /**********************************************************************/ void bfd_set_parm(BFD *bfd, const void *_parm) { /* change LP basis factorization control parameters */ const glp_bfcp *parm = _parm; xassert(bfd != NULL); bfd->type = parm->type; bfd->lu_size = parm->lu_size; bfd->piv_tol = parm->piv_tol; bfd->piv_lim = parm->piv_lim; bfd->suhl = parm->suhl; bfd->eps_tol = parm->eps_tol; bfd->max_gro = parm->max_gro; bfd->nfs_max = parm->nfs_max; bfd->upd_tol = parm->upd_tol; bfd->nrs_max = parm->nrs_max; bfd->rs_size = parm->rs_size; return; } /*********************************************************************** * NAME * * bfd_factorize - compute LP basis factorization * * SYNOPSIS * * #include "glpbfd.h" * int bfd_factorize(BFD *bfd, int m, int bh[], int (*col)(void *info, * int j, int ind[], double val[]), void *info); * * DESCRIPTION * * The routine bfd_factorize computes the factorization of the basis * matrix B specified by the routine col. * * The parameter bfd specified the basis factorization data structure * created with the routine bfd_create_it. * * The parameter m specifies the order of B, m > 0. * * The array bh specifies the basis header: bh[j], 1 <= j <= m, is the * number of j-th column of B in some original matrix. The array bh is * optional and can be specified as NULL. * * The formal routine col specifies the matrix B to be factorized. To * obtain j-th column of A the routine bfd_factorize calls the routine * col with the parameter j (1 <= j <= n). In response the routine col * should store row indices and numerical values of non-zero elements * of j-th column of B to locations ind[1,...,len] and val[1,...,len], * respectively, where len is the number of non-zeros in j-th column * returned on exit. Neither zero nor duplicate elements are allowed. * * The parameter info is a transit pointer passed to the routine col. * * RETURNS * * 0 The factorization has been successfully computed. * * BFD_ESING * The specified matrix is singular within the working precision. * * BFD_ECOND * The specified matrix is ill-conditioned. * * For more details see comments to the routine luf_factorize. */ int bfd_factorize(BFD *bfd, int m, const int bh[], int (*col) (void *info, int j, int ind[], double val[]), void *info) { LUF *luf; int nov, ret; xassert(bfd != NULL); xassert(1 <= m && m <= M_MAX); /* invalidate the factorization */ bfd->valid = 0; /* create the factorization, if necessary */ nov = 0; switch (bfd->type) { case GLP_BF_FT: if (bfd->lpf != NULL) lpf_delete_it(bfd->lpf), bfd->lpf = NULL; if (bfd->fhv == NULL) bfd->fhv = fhv_create_it(), nov = 1; break; case GLP_BF_BG: case GLP_BF_GR: if (bfd->fhv != NULL) fhv_delete_it(bfd->fhv), bfd->fhv = NULL; if (bfd->lpf == NULL) bfd->lpf = lpf_create_it(), nov = 1; break; default: xassert(bfd != bfd); } /* set control parameters specific to LUF */ if (bfd->fhv != NULL) luf = bfd->fhv->luf; else if (bfd->lpf != NULL) luf = bfd->lpf->luf; else xassert(bfd != bfd); if (nov) luf->new_sva = bfd->lu_size; luf->piv_tol = bfd->piv_tol; luf->piv_lim = bfd->piv_lim; luf->suhl = bfd->suhl; luf->eps_tol = bfd->eps_tol; luf->max_gro = bfd->max_gro; /* set control parameters specific to FHV */ if (bfd->fhv != NULL) { if (nov) bfd->fhv->hh_max = bfd->nfs_max; bfd->fhv->upd_tol = bfd->upd_tol; } /* set control parameters specific to LPF */ if (bfd->lpf != NULL) { if (nov) bfd->lpf->n_max = bfd->nrs_max; if (nov) bfd->lpf->v_size = bfd->rs_size; } /* try to factorize the basis matrix */ if (bfd->fhv != NULL) { switch (fhv_factorize(bfd->fhv, m, col, info)) { case 0: break; case FHV_ESING: ret = BFD_ESING; goto done; case FHV_ECOND: ret = BFD_ECOND; goto done; default: xassert(bfd != bfd); } } else if (bfd->lpf != NULL) { switch (lpf_factorize(bfd->lpf, m, bh, col, info)) { case 0: /* set the Schur complement update type */ switch (bfd->type) { case GLP_BF_BG: /* Bartels-Golub update */ bfd->lpf->scf->t_opt = SCF_TBG; break; case GLP_BF_GR: /* Givens rotation update */ bfd->lpf->scf->t_opt = SCF_TGR; break; default: xassert(bfd != bfd); } break; case LPF_ESING: ret = BFD_ESING; goto done; case LPF_ECOND: ret = BFD_ECOND; goto done; default: xassert(bfd != bfd); } } else xassert(bfd != bfd); /* the basis matrix has been successfully factorized */ bfd->valid = 1; bfd->upd_cnt = 0; ret = 0; done: /* return to the calling program */ return ret; } /*********************************************************************** * NAME * * bfd_ftran - perform forward transformation (solve system B*x = b) * * SYNOPSIS * * #include "glpbfd.h" * void bfd_ftran(BFD *bfd, double x[]); * * DESCRIPTION * * The routine bfd_ftran performs forward transformation, i.e. solves * the system B*x = b, where B is the basis matrix, x is the vector of * unknowns to be computed, b is the vector of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. */ void bfd_ftran(BFD *bfd, double x[]) { xassert(bfd != NULL); xassert(bfd->valid); if (bfd->fhv != NULL) fhv_ftran(bfd->fhv, x); else if (bfd->lpf != NULL) lpf_ftran(bfd->lpf, x); else xassert(bfd != bfd); return; } /*********************************************************************** * NAME * * bfd_btran - perform backward transformation (solve system B'*x = b) * * SYNOPSIS * * #include "glpbfd.h" * void bfd_btran(BFD *bfd, double x[]); * * DESCRIPTION * * The routine bfd_btran performs backward transformation, i.e. solves * the system B'*x = b, where B' is a matrix transposed to the basis * matrix B, x is the vector of unknowns to be computed, b is the vector * of right-hand sides. * * On entry elements of the vector b should be stored in dense format * in locations x[1], ..., x[m], where m is the number of rows. On exit * the routine stores elements of the vector x in the same locations. */ void bfd_btran(BFD *bfd, double x[]) { xassert(bfd != NULL); xassert(bfd->valid); if (bfd->fhv != NULL) fhv_btran(bfd->fhv, x); else if (bfd->lpf != NULL) lpf_btran(bfd->lpf, x); else xassert(bfd != bfd); return; } /*********************************************************************** * NAME * * bfd_update_it - update LP basis factorization * * SYNOPSIS * * #include "glpbfd.h" * int bfd_update_it(BFD *bfd, int j, int bh, int len, const int ind[], * const double val[]); * * DESCRIPTION * * The routine bfd_update_it updates the factorization of the basis * matrix B after replacing its j-th column by a new vector. * * The parameter j specifies the number of column of B, which has been * replaced, 1 <= j <= m, where m is the order of B. * * The parameter bh specifies the basis header entry for the new column * of B, which is the number of the new column in some original matrix. * This parameter is optional and can be specified as 0. * * Row indices and numerical values of non-zero elements of the new * column of B should be placed in locations ind[1], ..., ind[len] and * val[1], ..., val[len], resp., where len is the number of non-zeros * in the column. Neither zero nor duplicate elements are allowed. * * RETURNS * * 0 The factorization has been successfully updated. * * BFD_ESING * New basis matrix is singular within the working precision. * * BFD_ECHECK * The factorization is inaccurate. * * BFD_ELIMIT * Factorization update limit has been reached. * * BFD_EROOM * Overflow of the sparse vector area. * * In case of non-zero return code the factorization becomes invalid. * It should not be used until it has been recomputed with the routine * bfd_factorize. */ int bfd_update_it(BFD *bfd, int j, int bh, int len, const int ind[], const double val[]) { int ret; xassert(bfd != NULL); xassert(bfd->valid); /* try to update the factorization */ if (bfd->fhv != NULL) { switch (fhv_update_it(bfd->fhv, j, len, ind, val)) { case 0: break; case FHV_ESING: bfd->valid = 0; ret = BFD_ESING; goto done; case FHV_ECHECK: bfd->valid = 0; ret = BFD_ECHECK; goto done; case FHV_ELIMIT: bfd->valid = 0; ret = BFD_ELIMIT; goto done; case FHV_EROOM: bfd->valid = 0; ret = BFD_EROOM; goto done; default: xassert(bfd != bfd); } } else if (bfd->lpf != NULL) { switch (lpf_update_it(bfd->lpf, j, bh, len, ind, val)) { case 0: break; case LPF_ESING: bfd->valid = 0; ret = BFD_ESING; goto done; case LPF_ELIMIT: bfd->valid = 0; ret = BFD_ELIMIT; goto done; default: xassert(bfd != bfd); } } else xassert(bfd != bfd); /* the factorization has been successfully updated */ /* increase the update count */ bfd->upd_cnt++; ret = 0; done: /* return to the calling program */ return ret; } /**********************************************************************/ int bfd_get_count(BFD *bfd) { /* determine factorization update count */ xassert(bfd != NULL); xassert(bfd->valid); return bfd->upd_cnt; } /*********************************************************************** * NAME * * bfd_delete_it - delete LP basis factorization * * SYNOPSIS * * #include "glpbfd.h" * void bfd_delete_it(BFD *bfd); * * DESCRIPTION * * The routine bfd_delete_it deletes LP basis factorization specified * by the parameter fhv and frees all memory allocated to this program * object. */ void bfd_delete_it(BFD *bfd) { xassert(bfd != NULL); if (bfd->fhv != NULL) fhv_delete_it(bfd->fhv); if (bfd->lpf != NULL) lpf_delete_it(bfd->lpf); xfree(bfd); return; } /* eof */ igraph/src/glpios03.c0000644000176000001440000012453212325527073014164 0ustar ripleyusers/* glpios03.c (branch-and-cut driver) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpios.h" /*********************************************************************** * show_progress - display current progress of the search * * This routine displays some information about current progress of the * search. * * The information includes: * * the current number of iterations performed by the simplex solver; * * the objective value for the best known integer feasible solution, * which is upper (minimization) or lower (maximization) global bound * for optimal solution of the original mip problem; * * the best local bound for active nodes, which is lower (minimization) * or upper (maximization) global bound for optimal solution of the * original mip problem; * * the relative mip gap, in percents; * * the number of open (active) subproblems; * * the number of completely explored subproblems, i.e. whose nodes have * been removed from the tree. */ static void show_progress(glp_tree *T, int bingo) { int p; double temp; char best_mip[50], best_bound[50], *rho, rel_gap[50]; /* format the best known integer feasible solution */ if (T->mip->mip_stat == GLP_FEAS) sprintf(best_mip, "%17.9e", T->mip->mip_obj); else sprintf(best_mip, "%17s", "not found yet"); /* determine reference number of an active subproblem whose local bound is best */ p = ios_best_node(T); /* format the best bound */ if (p == 0) sprintf(best_bound, "%17s", "tree is empty"); else { temp = T->slot[p].node->bound; if (temp == -DBL_MAX) sprintf(best_bound, "%17s", "-inf"); else if (temp == +DBL_MAX) sprintf(best_bound, "%17s", "+inf"); else sprintf(best_bound, "%17.9e", temp); } /* choose the relation sign between global bounds */ if (T->mip->dir == GLP_MIN) rho = ">="; else if (T->mip->dir == GLP_MAX) rho = "<="; else xassert(T != T); /* format the relative mip gap */ temp = ios_relative_gap(T); if (temp == 0.0) sprintf(rel_gap, " 0.0%%"); else if (temp < 0.001) sprintf(rel_gap, "< 0.1%%"); else if (temp <= 9.999) sprintf(rel_gap, "%5.1f%%", 100.0 * temp); else sprintf(rel_gap, "%6s", ""); /* display progress of the search */ xprintf("+%6d: %s %s %s %s %s (%d; %d)\n", T->mip->it_cnt, bingo ? ">>>>>" : "mip =", best_mip, rho, best_bound, rel_gap, T->a_cnt, T->t_cnt - T->n_cnt); T->tm_lag = xtime(); return; } /*********************************************************************** * is_branch_hopeful - check if specified branch is hopeful * * This routine checks if the specified subproblem can have an integer * optimal solution which is better than the best known one. * * The check is based on comparison of the local objective bound stored * in the subproblem descriptor and the incumbent objective value which * is the global objective bound. * * If there is a chance that the specified subproblem can have a better * integer optimal solution, the routine returns non-zero. Otherwise, if * the corresponding branch can pruned, zero is returned. */ static int is_branch_hopeful(glp_tree *T, int p) { xassert(1 <= p && p <= T->nslots); xassert(T->slot[p].node != NULL); return ios_is_hopeful(T, T->slot[p].node->bound); } /*********************************************************************** * check_integrality - check integrality of basic solution * * This routine checks if the basic solution of LP relaxation of the * current subproblem satisfies to integrality conditions, i.e. that all * variables of integer kind have integral primal values. (The solution * is assumed to be optimal.) * * For each variable of integer kind the routine computes the following * quantity: * * ii(x[j]) = min(x[j] - floor(x[j]), ceil(x[j]) - x[j]), (1) * * which is a measure of the integer infeasibility (non-integrality) of * x[j] (for example, ii(2.1) = 0.1, ii(3.7) = 0.3, ii(5.0) = 0). It is * understood that 0 <= ii(x[j]) <= 0.5, and variable x[j] is integer * feasible if ii(x[j]) = 0. However, due to floating-point arithmetic * the routine checks less restrictive condition: * * ii(x[j]) <= tol_int, (2) * * where tol_int is a given tolerance (small positive number) and marks * each variable which does not satisfy to (2) as integer infeasible by * setting its fractionality flag. * * In order to characterize integer infeasibility of the basic solution * in the whole the routine computes two parameters: ii_cnt, which is * the number of variables with the fractionality flag set, and ii_sum, * which is the sum of integer infeasibilities (1). */ static void check_integrality(glp_tree *T) { glp_prob *mip = T->mip; int j, type, ii_cnt = 0; double lb, ub, x, temp1, temp2, ii_sum = 0.0; /* walk through the set of columns (structural variables) */ for (j = 1; j <= mip->n; j++) { GLPCOL *col = mip->col[j]; T->non_int[j] = 0; /* if the column is not integer, skip it */ if (col->kind != GLP_IV) continue; /* if the column is non-basic, it is integer feasible */ if (col->stat != GLP_BS) continue; /* obtain the type and bounds of the column */ type = col->type, lb = col->lb, ub = col->ub; /* obtain value of the column in optimal basic solution */ x = col->prim; /* if the column's primal value is close to the lower bound, the column is integer feasible within given tolerance */ if (type == GLP_LO || type == GLP_DB || type == GLP_FX) { temp1 = lb - T->parm->tol_int; temp2 = lb + T->parm->tol_int; if (temp1 <= x && x <= temp2) continue; #if 0 /* the lower bound must not be violated */ xassert(x >= lb); #else if (x < lb) continue; #endif } /* if the column's primal value is close to the upper bound, the column is integer feasible within given tolerance */ if (type == GLP_UP || type == GLP_DB || type == GLP_FX) { temp1 = ub - T->parm->tol_int; temp2 = ub + T->parm->tol_int; if (temp1 <= x && x <= temp2) continue; #if 0 /* the upper bound must not be violated */ xassert(x <= ub); #else if (x > ub) continue; #endif } /* if the column's primal value is close to nearest integer, the column is integer feasible within given tolerance */ temp1 = floor(x + 0.5) - T->parm->tol_int; temp2 = floor(x + 0.5) + T->parm->tol_int; if (temp1 <= x && x <= temp2) continue; /* otherwise the column is integer infeasible */ T->non_int[j] = 1; /* increase the number of fractional-valued columns */ ii_cnt++; /* compute the sum of integer infeasibilities */ temp1 = x - floor(x); temp2 = ceil(x) - x; xassert(temp1 > 0.0 && temp2 > 0.0); ii_sum += (temp1 <= temp2 ? temp1 : temp2); } /* store ii_cnt and ii_sum to the current problem descriptor */ xassert(T->curr != NULL); T->curr->ii_cnt = ii_cnt; T->curr->ii_sum = ii_sum; /* and also display these parameters */ if (T->parm->msg_lev >= GLP_MSG_DBG) { if (ii_cnt == 0) xprintf("There are no fractional columns\n"); else if (ii_cnt == 1) xprintf("There is one fractional column, integer infeasibil" "ity is %.3e\n", ii_sum); else xprintf("There are %d fractional columns, integer infeasibi" "lity is %.3e\n", ii_cnt, ii_sum); } return; } /*********************************************************************** * record_solution - record better integer feasible solution * * This routine records optimal basic solution of LP relaxation of the * current subproblem, which being integer feasible is better than the * best known integer feasible solution. */ static void record_solution(glp_tree *T) { glp_prob *mip = T->mip; int i, j; mip->mip_stat = GLP_FEAS; mip->mip_obj = mip->obj_val; for (i = 1; i <= mip->m; i++) { GLPROW *row = mip->row[i]; row->mipx = row->prim; } for (j = 1; j <= mip->n; j++) { GLPCOL *col = mip->col[j]; if (col->kind == GLP_CV) col->mipx = col->prim; else if (col->kind == GLP_IV) { /* value of the integer column must be integral */ col->mipx = floor(col->prim + 0.5); } else xassert(col != col); } T->sol_cnt++; return; } /*********************************************************************** * fix_by_red_cost - fix non-basic integer columns by reduced costs * * This routine fixes some non-basic integer columns if their reduced * costs indicate that increasing (decreasing) the column at least by * one involves the objective value becoming worse than the incumbent * objective value. */ static void fix_by_red_cost(glp_tree *T) { glp_prob *mip = T->mip; int j, stat, fixed = 0; double obj, lb, ub, dj; /* the global bound must exist */ xassert(T->mip->mip_stat == GLP_FEAS); /* basic solution of LP relaxation must be optimal */ xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS); /* determine the objective function value */ obj = mip->obj_val; /* walk through the column list */ for (j = 1; j <= mip->n; j++) { GLPCOL *col = mip->col[j]; /* if the column is not integer, skip it */ if (col->kind != GLP_IV) continue; /* obtain bounds of j-th column */ lb = col->lb, ub = col->ub; /* and determine its status and reduced cost */ stat = col->stat, dj = col->dual; /* analyze the reduced cost */ switch (mip->dir) { case GLP_MIN: /* minimization */ if (stat == GLP_NL) { /* j-th column is non-basic on its lower bound */ if (dj < 0.0) dj = 0.0; if (obj + dj >= mip->mip_obj) glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++; } else if (stat == GLP_NU) { /* j-th column is non-basic on its upper bound */ if (dj > 0.0) dj = 0.0; if (obj - dj >= mip->mip_obj) glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++; } break; case GLP_MAX: /* maximization */ if (stat == GLP_NL) { /* j-th column is non-basic on its lower bound */ if (dj > 0.0) dj = 0.0; if (obj + dj <= mip->mip_obj) glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++; } else if (stat == GLP_NU) { /* j-th column is non-basic on its upper bound */ if (dj < 0.0) dj = 0.0; if (obj - dj <= mip->mip_obj) glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++; } break; default: xassert(T != T); } } if (T->parm->msg_lev >= GLP_MSG_DBG) { if (fixed == 0) /* nothing to say */; else if (fixed == 1) xprintf("One column has been fixed by reduced cost\n"); else xprintf("%d columns have been fixed by reduced costs\n", fixed); } /* fixing non-basic columns on their current bounds does not change the basic solution */ xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS); return; } /*********************************************************************** * branch_on - perform branching on specified variable * * This routine performs branching on j-th column (structural variable) * of the current subproblem. The specified column must be of integer * kind and must have a fractional value in optimal basic solution of * LP relaxation of the current subproblem (i.e. only columns for which * the flag non_int[j] is set are valid candidates to branch on). * * Let x be j-th structural variable, and beta be its primal fractional * value in the current basic solution. Branching on j-th variable is * dividing the current subproblem into two new subproblems, which are * identical to the current subproblem with the following exception: in * the first subproblem that begins the down-branch x has a new upper * bound x <= floor(beta), and in the second subproblem that begins the * up-branch x has a new lower bound x >= ceil(beta). * * Depending on estimation of local bounds for down- and up-branches * this routine returns the following: * * 0 - both branches have been created; * 1 - one branch is hopeless and has been pruned, so now the current * subproblem is other branch; * 2 - both branches are hopeless and have been pruned; new subproblem * selection is needed to continue the search. */ static int branch_on(glp_tree *T, int j, int next) { glp_prob *mip = T->mip; IOSNPD *node; int m = mip->m; int n = mip->n; int type, dn_type, up_type, dn_bad, up_bad, p, ret, clone[1+2]; double lb, ub, beta, new_ub, new_lb, dn_lp, up_lp, dn_bnd, up_bnd; /* determine bounds and value of x[j] in optimal solution to LP relaxation of the current subproblem */ xassert(1 <= j && j <= n); type = mip->col[j]->type; lb = mip->col[j]->lb; ub = mip->col[j]->ub; beta = mip->col[j]->prim; /* determine new bounds of x[j] for down- and up-branches */ new_ub = floor(beta); new_lb = ceil(beta); switch (type) { case GLP_FR: dn_type = GLP_UP; up_type = GLP_LO; break; case GLP_LO: xassert(lb <= new_ub); dn_type = (lb == new_ub ? GLP_FX : GLP_DB); xassert(lb + 1.0 <= new_lb); up_type = GLP_LO; break; case GLP_UP: xassert(new_ub <= ub - 1.0); dn_type = GLP_UP; xassert(new_lb <= ub); up_type = (new_lb == ub ? GLP_FX : GLP_DB); break; case GLP_DB: xassert(lb <= new_ub && new_ub <= ub - 1.0); dn_type = (lb == new_ub ? GLP_FX : GLP_DB); xassert(lb + 1.0 <= new_lb && new_lb <= ub); up_type = (new_lb == ub ? GLP_FX : GLP_DB); break; default: xassert(type != type); } /* compute local bounds to LP relaxation for both branches */ ios_eval_degrad(T, j, &dn_lp, &up_lp); /* and improve them by rounding */ dn_bnd = ios_round_bound(T, dn_lp); up_bnd = ios_round_bound(T, up_lp); /* check local bounds for down- and up-branches */ dn_bad = !ios_is_hopeful(T, dn_bnd); up_bad = !ios_is_hopeful(T, up_bnd); if (dn_bad && up_bad) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Both down- and up-branches are hopeless\n"); ret = 2; goto done; } else if (up_bad) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Up-branch is hopeless\n"); glp_set_col_bnds(mip, j, dn_type, lb, new_ub); T->curr->lp_obj = dn_lp; if (mip->dir == GLP_MIN) { if (T->curr->bound < dn_bnd) T->curr->bound = dn_bnd; } else if (mip->dir == GLP_MAX) { if (T->curr->bound > dn_bnd) T->curr->bound = dn_bnd; } else xassert(mip != mip); ret = 1; goto done; } else if (dn_bad) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Down-branch is hopeless\n"); glp_set_col_bnds(mip, j, up_type, new_lb, ub); T->curr->lp_obj = up_lp; if (mip->dir == GLP_MIN) { if (T->curr->bound < up_bnd) T->curr->bound = up_bnd; } else if (mip->dir == GLP_MAX) { if (T->curr->bound > up_bnd) T->curr->bound = up_bnd; } else xassert(mip != mip); ret = 1; goto done; } /* both down- and up-branches seem to be hopeful */ if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Branching on column %d, primal value is %.9e\n", j, beta); /* determine the reference number of the current subproblem */ xassert(T->curr != NULL); p = T->curr->p; T->curr->br_var = j; T->curr->br_val = beta; /* freeze the current subproblem */ ios_freeze_node(T); /* create two clones of the current subproblem; the first clone begins the down-branch, the second one begins the up-branch */ ios_clone_node(T, p, 2, clone); if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Node %d begins down branch, node %d begins up branch " "\n", clone[1], clone[2]); /* set new upper bound of j-th column in the down-branch */ node = T->slot[clone[1]].node; xassert(node != NULL); xassert(node->up != NULL); xassert(node->b_ptr == NULL); node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND)); node->b_ptr->k = m + j; node->b_ptr->type = (unsigned char)dn_type; node->b_ptr->lb = lb; node->b_ptr->ub = new_ub; node->b_ptr->next = NULL; node->lp_obj = dn_lp; if (mip->dir == GLP_MIN) { if (node->bound < dn_bnd) node->bound = dn_bnd; } else if (mip->dir == GLP_MAX) { if (node->bound > dn_bnd) node->bound = dn_bnd; } else xassert(mip != mip); /* set new lower bound of j-th column in the up-branch */ node = T->slot[clone[2]].node; xassert(node != NULL); xassert(node->up != NULL); xassert(node->b_ptr == NULL); node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND)); node->b_ptr->k = m + j; node->b_ptr->type = (unsigned char)up_type; node->b_ptr->lb = new_lb; node->b_ptr->ub = ub; node->b_ptr->next = NULL; node->lp_obj = up_lp; if (mip->dir == GLP_MIN) { if (node->bound < up_bnd) node->bound = up_bnd; } else if (mip->dir == GLP_MAX) { if (node->bound > up_bnd) node->bound = up_bnd; } else xassert(mip != mip); /* suggest the subproblem to be solved next */ xassert(T->child == 0); if (next == GLP_NO_BRNCH) T->child = 0; else if (next == GLP_DN_BRNCH) T->child = clone[1]; else if (next == GLP_UP_BRNCH) T->child = clone[2]; else xassert(next != next); ret = 0; done: return ret; } /*********************************************************************** * cleanup_the_tree - prune hopeless branches from the tree * * This routine walks through the active list and checks the local * bound for every active subproblem. If the local bound indicates that * the subproblem cannot have integer optimal solution better than the * incumbent objective value, the routine deletes such subproblem that, * in turn, involves pruning the corresponding branch of the tree. */ static void cleanup_the_tree(glp_tree *T) { IOSNPD *node, *next_node; int count = 0; /* the global bound must exist */ xassert(T->mip->mip_stat == GLP_FEAS); /* walk through the list of active subproblems */ for (node = T->head; node != NULL; node = next_node) { /* deleting some active problem node may involve deleting its parents recursively; however, all its parents being created *before* it are always *precede* it in the node list, so the next problem node is never affected by such deletion */ next_node = node->next; /* if the branch is hopeless, prune it */ if (!is_branch_hopeful(T, node->p)) ios_delete_node(T, node->p), count++; } if (T->parm->msg_lev >= GLP_MSG_DBG) { if (count == 1) xprintf("One hopeless branch has been pruned\n"); else if (count > 1) xprintf("%d hopeless branches have been pruned\n", count); } return; } /**********************************************************************/ static void generate_cuts(glp_tree *T) { /* generate generic cuts with built-in generators */ if (!(T->parm->mir_cuts == GLP_ON || T->parm->gmi_cuts == GLP_ON || T->parm->cov_cuts == GLP_ON || T->parm->clq_cuts == GLP_ON)) goto done; #if 1 /* 20/IX-2008 */ { int i, max_cuts, added_cuts; max_cuts = T->n; if (max_cuts < 1000) max_cuts = 1000; added_cuts = 0; for (i = T->orig_m+1; i <= T->mip->m; i++) { if (T->mip->row[i]->origin == GLP_RF_CUT) added_cuts++; } /* xprintf("added_cuts = %d\n", added_cuts); */ if (added_cuts >= max_cuts) goto done; } #endif /* generate and add to POOL all cuts violated by x* */ if (T->parm->gmi_cuts == GLP_ON) { if (T->curr->changed < 5) ios_gmi_gen(T); } if (T->parm->mir_cuts == GLP_ON) { xassert(T->mir_gen != NULL); ios_mir_gen(T, T->mir_gen); } if (T->parm->cov_cuts == GLP_ON) { /* cover cuts works well along with mir cuts */ /*if (T->round <= 5)*/ ios_cov_gen(T); } if (T->parm->clq_cuts == GLP_ON) { if (T->clq_gen != NULL) { if (T->curr->level == 0 && T->curr->changed < 50 || T->curr->level > 0 && T->curr->changed < 5) ios_clq_gen(T, T->clq_gen); } } done: return; } /**********************************************************************/ static void remove_cuts(glp_tree *T) { /* remove inactive cuts (some valueable globally valid cut might be saved in the global cut pool) */ int i, cnt = 0, *num = NULL; xassert(T->curr != NULL); for (i = T->orig_m+1; i <= T->mip->m; i++) { if (T->mip->row[i]->origin == GLP_RF_CUT && T->mip->row[i]->level == T->curr->level && T->mip->row[i]->stat == GLP_BS) { if (num == NULL) num = xcalloc(1+T->mip->m, sizeof(int)); num[++cnt] = i; } } if (cnt > 0) { glp_del_rows(T->mip, cnt, num); #if 0 xprintf("%d inactive cut(s) removed\n", cnt); #endif xfree(num); xassert(glp_factorize(T->mip) == 0); } return; } /**********************************************************************/ static void display_cut_info(glp_tree *T) { glp_prob *mip = T->mip; int i, gmi = 0, mir = 0, cov = 0, clq = 0, app = 0; for (i = mip->m; i > 0; i--) { GLPROW *row; row = mip->row[i]; /* if (row->level < T->curr->level) break; */ if (row->origin == GLP_RF_CUT) { if (row->klass == GLP_RF_GMI) gmi++; else if (row->klass == GLP_RF_MIR) mir++; else if (row->klass == GLP_RF_COV) cov++; else if (row->klass == GLP_RF_CLQ) clq++; else app++; } } xassert(T->curr != NULL); if (gmi + mir + cov + clq + app > 0) { xprintf("Cuts on level %d:", T->curr->level); if (gmi > 0) xprintf(" gmi = %d;", gmi); if (mir > 0) xprintf(" mir = %d;", mir); if (cov > 0) xprintf(" cov = %d;", cov); if (clq > 0) xprintf(" clq = %d;", clq); if (app > 0) xprintf(" app = %d;", app); xprintf("\n"); } return; } /*********************************************************************** * NAME * * ios_driver - branch-and-cut driver * * SYNOPSIS * * #include "glpios.h" * int ios_driver(glp_tree *T); * * DESCRIPTION * * The routine ios_driver is a branch-and-cut driver. It controls the * MIP solution process. * * RETURNS * * 0 The MIP problem instance has been successfully solved. This code * does not necessarily mean that the solver has found optimal * solution. It only means that the solution process was successful. * * GLP_EFAIL * The search was prematurely terminated due to the solver failure. * * GLP_EMIPGAP * The search was prematurely terminated, because the relative mip * gap tolerance has been reached. * * GLP_ETMLIM * The search was prematurely terminated, because the time limit has * been exceeded. * * GLP_ESTOP * The search was prematurely terminated by application. */ int ios_driver(glp_tree *T) { int p, curr_p, p_stat, d_stat, ret; #if 1 /* carry out to glp_tree */ int pred_p = 0; /* if the current subproblem has been just created due to branching, pred_p is the reference number of its parent subproblem, otherwise pred_p is zero */ #endif glp_long ttt = T->tm_beg; #if 0 ((glp_iocp *)T->parm)->msg_lev = GLP_MSG_DBG; #endif /* on entry to the B&B driver it is assumed that the active list contains the only active (i.e. root) subproblem, which is the original MIP problem to be solved */ loop: /* main loop starts here */ /* at this point the current subproblem does not exist */ xassert(T->curr == NULL); /* if the active list is empty, the search is finished */ if (T->head == NULL) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Active list is empty!\n"); xassert(dmp_in_use(T->pool).lo == 0); ret = 0; goto done; } /* select some active subproblem to continue the search */ xassert(T->next_p == 0); /* let the application program select subproblem */ if (T->parm->cb_func != NULL) { xassert(T->reason == 0); T->reason = GLP_ISELECT; T->parm->cb_func(T, T->parm->cb_info); T->reason = 0; if (T->stop) { ret = GLP_ESTOP; goto done; } } if (T->next_p != 0) { /* the application program has selected something */ ; } else if (T->a_cnt == 1) { /* the only active subproblem exists, so select it */ xassert(T->head->next == NULL); T->next_p = T->head->p; } else if (T->child != 0) { /* select one of branching childs suggested by the branching heuristic */ T->next_p = T->child; } else { /* select active subproblem as specified by the backtracking technique option */ T->next_p = ios_choose_node(T); } /* the active subproblem just selected becomes current */ ios_revive_node(T, T->next_p); T->next_p = T->child = 0; /* invalidate pred_p, if it is not the reference number of the parent of the current subproblem */ if (T->curr->up != NULL && T->curr->up->p != pred_p) pred_p = 0; /* determine the reference number of the current subproblem */ p = T->curr->p; if (T->parm->msg_lev >= GLP_MSG_DBG) { xprintf("-----------------------------------------------------" "-------------------\n"); xprintf("Processing node %d at level %d\n", p, T->curr->level); } /* if it is the root subproblem, initialize cut generators */ if (p == 1) { if (T->parm->gmi_cuts == GLP_ON) { if (T->parm->msg_lev >= GLP_MSG_ALL) xprintf("Gomory's cuts enabled\n"); } if (T->parm->mir_cuts == GLP_ON) { if (T->parm->msg_lev >= GLP_MSG_ALL) xprintf("MIR cuts enabled\n"); xassert(T->mir_gen == NULL); T->mir_gen = ios_mir_init(T); } if (T->parm->cov_cuts == GLP_ON) { if (T->parm->msg_lev >= GLP_MSG_ALL) xprintf("Cover cuts enabled\n"); } if (T->parm->clq_cuts == GLP_ON) { xassert(T->clq_gen == NULL); if (T->parm->msg_lev >= GLP_MSG_ALL) xprintf("Clique cuts enabled\n"); T->clq_gen = ios_clq_init(T); } } more: /* minor loop starts here */ /* at this point the current subproblem needs either to be solved for the first time or re-optimized due to reformulation */ /* display current progress of the search */ if (T->parm->msg_lev >= GLP_MSG_DBG || T->parm->msg_lev >= GLP_MSG_ON && (double)(T->parm->out_frq - 1) <= 1000.0 * xdifftime(xtime(), T->tm_lag)) show_progress(T, 0); if (T->parm->msg_lev >= GLP_MSG_ALL && xdifftime(xtime(), ttt) >= 60.0) { glp_long total; glp_mem_usage(NULL, NULL, &total, NULL); xprintf("Time used: %.1f secs. Memory used: %.1f Mb.\n", xdifftime(xtime(), T->tm_beg), xltod(total) / 1048576.0); ttt = xtime(); } /* check the mip gap */ if (T->parm->mip_gap > 0.0 && ios_relative_gap(T) <= T->parm->mip_gap) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Relative gap tolerance reached; search terminated " "\n"); ret = GLP_EMIPGAP; goto done; } /* check if the time limit has been exhausted */ if (T->parm->tm_lim < INT_MAX && (double)(T->parm->tm_lim - 1) <= 1000.0 * xdifftime(xtime(), T->tm_beg)) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Time limit exhausted; search terminated\n"); ret = GLP_ETMLIM; goto done; } /* let the application program preprocess the subproblem */ if (T->parm->cb_func != NULL) { xassert(T->reason == 0); T->reason = GLP_IPREPRO; T->parm->cb_func(T, T->parm->cb_info); T->reason = 0; if (T->stop) { ret = GLP_ESTOP; goto done; } } /* perform basic preprocessing */ if (T->parm->pp_tech == GLP_PP_NONE) ; else if (T->parm->pp_tech == GLP_PP_ROOT) { if (T->curr->level == 0) { if (ios_preprocess_node(T, 100)) goto fath; } } else if (T->parm->pp_tech == GLP_PP_ALL) { if (ios_preprocess_node(T, T->curr->level == 0 ? 100 : 10)) goto fath; } else xassert(T != T); /* preprocessing may improve the global bound */ if (!is_branch_hopeful(T, p)) { xprintf("*** not tested yet ***\n"); goto fath; } /* solve LP relaxation of the current subproblem */ if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Solving LP relaxation...\n"); ret = ios_solve_node(T); if (!(ret == 0 || ret == GLP_EOBJLL || ret == GLP_EOBJUL)) { if (T->parm->msg_lev >= GLP_MSG_ERR) xprintf("ios_driver: unable to solve current LP relaxation;" " glp_simplex returned %d\n", ret); ret = GLP_EFAIL; goto done; } /* analyze status of the basic solution to LP relaxation found */ p_stat = T->mip->pbs_stat; d_stat = T->mip->dbs_stat; if (p_stat == GLP_FEAS && d_stat == GLP_FEAS) { /* LP relaxation has optimal solution */ if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Found optimal solution to LP relaxation\n"); } else if (d_stat == GLP_NOFEAS) { /* LP relaxation has no dual feasible solution */ /* since the current subproblem cannot have a larger feasible region than its parent, there is something wrong */ if (T->parm->msg_lev >= GLP_MSG_ERR) xprintf("ios_driver: current LP relaxation has no dual feas" "ible solution\n"); ret = GLP_EFAIL; goto done; } else if (p_stat == GLP_INFEAS && d_stat == GLP_FEAS) { /* LP relaxation has no primal solution which is better than the incumbent objective value */ xassert(T->mip->mip_stat == GLP_FEAS); if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("LP relaxation has no solution better than incumben" "t objective value\n"); /* prune the branch */ goto fath; } else if (p_stat == GLP_NOFEAS) { /* LP relaxation has no primal feasible solution */ if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("LP relaxation has no feasible solution\n"); /* prune the branch */ goto fath; } else { /* other cases cannot appear */ xassert(T->mip != T->mip); } /* at this point basic solution to LP relaxation of the current subproblem is optimal */ xassert(p_stat == GLP_FEAS && d_stat == GLP_FEAS); xassert(T->curr != NULL); T->curr->lp_obj = T->mip->obj_val; /* thus, it defines a local bound to integer optimal solution of the current subproblem */ { double bound = T->mip->obj_val; /* some local bound to the current subproblem could be already set before, so we should only improve it */ bound = ios_round_bound(T, bound); if (T->mip->dir == GLP_MIN) { if (T->curr->bound < bound) T->curr->bound = bound; } else if (T->mip->dir == GLP_MAX) { if (T->curr->bound > bound) T->curr->bound = bound; } else xassert(T->mip != T->mip); if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Local bound is %.9e\n", bound); } /* if the local bound indicates that integer optimal solution of the current subproblem cannot be better than the global bound, prune the branch */ if (!is_branch_hopeful(T, p)) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Current branch is hopeless and can be pruned\n"); goto fath; } /* let the application program generate additional rows ("lazy" constraints) */ xassert(T->reopt == 0); xassert(T->reinv == 0); if (T->parm->cb_func != NULL) { xassert(T->reason == 0); T->reason = GLP_IROWGEN; T->parm->cb_func(T, T->parm->cb_info); T->reason = 0; if (T->stop) { ret = GLP_ESTOP; goto done; } if (T->reopt) { /* some rows were added; re-optimization is needed */ T->reopt = T->reinv = 0; goto more; } if (T->reinv) { /* no rows were added, however, some inactive rows were removed */ T->reinv = 0; xassert(glp_factorize(T->mip) == 0); } } /* check if the basic solution is integer feasible */ check_integrality(T); /* if the basic solution satisfies to all integrality conditions, it is a new, better integer feasible solution */ if (T->curr->ii_cnt == 0) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("New integer feasible solution found\n"); if (T->parm->msg_lev >= GLP_MSG_ALL) display_cut_info(T); record_solution(T); if (T->parm->msg_lev >= GLP_MSG_ON) show_progress(T, 1); /* make the application program happy */ if (T->parm->cb_func != NULL) { xassert(T->reason == 0); T->reason = GLP_IBINGO; T->parm->cb_func(T, T->parm->cb_info); T->reason = 0; if (T->stop) { ret = GLP_ESTOP; goto done; } } /* since the current subproblem has been fathomed, prune its branch */ goto fath; } /* at this point basic solution to LP relaxation of the current subproblem is optimal, but integer infeasible */ /* try to fix some non-basic structural variables of integer kind on their current bounds due to reduced costs */ if (T->mip->mip_stat == GLP_FEAS) fix_by_red_cost(T); /* let the application program try to find some solution to the original MIP with a primal heuristic */ if (T->parm->cb_func != NULL) { xassert(T->reason == 0); T->reason = GLP_IHEUR; T->parm->cb_func(T, T->parm->cb_info); T->reason = 0; if (T->stop) { ret = GLP_ESTOP; goto done; } /* check if the current branch became hopeless */ if (!is_branch_hopeful(T, p)) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Current branch became hopeless and can be prune" "d\n"); goto fath; } } /* try to find solution with the feasibility pump heuristic */ if (T->parm->fp_heur) { xassert(T->reason == 0); T->reason = GLP_IHEUR; ios_feas_pump(T); T->reason = 0; /* check if the current branch became hopeless */ if (!is_branch_hopeful(T, p)) { if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Current branch became hopeless and can be prune" "d\n"); goto fath; } } /* it's time to generate cutting planes */ xassert(T->local != NULL); xassert(T->local->size == 0); /* let the application program generate some cuts; note that it can add cuts either to the local cut pool or directly to the current subproblem */ if (T->parm->cb_func != NULL) { xassert(T->reason == 0); T->reason = GLP_ICUTGEN; T->parm->cb_func(T, T->parm->cb_info); T->reason = 0; if (T->stop) { ret = GLP_ESTOP; goto done; } } /* try to generate generic cuts with built-in generators (as suggested by Matteo Fischetti et al. the built-in cuts are not generated at each branching node; an intense attempt of generating new cuts is only made at the root node, and then a moderate effort is spent after each backtracking step) */ if (T->curr->level == 0 || pred_p == 0) { xassert(T->reason == 0); T->reason = GLP_ICUTGEN; generate_cuts(T); T->reason = 0; } /* if the local cut pool is not empty, select useful cuts and add them to the current subproblem */ if (T->local->size > 0) { xassert(T->reason == 0); T->reason = GLP_ICUTGEN; ios_process_cuts(T); T->reason = 0; } /* clear the local cut pool */ ios_clear_pool(T, T->local); /* perform re-optimization, if necessary */ if (T->reopt) { T->reopt = 0; T->curr->changed++; goto more; } /* no cuts were generated; remove inactive cuts */ remove_cuts(T); if (T->parm->msg_lev >= GLP_MSG_ALL && T->curr->level == 0) display_cut_info(T); /* update history information used on pseudocost branching */ if (T->pcost != NULL) ios_pcost_update(T); /* it's time to perform branching */ xassert(T->br_var == 0); xassert(T->br_sel == 0); /* let the application program choose variable to branch on */ if (T->parm->cb_func != NULL) { xassert(T->reason == 0); xassert(T->br_var == 0); xassert(T->br_sel == 0); T->reason = GLP_IBRANCH; T->parm->cb_func(T, T->parm->cb_info); T->reason = 0; if (T->stop) { ret = GLP_ESTOP; goto done; } } /* if nothing has been chosen, choose some variable as specified by the branching technique option */ if (T->br_var == 0) T->br_var = ios_choose_var(T, &T->br_sel); /* perform actual branching */ curr_p = T->curr->p; ret = branch_on(T, T->br_var, T->br_sel); T->br_var = T->br_sel = 0; if (ret == 0) { /* both branches have been created */ pred_p = curr_p; goto loop; } else if (ret == 1) { /* one branch is hopeless and has been pruned, so now the current subproblem is other branch */ /* the current subproblem should be considered as a new one, since one bound of the branching variable was changed */ T->curr->solved = T->curr->changed = 0; goto more; } else if (ret == 2) { /* both branches are hopeless and have been pruned; new subproblem selection is needed to continue the search */ goto fath; } else xassert(ret != ret); fath: /* the current subproblem has been fathomed */ if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("Node %d fathomed\n", p); /* freeze the current subproblem */ ios_freeze_node(T); /* and prune the corresponding branch of the tree */ ios_delete_node(T, p); /* if a new integer feasible solution has just been found, other branches may become hopeless and therefore must be pruned */ if (T->mip->mip_stat == GLP_FEAS) cleanup_the_tree(T); /* new subproblem selection is needed due to backtracking */ pred_p = 0; goto loop; done: /* display progress of the search on exit from the solver */ if (T->parm->msg_lev >= GLP_MSG_ON) show_progress(T, 0); if (T->mir_gen != NULL) ios_mir_term(T->mir_gen), T->mir_gen = NULL; if (T->clq_gen != NULL) ios_clq_term(T->clq_gen), T->clq_gen = NULL; /* return to the calling program */ return ret; } /* eof */ igraph/src/bignum.c0000644000176000001440000012375512325527072014012 0ustar ripleyusers/****************************************************************************** * bn.c - big number math implementation * * Copyright (c) 2004 by Juergen Buchmueller * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * * $Id: bignum.c,v 1.17 2005/07/23 02:55:53 pullmoll Exp $ ******************************************************************************/ #include #include "bignum.h" #include "config.h" #include "math.h" #include "igraph_error.h" #ifndef ASM_X86 #ifdef X86 #define ASM_X86 1 #endif #endif /** * @brief Return hex representation of a big number * * Returns the hex representation of a[], * where a is a big number integer with nlimb limbs. * * @param a pointer to an array of limbs * @param nlimb number of limbs in the array * * @result string containing the hex representation of a */ const char *bn2x(limb_t *a, count_t nlimb) { static IGRAPH_THREAD_LOCAL count_t which = 0; static IGRAPH_THREAD_LOCAL char *xbuff[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; char *dst; count_t size; count_t n = nlimb; if (0 == n) return "0"; which = (which + 1) % 8; size = 8 * n + 1; if (NULL != xbuff[which]) free(xbuff[which]); dst = xbuff[which] = calloc(size, sizeof(char)); if (NULL == dst) return "memory error"; while (n-- > 0) { dst += snprintf(dst, size, "%08x", a[n]); size -= 8; } return xbuff[which]; } /** * @brief Return decimal representation of a big number * * Returns the decimal representation of a[], * where a is a big number integer with nlimb limbs. * * @param a pointer to an array of limbs * @param nlimb number of limbs in the array * * @result string containing the decimal representation of a */ const char *bn2d(limb_t *a, count_t nlimb) { static IGRAPH_THREAD_LOCAL count_t which = 0; static IGRAPH_THREAD_LOCAL char *dbuff[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static IGRAPH_THREAD_LOCAL limb_t v[BN_MAXSIZE]; limb_t r; char *dst; count_t size; count_t n = bn_sizeof(a, nlimb); if (0 == n) return "0"; bn_copy(v, a, n); which = (which + 1) % 8; size = 12 * n + 1; if (NULL != dbuff[which]) free(dbuff[which]); dst = dbuff[which] = calloc(size, sizeof(char)); if (NULL == dst) return "memory error"; size--; while (0 != bn_cmp_limb(v, 0, n)) { r = bn_div_limb(v, v, 10, n); dst[--size] = '0' + (char) r; } return &dst[size]; } /** * @brief Return decimal representation of a big number pair * * Returns the decimal representation of a[].b[], * where a is a big number integer with alimb limbs, * and b is a multiprecision fixed fraction with blimb limbs. * * @param a pointer to an array of limbs * @param alimb number of limbs in the a array * @param b pointer to an array of limbs * @param blimb number of limbs in the b array * * @result string containing the decimal representation of a.b */ const char *bn2f(limb_t *a, count_t alimb, limb_t *b, count_t blimb) { static IGRAPH_THREAD_LOCAL count_t which = 0; static IGRAPH_THREAD_LOCAL char *dbuff[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static IGRAPH_THREAD_LOCAL limb_t v[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t w[BN_MAXSIZE]; limb_t r; char *dst; count_t size; bn_copy(v, a, alimb); bn_copy(w, b, blimb); which = (which + 1) % 8; size = 12 * (alimb + blimb) + 1 + 1; if (NULL != dbuff[which]) free(dbuff[which]); dst = dbuff[which] = calloc(size, sizeof(char)); if (NULL == dst) return "memory error"; size = 12 * alimb; while (0 != bn_cmp_limb(w, 0, blimb) && size < 12 * (alimb + blimb)) { r = bn_mul_limb(w, w, 10, blimb); dst[size++] = '0' + (char) r; } size = 12 * alimb; dst[size] = '.'; while (0 != bn_cmp_limb(v, 0, alimb) && size > 0) { r = bn_div_limb(v, v, 10, alimb); dst[--size] = '0' + (char) r; } return &dst[size]; } /** * @brief Return binary representation of a big number * * Returns the binary representation of a[], * where a is a big number integer with nlimb limbs. * * @param a pointer to an array of limbs * @param nlimb number of limbs in the array * * @result string containing the binary representation of a */ const char *bn2b(limb_t *a, count_t nlimb) { static IGRAPH_THREAD_LOCAL count_t which = 0; static IGRAPH_THREAD_LOCAL char *bbuff[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; limb_t r; char *dst; count_t size; count_t n = bn_sizeof(a, nlimb); if (0 == n) return "0"; which = (which + 1) % 8; size = LIMBBITS * n + 1; if (NULL != bbuff[which]) free(bbuff[which]); dst = bbuff[which] = calloc(size, sizeof(char)); if (NULL == dst) return "memory error"; n = 0; size--; while (size-- > 0) { r = (a[n/LIMBBITS] >> (n%LIMBBITS)) & 1; n++; dst[size] = '0' + (char) r; } return &dst[size]; } /** * @brief Zero an array of limbs * * Sets a[] = 0 * where a is a big number integer of nlimb limbs. * * @param a pointer to an array of limbs * @param nlimb number of limbs in the array * */ void bn_zero(limb_t a[], count_t nlimb) { memset(a, 0, nlimb * sizeof(limb_t)); } /** * @brief Set an array of limbs to a single limb value * * Sets a[] = d * where a is a big number integer of nlimb limbs, * and d is a single limb * * @param a pointer to an array of limbs to set * @param d limb value to set a to * @param nlimb number of limbs in the array * */ void bn_limb(limb_t a[], limb_t d, count_t nlimb) { memset(a, 0, nlimb * sizeof(limb_t)); a[0] = d; } /** * @brief Copy an array of limbs * * Sets a[] = b[] * where a and b are a big number integers of nlimb limbs * * @param a pointer to an array of limbs (destination) * @param b pointer to an array of limbs (source) * @param nlimb number of limbs in the arrays */ void bn_copy(limb_t a[], limb_t b[], count_t nlimb) { memcpy(a, b, nlimb * sizeof(limb_t)); } /** * @brief Return significant size of a big number * * Returns size of significant limbs in a[] * i.e. searches for the first non-zero limb from * nlimb-1 downto 0. * * @param a pointer to an array of limbs (candidate) * @param nlimb number of limbs in the arrays * * @result number of significant limbs in a */ count_t bn_sizeof(limb_t a[], count_t nlimb) { while (nlimb-- > 0) if (0 != a[nlimb]) return ++nlimb; return 0; } /** * @brief Return sign of a bignum minus a limb * * Returns the sign of (a[] - b) * where a is a big number integer of nlimb limbs, * and b is a single limb + * @param a pointer to an array of limbs (minuend) * @param b a single limb (subtrahend) * @param nlimb number of limbs in the array a * * @result sign of the comparison: -1 ab */ int bn_cmp_limb(limb_t a[], limb_t b, count_t nlimb) { if (0 == nlimb) return 0; while (nlimb-- > 1) if (0 != a[nlimb]) return +1; if (a[0] < b) return -1; if (a[0] > b) return +1; return 0; } /** * @brief Return sign of bignum a minus bignum b * * Returns the sign of (a[] - b[]) * where a and b are a big number integers of nlimb limbs * * @param a pointer to an array of limbs (minuend) * @param b pointer to an array of limbs (subtrahend) * @param nlimb number of limbs in the arrays * * @result sign of the comparison: -1 ab */ int bn_cmp(limb_t a[], limb_t b[], count_t nlimb) { if (0 == nlimb) return 0; while (nlimb-- > 0) { if (a[nlimb] > b[nlimb]) return +1; /* GT */ if (a[nlimb] < b[nlimb]) return -1; /* LT */ } return 0; /* EQ */ } /** * @brief Single limb is even test * * Returns 1 if a is even, else 0 * where a is a single limb * * @param a a single limb * * @result zero if a is odd, 1 if a is even */ int sl_iseven(limb_t a) { return (a & 1) ? 0 : 1; } /** * @brief bignum is even test * * Returns 1 if a[] is even, else 0 * where a is a big number integer of nlimb limbs * Note: a zero limb big number integer is even! * * @param a pointer to an array of limbs * @param nlimb number of limbs in the arrays * * @result zero if a is odd, 1 if a is even */ int bn_iseven(limb_t *a, count_t nlimb) { if (0 == nlimb) return 1; return (a[0] & 1) ? 0 : 1; } /** * @brief Add a single limb to a bignum * * Computes w[] = u[] + v * where w, u are big number integers of nlimb lims each, * and v is a single limb. * Returns carry if the addition overflows. * * Ref: Derived from Knuth Algorithm A. * * @param w pointer to an array of limbs receiving result * @param u pointer to an array of limbs (addend 1) * @param v a single limb * @param nlimb number of limbs in the arrays w and u * * @result The carry status of the addition */ limb_t bn_add_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb) { limb_t carry; count_t j; /* Copy u to w, so we can bail out if no borrow is left */ if (w != u) bn_copy(w, u, nlimb); /* Add v to first limb of u */ w[0] += v; carry = (w[0] < v ? 1 : 0); /* Add carry to subsequent limbs */ for (j = 1; 0 != carry && j < nlimb; j++) { w[j] += carry; carry = (w[j] < carry ? 1 : 0); } return carry; } /** * @brief Subtract a single limb from a bignum * * Computes w[] = u[] - v * where w, u are big number integers of nlimb limbs each, * and v is a single limb. * Returns borrow (0 if u >= v, or 1 if v > u). * * Ref: Derived from Knuth Algorithm S. * * @param w pointer to an array of limbs receiving the result * @param u pointer to an array of limbs (minuend) * @param v single limb (subtrahend) * @param nlimb number of limbs in the arrays * * @result borrow of the subtraction (0 if u >= v, 1 if u < v) */ limb_t bn_sub_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb) { limb_t borrow; count_t j; /* Copy u to w, so we can bail out if no borrow is left */ if (w != u) bn_copy(w, u, nlimb); /* Subtract v from first limb of u */ w[0] -= v; borrow = (w[0] > ~v ? 1 : 0); /* Subtract borrow from subsequent limbs */ for (j = 1; 0 != borrow && j < nlimb; j++) { w[j] -= borrow; borrow = (w[j] > ~borrow ? 1 : 0); } return borrow; } /** * @brief Divide a bignum by a single limb * * Computes quotient q[] = u[] / v * and returns remainder r = u[] % v * where q, u are big number integers of nlimb limbs each, * and v is a single limb. * * Makes no assumptions about normalisation. * * Ref: Knuth Vol 2 Ch 4.3.1 Exercise 16 p625 * * @param q pointer to an array of limbs receiving the quotient * @param u pointer to an array of limbs (dividend) * @param v single limb (divisor) * @param nlimb number of limbs in the arrays * * @result single limb remainder of the division (modulo) */ limb_t bn_div_limb(limb_t q[], limb_t u[], limb_t v, count_t nlimb) { count_t j; limb_t t[2], r; count_t shift; if (0 == nlimb) return 0; if (0 == v) return LIMBMASK; /* Divide by zero error */ /* * Normalize first: * qequires high bit of V to be set, * so find most significant by shifting * until DIGMSB is set. */ for (shift = 0; 0 == (v & DIGMSB); shift++) v <<= 1; r = bn_shl(q, u, shift, nlimb); j = nlimb; while (j-- > 0) { t[0] = q[j]; t[1] = r; sl_div(&q[j], &r, t, v); } /* Unnormalize */ r >>= shift; return r; } /** * @brief Modulo a bignum by a single limb * * Computes remainder (modulo) r = u[] mod v * Computes r = u[] mod v * where u is a big number integer of nlimb * and r, v are single precision limbs * * Use remainder from divide function. * * @param u pointer to an array of limbs (dividend) * @param v single limb (divisor) * @param nlimb number of limbs in the arrays * * @result single limb remainder of the division (modulo) */ limb_t bn_mod_limb(limb_t u[], limb_t v, count_t nlimb) { static IGRAPH_THREAD_LOCAL limb_t q[2*BN_MAXSIZE]; limb_t r; r = bn_div_limb(q, u, v, nlimb); bn_zero(q, nlimb); return r; } /** * @brief Multiply a bignum by a single limb * * Computes product w[] = u[] * v * Returns overflow k * where w, u are big number integers of nlimb each * and v is a single limb * * @param w pointer to an array of limbs to receive the result * @param u pointer to an array of limbs (factor) * @param v single limb (other factor) * @param nlimb number of limbs in the arrays * * @result zero if no overflow, else overflow (value of w[nlimb]) */ limb_t bn_mul_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb) { limb_t t[2]; limb_t carry; count_t j; if (0 == v) { bn_zero(w, nlimb); return 0; } for (j = 0, carry = 0; j < nlimb; j++) { sl_mul(t, u[j], v); w[j] = t[0] + carry; carry = t[1] + (w[j] < carry ? 1 : 0); } return carry; } #if HAVE_U64 /** * @brief Computes quotient and remainder of 64 bit / 32 bit * * Computes quotient q = u[] / v, remainder r = u[] mod v * where u[] is a double limb. * * With native support for double limb division * * @param q pointer to the limb to receive the quotient * @param r pointer to the limb to receive the remainder * @param u pointer to an array of two limbs * @param v single limb divisor * * @result zero on success */ limb_t sl_div(limb_t *q, limb_t *r, limb_t u[2], limb_t v) { #if ASM_X86 limb_t qq; limb_t rr; if (0 == v) /* division by zero */ return LIMBMASK; asm volatile( "divl %4" : "=a"(qq), "=d"(rr) : "a"(u[0]), "d"(u[1]), "g"(v)); *q = qq; *r = rr; #else dlimb_t dd; if (0 == v) /* division by zero */ return LIMBMASK; dd = ((dlimb_t)u[1] << LIMBBITS) | u[0]; *q = (limb_t) (dd / v); *r = dd % v; #endif return 0; } #else #define B (HALFMASK + 1) /** * @brief Computes quotient and remainder of 64 bit / 32 bit * * Computes quotient q = u / v, remainder r = u mod v * where u is a double limb * and q, v, r are single precision limbs. * Returns high limb of quotient (max value is 1) * Assumes normalized such that v1 >= b/2 * where b is size of HALF_DIGIT * i.e. the most significant bit of v should be one * * In terms of half-limbs in Knuth notation: * (q2q1q0) = (u4u3u2u1u0) / (v1v0) * (r1r0) = (u4u3u2u1u0) % (v1v0) * for m = 2, n = 2 where u4 = 0 * * We set q = (q1q0) and return q2 as "overflow' * Returned q2 is either 0 or 1. * * @param q pointer to the limb to receive the quotient * @param r pointer to the limb to receive the remainder * @param u pointer to an array of two limbs * @param v single limb divisor * * @result zero on success */ limb_t sl_div(limb_t *q, limb_t *r, limb_t u[2], limb_t v) { limb_t quot; limb_t rem; limb_t ul; limb_t uh; limb_t p0; limb_t p1; limb_t v0; limb_t v1; limb_t u0; limb_t u1; limb_t u2; limb_t u3; limb_t borrow; limb_t q1; limb_t q2; limb_t s; limb_t t; /* Check for normalisation */ if (0 == (v & DIGMSB)) { *q = *r = 0; return LIMBMASK; } /* Split up into half-limbs */ v0 = LSH(v); v1 = MSH(v); u0 = LSH(u[0]); u1 = MSH(u[0]); u2 = LSH(u[1]); u3 = MSH(u[1]); /* Do three rounds of Knuth Algorithm D Vol 2 p272 */ /* * ROUND 1 calculate q2: * estimate quot = (u4u3)/v1 = 0 or 1, * then set (u4u3u2) -= quot*(v1v0) where u4 = 0. */ quot = u3 / v1; if (quot > 0) { rem = u3 - quot * v1; t = SHL(rem) | u2; if (quot * v0 > t) quot--; } uh = 0; /* (u4) */ ul = u[1]; /* (u3u2) */ if (quot > 0) { /* (u4u3u2) -= quot*(v1v0) where u4 = 0 */ p0 = quot * v0; p1 = quot * v1; s = p0 + SHL(p1); ul -= s; borrow = (ul > ~s ? 1 : 0); uh -= MSH(p1) - borrow; if (0 != MSH(uh)) { /* add back */ quot--; ul += v; uh = 0; } } q2 = quot; /* * ROUND 2 calculate q1: * estimate quot = (u3u2) / v1, * then set (u3u2u1) -= quot*(v1v0) */ t = ul; quot = t / v1; rem = t - quot * v1; /* Test on v0 */ t = SHL(rem) | u1; if (B == quot || (quot * v0) > t) { quot--; rem += v1; t = SHL(rem) | u1; if (rem < B && (quot * v0) > t) quot--; } /* * multiply and subtract: * (u3u2u1)' = (u3u2u1) - quot*(v1v0) */ uh = MSH(ul); /* (0u3) */ ul = SHL(ul) | u1; /* (u2u1) */ p0 = quot * v0; p1 = quot * v1; s = p0 + SHL(p1); ul -= s; borrow = (ul > ~s ? 1 : 0); uh -= MSH(p1) - borrow; if (0 != MSH(uh)) { /* add back v */ quot--; ul += v; uh = 0; } /* quotient q1 */ q1 = quot; /* * ROUND 3: * calculate q0; estimate quot = (u2u1) / v1, * then set (u2u1u0) -= quot(v1v0) */ t = ul; quot = t / v1; rem = t - quot * v1; /* Test on v0 */ t = SHL(rem) | u0; if (B == quot || (quot * v0) > t) { quot--; rem += v1; t = SHL(rem) | u0; if (rem < B && (quot * v0) > t) quot--; } /* * multiply and subtract: * (u2u1u0)" = (u2u1u0)' - quot(v1v0) */ uh = MSH(ul); /* (0u2) */ ul = SHL(ul) | u0; /* (u1u0) */ p0 = quot * v0; p1 = quot * v1; s = p0 + SHL(p1); ul -= s; borrow = (ul > ~s ? 1 : 0); uh -= MSH(p1) - borrow; if (0 != MSH(uh)) { /* add back v */ quot--; ul += v; uh = 0; } /* quotient q1q0 */ *q = SHL(q1) | LSH(quot); /* Remainder is in (u1u0) i.e. ul */ *r = ul; /* quotient q2 (overflow) is returned */ return q2; } #endif /* HAVE_U64 */ /** * @brief Return greatest common divisor of two single limbs * * Returns gcd(x, y) * * Ref: Schneier 2nd ed, p245 * * @param x single limb candidate #1 * @param y single limb candidate #2 * * @result return zero if x and y are zero, else gcd(x,y) */ limb_t sl_gcd(limb_t x, limb_t y) { limb_t g; if (x + y == 0) return 0; /* Error */ g = y; while (x > 0) { g = x; x = y % x; y = g; } return g; } /** * @brief Compute single limb exp = x^e mod m * * Computes exp = x^e mod m * Binary left-to-right method * * @param exp pointer to limb to receive result * @param x single limb x (base) * @param e single limb e (exponent) * @param m single limb m (modulus) * * @result zero on success (always!?) */ int sl_modexp(limb_t *exp, limb_t x, limb_t e, limb_t m) { limb_t mask; limb_t y; /* Temp variable */ /* Find most significant bit in e */ for (mask = DIGMSB; mask > 0; mask >>= 1) { if (e & mask) break; } y = x; for (mask >>= 1; mask > 0; mask >>= 1) { sl_modmul(&y, y, y, m); /* y = (y^2) % m */ if (e & mask) sl_modmul(&y, y, x, m); /* y = (y*x) % m*/ } *exp = y; return 0; } /** * @brief Compute single limb inverse inv = u^(-1) % v * * Computes inv = u^(-1) % v * Ref: Knuth Algorithm X Vol 2 p 342 * ignoring u2, v2, t2 and avoiding negative numbers * * @param inv pointer to limb to receive result * @param u single limb to inverse * @param v single limb modulus * * @result zero on success (always!?) */ int sl_modinv(limb_t *inv, limb_t u, limb_t v) { limb_t u1, u3, v1, v3, t1, t3, q, w; int iter = 1; /* Step X1. Initialize */ u1 = 1; u3 = u; v1 = 0; v3 = v; /* Step X2. */ while (v3 != 0) { /* Step X3. */ q = u3 / v3; /* Divide and */ t3 = u3 % v3; w = q * v1; /* "Subtract" */ t1 = u1 + w; /* Swap */ u1 = v1; v1 = t1; u3 = v3; v3 = t3; iter = -iter; } if (iter < 0) *inv = v - u1; else *inv = u1; return 0; } /** * @brief Compute single limb a = (x * y) % mod * * Computes a = (x * y) % m * * @param a pointer to single limb to receive result * @param x single limb factor 1 * @param y single limb factor 2 * @param m single limb modulus * * @result zero on success (always!?) */ int sl_modmul(limb_t *a, limb_t x, limb_t y, limb_t m) { static IGRAPH_THREAD_LOCAL limb_t pp[2]; /* pp[] = x * y */ sl_mul(pp, x, y); /* *a = pp[] % m */ *a = bn_mod_limb(pp, m, 2); /* Clean temp */ pp[0] = pp[1] = 0; return 0; } #if HAVE_U64 /** * @brief Compute double limb product of two single limbs * * Computes p[] = x * y * where p is two limbs (double precision) and x, y are single * limbs. Use double precision natively supported on this machine. * * @param p pointer to an array of two limbs receiving the result * @param x single limb factor #1 * @param y single limb factor #2 * * @result zero on success (always) */ int sl_mul(limb_t p[2], limb_t x, limb_t y) { dlimb_t dd; dd = (dlimb_t)x * y; p[0] = (limb_t)dd; p[1] = (limb_t)(dd >> 32); return 0; } #else /** * @brief Compute double limb product of two single limbs * * Computes p[] = x * y * Source: Arbitrary Precision Computation * http://numbers.computation.free.fr/Constants/constants.html * * The limbs x and y are split in halves and the four products * x1*y1, x0*y1, x1*y0 and x0*y0 are added shifting them to * their respective least significant bit position: * p[1] = x1*y1 + high(x0*y1 + x1*y0) + ch << 16 + cl * p[0] = x0*y0 + low(x0*y1 + x1*y0) << 16 * ch = carry from adding x0*y1 + x1*y0 * cl = carry from adding low(x0*y1 + x1*y0) << 16 to p[0] * * @param p pointer to an array of two limbs receiving the result * @param x single limb factor #1 * @param y single limb factor #2 * * @result zero on success (always) */ int sl_mul(limb_t p[2], limb_t x, limb_t y) { limb_t x0, y0, x1, y1; limb_t t, u, carry; /* * Split each x,y into two halves * x = x0 + B*x1 * y = y0 + B*y1 * where B = 2^16, half the limb size * Product is * xy = x0y0 + B(x0y1 + x1y0) + B^2(x1y1) */ x0 = LSH(x); x1 = MSH(x); y0 = LSH(y); y1 = MSH(y); /* Compute low part (w/o carry) */ p[0] = x0 * y0; /* middle part */ t = x0 * y1; u = x1 * y0; t += u; carry = (t < u ? 1 : 0); /* * The carry will go to high half of p[1], * and the high half of t will go into the * into low half of p[1] */ carry = SHL(carry) + MSH(t); /* add low half of t to high half of p[0] */ t = SHL(t); p[0] += t; if (p[0] < t) carry++; p[1] = x1 * y1 + carry; return 0; } #endif /* HAVE_U64 */ /** * @brief Compute division of big number by a "half digit" * * Computes q[] = u[] / v, also returns r = u[] % v * where q, a are big number integers of nlimb limbs each, * and d, r are single limbs * * Using bit-by-bit method from MSB to LSB, * so v must be <= HALFMASK * * According to "Principles in PGP by Phil Zimmermann" * * @param q pointer to an array of limbs to receive the result * @param u pointer to an array of limbs (dividend) * @param v single limb (actually half limb) divisor * @param nlimb number of limbs in the arrays * * @result returns remainder of the division */ limb_t bn_div_hdig(limb_t q[], limb_t u[], limb_t v, count_t nlimb) { limb_t mask = DIGMSB; limb_t r = 0; if (v > HALFMASK) { igraph_errorf("bn_div_hdig called with v:%x", __FILE__, __LINE__, (int) v); } if (0 == nlimb) return 0; if (0 == v) return 0; /* Divide by zero error */ /* Initialize quotient */ bn_zero(q, nlimb); /* Work from MSB to LSB */ while (nlimb > 0) { /* Multiply remainder by 2 */ r <<= 1; /* Look at current bit */ if (u[nlimb-1] & mask) r++; if (r >= v) { /* Remainder became greater than divisor */ r -= v; q[nlimb-1] |= mask; } /* next bit */ mask >>= 1; if (0 != mask) continue; /* next limb */ --nlimb; mask = DIGMSB; } return r; } /** * @brief Compute single limb remainder of bignum % single limb * * Computes r = u[] % v * where a is a big number integer of nlimb * and r, v are single limbs, using bit-by-bit * method from MSB to LSB. * * Ref: * Derived from principles in PGP by Phil Zimmermann * Note: * This method will only work until r <<= 1 overflows. * i.e. for d < DIGMSB, but we keep HALF_DIGIT * limit for safety, and also because we don't * have a 32nd bit. * * @param u pointer to big number to divide * @param v single limb (actually half limb) modulus * @param nlimb number of limbs in the array * * @result returns remainder of the division */ limb_t bn_mod_hdig(limb_t u[], limb_t v, count_t nlimb) { limb_t mask; limb_t r; if (0 == nlimb) return 0; if (0 == v) return 0; /* Divide by zero error */ if (v > HALFMASK) { igraph_errorf("bn_mod_hdig called with v:%x", __FILE__, __LINE__, (int) v); } /* Work from left to right */ mask = DIGMSB; r = 0; while (nlimb > 0) { /* Multiply remainder by 2 */ r <<= 1; /* Look at current bit */ if (u[nlimb-1] & mask) r++; if (r >= v) /* Remainder became greater than divisor */ r -= v; /* next bit */ mask >>= 1; if (0 != mask) continue; /* next limb */ --nlimb; mask = DIGMSB; } return r; } /** * @brief Addition of two bignum arrays * * Computes w[] = u[] + v[] * where w, u, v are big number integers of nlimb limbs each. * Returns carry, i.e. w[nlimb], as 0 or 1. * * Ref: Knuth Vol 2 Ch 4.3.1 p 266 Algorithm A. * * @param w pointer to array of limbs to receive the result * @param u pointer to array of limbs (addend #1) * @param v pointer to array of limbs (addend #2) * @param nlimb number of limbs in the arrays * * @result returns the carry, i.e. w[nlimb], as 0 or 1 */ limb_t bn_add(limb_t w[], limb_t u[], limb_t v[], count_t nlimb) { limb_t carry; count_t j; for (j = 0, carry = 0; j < nlimb; j++) { /* * add limbs w[j] = u[j] + v[j] + carry; * set carry = 1 if carry (overflow) occurs */ w[j] = u[j] + carry; carry = (w[j] < carry ? 1 : 0); w[j] = w[j] + v[j]; if (w[j] < v[j]) carry++; } /* w[n] = carry */ return carry; } /** * @brief Subtraction of two bignum arrays * * Calculates w[] = u[] - v[] where u[] >= v[] * w, u, v are big number integers of nlimb limbs each * Returns 0 if ok, or 1 if v was greater than u. * * Ref: Knuth Vol 2 Ch 4.3.1 p 267 Algorithm S. * * @param w pointer to array of limbs to receive the result * @param u pointer to array of limbs (minuend) * @param v pointer to array of limbs (subtrahend) * @param nlimb number of limbs in the arrays * * @result zero on success, 1 if v was greater than u */ limb_t bn_sub(limb_t w[], limb_t u[], limb_t v[], count_t nlimb) { limb_t borrow; count_t j; for (j = 0, borrow = 0; j < nlimb; j++) { /* * Subtract limbs w[j] = u[j] - v[j] - borrow; * set borrow = 1 if borrow occurs */ w[j] = u[j] - borrow; borrow = (w[j] > ~borrow ? 1 : 0); w[j] = w[j] - v[j]; if (w[j] > ~v[j]) borrow++; } /* borrow should be 0, if u >= v */ return borrow; } /** * @brief Product of two bignum arrays * * Computes product w[] = u[] * v[] * where u, v are big number integers of nlimb each * and w is a big number integer of 2*nlimb limbs. * * Ref: Knuth Vol 2 Ch 4.3.1 p 268 Algorithm M. * * @param w pointer to array of limbs to receive the result * @param u pointer to array of limbs (factor #1) * @param v pointer to array of limbs (factor #2) * @param nlimb number of limbs in the arrays * * @result zero on success (always!?) */ int bn_mul(limb_t w[], limb_t u[], limb_t v[], count_t nlimb) { limb_t t[2]; limb_t carry; count_t i, j, m, n; m = n = nlimb; /* zero result */ bn_zero(w, 2*nlimb); for (j = 0; j < n; j++) { /* zero multiplier? */ if (0 == v[j]) { w[j+m] = 0; continue; } /* Initialize i */ carry = 0; for (i = 0; i < m; i++) { /* * Multiply and add: * t = u[i] * v[j] + w[i+j] + carry */ sl_mul(t, u[i], v[j]); t[0] += carry; if (t[0] < carry) t[1]++; t[0] += w[i+j]; if (t[0] < w[i+j]) t[1]++; w[i+j] = t[0]; carry = t[1]; } w[j+m] = carry; } return 0; } /** * @brief Shift left a bignum by a number of bits (less than LIMBBITS) * * Computes a[] = b[] << x * Where a and b are big number integers of nlimb each. * The shift count must be less than LIMBBITS * * @param a pointer to array of limbs to receive the result * @param b pointer to array of limbs to shift left * @param x number of bits to shift (must be less than LIMBBITS) * @param nlimb number of limbs in the arrays * * @result returns a single limb "carry", i.e. bits that came out left */ limb_t bn_shl(limb_t a[], limb_t b[], count_t x, count_t nlimb) { count_t i, y; limb_t carry, temp; if (0 == nlimb) return 0; if (0 == x) { /* no shift at all */ if (a != b) bn_copy(a, b, nlimb); return 0; } /* check shift amount */ if (x >= LIMBBITS) { igraph_errorf("bn_shl() called with x >= %d", __FILE__, __LINE__, LIMBBITS); return 0; } y = LIMBBITS - x; carry = 0; for (i = 0; i < nlimb; i++) { temp = b[i] >> y; a[i] = (b[i] << x) | carry; carry = temp; } return carry; } /** * @brief Shift right a bignum by a number of bits (less than LIMBBITS) * * Computes a[] = b[] >> x * Where a and b are big number integers of nlimb each. * The shift count must be less than LIMBBITS * * @param a pointer to array of limbs to receive the result * @param b pointer to array of limbs to shift right * @param x number of bits to shift (must be less than LIMBBITS) * @param nlimb number of limbs in the arrays * * @result returns a single limb "carry", i.e. bits that came out right */ limb_t bn_shr(limb_t a[], limb_t b[], count_t x, count_t nlimb) { count_t i, y; limb_t carry, temp; if (0 == nlimb) return 0; if (0 == x) { /* no shift at all */ if (a != b) bn_copy(a, b, nlimb); return 0; } /* check shift amount */ if (x >= LIMBBITS) { igraph_errorf("bn_shr() called with x >= %d", __FILE__, __LINE__, LIMBBITS); } y = LIMBBITS - x; carry = 0; i = nlimb; while (i-- > 0) { temp = b[i] << y; a[i] = (b[i] >> x) | carry; carry = temp; } return carry; } /** * @brief Check a quotient for overflow * * Returns 1 if quot is too big, * i.e. if (quot * Vn-2) > (b.rem + Uj+n-2) * Returns 0 if ok * * @param quot quotient under test * @param rem remainder * @param * * @result zero on success */ static int quot_overflow(limb_t quot, limb_t rem, limb_t v, limb_t u) { limb_t t[2]; sl_mul(t, quot, v); if (t[1] < rem) return 0; if (t[1] > rem) return 1; if (t[0] > u) return 1; return 0; } /** * @brief Compute quotient and remainder of bignum division * * Computes quotient q[] = u[] / v[] * and remainder r[] = u[] % v[] * where q, r, u are big number integers of ulimb limbs, * and the divisor v of vlimb limbs. * * Ref: Knuth Vol 2 Ch 4.3.1 p 272 Algorithm D. * * @param q pointer to array of limbs to receive quotient * @param r pointer to array of limbs to receive remainder * @param u pointer to array of limbs (dividend) * @param ulimb number of limbs in the q, r, u arrays * @param v pointer to array of limbs (divisor) * @param vlimb number of limbs in the v array * * @result zero on success, LIMBASK on division by zero */ int bn_div(limb_t q[], limb_t r[], limb_t u[], limb_t v[], count_t ulimb, count_t vlimb) { static IGRAPH_THREAD_LOCAL limb_t qq[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t uu[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t vv[BN_MAXSIZE]; limb_t mask; limb_t overflow; limb_t quot; limb_t rem; limb_t t[2]; limb_t *ww; count_t n, m, i, j, shift; int ok, cmp; /* find size of v */ n = bn_sizeof(v, vlimb); /* Catch special cases */ if (0 == n) return (int) LIMBMASK; /* Error: divide by zero */ if (1 == n) { /* Use short division instead */ r[0] = bn_div_limb(q, u, v[0], ulimb); return 0; } /* find size of u */ m = bn_sizeof(u, ulimb); if (m < n) { /* v > u: just set q = 0 and r = u */ bn_zero(q, ulimb); bn_copy(r, u, ulimb); return 0; } if (m == n) { /* u and v are the same length: compare them */ cmp = bn_cmp(u, v, (unsigned int)n); if (0 == cmp) { /* v == u: set q = 1 and r = 0 */ bn_limb(q, 1, ulimb); bn_zero(r, ulimb); return 0; } if (cmp < 0) { /* v > u: set q = 0 and r = u */ bn_zero(q, ulimb); bn_copy(r, u, ulimb); return 0; } } /* m greater than or equal to n */ m -= n; /* clear quotient qq */ bn_zero(qq, ulimb); /* * Normalize v: requires high bit of v[n-1] to be set, * so find most significant bit, then shift left */ mask = DIGMSB; for (shift = 0; shift < LIMBBITS; shift++) { if (v[n-1] & mask) break; mask >>= 1; } /* normalize vv from v */ overflow = bn_shl(vv, v, shift, n); /* copy normalized dividend u into remainder uu */ overflow = bn_shl(uu, u, shift, n + m); /* new limb u[m+n] */ t[0] = overflow; j = m + 1; while (j-- > 0) { /* quot = (b * u[j+n] + u[j+n-1]) / v[n-1] */ ok = 0; /* This is Uj+n */ t[1] = t[0]; t[0] = uu[j+n-1]; overflow = sl_div(", &rem, t, vv[n-1]); if (overflow) { /* quot = b */ quot = LIMBMASK; rem = uu[j+n-1] + vv[n-1]; if (rem < vv[n-1]) ok = 1; } if (0 == ok && quot_overflow(quot, rem, vv[n-2], uu[j+n-2])) { /* quot * v[n-2] > b * rem + u[j+n-2] */ quot--; rem += vv[n-1]; if (rem >= vv[n-1]) if (quot_overflow(quot, rem, vv[n-2], uu[j+n-2])) quot--; } /* multiply and subtract vv[] * quot */ ww = &uu[j]; if (0 == quot) { overflow = 0; } else { /* quot is non zero */ limb_t tt[2]; limb_t borrow; for (i = 0, borrow = 0; i < n; i++) { sl_mul(tt, quot, vv[i]); ww[i] -= borrow; borrow = (ww[i] > ~borrow ? 1 : 0); ww[i] -= tt[0]; if (ww[i] > ~tt[0]) borrow++; borrow += tt[1]; } /* * w[n] is not in array w[0..n-1]: * subtract final borrow */ overflow = t[1] - borrow; } /* test for remainder */ if (overflow) { quot--; /* add back if mul/sub was negative */ overflow = bn_add(ww, ww, vv, n); } qq[j] = quot; /* u[j+n] for next round */ t[0] = uu[j+n-1]; } /* clear uu[] limbs from n to n+m */ for (j = n; j < m+n; j++) uu[j] = 0; /* denormalize remainder */ bn_shr(r, uu, shift, n); /* copy quotient */ bn_copy(q, qq, n + m); /* clear temps */ bn_zero(qq, n); bn_zero(uu, n); bn_zero(vv, n); return 0; } /** * @brief Compute remainder of bignum division (modulo) * * Calculates r[] = u[] % v[] * where r, v are big number integers of length vlimb * and u is a big number integer of length ulimb. * r may overlap v. * * Note that r here is only vlimb long, * whereas in bn_div it is ulimb long. * * Use remainder from bn_div function. * * @param r pointer to array of limbs to receive remainder * @param u pointer to array of limbs (dividend) * @param ulimb number of limbs in the u array * @param v pointer to array of limbs (divisor) * @param vlimb number of limbs in the r and v array * * @result zero on success, LIMBASK on division by zero */ limb_t bn_mod(limb_t r[], limb_t u[], count_t ulimb, limb_t v[], count_t vlimb) { static IGRAPH_THREAD_LOCAL limb_t qq[2*BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t rr[2*BN_MAXSIZE]; limb_t d0; /* rr[] = u[] % v[n] */ d0 = (limb_t) bn_div(qq, rr, u, v, ulimb, vlimb); /* copy vlimb limbs of remainder */ bn_copy(r, rr, vlimb); /* zero temps */ bn_zero(rr, ulimb); bn_zero(qq, ulimb); return d0; } /** * @brief Compute greatest common divisor * * Computes g = gcd(x, y) * Reference: Schneier * * @param g pointer to array of limbs to receive the gcd * @param x pointer to array of limbs (candidate #1) * @param y pointer to array of limbs (candidate #2) * @param nlimb number of limbs in the arrays * * @result zero on succes (always) */ int bn_gcd(limb_t g[], limb_t x[], limb_t y[], count_t nlimb) { static IGRAPH_THREAD_LOCAL limb_t yy[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t xx[BN_MAXSIZE]; bn_copy(xx, x, nlimb); bn_copy(yy, y, nlimb); /* g = y */ bn_copy(g, yy, nlimb); /* while (x > 0) { */ while (0 != bn_cmp_limb(xx, 0, nlimb)) { /* g = x */ bn_copy(g, xx, nlimb); /* x = y % x */ bn_mod(xx, yy, nlimb, xx, nlimb); /* y = g */ bn_copy(yy, g, nlimb); } bn_zero(xx, nlimb); bn_zero(yy, nlimb); /* gcd is left in g */ return 0; } /** * @brief Compute modular exponentiation of bignums * * Computes y[] = (x[]^e[]) % m[] * Binary MSB to LSB method * * @param y pointer to array of limbs to receive the result * @param x pointer to array of limbs (base) * @param e pointer to array of limbs (exponent) * @param m pointer to array of limbs (modulus) * @param nlimb number of limbs in the arrays * * @result zero on success, -1 on error (nlimb is zero) */ int bn_modexp(limb_t y[], limb_t x[], limb_t e[], limb_t m[], count_t nlimb) { limb_t mask; count_t n; if (nlimb == 0) return -1; /* Find second-most significant bit in e */ n = bn_sizeof(e, nlimb); for (mask = DIGMSB; 0 != mask; mask >>= 1) { if (e[n-1] & mask) break; } /* next bit, because we start off with y[] == x[] */ mask >>= 1; if (0 == mask) { mask = DIGMSB; n--; } /* y[] = x[] */ bn_copy(y, x, nlimb); while (n > 0) { /* y[] = (y[] ^ 2) % m[] */ bn_modmul(y, y, y, m, nlimb); if (e[n-1] & mask) /* y[] = (y[] * x[]) % m[] */ bn_modmul(y, y, x, m, nlimb); /* next bit */ mask >>= 1; if (0 == mask) { mask = DIGMSB; n--; } } return 0; } /** * @brief Compute modular product of two bignums * * Computes a[] = (x[] * y[]) % m[] * where a, x, y and m are big numbers of nlimb length * * @param a pointer to array of limbs to receive the result * @param x pointer to array of limbs (factor #1) * @param y pointer to array of limbs (factor #2) * @param m pointer to array of limbs (modulus) * @param nlimb number of limbs in the arrays * * @result zero on success, LIMBMASK if m was zero (division by zero) */ limb_t bn_modmul(limb_t a[], limb_t x[], limb_t y[], limb_t m[], count_t nlimb) { static IGRAPH_THREAD_LOCAL limb_t pp[2*BN_MAXSIZE]; limb_t d0; /* pp[] = x[] * y[] (NB: double size pp[]) */ bn_mul(pp, x, y, nlimb); /* a[] = pp[] % m[] */ d0 = bn_mod(a, pp, 2*nlimb, m, nlimb); /* zero temp */ bn_zero(pp, 2*nlimb); return d0; } /** * @brief Compute modular inverse * * Computes inv[] = u[]^(-1) % v[] * Ref: Knuth Algorithm X Vol 2 p 342 * ignoring u2, v2, t2 and avoiding negative numbers. * * @param inv pointer to array of limbs receiving the result * @param u pointer to array of limbs (candidate) * @param v pointer to array of limbs (modulus) * @param nlimb number of limbs in the arrays * * @result zero on success */ int bn_modinv(limb_t inv[], limb_t u[], limb_t v[], count_t nlimb) { /* Allocate temp variables */ static IGRAPH_THREAD_LOCAL limb_t u1[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t u3[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t v1[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t v3[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t t1[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t t3[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t q[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t w[2*BN_MAXSIZE]; int iter; /* Step X1. Initialize */ bn_limb(u1, 1, nlimb); /* u1 = 1 */ bn_limb(v1, 0, nlimb); /* v1 = 0 */ bn_copy(u3, u, nlimb); /* u3 = u */ bn_copy(v3, v, nlimb); /* v3 = v */ /* remember odd/even iterations */ iter = 1; /* Step X2. Loop while v3 != 0 */ while (0 != bn_cmp_limb(v3, 0, nlimb)) { /* Step X3. Divide and "Subtract" */ /* q = u3 / v3, t3 = u3 % v3 */ bn_div(q, t3, u3, v3, nlimb, nlimb); /* w = q * v1 */ bn_mul(w, q, v1, nlimb); /* t1 = u1 + w */ bn_add(t1, u1, w, nlimb); /* Swap u1 <= v1 <= t1 */ bn_copy(u1, v1, nlimb); bn_copy(v1, t1, nlimb); /* Swap u3 <= v3 <= t3 */ bn_copy(u3, v3, nlimb); bn_copy(v3, t3, nlimb); iter ^= 1; } if (iter) bn_copy(inv, u1, nlimb); /* inv = u1 */ else bn_sub(inv, v, u1, nlimb); /* inv = v - u1 */ /* clear temp vars */ bn_zero(u1, nlimb); bn_zero(v1, nlimb); bn_zero(t1, nlimb); bn_zero(u3, nlimb); bn_zero(v3, nlimb); bn_zero(t3, nlimb); bn_zero(q, nlimb); bn_zero(w, 2*nlimb); return 0; } /** * @brief Compute square root (and fraction) of a bignum * * Compute q[] = sqrt(u[]), * where q and u are big number integers of nlimb limbs * * Method according to sqrt.html of 2001-08-15: * Act on bytes from MSB to LSB, counting the number of times * that we can subtract consecutive odd numbers starting with * 1, 3, 5. Just uses add, subtract, shift and comparisons. * * The pointer r can be NULL if caller is not interested in * the (partial) fraction. * * @param q pointer to array of limbs to receive the result (integer) * @param r pointer to array of limbs to receive the result (fraction) * @param u pointer to array of limbs (square) * @param rlimb number of limbs in the q and r arrays * @param ulimb number of limbs in the u array * * @result zero on success */ int bn_sqrt(limb_t q[], limb_t r[], limb_t u[], count_t rlimb, count_t ulimb) { static IGRAPH_THREAD_LOCAL limb_t step[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t accu[BN_MAXSIZE]; static IGRAPH_THREAD_LOCAL limb_t w[2*BN_MAXSIZE]; limb_t d; count_t m, n; count_t shift; bn_zero(q, ulimb); bn_limb(step, 1, BN_MAXSIZE); bn_limb(accu, 0, BN_MAXSIZE); n = bn_sizeof(u, ulimb); /* determine first non-zero byte from MSB to LSB */ if (0 != (u[n-1] >> 24)) { shift = 32; } else if (0 != (u[n-1] >> 16)) { shift = 24; } else if (0 != (u[n-1] >> 8)) { shift = 16; } else { shift = 8; } m = 1; while (n-- > 0) { while (shift > 0) { /* shift accu one byte left */ bn_shl(accu, accu, 8, m+1); /* shift for next byte from u[] */ shift -= 8; accu[0] |= (u[n] >> shift) & 0xff; /* digit = 0 */ d = 0; /* subtract consecutive odd numbers step[] until overflow */ for (d = 0; bn_cmp(step, accu, m+1) <= 0; d++) { bn_sub(accu, accu, step, m+1); bn_add_limb(step, step, 2, m+1); } /* put digit into result */ bn_shl(q, q, 4, m); q[0] |= d; /* step[] = 2 * q[] * 16 + 1 */ bn_shl(step, q, 5, m+1); bn_add_limb(step, step, 1, m+1); } shift = 32; if (0 == (n & 1)) m++; } /* Caller does not want to know the fraction? */ if (NULL == r) return 0; /* nothing left to do if remainder is zero */ if (0 == bn_cmp_limb(accu, 0, ulimb)) { bn_zero(r, rlimb); return 0; } /* Start off with the integer part */ bn_zero(w, 2*BN_MAXSIZE); bn_copy(w, q, ulimb); n = rlimb * (LIMBBITS / 4); while (n-- > 0) { /* shift accu one byte left */ bn_shl(accu, accu, 8, rlimb); /* subtract consecutive odd numbers step[] until overflow */ for (d = 0; bn_cmp(step, accu, rlimb) <= 0; d++) { bn_sub(accu, accu, step, rlimb); bn_add_limb(step, step, 2, rlimb); } /* put digit into result */ bn_shl(w, w, 4, rlimb); w[0] |= d; /* step[] = 2 * w[] * 16 + 1 */ bn_shl(step, w, 5, rlimb); bn_add_limb(step, step, 1, rlimb); } /* copy remainder */ bn_copy(r, w, rlimb); return 0; } igraph/src/games.c0000644000176000001440000040121212325527073013611 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=8 sw=2 sts=2 et: */ /* IGraph R library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_interface.h" #include "igraph_games.h" #include "igraph_random.h" #include "igraph_memory.h" #include "igraph_interrupt_internal.h" #include "igraph_attributes.h" #include "igraph_constructors.h" #include "igraph_nongraph.h" #include "igraph_conversion.h" #include "igraph_psumtree.h" #include "igraph_dqueue.h" #include "igraph_adjlist.h" #include "igraph_iterators.h" #include "igraph_progress.h" #include "igraph_topology.h" #include "igraph_types_internal.h" #include "config.h" #include typedef struct { long int no; igraph_psumtree_t *sumtrees; } igraph_i_citing_cited_type_game_struct_t; void igraph_i_citing_cited_type_game_free ( igraph_i_citing_cited_type_game_struct_t *s); /** * \section about_games * * Games are randomized graph generators. Randomization means that * they generate a different graph every time you call them. */ int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_bool_t directed, const igraph_t *start_from); int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, const igraph_t *start_from); int igraph_i_barabasi_game_psumtree(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, const igraph_t *start_from); int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_bool_t directed, const igraph_t *start_from) { long int no_of_nodes=n; long int no_of_neighbors=m; long int *bag; long int bagp=0; igraph_vector_t edges=IGRAPH_VECTOR_NULL; long int resp; long int i,j,k; long int bagsize, start_nodes, start_edges, new_edges, no_of_edges; start_nodes= start_from ? igraph_vcount(start_from) : 1; start_edges= start_from ? igraph_ecount(start_from) : 0; if (outseq) { if (igraph_vector_size(outseq)>1) { new_edges=(long int) (igraph_vector_sum(outseq)-VECTOR(*outseq)[0]); } else { new_edges=0; } } else { new_edges=(no_of_nodes-start_nodes) * no_of_neighbors; } no_of_edges=start_edges+new_edges; resp=start_edges*2; bagsize=no_of_nodes + no_of_edges + (outpref ? no_of_edges : 0); IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2); bag=igraph_Calloc(bagsize, long int); if (bag==0) { IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, bag); /* TODO: hack */ /* The first node(s) in the bag */ if (start_from) { igraph_vector_t deg; long int ii, jj, sn=igraph_vcount(start_from); igraph_neimode_t mm= outpref ? IGRAPH_ALL : IGRAPH_IN; IGRAPH_VECTOR_INIT_FINALLY(°, sn); IGRAPH_CHECK(igraph_degree(start_from, °, igraph_vss_all(), mm, IGRAPH_LOOPS)); for (ii=0; ii1) { new_edges=(long int) (igraph_vector_sum(outseq)-VECTOR(*outseq)[0]); } else { new_edges=0; } } else { new_edges=(no_of_nodes-start_nodes) * no_of_neighbors; } no_of_edges=start_edges+new_edges; edgeptr=start_edges*2; IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2); IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); /* first node(s) */ if (start_from) { long int ii, sn=igraph_vcount(start_from); igraph_neimode_t mm=outpref ? IGRAPH_ALL : IGRAPH_IN; IGRAPH_CHECK(igraph_degree(start_from, °ree, igraph_vss_all(), mm, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); for (ii=0; ii1) { new_edges=(long int) (igraph_vector_sum(outseq)-VECTOR(*outseq)[0]); } else { new_edges=0; } } else { new_edges=(no_of_nodes-start_nodes) * no_of_neighbors; } no_of_edges=start_edges+new_edges; edgeptr=start_edges*2; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*2)); IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); RNG_BEGIN(); /* first node(s) */ if (start_from) { long int ii, sn=igraph_vcount(start_from); igraph_neimode_t mm=outpref ? IGRAPH_ALL : IGRAPH_IN; IGRAPH_CHECK(igraph_degree(start_from, °ree, igraph_vss_all(), mm, IGRAPH_LOOPS)); IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); for (ii=0; ii= i) { /* All existing vertices are cited */ for (to=0; to i ? i : no_of_neighbors; igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power)+A); } else { igraph_psumtree_update(&sumtree, i, A); } } RNG_END(); igraph_psumtree_destroy(&sumtree); igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \ingroup generators * \function igraph_barabasi_game * \brief Generates a graph based on the Barabási-Albert model. * * \param graph An uninitialized graph object. * \param n The number of vertices in the graph. * \param power Power of the preferential attachment. The probability * that a vertex is cited is proportional to d^power+A, where * d is its degree (see also the \p outpref argument), power * and A are given by arguments. In the classic preferential * attachment model power=1. * \param m The number of outgoing edges generated for each * vertex. (Only if \p outseq is \c NULL.) * \param outseq Gives the (out-)degrees of the vertices. If this is * constant, this can be a NULL pointer or an empty (but * initialized!) vector, in this case \p m contains * the constant out-degree. The very first vertex has by definition * no outgoing edges, so the first number in this vector is * ignored. * \param outpref Boolean, if true not only the in- but also the out-degree * of a vertex increases its citation probability. Ie. the * citation probability is determined by the total degree of * the vertices. * \param A The probability that a vertex is cited is proportional to * d^power+A, where d is its degree (see also the \p outpref * argument), power and A are given by arguments. In the * previous versions of the function this parameter was * implicitly set to one. * \param directed Boolean, whether to generate a directed graph. * \param algo The algorithm to use to generate the network. Possible * values: * \clist * \cli IGRAPH_BARABASI_BAG * This is the algorithm that was previously (before version * 0.6) solely implemented in igraph. It works by putting the * ids of the vertices into a bag (multiset, really), exactly * as many times as their (in-)degree, plus once more. Then * the required number of cited vertices are drawn from the * bag, with replacement. This method might generate multiple * edges. It only works if power=1 and A=1. * \cli IGRAPH_BARABASI_PSUMTREE * This algorithm uses a partial prefix-sum tree to generate * the graph. It does not generate multiple edges and * works for any power and A values. * \cli IGRAPH_BARABASI_PSUMTREE_MULTIPLE * This algorithm also uses a partial prefix-sum tree to * generate the graph. The difference is, that now multiple * edges are allowed. This method was implemented under the * name \c igraph_nonlinear_barabasi_game before version 0.6. * \endclist * \param start_from Either a null pointer, or a graph. In the latter * case the graph as a starting configuration. The graph must * be non-empty, i.e. it must have at least one vertex. If a * graph is supplied here and the \p outseq argument is also * given, then \p outseq should only contain information on the * vertices that are not in the \p start_from graph. * \return Error code: * \c IGRAPH_EINVAL: invalid \p n, * \p m or \p outseq parameter. * * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges. * * \example examples/simple/igraph_barabasi_game.c * \example examples/simple/igraph_barabasi_game2.c */ int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, igraph_barabasi_algorithm_t algo, const igraph_t *start_from) { long int start_nodes= start_from ? igraph_vcount(start_from) : 0; long int newn= start_from ? n-start_nodes : n; /* Fix an obscure parameterization */ if (outseq && igraph_vector_size(outseq) == 0) { outseq=0; } /* Check arguments */ if (algo != IGRAPH_BARABASI_BAG && algo != IGRAPH_BARABASI_PSUMTREE && algo != IGRAPH_BARABASI_PSUMTREE_MULTIPLE) { IGRAPH_ERROR("Invalid algorithm", IGRAPH_EINVAL); } if (n < 0) { IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } else if (newn < 0) { IGRAPH_ERROR("Starting graph has too many vertices", IGRAPH_EINVAL); } if (start_from && start_nodes==0) { IGRAPH_ERROR("Cannot start from an empty graph", IGRAPH_EINVAL); } if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != newn) { IGRAPH_ERROR("Invalid out degree sequence length", IGRAPH_EINVAL); } if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m<0) { IGRAPH_ERROR("Invalid out degree", IGRAPH_EINVAL); } if (outseq && igraph_vector_min(outseq) < 0) { IGRAPH_ERROR("Negative out degree in sequence", IGRAPH_EINVAL); } if (A <= 0) { IGRAPH_ERROR("Constant attractiveness (A) must be positive", IGRAPH_EINVAL); } if (algo == IGRAPH_BARABASI_BAG) { if (power != 1) { IGRAPH_ERROR("Power must be one for 'bag' algorithm", IGRAPH_EINVAL); } if (A != 1) { IGRAPH_ERROR("Constant attractiveness (A) must be one for bag algorithm", IGRAPH_EINVAL); } } if (start_from && directed != igraph_is_directed(start_from)) { IGRAPH_WARNING("Directedness of the start graph and the output graph" " mismatch"); } if (start_from && !igraph_is_directed(start_from) && !outpref) { IGRAPH_ERROR("`outpref' must be true if starting from an undirected " "graph", IGRAPH_EINVAL); } if (algo == IGRAPH_BARABASI_BAG) { return igraph_i_barabasi_game_bag(graph, n, m, outseq, outpref, directed, start_from); } else if (algo == IGRAPH_BARABASI_PSUMTREE) { return igraph_i_barabasi_game_psumtree(graph, n, power, m, outseq, outpref, A, directed, start_from); } else if (algo == IGRAPH_BARABASI_PSUMTREE_MULTIPLE) { return igraph_i_barabasi_game_psumtree_multiple(graph, n, power, m, outseq, outpref, A, directed, start_from); } return 0; } /** * \ingroup internal */ int igraph_erdos_renyi_game_gnp(igraph_t *graph, igraph_integer_t n, igraph_real_t p, igraph_bool_t directed, igraph_bool_t loops) { long int no_of_nodes=n; igraph_vector_t edges=IGRAPH_VECTOR_NULL; igraph_vector_t s=IGRAPH_VECTOR_NULL; int retval=0; if (n<0) { IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } if (p<0.0 || p>1.0) { IGRAPH_ERROR("Invalid probability given", IGRAPH_EINVAL); } if (p==0.0 || no_of_nodes<=1) { IGRAPH_CHECK(retval=igraph_empty(graph, n, directed)); } else if (p==1.0) { IGRAPH_CHECK(retval=igraph_full(graph, n, directed, loops)); } else { long int i; double maxedges = n, last; if (directed && loops) { maxedges *= n; } else if (directed && !loops) { maxedges *= (n-1); } else if (!directed && loops) { maxedges *= (n+1)/2.0; } else { maxedges *= (n-1)/2.0; } IGRAPH_VECTOR_INIT_FINALLY(&s, 0); IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges*p*1.1))); RNG_BEGIN(); last=RNG_GEOM(p); while (last < maxedges) { IGRAPH_CHECK(igraph_vector_push_back(&s, last)); last += RNG_GEOM(p); last += 1; } RNG_END(); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s)*2)); if (directed && loops) { for (i=0; i maxedges) { IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); } if (maxedges == no_of_edges) { retval=igraph_full(graph, n, directed, loops); } else { long int slen; IGRAPH_VECTOR_INIT_FINALLY(&s, 0); IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges-1, (igraph_integer_t) no_of_edges)); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s)*2)); slen=igraph_vector_size(&s); if (directed && loops) { for (i=0; i to) { dummy = from; from = to; to = dummy; } neis = igraph_adjlist_get(&al, from); if (from == to || igraph_vector_int_binsearch(neis, to, &j)) { /* Edge exists already */ VECTOR(residual_degrees)[from]++; VECTOR(residual_degrees)[to]++; IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, from)); IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, to)); } else { /* Insert the edge */ IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to)); } } finished = igraph_set_empty(&incomplete_vertices); if (!finished) { /* We are not done yet; check if the remaining stubs are feasible. This * is done by enumerating all possible pairs and checking whether at * least one feasible pair is found. */ i = 0; failed = 1; while (failed && igraph_set_iterate(&incomplete_vertices, &i, &from)) { j = 0; while (igraph_set_iterate(&incomplete_vertices, &j, &to)) { if (from == to) { /* This is used to ensure that each pair is checked once only */ break; } if (from > to) { dummy = from; from = to; to = dummy; } neis = igraph_adjlist_get(&al, from); if (!igraph_vector_int_binsearch(neis, to, 0)) { /* Found a suitable pair, so we can continue */ failed = 0; break; } } } } } } /* Finish the RNG */ RNG_END(); /* Clean up */ igraph_set_destroy(&incomplete_vertices); igraph_vector_destroy(&residual_degrees); igraph_vector_destroy(&stubs); IGRAPH_FINALLY_CLEAN(3); /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs * because we did not add edges in both directions in the adjacency list. * We will use igraph_to_undirected in an extra step. */ IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0)); /* Clear the adjacency list */ igraph_adjlist_destroy(&al); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } int igraph_degree_sequence_game_no_multiple_directed(igraph_t *graph, const igraph_vector_t *out_seq, const igraph_vector_t *in_seq) { igraph_adjlist_t al; igraph_bool_t deg_seq_ok, failed, finished; igraph_vector_t in_stubs = IGRAPH_VECTOR_NULL; igraph_vector_t out_stubs = IGRAPH_VECTOR_NULL; igraph_vector_int_t *neis; igraph_vector_t residual_in_degrees=IGRAPH_VECTOR_NULL; igraph_vector_t residual_out_degrees=IGRAPH_VECTOR_NULL; igraph_set_t incomplete_in_vertices; igraph_set_t incomplete_out_vertices; igraph_integer_t from, to; long int i, j, k; long int no_of_nodes, outsum; IGRAPH_CHECK(igraph_is_graphical_degree_sequence(out_seq, in_seq, °_seq_ok)); if (!deg_seq_ok) { IGRAPH_ERROR("No simple directed graph can realize the given degree sequence", IGRAPH_EINVAL); } outsum=(long int) igraph_vector_sum(out_seq); no_of_nodes=igraph_vector_size(out_seq); /* Allocate required data structures */ IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes)); IGRAPH_FINALLY(igraph_adjlist_destroy, &al); IGRAPH_VECTOR_INIT_FINALLY(&out_stubs, 0); IGRAPH_CHECK(igraph_vector_reserve(&out_stubs, outsum)); IGRAPH_VECTOR_INIT_FINALLY(&in_stubs, 0); IGRAPH_CHECK(igraph_vector_reserve(&in_stubs, outsum)); IGRAPH_VECTOR_INIT_FINALLY(&residual_out_degrees, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&residual_in_degrees, no_of_nodes); IGRAPH_CHECK(igraph_set_init(&incomplete_out_vertices, 0)); IGRAPH_FINALLY(igraph_set_destroy, &incomplete_out_vertices); IGRAPH_CHECK(igraph_set_init(&incomplete_in_vertices, 0)); IGRAPH_FINALLY(igraph_set_destroy, &incomplete_in_vertices); /* Start the RNG */ RNG_BEGIN(); /* Outer loop; this will try to construct a graph several times from scratch * until it finally succeeds. */ finished = 0; while (!finished) { /* Be optimistic :) */ failed = 0; /* Clear the adjacency list to get rid of the previous attempt (if any) */ igraph_adjlist_clear(&al); /* Initialize the residual degrees from the degree sequences */ IGRAPH_CHECK(igraph_vector_update(&residual_out_degrees, out_seq)); IGRAPH_CHECK(igraph_vector_update(&residual_in_degrees, in_seq)); /* While there are some unconnected stubs left... */ while (!finished && !failed) { /* Construct the initial stub vectors */ igraph_vector_clear(&out_stubs); igraph_vector_clear(&in_stubs); for (i = 0; i < no_of_nodes; i++) { for (j = 0; j < VECTOR(residual_out_degrees)[i]; j++) { igraph_vector_push_back(&out_stubs, i); } for (j = 0; j < VECTOR(residual_in_degrees)[i]; j++) { igraph_vector_push_back(&in_stubs, i); } } /* Clear the skipped stub counters and the set of incomplete vertices */ igraph_vector_null(&residual_out_degrees); igraph_vector_null(&residual_in_degrees); igraph_set_clear(&incomplete_out_vertices); igraph_set_clear(&incomplete_in_vertices); outsum = 0; /* Shuffle the out-stubs in-place */ igraph_vector_shuffle(&out_stubs); /* Connect the stubs where possible */ k = igraph_vector_size(&out_stubs); for (i = 0; i < k; i++) { from = (igraph_integer_t) VECTOR(out_stubs)[i]; to = (igraph_integer_t) VECTOR(in_stubs)[i]; neis = igraph_adjlist_get(&al, from); if (from == to || igraph_vector_int_binsearch(neis, to, &j)) { /* Edge exists already */ VECTOR(residual_out_degrees)[from]++; VECTOR(residual_in_degrees)[to]++; IGRAPH_CHECK(igraph_set_add(&incomplete_out_vertices, from)); IGRAPH_CHECK(igraph_set_add(&incomplete_in_vertices, to)); } else { /* Insert the edge */ IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to)); } } /* Are we finished? */ finished = igraph_set_empty(&incomplete_out_vertices); if (!finished) { /* We are not done yet; check if the remaining stubs are feasible. This * is done by enumerating all possible pairs and checking whether at * least one feasible pair is found. */ i = 0; failed = 1; while (failed && igraph_set_iterate(&incomplete_out_vertices, &i, &from)) { j = 0; while (igraph_set_iterate(&incomplete_in_vertices, &j, &to)) { neis = igraph_adjlist_get(&al, from); if (from != to && !igraph_vector_int_binsearch(neis, to, 0)) { /* Found a suitable pair, so we can continue */ failed = 0; break; } } } } } } /* Finish the RNG */ RNG_END(); /* Clean up */ igraph_set_destroy(&incomplete_in_vertices); igraph_set_destroy(&incomplete_out_vertices); igraph_vector_destroy(&residual_in_degrees); igraph_vector_destroy(&residual_out_degrees); igraph_vector_destroy(&in_stubs); igraph_vector_destroy(&out_stubs); IGRAPH_FINALLY_CLEAN(6); /* Create the graph */ IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); /* Clear the adjacency list */ igraph_adjlist_destroy(&al); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /* This is in gengraph_mr-connected.cpp */ int igraph_degree_sequence_game_vl(igraph_t *graph, const igraph_vector_t *out_seq, const igraph_vector_t *in_seq); /** * \ingroup generators * \function igraph_degree_sequence_game * \brief Generates a random graph with a given degree sequence * * \param graph Pointer to an uninitialized graph object. * \param out_deg The degree sequence for an undirected graph (if * \p in_seq is of length zero), or the out-degree * sequence of a directed graph (if \p in_deq is not * of length zero. * \param in_deg It is either a zero-length vector or * \c NULL (if an undirected * graph is generated), or the in-degree sequence. * \param method The method to generate the graph. Possible values: * \clist * \cli IGRAPH_DEGSEQ_SIMPLE * For undirected graphs, this method puts all vertex ids in a bag * such that the multiplicity of a vertex in the bag is the same as * its degree. Then it draws pairs from the bag until the bag becomes * empty. This method can generate both loop (self) edges and multiple * edges. For directed graphs, the algorithm is basically the same, * but two separate bags are used for the in- and out-degrees. * \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE * This method is similar to \c IGRAPH_DEGSEQ_SIMPLE * but tries to avoid multiple and loop edges and restarts the * generation from scratch if it gets stuck. It is not guaranteed * to sample uniformly from the space of all possible graphs with * the given sequence, but it is relatively fast and it will * eventually succeed if the provided degree sequence is graphical, * but there is no upper bound on the number of iterations. * \cli IGRAPH_DEGSEQ_VL * This method is a much more sophisticated generator than the * previous ones. It can sample undirected, connected simple graphs * uniformly and uses Monte-Carlo methods to randomize the graphs. * This generator should be favoured if undirected and connected * graphs are to be generated and execution time is not a concern. * igraph uses the original implementation of Fabien Viger; see * http://www-rp.lip6.fr/~latapy/FV/generation.html * and the paper cited on it for the details of the algorithm. * \endclist * \return Error code: * \c IGRAPH_ENOMEM: there is not enough * memory to perform the operation. * \c IGRAPH_EINVAL: invalid method parameter, or * invalid in- and/or out-degree vectors. The degree vectors * should be non-negative, \p out_deg should sum * up to an even integer for undirected graphs; the length * and sum of \p out_deg and * \p in_deg * should match for directed graphs. * * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges * for \c IGRAPH_DEGSEQ_SIMPLE. The time complexity of the * other modes is not known. * * \sa \ref igraph_barabasi_game(), \ref igraph_erdos_renyi_game(), * \ref igraph_is_degree_sequence(), * \ref igraph_is_graphical_degree_sequence() * * \example examples/simple/igraph_degree_sequence_game.c */ int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg, const igraph_vector_t *in_deg, igraph_degseq_t method) { int retval; if (in_deg && igraph_vector_empty(in_deg) && !igraph_vector_empty(out_deg)) { in_deg=0; } if (method==IGRAPH_DEGSEQ_SIMPLE) { retval=igraph_degree_sequence_game_simple(graph, out_deg, in_deg); } else if (method==IGRAPH_DEGSEQ_VL) { retval=igraph_degree_sequence_game_vl(graph, out_deg, in_deg); } else if (method==IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE) { if (in_deg == 0 || (igraph_vector_empty(in_deg) && !igraph_vector_empty(out_deg))) { retval=igraph_degree_sequence_game_no_multiple_undirected(graph, out_deg); } else { retval=igraph_degree_sequence_game_no_multiple_directed(graph, out_deg, in_deg); } } else { IGRAPH_ERROR("Invalid degree sequence game method", IGRAPH_EINVAL); } return retval; } /** * \ingroup generators * \function igraph_growing_random_game * \brief Generates a growing random graph. * * * This function simulates a growing random graph. In each discrete * time step a new vertex is added and a number of new edges are also * added. These graphs are known to be different from standard (not * growing) random graphs. * \param graph Uninitialized graph object. * \param n The number of vertices in the graph. * \param m The number of edges to add in a time step (ie. after * adding a vertex). * \param directed Boolean, whether to generate a directed graph. * \param citation Boolean, if \c TRUE, the edges always * originate from the most recently added vertex. * \return Error code: * \c IGRAPH_EINVAL: invalid * \p n or \p m * parameter. * * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges. * * \example examples/simple/igraph_growing_random_game.c */ int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation) { long int no_of_nodes=n; long int no_of_neighbors=m; long int no_of_edges; igraph_vector_t edges=IGRAPH_VECTOR_NULL; long int resp=0; long int i,j; if (n<0) { IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } if (m<0) { IGRAPH_ERROR("Invalid number of edges per step (m)", IGRAPH_EINVAL); } no_of_edges=(no_of_nodes-1) * no_of_neighbors; IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2); RNG_BEGIN(); for (i=1; i * The different types of vertices prefer to connect other types of * vertices with a given probability. * * * The simulation goes like this: in each discrete time step a new * vertex is added to the graph. The type of this vertex is generated * based on \p type_dist. Then two vertices are selected uniformly * randomly from the graph. The probability that they will be * connected depends on the types of these vertices and is taken from * \p pref_matrix. Then another two vertices are selected and this is * repeated \p edges_per_step times in each time step. * \param graph Pointer to an uninitialized graph. * \param nodes The number of nodes in the graph. * \param types Number of node types. * \param edges_per_step The number of edges to be add per time step. * \param type_dist Vector giving the distribution of the vertex * types. * \param pref_matrix Matrix giving the connection probabilities for * the vertex types. * \param directed Logical, whether to generate a directed graph. * \return Error code. * * Added in version 0.2. * * Time complexity: O(|V|e*log(|V|)), |V| is the number of vertices, e * is \p edges_per_step. */ int igraph_callaway_traits_game (igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, igraph_integer_t edges_per_step, igraph_vector_t *type_dist, igraph_matrix_t *pref_matrix, igraph_bool_t directed) { long int i, j; igraph_vector_t edges; igraph_vector_t cumdist; igraph_real_t maxcum; igraph_vector_t nodetypes; /* TODO: parameter checks */ IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types+1); IGRAPH_VECTOR_INIT_FINALLY(&nodetypes, nodes); VECTOR(cumdist)[0]=0; for (i=0; i * The simulation goes like this: a single vertex is added at each * time step. This new vertex tries to connect to \p k vertices in the * graph. The probability that such a connection is realized depends * on the types of the vertices involved. * * \param graph Pointer to an uninitialized graph. * \param nodes The number of vertices in the graph. * \param types The number of vertex types. * \param k The number of connections tried in each time step. * \param type_dist Vector giving the distribution of vertex types. * \param pref_matrix Matrix giving the connection probabilities for * different vertex types. * \param directed Logical, whether to generate a directed graph. * \return Error code. * * Added in version 0.2. * * Time complexity: O(|V|*k*log(|V|)), |V| is the number of vertices * and k is the \p k parameter. */ int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, igraph_integer_t k, igraph_vector_t *type_dist, igraph_matrix_t *pref_matrix, igraph_bool_t directed) { long int i, j; igraph_vector_t edges; igraph_vector_t cumdist; igraph_vector_t potneis; igraph_real_t maxcum; igraph_vector_t nodetypes; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types+1); IGRAPH_VECTOR_INIT_FINALLY(&potneis, k); IGRAPH_VECTOR_INIT_FINALLY(&nodetypes, nodes); VECTOR(cumdist)[0]=0; for (i=0; i=time_window) { while ((j=(long int) igraph_dqueue_pop(&history)) != -1) { VECTOR(degree)[j] -= 1; igraph_psumtree_update(&sumtree, j, pow(VECTOR(degree)[j], power)+zero_appeal); } } sum=igraph_psumtree_sum(&sumtree); for (j=0; j * In this game, the probability that a node gains a new edge is * given by its (in-)degree (k) and age (l). This probability has a * degree dependent component multiplied by an age dependent * component. The degree dependent part is: \p deg_coef times k to the * power of \p pa_exp plus \p zero_deg_appeal; and the age dependent * part is \p age_coef times l to the power of \p aging_exp plus \p * zero_age_appeal. * * * The age is based on the number of vertices in the * network and the \p aging_bin argument: vertices grew one unit older * after each \p aging_bin vertices added to the network. * \param graph Pointer to an uninitialized graph object. * \param nodes The number of vertices in the graph. * \param m The number of edges to add in each time step. If the \p * outseq argument is not a null vector and not a zero-length * vector. * \param outseq The number of edges to add in each time step. If it * is a null pointer or a zero-length vector then it is ignored * and the \p m argument is used instead. * \param outpref Logical constant, whether the edges * initiated by a vertex contribute to the probability to gain * a new edge. * \param pa_exp The exponent of the preferential attachment, a small * positive number usually, the value 1 yields the classic * linear preferential attachment. * \param aging_exp The exponent of the aging, this is a negative * number usually. * \param aging_bin Integer constant, the number of vertices to add * before vertices in the network grew one unit older. * \param zero_deg_appeal The degree dependent part of the * attractiveness of the zero degree vertices. * \param zero_age_appeal The age dependent part of the attractiveness * of the vertices of age zero. This parameter is usually zero. * \param deg_coef The coefficient for the degree. * \param age_coef The coefficient for the age. * \param directed Logical constant, whether to generate a directed * graph. * \return Error code. * * Time complexity: O((|V|+|V|/aging_bin)*log(|V|)+|E|). |V| is the number * of vertices, |E| the number of edges. */ int igraph_barabasi_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, igraph_integer_t aging_bin, igraph_real_t zero_deg_appeal, igraph_real_t zero_age_appeal, igraph_real_t deg_coef, igraph_real_t age_coef, igraph_bool_t directed) { long int no_of_nodes=nodes; long int no_of_neighbors=m; long int binwidth=nodes/aging_bin+1; long int no_of_edges; igraph_vector_t edges; long int i, j, k; igraph_psumtree_t sumtree; long int edgeptr=0; igraph_vector_t degree; if (no_of_nodes<0) { IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != no_of_nodes) { IGRAPH_ERROR("Invalid out degree sequence length", IGRAPH_EINVAL); } if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m<0) { IGRAPH_ERROR("Invalid out degree", IGRAPH_EINVAL); } if (aging_bin <= 0) { IGRAPH_ERROR("Invalid aging bin", IGRAPH_EINVAL); } if (outseq==0 || igraph_vector_size(outseq) == 0) { no_of_neighbors=m; no_of_edges=(no_of_nodes-1)*no_of_neighbors; } else { no_of_edges=0; for (i=1; i= 1; k++) { long int shnode=i-binwidth*k; long int deg=(long int) VECTOR(degree)[shnode]; long int age=(i-shnode)/binwidth; /* igraph_real_t old=igraph_psumtree_get(&sumtree, shnode); */ igraph_psumtree_update(&sumtree, shnode, (deg_coef*pow(deg, pa_exp)+zero_deg_appeal) * (age_coef*pow(age+2, aging_exp)+zero_age_appeal)); } } RNG_END(); igraph_vector_destroy(°ree); igraph_psumtree_destroy(&sumtree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_recent_degree_aging_game * \brief Preferential attachment based on the number of edges gained recently, with aging of vertices * * * This game is very similar to \ref igraph_barabasi_aging_game(), * except that instead of the total number of incident edges the * number of edges gained in the last \p time_window time steps are * counted. * * The degree dependent part of the attractiveness is * given by k to the power of \p pa_exp plus \p zero_appeal; the age * dependent part is l to the power to \p aging_exp. * k is the number of edges gained in the last \p time_window time * steps, l is the age of the vertex. * \param graph Pointer to an uninitialized graph object. * \param nodes The number of vertices in the graph. * \param m The number of edges to add in each time step. If the \p * outseq argument is not a null vector or a zero-length vector * then it is ignored. * \param outseq Vector giving the number of edges to add in each time * step. If it is a null pointer or a zero-length vector then * it is ignored and the \p m argument is used. * \param outpref Logical constant, if true the edges initiated by a * vertex are also counted. Normally it is false. * \param pa_exp The exponent for the preferential attachment. * \param aging_exp The exponent for the aging, normally it is * negative: old vertices gain edges with less probability. * \param aging_bin Integer constant, gives the scale of the aging. * The age of the vertices is incremented by one after every \p * aging_bin vertex added. * \param time_window The time window to use to count the number of * incident edges for the vertices. * \param zero_appeal The degree dependent part of the attractiveness * for zero degree vertices. * \param directed Logical constant, whether to create a directed * graph. * \return Error code. * * Time complexity: O((|V|+|V|/aging_bin)*log(|V|)+|E|). |V| is the number * of vertices, |E| the number of edges. */ int igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, igraph_integer_t aging_bin, igraph_integer_t time_window, igraph_real_t zero_appeal, igraph_bool_t directed) { long int no_of_nodes=nodes; long int no_of_neighbors=m; long int binwidth=nodes/aging_bin+1; long int no_of_edges; igraph_vector_t edges; long int i, j, k; igraph_psumtree_t sumtree; long int edgeptr=0; igraph_vector_t degree; igraph_dqueue_t history; if (no_of_nodes<0) { IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != no_of_nodes) { IGRAPH_ERROR("Invalid out degree sequence length", IGRAPH_EINVAL); } if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m<0) { IGRAPH_ERROR("Invalid out degree", IGRAPH_EINVAL); } if (aging_bin <= 0) { IGRAPH_ERROR("Invalid aging bin", IGRAPH_EINVAL); } if (outseq==0 || igraph_vector_size(outseq) == 0) { no_of_neighbors=m; no_of_edges=(no_of_nodes-1)*no_of_neighbors; } else { no_of_edges=0; for (i=1; i=time_window) { while ((j=(long int) igraph_dqueue_pop(&history)) != -1) { long int age=(i-j)/binwidth; VECTOR(degree)[j] -= 1; igraph_psumtree_update(&sumtree, j, (pow(VECTOR(degree)[j], pa_exp)+zero_appeal)* pow(age+1, aging_exp)); } } sum=igraph_psumtree_sum(&sumtree); for (j=0; j= 1; k++) { long int shnode=i-binwidth*k; long int deg=(long int) VECTOR(degree)[shnode]; long int age=(i-shnode)/binwidth; igraph_psumtree_update(&sumtree, shnode, (pow(deg, pa_exp)+zero_appeal) * pow(age+2, aging_exp)); } } RNG_END(); igraph_dqueue_destroy(&history); igraph_vector_destroy(°ree); igraph_psumtree_destroy(&sumtree); IGRAPH_FINALLY_CLEAN(3); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_grg_game * \brief Generating geometric random graphs. * * A geometric random graph is created by dropping points (=vertices) * randomly to the unit square and then connecting all those pairs * which are less than \c radius apart in Euclidean norm. * * * Original code contributed by Keith Briggs, thanks Keith. * \param graph Pointer to an uninitialized graph object, * \param nodes The number of vertices in the graph. * \param radius The radius within which the vertices will be connected. * \param torus Logical constant, if true periodic boundary conditions * will be used, ie. the vertices are assumed to be on a torus * instead of a square. * \return Error code. * * Time complexity: TODO, less than O(|V|^2+|E|). * * \example examples/simple/igraph_grg_game.c */ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, igraph_real_t radius, igraph_bool_t torus, igraph_vector_t *x, igraph_vector_t *y) { long int i; igraph_vector_t myx, myy, *xx=&myx, *yy=&myy, edges; igraph_real_t r2=radius*radius; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes)); if (x) { xx=x; IGRAPH_CHECK(igraph_vector_resize(xx, nodes)); } else { IGRAPH_VECTOR_INIT_FINALLY(xx, nodes); } if (y) { yy=y; IGRAPH_CHECK(igraph_vector_resize(yy, nodes)); } else { IGRAPH_VECTOR_INIT_FINALLY(yy, nodes); } RNG_BEGIN(); for (i=0; i 0.5) { dx=1-dx; } if (dy > 0.5) { dy=1-dy; } if (dx*dx+dy*dy < r2) { IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } j++; } if (j==nodes) { j=0; while (j=radius) { dy=fabs(VECTOR(*yy)[j]-yy1); if (dy > 0.5) { dy=1-dy; } if (dx*dx+dy*dy < r2) { IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } j++; } } } } if (!y) { igraph_vector_destroy(yy); IGRAPH_FINALLY_CLEAN(1); } if (!x) { igraph_vector_destroy(xx); IGRAPH_FINALLY_CLEAN(1); } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, IGRAPH_UNDIRECTED)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } void igraph_i_preference_game_free_vids_by_type(igraph_vector_ptr_t *vecs); void igraph_i_preference_game_free_vids_by_type(igraph_vector_ptr_t *vecs) { int i=0, n; igraph_vector_t *v; n = (int) igraph_vector_ptr_size(vecs); for (i=0; i * This is practically the nongrowing variant of \ref * igraph_establishment_game. A given number of vertices are * generated. Every vertex is assigned to a vertex type according to * the given type probabilities. Finally, every * vertex pair is evaluated and an edge is created between them with a * probability depending on the types of the vertices involved. * * * In other words, this function generates a graph according to a * block-model. Vertices are divided into groups (or blocks), and * the probability the two vertices are connected depends on their * groups only. * * \param graph Pointer to an uninitialized graph. * \param nodes The number of vertices in the graph. * \param types The number of vertex types. * \param type_dist Vector giving the distribution of vertex types. If * \c NULL, all vertex types will have equal probability. See also the * \c fixed_sizes argument. * \param fixed_sizes Boolean. If true, then the number of vertices with a * given vertex type is fixed and the \c type_dist argument gives these * numbers for each vertex type. If true, and \c type_dist is \c NULL, * then the function tries to make vertex groups of the same size. If this * is not possible, then some groups will have an extra vertex. * \param pref_matrix Matrix giving the connection probabilities for * different vertex types. This should be symmetric if the requested * graph is undirected. * \param node_type_vec A vector where the individual generated vertex types * will be stored. If \c NULL , the vertex types won't be saved. * \param directed Logical, whether to generate a directed graph. If undirected * graphs are requested, only the lower left triangle of the preference * matrix is considered. * \param loops Logical, whether loop edges are allowed. * \return Error code. * * Added in version 0.3. * * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges in the graph. * * \sa igraph_establishment_game() * * \example examples/simple/igraph_preference_game.c */ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, const igraph_vector_t *type_dist, igraph_bool_t fixed_sizes, const igraph_matrix_t *pref_matrix, igraph_vector_t *node_type_vec, igraph_bool_t directed, igraph_bool_t loops) { long int i, j; igraph_vector_t edges, s; igraph_vector_t* nodetypes; igraph_vector_ptr_t vids_by_type; igraph_real_t maxcum, maxedges; if (types < 1) IGRAPH_ERROR("types must be >= 1", IGRAPH_EINVAL); if (nodes < 0) IGRAPH_ERROR("nodes must be >= 0", IGRAPH_EINVAL); if (type_dist && igraph_vector_size(type_dist) != types) { if (igraph_vector_size(type_dist) > types) IGRAPH_WARNING("length of type_dist > types, type_dist will be trimmed"); else IGRAPH_ERROR("type_dist vector too short", IGRAPH_EINVAL); } if (igraph_matrix_nrow(pref_matrix) < types || igraph_matrix_ncol(pref_matrix) < types) IGRAPH_ERROR("pref_matrix too small", IGRAPH_EINVAL); if (fixed_sizes && type_dist) { if (igraph_vector_sum(type_dist) != nodes) { IGRAPH_ERROR("Invalid group sizes, their sum must match the number" " of vertices", IGRAPH_EINVAL); } } if (node_type_vec) { IGRAPH_CHECK(igraph_vector_resize(node_type_vec, nodes)); nodetypes = node_type_vec; } else { nodetypes = igraph_Calloc(1, igraph_vector_t); if (nodetypes == 0) { IGRAPH_ERROR("preference_game failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nodetypes); IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); } IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_type, types)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_type); for (i=0; i j && !directed) continue; maxedges = v1_size * v2_size; } else { if (directed && loops) maxedges = v1_size * v1_size; else if (directed && !loops) maxedges = v1_size * (v1_size-1); else if (!directed && loops) maxedges = v1_size * (v1_size+1)/2; else maxedges = v1_size * (v1_size-1)/2; } IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges*p*1.1))); last=RNG_GEOM(p); while (last < maxedges) { IGRAPH_CHECK(igraph_vector_push_back(&s, last)); last += RNG_GEOM(p); last += 1; } l = igraph_vector_size(&s); IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges)+l*2)); if (i != j) { /* Generating the subgraph between vertices of type i and j */ for (k=0; k * This is the asymmetric variant of \ref igraph_preference_game() . * A given number of vertices are generated. Every vertex is assigned to an * "incoming" and an "outgoing" vertex type according to the given joint * type probabilities. Finally, every vertex pair is evaluated and a * directed edge is created between them with a probability depending on the * "outgoing" type of the source vertex and the "incoming" type of the target * vertex. * * \param graph Pointer to an uninitialized graph. * \param nodes The number of vertices in the graph. * \param types The number of vertex types. * \param type_dist_matrix Matrix giving the joint distribution of vertex types. * If null, incoming and outgoing vertex types are independent and uniformly * distributed. * \param pref_matrix Matrix giving the connection probabilities for * different vertex types. * \param node_type_in_vec A vector where the individual generated "incoming" * vertex types will be stored. If NULL, the vertex types won't be saved. * \param node_type_out_vec A vector where the individual generated "outgoing" * vertex types will be stored. If NULL, the vertex types won't be saved. * \param loops Logical, whether loop edges are allowed. * \return Error code. * * Added in version 0.3. * * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges in the graph. * * \sa \ref igraph_preference_game() */ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t types, igraph_matrix_t *type_dist_matrix, igraph_matrix_t *pref_matrix, igraph_vector_t *node_type_in_vec, igraph_vector_t *node_type_out_vec, igraph_bool_t loops) { long int i, j, k; igraph_vector_t edges, cumdist, s, intersect; igraph_vector_t *nodetypes_in; igraph_vector_t *nodetypes_out; igraph_vector_ptr_t vids_by_intype, vids_by_outtype; igraph_real_t maxcum, maxedges; if (types < 1) IGRAPH_ERROR("types must be >= 1", IGRAPH_EINVAL); if (nodes < 0) IGRAPH_ERROR("nodes must be >= 0", IGRAPH_EINVAL); if (type_dist_matrix) { if (igraph_matrix_nrow(type_dist_matrix) < types || igraph_matrix_ncol(type_dist_matrix) < types) IGRAPH_ERROR("type_dist_matrix too small", IGRAPH_EINVAL); else if (igraph_matrix_nrow(type_dist_matrix) > types || igraph_matrix_ncol(type_dist_matrix) > types) IGRAPH_WARNING("type_dist_matrix will be trimmed"); } if (igraph_matrix_nrow(pref_matrix) < types || igraph_matrix_ncol(pref_matrix) < types) IGRAPH_ERROR("pref_matrix too small", IGRAPH_EINVAL); IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types*types+1); if (node_type_in_vec) { nodetypes_in=node_type_in_vec; IGRAPH_CHECK(igraph_vector_resize(nodetypes_in, nodes)); } else { nodetypes_in = igraph_Calloc(1, igraph_vector_t); if (nodetypes_in == 0) { IGRAPH_ERROR("asymmetric_preference_game failed", IGRAPH_ENOMEM); } IGRAPH_VECTOR_INIT_FINALLY(nodetypes_in, nodes); } if (node_type_out_vec) { nodetypes_out=node_type_out_vec; IGRAPH_CHECK(igraph_vector_resize(nodetypes_out, nodes)); } else { nodetypes_out = igraph_Calloc(1, igraph_vector_t); if (nodetypes_out == 0) { IGRAPH_ERROR("asymmetric_preference_game failed", IGRAPH_ENOMEM); } IGRAPH_VECTOR_INIT_FINALLY(nodetypes_out, nodes); } IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_intype, types)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_intype); IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_outtype, types)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_outtype); for (i=0; i0) { for (kk=0; kk0) { c--; from--; if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) from--; } } igraph_vector_push_back(&edges, VECTOR(*v1)[from]); igraph_vector_push_back(&edges, VECTOR(*v2)[to]); } } else { for (kk=0; kk Note that this function modifies the input \p graph, * call \ref igraph_copy() if you want to keep it. * * \param graph The input graph, this will be rewired, it can be * directed or undirected. * \param prob The rewiring probability a constant between zero and * one (inclusive). * \param loops Boolean, whether loop edges are allowed in the new * graph, or not. * \param multiple Boolean, whether multiple edges are allowed in the * new graph. * \return Error code. * * \sa \ref igraph_watts_strogatz_game() uses this function for the * rewiring. * * Time complexity: O(|V|+|E|). */ int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_bool_t multiple) { igraph_t newgraph; long int no_of_edges=igraph_ecount(graph); long int no_of_nodes=igraph_vcount(graph); long int endpoints=no_of_edges*2; long int to_rewire; igraph_vector_t edges; if (prob < 0 || prob > 1) { IGRAPH_ERROR("Rewiring probability should be between zero and one", IGRAPH_EINVAL); } if (prob == 0) { /* This is easy, just leave things as they are */ return IGRAPH_SUCCESS; } IGRAPH_VECTOR_INIT_FINALLY(&edges, endpoints); RNG_BEGIN(); if (prob != 0 && no_of_edges > 0) { if (multiple) { /* If multiple edges are allowed, then there is an easy and fast method. Each endpoint of an edge is rewired with probability p, so the "skips" between the really rewired endpoints follow a geometric distribution. */ IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); to_rewire=(long int) RNG_GEOM(prob); while (to_rewire < endpoints) { if (loops) { VECTOR(edges)[to_rewire] = RNG_INTEGER(0, no_of_nodes-1); } else { long int opos = to_rewire % 2 ? to_rewire-1 : to_rewire+1; long int nei= (long int) VECTOR(edges)[opos]; long int r=RNG_INTEGER(0, no_of_nodes-2); VECTOR(edges)[ to_rewire ] = (r != nei ? r : no_of_nodes-1); } to_rewire += RNG_GEOM(prob)+1; } } else { IGRAPH_CHECK(igraph_i_rewire_edges_no_multiple(graph, prob, loops, &edges)); } } RNG_END(); IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, igraph_is_directed(graph))); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1,1,1); IGRAPH_FINALLY_CLEAN(1); igraph_destroy(graph); *graph=newgraph; return 0; } /** * \function igraph_watts_strogatz_game * \brief The Watts-Strogatz small-world model * * This function generates a graph according to the Watts-Strogatz * model of small-world networks. The graph is obtained by creating a * circular undirected lattice and then rewire the edges randomly with * a constant probability. * * See also: Duncan J Watts and Steven H Strogatz: * Collective dynamics of small world networks, Nature * 393, 440-442, 1998. * \param graph The graph to initialize. * \param dim The dimension of the lattice. * \param size The size of the lattice along each dimension. * \param nei The size of the neighborhood for each vertex. This is * the same as the \p nei argument of \ref * igraph_connect_neighborhood(). * \param p The rewiring probability. A real number between zero and * one (inclusive). * \param loops Logical, whether to generate loop edges. * \param multiple Logical, whether to allow multiple edges in the * generated graph. * \return Error code. * * \sa \ref igraph_lattice(), \ref igraph_connect_neighborhood() and * \ref igraph_rewire_edges() can be used if more flexibility is * needed, eg. a different type of lattice. * * Time complexity: O(|V|*d^o+|E|), |V| and |E| are the number of * vertices and edges, d is the average degree, o is the \p nei * argument. */ int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, igraph_integer_t size, igraph_integer_t nei, igraph_real_t p, igraph_bool_t loops, igraph_bool_t multiple) { igraph_vector_t dimvector; long int i; if (dim < 1) { IGRAPH_ERROR("WS game: dimension should be at least one", IGRAPH_EINVAL); } if (size < 1) { IGRAPH_ERROR("WS game: lattice size should be at least one", IGRAPH_EINVAL); } if (p < 0 || p > 1) { IGRAPH_ERROR("WS game: rewiring probability should be between 0 and 1", IGRAPH_EINVAL); } /* Create the lattice first */ IGRAPH_VECTOR_INIT_FINALLY(&dimvector, dim); for (i=0; i * The \p preference argument specifies the preferences for the * citation lags, ie. its first elements contains the attractivity * of the very recently cited vertices, etc. The last element is * special, it contains the attractivity of the vertices which were * never cited. This element should be bigger than zero. * * * Note that this function generates networks with multiple edges if * \p edges_per_step is bigger than one, call \ref igraph_simplify() * on the result to get rid of these edges. * \param graph Pointer to an uninitialized graph object, the result * will be stored here. * \param node The number of vertices in the network. * \param edges_per_node The number of edges to add in each time * step. * \param pagebins The number of age bins to use. * \param preference Pointer to an initialized vector of length * \c pagebins+1. This contains the `attractivity' of the various * age bins, the last element is the attractivity of the vertices * which were never cited, and it should be greater than zero. * It is a good idea to have all positive values in this vector. * \param directed Logical constant, whether to create directed * networks. * \return Error code. * * \sa \ref igraph_barabasi_aging_game(). * * Time complexity: O(|V|*a+|E|*log|V|), |V| is the number of vertices, * |E| is the total number of edges, a is the \p pagebins parameter. */ int igraph_lastcit_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t edges_per_node, igraph_integer_t pagebins, const igraph_vector_t *preference, igraph_bool_t directed) { long int no_of_nodes=nodes; igraph_psumtree_t sumtree; igraph_vector_t edges; long int i, j, k; long int *lastcit; long int *index; long int agebins=pagebins; long int binwidth=no_of_nodes/agebins+1; if (agebins != igraph_vector_size(preference)-1) { IGRAPH_ERROR("`preference' vector should be of length `agebins' plus one", IGRAPH_EINVAL); } if (agebins <=1 ) { IGRAPH_ERROR("at least two age bins are need for lastcit game", IGRAPH_EINVAL); } if (VECTOR(*preference)[agebins] <= 0) { IGRAPH_ERROR("the last element of the `preference' vector needs to be positive", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); lastcit=igraph_Calloc(no_of_nodes, long int); if (!lastcit) { IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, lastcit); index=igraph_Calloc(no_of_nodes+1, long int); if (!index) { IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, index); IGRAPH_CHECK(igraph_psumtree_init(&sumtree, nodes)); IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes*edges_per_node)); /* The first node */ igraph_psumtree_update(&sumtree, 0, VECTOR(*preference)[agebins]); index[0]=0; index[1]=0; RNG_BEGIN(); for (i=1; i= 1; k++) { long int shnode=i-binwidth*k; long int m=index[shnode], n=index[shnode+1]; for (j=2*m; j<2*n; j+=2) { long int cnode=(long int) VECTOR(edges)[j+1]; if (lastcit[cnode]==shnode+1) { igraph_psumtree_update(&sumtree, cnode, VECTOR(*preference)[k]); } } } } RNG_END(); igraph_psumtree_destroy(&sumtree); igraph_free(index); igraph_free(lastcit); IGRAPH_FINALLY_CLEAN(3); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_cited_type_game * \brief Simulate a citation based on vertex types. * * Function to create a network based on some vertex categories. This * function creates a citation network, in each step a single vertex * and \p edges_per_step citating edges are added, nodes with * different categories (may) have different probabilities to get * cited, as given by the \p pref vector. * * * Note that this function might generate networks with multiple edges * if \p edges_per_step is greater than one. You might want to call * \ref igraph_simplify() on the result to remove multiple edges. * \param graph Pointer to an uninitialized graph object. * \param nodes The number of vertices in the network. * \param types Numeric vector giving the categories of the vertices, * so it should contain \p nodes non-negative integer * numbers. Types are numbered from zero. * \param pref The attractivity of the different vertex categories in * a vector. Its length should be the maximum element in \p types * plus one (types are numbered from zero). * \param edges_per_step Integer constant, the number of edges to add * in each time step. * \param directed Logical constant, whether to create a directed * network. * \return Error code. * * \sa \ref igraph_citing_cited_type_game() for a bit more general * game. * * Time complexity: O((|V|+|E|)log|V|), |V| and |E| are number of * vertices and edges, respectively. */ int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, const igraph_vector_t *types, const igraph_vector_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed) { igraph_vector_t edges; igraph_vector_t cumsum; igraph_real_t sum; long int i,j; IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumsum, 2); IGRAPH_CHECK(igraph_vector_reserve(&cumsum, nodes+1)); IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes*edges_per_step)); /* first node */ VECTOR(cumsum)[0]=0; sum=VECTOR(cumsum)[1]=VECTOR(*pref)[ (long int) VECTOR(*types)[0] ]; RNG_BEGIN(); for (i=1; isumtrees) { return; } for (i=0; ino; i++) { igraph_psumtree_destroy(&s->sumtrees[i]); } } /** * \function igraph_citing_cited_type_game * \brief Simulate a citation network based on vertex types. * * This game is similar to \ref igraph_cited_type_game() but here the * category of the citing vertex is also considered. * * * An evolving citation network is modeled here, a single vertex and * its \p edges_per_step citation are added in each time step. The * odds the a given vertex is cited by the new vertex depends on the * category of both the citing and the cited vertex and is given in * the \p pref matrix. The categories of the citing vertex correspond * to the rows, the categories of the cited vertex to the columns of * this matrix. Ie. the element in row \c i and column \c j gives the * probability that a \c j vertex is cited, if the category of the * citing vertex is \c i. * * * Note that this function might generate networks with multiple edges * if \p edges_per_step is greater than one. You might want to call * \ref igraph_simplify() on the result to remove multiple edges. * \param graph Pointer to an uninitialized graph object. * \param nodes The number of vertices in the network. * \param types A numeric matrix of length \p nodes, containing the * categories of the vertices. The categories are numbered from * zero. * \param pref The preference matrix, a square matrix is required, * both the number of rows and columns should be the maximum * element in \p types plus one (types are numbered from zero). * \param directed Logical constant, whether to create a directed * network. * \return Error code. * * Time complexity: O((|V|+|E|)log|V|), |V| and |E| are number of * vertices and edges, respectively. */ int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, const igraph_vector_t *types, const igraph_matrix_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed) { igraph_vector_t edges; igraph_i_citing_cited_type_game_struct_t str = { 0, 0 }; igraph_psumtree_t *sumtrees; igraph_vector_t sums; long int nocats=igraph_matrix_ncol(pref); long int i, j; IGRAPH_VECTOR_INIT_FINALLY(&edges,0); str.sumtrees=sumtrees=igraph_Calloc(nocats, igraph_psumtree_t); if (!sumtrees) { IGRAPH_ERROR("Citing-cited type game failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_i_citing_cited_type_game_free, &str); for (i=0; i1) { IGRAPH_ERROR("Invalid probability for islands", IGRAPH_EINVAL); } if ( (n_inter<0) || (n_inter>islands_size) ) { IGRAPH_ERROR("Invalid number of inter-islands links", IGRAPH_EINVAL); } // how much memory ? nbNodes = islands_n*islands_size; maxpossibleedgesPerIsland = ((double)islands_size*((double)islands_size-(double)1))/(double)2; maxedgesPerIsland = islands_pin*maxpossibleedgesPerIsland; nbEdgesInterIslands = n_inter*(islands_n*(islands_n-1))/2; maxedges = maxedgesPerIsland*islands_n + nbEdgesInterIslands; // debug&tests : printf("total nodes %d, maxedgesperisland %f, maxedgesinterislands %d, maxedges %f\n", nbNodes, maxedgesPerIsland, nbEdgesInterIslands, maxedges); // reserve enough place for all the edges, thanks ! IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, (long int) maxedges)); RNG_BEGIN(); // first create all the islands for (is=1; is<=islands_n; is++) { // for each island // index for start and end of nodes in this island startIsland = islands_size*(is-1); endIsland = startIsland+islands_size -1; // debug&tests : printf("start %d,end %d\n", startIsland, endIsland); // create the random numbers to be used (into s) IGRAPH_VECTOR_INIT_FINALLY(&s, 0); IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) maxedgesPerIsland)); last=RNG_GEOM(islands_pin); // debug&tests : printf("last=%f \n", last); while (last < maxpossibleedgesPerIsland) { // maxedgesPerIsland IGRAPH_CHECK(igraph_vector_push_back(&s, last)); myrand = RNG_GEOM(islands_pin); last += myrand; //RNG_GEOM(islands_pin); //printf("myrand=%f , last=%f \n", myrand, last); last += 1; } // change this to edges ! for (i=0; i * The generation process goes as follows. We start from N disconnected nodes * (where N is given by the length of the fitness vector). Then we randomly * select two vertices i and j, with probabilities proportional to their * fitnesses. (When the generated graph is directed, i is selected according to * the out-fitnesses and j is selected according to the in-fitnesses). If the * vertices are not connected yet (or if multiple edges are allowed), we * connect them; otherwise we select a new pair. This is repeated until the * desired number of links are created. * * * It can be shown that the \em expected degree of each vertex will be * proportional to its fitness, although the actual, observed degree will not * be. If you need to generate a graph with an exact degree sequence, consider * \ref igraph_degree_sequence_game instead. * * * This model is commonly used to generate static scale-free networks. To * achieve this, you have to draw the fitness scores from the desired power-law * distribution. Alternatively, you may use \ref igraph_static_power_law_game * which generates the fitnesses for you with a given exponent. * * * Reference: Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution * in scale-free networks. Phys Rev Lett 87(27):278701, 2001. * * \param graph Pointer to an uninitialized graph object. * \param fitness_out A numeric vector containing the fitness of each vertex. * For directed graphs, this specifies the out-fitness * of each vertex. * \param fitness_in If \c NULL, the generated graph will be undirected. * If not \c NULL, this argument specifies the in-fitness * of each vertex. * \param no_of_edges The number of edges in the generated graph. * \param loops Whether to allow loop edges in the generated graph. * \param multiple Whether to allow multiple edges in the generated graph. * * \return Error code: * \c IGRAPH_EINVAL: invalid parameter * \c IGRAPH_ENOMEM: there is not enough * memory for the operation. * * Time complexity: O(|V| + |E| log |E|). */ int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, igraph_vector_t* fitness_out, igraph_vector_t* fitness_in, igraph_bool_t loops, igraph_bool_t multiple) { igraph_vector_t edges=IGRAPH_VECTOR_NULL; igraph_integer_t no_of_nodes; igraph_vector_t cum_fitness_in, cum_fitness_out; igraph_vector_t *p_cum_fitness_in, *p_cum_fitness_out; igraph_real_t x, max_in, max_out; igraph_bool_t is_directed = (fitness_in != 0); float num_steps; long int from, to, pos; if (fitness_out == 0) { IGRAPH_ERROR("fitness_out must not be null", IGRAPH_EINVAL); } if (no_of_edges < 0) { IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL); } no_of_nodes = (int) igraph_vector_size(fitness_out); if (no_of_nodes == 0) { IGRAPH_CHECK(igraph_empty(graph, 0, is_directed)); return IGRAPH_SUCCESS; } /* Sanity checks for the fitnesses */ if (igraph_vector_min(fitness_out) < 0) { IGRAPH_ERROR("Fitness scores must be non-negative", IGRAPH_EINVAL); } if (fitness_in != 0 && igraph_vector_min(fitness_in) < 0) { IGRAPH_ERROR("Fitness scores must be non-negative", IGRAPH_EINVAL); } /* Calculate the cumulative fitness scores */ IGRAPH_VECTOR_INIT_FINALLY(&cum_fitness_out, no_of_nodes); IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_out, fitness_out)); max_out = igraph_vector_tail(&cum_fitness_out); p_cum_fitness_out = &cum_fitness_out; if (is_directed) { IGRAPH_VECTOR_INIT_FINALLY(&cum_fitness_in, no_of_nodes); IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_in, fitness_in)); max_in = igraph_vector_tail(&cum_fitness_in); p_cum_fitness_in = &cum_fitness_in; } else { max_in = max_out; p_cum_fitness_in = &cum_fitness_out; } RNG_BEGIN(); num_steps = no_of_edges; if (multiple) { /* Generating when multiple edges are allowed */ IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * no_of_edges)); while (no_of_edges > 0) { /* Report progress after every 10000 edges */ if (no_of_edges % 10000 == 0) { IGRAPH_PROGRESS("Static fitness game", 100.0*(1 - no_of_edges/num_steps), NULL); IGRAPH_ALLOW_INTERRUPTION(); } x = RNG_UNIF(0, max_out); igraph_vector_binsearch(p_cum_fitness_out, x, &from); x = RNG_UNIF(0, max_in); igraph_vector_binsearch(p_cum_fitness_in, x, &to); /* Skip if loop edge and loops = false */ if (!loops && from == to) continue; igraph_vector_push_back(&edges, from); igraph_vector_push_back(&edges, to); no_of_edges--; } /* Create the graph */ IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, is_directed)); /* Clear the edge list */ igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } else { /* Multiple edges are disallowed */ igraph_adjlist_t al; igraph_vector_int_t* neis; IGRAPH_CHECK(igraph_adjlist_init_empty(&al, no_of_nodes)); IGRAPH_FINALLY(igraph_adjlist_destroy, &al); while (no_of_edges > 0) { /* Report progress after every 10000 edges */ if (no_of_edges % 10000 == 0) { IGRAPH_PROGRESS("Static fitness game", 100.0*(1 - no_of_edges/num_steps), NULL); IGRAPH_ALLOW_INTERRUPTION(); } x = RNG_UNIF(0, max_out); igraph_vector_binsearch(p_cum_fitness_out, x, &from); x = RNG_UNIF(0, max_in); igraph_vector_binsearch(p_cum_fitness_in, x, &to); /* Skip if loop edge and loops = false */ if (!loops && from == to) continue; /* For undirected graphs, ensure that from < to */ if (!is_directed && from > to) { pos = from; from = to; to = pos; } /* Is there already an edge? If so, try again */ neis = igraph_adjlist_get(&al, from); if (igraph_vector_int_binsearch(neis, to, &pos)) continue; /* Insert the edge */ IGRAPH_CHECK(igraph_vector_int_insert(neis, pos, to)); no_of_edges--; } /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs * because we did not add edges in both directions in the adjacency list. * We will use igraph_to_undirected in an extra step. */ IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); if (!is_directed) IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0)); /* Clear the adjacency list */ igraph_adjlist_destroy(&al); IGRAPH_FINALLY_CLEAN(1); } RNG_END(); IGRAPH_PROGRESS("Static fitness game", 100.0, NULL); /* Cleanup before we create the graph */ if (is_directed) { igraph_vector_destroy(&cum_fitness_in); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&cum_fitness_out); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * \ingroup generators * \function igraph_static_power_law_game * \brief Generates a non-growing random graph with expected power-law degree distributions. * * This game generates a directed or undirected random graph where the * degrees of vertices follow power-law distributions with prescribed * exponents. For directed graphs, the exponents of the in- and out-degree * distributions may be specified separately. * * * The game simply uses \ref igraph_static_fitness_game with appropriately * constructed fitness vectors. In particular, the fitness of vertex i * is i-alpha, where alpha = 1/(gamma-1) * and gamma is the exponent given in the arguments. * * * To remove correlations between in- and out-degrees in case of directed * graphs, the in-fitness vector will be shuffled after it has been set up * and before \ref igraph_static_fitness_game is called. * * * Note that significant finite size effects may be observed for exponents * smaller than 3 in the original formulation of the game. This function * provides an argument that lets you remove the finite size effects by * assuming that the fitness of vertex i is * (i+i0-1)-alpha, * where i0 is a constant chosen appropriately to ensure that the maximum * degree is less than the square root of the number of edges times the * average degree; see the paper of Chung and Lu, and Cho et al for more * details. * * * References: * * * Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution * in scale-free networks. Phys Rev Lett 87(27):278701, 2001. * * * Chung F and Lu L: Connected components in a random graph with given * degree sequences. Annals of Combinatorics 6, 125-145, 2002. * * * Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in * scale-free networks under the Achlioptas process. Phys Rev Lett * 103:135702, 2009. * * \param graph Pointer to an uninitialized graph object. * \param no_of_nodes The number of nodes in the generated graph. * \param no_of_edges The number of edges in the generated graph. * \param exponent_out The power law exponent of the degree distribution. * For directed graphs, this specifies the exponent of the * out-degree distribution. It must be greater than or * equal to 2. If you pass \c IGRAPH_INFINITY here, you * will get back an Erdos-Renyi random network. * \param exponent_in If negative, the generated graph will be undirected. * If greater than or equal to 2, this argument specifies * the exponent of the in-degree distribution. If * non-negative but less than 2, an error will be * generated. * \param loops Whether to allow loop edges in the generated graph. * \param multiple Whether to allow multiple edges in the generated graph. * \param finite_size_correction Whether to use the proposed finite size * correction of Cho et al. * * \return Error code: * \c IGRAPH_EINVAL: invalid parameter * \c IGRAPH_ENOMEM: there is not enough * memory for the operation. * * Time complexity: O(|V| + |E| log |E|). */ int igraph_static_power_law_game(igraph_t *graph, igraph_integer_t no_of_nodes, igraph_integer_t no_of_edges, igraph_real_t exponent_out, igraph_real_t exponent_in, igraph_bool_t loops, igraph_bool_t multiple, igraph_bool_t finite_size_correction) { igraph_vector_t fitness_out, fitness_in; igraph_real_t alpha_out = 0.0, alpha_in = 0.0; long int i; igraph_real_t j; if (no_of_nodes < 0) { IGRAPH_ERROR("Invalid number of nodes", IGRAPH_EINVAL); } /* Calculate alpha_out */ if (exponent_out < 2) { IGRAPH_ERROR("out-degree exponent must be >= 2", IGRAPH_EINVAL); } else if (igraph_finite(exponent_out)) { alpha_out = -1.0 / (exponent_out - 1); } else { alpha_out = 0.0; } /* Construct the out-fitnesses */ IGRAPH_VECTOR_INIT_FINALLY(&fitness_out, no_of_nodes); j = no_of_nodes; if (finite_size_correction && alpha_out < -0.5) { /* See the Cho et al paper, first page first column + footnote 7 */ j += pow(no_of_nodes, 1 + 0.5 / alpha_out) * pow(10*sqrt(2)*(1 + alpha_out), -1.0 / alpha_out)-1; } if (j < no_of_nodes) j = no_of_nodes; for (i = 0; i < no_of_nodes; i++, j--) { VECTOR(fitness_out)[i] = pow(j, alpha_out); } if (exponent_in >= 0) { if (exponent_in < 2) { IGRAPH_ERROR("in-degree exponent must be >= 2; use negative numbers " "for undirected graphs", IGRAPH_EINVAL); } else if (igraph_finite(exponent_in)) { alpha_in = -1.0 / (exponent_in - 1); } else { alpha_in = 0.0; } IGRAPH_VECTOR_INIT_FINALLY(&fitness_in, no_of_nodes); j = no_of_nodes; if (finite_size_correction && alpha_in < -0.5) { /* See the Cho et al paper, first page first column + footnote 7 */ j += pow(no_of_nodes, 1 + 0.5 / alpha_in) * pow(10*sqrt(2)*(1 + alpha_in), -1.0 / alpha_in)-1; } if (j < no_of_nodes) j = no_of_nodes; for (i = 0; i < no_of_nodes; i++, j--) { VECTOR(fitness_in)[i] = pow(j, alpha_in); } IGRAPH_CHECK(igraph_vector_shuffle(&fitness_in)); IGRAPH_CHECK(igraph_static_fitness_game(graph, no_of_edges, &fitness_out, &fitness_in, loops, multiple)); igraph_vector_destroy(&fitness_in); IGRAPH_FINALLY_CLEAN(1); } else { IGRAPH_CHECK(igraph_static_fitness_game(graph, no_of_edges, &fitness_out, 0, loops, multiple)); } igraph_vector_destroy(&fitness_out); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * \ingroup generators * \function igraph_k_regular_game * \brief Generates a random graph where each vertex has the same degree. * * This game generates a directed or undirected random graph where the * degrees of vertices are equal to a predefined constant k. For undirected * graphs, at least one of k and the number of vertices must be even. * * * The game simply uses \ref igraph_degree_sequence_game with appropriately * constructed degree sequences. * * \param graph Pointer to an uninitialized graph object. * \param no_of_nodes The number of nodes in the generated graph. * \param k The degree of each vertex in an undirected graph, or * the out-degree and in-degree of each vertex in a * directed graph. * \param directed Whether the generated graph will be directed. * \param multiple Whether to allow multiple edges in the generated graph. * * \return Error code: * \c IGRAPH_EINVAL: invalid parameter; e.g., negative number of nodes, * or odd number of nodes and odd k for undirected * graphs. * \c IGRAPH_ENOMEM: there is not enough memory for the operation. * * Time complexity: O(|V|+|E|) if \c multiple is true, otherwise not known. */ int igraph_k_regular_game(igraph_t *graph, igraph_integer_t no_of_nodes, igraph_integer_t k, igraph_bool_t directed, igraph_bool_t multiple) { igraph_vector_t degseq; igraph_degseq_t mode = multiple ? IGRAPH_DEGSEQ_SIMPLE : IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE; /* Note to self: we are not using IGRAPH_DEGSEQ_VL when multiple = false * because the VL method is not really good at generating k-regular graphs. * Actually, that's why we have added SIMPLE_NO_MULTIPLE. */ if (no_of_nodes < 0) { IGRAPH_ERROR("number of nodes must be non-negative", IGRAPH_EINVAL); } if (k < 0) { IGRAPH_ERROR("degree must be non-negative", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(°seq, no_of_nodes); igraph_vector_fill(°seq, k); IGRAPH_CHECK(igraph_degree_sequence_game(graph, °seq, directed ? °seq : 0, mode)); igraph_vector_destroy(°seq); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } igraph/src/maximal_cliques.c0000644000176000001440000003072712325527073015703 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2013 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_cliques.h" #include "igraph_constants.h" #include "igraph_interface.h" #include "igraph_community.h" #include "igraph_adjlist.h" #include "igraph_interrupt_internal.h" #include "igraph_memory.h" #include "igraph_progress.h" #include "igraph_math.h" #define CONCAT2x(a,b) a ## b #define CONCAT2(a,b) CONCAT2x(a,b) #define FUNCTION(name,sfx) CONCAT2(name,sfx) int igraph_i_maximal_cliques_reorder_adjlists( const igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, igraph_adjlist_t *adjlist); int igraph_i_maximal_cliques_select_pivot(const igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, const igraph_adjlist_t *adjlist, int *pivot, igraph_vector_int_t *nextv, int oldPS, int oldXE); int igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, int mynextv, igraph_vector_int_t *R, int *newPS, int *newXE); int igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, int PS, int *PE, int *XS, int XE, igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, int v, igraph_vector_int_t *H); int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, igraph_vector_int_t *R, igraph_vector_int_t *H); #define PRINT_PX do { \ int j; \ printf("PX="); \ for (j=0; j= sPS && avneipos <= sPE) { if (pp != avnei) { int tmp=*avnei; *avnei = *pp; *pp = tmp; } pp++; } } } return 0; } int igraph_i_maximal_cliques_select_pivot(const igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, const igraph_adjlist_t *adjlist, int *pivot, igraph_vector_int_t *nextv, int oldPS, int oldXE) { igraph_vector_int_t *pivotvectneis; int i, pivotvectlen, j, usize=-1; int soldPS=oldPS+1, soldXE=oldXE+1, sPS=PS+1, sPE=PE+1; /* Choose a pivotvect, and bring up P vertices at the same time */ for (i=PS; i<=XE; i++) { int av=VECTOR(*PX)[i]; igraph_vector_int_t *avneis=igraph_adjlist_get(adjlist, av); int *avp=VECTOR(*avneis); int avlen=igraph_vector_int_size(avneis); int *ave=avp+avlen; int *avnei=avp, *pp=avp; for (; avnei < ave; avnei++) { int avneipos=VECTOR(*pos)[(int)(*avnei)]; if (avneipos < soldPS || avneipos > soldXE) { break; } if (avneipos >= sPS && avneipos <= sPE) { if (pp != avnei) { int tmp=*avnei; *avnei = *pp; *pp = tmp; } pp++; } } if ((j=pp-avp) > usize) { *pivot = av; usize=j; } } igraph_vector_int_push_back(nextv, -1); pivotvectneis=igraph_adjlist_get(adjlist, *pivot); pivotvectlen=igraph_vector_int_size(pivotvectneis); for (j=PS; j <= PE; j++) { int vcand=VECTOR(*PX)[j]; igraph_bool_t nei=0; int k=0; for (k=0; k < pivotvectlen; k++) { int unv=VECTOR(*pivotvectneis)[k]; int unvpos=VECTOR(*pos)[unv]; if (unvpos < sPS || unvpos > sPE) { break; } if (unv == vcand) { nei=1; break; } } if (!nei) { igraph_vector_int_push_back(nextv, vcand); } } return 0; } #define SWAP(p1,p2) do { \ int v1=VECTOR(*PX)[p1]; \ int v2=VECTOR(*PX)[p2]; \ VECTOR(*PX)[p1] = v2; \ VECTOR(*PX)[p2] = v1; \ VECTOR(*pos)[v1] = (p2)+1; \ VECTOR(*pos)[v2] = (p1)+1; \ } while (0) int igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, int mynextv, igraph_vector_int_t *R, int *newPS, int *newXE) { igraph_vector_int_t *vneis=igraph_adjlist_get(adjlist, mynextv); int j, vneislen=igraph_vector_int_size(vneis); int sPS=PS+1, sPE=PE+1, sXS=XS+1, sXE=XE+1; *newPS=PE+1; *newXE=XS-1; for (j=0; j= sPS && vneipos <= sPE) { (*newPS)--; SWAP(vneipos-1, *newPS); } else if (vneipos >= sXS && vneipos <= sXE) { (*newXE)++; SWAP(vneipos-1, *newXE); } } igraph_vector_int_push_back(R, mynextv); return 0; } #undef SWAP int igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, int PS, int *PE, int *XS, int XE, igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, int v, igraph_vector_int_t *H) { int vpos=VECTOR(*pos)[v]-1; int tmp=VECTOR(*PX)[*PE]; VECTOR(*PX)[vpos]=tmp; VECTOR(*PX)[*PE]=v; VECTOR(*pos)[v]=(*PE)+1; VECTOR(*pos)[tmp]=vpos+1; (*PE)--; (*XS)--; igraph_vector_int_push_back(H, v); return 0; } int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, igraph_vector_int_t *R, igraph_vector_int_t *H) { int vv; igraph_vector_int_pop_back(R); while ((vv=igraph_vector_int_pop_back(H)) != -1) { int vvpos=VECTOR(*pos)[vv]; int tmp=VECTOR(*PX)[XS]; VECTOR(*PX)[XS]=vv; VECTOR(*PX)[vvpos-1]=tmp; VECTOR(*pos)[vv]=XS+1; VECTOR(*pos)[tmp]=vvpos; PE++; XS++; } return 0; } /** * \function igraph_maximal_cliques * \brief Find all maximal cliques of a graph * * * A maximal clique is a clique which can't be extended any more by * adding a new vertex to it. * * * If you are only interested in the size of the largest clique in the * graph, use \ref igraph_clique_number() instead. * * * The current implementation uses a modified Bron-Kerbosch * algorithm to find the maximal cliques, see: David Eppstein, * Maarten Löffler, Darren Strash: Listing All Maximal Cliques in * Sparse Graphs in Near-Optimal Time. Algorithms and Computation, * Lecture Notes in Computer Science Volume 6506, 2010, pp 403-414. * * The implementation of this function changed between * igraph 0.5 and 0.6 and also between 0.6 and 0.7, so the order of * the cliques and the order of vertices within the cliques will * almost surely be different between these three versions. * * \param graph The input graph. * \param res Pointer to a pointer vector, the result will be stored * here, ie. \c res will contain pointers to \c igraph_vector_t * objects which contain the indices of vertices involved in a clique. * The pointer vector will be resized if needed but note that the * objects in the pointer vector will not be freed. Note that vertices * of a clique may be returned in arbitrary order. * \param min_size Integer giving the minimum size of the cliques to be * returned. If negative or zero, no lower bound will be used. * \param max_size Integer giving the maximum size of the cliques to be * returned. If negative or zero, no upper bound will be used. * \return Error code. * * \sa \ref igraph_maximal_independent_vertex_sets(), \ref * igraph_clique_number() * * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy * of the graph, this is typically small for sparse graphs. * * \example examples/simple/igraph_maximal_cliques.c */ int igraph_maximal_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size); #define IGRAPH_MC_ORIG #include "maximal_cliques_template.h" #undef IGRAPH_MC_ORIG /** * \function igraph_maximal_cliques_count * Count the number of maximal cliques in a graph * * * The current implementation uses a modified Bron-Kerbosch * algorithm to find the maximal cliques, see: David Eppstein, * Maarten Löffler, Darren Strash: Listing All Maximal Cliques in * Sparse Graphs in Near-Optimal Time. Algorithms and Computation, * Lecture Notes in Computer Science Volume 6506, 2010, pp 403-414. * * \param graph The input graph. * \param res Pointer to a pointer vector, the result will be stored * here, ie. \c res will contain pointers to \c igraph_vector_t * objects which contain the indices of vertices involved in a clique. * The pointer vector will be resized if needed but note that the * objects in the pointer vector will not be freed. Note that vertices * of a clique may be returned in arbitrary order. * \param min_size Integer giving the minimum size of the cliques to be * returned. If negative or zero, no lower bound will be used. * \param max_size Integer giving the maximum size of the cliques to be * returned. If negative or zero, no upper bound will be used. * \return Error code. * * \sa \ref igraph_maximal_cliques(). * * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy * of the graph, this is typically small for sparse graphs. * * \example examples/simple/igraph_maximal_cliques.c */ int igraph_maximal_cliques_count(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t min_size, igraph_integer_t max_size); #define IGRAPH_MC_COUNT #include "maximal_cliques_template.h" #undef IGRAPH_MC_COUNT /** * \function igraph_maximal_cliques_file * Find maximal cliques and write them to a file * * TODO */ int igraph_maximal_cliques_file(const igraph_t *graph, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); #define IGRAPH_MC_FILE #include "maximal_cliques_template.h" #undef IGRAPH_MC_FILE /** * \function igraph_maximal_cliques_subset * Maximal cliques for a subset of initial vertices * * TODO */ int igraph_maximal_cliques_subset(const igraph_t *graph, igraph_vector_int_t *subset, igraph_vector_ptr_t *res, igraph_integer_t *no, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); #define IGRAPH_MC_FULL #include "maximal_cliques_template.h" #undef IGRAPH_MC_FULL igraph/src/glpavl.c0000644000176000001440000002621012325527073014003 0ustar ripleyusers/* glpavl.c (binary search tree) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpavl.h" AVL *avl_create_tree(int (*fcmp)(void *info, const void *key1, const void *key2), void *info) { /* create AVL tree */ AVL *tree; tree = xmalloc(sizeof(AVL)); tree->pool = dmp_create_pool(); tree->root = NULL; tree->fcmp = fcmp; tree->info = info; tree->size = 0; tree->height = 0; return tree; } int avl_strcmp(void *info, const void *key1, const void *key2) { /* compare character string keys */ xassert(info == info); return strcmp(key1, key2); } static AVLNODE *rotate_subtree(AVL *tree, AVLNODE *node); AVLNODE *avl_insert_node(AVL *tree, const void *key) { /* insert new node into AVL tree */ AVLNODE *p, *q, *r; short int flag; /* find an appropriate point for insertion */ p = NULL; q = tree->root; while (q != NULL) { p = q; if (tree->fcmp(tree->info, key, p->key) <= 0) { flag = 0; q = p->left; p->rank++; } else { flag = 1; q = p->right; } } /* create new node and insert it into the tree */ r = dmp_get_atom(tree->pool, sizeof(AVLNODE)); r->key = key; r->type = 0; r->link = NULL; r->rank = 1; r->up = p; r->flag = (short int)(p == NULL ? 0 : flag); r->bal = 0; r->left = NULL; r->right = NULL; tree->size++; if (p == NULL) tree->root = r; else if (flag == 0) p->left = r; else p->right = r; /* go upstairs to the root and correct all subtrees affected by insertion */ while (p != NULL) { if (flag == 0) { /* the height of the left subtree of [p] is increased */ if (p->bal > 0) { p->bal = 0; break; } if (p->bal < 0) { rotate_subtree(tree, p); break; } p->bal = -1; flag = p->flag; p = p->up; } else { /* the height of the right subtree of [p] is increased */ if (p->bal < 0) { p->bal = 0; break; } if (p->bal > 0) { rotate_subtree(tree, p); break; } p->bal = +1; flag = p->flag; p = p->up; } } /* if the root has been reached, the height of the entire tree is increased */ if (p == NULL) tree->height++; return r; } void avl_set_node_type(AVLNODE *node, int type) { /* assign the type field of specified node */ node->type = type; return; } void avl_set_node_link(AVLNODE *node, void *link) { /* assign the link field of specified node */ node->link = link; return; } AVLNODE *avl_find_node(AVL *tree, const void *key) { /* find node in AVL tree */ AVLNODE *p; int c; p = tree->root; while (p != NULL) { c = tree->fcmp(tree->info, key, p->key); if (c == 0) break; p = (c < 0 ? p->left : p->right); } return p; } int avl_get_node_type(AVLNODE *node) { /* retrieve the type field of specified node */ return node->type; } void *avl_get_node_link(AVLNODE *node) { /* retrieve the link field of specified node */ return node->link; } static AVLNODE *find_next_node(AVL *tree, AVLNODE *node) { /* find next node in AVL tree */ AVLNODE *p, *q; if (tree->root == NULL) return NULL; p = node; q = (p == NULL ? tree->root : p->right); if (q == NULL) { /* go upstairs from the left subtree */ for (;;) { q = p->up; if (q == NULL) break; if (p->flag == 0) break; p = q; } } else { /* go downstairs into the right subtree */ for (;;) { p = q->left; if (p == NULL) break; q = p; } } return q; } void avl_delete_node(AVL *tree, AVLNODE *node) { /* delete specified node from AVL tree */ AVLNODE *f, *p, *q, *r, *s, *x, *y; short int flag; p = node; /* if both subtrees of the specified node are non-empty, the node should be interchanged with the next one, at least one subtree of which is always empty */ if (p->left == NULL || p->right == NULL) goto skip; f = p->up; q = p->left; r = find_next_node(tree, p); s = r->right; if (p->right == r) { if (f == NULL) tree->root = r; else if (p->flag == 0) f->left = r; else f->right = r; r->rank = p->rank; r->up = f; r->flag = p->flag; r->bal = p->bal; r->left = q; r->right = p; q->up = r; p->rank = 1; p->up = r; p->flag = 1; p->bal = (short int)(s == NULL ? 0 : +1); p->left = NULL; p->right = s; if (s != NULL) s->up = p; } else { x = p->right; y = r->up; if (f == NULL) tree->root = r; else if (p->flag == 0) f->left = r; else f->right = r; r->rank = p->rank; r->up = f; r->flag = p->flag; r->bal = p->bal; r->left = q; r->right = x; q->up = r; x->up = r; y->left = p; p->rank = 1; p->up = y; p->flag = 0; p->bal = (short int)(s == NULL ? 0 : +1); p->left = NULL; p->right = s; if (s != NULL) s->up = p; } skip: /* now the specified node [p] has at least one empty subtree; go upstairs to the root and adjust the rank field of all nodes affected by deletion */ q = p; f = q->up; while (f != NULL) { if (q->flag == 0) f->rank--; q = f; f = q->up; } /* delete the specified node from the tree */ f = p->up; flag = p->flag; q = p->left != NULL ? p->left : p->right; if (f == NULL) tree->root = q; else if (flag == 0) f->left = q; else f->right = q; if (q != NULL) q->up = f, q->flag = flag; tree->size--; /* go upstairs to the root and correct all subtrees affected by deletion */ while (f != NULL) { if (flag == 0) { /* the height of the left subtree of [f] is decreased */ if (f->bal == 0) { f->bal = +1; break; } if (f->bal < 0) f->bal = 0; else { f = rotate_subtree(tree, f); if (f->bal < 0) break; } flag = f->flag; f = f->up; } else { /* the height of the right subtree of [f] is decreased */ if (f->bal == 0) { f->bal = -1; break; } if (f->bal > 0) f->bal = 0; else { f = rotate_subtree(tree, f); if (f->bal > 0) break; } flag = f->flag; f = f->up; } } /* if the root has been reached, the height of the entire tree is decreased */ if (f == NULL) tree->height--; /* returns the deleted node to the memory pool */ dmp_free_atom(tree->pool, p, sizeof(AVLNODE)); return; } static AVLNODE *rotate_subtree(AVL *tree, AVLNODE *node) { /* restore balance of AVL subtree */ AVLNODE *f, *p, *q, *r, *x, *y; xassert(node != NULL); p = node; if (p->bal < 0) { /* perform negative (left) rotation */ f = p->up; q = p->left; r = q->right; if (q->bal <= 0) { /* perform single negative rotation */ if (f == NULL) tree->root = q; else if (p->flag == 0) f->left = q; else f->right = q; p->rank -= q->rank; q->up = f; q->flag = p->flag; q->bal++; q->right = p; p->up = q; p->flag = 1; p->bal = (short int)(-q->bal); p->left = r; if (r != NULL) r->up = p, r->flag = 0; node = q; } else { /* perform double negative rotation */ x = r->left; y = r->right; if (f == NULL) tree->root = r; else if (p->flag == 0) f->left = r; else f->right = r; p->rank -= (q->rank + r->rank); r->rank += q->rank; p->bal = (short int)(r->bal >= 0 ? 0 : +1); q->bal = (short int)(r->bal <= 0 ? 0 : -1); r->up = f; r->flag = p->flag; r->bal = 0; r->left = q; r->right = p; p->up = r; p->flag = 1; p->left = y; q->up = r; q->flag = 0; q->right = x; if (x != NULL) x->up = q, x->flag = 1; if (y != NULL) y->up = p, y->flag = 0; node = r; } } else { /* perform positive (right) rotation */ f = p->up; q = p->right; r = q->left; if (q->bal >= 0) { /* perform single positive rotation */ if (f == NULL) tree->root = q; else if (p->flag == 0) f->left = q; else f->right = q; q->rank += p->rank; q->up = f; q->flag = p->flag; q->bal--; q->left = p; p->up = q; p->flag = 0; p->bal = (short int)(-q->bal); p->right = r; if (r != NULL) r->up = p, r->flag = 1; node = q; } else { /* perform double positive rotation */ x = r->left; y = r->right; if (f == NULL) tree->root = r; else if (p->flag == 0) f->left = r; else f->right = r; q->rank -= r->rank; r->rank += p->rank; p->bal = (short int)(r->bal <= 0 ? 0 : -1); q->bal = (short int)(r->bal >= 0 ? 0 : +1); r->up = f; r->flag = p->flag; r->bal = 0; r->left = p; r->right = q; p->up = r; p->flag = 0; p->right = x; q->up = r; q->flag = 1; q->left = y; if (x != NULL) x->up = p, x->flag = 1; if (y != NULL) y->up = q, y->flag = 0; node = r; } } return node; } void avl_delete_tree(AVL *tree) { /* delete AVL tree */ dmp_delete_pool(tree->pool); xfree(tree); return; } /* eof */ igraph/src/glpnpp04.c0000644000176000001440000014275312325527073014175 0ustar ripleyusers/* glpnpp04.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #include "glpnpp.h" /*********************************************************************** * NAME * * npp_binarize_prob - binarize MIP problem * * SYNOPSIS * * #include "glpnpp.h" * int npp_binarize_prob(NPP *npp); * * DESCRIPTION * * The routine npp_binarize_prob replaces in the original MIP problem * every integer variable: * * l[q] <= x[q] <= u[q], (1) * * where l[q] < u[q], by an equivalent sum of binary variables. * * RETURNS * * The routine returns the number of integer variables for which the * transformation failed, because u[q] - l[q] > d_max. * * PROBLEM TRANSFORMATION * * If variable x[q] has non-zero lower bound, it is first processed * with the routine npp_lbnd_col. Thus, we can assume that: * * 0 <= x[q] <= u[q]. (2) * * If u[q] = 1, variable x[q] is already binary, so further processing * is not needed. Let, therefore, that 2 <= u[q] <= d_max, and n be a * smallest integer such that u[q] <= 2^n - 1 (n >= 2, since u[q] >= 2). * Then variable x[q] can be replaced by the following sum: * * n-1 * x[q] = sum 2^k x[k], (3) * k=0 * * where x[k] are binary columns (variables). If u[q] < 2^n - 1, the * following additional inequality constraint must be also included in * the transformed problem: * * n-1 * sum 2^k x[k] <= u[q]. (4) * k=0 * * Note: Assuming that in the transformed problem x[q] becomes binary * variable x[0], this transformation causes new n-1 binary variables * to appear. * * Substituting x[q] from (3) to the objective row gives: * * z = sum c[j] x[j] + c[0] = * j * * = sum c[j] x[j] + c[q] x[q] + c[0] = * j!=q * n-1 * = sum c[j] x[j] + c[q] sum 2^k x[k] + c[0] = * j!=q k=0 * n-1 * = sum c[j] x[j] + sum c[k] x[k] + c[0], * j!=q k=0 * * where: * * c[k] = 2^k c[q], k = 0, ..., n-1. (5) * * And substituting x[q] from (3) to i-th constraint row i gives: * * L[i] <= sum a[i,j] x[j] <= U[i] ==> * j * * L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> * j!=q * n-1 * L[i] <= sum a[i,j] x[j] + a[i,q] sum 2^k x[k] <= U[i] ==> * j!=q k=0 * n-1 * L[i] <= sum a[i,j] x[j] + sum a[i,k] x[k] <= U[i], * j!=q k=0 * * where: * * a[i,k] = 2^k a[i,q], k = 0, ..., n-1. (6) * * RECOVERING SOLUTION * * Value of variable x[q] is computed with formula (3). */ struct binarize { int q; /* column reference number for x[q] = x[0] */ int j; /* column reference number for x[1]; x[2] has reference number j+1, x[3] - j+2, etc. */ int n; /* total number of binary variables, n >= 2 */ }; static int rcv_binarize_prob(NPP *npp, void *info); int npp_binarize_prob(NPP *npp) { /* binarize MIP problem */ struct binarize *info; NPPROW *row; NPPCOL *col, *bin; NPPAIJ *aij; int u, n, k, temp, nfails, nvars, nbins, nrows; /* new variables will be added to the end of the column list, so we go from the end to beginning of the column list */ nfails = nvars = nbins = nrows = 0; for (col = npp->c_tail; col != NULL; col = col->prev) { /* skip continuous variable */ if (!col->is_int) continue; /* skip fixed variable */ if (col->lb == col->ub) continue; /* skip binary variable */ if (col->lb == 0.0 && col->ub == 1.0) continue; /* check if the transformation is applicable */ if (col->lb < -1e6 || col->ub > +1e6 || col->ub - col->lb > 4095.0) { /* unfortunately, not */ nfails++; continue; } /* process integer non-binary variable x[q] */ nvars++; /* make x[q] non-negative, if its lower bound is non-zero */ if (col->lb != 0.0) npp_lbnd_col(npp, col); /* now 0 <= x[q] <= u[q] */ xassert(col->lb == 0.0); u = (int)col->ub; xassert(col->ub == (double)u); /* if x[q] is binary, further processing is not needed */ if (u == 1) continue; /* determine smallest n such that u <= 2^n - 1 (thus, n is the number of binary variables needed) */ n = 2, temp = 4; while (u >= temp) n++, temp += temp; nbins += n; /* create transformation stack entry */ info = npp_push_tse(npp, rcv_binarize_prob, sizeof(struct binarize)); info->q = col->j; info->j = 0; /* will be set below */ info->n = n; /* if u < 2^n - 1, we need one additional row for (4) */ if (u < temp - 1) { row = npp_add_row(npp), nrows++; row->lb = -DBL_MAX, row->ub = u; } else row = NULL; /* in the transformed problem variable x[q] becomes binary variable x[0], so its objective and constraint coefficients are not changed */ col->ub = 1.0; /* include x[0] into constraint (4) */ if (row != NULL) npp_add_aij(npp, row, col, 1.0); /* add other binary variables x[1], ..., x[n-1] */ for (k = 1, temp = 2; k < n; k++, temp += temp) { /* add new binary variable x[k] */ bin = npp_add_col(npp); bin->is_int = 1; bin->lb = 0.0, bin->ub = 1.0; bin->coef = (double)temp * col->coef; /* store column reference number for x[1] */ if (info->j == 0) info->j = bin->j; else xassert(info->j + (k-1) == bin->j); /* duplicate constraint coefficients for x[k]; this also automatically includes x[k] into constraint (4) */ for (aij = col->ptr; aij != NULL; aij = aij->c_next) npp_add_aij(npp, aij->row, bin, (double)temp * aij->val); } } if (nvars > 0) xprintf("%d integer variable(s) were replaced by %d binary one" "s\n", nvars, nbins); if (nrows > 0) xprintf("%d row(s) were added due to binarization\n", nrows); if (nfails > 0) xprintf("Binarization failed for %d integer variable(s)\n", nfails); return nfails; } static int rcv_binarize_prob(NPP *npp, void *_info) { /* recovery binarized variable */ struct binarize *info = _info; int k, temp; double sum; /* compute value of x[q]; see formula (3) */ sum = npp->c_value[info->q]; for (k = 1, temp = 2; k < info->n; k++, temp += temp) sum += (double)temp * npp->c_value[info->j + (k-1)]; npp->c_value[info->q] = sum; return 0; } /**********************************************************************/ struct elem { /* linear form element a[j] x[j] */ double aj; /* non-zero coefficient value */ NPPCOL *xj; /* pointer to variable (column) */ struct elem *next; /* pointer to another term */ }; static struct elem *copy_form(NPP *npp, NPPROW *row, double s) { /* copy linear form */ NPPAIJ *aij; struct elem *ptr, *e; ptr = NULL; for (aij = row->ptr; aij != NULL; aij = aij->r_next) { e = dmp_get_atom(npp->pool, sizeof(struct elem)); e->aj = s * aij->val; e->xj = aij->col; e->next = ptr; ptr = e; } return ptr; } static void drop_form(NPP *npp, struct elem *ptr) { /* drop linear form */ struct elem *e; while (ptr != NULL) { e = ptr; ptr = e->next; dmp_free_atom(npp->pool, e, sizeof(struct elem)); } return; } /*********************************************************************** * NAME * * npp_is_packing - test if constraint is packing inequality * * SYNOPSIS * * #include "glpnpp.h" * int npp_is_packing(NPP *npp, NPPROW *row); * * RETURNS * * If the specified row (constraint) is packing inequality (see below), * the routine npp_is_packing returns non-zero. Otherwise, it returns * zero. * * PACKING INEQUALITIES * * In canonical format the packing inequality is the following: * * sum x[j] <= 1, (1) * j in J * * where all variables x[j] are binary. This inequality expresses the * condition that in any integer feasible solution at most one variable * from set J can take non-zero (unity) value while other variables * must be equal to zero. W.l.o.g. it is assumed that |J| >= 2, because * if J is empty or |J| = 1, the inequality (1) is redundant. * * In general case the packing inequality may include original variables * x[j] as well as their complements x~[j]: * * sum x[j] + sum x~[j] <= 1, (2) * j in Jp j in Jn * * where Jp and Jn are not intersected. Therefore, using substitution * x~[j] = 1 - x[j] gives the packing inequality in generalized format: * * sum x[j] - sum x[j] <= 1 - |Jn|. (3) * j in Jp j in Jn */ int npp_is_packing(NPP *npp, NPPROW *row) { /* test if constraint is packing inequality */ NPPCOL *col; NPPAIJ *aij; int b; xassert(npp == npp); if (!(row->lb == -DBL_MAX && row->ub != +DBL_MAX)) return 0; b = 1; for (aij = row->ptr; aij != NULL; aij = aij->r_next) { col = aij->col; if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) return 0; if (aij->val == +1.0) ; else if (aij->val == -1.0) b--; else return 0; } if (row->ub != (double)b) return 0; return 1; } /*********************************************************************** * NAME * * npp_hidden_packing - identify hidden packing inequality * * SYNOPSIS * * #include "glpnpp.h" * int npp_hidden_packing(NPP *npp, NPPROW *row); * * DESCRIPTION * * The routine npp_hidden_packing processes specified inequality * constraint, which includes only binary variables, and the number of * the variables is not less than two. If the original inequality is * equivalent to a packing inequality, the routine replaces it by this * equivalent inequality. If the original constraint is double-sided * inequality, it is replaced by a pair of single-sided inequalities, * if necessary. * * RETURNS * * If the original inequality constraint was replaced by equivalent * packing inequality, the routine npp_hidden_packing returns non-zero. * Otherwise, it returns zero. * * PROBLEM TRANSFORMATION * * Consider an inequality constraint: * * sum a[j] x[j] <= b, (1) * j in J * * where all variables x[j] are binary, and |J| >= 2. (In case of '>=' * inequality it can be transformed to '<=' format by multiplying both * its sides by -1.) * * Let Jp = {j: a[j] > 0}, Jn = {j: a[j] < 0}. Performing substitution * x[j] = 1 - x~[j] for all j in Jn, we have: * * sum a[j] x[j] <= b ==> * j in J * * sum a[j] x[j] + sum a[j] x[j] <= b ==> * j in Jp j in Jn * * sum a[j] x[j] + sum a[j] (1 - x~[j]) <= b ==> * j in Jp j in Jn * * sum a[j] x[j] - sum a[j] x~[j] <= b - sum a[j]. * j in Jp j in Jn j in Jn * * Thus, meaning the transformation above, we can assume that in * inequality (1) all coefficients a[j] are positive. Moreover, we can * assume that a[j] <= b. In fact, let a[j] > b; then the following * three cases are possible: * * 1) b < 0. In this case inequality (1) is infeasible, so the problem * has no feasible solution (see the routine npp_analyze_row); * * 2) b = 0. In this case inequality (1) is a forcing inequality on its * upper bound (see the routine npp_forcing row), from which it * follows that all variables x[j] should be fixed at zero; * * 3) b > 0. In this case inequality (1) defines an implied zero upper * bound for variable x[j] (see the routine npp_implied_bounds), from * which it follows that x[j] should be fixed at zero. * * It is assumed that all three cases listed above have been recognized * by the routine npp_process_prob, which performs basic MIP processing * prior to a call the routine npp_hidden_packing. So, if one of these * cases occurs, we should just skip processing such constraint. * * Thus, let 0 < a[j] <= b. Then it is obvious that constraint (1) is * equivalent to packing inquality only if: * * a[j] + a[k] > b + eps (2) * * for all j, k in J, j != k, where eps is an absolute tolerance for * row (linear form) value. Checking the condition (2) for all j and k, * j != k, requires time O(|J|^2). However, this time can be reduced to * O(|J|), if use minimal a[j] and a[k], in which case it is sufficient * to check the condition (2) only once. * * Once the original inequality (1) is replaced by equivalent packing * inequality, we need to perform back substitution x~[j] = 1 - x[j] for * all j in Jn (see above). * * RECOVERING SOLUTION * * None needed. */ static int hidden_packing(NPP *npp, struct elem *ptr, double *_b) { /* process inequality constraint: sum a[j] x[j] <= b; 0 - specified row is NOT hidden packing inequality; 1 - specified row is packing inequality; 2 - specified row is hidden packing inequality. */ struct elem *e, *ej, *ek; int neg; double b = *_b, eps; xassert(npp == npp); /* a[j] must be non-zero, x[j] must be binary, for all j in J */ for (e = ptr; e != NULL; e = e->next) { xassert(e->aj != 0.0); xassert(e->xj->is_int); xassert(e->xj->lb == 0.0 && e->xj->ub == 1.0); } /* check if the specified inequality constraint already has the form of packing inequality */ neg = 0; /* neg is |Jn| */ for (e = ptr; e != NULL; e = e->next) { if (e->aj == +1.0) ; else if (e->aj == -1.0) neg++; else break; } if (e == NULL) { /* all coefficients a[j] are +1 or -1; check rhs b */ if (b == (double)(1 - neg)) { /* it is packing inequality; no processing is needed */ return 1; } } /* substitute x[j] = 1 - x~[j] for all j in Jn to make all a[j] positive; the result is a~[j] = |a[j]| and new rhs b */ for (e = ptr; e != NULL; e = e->next) if (e->aj < 0) b -= e->aj; /* now a[j] > 0 for all j in J (actually |a[j]| are used) */ /* if a[j] > b, skip processing--this case must not appear */ for (e = ptr; e != NULL; e = e->next) if (fabs(e->aj) > b) return 0; /* now 0 < a[j] <= b for all j in J */ /* find two minimal coefficients a[j] and a[k], j != k */ ej = NULL; for (e = ptr; e != NULL; e = e->next) if (ej == NULL || fabs(ej->aj) > fabs(e->aj)) ej = e; xassert(ej != NULL); ek = NULL; for (e = ptr; e != NULL; e = e->next) if (e != ej) if (ek == NULL || fabs(ek->aj) > fabs(e->aj)) ek = e; xassert(ek != NULL); /* the specified constraint is equivalent to packing inequality iff a[j] + a[k] > b + eps */ eps = 1e-3 + 1e-6 * fabs(b); if (fabs(ej->aj) + fabs(ek->aj) <= b + eps) return 0; /* perform back substitution x~[j] = 1 - x[j] and construct the final equivalent packing inequality in generalized format */ b = 1.0; for (e = ptr; e != NULL; e = e->next) { if (e->aj > 0.0) e->aj = +1.0; else /* e->aj < 0.0 */ e->aj = -1.0, b -= 1.0; } *_b = b; return 2; } int npp_hidden_packing(NPP *npp, NPPROW *row) { /* identify hidden packing inequality */ NPPROW *copy; NPPAIJ *aij; struct elem *ptr, *e; int kase, ret, count = 0; double b; /* the row must be inequality constraint */ xassert(row->lb < row->ub); for (kase = 0; kase <= 1; kase++) { if (kase == 0) { /* process row upper bound */ if (row->ub == +DBL_MAX) continue; ptr = copy_form(npp, row, +1.0); b = + row->ub; } else { /* process row lower bound */ if (row->lb == -DBL_MAX) continue; ptr = copy_form(npp, row, -1.0); b = - row->lb; } /* now the inequality has the form "sum a[j] x[j] <= b" */ ret = hidden_packing(npp, ptr, &b); xassert(0 <= ret && ret <= 2); if (kase == 1 && ret == 1 || ret == 2) { /* the original inequality has been identified as hidden packing inequality */ count++; #ifdef GLP_DEBUG xprintf("Original constraint:\n"); for (aij = row->ptr; aij != NULL; aij = aij->r_next) xprintf(" %+g x%d", aij->val, aij->col->j); if (row->lb != -DBL_MAX) xprintf(", >= %g", row->lb); if (row->ub != +DBL_MAX) xprintf(", <= %g", row->ub); xprintf("\n"); xprintf("Equivalent packing inequality:\n"); for (e = ptr; e != NULL; e = e->next) xprintf(" %sx%d", e->aj > 0.0 ? "+" : "-", e->xj->j); xprintf(", <= %g\n", b); #endif if (row->lb == -DBL_MAX || row->ub == +DBL_MAX) { /* the original row is single-sided inequality; no copy is needed */ copy = NULL; } else { /* the original row is double-sided inequality; we need to create its copy for other bound before replacing it with the equivalent inequality */ copy = npp_add_row(npp); if (kase == 0) { /* the copy is for lower bound */ copy->lb = row->lb, copy->ub = +DBL_MAX; } else { /* the copy is for upper bound */ copy->lb = -DBL_MAX, copy->ub = row->ub; } /* copy original row coefficients */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) npp_add_aij(npp, copy, aij->col, aij->val); } /* replace the original inequality by equivalent one */ npp_erase_row(npp, row); row->lb = -DBL_MAX, row->ub = b; for (e = ptr; e != NULL; e = e->next) npp_add_aij(npp, row, e->xj, e->aj); /* continue processing lower bound for the copy */ if (copy != NULL) row = copy; } drop_form(npp, ptr); } return count; } /*********************************************************************** * NAME * * npp_implied_packing - identify implied packing inequality * * SYNOPSIS * * #include "glpnpp.h" * int npp_implied_packing(NPP *npp, NPPROW *row, int which, * NPPCOL *var[], char set[]); * * DESCRIPTION * * The routine npp_implied_packing processes specified row (constraint) * of general format: * * L <= sum a[j] x[j] <= U. (1) * j * * If which = 0, only lower bound L, which must exist, is considered, * while upper bound U is ignored. Similarly, if which = 1, only upper * bound U, which must exist, is considered, while lower bound L is * ignored. Thus, if the specified row is a double-sided inequality or * equality constraint, this routine should be called twice for both * lower and upper bounds. * * The routine npp_implied_packing attempts to find a non-trivial (i.e. * having not less than two binary variables) packing inequality: * * sum x[j] - sum x[j] <= 1 - |Jn|, (2) * j in Jp j in Jn * * which is relaxation of the constraint (1) in the sense that any * solution satisfying to that constraint also satisfies to the packing * inequality (2). If such relaxation exists, the routine stores * pointers to descriptors of corresponding binary variables and their * flags, resp., to locations var[1], var[2], ..., var[len] and set[1], * set[2], ..., set[len], where set[j] = 0 means that j in Jp and * set[j] = 1 means that j in Jn. * * RETURNS * * The routine npp_implied_packing returns len, which is the total * number of binary variables in the packing inequality found, len >= 2. * However, if the relaxation does not exist, the routine returns zero. * * ALGORITHM * * If which = 0, the constraint coefficients (1) are multiplied by -1 * and b is assigned -L; if which = 1, the constraint coefficients (1) * are not changed and b is assigned +U. In both cases the specified * constraint gets the following format: * * sum a[j] x[j] <= b. (3) * j * * (Note that (3) is a relaxation of (1), because one of bounds L or U * is ignored.) * * Let J be set of binary variables, Kp be set of non-binary (integer * or continuous) variables with a[j] > 0, and Kn be set of non-binary * variables with a[j] < 0. Then the inequality (3) can be written as * follows: * * sum a[j] x[j] <= b - sum a[j] x[j] - sum a[j] x[j]. (4) * j in J j in Kp j in Kn * * To get rid of non-binary variables we can replace the inequality (4) * by the following relaxed inequality: * * sum a[j] x[j] <= b~, (5) * j in J * * where: * * b~ = sup(b - sum a[j] x[j] - sum a[j] x[j]) = * j in Kp j in Kn * * = b - inf sum a[j] x[j] - inf sum a[j] x[j] = (6) * j in Kp j in Kn * * = b - sum a[j] l[j] - sum a[j] u[j]. * j in Kp j in Kn * * Note that if lower bound l[j] (if j in Kp) or upper bound u[j] * (if j in Kn) of some non-binary variable x[j] does not exist, then * formally b = +oo, in which case further analysis is not performed. * * Let Bp = {j in J: a[j] > 0}, Bn = {j in J: a[j] < 0}. To make all * the inequality coefficients in (5) positive, we replace all x[j] in * Bn by their complementaries, substituting x[j] = 1 - x~[j] for all * j in Bn, that gives: * * sum a[j] x[j] - sum a[j] x~[j] <= b~ - sum a[j]. (7) * j in Bp j in Bn j in Bn * * This inequality is a relaxation of the original constraint (1), and * it is a binary knapsack inequality. Writing it in the standard format * we have: * * sum alfa[j] z[j] <= beta, (8) * j in J * * where: * ( + a[j], if j in Bp, * alfa[j] = < (9) * ( - a[j], if j in Bn, * * ( x[j], if j in Bp, * z[j] = < (10) * ( 1 - x[j], if j in Bn, * * beta = b~ - sum a[j]. (11) * j in Bn * * In the inequality (8) all coefficients are positive, therefore, the * packing relaxation to be found for this inequality is the following: * * sum z[j] <= 1. (12) * j in P * * It is obvious that set P within J, which we would like to find, must * satisfy to the following condition: * * alfa[j] + alfa[k] > beta + eps for all j, k in P, j != k, (13) * * where eps is an absolute tolerance for value of the linear form. * Thus, it is natural to take P = {j: alpha[j] > (beta + eps) / 2}. * Moreover, if in the equality (8) there exist coefficients alfa[k], * for which alfa[k] <= (beta + eps) / 2, but which, nevertheless, * satisfies to the condition (13) for all j in P, *one* corresponding * variable z[k] (having, for example, maximal coefficient alfa[k]) can * be included in set P, that allows increasing the number of binary * variables in (12) by one. * * Once the set P has been built, for the inequality (12) we need to * perform back substitution according to (10) in order to express it * through the original binary variables. As the result of such back * substitution the relaxed packing inequality get its final format (2), * where Jp = J intersect Bp, and Jn = J intersect Bn. */ int npp_implied_packing(NPP *npp, NPPROW *row, int which, NPPCOL *var[], char set[]) { struct elem *ptr, *e, *i, *k; int len = 0; double b, eps; /* build inequality (3) */ if (which == 0) { ptr = copy_form(npp, row, -1.0); xassert(row->lb != -DBL_MAX); b = - row->lb; } else if (which == 1) { ptr = copy_form(npp, row, +1.0); xassert(row->ub != +DBL_MAX); b = + row->ub; } /* remove non-binary variables to build relaxed inequality (5); compute its right-hand side b~ with formula (6) */ for (e = ptr; e != NULL; e = e->next) { if (!(e->xj->is_int && e->xj->lb == 0.0 && e->xj->ub == 1.0)) { /* x[j] is non-binary variable */ if (e->aj > 0.0) { if (e->xj->lb == -DBL_MAX) goto done; b -= e->aj * e->xj->lb; } else /* e->aj < 0.0 */ { if (e->xj->ub == +DBL_MAX) goto done; b -= e->aj * e->xj->ub; } /* a[j] = 0 means that variable x[j] is removed */ e->aj = 0.0; } } /* substitute x[j] = 1 - x~[j] to build knapsack inequality (8); compute its right-hand side beta with formula (11) */ for (e = ptr; e != NULL; e = e->next) if (e->aj < 0.0) b -= e->aj; /* if beta is close to zero, the knapsack inequality is either infeasible or forcing inequality; this must never happen, so we skip further analysis */ if (b < 1e-3) goto done; /* build set P as well as sets Jp and Jn, and determine x[k] as explained above in comments to the routine */ eps = 1e-3 + 1e-6 * b; i = k = NULL; for (e = ptr; e != NULL; e = e->next) { /* note that alfa[j] = |a[j]| */ if (fabs(e->aj) > 0.5 * (b + eps)) { /* alfa[j] > (b + eps) / 2; include x[j] in set P, i.e. in set Jp or Jn */ var[++len] = e->xj; set[len] = (char)(e->aj > 0.0 ? 0 : 1); /* alfa[i] = min alfa[j] over all j included in set P */ if (i == NULL || fabs(i->aj) > fabs(e->aj)) i = e; } else if (fabs(e->aj) >= 1e-3) { /* alfa[k] = max alfa[j] over all j not included in set P; we skip coefficient a[j] if it is close to zero to avoid numerically unreliable results */ if (k == NULL || fabs(k->aj) < fabs(e->aj)) k = e; } } /* if alfa[k] satisfies to condition (13) for all j in P, include x[k] in P */ if (i != NULL && k != NULL && fabs(i->aj) + fabs(k->aj) > b + eps) { var[++len] = k->xj; set[len] = (char)(k->aj > 0.0 ? 0 : 1); } /* trivial packing inequality being redundant must never appear, so we just ignore it */ if (len < 2) len = 0; done: drop_form(npp, ptr); return len; } /*********************************************************************** * NAME * * npp_is_covering - test if constraint is covering inequality * * SYNOPSIS * * #include "glpnpp.h" * int npp_is_covering(NPP *npp, NPPROW *row); * * RETURNS * * If the specified row (constraint) is covering inequality (see below), * the routine npp_is_covering returns non-zero. Otherwise, it returns * zero. * * COVERING INEQUALITIES * * In canonical format the covering inequality is the following: * * sum x[j] >= 1, (1) * j in J * * where all variables x[j] are binary. This inequality expresses the * condition that in any integer feasible solution variables in set J * cannot be all equal to zero at the same time, i.e. at least one * variable must take non-zero (unity) value. W.l.o.g. it is assumed * that |J| >= 2, because if J is empty, the inequality (1) is * infeasible, and if |J| = 1, the inequality (1) is a forcing row. * * In general case the covering inequality may include original * variables x[j] as well as their complements x~[j]: * * sum x[j] + sum x~[j] >= 1, (2) * j in Jp j in Jn * * where Jp and Jn are not intersected. Therefore, using substitution * x~[j] = 1 - x[j] gives the packing inequality in generalized format: * * sum x[j] - sum x[j] >= 1 - |Jn|. (3) * j in Jp j in Jn * * (May note that the inequality (3) cuts off infeasible solutions, * where x[j] = 0 for all j in Jp and x[j] = 1 for all j in Jn.) * * NOTE: If |J| = 2, the inequality (3) is equivalent to packing * inequality (see the routine npp_is_packing). */ int npp_is_covering(NPP *npp, NPPROW *row) { /* test if constraint is covering inequality */ NPPCOL *col; NPPAIJ *aij; int b; xassert(npp == npp); if (!(row->lb != -DBL_MAX && row->ub == +DBL_MAX)) return 0; b = 1; for (aij = row->ptr; aij != NULL; aij = aij->r_next) { col = aij->col; if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) return 0; if (aij->val == +1.0) ; else if (aij->val == -1.0) b--; else return 0; } if (row->lb != (double)b) return 0; return 1; } /*********************************************************************** * NAME * * npp_hidden_covering - identify hidden covering inequality * * SYNOPSIS * * #include "glpnpp.h" * int npp_hidden_covering(NPP *npp, NPPROW *row); * * DESCRIPTION * * The routine npp_hidden_covering processes specified inequality * constraint, which includes only binary variables, and the number of * the variables is not less than three. If the original inequality is * equivalent to a covering inequality (see below), the routine * replaces it by the equivalent inequality. If the original constraint * is double-sided inequality, it is replaced by a pair of single-sided * inequalities, if necessary. * * RETURNS * * If the original inequality constraint was replaced by equivalent * covering inequality, the routine npp_hidden_covering returns * non-zero. Otherwise, it returns zero. * * PROBLEM TRANSFORMATION * * Consider an inequality constraint: * * sum a[j] x[j] >= b, (1) * j in J * * where all variables x[j] are binary, and |J| >= 3. (In case of '<=' * inequality it can be transformed to '>=' format by multiplying both * its sides by -1.) * * Let Jp = {j: a[j] > 0}, Jn = {j: a[j] < 0}. Performing substitution * x[j] = 1 - x~[j] for all j in Jn, we have: * * sum a[j] x[j] >= b ==> * j in J * * sum a[j] x[j] + sum a[j] x[j] >= b ==> * j in Jp j in Jn * * sum a[j] x[j] + sum a[j] (1 - x~[j]) >= b ==> * j in Jp j in Jn * * sum m a[j] x[j] - sum a[j] x~[j] >= b - sum a[j]. * j in Jp j in Jn j in Jn * * Thus, meaning the transformation above, we can assume that in * inequality (1) all coefficients a[j] are positive. Moreover, we can * assume that b > 0, because otherwise the inequality (1) would be * redundant (see the routine npp_analyze_row). It is then obvious that * constraint (1) is equivalent to covering inequality only if: * * a[j] >= b, (2) * * for all j in J. * * Once the original inequality (1) is replaced by equivalent covering * inequality, we need to perform back substitution x~[j] = 1 - x[j] for * all j in Jn (see above). * * RECOVERING SOLUTION * * None needed. */ static int hidden_covering(NPP *npp, struct elem *ptr, double *_b) { /* process inequality constraint: sum a[j] x[j] >= b; 0 - specified row is NOT hidden covering inequality; 1 - specified row is covering inequality; 2 - specified row is hidden covering inequality. */ struct elem *e; int neg; double b = *_b, eps; xassert(npp == npp); /* a[j] must be non-zero, x[j] must be binary, for all j in J */ for (e = ptr; e != NULL; e = e->next) { xassert(e->aj != 0.0); xassert(e->xj->is_int); xassert(e->xj->lb == 0.0 && e->xj->ub == 1.0); } /* check if the specified inequality constraint already has the form of covering inequality */ neg = 0; /* neg is |Jn| */ for (e = ptr; e != NULL; e = e->next) { if (e->aj == +1.0) ; else if (e->aj == -1.0) neg++; else break; } if (e == NULL) { /* all coefficients a[j] are +1 or -1; check rhs b */ if (b == (double)(1 - neg)) { /* it is covering inequality; no processing is needed */ return 1; } } /* substitute x[j] = 1 - x~[j] for all j in Jn to make all a[j] positive; the result is a~[j] = |a[j]| and new rhs b */ for (e = ptr; e != NULL; e = e->next) if (e->aj < 0) b -= e->aj; /* now a[j] > 0 for all j in J (actually |a[j]| are used) */ /* if b <= 0, skip processing--this case must not appear */ if (b < 1e-3) return 0; /* now a[j] > 0 for all j in J, and b > 0 */ /* the specified constraint is equivalent to covering inequality iff a[j] >= b for all j in J */ eps = 1e-9 + 1e-12 * fabs(b); for (e = ptr; e != NULL; e = e->next) if (fabs(e->aj) < b - eps) return 0; /* perform back substitution x~[j] = 1 - x[j] and construct the final equivalent covering inequality in generalized format */ b = 1.0; for (e = ptr; e != NULL; e = e->next) { if (e->aj > 0.0) e->aj = +1.0; else /* e->aj < 0.0 */ e->aj = -1.0, b -= 1.0; } *_b = b; return 2; } int npp_hidden_covering(NPP *npp, NPPROW *row) { /* identify hidden covering inequality */ NPPROW *copy; NPPAIJ *aij; struct elem *ptr, *e; int kase, ret, count = 0; double b; /* the row must be inequality constraint */ xassert(row->lb < row->ub); for (kase = 0; kase <= 1; kase++) { if (kase == 0) { /* process row lower bound */ if (row->lb == -DBL_MAX) continue; ptr = copy_form(npp, row, +1.0); b = + row->lb; } else { /* process row upper bound */ if (row->ub == +DBL_MAX) continue; ptr = copy_form(npp, row, -1.0); b = - row->ub; } /* now the inequality has the form "sum a[j] x[j] >= b" */ ret = hidden_covering(npp, ptr, &b); xassert(0 <= ret && ret <= 2); if (kase == 1 && ret == 1 || ret == 2) { /* the original inequality has been identified as hidden covering inequality */ count++; #ifdef GLP_DEBUG xprintf("Original constraint:\n"); for (aij = row->ptr; aij != NULL; aij = aij->r_next) xprintf(" %+g x%d", aij->val, aij->col->j); if (row->lb != -DBL_MAX) xprintf(", >= %g", row->lb); if (row->ub != +DBL_MAX) xprintf(", <= %g", row->ub); xprintf("\n"); xprintf("Equivalent covering inequality:\n"); for (e = ptr; e != NULL; e = e->next) xprintf(" %sx%d", e->aj > 0.0 ? "+" : "-", e->xj->j); xprintf(", >= %g\n", b); #endif if (row->lb == -DBL_MAX || row->ub == +DBL_MAX) { /* the original row is single-sided inequality; no copy is needed */ copy = NULL; } else { /* the original row is double-sided inequality; we need to create its copy for other bound before replacing it with the equivalent inequality */ copy = npp_add_row(npp); if (kase == 0) { /* the copy is for upper bound */ copy->lb = -DBL_MAX, copy->ub = row->ub; } else { /* the copy is for lower bound */ copy->lb = row->lb, copy->ub = +DBL_MAX; } /* copy original row coefficients */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) npp_add_aij(npp, copy, aij->col, aij->val); } /* replace the original inequality by equivalent one */ npp_erase_row(npp, row); row->lb = b, row->ub = +DBL_MAX; for (e = ptr; e != NULL; e = e->next) npp_add_aij(npp, row, e->xj, e->aj); /* continue processing upper bound for the copy */ if (copy != NULL) row = copy; } drop_form(npp, ptr); } return count; } /*********************************************************************** * NAME * * npp_is_partitioning - test if constraint is partitioning equality * * SYNOPSIS * * #include "glpnpp.h" * int npp_is_partitioning(NPP *npp, NPPROW *row); * * RETURNS * * If the specified row (constraint) is partitioning equality (see * below), the routine npp_is_partitioning returns non-zero. Otherwise, * it returns zero. * * PARTITIONING EQUALITIES * * In canonical format the partitioning equality is the following: * * sum x[j] = 1, (1) * j in J * * where all variables x[j] are binary. This equality expresses the * condition that in any integer feasible solution exactly one variable * in set J must take non-zero (unity) value while other variables must * be equal to zero. W.l.o.g. it is assumed that |J| >= 2, because if * J is empty, the inequality (1) is infeasible, and if |J| = 1, the * inequality (1) is a fixing row. * * In general case the partitioning equality may include original * variables x[j] as well as their complements x~[j]: * * sum x[j] + sum x~[j] = 1, (2) * j in Jp j in Jn * * where Jp and Jn are not intersected. Therefore, using substitution * x~[j] = 1 - x[j] leads to the partitioning equality in generalized * format: * * sum x[j] - sum x[j] = 1 - |Jn|. (3) * j in Jp j in Jn */ int npp_is_partitioning(NPP *npp, NPPROW *row) { /* test if constraint is partitioning equality */ NPPCOL *col; NPPAIJ *aij; int b; xassert(npp == npp); if (row->lb != row->ub) return 0; b = 1; for (aij = row->ptr; aij != NULL; aij = aij->r_next) { col = aij->col; if (!(col->is_int && col->lb == 0.0 && col->ub == 1.0)) return 0; if (aij->val == +1.0) ; else if (aij->val == -1.0) b--; else return 0; } if (row->lb != (double)b) return 0; return 1; } /*********************************************************************** * NAME * * npp_reduce_ineq_coef - reduce inequality constraint coefficients * * SYNOPSIS * * #include "glpnpp.h" * int npp_reduce_ineq_coef(NPP *npp, NPPROW *row); * * DESCRIPTION * * The routine npp_reduce_ineq_coef processes specified inequality * constraint attempting to replace it by an equivalent constraint, * where magnitude of coefficients at binary variables is smaller than * in the original constraint. If the inequality is double-sided, it is * replaced by a pair of single-sided inequalities, if necessary. * * RETURNS * * The routine npp_reduce_ineq_coef returns the number of coefficients * reduced. * * BACKGROUND * * Consider an inequality constraint: * * sum a[j] x[j] >= b. (1) * j in J * * (In case of '<=' inequality it can be transformed to '>=' format by * multiplying both its sides by -1.) Let x[k] be a binary variable; * other variables can be integer as well as continuous. We can write * constraint (1) as follows: * * a[k] x[k] + t[k] >= b, (2) * * where: * * t[k] = sum a[j] x[j]. (3) * j in J\{k} * * Since x[k] is binary, constraint (2) is equivalent to disjunction of * the following two constraints: * * x[k] = 0, t[k] >= b (4) * * OR * * x[k] = 1, t[k] >= b - a[k]. (5) * * Let also that for the partial sum t[k] be known some its implied * lower bound inf t[k]. * * Case a[k] > 0. Let inf t[k] < b, since otherwise both constraints * (4) and (5) and therefore constraint (2) are redundant. * If inf t[k] > b - a[k], only constraint (5) is redundant, in which * case it can be replaced with the following redundant and therefore * equivalent constraint: * * t[k] >= b - a'[k] = inf t[k], (6) * * where: * * a'[k] = b - inf t[k]. (7) * * Thus, the original constraint (2) is equivalent to the following * constraint with coefficient at variable x[k] changed: * * a'[k] x[k] + t[k] >= b. (8) * * From inf t[k] < b it follows that a'[k] > 0, i.e. the coefficient * at x[k] keeps its sign. And from inf t[k] > b - a[k] it follows that * a'[k] < a[k], i.e. the coefficient reduces in magnitude. * * Case a[k] < 0. Let inf t[k] < b - a[k], since otherwise both * constraints (4) and (5) and therefore constraint (2) are redundant. * If inf t[k] > b, only constraint (4) is redundant, in which case it * can be replaced with the following redundant and therefore equivalent * constraint: * * t[k] >= b' = inf t[k]. (9) * * Rewriting constraint (5) as follows: * * t[k] >= b - a[k] = b' - a'[k], (10) * * where: * * a'[k] = a[k] + b' - b = a[k] + inf t[k] - b, (11) * * we can see that disjunction of constraint (9) and (10) is equivalent * to disjunction of constraint (4) and (5), from which it follows that * the original constraint (2) is equivalent to the following constraint * with both coefficient at variable x[k] and right-hand side changed: * * a'[k] x[k] + t[k] >= b'. (12) * * From inf t[k] < b - a[k] it follows that a'[k] < 0, i.e. the * coefficient at x[k] keeps its sign. And from inf t[k] > b it follows * that a'[k] > a[k], i.e. the coefficient reduces in magnitude. * * PROBLEM TRANSFORMATION * * In the routine npp_reduce_ineq_coef the following implied lower * bound of the partial sum (3) is used: * * inf t[k] = sum a[j] l[j] + sum a[j] u[j], (13) * j in Jp\{k} k in Jn\{k} * * where Jp = {j : a[j] > 0}, Jn = {j : a[j] < 0}, l[j] and u[j] are * lower and upper bounds, resp., of variable x[j]. * * In order to compute inf t[k] more efficiently, the following formula, * which is equivalent to (13), is actually used: * * ( h - a[k] l[k] = h, if a[k] > 0, * inf t[k] = < (14) * ( h - a[k] u[k] = h - a[k], if a[k] < 0, * * where: * * h = sum a[j] l[j] + sum a[j] u[j] (15) * j in Jp j in Jn * * is the implied lower bound of row (1). * * Reduction of positive coefficient (a[k] > 0) does not change value * of h, since l[k] = 0. In case of reduction of negative coefficient * (a[k] < 0) from (11) it follows that: * * delta a[k] = a'[k] - a[k] = inf t[k] - b (> 0), (16) * * so new value of h (accounting that u[k] = 1) can be computed as * follows: * * h := h + delta a[k] = h + (inf t[k] - b). (17) * * RECOVERING SOLUTION * * None needed. */ static int reduce_ineq_coef(NPP *npp, struct elem *ptr, double *_b) { /* process inequality constraint: sum a[j] x[j] >= b */ /* returns: the number of coefficients reduced */ struct elem *e; int count = 0; double h, inf_t, new_a, b = *_b; xassert(npp == npp); /* compute h; see (15) */ h = 0.0; for (e = ptr; e != NULL; e = e->next) { if (e->aj > 0.0) { if (e->xj->lb == -DBL_MAX) goto done; h += e->aj * e->xj->lb; } else /* e->aj < 0.0 */ { if (e->xj->ub == +DBL_MAX) goto done; h += e->aj * e->xj->ub; } } /* perform reduction of coefficients at binary variables */ for (e = ptr; e != NULL; e = e->next) { /* skip non-binary variable */ if (!(e->xj->is_int && e->xj->lb == 0.0 && e->xj->ub == 1.0)) continue; if (e->aj > 0.0) { /* compute inf t[k]; see (14) */ inf_t = h; if (b - e->aj < inf_t && inf_t < b) { /* compute reduced coefficient a'[k]; see (7) */ new_a = b - inf_t; if (new_a >= +1e-3 && e->aj - new_a >= 0.01 * (1.0 + e->aj)) { /* accept a'[k] */ #ifdef GLP_DEBUG xprintf("+"); #endif e->aj = new_a; count++; } } } else /* e->aj < 0.0 */ { /* compute inf t[k]; see (14) */ inf_t = h - e->aj; if (b < inf_t && inf_t < b - e->aj) { /* compute reduced coefficient a'[k]; see (11) */ new_a = e->aj + (inf_t - b); if (new_a <= -1e-3 && new_a - e->aj >= 0.01 * (1.0 - e->aj)) { /* accept a'[k] */ #ifdef GLP_DEBUG xprintf("-"); #endif e->aj = new_a; /* update h; see (17) */ h += (inf_t - b); /* compute b'; see (9) */ b = inf_t; count++; } } } } *_b = b; done: return count; } int npp_reduce_ineq_coef(NPP *npp, NPPROW *row) { /* reduce inequality constraint coefficients */ NPPROW *copy; NPPAIJ *aij; struct elem *ptr, *e; int kase, count[2]; double b; /* the row must be inequality constraint */ xassert(row->lb < row->ub); count[0] = count[1] = 0; for (kase = 0; kase <= 1; kase++) { if (kase == 0) { /* process row lower bound */ if (row->lb == -DBL_MAX) continue; #ifdef GLP_DEBUG xprintf("L"); #endif ptr = copy_form(npp, row, +1.0); b = + row->lb; } else { /* process row upper bound */ if (row->ub == +DBL_MAX) continue; #ifdef GLP_DEBUG xprintf("U"); #endif ptr = copy_form(npp, row, -1.0); b = - row->ub; } /* now the inequality has the form "sum a[j] x[j] >= b" */ count[kase] = reduce_ineq_coef(npp, ptr, &b); if (count[kase] > 0) { /* the original inequality has been replaced by equivalent one with coefficients reduced */ if (row->lb == -DBL_MAX || row->ub == +DBL_MAX) { /* the original row is single-sided inequality; no copy is needed */ copy = NULL; } else { /* the original row is double-sided inequality; we need to create its copy for other bound before replacing it with the equivalent inequality */ #ifdef GLP_DEBUG xprintf("*"); #endif copy = npp_add_row(npp); if (kase == 0) { /* the copy is for upper bound */ copy->lb = -DBL_MAX, copy->ub = row->ub; } else { /* the copy is for lower bound */ copy->lb = row->lb, copy->ub = +DBL_MAX; } /* copy original row coefficients */ for (aij = row->ptr; aij != NULL; aij = aij->r_next) npp_add_aij(npp, copy, aij->col, aij->val); } /* replace the original inequality by equivalent one */ npp_erase_row(npp, row); row->lb = b, row->ub = +DBL_MAX; for (e = ptr; e != NULL; e = e->next) npp_add_aij(npp, row, e->xj, e->aj); /* continue processing upper bound for the copy */ if (copy != NULL) row = copy; } drop_form(npp, ptr); } return count[0] + count[1]; } /* eof */ igraph/src/igraph_buckets.c0000644000176000001440000001322712325527073015514 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_types_internal.h" #include "config.h" #include /* The igraph_buckets_t data structure can store at most 'size' * unique integers in 'bsize' buckets. It has the following simple * operations (in addition to _init() and _destroy(): * - _add() adding an element to the given bucket. * - _popmax() removing an element from the bucket with the highest * id. * Currently buckets work as stacks, last-in-first-out mode. * - _empty() queries whether the buckets is empty. * * Internal representation: we use a vector to create single linked * lists, and another vector that points to the starting element of * each bucket. Zero means the end of the chain. So bucket i contains * elements bptr[i], buckets[bptr[i]], buckets[buckets[bptr[i]]], * etc., until a zero is found. * * We also keep the total number of elements in the buckets and the * id of the non-empty bucket with the highest id, to facilitate the * _empty() and _popmax() operations. */ int igraph_buckets_init(igraph_buckets_t *b, long int bsize, long int size) { IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->bptr, bsize); IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->buckets, size); b->max=-1; b->no=0; IGRAPH_FINALLY_CLEAN(2); return 0; } void igraph_buckets_destroy(igraph_buckets_t *b) { igraph_vector_long_destroy(&b->bptr); igraph_vector_long_destroy(&b->buckets); } long int igraph_buckets_popmax(igraph_buckets_t *b) { /* Precondition: there is at least a non-empty bucket */ /* Search for the highest bucket first */ long int max; while ( (max=(long int) VECTOR(b->bptr)[(long int) b->max]) == 0) { b->max --; } VECTOR(b->bptr)[(long int) b->max] = VECTOR(b->buckets)[max-1]; b->no--; return max-1; } long int igraph_buckets_pop(igraph_buckets_t *b, long int bucket) { long int ret=VECTOR(b->bptr)[bucket]-1; VECTOR(b->bptr)[bucket] = VECTOR(b->buckets)[ret]; b->no--; return ret; } igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b) { return (b->no == 0); } igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b, long int bucket) { return VECTOR(b->bptr)[bucket] == 0; } void igraph_buckets_add(igraph_buckets_t *b, long int bucket, long int elem) { VECTOR(b->buckets)[(long int) elem] = VECTOR(b->bptr)[(long int) bucket]; VECTOR(b->bptr)[(long int) bucket] = elem+1; if (bucket > b->max) { b->max = (int) bucket; } b->no++; } void igraph_buckets_clear(igraph_buckets_t *b) { igraph_vector_long_null(&b->bptr); igraph_vector_long_null(&b->buckets); b->max = -1; b->no = 0; } int igraph_dbuckets_init(igraph_dbuckets_t *b, long int bsize, long int size) { IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->bptr, bsize); IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->next, size); IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->prev, size); b->max=-1; b->no=0; IGRAPH_FINALLY_CLEAN(3); return 0; } void igraph_dbuckets_destroy(igraph_dbuckets_t *b) { igraph_vector_long_destroy(&b->bptr); igraph_vector_long_destroy(&b->next); igraph_vector_long_destroy(&b->prev); } void igraph_dbuckets_clear(igraph_dbuckets_t *b) { igraph_vector_long_null(&b->bptr); igraph_vector_long_null(&b->next); igraph_vector_long_null(&b->prev); b->max = -1; b->no = 0; } long int igraph_dbuckets_popmax(igraph_dbuckets_t *b) { long int max; while ( (max=(long int) VECTOR(b->bptr)[(long int) b->max]) == 0) { b->max --; } return igraph_dbuckets_pop(b, b->max); } long int igraph_dbuckets_pop(igraph_dbuckets_t *b, long int bucket) { long int ret=VECTOR(b->bptr)[bucket]-1; long int next=VECTOR(b->next)[ret]; VECTOR(b->bptr)[bucket] = next; if (next != 0) { VECTOR(b->prev)[next-1] = 0; } b->no--; return ret; } igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b) { return (b->no == 0); } igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b, long int bucket) { return VECTOR(b->bptr)[bucket] == 0; } void igraph_dbuckets_add(igraph_dbuckets_t *b, long int bucket, long int elem) { long int oldfirst=VECTOR(b->bptr)[bucket]; VECTOR(b->bptr)[bucket] = elem+1; VECTOR(b->next)[elem] = oldfirst; if (oldfirst != 0) { VECTOR(b->prev)[oldfirst-1] = elem+1; } if (bucket > b->max) { b->max = (int) bucket; } b->no++; } /* Remove an arbitrary element */ void igraph_dbuckets_delete(igraph_dbuckets_t *b, long int bucket, long int elem) { if (VECTOR(b->bptr)[bucket] == elem+1) { /* First element in bucket */ long int next=VECTOR(b->next)[elem]; if (next != 0) { VECTOR(b->prev)[next-1] = 0; } VECTOR(b->bptr)[bucket] = next; } else { long int next=VECTOR(b->next)[elem]; long int prev=VECTOR(b->prev)[elem]; if (next != 0) { VECTOR(b->prev)[next-1] = prev; } if (prev != 0) { VECTOR(b->next)[prev-1] = next; } } b->no--; } igraph/src/Shape.cpp0000755000176000001440000000402312325527072014116 0ustar ripleyusers#include "Shape.h" #include "unit_limiter.h" namespace igraph { Shape::Shape() { mName = 0; mAmbientReflectivity = .6; mSpecularReflectivity = 0; mDiffuseReflectivity = 0; mSpecularSize = 64; } Shape::~Shape() {} int Shape::Name() const { return mName; } void Shape::Name(int vName) { mName = vName; } const Color& Shape::ShapeColor() const { return mShapeColor; } void Shape::ShapeColor(const Color& rColor) { mShapeColor = rColor; } double Shape::AmbientReflectivity() const { return mAmbientReflectivity; } double Shape::SpecularReflectivity() const { return mSpecularReflectivity; } double Shape::DiffuseReflectivity() const { return mDiffuseReflectivity; } void Shape::AmbientReflectivity(double rReflectivity) { mAmbientReflectivity = unit_limiter(rReflectivity); } void Shape::SpecularReflectivity(double rReflectivity) { mSpecularReflectivity = unit_limiter(rReflectivity); } void Shape::DiffuseReflectivity(double rReflectivity) { mDiffuseReflectivity = unit_limiter(rReflectivity); } Ray Shape::Reflect(const Point& rReflectFrom, const Ray& rIncidentRay) const { Ray result; // the reflected ray Vector result_direction; // the reflected direction vector Vector incident_unit = rIncidentRay.Direction().Normalize(); Vector normal = this->Normal(rReflectFrom, rIncidentRay.Origin() ); if ( !normal.IsSameDirection(incident_unit) ) normal.ReverseDirection(); // we want the normal in the same direction of the incident ray. result.Origin(rReflectFrom); result.Direction( normal*2.0*normal.Dot(incident_unit) - incident_unit ); /* if ( normal.Dot(rIncidentRay.Direction().Normalize()) < 0.0 ) normal.ReverseDirection(); result.Origin(rReflectFrom); result.Direction((normal*2.0) - rIncidentRay.Direction().Normalize()); */ return result; } const string& Shape::Type() const { return mType; } void Shape::Type(const string& rType) { mType = rType; } int Shape::SpecularSize() const { return mSpecularSize; } void Shape::SpecularSize(int vSpecularSize) { mSpecularSize = vSpecularSize; } } // namespace igraph igraph/src/matrix.pmt0000644000176000001440000013166212325372072014405 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include #include /* memcpy & co. */ #include /** * \section about_igraph_matrix_t_objects About \type igraph_matrix_t objects * * This type is just an interface to \type igraph_vector_t. * * The \type igraph_matrix_t type usually stores n * elements in O(n) space, but not always. See the documentation of * the vector type. */ /** * \section igraph_matrix_constructor_and_destructor Matrix constructors and * destructors */ /** * \ingroup matrix * \function igraph_matrix_init * \brief Initializes a matrix. * * * Every matrix needs to be initialized before using it. This is done * by calling this function. A matrix has to be destroyed if it is not * needed any more; see \ref igraph_matrix_destroy(). * \param m Pointer to a not yet initialized matrix object to be * initialized. * \param nrow The number of rows in the matrix. * \param ncol The number of columns in the matrix. * \return Error code. * * Time complexity: usually O(n), * n is the * number of elements in the matrix. */ int FUNCTION(igraph_matrix,init)(TYPE(igraph_matrix) *m, long int nrow, long int ncol) { int ret1; ret1=FUNCTION(igraph_vector,init)(&m->data, nrow*ncol); m->nrow=nrow; m->ncol=ncol; return ret1; } /** * \ingroup matrix * \function igraph_matrix_destroy * \brief Destroys a matrix object. * * * This function frees all the memory allocated for a matrix * object. The destroyed object needs to be reinitialized before using * it again. * \param m The matrix to destroy. * * Time complexity: operating system dependent. */ void FUNCTION(igraph_matrix,destroy)(TYPE(igraph_matrix) *m) { FUNCTION(igraph_vector,destroy)(&m->data); } /** * \ingroup matrix * \function igraph_matrix_capacity * \brief Returns the number of elements allocated for a matrix. * * Note that this might be different from the size of the matrix (as * queried by \ref igraph_matrix_size(), and specifies how many elements * the matrix can hold, without reallocation. * \param v Pointer to the (previously initialized) matrix object * to query. * \return The allocated capacity. * * \sa \ref igraph_matrix_size(), \ref igraph_matrix_nrow(), * \ref igraph_matrix_ncol(). * * Time complexity: O(1). */ long int FUNCTION(igraph_matrix,capacity)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector,capacity)(&m->data); } /** * \section igraph_matrix_accessing_elements Accessing elements of a matrix */ /** * \ingroup matrix * \function igraph_matrix_resize * \brief Resizes a matrix. * * * This function resizes a matrix by adding more elements to it. * The matrix contains arbitrary data after resizing it. * That is, after calling this function you cannot expect that element * (i,j) in the matrix remains the * same as before. * \param m Pointer to an already initialized matrix object. * \param nrow The number of rows in the resized matrix. * \param ncol The number of columns in the resized matrix. * \return Error code. * * Time complexity: O(1) if the * matrix gets smaller, usually O(n) * if it gets larger, n is the * number of elements in the resized matrix. */ int FUNCTION(igraph_matrix,resize)(TYPE(igraph_matrix) *m, long int nrow, long int ncol) { FUNCTION(igraph_vector,resize)(&m->data, nrow*ncol); m->nrow=nrow; m->ncol=ncol; return 0; } /** * \ingroup matrix * \function igraph_matrix_resize_min * \brief Deallocates unused memory for a matrix. * * * Note that this function might fail if there is not enough memory * available. * * * Also note, that this function leaves the matrix intact, i.e. * it does not destroy any of the elements. However, usually it involves * copying the matrix in memory. * \param m Pointer to an initialized matrix. * \return Error code. * * \sa \ref igraph_matrix_resize(). * * Time complexity: operating system dependent. */ int FUNCTION(igraph_matrix,resize_min)(TYPE(igraph_matrix) *m) { TYPE(igraph_vector) tmp; long int size=FUNCTION(igraph_matrix,size)(m); long int capacity=FUNCTION(igraph_matrix,capacity)(m); if (size == capacity) { return 0; } IGRAPH_CHECK(FUNCTION(igraph_vector,init)(&tmp, size)); FUNCTION(igraph_vector,update)(&tmp, &m->data); FUNCTION(igraph_vector,destroy)(&m->data); m->data = tmp; return 0; } /** * \ingroup matrix * \function igraph_matrix_size * \brief The number of elements in a matrix. * * \param m Pointer to an initialized matrix object. * \return The size of the matrix. * * Time complexity: O(1). */ long int FUNCTION(igraph_matrix,size)(const TYPE(igraph_matrix) *m) { return (m->nrow) * (m->ncol); } /** * \ingroup matrix * \function igraph_matrix_nrow * \brief The number of rows in a matrix. * * \param m Pointer to an initialized matrix object. * \return The number of rows in the matrix. * * Time complexity: O(1). */ long int FUNCTION(igraph_matrix,nrow)(const TYPE(igraph_matrix) *m) { return m->nrow; } /** * \ingroup matrix * \function igraph_matrix_ncol * \brief The number of columns in a matrix. * * \param m Pointer to an initialized matrix object. * \return The number of columns in the matrix. * * Time complexity: O(1). */ long int FUNCTION(igraph_matrix,ncol)(const TYPE(igraph_matrix) *m) { return m->ncol; } /** * \ingroup matrix * \function igraph_matrix_copy_to * \brief Copies a matrix to a regular C array. * * * The matrix is copied columnwise, as this is the format most * programs and languages use. * The C array should be of sufficient size; there are (of course) no * range checks. * \param m Pointer to an initialized matrix object. * \param to Pointer to a C array; the place to copy the data to. * \return Error code. * * Time complexity: O(n), * n is the number of * elements in the matrix. */ void FUNCTION(igraph_matrix,copy_to)(const TYPE(igraph_matrix) *m, BASE *to) { FUNCTION(igraph_vector,copy_to)(&m->data, to); } /** * \ingroup matrix * \function igraph_matrix_null * \brief Sets all elements in a matrix to zero. * * \param m Pointer to an initialized matrix object. * * Time complexity: O(n), * n is the number of elements in * the matrix. */ void FUNCTION(igraph_matrix,null)(TYPE(igraph_matrix) *m) { FUNCTION(igraph_vector,null)(&m->data); } /** * \ingroup matrix * \function igraph_matrix_add_cols * \brief Adds columns to a matrix. * \param m The matrix object. * \param n The number of columns to add. * \return Error code, \c IGRAPH_ENOMEM if there is * not enough memory to perform the operation. * * Time complexity: linear with the number of elements of the new, * resized matrix. */ int FUNCTION(igraph_matrix,add_cols)(TYPE(igraph_matrix) *m, long int n) { FUNCTION(igraph_matrix,resize)(m, m->nrow, m->ncol+n); return 0; } /** * \ingroup matrix * \function igraph_matrix_add_rows * \brief Adds rows to a matrix. * \param m The matrix object. * \param n The number of rows to add. * \return Error code, \c IGRAPH_ENOMEM if there * isn't enough memory for the operation. * * Time complexity: linear with the number of elements of the new, * resized matrix. */ int FUNCTION(igraph_matrix,add_rows)(TYPE(igraph_matrix) *m, long int n) { long int i; FUNCTION(igraph_vector,resize)(&m->data, (m->ncol)*(m->nrow+n)); for (i=m->ncol-1; i>=0; i--) { FUNCTION(igraph_vector,move_interval2)(&m->data, (m->nrow)*i, (m->nrow)*(i+1), (m->nrow+n)*i); } m->nrow += n; return 0; } /** * \ingroup matrix * \function igraph_matrix_remove_col * \brief Removes a column from a matrix. * * \param m The matrix object. * \param col The column to remove. * \return Error code, always returns with success. * * Time complexity: linear with the number of elements of the new, * resized matrix. */ int FUNCTION(igraph_matrix,remove_col)(TYPE(igraph_matrix) *m, long int col) { FUNCTION(igraph_vector,remove_section)(&m->data, (m->nrow)*col, (m->nrow)*(col+1)); m->ncol--; return 0; } /** * \ingroup matrix * \function igraph_matrix_permdelete_rows * \brief Removes rows from a matrix (for internal use). * * Time complexity: linear with the number of elements of the original * matrix. */ int FUNCTION(igraph_matrix,permdelete_rows)(TYPE(igraph_matrix) *m, long int *index, long int nremove) { long int i, j; for (j=0; jnrow; j++) { if (index[j] != 0) { for (i=0; incol; i++) { MATRIX(*m, index[j]-1, i) = MATRIX(*m, j, i); } } } /* Remove unnecessary elements from the end of each column */ for (i=0; incol; i++) FUNCTION(igraph_vector,remove_section)(&m->data, (i+1)*(m->nrow-nremove), (i+1)*(m->nrow-nremove)+nremove); FUNCTION(igraph_matrix,resize)(m, m->nrow-nremove, m->ncol); return 0; } /** * \ingroup matrix * \function igraph_matrix_delete_rows_neg * \brief Removes columns from a matrix (for internal use). * * Time complexity: linear with the number of elements of the original * matrix. */ int FUNCTION(igraph_matrix,delete_rows_neg)(TYPE(igraph_matrix) *m, const igraph_vector_t *neg, long int nremove) { long int i, j, idx=0; for (i=0; incol; i++) { for (j=0; jnrow; j++) { if (VECTOR(*neg)[j] >= 0) { MATRIX(*m, idx++, i) = MATRIX(*m, j, i); } } idx=0; } FUNCTION(igraph_matrix,resize)(m, m->nrow-nremove, m->ncol); return 0; } /** * \ingroup matrix * \function igraph_matrix_copy * \brief Copies a matrix. * * * Creates a matrix object by copying from an existing matrix. * \param to Pointer to an uninitialized matrix object. * \param from The initialized matrix object to copy. * \return Error code, \c IGRAPH_ENOMEM if there * isn't enough memory to allocate the new matrix. * * Time complexity: O(n), the number * of elements in the matrix. */ int FUNCTION(igraph_matrix,copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { to->nrow = from->nrow; to->ncol = from->ncol; return FUNCTION(igraph_vector,copy)(&to->data, &from->data); } #ifndef NOTORDERED /** * \function igraph_matrix_max * * Returns the maximal element of a matrix. * \param m The matrix object. * \return The maximum element. For empty matrix the returned value is * undefined. * * Added in version 0.2. * * Time complexity: O(n), the number of elements in the matrix. */ igraph_real_t FUNCTION(igraph_matrix,max)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector,max)(&m->data); } #endif /** * \function igraph_matrix_scale * * Multiplies each element of the matrix by a constant. * \param m The matrix. * \param by The constant. * * Added in version 0.2. * * Time complexity: O(n), the number of elements in the matrix. */ void FUNCTION(igraph_matrix,scale)(TYPE(igraph_matrix) *m, BASE by) { FUNCTION(igraph_vector,scale)(&m->data, by); } /** * \function igraph_matrix_select_rows * \brief Select some rows of a matrix. * * This function selects some rows of a matrix and returns them in a * new matrix. The result matrix should be initialized before calling * the function. * \param m The input matrix. * \param res The result matrix. It should be initialized and will be * resized as needed. * \param rows Vector; it contains the row indices (starting with * zero) to extract. Note that no range checking is performed. * \return Error code. * * Time complexity: O(nm), n is the number of rows, m the number of * columns of the result matrix. */ int FUNCTION(igraph_matrix,select_rows)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_t *rows) { long int norows=igraph_vector_size(rows); long int i, j, ncols=FUNCTION(igraph_matrix,ncol)(m); IGRAPH_CHECK(FUNCTION(igraph_matrix,resize)(res, norows, ncols)); for (i=0; i=m->ncol) { IGRAPH_ERROR("Index out of range for selecting matrix column", IGRAPH_EINVAL); } IGRAPH_CHECK(FUNCTION(igraph_vector,get_interval)(&m->data, res, nrow*index, nrow*(index+1))); return 0; } /** * \function igraph_matrix_sum * \brief Sum of elements. * * Returns the sum of the elements of a matrix. * \param m The input matrix. * \return The sum of the elements. * * Time complexity: O(mn), the number of elements in the matrix. */ BASE FUNCTION(igraph_matrix,sum)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector,sum)(&m->data); } /** * \function igraph_matrix_all_e * \brief Are all elements equal? * * \param lhs The first matrix. * \param rhs The second matrix. * \return Positive integer (=true) if the elements in the \p lhs are all * equal to the corresponding elements in \p rhs. Returns \c 0 * (=false) if the dimensions of the matrices don't match. * * Time complexity: O(nm), the size of the matrices. */ igraph_bool_t FUNCTION(igraph_matrix,all_e)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs) { return lhs->ncol==rhs->ncol && lhs->nrow==rhs->nrow && FUNCTION(igraph_vector,all_e)(&lhs->data, &rhs->data); } igraph_bool_t FUNCTION(igraph_matrix,is_equal)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs) { return FUNCTION(igraph_matrix,all_e)(lhs, rhs); } #ifndef NOTORDERED /** * \function igraph_matrix_all_l * \brief Are all elements less? * * \param lhs The first matrix. * \param rhs The second matrix. * \return Positive integer (=true) if the elements in the \p lhs are all * less than the corresponding elements in \p rhs. Returns \c 0 * (=false) if the dimensions of the matrices don't match. * * Time complexity: O(nm), the size of the matrices. */ igraph_bool_t FUNCTION(igraph_matrix,all_l)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs) { return lhs->ncol==rhs->ncol && lhs->nrow==rhs->nrow && FUNCTION(igraph_vector,all_l)(&lhs->data, &rhs->data); } /** * \function igraph_matrix_all_g * \brief Are all elements greater? * * \param lhs The first matrix. * \param rhs The second matrix. * \return Positive integer (=true) if the elements in the \p lhs are all * greater than the corresponding elements in \p rhs. Returns \c 0 * (=false) if the dimensions of the matrices don't match. * * Time complexity: O(nm), the size of the matrices. */ igraph_bool_t FUNCTION(igraph_matrix,all_g)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs) { return lhs->ncol==rhs->ncol && lhs->nrow==rhs->nrow && FUNCTION(igraph_vector,all_g)(&lhs->data, &rhs->data); } /** * \function igraph_matrix_all_le * \brief Are all elements less or equal? * * \param lhs The first matrix. * \param rhs The second matrix. * \return Positive integer (=true) if the elements in the \p lhs are all * less than or equal to the corresponding elements in \p * rhs. Returns \c 0 (=false) if the dimensions of the matrices * don't match. * * Time complexity: O(nm), the size of the matrices. */ igraph_bool_t FUNCTION(igraph_matrix,all_le)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs) { return lhs->ncol==rhs->ncol && lhs->nrow==rhs->nrow && FUNCTION(igraph_vector,all_le)(&lhs->data, &rhs->data); } /** * \function igraph_matrix_all_ge * \brief Are all elements greater or equal? * * \param lhs The first matrix. * \param rhs The second matrix. * \return Positive integer (=true) if the elements in the \p lhs are all * greater than or equal to the corresponding elements in \p * rhs. Returns \c 0 (=false) if the dimensions of the matrices * don't match. * * Time complexity: O(nm), the size of the matrices. */ igraph_bool_t FUNCTION(igraph_matrix,all_ge)(const TYPE(igraph_matrix) *lhs, const TYPE(igraph_matrix) *rhs) { return lhs->ncol==rhs->ncol && lhs->nrow==rhs->nrow && FUNCTION(igraph_vector,all_ge)(&lhs->data, &rhs->data); } #endif #ifndef NOTORDERED /** * \function igraph_matrix_maxdifference * \brief Maximum absolute difference between two matrices. * * Calculate the maximum absolute difference of two matrices. Both matrices * must be non-empty. If their dimensions differ then a warning is given and * the comparison is performed by vectors columnwise from both matrices. * The remaining elements in the larger vector are ignored. * \param m1 The first matrix. * \param m2 The second matrix. * \return The element with the largest absolute value in \c m1 - \c m2. * * Time complexity: O(mn), the elements in the smaller matrix. */ BASE FUNCTION(igraph_matrix,maxdifference)(const TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2) { long int col1=FUNCTION(igraph_matrix,ncol)(m1); long int col2=FUNCTION(igraph_matrix,ncol)(m2); long int row1=FUNCTION(igraph_matrix,nrow)(m1); long int row2=FUNCTION(igraph_matrix,nrow)(m2); if (col1 != col2 || row1 != row2) { IGRAPH_WARNING("Comparing non-conformant matrices"); } return FUNCTION(igraph_vector,maxdifference)(&m1->data, &m2->data); } #endif /** * \function igraph_matrix_transpose * \brief Transpose a matrix. * * Calculate the transpose of a matrix. Note that the function * reallocates the memory used for the matrix. * \param m The input (and output) matrix. * \return Error code. * * Time complexity: O(mn), the number of elements in the matrix. */ int FUNCTION(igraph_matrix,transpose)(TYPE(igraph_matrix) *m) { long int nrow=m->nrow; long int ncol=m->ncol; if (nrow>1 && ncol>1) { TYPE(igraph_vector) newdata; long int i, size=nrow*ncol, mod=size-1; FUNCTION(igraph_vector,init)(&newdata, size); IGRAPH_FINALLY(FUNCTION(igraph_vector,destroy), &newdata); for (i=0; idata)[ (i*nrow) % mod ]; } VECTOR(newdata)[size-1]=VECTOR(m->data)[size-1]; FUNCTION(igraph_vector,destroy)(&m->data); IGRAPH_FINALLY_CLEAN(1); m->data=newdata; } m->nrow=ncol; m->ncol=nrow; return 0; } /** * \function igraph_matrix_e * Extract an element from a matrix. * * Use this if you need a function for some reason and cannot use the * \ref MATRIX macro. Note that no range checking is performed. * \param m The input matrix. * \param row The row index. * \param col The column index. * \return The element in the given row and column. * * Time complexity: O(1). */ BASE FUNCTION(igraph_matrix,e)(const TYPE(igraph_matrix) *m, long int row, long int col) { return MATRIX(*m, row, col); } /** * \function igraph_matrix_e_ptr * Pointer to an element of a matrix. * * The function returns a pointer to an element. No range checking is * performed. * \param m The input matrix. * \param row The row index. * \param col The column index. * \return Pointer to the element in the given row and column. * * Time complexity: O(1). */ BASE* FUNCTION(igraph_matrix,e_ptr)(const TYPE(igraph_matrix) *m, long int row, long int col) { return &MATRIX(*m, row, col); } /** * \function igraph_matrix_set * Set an element. * * Set an element of a matrix. No range checking is performed. * \param m The input matrix. * \param row The row index. * \param col The column index. * \param value The new value of the element. * * Time complexity: O(1). */ void FUNCTION(igraph_matrix,set)(TYPE(igraph_matrix)* m, long int row, long int col, BASE value) { MATRIX(*m, row, col) = value; } /** * \function igraph_matrix_fill * Fill with an element. * * Set the matrix to a constant matrix. * \param m The input matrix. * \param e The element to set. * * Time complexity: O(mn), the number of elements. */ void FUNCTION(igraph_matrix,fill)(TYPE(igraph_matrix) *m, BASE e) { FUNCTION(igraph_vector,fill)(&m->data, e); } /** * \function igraph_matrix_update * Update from another matrix. * * This function replicates \p from in the matrix \p to. * Note that \p to must be already initialized. * \param to The result matrix. * \param from The matrix to replicate; it is left unchanged. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,update)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { IGRAPH_CHECK(FUNCTION(igraph_matrix,resize)(to, from->nrow, from->ncol)); FUNCTION(igraph_vector,update)(&to->data, &from->data); return 0; } /** * \function igraph_matrix_rbind * Combine two matrices rowwise. * * This function places the rows of \p from below the rows of \c to * and stores the result in \p to. The number of columns in the two * matrices must match. * \param to The upper matrix; the result is also stored here. * \param from The lower matrix. It is left unchanged. * \return Error code. * * Time complexity: O(mn), the number of elements in the newly created * matrix. */ int FUNCTION(igraph_matrix,rbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { long int tocols=to->ncol, fromcols=from->ncol; long int torows=to->nrow, fromrows=from->nrow; long int offset, c, r, index, offset2; if (tocols != fromcols) { IGRAPH_ERROR("Cannot do rbind, number of columns do not match", IGRAPH_EINVAL); } IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(&to->data, tocols * (fromrows+torows))); to->nrow += fromrows; offset=(tocols-1) * fromrows; index=tocols*torows-1; for (c=tocols-1; c>0; c--) { for (r=0; rdata)[index+offset] = VECTOR(to->data)[index]; } offset -= fromrows; } offset=torows; offset2=0; for (c=0; cdata)+offset, VECTOR(from->data)+offset2, sizeof(BASE) * (size_t) fromrows); offset+=fromrows+torows; offset2+=fromrows; } return 0; } /** * \function igraph_matrix_cbind * Combine matrices columnwise. * * This function places the columns of \p from on the right of \p to, * and stores the result in \p to. * \param to The left matrix; the result is stored here too. * \param from The right matrix. It is left unchanged. * \return Error code. * * Time complexity: O(mn), the number of elements on the new matrix. */ int FUNCTION(igraph_matrix,cbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { long int tocols=to->ncol, fromcols=from->ncol; long int torows=to->nrow, fromrows=from->nrow; if (torows != fromrows) { IGRAPH_ERROR("Cannot do rbind, number of rows do not match", IGRAPH_EINVAL); } IGRAPH_CHECK(FUNCTION(igraph_matrix,resize)(to, torows, tocols+fromcols)); FUNCTION(igraph_vector,copy_to)(&from->data, VECTOR(to->data)+tocols*torows); return 0; } /** * \function igraph_matrix_swap * Swap two matrices. * * The contents of the two matrices will be swapped. They must have the * same dimensions. * \param m1 The first matrix. * \param m2 The second matrix. * \return Error code. * * Time complexity: O(mn), the number of elements in the matrices. */ int FUNCTION(igraph_matrix,swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) { if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { IGRAPH_ERROR("Cannot swap non-conformant matrices", IGRAPH_EINVAL); } return FUNCTION(igraph_vector,swap)(&m1->data, &m2->data); } /** * \function igraph_matrix_get_row * Extract a row. * * Extract a row from a matrix and return it as a vector. * \param m The input matrix. * \param res Pointer to an initialized vector; it will be resized if * needed. * \param index The index of the row to select. * \return Error code. * * Time complexity: O(n), the number of columns in the matrix. */ int FUNCTION(igraph_matrix,get_row)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, long int index) { long int rows=m->nrow, cols=m->ncol; long int i, j; if (index >= rows) { IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL); } IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(res, cols)); for (i=index, j=0; jdata)[i]; } return 0; } /** * \function igraph_matrix_set_row * Set a row from a vector. * * Sets the elements of a row with the given vector. This has the effect of * setting row \c index to have the elements in the vector \c v. The length of * the vector and the number of columns in the matrix must match, * otherwise an error is triggered. * \param m The input matrix. * \param v The vector containing the new elements of the row. * \param index Index of the row to set. * \return Error code. * * Time complexity: O(n), the number of columns in the matrix. */ int FUNCTION(igraph_matrix,set_row)(TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, long int index) { long int rows=m->nrow, cols=m->ncol; long int i, j; if (index >= rows) { IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL); } if (FUNCTION(igraph_vector,size)(v) != cols) { IGRAPH_ERROR("Cannot set matrix row, invalid vector length", IGRAPH_EINVAL); } for (i=index, j=0; jdata)[i]=VECTOR(*v)[j]; } return 0; } /** * \function igraph_matrix_set_col * Set a column from a vector. * * Sets the elements of a column with the given vector. In effect, column * \c index will be set with elements from the vector \c v. The length of * the vector and the number of rows in the matrix must match, * otherwise an error is triggered. * \param m The input matrix. * \param v The vector containing the new elements of the column. * \param index Index of the column to set. * \return Error code. * * Time complexity: O(m), the number of rows in the matrix. */ int FUNCTION(igraph_matrix,set_col)(TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, long int index) { long int rows=m->nrow, cols=m->ncol; long int i, j; if (index >= cols) { IGRAPH_ERROR("Index out of range for setting matrix column", IGRAPH_EINVAL); } if (FUNCTION(igraph_vector,size)(v) != rows) { IGRAPH_ERROR("Cannot set matrix column, invalid vector length", IGRAPH_EINVAL); } for (i=index*rows, j=0; jdata)[i]=VECTOR(*v)[j]; } return 0; } /** * \function igraph_matrix_swap_rows * Swap two rows. * * Swap two rows in the matrix. * \param m The input matrix. * \param i The index of the first row. * \param j The index of the second row. * \return Error code. * * Time complexity: O(n), the number of columns. */ int FUNCTION(igraph_matrix,swap_rows)(TYPE(igraph_matrix) *m, long int i, long int j) { long int ncol=m->ncol, nrow=m->nrow; long int n=nrow*ncol; long int index1, index2; if (i>=nrow || j>=nrow) { IGRAPH_ERROR("Cannot swap rows, index out of range", IGRAPH_EINVAL); } if (i==j) { return 0; } for (index1=i, index2=j; index1data)[index1]; VECTOR(m->data)[index1]=VECTOR(m->data)[index2]; VECTOR(m->data)[index2]=tmp; } return 0; } /** * \function igraph_matrix_swap_cols * Swap two columns. * * Swap two columns in the matrix. * \param m The input matrix. * \param i The index of the first column. * \param j The index of the second column. * \return Error code. * * Time complexity: O(m), the number of rows. */ int FUNCTION(igraph_matrix,swap_cols)(TYPE(igraph_matrix) *m, long int i, long int j) { long int ncol=m->ncol, nrow=m->nrow; long int k, index1, index2; if (i>=ncol || j >= ncol) { IGRAPH_ERROR("Cannot swap columns, index out of range", IGRAPH_EINVAL); } if (i==j) { return 0; } for (index1=i*nrow, index2=j*nrow, k=0; kdata)[index1]; VECTOR(m->data)[index1]=VECTOR(m->data)[index2]; VECTOR(m->data)[index2]=tmp; } return 0; } /** * \function igraph_matrix_add_constant * Add a constant to every element. * * \param m The input matrix. * \param plud The constant to add. * * Time complexity: O(mn), the number of elements. */ void FUNCTION(igraph_matrix,add_constant)(TYPE(igraph_matrix) *m, BASE plus) { FUNCTION(igraph_vector,add_constant)(&m->data, plus); } /** * \function igraph_matrix_add * Add two matrices. * * Add \p m2 to \p m1, and store the result in \p m1. The dimensions of the * matrices must match. * \param m1 The first matrix; the result will be stored here. * \param m2 The second matrix; it is left unchanged. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,add)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2) { if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { IGRAPH_ERROR("Cannot add non-conformant matrices", IGRAPH_EINVAL); } return FUNCTION(igraph_vector,add)(&m1->data, &m2->data); } /** * \function igraph_matrix_sub * Difference of two matrices. * * Subtract \p m2 from \p m1 and store the result in \p m1. * The dimensions of the two matrices must match. * \param m1 The first matrix; the result is stored here. * \param m2 The second matrix; it is left unchanged. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,sub)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2) { if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { IGRAPH_ERROR("Cannot subtract non-conformant matrices", IGRAPH_EINVAL); } return FUNCTION(igraph_vector,sub)(&m1->data, &m2->data); } /** * \function igraph_matrix_mul_elements * Elementwise multiplication. * * Multiply \p m1 by \p m2 elementwise and store the result in \p m1. * The dimensions of the two matrices must match. * \param m1 The first matrix; the result is stored here. * \param m2 The second matrix; it is left unchanged. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,mul_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2) { if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { IGRAPH_ERROR("Cannot multiply non-conformant matrices", IGRAPH_EINVAL); } return FUNCTION(igraph_vector,mul)(&m1->data, &m2->data); } /** * \function igraph_matrix_div_elements * Elementwise division. * * Divide \p m1 by \p m2 elementwise and store the result in \p m1. * The dimensions of the two matrices must match. * \param m1 The dividend. The result is store here. * \param m2 The divisor. It is left unchanged. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,div_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2) { if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { IGRAPH_ERROR("Cannot divide non-conformant matrices", IGRAPH_EINVAL); } return FUNCTION(igraph_vector,div)(&m1->data, &m2->data); } #ifndef NOTORDERED /** * \function igraph_matrix_min * Minimum element. * * Returns the smallest element of a non-empty matrix. * \param m The input matrix. * \return The smallest element. * * Time complexity: O(mn), the number of elements. */ igraph_real_t FUNCTION(igraph_matrix,min)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector,min)(&m->data); } /** * \function igraph_matrix_which_min * Indices of the minimum. * * Gives the indices of the (first) smallest element in a non-empty * matrix. * \param m The matrix. * \param i Pointer to a long int. The row index of the * minimum is stored here. * \param j Pointer to a long int. The column index of * the minimum is stored here. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,which_min)(const TYPE(igraph_matrix) *m, long int *i, long int *j) { long int vmin=FUNCTION(igraph_vector,which_min)(&m->data); *i = vmin % m->nrow; *j = vmin / m->nrow; return 0; } /** * \function igraph_matrix_which_max * Indices of the maximum. * * Gives the indices of the (first) largest element in a non-empty * matrix. * \param m The matrix. * \param i Pointer to a long int. The row index of the * maximum is stored here. * \param j Pointer to a long int. The column index of * the maximum is stored here. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,which_max)(const TYPE(igraph_matrix) *m, long int *i, long int *j) { long int vmax=FUNCTION(igraph_vector,which_max)(&m->data); *i = vmax % m->nrow; *j = vmax / m->nrow; return 0; } /** * \function igraph_matrix_minmax * Minimum and maximum * * The maximum and minimum elements of a non-empty matrix. * \param m The input matrix. * \param min Pointer to a base type. The minimum is stored here. * \param max Pointer to a base type. The maximum is stored here. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,minmax)(const TYPE(igraph_matrix) *m, BASE *min, BASE *max) { return FUNCTION(igraph_vector,minmax)(&m->data, min, max); } /** * \function igraph_matrix_which_minmax * Indices of the minimum and maximum * * Find the positions of the smallest and largest elements of a * non-empty matrix. * \param m The input matrix. * \param imin Pointer to a long int, the row index of * the minimum is stored here. * \param jmin Pointer to a long int, the column index of * the minimum is stored here. * \param imax Pointer to a long int, the row index of * the maximum is stored here. * \param jmax Pointer to a long int, the column index of * the maximum is stored here. * \return Error code. * * Time complexity: O(mn), the number of elements. */ int FUNCTION(igraph_matrix,which_minmax)(const TYPE(igraph_matrix) *m, long int *imin, long int *jmin, long int *imax, long int *jmax) { long int vmin, vmax; FUNCTION(igraph_vector,which_minmax)(&m->data, &vmin, &vmax); *imin = vmin % m->nrow; *jmin = vmin / m->nrow; *imax = vmax % m->nrow; *jmax = vmax / m->nrow; return 0; } #endif /** * \function igraph_matrix_isnull * Check for a null matrix. * * Checks whether all elements are zero. * \param m The input matrix. * \return Boolean, \c TRUE is \p m contains only zeros and \c FALSE * otherwise. * * Time complexity: O(mn), the number of elements. */ igraph_bool_t FUNCTION(igraph_matrix,isnull)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector,isnull)(&m->data); } /** * \function igraph_matrix_empty * Check for an empty matrix. * * It is possible to have a matrix with zero rows or zero columns, or * even both. This functions checks for these. * \param m The input matrix. * \return Boolean, \c TRUE if the matrix contains zero elements, and * \c FALSE otherwise. * * Time complexity: O(1). */ igraph_bool_t FUNCTION(igraph_matrix,empty)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector,empty)(&m->data); } /** * \function igraph_matrix_is_symmetric * Check for symmetric matrix. * * A non-square matrix is not symmetric by definition. * \param m The input matrix. * \return Boolean, \c TRUE if the matrix is square and symmetric, \c * FALSE otherwise. * * Time complexity: O(mn), the number of elements. O(1) for non-square * matrices. */ igraph_bool_t FUNCTION(igraph_matrix,is_symmetric)(const TYPE(igraph_matrix) *m) { long int n=m->nrow; long int r,c; if (m->ncol != n) { return 0; } for (r=1; rdata); } /** * \function igraph_matrix_rowsum * Rowwise sum. * * Calculate the sum of the elements in each row. * \param m The input matrix. * \param res Pointer to an initialized vector; the result is stored * here. It will be resized if necessary. * \return Error code. * * Time complexity: O(mn), the number of elements in the matrix. */ int FUNCTION(igraph_matrix,rowsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res) { long int nrow=m->nrow, ncol=m->ncol; long int r, c; BASE sum; IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(res, nrow)); for (r=0; rnrow, ncol=m->ncol; long int r, c; BASE sum; IGRAPH_CHECK(FUNCTION(igraph_vector,resize)(res, ncol)); for (c=0; cdata, e); } /** * \function igraph_matrix_search * Search from a given position. * * Search for an element in a matrix and start the search from the * given position. The search is performed columnwise. * \param m The input matrix. * \param from The position to search from, the positions are * enumerated columnwise. * \param what The element to search for. * \param pos Pointer to a long int. If the element is * found, then this is set to the position of its first appearance. * \param row Pointer to a long int. If the element is * found, then this is set to its row index. * \param col Pointer to a long int. If the element is * found, then this is set to its column index. * \return Boolean, \c TRUE if the element is found, \c FALSE * otherwise. * * Time complexity: O(mn), the number of elements. */ igraph_bool_t FUNCTION(igraph_matrix,search)(const TYPE(igraph_matrix) *m, long int from, BASE what, long int *pos, long int *row, long int *col) { igraph_bool_t find=FUNCTION(igraph_vector,search)(&m->data, from, what, pos); if (find) { *row = *pos % m->nrow; *col = *pos / m->nrow; } return find; } /** * \function igraph_matrix_remove_row * Remove a row. * * A row is removed from the matrix. * \param m The input matrix. * \param row The index of the row to remove. * \return Error code. * * Time complexity: O(mn), the number of elements in the matrix. */ int FUNCTION(igraph_matrix,remove_row)(TYPE(igraph_matrix) *m, long int row) { long int c, r, index=row+1, leap=1, n=m->nrow * m->ncol; if (row >= m->nrow) { IGRAPH_ERROR("Cannot remove row, index out of range", IGRAPH_EINVAL); } for (c=0; cncol; c++) { for (r=0; rnrow-1 && index < n; r++) { VECTOR(m->data)[index-leap] = VECTOR(m->data)[index]; index++; } leap++; index++; } m->nrow--; FUNCTION(igraph_vector,resize)(&m->data, m->nrow * m->ncol); return 0; } /** * \function igraph_matrix_select_cols * \brief Select some columns of a matrix. * * This function selects some columns of a matrix and returns them in a * new matrix. The result matrix should be initialized before calling * the function. * \param m The input matrix. * \param res The result matrix. It should be initialized and will be * resized as needed. * \param cols Vector; it contains the column indices (starting with * zero) to extract. Note that no range checking is performed. * \return Error code. * * Time complexity: O(nm), n is the number of rows, m the number of * columns of the result matrix. */ int FUNCTION(igraph_matrix,select_cols)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_t *cols) { long int ncols=igraph_vector_size(cols); long int nrows=m->nrow; long int i, j; IGRAPH_CHECK(FUNCTION(igraph_matrix,resize)(res, nrows, ncols)); for (i=0; i. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #pragma clang diagnostic ignored "-Wpointer-sign" #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif #include "glpios.h" /*********************************************************************** * NAME * * ios_choose_var - select variable to branch on * * SYNOPSIS * * #include "glpios.h" * int ios_choose_var(glp_tree *T, int *next); * * The routine ios_choose_var chooses a variable from the candidate * list to branch on. Additionally the routine provides a flag stored * in the location next to suggests which of the child subproblems * should be solved next. * * RETURNS * * The routine ios_choose_var returns the ordinal number of the column * choosen. */ static int branch_first(glp_tree *T, int *next); static int branch_last(glp_tree *T, int *next); static int branch_mostf(glp_tree *T, int *next); static int branch_drtom(glp_tree *T, int *next); int ios_choose_var(glp_tree *T, int *next) { int j; if (T->parm->br_tech == GLP_BR_FFV) { /* branch on first fractional variable */ j = branch_first(T, next); } else if (T->parm->br_tech == GLP_BR_LFV) { /* branch on last fractional variable */ j = branch_last(T, next); } else if (T->parm->br_tech == GLP_BR_MFV) { /* branch on most fractional variable */ j = branch_mostf(T, next); } else if (T->parm->br_tech == GLP_BR_DTH) { /* branch using the heuristic by Dreebeck and Tomlin */ j = branch_drtom(T, next); } else if (T->parm->br_tech == GLP_BR_PCH) { /* hybrid pseudocost heuristic */ j = ios_pcost_branch(T, next); } else xassert(T != T); return j; } /*********************************************************************** * branch_first - choose first branching variable * * This routine looks up the list of structural variables and chooses * the first one, which is of integer kind and has fractional value in * optimal solution to the current LP relaxation. * * This routine also selects the branch to be solved next where integer * infeasibility of the chosen variable is less than in other one. */ static int branch_first(glp_tree *T, int *_next) { int j, next; double beta; /* choose the column to branch on */ for (j = 1; j <= T->n; j++) if (T->non_int[j]) break; xassert(1 <= j && j <= T->n); /* select the branch to be solved next */ beta = glp_get_col_prim(T->mip, j); if (beta - floor(beta) < ceil(beta) - beta) next = GLP_DN_BRNCH; else next = GLP_UP_BRNCH; *_next = next; return j; } /*********************************************************************** * branch_last - choose last branching variable * * This routine looks up the list of structural variables and chooses * the last one, which is of integer kind and has fractional value in * optimal solution to the current LP relaxation. * * This routine also selects the branch to be solved next where integer * infeasibility of the chosen variable is less than in other one. */ static int branch_last(glp_tree *T, int *_next) { int j, next; double beta; /* choose the column to branch on */ for (j = T->n; j >= 1; j--) if (T->non_int[j]) break; xassert(1 <= j && j <= T->n); /* select the branch to be solved next */ beta = glp_get_col_prim(T->mip, j); if (beta - floor(beta) < ceil(beta) - beta) next = GLP_DN_BRNCH; else next = GLP_UP_BRNCH; *_next = next; return j; } /*********************************************************************** * branch_mostf - choose most fractional branching variable * * This routine looks up the list of structural variables and chooses * that one, which is of integer kind and has most fractional value in * optimal solution to the current LP relaxation. * * This routine also selects the branch to be solved next where integer * infeasibility of the chosen variable is less than in other one. * * (Alexander Martin notices that "...most infeasible is as good as * random...".) */ static int branch_mostf(glp_tree *T, int *_next) { int j, jj, next; double beta, most, temp; /* choose the column to branch on */ jj = 0, most = DBL_MAX; for (j = 1; j <= T->n; j++) { if (T->non_int[j]) { beta = glp_get_col_prim(T->mip, j); temp = floor(beta) + 0.5; if (most > fabs(beta - temp)) { jj = j, most = fabs(beta - temp); if (beta < temp) next = GLP_DN_BRNCH; else next = GLP_UP_BRNCH; } } } *_next = next; return jj; } /*********************************************************************** * branch_drtom - choose branching var using Driebeck-Tomlin heuristic * * This routine chooses a structural variable, which is required to be * integral and has fractional value in optimal solution of the current * LP relaxation, using a heuristic proposed by Driebeck and Tomlin. * * The routine also selects the branch to be solved next, again due to * Driebeck and Tomlin. * * This routine is based on the heuristic proposed in: * * Driebeck N.J. An algorithm for the solution of mixed-integer * programming problems, Management Science, 12: 576-87 (1966); * * and improved in: * * Tomlin J.A. Branch and bound methods for integer and non-convex * programming, in J.Abadie (ed.), Integer and Nonlinear Programming, * North-Holland, Amsterdam, pp. 437-50 (1970). * * Must note that this heuristic is time-expensive, because computing * one-step degradation (see the routine below) requires one BTRAN for * each fractional-valued structural variable. */ static int branch_drtom(glp_tree *T, int *_next) { glp_prob *mip = T->mip; int m = mip->m; int n = mip->n; char *non_int = T->non_int; int j, jj, k, t, next, kase, len, stat, *ind; double x, dk, alfa, delta_j, delta_k, delta_z, dz_dn, dz_up, dd_dn, dd_up, degrad, *val; /* basic solution of LP relaxation must be optimal */ xassert(glp_get_status(mip) == GLP_OPT); /* allocate working arrays */ ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); /* nothing has been chosen so far */ jj = 0, degrad = -1.0; /* walk through the list of columns (structural variables) */ for (j = 1; j <= n; j++) { /* if j-th column is not marked as fractional, skip it */ if (!non_int[j]) continue; /* obtain (fractional) value of j-th column in basic solution of LP relaxation */ x = glp_get_col_prim(mip, j); /* since the value of j-th column is fractional, the column is basic; compute corresponding row of the simplex table */ len = glp_eval_tab_row(mip, m+j, ind, val); /* the following fragment computes a change in the objective function: delta Z = new Z - old Z, where old Z is the objective value in the current optimal basis, and new Z is the objective value in the adjacent basis, for two cases: 1) if new upper bound ub' = floor(x[j]) is introduced for j-th column (down branch); 2) if new lower bound lb' = ceil(x[j]) is introduced for j-th column (up branch); since in both cases the solution remaining dual feasible becomes primal infeasible, one implicit simplex iteration is performed to determine the change delta Z; it is obvious that new Z, which is never better than old Z, is a lower (minimization) or upper (maximization) bound of the objective function for down- and up-branches. */ for (kase = -1; kase <= +1; kase += 2) { /* if kase < 0, the new upper bound of x[j] is introduced; in this case x[j] should decrease in order to leave the basis and go to its new upper bound */ /* if kase > 0, the new lower bound of x[j] is introduced; in this case x[j] should increase in order to leave the basis and go to its new lower bound */ /* apply the dual ratio test in order to determine which auxiliary or structural variable should enter the basis to keep dual feasibility */ k = glp_dual_rtest(mip, len, ind, val, kase, 1e-9); if (k != 0) k = ind[k]; /* if no non-basic variable has been chosen, LP relaxation of corresponding branch being primal infeasible and dual unbounded has no primal feasible solution; in this case the change delta Z is formally set to infinity */ if (k == 0) { delta_z = (T->mip->dir == GLP_MIN ? +DBL_MAX : -DBL_MAX); goto skip; } /* row of the simplex table that corresponds to non-basic variable x[k] choosen by the dual ratio test is: x[j] = ... + alfa * x[k] + ... where alfa is the influence coefficient (an element of the simplex table row) */ /* determine the coefficient alfa */ for (t = 1; t <= len; t++) if (ind[t] == k) break; xassert(1 <= t && t <= len); alfa = val[t]; /* since in the adjacent basis the variable x[j] becomes non-basic, knowing its value in the current basis we can determine its change delta x[j] = new x[j] - old x[j] */ delta_j = (kase < 0 ? floor(x) : ceil(x)) - x; /* and knowing the coefficient alfa we can determine the corresponding change delta x[k] = new x[k] - old x[k], where old x[k] is a value of x[k] in the current basis, and new x[k] is a value of x[k] in the adjacent basis */ delta_k = delta_j / alfa; /* Tomlin noticed that if the variable x[k] is of integer kind, its change cannot be less (eventually) than one in the magnitude */ if (k > m && glp_get_col_kind(mip, k-m) != GLP_CV) { /* x[k] is structural integer variable */ if (fabs(delta_k - floor(delta_k + 0.5)) > 1e-3) { if (delta_k > 0.0) delta_k = ceil(delta_k); /* +3.14 -> +4 */ else delta_k = floor(delta_k); /* -3.14 -> -4 */ } } /* now determine the status and reduced cost of x[k] in the current basis */ if (k <= m) { stat = glp_get_row_stat(mip, k); dk = glp_get_row_dual(mip, k); } else { stat = glp_get_col_stat(mip, k-m); dk = glp_get_col_dual(mip, k-m); } /* if the current basis is dual degenerate, some reduced costs which are close to zero may have wrong sign due to round-off errors, so correct the sign of d[k] */ switch (T->mip->dir) { case GLP_MIN: if (stat == GLP_NL && dk < 0.0 || stat == GLP_NU && dk > 0.0 || stat == GLP_NF) dk = 0.0; break; case GLP_MAX: if (stat == GLP_NL && dk > 0.0 || stat == GLP_NU && dk < 0.0 || stat == GLP_NF) dk = 0.0; break; default: xassert(T != T); } /* now knowing the change of x[k] and its reduced cost d[k] we can compute the corresponding change in the objective function delta Z = new Z - old Z = d[k] * delta x[k]; note that due to Tomlin's modification new Z can be even worse than in the adjacent basis */ delta_z = dk * delta_k; skip: /* new Z is never better than old Z, therefore the change delta Z is always non-negative (in case of minimization) or non-positive (in case of maximization) */ switch (T->mip->dir) { case GLP_MIN: xassert(delta_z >= 0.0); break; case GLP_MAX: xassert(delta_z <= 0.0); break; default: xassert(T != T); } /* save the change in the objective fnction for down- and up-branches, respectively */ if (kase < 0) dz_dn = delta_z; else dz_up = delta_z; } /* thus, in down-branch no integer feasible solution can be better than Z + dz_dn, and in up-branch no integer feasible solution can be better than Z + dz_up, where Z is value of the objective function in the current basis */ /* following the heuristic by Driebeck and Tomlin we choose a column (i.e. structural variable) which provides largest degradation of the objective function in some of branches; besides, we select the branch with smaller degradation to be solved next and keep other branch with larger degradation in the active list hoping to minimize the number of further backtrackings */ if (degrad < fabs(dz_dn) || degrad < fabs(dz_up)) { jj = j; if (fabs(dz_dn) < fabs(dz_up)) { /* select down branch to be solved next */ next = GLP_DN_BRNCH; degrad = fabs(dz_up); } else { /* select up branch to be solved next */ next = GLP_UP_BRNCH; degrad = fabs(dz_dn); } /* save the objective changes for printing */ dd_dn = dz_dn, dd_up = dz_up; /* if down- or up-branch has no feasible solution, we does not need to consider other candidates (in principle, the corresponding branch could be pruned right now) */ if (degrad == DBL_MAX) break; } } /* free working arrays */ xfree(ind); xfree(val); /* something must be chosen */ xassert(1 <= jj && jj <= n); #if 1 /* 02/XI-2009 */ if (degrad < 1e-6 * (1.0 + 0.001 * fabs(mip->obj_val))) { jj = branch_mostf(T, &next); goto done; } #endif if (T->parm->msg_lev >= GLP_MSG_DBG) { xprintf("branch_drtom: column %d chosen to branch on\n", jj); if (fabs(dd_dn) == DBL_MAX) xprintf("branch_drtom: down-branch is infeasible\n"); else xprintf("branch_drtom: down-branch bound is %.9e\n", lpx_get_obj_val(mip) + dd_dn); if (fabs(dd_up) == DBL_MAX) xprintf("branch_drtom: up-branch is infeasible\n"); else xprintf("branch_drtom: up-branch bound is %.9e\n", lpx_get_obj_val(mip) + dd_up); } done: *_next = next; return jj; } /**********************************************************************/ struct csa { /* common storage area */ int *dn_cnt; /* int dn_cnt[1+n]; */ /* dn_cnt[j] is the number of subproblems, whose LP relaxations have been solved and which are down-branches for variable x[j]; dn_cnt[j] = 0 means the down pseudocost is uninitialized */ double *dn_sum; /* double dn_sum[1+n]; */ /* dn_sum[j] is the sum of per unit degradations of the objective over all dn_cnt[j] subproblems */ int *up_cnt; /* int up_cnt[1+n]; */ /* up_cnt[j] is the number of subproblems, whose LP relaxations have been solved and which are up-branches for variable x[j]; up_cnt[j] = 0 means the up pseudocost is uninitialized */ double *up_sum; /* double up_sum[1+n]; */ /* up_sum[j] is the sum of per unit degradations of the objective over all up_cnt[j] subproblems */ }; void *ios_pcost_init(glp_tree *tree) { /* initialize working data used on pseudocost branching */ struct csa *csa; int n = tree->n, j; csa = xmalloc(sizeof(struct csa)); csa->dn_cnt = xcalloc(1+n, sizeof(int)); csa->dn_sum = xcalloc(1+n, sizeof(double)); csa->up_cnt = xcalloc(1+n, sizeof(int)); csa->up_sum = xcalloc(1+n, sizeof(double)); for (j = 1; j <= n; j++) { csa->dn_cnt[j] = csa->up_cnt[j] = 0; csa->dn_sum[j] = csa->up_sum[j] = 0.0; } return csa; } static double eval_degrad(glp_prob *P, int j, double bnd) { /* compute degradation of the objective on fixing x[j] at given value with a limited number of dual simplex iterations */ /* this routine fixes column x[j] at specified value bnd, solves resulting LP, and returns a lower bound to degradation of the objective, degrad >= 0 */ glp_prob *lp; glp_smcp parm; int ret; double degrad; /* the current basis must be optimal */ xassert(glp_get_status(P) == GLP_OPT); /* create a copy of P */ lp = glp_create_prob(); glp_copy_prob(lp, P, 0); /* fix column x[j] at specified value */ glp_set_col_bnds(lp, j, GLP_FX, bnd, bnd); /* try to solve resulting LP */ glp_init_smcp(&parm); parm.msg_lev = GLP_MSG_OFF; parm.meth = GLP_DUAL; parm.it_lim = 30; parm.out_dly = 1000; parm.meth = GLP_DUAL; ret = glp_simplex(lp, &parm); if (ret == 0 || ret == GLP_EITLIM) { if (glp_get_prim_stat(lp) == GLP_NOFEAS) { /* resulting LP has no primal feasible solution */ degrad = DBL_MAX; } else if (glp_get_dual_stat(lp) == GLP_FEAS) { /* resulting basis is optimal or at least dual feasible, so we have the correct lower bound to degradation */ if (P->dir == GLP_MIN) degrad = lp->obj_val - P->obj_val; else if (P->dir == GLP_MAX) degrad = P->obj_val - lp->obj_val; else xassert(P != P); /* degradation cannot be negative by definition */ /* note that the lower bound to degradation may be close to zero even if its exact value is zero due to round-off errors on computing the objective value */ if (degrad < 1e-6 * (1.0 + 0.001 * fabs(P->obj_val))) degrad = 0.0; } else { /* the final basis reported by the simplex solver is dual infeasible, so we cannot determine a non-trivial lower bound to degradation */ degrad = 0.0; } } else { /* the simplex solver failed */ degrad = 0.0; } /* delete the copy of P */ glp_delete_prob(lp); return degrad; } void ios_pcost_update(glp_tree *tree) { /* update history information for pseudocost branching */ /* this routine is called every time when LP relaxation of the current subproblem has been solved to optimality with all lazy and cutting plane constraints included */ int j; double dx, dz, psi; struct csa *csa = tree->pcost; xassert(csa != NULL); xassert(tree->curr != NULL); /* if the current subproblem is the root, skip updating */ if (tree->curr->up == NULL) goto skip; /* determine branching variable x[j], which was used in the parent subproblem to create the current subproblem */ j = tree->curr->up->br_var; xassert(1 <= j && j <= tree->n); /* determine the change dx[j] = new x[j] - old x[j], where new x[j] is a value of x[j] in optimal solution to LP relaxation of the current subproblem, old x[j] is a value of x[j] in optimal solution to LP relaxation of the parent subproblem */ dx = tree->mip->col[j]->prim - tree->curr->up->br_val; xassert(dx != 0.0); /* determine corresponding change dz = new dz - old dz in the objective function value */ dz = tree->mip->obj_val - tree->curr->up->lp_obj; /* determine per unit degradation of the objective function */ psi = fabs(dz / dx); /* update history information */ if (dx < 0.0) { /* the current subproblem is down-branch */ csa->dn_cnt[j]++; csa->dn_sum[j] += psi; } else /* dx > 0.0 */ { /* the current subproblem is up-branch */ csa->up_cnt[j]++; csa->up_sum[j] += psi; } skip: return; } void ios_pcost_free(glp_tree *tree) { /* free working area used on pseudocost branching */ struct csa *csa = tree->pcost; xassert(csa != NULL); xfree(csa->dn_cnt); xfree(csa->dn_sum); xfree(csa->up_cnt); xfree(csa->up_sum); xfree(csa); tree->pcost = NULL; return; } static double eval_psi(glp_tree *T, int j, int brnch) { /* compute estimation of pseudocost of variable x[j] for down- or up-branch */ struct csa *csa = T->pcost; double beta, degrad, psi; xassert(csa != NULL); xassert(1 <= j && j <= T->n); if (brnch == GLP_DN_BRNCH) { /* down-branch */ if (csa->dn_cnt[j] == 0) { /* initialize down pseudocost */ beta = T->mip->col[j]->prim; degrad = eval_degrad(T->mip, j, floor(beta)); if (degrad == DBL_MAX) { psi = DBL_MAX; goto done; } csa->dn_cnt[j] = 1; csa->dn_sum[j] = degrad / (beta - floor(beta)); } psi = csa->dn_sum[j] / (double)csa->dn_cnt[j]; } else if (brnch == GLP_UP_BRNCH) { /* up-branch */ if (csa->up_cnt[j] == 0) { /* initialize up pseudocost */ beta = T->mip->col[j]->prim; degrad = eval_degrad(T->mip, j, ceil(beta)); if (degrad == DBL_MAX) { psi = DBL_MAX; goto done; } csa->up_cnt[j] = 1; csa->up_sum[j] = degrad / (ceil(beta) - beta); } psi = csa->up_sum[j] / (double)csa->up_cnt[j]; } else xassert(brnch != brnch); done: return psi; } static void progress(glp_tree *T) { /* display progress of pseudocost initialization */ struct csa *csa = T->pcost; int j, nv = 0, ni = 0; for (j = 1; j <= T->n; j++) { if (glp_ios_can_branch(T, j)) { nv++; if (csa->dn_cnt[j] > 0 && csa->up_cnt[j] > 0) ni++; } } xprintf("Pseudocosts initialized for %d of %d variables\n", ni, nv); return; } int ios_pcost_branch(glp_tree *T, int *_next) { /* choose branching variable with pseudocost branching */ glp_long t = xtime(); int j, jjj, sel; double beta, psi, d1, d2, d, dmax; /* initialize the working arrays */ if (T->pcost == NULL) T->pcost = ios_pcost_init(T); /* nothing has been chosen so far */ jjj = 0, dmax = -1.0; /* go through the list of branching candidates */ for (j = 1; j <= T->n; j++) { if (!glp_ios_can_branch(T, j)) continue; /* determine primal value of x[j] in optimal solution to LP relaxation of the current subproblem */ beta = T->mip->col[j]->prim; /* estimate pseudocost of x[j] for down-branch */ psi = eval_psi(T, j, GLP_DN_BRNCH); if (psi == DBL_MAX) { /* down-branch has no primal feasible solution */ jjj = j, sel = GLP_DN_BRNCH; goto done; } /* estimate degradation of the objective for down-branch */ d1 = psi * (beta - floor(beta)); /* estimate pseudocost of x[j] for up-branch */ psi = eval_psi(T, j, GLP_UP_BRNCH); if (psi == DBL_MAX) { /* up-branch has no primal feasible solution */ jjj = j, sel = GLP_UP_BRNCH; goto done; } /* estimate degradation of the objective for up-branch */ d2 = psi * (ceil(beta) - beta); /* determine d = max(d1, d2) */ d = (d1 > d2 ? d1 : d2); /* choose x[j] which provides maximal estimated degradation of the objective either in down- or up-branch */ if (dmax < d) { dmax = d; jjj = j; /* continue the search from a subproblem, where degradation is less than in other one */ sel = (d1 <= d2 ? GLP_DN_BRNCH : GLP_UP_BRNCH); } /* display progress of pseudocost initialization */ if (T->parm->msg_lev >= GLP_ON) { if (xdifftime(xtime(), t) >= 10.0) { progress(T); t = xtime(); } } } if (dmax == 0.0) { /* no degradation is indicated; choose a variable having most fractional value */ jjj = branch_mostf(T, &sel); } done: *_next = sel; return jjj; } /* eof */ igraph/src/foreign-gml-parser.y0000644000176000001440000001656212325372071016251 0ustar ripleyusers/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include #include "igraph_error.h" #include "igraph_memory.h" #include "config.h" #include "igraph_hacks_internal.h" #include "igraph_math.h" #include "igraph_gml_tree.h" #include "foreign-gml-header.h" #include "foreign-gml-parser.h" #define yyscan_t void* int igraph_gml_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void *scanner); int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, char *s); char *igraph_gml_yyget_text (yyscan_t yyscanner ); int igraph_gml_yyget_leng (yyscan_t yyscanner ); void igraph_i_gml_get_keyword(char *s, int len, void *res); void igraph_i_gml_get_string(char *s, int len, void *res); double igraph_i_gml_get_real(char *s, int len); igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value); igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, char *v, int vlen); igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, char *value, int valuelen); igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, igraph_gml_tree_t *list); igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2); #define scanner context->scanner #define USE(x) /*(x)*/ %} %pure-parser %output="y.tab.c" %name-prefix="igraph_gml_yy" %defines %locations %error-verbose %parse-param { igraph_i_gml_parsedata_t* context } %lex-param { void *scanner } %union { struct { char *s; int len; } str; void *tree; double real; } %type list; %type keyvalue; %type key; %type num; %type string; %token STRING %token NUM %token KEYWORD %token LISTOPEN %token LISTCLOSE %token EOFF %destructor { igraph_Free($$.s); } string key KEYWORD; %destructor { igraph_gml_tree_destroy($$); } list keyvalue; %% input: list { context->tree=$1; } | list EOFF { context->tree=$1; } ; list: keyvalue { $$=$1; } | list keyvalue { $$=igraph_i_gml_merge($1, $2); }; keyvalue: key num { $$=igraph_i_gml_make_numeric($1.s, $1.len, $2); } | key string { $$=igraph_i_gml_make_string($1.s, $1.len, $2.s, $2.len); } | key LISTOPEN list LISTCLOSE { $$=igraph_i_gml_make_list($1.s, $1.len, $3); } | key key { $$=igraph_i_gml_make_numeric2($1.s, $1.len, $2.s, $2.len); } ; key: KEYWORD { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner), &$$); USE($1) }; num : NUM { $$=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner)); }; string: STRING { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner), &$$); }; %% int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "Parse error in GML file, line %i (%s)", locp->first_line, s); return 0; } void igraph_i_gml_get_keyword(char *s, int len, void *res) { struct { char *s; int len; } *p=res; p->s=igraph_Calloc(len+1, char); if (!p->s) { igraph_error("Cannot read GML file", __FILE__, __LINE__, IGRAPH_PARSEERROR); } memcpy(p->s, s, sizeof(char)*len); p->s[len]='\0'; p->len=len; } void igraph_i_gml_get_string(char *s, int len, void *res) { struct { char *s; int len; } *p=res; p->s=igraph_Calloc(len-1, char); if (!p->s) { igraph_error("Cannot read GML file", __FILE__, __LINE__, IGRAPH_PARSEERROR); } memcpy(p->s, s+1, sizeof(char)*(len-2)); p->s[len-2]='\0'; p->len=len-2; } double igraph_i_gml_get_real(char *s, int len) { igraph_real_t num; char tmp=s[len]; s[len]='\0'; sscanf(s, "%lf", &num); s[len]=tmp; return num; } igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } if (floor(value)==value) { igraph_gml_tree_init_integer(t, s, len, value); } else { igraph_gml_tree_init_real(t, s, len, value); } return t; } igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, char *v, int vlen) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); char tmp=v[vlen]; igraph_real_t value=0; if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } v[vlen]='\0'; if (strcasecmp(v, "inf")) { value=IGRAPH_INFINITY; } else if (strcasecmp(v, "nan")) { value=IGRAPH_NAN; } else { igraph_error("Parse error", __FILE__, __LINE__, IGRAPH_PARSEERROR); } v[vlen]=tmp; igraph_gml_tree_init_real(t, s, len, value); return t; } igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, char *value, int valuelen) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } igraph_gml_tree_init_string(t, s, len, value, valuelen); return t; } igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, igraph_gml_tree_t *list) { igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t); if (!t) { igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM); return 0; } igraph_gml_tree_init_tree(t, s, len, list); return t; } igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { igraph_gml_tree_mergedest(t1, t2); igraph_Free(t2); return t1; } igraph/src/igraph_marked_queue.c0000644000176000001440000000622212325527073016520 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_marked_queue.h" #define BATCH_MARKER -1 int igraph_marked_queue_init(igraph_marked_queue_t *q, long int size) { IGRAPH_CHECK(igraph_dqueue_init(&q->Q, 0)); IGRAPH_FINALLY(igraph_dqueue_destroy, &q->Q); IGRAPH_CHECK(igraph_vector_long_init(&q->set, size)); q->mark=1; q->size=0; IGRAPH_FINALLY_CLEAN(1); return 0; } void igraph_marked_queue_destroy(igraph_marked_queue_t *q) { igraph_vector_long_destroy(&q->set); igraph_dqueue_destroy(&q->Q); } void igraph_marked_queue_reset(igraph_marked_queue_t *q) { igraph_dqueue_clear(&q->Q); q->size = 0; q->mark += 1; if (q->mark==0) { igraph_vector_long_null(&q->set); q->mark += 1; } } igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q) { return q->size == 0; } long int igraph_marked_queue_size(const igraph_marked_queue_t *q) { return q->size; } igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q, long int elem) { return (VECTOR(q->set)[elem] == q->mark); } int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem) { if (VECTOR(q->set)[elem] != q->mark) { IGRAPH_CHECK(igraph_dqueue_push(&q->Q, elem)); VECTOR(q->set)[elem] = q->mark; q->size += 1; } return 0; } int igraph_marked_queue_start_batch(igraph_marked_queue_t *q) { IGRAPH_CHECK(igraph_dqueue_push(&q->Q, BATCH_MARKER)); return 0; } void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q) { long int size=igraph_dqueue_size(&q->Q); long int elem; while (size > 0 && (elem=(long int) igraph_dqueue_pop_back(&q->Q)) != BATCH_MARKER) { VECTOR(q->set)[elem]=0; size--; q->size--; } } #ifndef USING_R int igraph_marked_queue_print(const igraph_marked_queue_t *q) { IGRAPH_CHECK(igraph_dqueue_print(&q->Q)); return 0; } #endif int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file) { IGRAPH_CHECK(igraph_dqueue_fprint(&q->Q, file)); return 0; } int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q, igraph_vector_t *vec) { long int i, p, n=igraph_dqueue_size(&q->Q); IGRAPH_CHECK(igraph_vector_resize(vec, q->size)); for (i=0, p=0; iQ, i); if (e != BATCH_MARKER) { VECTOR(*vec)[p++]=e; } } return 0; } igraph/src/triangles_template1.h0000644000176000001440000000540612325527074016474 0ustar ripleyusers/* -*- mode: C -*- */ /* vim:set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ long int no_of_nodes=igraph_vcount(graph); igraph_vit_t vit; long int nodes_to_calc; igraph_vector_t *neis1, *neis2; #ifdef TRIPLES igraph_real_t triples; #endif igraph_real_t triangles; long int i, j, k; long int neilen1, neilen2; long int *neis; igraph_lazy_adjlist_t adjlist; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc=IGRAPH_VIT_SIZE(vit); neis=igraph_Calloc(no_of_nodes, long int); if (neis==0) { IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, neis); IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_SIMPLIFY); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); for (i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int node=IGRAPH_VIT_GET(vit); IGRAPH_ALLOW_INTERRUPTION(); neis1=igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); neilen1=igraph_vector_size(neis1); for (j=0; j 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_CLIQUES_H #define IGRAPH_CLIQUES_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Cliques, maximal independent vertex sets */ /* -------------------------------------------------- */ int igraph_maximal_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size); int igraph_maximal_cliques_file(const igraph_t *graph, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); int igraph_maximal_cliques_count(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t min_size, igraph_integer_t max_size); int igraph_maximal_cliques_subset(const igraph_t *graph, igraph_vector_int_t *subset, igraph_vector_ptr_t *res, igraph_integer_t *no, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size); int igraph_largest_cliques(const igraph_t *graph, igraph_vector_ptr_t *cliques); int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); int igraph_independent_vertex_sets(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size); int igraph_largest_independent_vertex_sets(const igraph_t *graph, igraph_vector_ptr_t *res); int igraph_maximal_independent_vertex_sets(const igraph_t *graph, igraph_vector_ptr_t *res); int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no); __END_DECLS #endif igraph/src/foreign-dl-lexer.l0000644000176000001440000001104112325372071015662 0ustar ripleyusers/* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include #include "foreign-dl-header.h" #include "foreign-dl-parser.h" #define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); %} %option noyywrap %option prefix="igraph_dl_yy" %option outfile="lex.yy.c" %option nounput %option noinput %option reentrant %option bison-bridge %option bison-locations digit [0-9] whitespace [ \t\v\f] %x LABELM FULLMATRIX EDGELIST NODELIST %% <*>\n\r|\r\n|\r|\n { return NEWLINE; } [dD][lL]{whitespace}+ { return DL; } [nN]{whitespace}*[=]{whitespace}* { return NEQ; } {digit}+ { return NUM; } [dD][aA][tT][aA][:] { switch (yyextra->mode) { case 0: BEGIN(FULLMATRIX); break; case 1: BEGIN(EDGELIST); break; case 2: BEGIN(NODELIST); break; } return DATA; } [lL][aA][bB][eE][lL][sS]: { BEGIN(LABELM); return LABELS; } [lL][aA][bB][eE][lL][sS]{whitespace}+[eE][mM][bB][eE][dD][dD][eE][dD]:?{whitespace}* { return LABELSEMBEDDED; } [fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[fF][uU][lL][lL][mM][aA][tT][rR][iI][xX]{whitespace}* { yyextra->mode=0; return FORMATFULLMATRIX; } [fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[eE][dD][gG][eE][lL][iI][sS][tT][1]{whitespace}* { yyextra->mode=1; return FORMATEDGELIST1; } [fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[nN][oO][dD][eE][lL][iI][sS][tT][1]{whitespace}* { yyextra->mode=2; return FORMATNODELIST1; } [, ] { /* eaten up */ } [^, \t\n\r\f\v]+{whitespace}* { return LABEL; } {digit}{whitespace}* { return DIGIT; } [^ \t\n\r\v\f,]+ { return LABEL; } {whitespace} { } \-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } [^ \t\n\r\v\f,]+ { return LABEL; } {whitespace}* { } {digit}+ { return NUM; } [^ \t\r\n\v\f,]+ { return LABEL; } {whitespace}* { } {whitespace}+ { /* eaten up */ } <> { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; BEGIN(INITIAL); return EOFF; } } <*>. { return 0; } igraph/src/igraph_dqueue.h0000644000176000001440000000401212325527073015341 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_DQUEUE_H #define IGRAPH_DQUEUE_H #include "igraph_types.h" #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* -------------------------------------------------- */ /* double ended queue, very useful */ /* -------------------------------------------------- */ #define BASE_IGRAPH_REAL #include "igraph_pmt.h" #include "igraph_dqueue_pmt.h" #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL #define BASE_LONG #include "igraph_pmt.h" #include "igraph_dqueue_pmt.h" #include "igraph_pmt_off.h" #undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_dqueue_pmt.h" #include "igraph_pmt_off.h" #undef BASE_CHAR #define BASE_BOOL #include "igraph_pmt.h" #include "igraph_dqueue_pmt.h" #include "igraph_pmt_off.h" #undef BASE_BOOL #define IGRAPH_DQUEUE_NULL { 0,0,0,0 } #define IGRAPH_DQUEUE_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_dqueue_init(v, size)); \ IGRAPH_FINALLY(igraph_dqueue_destroy, v); } while (0) __END_DECLS #endif igraph/src/glpnet01.c0000644000176000001440000002335012325527073014152 0ustar ripleyusers/* glpnet01.c (permutations for zero-free diagonal) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * This code is the result of translation of the Fortran subroutines * MC21A and MC21B associated with the following paper: * * I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM * Trans. on Math. Softw. 7 (1981), 387-390. * * Use of ACM Algorithms is subject to the ACM Software Copyright and * License Agreement. See . * * The translation was made by Andrew Makhorin . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpnet.h" /*********************************************************************** * NAME * * mc21a - permutations for zero-free diagonal * * SYNOPSIS * * #include "glpnet.h" * int mc21a(int n, const int icn[], const int ip[], const int lenr[], * int iperm[], int pr[], int arp[], int cv[], int out[]); * * DESCRIPTION * * Given the pattern of nonzeros of a sparse matrix, the routine mc21a * attempts to find a permutation of its rows that makes the matrix have * no zeros on its diagonal. * * INPUT PARAMETERS * * n order of matrix. * * icn array containing the column indices of the non-zeros. Those * belonging to a single row must be contiguous but the ordering * of column indices within each row is unimportant and wasted * space between rows is permitted. * * ip ip[i], i = 1,2,...,n, is the position in array icn of the * first column index of a non-zero in row i. * * lenr lenr[i], i = 1,2,...,n, is the number of non-zeros in row i. * * OUTPUT PARAMETER * * iperm contains permutation to make diagonal have the smallest * number of zeros on it. Elements (iperm[i], i), i = 1,2,...,n, * are non-zero at the end of the algorithm unless the matrix is * structurally singular. In this case, (iperm[i], i) will be * zero for n - numnz entries. * * WORKING ARRAYS * * pr working array of length [1+n], where pr[0] is not used. * pr[i] is the previous row to i in the depth first search. * * arp working array of length [1+n], where arp[0] is not used. * arp[i] is one less than the number of non-zeros in row i which * have not been scanned when looking for a cheap assignment. * * cv working array of length [1+n], where cv[0] is not used. * cv[i] is the most recent row extension at which column i was * visited. * * out working array of length [1+n], where out[0] is not used. * out[i] is one less than the number of non-zeros in row i * which have not been scanned during one pass through the main * loop. * * RETURNS * * The routine mc21a returns numnz, the number of non-zeros on diagonal * of permuted matrix. */ int mc21a(int n, const int icn[], const int ip[], const int lenr[], int iperm[], int pr[], int arp[], int cv[], int out[]) { int i, ii, in1, in2, j, j1, jord, k, kk, numnz; /* Initialization of arrays. */ for (i = 1; i <= n; i++) { arp[i] = lenr[i] - 1; cv[i] = iperm[i] = 0; } numnz = 0; /* Main loop. */ /* Each pass round this loop either results in a new assignment or gives a row with no assignment. */ for (jord = 1; jord <= n; jord++) { j = jord; pr[j] = -1; for (k = 1; k <= jord; k++) { /* Look for a cheap assignment. */ in1 = arp[j]; if (in1 >= 0) { in2 = ip[j] + lenr[j] - 1; in1 = in2 - in1; for (ii = in1; ii <= in2; ii++) { i = icn[ii]; if (iperm[i] == 0) goto L110; } /* No cheap assignment in row. */ arp[j] = -1; } /* Begin looking for assignment chain starting with row j.*/ out[j] = lenr[j] - 1; /* Inner loop. Extends chain by one or backtracks. */ for (kk = 1; kk <= jord; kk++) { in1 = out[j]; if (in1 >= 0) { in2 = ip[j] + lenr[j] - 1; in1 = in2 - in1; /* Forward scan. */ for (ii = in1; ii <= in2; ii++) { i = icn[ii]; if (cv[i] != jord) { /* Column i has not yet been accessed during this pass. */ j1 = j; j = iperm[i]; cv[i] = jord; pr[j] = j1; out[j1] = in2 - ii - 1; goto L100; } } } /* Backtracking step. */ j = pr[j]; if (j == -1) goto L130; } L100: ; } L110: /* New assignment is made. */ iperm[i] = j; arp[j] = in2 - ii - 1; numnz++; for (k = 1; k <= jord; k++) { j = pr[j]; if (j == -1) break; ii = ip[j] + lenr[j] - out[j] - 2; i = icn[ii]; iperm[i] = j; } L130: ; } /* If matrix is structurally singular, we now complete the permutation iperm. */ if (numnz < n) { for (i = 1; i <= n; i++) arp[i] = 0; k = 0; for (i = 1; i <= n; i++) { if (iperm[i] == 0) out[++k] = i; else arp[iperm[i]] = i; } k = 0; for (i = 1; i <= n; i++) { if (arp[i] == 0) iperm[out[++k]] = i; } } return numnz; } /**********************************************************************/ #if 0 #include "glplib.h" int sing; void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum, int iw[]); void fa01bs(int max, int *nrand); int main(void) { /* test program for the routine mc21a */ /* these runs on random matrices cause all possible statements in mc21a to be executed */ int i, iold, j, j1, j2, jj, knum, l, licn, n, nov4, num, numnz; int ip[1+21], icn[1+1000], iperm[1+20], lenr[1+20], iw1[1+80]; licn = 1000; /* run on random matrices of orders 1 through 20 */ for (n = 1; n <= 20; n++) { nov4 = n / 4; if (nov4 < 1) nov4 = 1; L10: fa01bs(nov4, &l); knum = l * n; /* knum is requested number of non-zeros in random matrix */ if (knum > licn) goto L10; /* if sing is false, matrix is guaranteed structurally non-singular */ sing = ((n / 2) * 2 == n); /* call to subroutine to generate random matrix */ ranmat(n, n, icn, ip, n+1, &knum, iw1); /* knum is now actual number of non-zeros in random matrix */ if (knum > licn) goto L10; xprintf("n = %2d; nz = %4d; sing = %d\n", n, knum, sing); /* set up array of row lengths */ for (i = 1; i <= n; i++) lenr[i] = ip[i+1] - ip[i]; /* call to mc21a */ numnz = mc21a(n, icn, ip, lenr, iperm, &iw1[0], &iw1[n], &iw1[n+n], &iw1[n+n+n]); /* testing to see if there are numnz non-zeros on the diagonal of the permuted matrix. */ num = 0; for (i = 1; i <= n; i++) { iold = iperm[i]; j1 = ip[iold]; j2 = j1 + lenr[iold] - 1; if (j2 < j1) continue; for (jj = j1; jj <= j2; jj++) { j = icn[jj]; if (j == i) { num++; break; } } } if (num != numnz) xprintf("Failure in mc21a, numnz = %d instead of %d\n", numnz, num); } return 0; } void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum, int iw[]) { /* subroutine to generate random matrix */ int i, ii, inum, j, lrow, matnum; inum = (*knum / n) * 2; if (inum > n-1) inum = n-1; matnum = 1; /* each pass through this loop generates a row of the matrix */ for (j = 1; j <= m; j++) { iptr[j] = matnum; if (!(sing || j > n)) icn[matnum++] = j; if (n == 1) continue; for (i = 1; i <= n; i++) iw[i] = 0; if (!sing) iw[j] = 1; fa01bs(inum, &lrow); lrow--; if (lrow == 0) continue; /* lrow off-diagonal non-zeros in row j of the matrix */ for (ii = 1; ii <= lrow; ii++) { for (;;) { fa01bs(n, &i); if (iw[i] != 1) break; } iw[i] = 1; icn[matnum++] = i; } } for (i = m+1; i <= nnnp1; i++) iptr[i] = matnum; *knum = matnum - 1; return; } double g = 1431655765.0; double fa01as(int i) { /* random number generator */ g = fmod(g * 9228907.0, 4294967296.0); if (i >= 0) return g / 4294967296.0; else return 2.0 * g / 4294967296.0 - 1.0; } void fa01bs(int max, int *nrand) { *nrand = (int)(fa01as(1) * (double)max) + 1; return; } #endif /* eof */ igraph/src/igraph_memory.h0000644000176000001440000000306212325527073015365 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef REST_MEMORY_H #define REST_MEMORY_H #include #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS #define igraph_Calloc(n,t) (t*) calloc( (size_t)(n), sizeof(t) ) #define igraph_Realloc(p,n,t) (t*) realloc((void*)(p), (size_t)((n)*sizeof(t))) #define igraph_Free(p) (free( (void *)(p) ), (p) = NULL) /* #ifndef IGRAPH_NO_CALLOC */ /* # define Calloc igraph_Calloc */ /* # define Realloc igraph_Realloc */ /* # define Free igraph_Free */ /* #endif */ int igraph_free(void *p); __END_DECLS #endif igraph/src/igraph_adjlist.h0000644000176000001440000002037112325527073015511 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_ADJLIST_H #define IGRAPH_ADJLIST_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" __BEGIN_DECLS typedef struct igraph_adjlist_t { igraph_integer_t length; igraph_vector_int_t *adjs; } igraph_adjlist_t; int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode); int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al); int igraph_adjlist_init_complementer(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode, igraph_bool_t loops); void igraph_adjlist_destroy(igraph_adjlist_t *al); void igraph_adjlist_clear(igraph_adjlist_t *al); void igraph_adjlist_sort(igraph_adjlist_t *al); int igraph_adjlist_simplify(igraph_adjlist_t *al); int igraph_adjlist_remove_duplicate(const igraph_t *graph, igraph_adjlist_t *al); int igraph_adjlist_print(const igraph_adjlist_t *al); int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile); /* igraph_vector_int_t *igraph_adjlist_get(const igraph_adjlist_t *al, */ /* igraph_integer_t no); */ /** * \define igraph_adjlist_get * Query a vector in an adjlist * * Returns a pointer to an igraph_vector_int_t object from an * adjacency list. The vector can be modified as desired. * \param al The adjacency list object. * \param no The vertex of which the vertex of adjacent vertices are * returned. * \return Pointer to the igraph_vector_int_t object. * * Time complexity: O(1). */ #define igraph_adjlist_get(al,no) (&(al)->adjs[(long int)(no)]) int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, igraph_neimode_t mode, igraph_bool_t duplicate); typedef struct igraph_inclist_t { igraph_integer_t length; igraph_vector_t *incs; } igraph_inclist_t; int igraph_inclist_init(const igraph_t *graph, igraph_inclist_t *il, igraph_neimode_t mode); int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); void igraph_inclist_destroy(igraph_inclist_t *il); void igraph_inclist_clear(igraph_inclist_t *il); int igraph_inclist_remove_duplicate(const igraph_t *graph, igraph_inclist_t *il); int igraph_inclist_print(const igraph_inclist_t *il); int igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfile); /** * \define igraph_inclist_get * Query a vector in an incidence list * * Returns a pointer to an igraph_vector_t object from an * incidence list containing edge ids. The vector can be modified, * resized, etc. as desired. * \param graph il The incidence list. * \param no The vertex for which the incident edges are returned. * \return Pointer to an igraph_vector_t object. * * Time complexity: O(1). */ #define igraph_inclist_get(il,no) (&(il)->incs[(long int)(no)]) typedef struct igraph_lazy_adjlist_t { const igraph_t *graph; igraph_integer_t length; igraph_vector_t **adjs; igraph_neimode_t mode; igraph_lazy_adlist_simplify_t simplify; } igraph_lazy_adjlist_t; int igraph_lazy_adjlist_init(const igraph_t *graph, igraph_lazy_adjlist_t *al, igraph_neimode_t mode, igraph_lazy_adlist_simplify_t simplify); void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al); void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al); /* igraph_vector_t *igraph_lazy_adjlist_get(igraph_lazy_adjlist_t *al, */ /* igraph_integer_t no); */ /** * \define igraph_lazy_adjlist_get * Query neighbor vertices * * If the function is called for the first time for a vertex then the * result is stored in the adjacency list and no further query * operations are needed when the neighbors of the same vertex are * queried again. * \param al The lazy adjacency list. * \param no The vertex id to query. * \return Pointer to a vector. It is allowed to modify it and * modification does not affect the original graph. * * Time complexity: O(d), the number of neighbor vertices for the * first time, O(1) for subsequent calls. */ #define igraph_lazy_adjlist_get(al,no) \ ((al)->adjs[(long int)(no)] != 0 ? ((al)->adjs[(long int)(no)]) : \ (igraph_lazy_adjlist_get_real(al, no))) igraph_vector_t *igraph_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, igraph_integer_t no); typedef struct igraph_lazy_inclist_t { const igraph_t *graph; igraph_integer_t length; igraph_vector_t **incs; igraph_neimode_t mode; } igraph_lazy_inclist_t; int igraph_lazy_inclist_init(const igraph_t *graph, igraph_lazy_inclist_t *il, igraph_neimode_t mode); void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il); void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il); /** * \define igraph_lazy_inclist_get * Query incident edges * * If the function is called for the first time for a vertex, then the * result is stored in the incidence list and no further query * operations are needed when the incident edges of the same vertex are * queried again. * \param al The lazy incidence list object. * \param no The vertex id to query. * \return Pointer to a vector. It is allowed to modify it and * modification does not affect the original graph. * * Time complexity: O(d), the number of incident edges for the first * time, O(1) for subsequent calls with the same \p no argument. */ #define igraph_lazy_inclist_get(al,no) \ ((al)->incs[(long int)(no)] != 0 ? ((al)->incs[(long int)(no)]) : \ (igraph_lazy_inclist_get_real(al, no))) igraph_vector_t *igraph_lazy_inclist_get_real(igraph_lazy_inclist_t *al, igraph_integer_t no); /************************************************************************* * DEPRECATED TYPES AND FUNCTIONS */ typedef igraph_inclist_t igraph_adjedgelist_t; int igraph_adjedgelist_init(const igraph_t *graph, igraph_inclist_t *il, igraph_neimode_t mode); void igraph_adjedgelist_destroy(igraph_inclist_t *il); int igraph_adjedgelist_remove_duplicate(const igraph_t *graph, igraph_inclist_t *il); int igraph_adjedgelist_print(const igraph_inclist_t *il, FILE *outfile); /** * \define igraph_adjedgelist_get * Query a vector in an incidence list * * This macro was superseded by \ref igraph_inclist_get() in igraph 0.6. * Please use \ref igraph_inclist_get() instead of this macro. * * * Deprecated in version 0.6. */ #define igraph_adjedgelist_get(ael,no) (&(ael)->incs[(long int)(no)]) typedef igraph_lazy_inclist_t igraph_lazy_adjedgelist_t; int igraph_lazy_adjedgelist_init(const igraph_t *graph, igraph_lazy_inclist_t *il, igraph_neimode_t mode); void igraph_lazy_adjedgelist_destroy(igraph_lazy_inclist_t *il); /** * \define igraph_lazy_adjedgelist_get * Query a vector in a lazy incidence list * * This macro was superseded by \ref igraph_lazy_inclist_get() in igraph 0.6. * Please use \ref igraph_lazy_inclist_get() instead of this macro. * * * Deprecated in version 0.6. */ #define igraph_lazy_adjedgelist_get(al,no) \ ((al)->incs[(long int)(no)] != 0 ? ((al)->incs[(long int)(no)]) : \ (igraph_lazy_adjedgelist_get_real(al, no))) igraph_vector_t *igraph_lazy_adjedgelist_get_real(igraph_lazy_inclist_t *al, igraph_integer_t no); __END_DECLS #endif igraph/src/igraph_grid.c0000644000176000001440000003446012325527073015003 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_memory.h" #include "config.h" #include /* internal function */ int igraph_2dgrid_which(igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t yc, long int *x, long int *y) { if (xc <= grid->minx) { *x=0; } else if (xc >= grid->maxx) { *x=grid->stepsx-1; } else { *x=(long int) floor((xc-(grid->minx))/(grid->deltax)); } if (yc <= grid->miny) { *y=0; } else if (yc >= grid->maxy) { *y=grid->stepsy-1; } else { *y=(long int) floor((yc-(grid->miny))/(grid->deltay)); } return 0; } int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, igraph_real_t minx, igraph_real_t maxx, igraph_real_t deltax, igraph_real_t miny, igraph_real_t maxy, igraph_real_t deltay) { long int i; grid->coords=coords; grid->minx=minx; grid->maxx=maxx; grid->deltax=deltax; grid->miny=miny; grid->maxy=maxy; grid->deltay=deltay; grid->stepsx=(long int) ceil((maxx-minx)/deltax); grid->stepsy=(long int) ceil((maxy-miny)/deltay); IGRAPH_CHECK(igraph_matrix_init(&grid->startidx, grid->stepsx, grid->stepsy)); IGRAPH_FINALLY(igraph_matrix_destroy, &grid->startidx); IGRAPH_VECTOR_INIT_FINALLY(&grid->next, igraph_matrix_nrow(coords)); IGRAPH_VECTOR_INIT_FINALLY(&grid->prev, igraph_matrix_nrow(coords)); for (i=0; inext); i++) { VECTOR(grid->next)[i]=-1; } grid->massx=0; grid->massy=0; grid->vertices=0; IGRAPH_FINALLY_CLEAN(3); return 0; } void igraph_2dgrid_destroy(igraph_2dgrid_t *grid) { igraph_matrix_destroy(&grid->startidx); igraph_vector_destroy(&grid->next); igraph_vector_destroy(&grid->prev); } void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem, igraph_real_t xc, igraph_real_t yc) { long int x, y; long int first; MATRIX(*grid->coords, elem, 0)=xc; MATRIX(*grid->coords, elem, 1)=yc; /* add to cell */ igraph_2dgrid_which(grid, xc, yc, &x, &y); first=(long int) MATRIX(grid->startidx, x, y); VECTOR(grid->prev)[elem]=0; VECTOR(grid->next)[elem]=first; if (first != 0) { VECTOR(grid->prev)[first-1]=elem+1; } MATRIX(grid->startidx, x, y)=elem+1; grid->massx += xc; grid->massy += yc; grid->vertices += 1; } void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem) { long int x, y; long int first; igraph_real_t xc, yc; xc=MATRIX(*grid->coords, elem, 0); yc=MATRIX(*grid->coords, elem, 1); /* add to cell */ igraph_2dgrid_which(grid, xc, yc, &x, &y); first=(long int) MATRIX(grid->startidx, x, y); VECTOR(grid->prev)[elem]=0; VECTOR(grid->next)[elem]=first; if (first != 0) { VECTOR(grid->prev)[first-1]=elem+1; } MATRIX(grid->startidx, x, y)=elem+1; grid->massx += xc; grid->massy += yc; grid->vertices += 1; } void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem, igraph_real_t xc, igraph_real_t yc) { long int oldx, oldy; long int newx, newy; igraph_real_t oldxc=MATRIX(*grid->coords, elem, 0); igraph_real_t oldyc=MATRIX(*grid->coords, elem, 1); long int first; xc=oldxc+xc; yc=oldyc+yc; igraph_2dgrid_which(grid, oldxc, oldyc, &oldx, &oldy); igraph_2dgrid_which(grid, xc, yc, &newx, &newy); if (oldx != newx || oldy != newy) { /* remove from this cell */ if (VECTOR(grid->prev)[elem] != 0) { VECTOR(grid->next) [ (long int) VECTOR(grid->prev)[elem]-1 ] = VECTOR(grid->next)[elem]; } else { MATRIX(grid->startidx, oldx, oldy)=VECTOR(grid->next)[elem]; } if (VECTOR(grid->next)[elem] != 0) { VECTOR(grid->prev)[ (long int) VECTOR(grid->next)[elem]-1 ] = VECTOR(grid->prev)[elem]; } /* add to this cell */ first=(long int) MATRIX(grid->startidx, newx, newy); VECTOR(grid->prev)[elem]=0; VECTOR(grid->next)[elem]=first; if (first != 0) { VECTOR(grid->prev)[first-1]=elem+1; } MATRIX(grid->startidx, newx, newy)=elem+1; } grid->massx += -oldxc+xc; grid->massy += -oldyc+yc; MATRIX(*grid->coords, elem, 0)=xc; MATRIX(*grid->coords, elem, 1)=yc; } void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid, igraph_real_t *massx, igraph_real_t *massy) { *massx = (grid->massx)/(grid->vertices); *massy = (grid->massy)/(grid->vertices); } igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, long int elem) { return VECTOR(grid->next)[elem] != -1; } igraph_real_t igraph_2dgrid_dist(const igraph_2dgrid_t *grid, long int e1, long int e2) { igraph_real_t x=MATRIX(*grid->coords, e1, 0)-MATRIX(*grid->coords, e2, 0); igraph_real_t y=MATRIX(*grid->coords, e1, 1)-MATRIX(*grid->coords, e2, 1); return sqrt(x*x + y*y); } igraph_real_t igraph_2dgrid_dist2(const igraph_2dgrid_t *grid, long int e1, long int e2) { igraph_real_t x=MATRIX(*grid->coords, e1, 0)-MATRIX(*grid->coords, e2, 0); igraph_real_t y=MATRIX(*grid->coords, e1, 1)-MATRIX(*grid->coords, e2, 1); return x*x + y*y; } int igraph_i_2dgrid_addvertices(igraph_2dgrid_t *grid, igraph_vector_t *eids, igraph_integer_t vid, igraph_real_t r, long int x, long int y) { long int act; igraph_real_t *v=VECTOR(grid->next); r=r*r; act=(long int) MATRIX(grid->startidx, x, y); while (act != 0) { if (igraph_2dgrid_dist2(grid, vid, act-1) < r) { IGRAPH_CHECK(igraph_vector_push_back(eids, act-1)); } act=(long int) v[act-1]; } return 0; } int igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_t *eids, igraph_integer_t vid, igraph_real_t r) { igraph_real_t xc=MATRIX(*grid->coords, (long int)vid, 0); igraph_real_t yc=MATRIX(*grid->coords, (long int)vid, 1); long int x, y; igraph_vector_clear(eids); igraph_2dgrid_which(grid, xc, yc, &x, &y); /* this cell */ igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y); /* left */ if (x!=0) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x-1, y); } /* right */ if (x!=grid->stepsx-1) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x+1, y); } /* up */ if (y!=0) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y-1); } /* down */ if (y!=grid->stepsy-1) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y+1); } /* up & left */ if (x != 0 && y != 0) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x-1, y-1); } /* up & right */ if (x != grid->stepsx-1 && y != 0) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x+1, y-1); } /* down & left */ if (x != 0 && y != grid->stepsy-1) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x-1, y+1); } /* down & right */ if (x != grid->stepsx-1 && y != grid->stepsy-1) { igraph_i_2dgrid_addvertices(grid, eids, vid, r, x-1, y+1); } return 0; } void igraph_2dgrid_reset(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it) { /* Search for the first cell containing a vertex */ it->x=0; it->y=0; it->vid=(long int) MATRIX(grid->startidx, 0, 0); while ( it->vid==0 && (it->x < grid->stepsx-1 || it->ystepsy-1)) { it->x += 1; if (it->x == grid->stepsx) { it->x=0; it->y += 1; } it->vid=(long int) MATRIX(grid->startidx, it->x, it->y); } } igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it) { long int ret=it->vid; if (ret==0) { return 0; } /* First neighbor */ it->ncells=-1; if (it->x != grid->stepsx-1) { it->ncells += 1; it->nx[it->ncells]=it->x+1; it->ny[it->ncells]=it->y; } if (it->y != grid->stepsy-1) { it->ncells += 1; it->nx[it->ncells]=it->x; it->ny[it->ncells]=it->y+1; } if (it->ncells==1) { it->ncells += 1; it->nx[it->ncells]=it->x+1; it->ny[it->ncells]=it->y+1; } it->ncells+=1; it->nx[it->ncells]=it->x; it->ny[it->ncells]=it->y; it->nei=(long int) VECTOR(grid->next) [ ret-1 ]; while (it->ncells > 0 && it->nei==0 ) { it->ncells -= 1; it->nei=(long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); } /* Next vertex */ it->vid=(long int) VECTOR(grid->next)[ it->vid-1 ]; while ( (it->x < grid->stepsx-1 || it->ystepsy-1) && it->vid == 0) { it->x += 1; if (it->x == grid->stepsx) { it->x=0; it->y += 1; } it->vid=(long int) MATRIX(grid->startidx, it->x, it->y); } return (igraph_integer_t) ret; } igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it) { long int ret=it->nei; if (it->nei != 0) { it->nei=(long int) VECTOR(grid->next) [ ret-1 ]; } while (it->ncells > 0 && it->nei==0 ) { it->ncells -= 1; it->nei=(long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); } return (igraph_integer_t) ret; } /*-----------------------------------------------------------------------*/ int igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, igraph_real_t xc, igraph_real_t yc, long int *x, long int *y) { if (xc <= grid->minx) { *x=0; } else if (xc >= grid->maxx) { *x=grid->stepsx-1; } else { *x=(long int) floor((xc-(grid->minx))/(grid->deltax)); } if (yc <= grid->miny) { *y=0; } else if (yc >= grid->maxy) { *y=grid->stepsy-1; } else { *y=(long int) floor((yc-(grid->miny))/(grid->deltay)); } return 0; } int igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, igraph_real_t minx, igraph_real_t maxx, long int stepsx, igraph_real_t miny, igraph_real_t maxy, long int stepsy) { grid->minx=minx; grid->maxx=maxx; grid->stepsx=stepsx; grid->deltax=(maxx-minx)/stepsx; grid->miny=miny; grid->maxy=maxy; grid->stepsy=stepsy; grid->deltay=(maxy-miny)/stepsy; grid->data=igraph_Calloc(stepsx*stepsy, long int); if (grid->data==0) { IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM); } return 0; } void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid) { igraph_Free(grid->data); } #define MAT(i,j) (grid->data[(grid->stepsy)*(j)+(i)]) #define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2))) int igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, igraph_real_t x, igraph_real_t y, igraph_real_t r, long int id) { long int cx, cy; long int i, j; igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); MAT(cx, cy)=id+1; #define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \ grid->miny+(cy+(j))*grid->deltay)) for (i=0; cx+istepsx && DIST(i,0)stepsy && DIST(i,j)minx+(cx+(i))*grid->deltax, \ grid->miny+(cy-(j)+1)*grid->deltay)) for (i=0; cx+istepsx && DIST(i,0)0 && DIST(i,j)minx+(cx-(i)+1)*grid->deltax, \ grid->miny+(cy+(j))*grid->deltay)) for (i=1; cx-i>0 && DIST(i,0)stepsy && DIST(i,j)minx+(cx-(i)+1)*grid->deltax, \ grid->miny+(cy-(j)+1)*grid->deltay)) for (i=1; cx-i>0 && DIST(i,0)0 && DIST(i,j)minx || x >= grid->maxx || y <= grid->miny || y >= grid->maxy) { res=-1; } else { igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); res=MAT(cx, cy)-1; } return res; } #define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2))) long int igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *grid, igraph_real_t x, igraph_real_t y, igraph_real_t r) { long int cx, cy; long int i,j; long int ret; if (x-r <= grid->minx || x+r >= grid->maxx || y-r <= grid->miny || y+r >= grid->maxy) { ret=-1; } else { igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); ret=MAT(cx, cy)-1; #define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \ grid->miny+(cy+(j))*grid->deltay)) for (i=0; ret<0 && cx+istepsx && DIST(i,0)stepsy && DIST(i,j)minx+(cx+(i))*grid->deltax, \ grid->miny+(cy-(j)+1)*grid->deltay)) for (i=0; ret<0 && cx+istepsx && DIST(i,0)0 && DIST(i,j)minx+(cx-(i)+1)*grid->deltax, \ grid->miny+(cy+(j))*grid->deltay)) for (i=1; ret<0 && cx-i>0 && DIST(i,0)stepsy && DIST(i,j)minx+(cx-(i)+1)*grid->deltax, \ grid->miny+(cy-(j)+1)*grid->deltay)) for (i=1; ret<0 && cx+i>0 && DIST(i,0)0 && DIST(i,j)stepsx; i++) { */ /* for (j=0; jstepsy; j++) { */ /* printf("%li ", MAT(i,j)-1); */ /* } */ /* printf("\n"); */ /* } */ /* } */ igraph/src/cs_dupl.c0000644000176000001440000000437312325527073014155 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* remove duplicate entries from A */ CS_INT cs_dupl (cs *A) { CS_INT i, j, p, q, nz = 0, n, m, *Ap, *Ai, *w ; CS_ENTRY *Ax ; if (!CS_CSC (A)) return (0) ; /* check inputs */ m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; w = cs_malloc (m, sizeof (CS_INT)) ; /* get workspace */ if (!w) return (0) ; /* out of memory */ for (i = 0 ; i < m ; i++) w [i] = -1 ; /* row i not yet seen */ for (j = 0 ; j < n ; j++) { q = nz ; /* column j will start at q */ for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; /* A(i,j) is nonzero */ if (w [i] >= q) { Ax [w [i]] += Ax [p] ; /* A(i,j) is a duplicate */ } else { w [i] = nz ; /* record where row i occurs */ Ai [nz] = i ; /* keep A(i,j) */ Ax [nz++] = Ax [p] ; } } Ap [j] = q ; /* record start of column j */ } Ap [n] = nz ; /* finalize A */ cs_free (w) ; /* free workspace */ return (cs_sprealloc (A, 0)) ; /* remove extra space from A */ } igraph/src/infomap.cc0000644000176000001440000002415112325527073014314 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ---- The original version of this file was written by Martin Rosvall email: martin.rosvall@physics.umu.se homePage: http://www.tp.umu.se/~rosvall/ It was integrated in igraph by Emmanuel Navarro email: navarro@irit.fr homePage: http://www.irit.fr/~Emmanuel.Navarro/ */ #include #include "igraph_interface.h" #include "igraph_community.h" #include "igraph_interrupt_internal.h" #include "infomap_Node.h" #include "infomap_Greedy.h" /****************************************************************************/ int infomap_partition(FlowGraph * fgraph, bool rcall) { Greedy * greedy; // save the original graph FlowGraph * cpy_fgraph = new FlowGraph(fgraph); IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph); int Nnode = cpy_fgraph->Nnode; // "real" number of vertex, ie. number of vertex of the graph int iteration = 0; double outer_oldCodeLength, newCodeLength; int *initial_move = NULL; bool initial_move_done = true; do { // Main loop outer_oldCodeLength = fgraph->codeLength; if (iteration > 0) { /**********************************************************************/ // FIRST PART: re-split the network (if need) // =========================================== // intial_move indicate current clustering initial_move = new int[Nnode]; // new_cluster_id --> old_cluster_id (save curent clustering state) IGRAPH_FINALLY(operator delete [], initial_move); initial_move_done = false; int *subMoveTo = NULL; // enventual new partitionment of original graph if ((iteration % 2 == 0) && (fgraph->Nnode > 1)) { // 0/ Submodule movements : partition each module of the // current partition (rec. call) subMoveTo = new int[Nnode]; // vid_cpy_fgraph --> new_cluster_id (new partition) IGRAPH_FINALLY(operator delete [], subMoveTo); int subModIndex = 0; for (int i=0 ; i < fgraph->Nnode ; i++) { // partition each non trivial module int sub_Nnode = fgraph->node[i]->members.size(); if (sub_Nnode > 1) { // If the module is not trivial int *sub_members = new int[sub_Nnode]; // id_sub --> id IGRAPH_FINALLY(operator delete [], sub_members); for (int j=0 ; j < sub_Nnode ; j++) sub_members[j] = fgraph->node[i]->members[j]; // extraction of the subgraph FlowGraph *sub_fgraph = new FlowGraph(cpy_fgraph, sub_Nnode, sub_members); IGRAPH_FINALLY(delete_FlowGraph, sub_fgraph); sub_fgraph->initiate(); // recursif call of partitionment on the subgraph infomap_partition(sub_fgraph, true); // Record membership changes for (int j=0; j < sub_fgraph->Nnode; j++) { int Nmembers = sub_fgraph->node[j]->members.size(); for (int k=0; knode[j]->members[k]]] = subModIndex; } initial_move[subModIndex] = i; subModIndex++; } delete sub_fgraph; IGRAPH_FINALLY_CLEAN(1); delete [] sub_members; IGRAPH_FINALLY_CLEAN(1); } else{ subMoveTo[fgraph->node[i]->members[0]] = subModIndex; initial_move[subModIndex] = i; subModIndex++; } } } else { // 1/ Single-node movements : allows each node to move (again) // save current modules for (int i=0; i < fgraph->Nnode; i++) { // for each module int Nmembers = fgraph->node[i]->members.size(); // Module size for (int j=0;jnode[i]->members[j]] = i; } } } fgraph->back_to(cpy_fgraph); if (subMoveTo) { Greedy *cpy_greedy = new Greedy(fgraph); IGRAPH_FINALLY(delete_Greedy, cpy_greedy); cpy_greedy->setMove(subMoveTo); cpy_greedy->apply(false); delete_Greedy(cpy_greedy); IGRAPH_FINALLY_CLEAN(1); delete [] subMoveTo; IGRAPH_FINALLY_CLEAN(1); } } /**********************************************************************/ // SECOND PART: greedy optimizing it self // =========================================== double oldCodeLength; do { // greedy optimizing object creation greedy = new Greedy(fgraph); IGRAPH_FINALLY(delete_Greedy, greedy); // Initial move to apply ? if (!initial_move_done && initial_move) { initial_move_done = true; greedy->setMove(initial_move); } oldCodeLength = greedy->codeLength; bool moved = true; int Nloops = 0; //int count = 0; double inner_oldCodeLength = 1000; while (moved) { // main greedy optimizing loop inner_oldCodeLength = greedy->codeLength; moved = greedy->optimize(); Nloops++; //count++; if (fabs(greedy->codeLength - inner_oldCodeLength) < 1.0e-10) // if the move does'n reduce the codelenght -> exit ! moved = false; //if (count == 10) { // greedy->tune(); // count = 0; //} } // transform the network to network of modules: greedy->apply(true); newCodeLength = greedy->codeLength; // destroy greedy object delete greedy; IGRAPH_FINALLY_CLEAN(1); } while (oldCodeLength - newCodeLength > 1.0e-10); // while there is some improvement if (iteration > 0) { delete [] initial_move; IGRAPH_FINALLY_CLEAN(1); } iteration++; if (!rcall) IGRAPH_ALLOW_INTERRUPTION(); } while (outer_oldCodeLength - newCodeLength > 1.0e-10); delete cpy_fgraph; IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** * \function igraph_community_infomap * \brief Find community structure that minimizes the expected * description length of a random walker trajectory. * * Implementation of the InfoMap community detection algorithm.of * Martin Rosvall and Carl T. Bergstrom. * * See : * Visualization of the math and the map generator: www.mapequation.org * [2] The original paper: M. Rosvall and C. T. Bergstrom, Maps of * information flow reveal community structure in complex networks, PNAS * 105, 1118 (2008) [http://dx.doi.org/10.1073/pnas.0706851105 , * http://arxiv.org/abs/0707.0609 ] * [3] A more detailed paper: M. Rosvall, D. Axelsson, and C. T. Bergstrom, * The map equation, Eur. Phys. J. Special Topics 178, 13 (2009). * [http://dx.doi.org/10.1140/epjst/e2010-01179-1 , * http://arxiv.org/abs/0906.1405 ] * * The original C++ implementation of Martin Rosvall is used, * see http://www.tp.umu.se/~rosvall/downloads/infomap_undir.tgz . * Intergation in igraph has be done by Emmanuel Navarro (who is grateful to * Martin Rosvall and Carl T. Bergstrom for providing this source code.) * * * Note that the graph must not contain isolated vertices. * * * If you want to specify a random seed (as in original * implementation) you can use \ref igraph_rng_seed(). * * \param graph The input graph. * \param e_weights Numeric vector giving the weights of the edges. * If it is a NULL pointer then all edges will have equal * weights. The weights are expected to be positive. * \param v_weights Numeric vector giving the weights of the vertices. * If it is a NULL pointer then all vertices will have equal * weights. The weights are expected to be positive. * \param nb_trials The number of attempts to partition the network * (can be any integer value equal or larger than 1). * \param membership Pointer to a vector. The membership vector is * stored here. * \param codelength Pointer to a real. If not NULL the code length of the * partition is stored here. * \return Error code. * * \sa \ref igraph_community_spinglass(), \ref * igraph_community_edge_betweenness(), \ref igraph_community_walktrap(). * * Time complexity: TODO. */ int igraph_community_infomap(const igraph_t * graph, const igraph_vector_t *e_weights, const igraph_vector_t *v_weights, int nb_trials, igraph_vector_t *membership, igraph_real_t *codelength) { FlowGraph * fgraph = new FlowGraph(graph, e_weights, v_weights); IGRAPH_FINALLY(delete_FlowGraph, fgraph); // compute stationary distribution fgraph->initiate(); FlowGraph * cpy_fgraph ; double shortestCodeLength = 1000.0; // create membership vector int Nnode = fgraph->Nnode; IGRAPH_CHECK(igraph_vector_resize(membership, Nnode)); for (int trial = 0; trial < nb_trials; trial++) { cpy_fgraph = new FlowGraph(fgraph); IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph); //partition the network IGRAPH_CHECK(infomap_partition(cpy_fgraph, false)); // if better than the better... if (cpy_fgraph->codeLength < shortestCodeLength) { shortestCodeLength = cpy_fgraph->codeLength; // ... store the partition for (int i=0 ; i < cpy_fgraph->Nnode ; i++) { int Nmembers = cpy_fgraph->node[i]->members.size(); for (int k=0; k < Nmembers; k++) { //cluster[ cpy_fgraph->node[i]->members[k] ] = i; VECTOR(*membership)[cpy_fgraph->node[i]->members[k]] = i; } } } delete_FlowGraph(cpy_fgraph); IGRAPH_FINALLY_CLEAN(1); } *codelength = (igraph_real_t) shortestCodeLength/log(2.0); delete fgraph; IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } igraph/src/cohesive_blocks.c0000644000176000001440000004405712325527072015670 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_cohesive_blocks.h" #include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_flow.h" #include "igraph_separators.h" #include "igraph_structural.h" #include "igraph_components.h" #include "igraph_dqueue.h" #include "igraph_constructors.h" #include "igraph_interrupt_internal.h" #include "igraph_statusbar.h" void igraph_i_cohesive_blocks_free(igraph_vector_ptr_t *ptr) { long int i, n=igraph_vector_ptr_size(ptr); for (i=0; ik. Thus a hiearchy of vertex subsets * is found, whith the entire graph G at its root. See the following * reference for details: J. Moody and D. R. White. Structural * cohesion and embeddedness: A hierarchical concept of social * groups. American Sociological Review, 68(1):103--127, Feb 2003. * * This function implements cohesive blocking and * calculates the complete cohesive block hierarchy of a graph. * * \param graph The input graph. It must be undirected and simple. See * \ref igraph_is_simple(). * \param blocks If not a null pointer, then it must be an initialized * vector of pointers and the cohesive blocks are stored here. * Each block is encoded with a numeric vector, that contains the * vertex ids of the block. * \param cohesion If not a null pointer, then it must be an initialized * vector and the cohesion of the blocks is stored here, in the same * order as the blocks in the \p blocks pointer vector. * \param parent If not a null pointer, then it must be an initialized * vector and the block hierarchy is stored here. For each block, the * id (i.e. the position in the \p blocks pointer vector) of its * parent block is stored. For the top block in the hierarchy, * -1 is stored. * \param block_tree If not a null pointer, then it must be a pointer * to an uninitialized graph, and the block hierarchy is stored * here as an igraph graph. The vertex ids correspond to the order * of the blocks in the \p blocks vector. * \return Error code. * * Time complexity: TODO. * * \example examples/simple/cohesive_blocks.c */ int igraph_cohesive_blocks(const igraph_t *graph, igraph_vector_ptr_t *blocks, igraph_vector_t *cohesion, igraph_vector_t *parent, igraph_t *block_tree) { /* Some implementation comments. Everything is relatively straightforward, except, that we need to follow the vertex ids of the various subgraphs, without having to store two-way mappings at each level. The subgraphs can overlap, this complicates things a bit. The 'Q' vector is used as a double ended queue and it contains the subgraphs to work on in the future. Some other vectors are associated with it. 'Qparent' gives the parent graph of a graph in Q. Qmapping gives the mapping of the vertices from the graph to the parent graph. Qcohesion is the vertex connectivity of the graph. Qptr is an integer and points to the next graph to work on. */ igraph_vector_ptr_t Q; igraph_vector_ptr_t Qmapping; igraph_vector_long_t Qparent; igraph_vector_long_t Qcohesion; igraph_vector_bool_t Qcheck; long int Qptr=0; igraph_integer_t conn; igraph_t *graph_copy; igraph_vector_ptr_t separators; igraph_vector_t compvertices; igraph_vector_long_t components; igraph_vector_bool_t marked; igraph_vector_long_t compid; igraph_dqueue_t bfsQ; igraph_vector_t neis; if (igraph_is_directed(graph)) { IGRAPH_ERROR("Cohesive blocking only works on undirected graphs", IGRAPH_EINVAL); } IGRAPH_STATUS("Starting cohesive block calculation.\n", 0); if (blocks) { igraph_vector_ptr_clear(blocks); } if (cohesion) { igraph_vector_clear(cohesion); } if (parent) { igraph_vector_clear(parent); } IGRAPH_CHECK(igraph_vector_ptr_init(&Q, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &Q); IGRAPH_FINALLY(igraph_i_cohesive_blocks_free, &Q); IGRAPH_CHECK(igraph_vector_ptr_init(&Qmapping, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &Qmapping); IGRAPH_FINALLY(igraph_i_cohesive_blocks_free2, &Qmapping); IGRAPH_CHECK(igraph_vector_long_init(&Qparent, 1)); IGRAPH_FINALLY(igraph_vector_long_destroy, &Qparent); IGRAPH_CHECK(igraph_vector_long_init(&Qcohesion, 1)); IGRAPH_FINALLY(igraph_vector_long_destroy, &Qcohesion); IGRAPH_CHECK(igraph_vector_bool_init(&Qcheck, 1)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &Qcheck); IGRAPH_CHECK(igraph_vector_ptr_init(&separators, 0)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &separators); IGRAPH_VECTOR_INIT_FINALLY(&compvertices, 0); IGRAPH_CHECK(igraph_vector_bool_init(&marked, 0)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &marked); IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_dqueue_init(&bfsQ, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &bfsQ); IGRAPH_CHECK(igraph_vector_long_init(&compid, 0)); IGRAPH_FINALLY(igraph_vector_long_destroy, &compid); IGRAPH_CHECK(igraph_vector_long_init(&components, 0)); IGRAPH_FINALLY(igraph_vector_long_destroy, &components); /* Put the input graph in the queue */ graph_copy=igraph_Calloc(1, igraph_t); if (!graph_copy) { IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_copy(graph_copy, graph)); VECTOR(Q)[0] = graph_copy; VECTOR(Qmapping)[0] = 0; /* Identity mapping */ VECTOR(Qparent)[0] = -1; /* Has no parent */ IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /*checks=*/ 1)); VECTOR(Qcohesion)[0] = conn; VECTOR(Qcheck)[0] = 0; /* Then work until the queue is empty */ while (Qptr < igraph_vector_ptr_size(&Q)) { igraph_t *mygraph=VECTOR(Q)[Qptr]; igraph_bool_t mycheck=VECTOR(Qcheck)[Qptr]; long int mynodes=igraph_vcount(mygraph); long int i, nsep; long int no, kept=0; long int cptr=0; long int nsepv=0; igraph_bool_t addedsep=0; IGRAPH_STATUSF(("Candidate %li: %li vertices,", 0, Qptr, mynodes)); IGRAPH_ALLOW_INTERRUPTION(); /* Get the separators */ IGRAPH_CHECK(igraph_minimum_size_separators(mygraph, &separators)); IGRAPH_FINALLY(igraph_i_cohesive_blocks_free3, &separators); nsep=igraph_vector_ptr_size(&separators); IGRAPH_STATUSF((" %li separators,", 0, nsep)); /* Remove them from the graph, also mark them */ IGRAPH_CHECK(igraph_vector_bool_resize(&marked, mynodes)); igraph_vector_bool_null(&marked); for (i=0; i VECTOR(Qcohesion)[Qptr]) { igraph_integer_t newconn; kept++; IGRAPH_CHECK(igraph_vector_ptr_push_back(&Q, newgraph)); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_vector_ptr_push_back(&Qmapping, newmapping)); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_vertex_connectivity(newgraph, &newconn, /*checks=*/ 1)); IGRAPH_CHECK(igraph_vector_long_push_back(&Qcohesion, newconn)); IGRAPH_CHECK(igraph_vector_long_push_back(&Qparent, Qptr)); IGRAPH_CHECK(igraph_vector_bool_push_back(&Qcheck, mycheck || addedsep)); } else { igraph_destroy(newgraph); igraph_free(newgraph); igraph_vector_destroy(newmapping); igraph_free(newmapping); IGRAPH_FINALLY_CLEAN(4); } } IGRAPH_STATUSF((" keeping %li.\n", 0, kept)); igraph_destroy(mygraph); igraph_free(mygraph); VECTOR(Q)[Qptr] = 0; igraph_i_cohesive_blocks_free3(&separators); IGRAPH_FINALLY_CLEAN(1); Qptr++; } igraph_vector_long_destroy(&components); igraph_vector_long_destroy(&compid); igraph_dqueue_destroy(&bfsQ); igraph_vector_destroy(&neis); igraph_vector_bool_destroy(&marked); igraph_vector_destroy(&compvertices); igraph_vector_ptr_destroy(&separators); IGRAPH_FINALLY_CLEAN(7); if (blocks || cohesion || parent || block_tree) { igraph_integer_t noblocks=(igraph_integer_t) Qptr, badblocks=0; igraph_vector_bool_t removed; long int i, resptr=0; igraph_vector_long_t rewritemap; IGRAPH_CHECK(igraph_vector_bool_init(&removed, noblocks)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed); IGRAPH_CHECK(igraph_vector_long_init(&rewritemap, noblocks)); IGRAPH_FINALLY(igraph_vector_long_destroy, &rewritemap); for (i=1; i= VECTOR(Qcohesion)[i]) { VECTOR(removed)[i]=1; badblocks++; } } /* Rewrite the mappings */ for (i=1; i= ic) { badblocks++; VECTOR(removed)[i]=1; break; } } } noblocks -= badblocks; if (blocks) { IGRAPH_CHECK(igraph_vector_ptr_resize(blocks, noblocks)); } if (cohesion) { IGRAPH_CHECK(igraph_vector_resize(cohesion, noblocks)); } if (parent) { IGRAPH_CHECK(igraph_vector_resize(parent, noblocks)); } for (i=0; i=0 && VECTOR(removed)[p]) { p=VECTOR(Qparent)[p]; } if (p>=0) { p=VECTOR(rewritemap)[p]; } VECTOR(Qparent)[i]=p; if (parent) { VECTOR(*parent)[resptr]=p; } } if (blocks) { VECTOR(*blocks)[resptr]=VECTOR(Qmapping)[i]; VECTOR(Qmapping)[i]=0; } resptr++; } /* Plus the original graph */ if (blocks) { igraph_vector_t *orig=igraph_Calloc(1, igraph_vector_t); if (!orig) { IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, orig); IGRAPH_CHECK(igraph_vector_init_seq(orig, 0, igraph_vcount(graph)-1)); VECTOR(*blocks)[0]=orig; IGRAPH_FINALLY_CLEAN(1); } if (block_tree) { igraph_vector_t edges; long int eptr=0; IGRAPH_VECTOR_INIT_FINALLY(&edges, noblocks*2-2); for (i=1; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Jörg Reichardt This file was modified by Vincent Traag The original copyright notice follows here */ /*************************************************************************** pottsmodel.cpp - description ------------------- begin : Fri May 28 2004 copyright : (C) 2004 by email : ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include "pottsmodel_2.h" #include "NetRoutines.h" using namespace std; #include "igraph_random.h" #include "igraph_interrupt_internal.h" #include "config.h" //################################################################################################# PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : acceptance(0) { DLList_Iter iter; NNode *n_cur; unsigned int *i_ptr; net=n; q=qvalue; operation_mode=m; k_max=0; //needed in calculating modularity Qa =new double[q+1]; //weights for each spin state needed in Monte Carlo process weights=new double[q+1]; //bookkeeping of occupation numbers of spin states or the number of links in community color_field=new double[q+1]; neighbours=new double[q+1]; num_of_nodes=net->node_list->Size(); num_of_links=net->link_list->Size(); n_cur=iter.First(net->node_list); //these lists are needed to keep track of spin states for parallel update mode new_spins=new DL_Indexed_List(); previous_spins=new DL_Indexed_List(); while (!iter.End()) { if (k_maxGet_Degree()) k_max=n_cur->Get_Degree(); i_ptr=new unsigned int; *i_ptr=0; new_spins->Push(i_ptr); i_ptr=new unsigned int; *i_ptr=0; previous_spins->Push(i_ptr); n_cur=iter.Next(); } return; } //####################################################### //Destructor of PottsModel //######################################################## PottsModel::~PottsModel() { /* The DLItem destructor does not delete its item currently, because of some bad design. As a workaround, we delete them here by hand */ new_spins->delete_items(); previous_spins->delete_items(); delete new_spins; delete previous_spins; delete [] Qa; delete [] weights; delete [] color_field; delete [] neighbours; return; } //##################################################### //Assing an initial random configuration of spins to nodes //if called with negative argument or the spin used as argument //when called with positve one. //This may be handy, if you want to warm up the network. //#################################################### unsigned long PottsModel::assign_initial_conf(int spin) { int s; DLList_Iter iter; DLList_Iter l_iter; NNode *n_cur; NLink *l_cur; double sum_weight; double av_k_squared=0.0; double av_k=0.0; // printf("Assigning initial configuration...\n"); // initialize colorfield for (unsigned int i=0; i<=q; i++) color_field[i]=0.0; // total_degree_sum=0.0; n_cur=iter.First(net->node_list); while (!iter.End()) { if (spin<0) s=RNG_INTEGER(1,q); else s=spin; n_cur->Set_ClusterIndex(s); l_cur=l_iter.First(n_cur->Get_Links()); sum_weight=0; while (!l_iter.End()) { sum_weight+=l_cur->Get_Weight(); //weight should be one, in case we are not using it. l_cur=l_iter.Next(); } // we set the sum of the weights or the degree as the weight of the node, this way // we do not have to calculate it again. n_cur->Set_Weight(sum_weight); av_k_squared+=sum_weight*sum_weight; av_k+=sum_weight; // in case we want all links to be contribute equally - parameter gamm=fixed if (operation_mode==0) { color_field[s]++; } else { color_field[s]+=sum_weight; } // or in case we want to use a weight of each link that is proportional to k_i\times k_j total_degree_sum+=sum_weight; n_cur=iter.Next(); } av_k_squared/=double(net->node_list->Size()); av_k/=double(net->node_list->Size()); // total_degree_sum-=av_k_squared/av_k; // printf("Total Degree Sum=2M=%f\n",total_degree_sum); return net->node_list->Size(); } //##################################################################### //If I ever manage to write a decent LookUp function, it will be here //##################################################################### unsigned long PottsModel::initialize_lookup(double kT, double gamma) { IGRAPH_UNUSED(kT); IGRAPH_UNUSED(gamma); /* double beta; // the look-up table contains all entries of exp(-beta(-neighbours+gamma*h)) // as needed in the HeatBath algorithm beta=1.0/kT; for (long w=0; w<=k_max+num_of_nodes; w++) { neg_lookup[w]=exp(-beta*-w } delta_ij[0]=1.0; for (long w=-num_of_nodes-k_max; w<=k_max+num_of_nodes; w++) { } // wenn wir spaeter exp(-1/kT*gamma*(nk+1-nj) fuer eine spin-flip von j nach k benoetigen schauen wir nur noch hier nach for (unsigned long n=1; n<=num_of_nodes; n++) { gamma_term[n]=exp(-double(n)/kT*gamma); } gamma_term[0]=1.0; */ return 1; } //##################################################################### // Q denotes the modulary of the network // This function calculates it initially // In the event of a spin changing its state, it only needs updating // Note that Qmatrix and Qa are only counting! The normalization // by num_of_links is done later //#################################################################### double PottsModel::initialize_Qmatrix(void) { DLList_Iter l_iter; NLink *l_cur; unsigned int i,j; //initialize with zeros num_of_links=net->link_list->Size(); for (i=0; i<=q; i++) { Qa[i]=0.0; for (j=i; j<=q; j++) { Qmatrix[i][j]=0.0; Qmatrix[j][i]=0.0; } } //go over all links and make corresponding entries in Q matrix //An edge connecting state i wiht state j will get an entry in Qij and Qji l_cur=l_iter.First(net->link_list); while (!l_iter.End()) { i=l_cur->Get_Start()->Get_ClusterIndex(); j=l_cur->Get_End()->Get_ClusterIndex(); //printf("%d %d\n",i,j); Qmatrix[i][j]+=l_cur->Get_Weight(); Qmatrix[j][i]+=l_cur->Get_Weight(); l_cur=l_iter.Next(); } //Finally, calculate sum over rows and keep in Qa for (i=0; i<=q; i++) { for (j=0; j<=q; j++) Qa[i]+=Qmatrix[i][j]; } return calculate_Q(); } //#################################################################### // This function does the actual calculation of Q from the matrix // The normalization by num_of_links is done here //#################################################################### double PottsModel::calculate_Q() { double Q=0.0; for (unsigned int i=0; i<=q; i++) { Q+=Qmatrix[i][i]-Qa[i]*Qa[i]/double(2.0*net->sum_weights); if ((Qa[i]<0.0) || Qmatrix[i][i]<0.0) { // printf("Negatives Qa oder Qii\n\n\n"); //printf("Press any key to continue\n\n"); //cin >> Q; } } Q/=double(2.0*net->sum_weights); return Q; } double PottsModel::calculate_genQ(double gamma) { double Q=0.0; for (unsigned int i=0; i<=q; i++) { Q+=Qmatrix[i][i]-gamma*Qa[i]*Qa[i]/double(2.0*net->sum_weights); if ((Qa[i]<0.0) || Qmatrix[i][i]<0.0) { // printf("Negatives Qa oder Qii\n\n\n"); //printf("Press any key to continue\n\n"); //cin >> Q; } } Q/=double(2.0*net->sum_weights); return Q; } //####################################################################### // This function calculates the Energy for the standard Hamiltonian // given a particular value of gamma and the current spin states // ##################################################################### double PottsModel::calculate_energy(double gamma) { double e=0.0; DLList_Iter l_iter; NLink *l_cur; l_cur=l_iter.First(net->link_list); //every in-cluster edge contributes -1 while (!l_iter.End()) { if (l_cur->Get_Start()->Get_ClusterIndex()==l_cur->Get_End()->Get_ClusterIndex()) e--;; l_cur=l_iter.Next(); } //and the penalty term contributes according to cluster sizes for (unsigned int i=1; i<=q; i++) { e+=gamma*0.5*double(color_field[i])*double((color_field[i]-1)); } energy=e; return e; } //########################################################################## // We would like to start from a temperature with at least 95 of all proposed // spin changes accepted in 50 sweeps over the network // The function returns the Temperature found //######################################################################### double PottsModel::FindStartTemp(double gamma, double prob, double ts) { double kT; kT=ts; //assing random initial condition assign_initial_conf(-1); //initialize Modularity matrix, from now on, it will be updated at every spin change initialize_Qmatrix(); // the factor 1-1/q is important, since even, at infinite temperature, // only 1-1/q of all spins do change their state, since a randomly chooses new // state is with prob. 1/q the old state. while (acceptance<(1.0-1.0/double(q))*0.95) //want 95% acceptance { kT=kT*1.1; // if I ever have a lookup table, it will need initialization for every kT //initialize_lookup(kT,k_max,net->node_list->Size()); HeatBathParallelLookup(gamma,prob, kT,50); // printf("kT=%f acceptance=%f\n", kT, acceptance); } kT*=1.1; // just to be sure... // printf("Starting with acceptance ratio: %1.6f bei kT=%2.4f\n",acceptance,kT); return kT; } //############################################################## //This function does a parallel update at zero T //Hence, it is really fast on easy problems //max sweeps is the maximum number of sweeps it should perform, //if it does not converge earlier //############################################################## long PottsModel::HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps) { DLList_Iter iter, net_iter; DLList_Iter l_iter; DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; unsigned int *SPIN, *P_SPIN, new_spin, spin_opt, old_spin, spin, sweep; // long h; // degree; unsigned long changes; double h, delta=0, deltaE, deltaEmin, w, degree; //HugeArray neighbours; bool cyclic=0; sweep=0; changes=1; while (sweepnode_list); SPIN=i_iter.First(new_spins); while (!net_iter.End()) { // How many neigbors of each type? // set them all zero for (unsigned int i=0; i<=q; i++) neighbours[i]=0; degree=node->Get_Weight(); //Loop over all links (=neighbours) l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } neighbours[n_cur->Get_ClusterIndex()]+=w; l_cur=l_iter.Next(); } //Search optimal Spin old_spin=node->Get_ClusterIndex(); //degree=node->Get_Degree(); switch (operation_mode) { case 0: { delta=1.0; break; } case 1: { //newman modularity prob=degree/total_degree_sum; delta=degree; break; } } spin_opt=old_spin; deltaEmin=0.0; for (spin=1; spin<=q; spin++) // all possible spin states { if (spin!=old_spin) { h=color_field[spin]+delta-color_field[old_spin]; deltaE=double(neighbours[old_spin]-neighbours[spin])+gamma*prob*double(h); if (deltaEnode_list); SPIN=i_iter.First(new_spins); P_SPIN=i_iter2.First(previous_spins); while (!net_iter.End()) { old_spin=node->Get_ClusterIndex(); new_spin=*SPIN; if (new_spin!=old_spin) // Do we really have a change?? { changes++; node->Set_ClusterIndex(new_spin); //this is important!! //In Parallel update, there occur cyclic attractors of size two //which then make the program run for ever if (new_spin!=*P_SPIN) cyclic=false; *P_SPIN=old_spin; color_field[old_spin]--; color_field[new_spin]++; //Qmatrix update //iteration over all neighbours l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w; Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w; Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w; Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w; Qa[old_spin]-=w; Qa[new_spin]+=w; l_cur=l_iter.Next(); } // while l_iter } node=net_iter.Next(); SPIN=i_iter.Next(); P_SPIN=i_iter2.Next(); } // while (!net_iter.End()) } // while markov // In case of a cyclic attractor, we want to interrupt if (cyclic) { // printf("Cyclic attractor!\n"); acceptance=0.0; return 0; } else { acceptance=double(changes)/double(num_of_nodes); return changes; } } //################################################################################### //The same function as before, but rather than parallel update, it pics the nodes to update //randomly //################################################################################### double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps) { DLList_Iter iter; DLList_Iter l_iter; DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; unsigned int new_spin, spin_opt, old_spin, spin, sweep; long r;// degree; unsigned long changes; double delta=0, h, deltaE, deltaEmin,w,degree; //HugeArray neighbours; sweep=0; changes=0; while (sweep(long)num_of_nodes-1)) r=RNG_INTEGER(0,num_of_nodes-1); /* r=long(double(num_of_nodes*double(rand())/double(RAND_MAX+1.0)));*/ node=net->node_list->Get(r); // Wir zaehlen, wieviele Nachbarn von jedem spin vorhanden sind // erst mal alles Null setzen for (unsigned int i=0; i<=q; i++) neighbours[i]=0; degree=node->Get_Weight(); //Loop over all links (=neighbours) l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } neighbours[n_cur->Get_ClusterIndex()]+=w; l_cur=l_iter.Next(); } //Search optimal Spin old_spin=node->Get_ClusterIndex(); //degree=node->Get_Degree(); switch (operation_mode) { case 0: { delta=1.0; break; } case 1: { //newman modularity prob=degree/total_degree_sum; delta=degree; break; } } spin_opt=old_spin; deltaEmin=0.0; for (spin=1; spin<=q; spin++) // alle moeglichen Spins { if (spin!=old_spin) { h=color_field[spin]+delta-color_field[old_spin]; deltaE=double(neighbours[old_spin]-neighbours[spin])+gamma*prob*double(h); if (deltaESet_ClusterIndex(new_spin); color_field[old_spin]-=delta; color_field[new_spin]+=delta; //Qmatrix update //iteration over all neighbours l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w; Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w; Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w; Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w; Qa[old_spin]-=w; Qa[new_spin]+=w; l_cur=l_iter.Next(); } // while l_iter } } // for n } // while markov acceptance=double(changes)/double(num_of_nodes)/double(sweep); return acceptance; } //##################################################################################### //This function performs a parallel update at Terperature T //##################################################################################### long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps) { DLList_Iter iter, net_iter; DLList_Iter l_iter; DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; unsigned int new_spin, spin_opt, old_spin; unsigned int *SPIN, *P_SPIN; unsigned int sweep; long max_q; unsigned long changes, /*degree,*/ problemcount; //HugeArray neighbours; double h, delta=0, norm, r, beta,minweight, prefac=0,w, degree; bool cyclic=0, found; unsigned long num_of_nodes; sweep=0; changes=1; num_of_nodes=net->node_list->Size(); while (sweepnode_list); SPIN=i_iter.First(new_spins); while (!net_iter.End()) { // Initialize neighbours and weights problemcount=0; for (unsigned int i=0; i<=q; i++) { neighbours[i]=0; weights[i]=0; } norm=0.0; degree=node->Get_Weight(); //Loop over all links (=neighbours) l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } neighbours[n_cur->Get_ClusterIndex()]+=w; l_cur=l_iter.Next(); } //Search optimal Spin old_spin=node->Get_ClusterIndex(); //degree=node->Get_Degree(); switch (operation_mode) { case 0: { prefac=1.0; delta=1.0; break; } case 1: { //newman modularity prefac=1.0; prob=degree/total_degree_sum; delta=degree; break; } } spin_opt=old_spin; beta=1.0/kT*prefac; minweight=0.0; weights[old_spin]=0.0; for (unsigned spin=1; spin<=q; spin++) // loop over all possible new spins { if (spin!=old_spin) // only if we have a different than old spin! { h=color_field[spin]+delta-color_field[old_spin]; weights[spin]=double(neighbours[old_spin]-neighbours[spin])+gamma*prob*double(h); if (weights[spin]node_list); SPIN=i_iter.First(new_spins); P_SPIN=i_iter2.First(previous_spins); while (!net_iter.End()) { old_spin=node->Get_ClusterIndex(); new_spin=*SPIN; if (new_spin!=old_spin) // Did we really change something?? { changes++; node->Set_ClusterIndex(new_spin); if (new_spin!=*P_SPIN) cyclic=false; *P_SPIN=old_spin; color_field[old_spin]-=delta; color_field[new_spin]+=delta; //Qmatrix update //iteration over all neighbours l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w; Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w; Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w; Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w; Qa[old_spin]-=w; Qa[new_spin]+=w; l_cur=l_iter.Next(); } // while l_iter } node=net_iter.Next(); SPIN=i_iter.Next(); P_SPIN=i_iter2.Next(); } // while (!net_iter.End()) } // while markov max_q=0; for (unsigned int i=1; i<=q; i++) if (color_field[i]>max_q) max_q=long(color_field[i]); //again, we would not like to end up in cyclic attractors if (cyclic && changes) { // printf("Cyclic attractor!\n"); acceptance=double(changes)/double(num_of_nodes); return 0; } else { acceptance=double(changes)/double(num_of_nodes); return changes; } } //############################################################## // This is the function generally used for optimisation, // as the parallel update has its flaws, due to the cyclic attractors //############################################################## double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps) { DLList_Iter iter; DLList_Iter l_iter; DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; unsigned int new_spin, spin_opt, old_spin; unsigned int sweep; long max_q, rn; unsigned long changes, /*degree,*/ problemcount; double degree,w, delta=0, h; //HugeArray neighbours; double norm, r, beta,minweight, prefac=0; bool found; long int num_of_nodes; sweep=0; changes=0; num_of_nodes=net->node_list->Size(); while (sweepnum_of_nodes-1)) rn=RNG_INTEGER(0, num_of_nodes-1); /* rn=long(double(num_of_nodes*double(rand())/double(RAND_MAX+1.0))); */ node=net->node_list->Get(rn); // initialize the neighbours and the weights problemcount=0; for (unsigned int i=0; i<=q; i++) { neighbours[i]=0.0; weights[i]=0.0; } norm=0.0; degree=node->Get_Weight(); //Loop over all links (=neighbours) l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } neighbours[n_cur->Get_ClusterIndex()]+=w; l_cur=l_iter.Next(); } //Look for optimal spin old_spin=node->Get_ClusterIndex(); //degree=node->Get_Degree(); switch (operation_mode) { case 0: { prefac=1.0; delta=1.0; break; } case 1: {//newman modularity prefac=1.0; prob=degree/total_degree_sum; delta=degree; break; } } spin_opt=old_spin; beta=1.0/kT*prefac; minweight=0.0; weights[old_spin]=0.0; for (unsigned spin=1; spin<=q; spin++) // all possible new spins { if (spin!=old_spin) // except the old one! { h=color_field[spin]-(color_field[old_spin]-delta); weights[spin]=neighbours[old_spin]-neighbours[spin]+gamma*prob*h; if (weights[spin]Set_ClusterIndex(new_spin); color_field[old_spin]-=delta; color_field[new_spin]+=delta; //Qmatrix update //iteration over all neighbours l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w; Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w; Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w; Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w; Qa[old_spin]-=w; Qa[new_spin]+=w; l_cur=l_iter.Next(); } // while l_iter } } // for n } // while markov max_q=0; for (unsigned int i=1; i<=q; i++) if (color_field[i]>max_q) max_q=long(color_field[i]+0.5); acceptance=double(changes)/double(num_of_nodes)/double(sweep); return acceptance; } //############################################################################################### //# Here we try to minimize the affinity to the rest of the network //############################################################################################### double PottsModel::FindCommunityFromStart(double gamma, double prob, char *nodename, igraph_vector_t *result, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *my_inner_links, igraph_integer_t *my_outer_links) { DLList_Iter iter, iter2; DLList_Iter l_iter; DLList* to_do; DLList* community; NNode *start_node=0, *n_cur, *neighbor, *max_aff_node, *node; NLink *l_cur; bool found=false, add=false, remove=false; double degree, delta_aff_add, delta_aff_rem, max_delta_aff, Ks=0.0, Kr=0, kis, kir, w; long community_marker=5; long to_do_marker=10; double inner_links=0, outer_links=0, aff_r, aff_s; IGRAPH_UNUSED(prob); to_do=new DLList; community=new DLList; // find the node in the network n_cur=iter.First(net->node_list); while (!found && !iter.End()) { if (0==strcmp(n_cur->Get_Name(),nodename)) { start_node=n_cur; found=true; start_node->Set_Affinity(0.0); community->Push(start_node); start_node->Set_Marker(community_marker); Ks=start_node->Get_Weight(); Kr=total_degree_sum-start_node->Get_Weight(); } n_cur=iter.Next(); } if (!found) { // printf("%s not found found. Aborting.\n",nodename); // fprintf(file,"%s not found found. Aborting.\n",nodename); delete to_do; delete community; return -1; } //############################# // initialize the to_do list and community with the neighbours of start node //############################# neighbor=iter.First(start_node->Get_Neighbours()); while (!iter.End()) { // printf("Adding node %s to comunity.\n",neighbor->Get_Name()); community->Push(neighbor); neighbor->Set_Marker(community_marker); Ks+=neighbor->Get_Weight(); Kr-=neighbor->Get_Weight(); neighbor=iter.Next(); } node=iter.First(community); while (!iter.End()) { //now add at the second neighbors to the to_do list neighbor=iter2.First(node->Get_Neighbours()); while (!iter2.End()) { if ((long)neighbor->Get_Marker()!=community_marker && (long)neighbor->Get_Marker()!=to_do_marker) { to_do->Push(neighbor); neighbor->Set_Marker(to_do_marker); // printf("Adding node %s to to_do list.\n",neighbor->Get_Name()); } neighbor=iter2.Next(); } node=iter.Next(); } //############# //repeat, as long as we are still adding nodes to the communtiy //############# add=true; remove=true; while (add || remove) { //############################# //calculate the affinity changes of all nodes for adding every node in the to_do list to the community //############################## IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ max_delta_aff=0.0; max_aff_node=NULL; add=false; node=iter.First(to_do); while (!iter.End()) { //printf("Checking Links of %s\n",node->Get_Name()); degree=node->Get_Weight(); kis=0.0; kir=0.0; // For every of the neighbors, check, count the links to the community l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } if ((long)n_cur->Get_Marker()==community_marker) { kis+=w; //the weight/number of links to the community } else { kir+=w; //the weight/number of links to the rest of the network } l_cur=l_iter.Next(); } aff_r=kir-gamma/total_degree_sum*(Kr-degree)*degree; aff_s=kis-gamma/total_degree_sum*Ks*degree; delta_aff_add=aff_r-aff_s; // if (aff_s>=aff_r && delta_aff_add<=max_delta_aff) { if (delta_aff_add<=max_delta_aff) { node->Set_Affinity(aff_s); max_delta_aff=delta_aff_add; max_aff_node=node; add=true; } //printf("%s in to_do list with affinity %f\n",node->Get_Name(),node->Get_Affinity()); node=iter.Next(); } //################ //calculate the affinity changes for removing every single node from the community //################ inner_links=0; outer_links=0; remove=false; node=iter.First(community); while (!iter.End()) { //printf("Checking Links of %s\n",node->Get_Name()); degree=node->Get_Weight(); kis=0.0; kir=0.0; // For every of the neighbors, check, count the links to the community l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } if ((long)n_cur->Get_Marker()==community_marker) { kis+=w; inner_links+=w; //summing all w gives twice the number of inner links(weights) } else { kir+=w; outer_links+=w; } l_cur=l_iter.Next(); } // if (kir+kis!=degree) { printf("error kir=%f\tkis=%f\tk=%f\n",kir,kis,degree); } aff_r=kir-gamma/total_degree_sum*Kr*degree; aff_s=kis-gamma/total_degree_sum*(Ks-degree)*degree; delta_aff_rem=aff_s-aff_r; node->Set_Affinity(aff_s); // we should not remove the nodes, we have just added if (delta_aff_remGet_Name(),node->Get_Affinity()); node=iter.Next(); } inner_links=inner_links*0.5; //################ // Now check, whether we want to remove or add a node //################ if (add) { //################ //add the node of maximum affinity to the community //############### community->Push(max_aff_node); max_aff_node->Set_Marker(community_marker); //delete node from to_do to_do->fDelete(max_aff_node); //update the sum of degrees in the community Ks+=max_aff_node->Get_Weight(); Kr-=max_aff_node->Get_Weight(); // printf("Adding node %s to community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff); //now add all neighbors of this node, that are not already //in the to_do list or in the community neighbor=iter.First(max_aff_node->Get_Neighbours()); while (!iter.End()) { if ((long)neighbor->Get_Marker()!=community_marker && (long)neighbor->Get_Marker()!=to_do_marker) { to_do->Push(neighbor); neighbor->Set_Marker(to_do_marker); //printf("Adding node %s to to_do list.\n",neighbor->Get_Name()); } neighbor=iter.Next(); } } if (remove) { //################ //remove those with negative affinities //################ community->fDelete(max_aff_node); max_aff_node->Set_Marker(to_do_marker); //update the sum of degrees in the community Ks-=max_aff_node->Get_Weight(); Kr+=max_aff_node->Get_Weight(); //add the node to to_do again to_do->Push(max_aff_node); // printf("Removing node %s from community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff); } IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ } //################### //write the node in the community to a file //################### // TODO return this instead of writing it // fprintf(file,"Number_of_nodes:\t%d\n",community->Size()); // fprintf(file,"Inner_Links:\t%f\n",inner_links); // fprintf(file,"Outer_Links:\t%f\n",Ks-2*inner_links); // fprintf(file,"Cohesion:\t%f\n",inner_links-gamma/total_degree_sum*Ks*Ks*0.5); // fprintf(file,"Adhesion:\t%f\n",outer_links-gamma/total_degree_sum*Ks*Kr); // fprintf(file,"\n"); if (cohesion) { *cohesion=inner_links-gamma/total_degree_sum*Ks*Ks*0.5; } if (adhesion) { *adhesion=outer_links-gamma/total_degree_sum*Ks*Kr; } if (my_inner_links) { *my_inner_links=inner_links; } if (my_outer_links) { *my_outer_links=outer_links; } if (result) { node=iter.First(community); igraph_vector_resize(result, 0); while (!iter.End()) { // printf("%s in community.\n",node->Get_Name()); // fprintf(file,"%s\t%f\n",node->Get_Name(),node->Get_Affinity()); IGRAPH_CHECK(igraph_vector_push_back(result, node->Get_Index())); node=iter.Next(); } } // printf("%d nodes in community around %s\n",community->Size(),start_node->Get_Name()); // fclose(file); unsigned int size=community->Size(); delete to_do; delete community; return size; } //################################################################################################ // this Function writes the clusters to disk //################################################################################################ long PottsModel::WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *csize, igraph_vector_t *membership, double kT, double gamma) { NNode *n_cur, *n_cur2; /* double a1,a2,a3,p,p1,p2; long n,N,lin,lout; */ DLList_Iter iter, iter2; HugeArray inner_links; HugeArray outer_links; HugeArray nodes; //den Header schreiben // p=2.0*double(num_of_links)/double(num_of_nodes)/double(num_of_nodes-1); // fprintf(file," Nodes=\t%lu\n",num_of_nodes); // fprintf(file," Links=\t%lu\n",num_of_links); // fprintf(file," q=\t%d\n",q); // fprintf(file," p=\t%f\n",p); // fprintf(file," Modularity=\t%f\n",calculate_Q()); // fprintf(file,"Temperature=\t%f\n", kT); // fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\t\n"); if (temperature) { *temperature=kT; } if (csize || membership || modularity) { // TODO: count the number of clusters for (unsigned int spin=1; spin<=q; spin++) { inner_links[spin]=0; outer_links[spin]=0; nodes[spin]=0; n_cur=iter.First(net->node_list); while (!iter.End()) { if (n_cur->Get_ClusterIndex()==spin) { nodes[spin]++; n_cur2=iter2.First(n_cur->Get_Neighbours()); while (!iter2.End()) { if (n_cur2->Get_ClusterIndex()==spin) inner_links[spin]++; else outer_links[spin]++; n_cur2=iter2.Next(); } } n_cur=iter.Next(); } } } if (modularity) { *modularity=0.0; for (unsigned int spin=1; spin<=q; spin++) { if (nodes[spin]>0) { double t1= inner_links[spin] / net->sum_weights / 2.0; double t2= (inner_links[spin] + outer_links[spin]) / net->sum_weights / 2.0; *modularity += t1; *modularity -= gamma * t2 * t2; } } } if (csize) { igraph_vector_resize(csize, 0); for (unsigned int spin=1; spin<=q; spin++) { if (nodes[spin]>0) { inner_links[spin]/=2; // fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\n"); /* N=num_of_nodes; n=nodes[spin]; lin=inner_links[spin]; lout=outer_links[spin]; a1=N*log((double)N)-n*log((double)n)*(N-n)*log((double)N-n); if ((lin==long(n*(n-1)*0.5+0.5)) || (n==1)) a2=0.0; else a2=(n*(n-1)*0.5 )*log((double)n*(n-1)*0.5 )-(n*(n-1)*0.5 )- (n*(n-1)*0.5-lin)*log((double)n*(n-1)*0.5-lin)+(n*(n-1)*0.5-lin)- lin*log((double)lin )+lin; */ /* if ((lout==n*(N-n)) || n==N) a3=0.0; else a3=(n*(N-n) )*log((double)n*(N-n) )-(n*(N-n))- (n*(N-n)-lout)*log((double)n*(N-n)-lout)+(n*(N-n)-lout)- lout*log((double)lout )+lout; */ /* p1=(lin+lout)*log((double)p); p2=(0.5*n*(n-1)-lin + n*(N-n)-lout)*log((double)1.0-p); */ // fprintf(file,"%d\t%d\t%d\t%d\t%f\t%f\t%f\n",spin,nodes[spin], inner_links[spin], outer_links[spin], p_in, p_out,log_num_exp); IGRAPH_CHECK(igraph_vector_push_back(csize, nodes[spin])); } } // fprintf(file,"\n"); } //die Elemente der Cluster if (membership) { long int no=-1; IGRAPH_CHECK(igraph_vector_resize(membership, num_of_nodes)); for (unsigned int spin=1; spin<=q; spin++) { if (nodes[spin]>0) { no++; } n_cur=iter.First(net->node_list); while (!iter.End()) { if (n_cur->Get_ClusterIndex()==spin) { // fprintf(file,"%d\t%s\n",spin,n_cur->Get_Name()); VECTOR(*membership)[ n_cur->Get_Index() ]=no; } n_cur=iter.Next(); } } } return num_of_nodes; } //################################################################################################ //This function writes the soft clusters after a gamma sweep //that is, it groups every node together that was found in // more than threshold percent together with the other node // in the same cluster //################################################################################################ // Does not work at the moment !!! //################################################################################################ // long PottsModel::WriteSoftClusters(char *filename, double threshold) // { // FILE *file; // NNode *n_cur, *n_cur2; // DLList_Iter iter, iter2; // DL_Indexed_List*> *cl_list, *old_clusterlist; // ClusterList *cl_cur; // double max; // file=fopen(filename,"w"); // if (!file) { // printf("Could not open %s for writing.\n",filename); // return -1; // } // max=correlation[0]->Get(0); // //printf("max=%f\n",max); // cl_list=new DL_Indexed_List*>(); // n_cur=iter.First(net->node_list); // while (!iter.End()) // { // cl_cur=new ClusterList(); // cl_list->Push(cl_cur); // n_cur2=iter2.First(net->node_list); // while (!iter2.End()) // { // if (double(correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()))/max>threshold) // cl_cur->Push(n_cur2); // n_cur2=iter2.Next(); // } // n_cur=iter.Next(); // } // old_clusterlist=net->cluster_list; // net->cluster_list=cl_list; // clear_all_markers(net); // //printf("Es gibt %d Cluster\n",cl_list->Size()); // reduce_cliques2(net, false, 15); // //printf("Davon bleiben %d Cluster uebrig\n",cl_list->Size()); // clear_all_markers(net); // while (net->cluster_list->Size()){ // cl_cur=net->cluster_list->Pop(); // while (cl_cur->Size()) // { // n_cur=cl_cur->Pop(); // fprintf(file,"%s\n",n_cur->Get_Name()); // //printf("%s\n",n_cur->Get_Name()); // } // fprintf(file,"\n"); // } // net->cluster_list=old_clusterlist; // fclose(file); // return 1; // } //############################################################################# // Performs a gamma sweep //############################################################################# double PottsModel::GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions) { double stepsize; double kT, kT_start; long changes; double gamma, acc; NNode *n_cur, *n_cur2; DLList_Iter iter, iter2; stepsize=(gamma_stop-gamma_start)/double(steps); n_cur=iter.First(net->node_list); while (!iter.End()) { correlation[n_cur->Get_Index()]=new HugeArray(); n_cur2=iter2.First(net->node_list); while (!iter2.End()) { correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index())=0.0; n_cur2=iter2.Next(); } n_cur=iter.Next(); } for (unsigned int n=0; n<=steps; n++) { assign_initial_conf(-1); initialize_Qmatrix(); gamma=gamma_start+stepsize*n; kT=0.5; acceptance=0.5; while (acceptance<(1.0-1.0/double(q))*0.95) //wollen 95% Acceptance { kT*=1.1; //initialize_lookup(kT,kmax,net->node_list->Size()); if (!non_parallel) HeatBathParallelLookup(gamma,prob, kT,25); else HeatBathLookup(gamma,prob, kT,25); // printf("kT=%f acceptance=%f\n", kT, acceptance); } // printf("Starting with gamma=%f\n", gamma); kT_start=kT; for (int i=0; i0) && (kT>0.01)) { kT=kT*0.99; //initialize_lookup(kT,kmax,net->node_list->Size()); if (!non_parallel) { changes=HeatBathParallelLookup(gamma, prob, kT, 50); // printf("kT: %f \t Changes %li\n",kT, changes); } else { acc=HeatBathLookup(gamma, prob, kT, 50); if (acc>(1.0-1.0/double(q))*0.01) changes=1; else changes=0; // printf("kT: %f Acceptance: %f\n",kT, acc); } } // printf("Finisched with acceptance: %1.6f bei kT=%2.4f und gamma=%2.4f\n",acceptance,kT, gamma); // fprintf(file,"%f\t%f\n",gamma_,acceptance); // fprintf(file2,"%f\t%f\n",gamma_,kT); // fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5)); //Die Correlation berechnen n_cur=iter.First(net->node_list); while (!iter.End()) { n_cur2=iter2.First(net->node_list); while (!iter2.End()) { if (n_cur->Get_ClusterIndex()==n_cur2->Get_ClusterIndex()) { correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index())+=0.5; } n_cur2=iter2.Next(); } n_cur=iter.Next(); } } // for i } //for n return kT; } //############################################################################# //Performs a Gamma sweep at zero T //############################################################################# double PottsModel::GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions) { double stepsize; long changes; double gamma, acc; long runs; NNode *n_cur, *n_cur2; DLList_Iter iter, iter2; stepsize=(gamma_stop-gamma_start)/double(steps); n_cur=iter.First(net->node_list); while (!iter.End()) { correlation[n_cur->Get_Index()]=new HugeArray(); n_cur2=iter2.First(net->node_list); while (!iter2.End()) { correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index())=0.0; n_cur2=iter2.Next(); } n_cur=iter.Next(); } for (unsigned int n=0; n<=steps; n++) { assign_initial_conf(-1); initialize_Qmatrix(); gamma=gamma_start+stepsize*n; // printf("Starting with gamma=%f\n", gamma); for (int i=0; i0 && runs<250) { //initialize_lookup(kT,kmax,net->node_list->Size()); if (!non_parallel) { changes=HeatBathParallelLookupZeroTemp(gamma, prob, 1); // printf("Changes %li\n", changes); } else { acc=HeatBathLookupZeroTemp(gamma, prob, 1); if (acc>(1.0-1.0/double(q))*0.01) changes=1; else changes=0; // printf("Acceptance: %f\n", acc); } runs++; } // printf("Finisched with Modularity: %1.6f bei Gamma=%1.6f\n",calculate_Q(), gamma); // fprintf(file,"%f\t%f\n",gamma_,acceptance); // fprintf(file2,"%f\t%f\n",gamma_,kT); // fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5)); //Die Correlation berechnen n_cur=iter.First(net->node_list); while (!iter.End()) { n_cur2=iter2.First(net->node_list); while (!iter2.End()) { if (n_cur->Get_ClusterIndex()==n_cur2->Get_ClusterIndex()) { correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index())+=0.5; correlation[n_cur2->Get_Index()]->Set(n_cur->Get_Index())+=0.5; } n_cur2=iter2.Next(); } n_cur=iter.Next(); } } // for i } //for n return gamma; } //####################################################################### //----------------------------------------------------------------------- //####################################################################### // This function writes the Correlation Matrix that results from a // Gamma-Sweep, this matrix is used to make ps files of it. // ###################################################################### // long PottsModel::WriteCorrelationMatrix(char *filename) // { // FILE *file, *file2; // char filename2[255]; // NNode *n_cur, *n_cur2; // DLList_Iter iter, iter2; // sprintf(filename2,"%s.mat",filename); // file=fopen(filename,"w"); // if (!file) { // printf("Could not open %s for writing.\n",filename); // return -1; // } // file2=fopen(filename2,"w"); // if (!file2) { // printf("Could not open %s for writing.\n",filename2); // return -1; // } // //write the header in one line // n_cur=iter.First(net->node_list); // while (!iter.End()) // { // fprintf(file, "\t%s",n_cur->Get_Name()); // n_cur=iter.Next(); // } // fprintf(file, "\n"); // //fprintf(file, "%d\t%d\n",net->node_list->Size(),net->node_list->Size()); // long r=0,c=0; // n_cur=iter.First(net->node_list); // while (!iter.End()) // { // fprintf(file, "%s",n_cur->Get_Name()); // r++; // n_cur2=iter2.First(net->node_list); // while (!iter2.End()) // { // c++; // fprintf(file,"\t%f",correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index())); // fprintf(file2,"%li\t%li\t%f\n",r,c,correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index())); // n_cur2=iter2.Next(); // } // fprintf(file,"\n"); // n_cur=iter.Next(); // } // fclose(file); // fclose(file2); // return 1; // } //############################################################################## //################################################################################################# PottsModelN::PottsModelN(network *n, unsigned int num_communities, bool directed) { //Set internal variable net = n; q = num_communities; is_directed = directed; is_init = false; num_nodes = net->node_list->Size(); } //####################################################### //Destructor of PottsModel //######################################################## PottsModelN::~PottsModelN() { delete degree_pos_in; delete degree_neg_in; delete degree_pos_out; delete degree_neg_out; delete degree_community_pos_in; delete degree_community_neg_in; delete degree_community_pos_out; delete degree_community_neg_out; delete weights; delete neighbours; delete csize; delete spin; return; } void PottsModelN::assign_initial_conf(bool init_spins) { #ifdef DEBUG printf("Start assigning.\n"); #endif int s; DLList_Iter iter; DLList_Iter l_iter; NNode *n_cur; NLink *l_cur; if(init_spins) { #ifdef DEBUG printf("Initializing spin.\n"); #endif //Bookkeeping of the various degrees (positive/negative) and (in/out) degree_pos_in = new double[num_nodes]; //Postive indegree of the nodes (or sum of weights) degree_neg_in = new double[num_nodes]; //Negative indegree of the nodes (or sum of weights) degree_pos_out = new double[num_nodes]; //Postive outdegree of the nodes (or sum of weights) degree_neg_out = new double[num_nodes]; //Negative outdegree of the nodes (or sum of weights) spin = new unsigned int[num_nodes]; //The spin state of each node } if (is_init) { delete degree_community_pos_in; delete degree_community_neg_in; delete degree_community_pos_out; delete degree_community_neg_out; delete weights; delete neighbours; delete csize; } is_init = true; //Bookkeep of occupation numbers of spin states or the number of links in community... degree_community_pos_in = new double[q+1]; //Positive sum of indegree for communities degree_community_neg_in = new double[q+1]; //Negative sum of indegree for communities degree_community_pos_out = new double[q+1];//Positive sum of outegree for communities degree_community_neg_out = new double[q+1]; //Negative sum of outdegree for communities //...and of weights and neighbours for in the HeathBathLookup weights = new double[q+1]; //The weights for changing to another spin state neighbours = new double[q+1]; //The number of neighbours (or weights) in different spin states csize = new unsigned int[q+1]; //The number of nodes in each community //Initialize communities for (unsigned int i=0; i<=q; i++) { degree_community_pos_in[i] = 0.0; degree_community_neg_in[i] = 0.0; degree_community_pos_out[i] = 0.0; degree_community_neg_out[i] = 0.0; csize[i] = 0; } //Initialize vectors if (init_spins) { for (unsigned int i = 0; i < num_nodes; i++) { degree_pos_in[i] = 0.0; degree_neg_in[i] = 0.0; degree_pos_out[i] = 0.0; degree_neg_out[i] = 0.0; #ifdef DEBUG printf("Initializing spin %d", i); #endif spin[i] = 0; } } m_p=0.0; m_n=0.0; //Set community for each node, and //correctly store it in the bookkeeping double sum_weight_pos_in, sum_weight_pos_out, sum_weight_neg_in, sum_weight_neg_out; //double av_w = 0.0, av_k=0.0; //int l = 0; #ifdef DEBUG printf("Visiting each node.\n"); #endif for (unsigned int v = 0; v < num_nodes; v++) { if (init_spins) { s = RNG_INTEGER(1, q); //The new spin s spin[v] = (unsigned int)s; } else s = spin[v]; #ifdef DEBUG printf("Spin %d assigned to node %d.\n", s, v); #endif n_cur = net->node_list->Get(v); l_cur = l_iter.First(n_cur->Get_Links()); sum_weight_pos_in = 0.0; sum_weight_pos_out = 0.0; sum_weight_neg_in = 0.0; sum_weight_neg_out = 0.0; while (!l_iter.End()) { double w = l_cur->Get_Weight(); //av_w = (av_w*l + w)/(l+1); //Average weight //l++; if (l_cur->Get_Start() == n_cur) //From this to other, so outgoing link if (w > 0) sum_weight_pos_out += w; //Increase positive outgoing weight else sum_weight_neg_out -= w; //Increase negative outgoing weight else if (w > 0) sum_weight_pos_in += w; //Increase positive incoming weight else sum_weight_neg_in -= w; //Increase negative incoming weight l_cur=l_iter.Next(); } if (!is_directed) { double sum_weight_pos = sum_weight_pos_out + sum_weight_pos_in; sum_weight_pos_out = sum_weight_pos; sum_weight_pos_in = sum_weight_pos; double sum_weight_neg = sum_weight_neg_out + sum_weight_neg_in; sum_weight_neg_out = sum_weight_neg; sum_weight_neg_in = sum_weight_neg; } //av_k = (av_k*l + sum_weight_pos_in)/(l+1); //Average k if (init_spins) { //Set the degrees correctly degree_pos_in[v] = sum_weight_pos_in; degree_neg_in[v] = sum_weight_neg_in; degree_pos_out[v] = sum_weight_pos_out; degree_neg_out[v] = sum_weight_neg_out; } //Correct the community bookkeeping degree_community_pos_in[s] += sum_weight_pos_in; degree_community_neg_in[s] += sum_weight_neg_in; degree_community_pos_out[s] += sum_weight_pos_out; degree_community_neg_out[s] += sum_weight_neg_out; //Community just increased csize[s]++; //Sum the weights (notice that sum of indegrees equals sum of outdegrees) m_p += sum_weight_pos_in; m_n += sum_weight_neg_in; } #ifdef DEBUG printf("Done assigning.\n"); #endif return; } //############################################################## // This is the function generally used for optimisation, // as the parallel update has its flaws, due to the cyclic attractors //############################################################## double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps) { #ifdef DEBUG printf("Starting sweep at temperature %f.\n", t); #endif DLList_Iter iter; DLList_Iter l_iter; DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; /* The new_spin contains the spin to which we will update, * the spin_opt is the optional spin we will consider and * the old_spin is the spin of the node we are currently * changing. */ unsigned int new_spin, spin_opt, old_spin; unsigned int sweep; //current sweep unsigned long changes, problemcount; //Number of changes and number of problems encountered double exp_old_spin; //The expectation value for the old spin double exp_spin; //The expectation value for the other spin(s) int v; //The node we will be investigating //The variables required for the calculations double delta_pos_out, delta_pos_in, delta_neg_out, delta_neg_in; double k_v_pos_out, k_v_pos_in, k_v_neg_out, k_v_neg_in; //weight of edge double w; double beta = 1/t; //Weight for probabilities double r = 0.0; //random number used for assigning new spin double maxweight = 0.0; double sum_weights = 0.0; //sum_weights for normalizing the probabilities sweep=0; changes=0; double m_pt = m_p; double m_nt = m_n; if (m_pt < 0.001) m_pt = 1; if (m_nt < 0.001) m_nt = 1; while (sweepnode_list->Get(v); /*******************************************/ // initialize the neighbours and the weights problemcount=0; for (unsigned int i=0; i<=q; i++) { neighbours[i]=0.0; weights[i]=0.0; } //Loop over all links (=neighbours) l_cur=l_iter.First(node->Get_Links()); while (!l_iter.End()) { w=l_cur->Get_Weight(); if (node==l_cur->Get_Start()) { n_cur=l_cur->Get_End(); } else { n_cur=l_cur->Get_Start(); } //Add the link to the correct cluster neighbours[spin[n_cur->Get_Index()]]+=w; l_cur=l_iter.Next(); } //We now have the weight of the (in and out) neighbours //in each cluster available to us. /*******************************************/ old_spin=spin[v]; //Look for optimal spin //Set the appropriate variable delta_pos_out = degree_pos_out[v]; delta_pos_in = degree_pos_in[v]; delta_neg_out = degree_neg_out[v]; delta_neg_in = degree_neg_in[v]; k_v_pos_out = gamma*delta_pos_out/m_pt; k_v_pos_in = gamma*delta_pos_in/m_pt; k_v_neg_out = lambda*delta_neg_out/m_nt; k_v_neg_in = lambda*delta_neg_in/m_nt; //The expectation value for the old spin if (is_directed) exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) - k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in)) + (k_v_pos_in * (degree_community_pos_out[old_spin] - delta_pos_out) - k_v_neg_in * (degree_community_neg_out[old_spin] - delta_neg_out)); else exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) - k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in)); /*******************************************/ //Calculating probabilities for each transition to another //community. maxweight=0.0; weights[old_spin]=0.0; for (spin_opt=1; spin_opt<=q; spin_opt++) // all possible new spins { if (spin_opt!=old_spin) // except the old one! { if (is_directed) exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]) + (k_v_pos_in * degree_community_pos_out[spin_opt] - k_v_neg_in * degree_community_neg_out[spin_opt]); else exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]); weights[spin_opt] = (neighbours[spin_opt] - exp_spin) - (neighbours[old_spin] - exp_old_spin); if (weights[spin_opt] > maxweight) maxweight = weights[spin_opt]; } } // for spin //Calculate exp. prob. an sum_weights = 0.0; for (spin_opt=1; spin_opt<=q; spin_opt++) // all possible new spins { weights[spin_opt] -= maxweight; //subtract maxweight for numerical stability (otherwise overflow). weights[spin_opt] = exp((double)(beta*weights[spin_opt])); sum_weights += weights[spin_opt]; } // for spin /*******************************************/ /*******************************************/ //Choose a new spin dependent on the calculated probabilities r = RNG_UNIF(0, sum_weights); new_spin = 1; bool found = false; while (!found && new_spin <= q) { if (r <= weights[new_spin]) { spin_opt = new_spin; //We have found are new spin found = true; break; } else r -= weights[new_spin]; //Perhaps the next spin is the one we want new_spin++; } //Some weird thing happened. We haven't found a new spin //while that shouldn't be the case. Numerical problems? if (!found) problemcount++; new_spin=spin_opt; //If there wasn't a problem we should have found //our new spin. /*******************************************/ /*******************************************/ //The new spin is available to us, so change //all the appropriate counters. if (new_spin!=old_spin) // Did we really change something?? { changes++; spin[v] = new_spin; //The new spin increase by one, and the old spin decreases by one csize[new_spin]++; csize[old_spin]--; //Change the sums of degree for the old spin... degree_community_pos_in[old_spin] -= delta_pos_in; degree_community_neg_in[old_spin] -= delta_neg_in; degree_community_pos_out[old_spin] -= delta_pos_out; degree_community_neg_out[old_spin] -= delta_neg_out; //...and for the new spin degree_community_pos_in[new_spin] += delta_pos_in; degree_community_neg_in[new_spin] += delta_neg_in; degree_community_pos_out[new_spin] += delta_pos_out; degree_community_neg_out[new_spin] += delta_neg_out; } //We have no change a node from old_spin to new_spin /*******************************************/ } // for n } // while sweep #ifdef DEBUG printf("Done %d sweeps.\n", max_sweeps); printf("%d changes made for %d nodes.\n", changes, num_nodes); printf("Last node is %d and last random number is %f with sum of weights %f with spin %d.\n", v, r, sum_weights, old_spin); #endif return (double(changes)/double(num_nodes)/double(sweep)); } //We need to begin at a suitable temperature. That is, a temperature at which //enough nodes may change their initially assigned communties double PottsModelN::FindStartTemp(double gamma, double lambda, double ts) { double kT; kT=ts; //assing random initial condition assign_initial_conf(true); // the factor 1-1/q is important, since even, at infinite temperature, // only 1-1/q of all spins do change their state, since a randomly chooses new // state is with prob. 1/q the old state. double acceptance = 0.0; while (acceptance<(1.0-1.0/double(q))*0.95) //want 95% acceptance { kT=kT*1.1; acceptance=HeatBathLookup(gamma,lambda, kT,50); } kT*=1.1; // just to be sure... return kT; } long PottsModelN::WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, igraph_vector_t *community_size, igraph_vector_t *membership, igraph_matrix_t *adhesion, igraph_matrix_t *normalised_adhesion, igraph_real_t *polarization, double t, double d_p, double d_n, double gamma, double lambda) { IGRAPH_UNUSED(gamma); IGRAPH_UNUSED(lambda); #ifdef DEBUG printf("Start writing clusters.\n"); #endif //Reassign each community so that we retrieve a community assignment 1 through num_communities unsigned int *cluster_assign = new unsigned int[q+1]; for (unsigned int i = 0; i <= q; i++) { cluster_assign[i] = 0; } int num_clusters = 0; //Find out what the new communities will be for (unsigned int i = 0; i < num_nodes; i++) { int s = spin[i]; if (cluster_assign[s] == 0) { num_clusters++; cluster_assign[s] = num_clusters; #ifdef DEBUG printf("Setting cluster %d to %d.\n", s, num_clusters); #endif } } /* DLList_Iter iter; NNode *n_cur=iter.First(net->node_list); n_cur = iter.First(net->node_list); */ //And now assign each node to its new community q = num_clusters; for (unsigned int i = 0; i < num_nodes; i++) { #ifdef DEBUG printf("Setting node %d to %d.\n", i, cluster_assign[spin[i]]); #endif unsigned int s = cluster_assign[spin[i]]; spin[i] = s; #ifdef DEBUG printf("Have set node %d to %d.\n", i, s); #endif } assign_initial_conf(false); delete cluster_assign; if (temperature) { *temperature=t; } if (community_size) { //Initialize the vector IGRAPH_CHECK(igraph_vector_resize(community_size, q)); for (unsigned int spin_opt = 1; spin_opt <= q; spin_opt++) { //Set the community size VECTOR(*community_size)[spin_opt-1]=csize[spin_opt]; } } //Set the membership if (membership) { IGRAPH_CHECK(igraph_vector_resize(membership, num_nodes)); for (unsigned int i = 0; i < num_nodes; i++) { VECTOR(*membership)[ i ]= spin[i]-1; } } double Q = 0.0; //Modularity if (adhesion) { IGRAPH_CHECK(igraph_matrix_resize(adhesion, q, q)); IGRAPH_CHECK(igraph_matrix_resize(normalised_adhesion, q, q)); double **num_links_pos = 0; double **num_links_neg = 0; //memory allocated for elements of rows. num_links_pos = new double *[q+1] ; num_links_neg = new double *[q+1] ; //memory allocated for elements of each column. for( unsigned int i = 0 ; i < q+1 ; i++) { num_links_pos[i] = new double[q+1]; num_links_neg[i] = new double[q+1]; } //Init num_links for (unsigned int i = 0; i <= q; i++) { for (unsigned int j = 0; j <= q; j++) { num_links_pos[i][j] = 0.0; num_links_neg[i][j] = 0.0; } } DLList_Iter iter_l; NLink *l_cur = iter_l.First(net->link_list); double w = 0.0; while (!iter_l.End()) { w = l_cur->Get_Weight(); unsigned int a = spin[l_cur->Get_Start()->Get_Index()]; unsigned int b = spin[l_cur->Get_End()->Get_Index()]; if (w > 0) { num_links_pos[a][b] += w; if (!is_directed && a != b) //Only one edge is defined in case it is undirected num_links_pos[b][a] += w; } else { num_links_neg[a][b] -= w; if (!is_directed && a != b) //Only one edge is defined in case it is undirected num_links_neg[b][a] -= w; } l_cur = iter_l.Next(); } //while links #ifdef DEBUG printf("d_p: %f\n", d_p); printf("d_n: %f\n", d_n); #endif double expected = 0.0; double a = 0.0; double normal_a = 0.0; double delta, u_p, u_n; double max_expected, max_a; //We don't take into account the lambda or gamma for //computing the modularity and adhesion, since they //are then incomparable to other definitions. for (unsigned int i = 1; i <= q; i++) { for (unsigned int j = 1; j <= q; j++) { if (!is_directed && i == j) expected = degree_community_pos_out[i] * degree_community_pos_in[j]/(m_p == 0 ? 1 : 2*m_p) - degree_community_neg_out[i] * degree_community_neg_in[j]/(m_n == 0 ? 1 : 2*m_n); else expected = degree_community_pos_out[i] * degree_community_pos_in[j]/(m_p == 0 ? 1: m_p) - degree_community_neg_out[i] * degree_community_neg_in[j]/(m_n == 0 ? 1 : m_n); a = (num_links_pos[i][j] - num_links_neg[i][j]) - expected; if (i == j) //cohesion { if (is_directed) delta = d_p * csize[i] * (csize[i] - 1); //Maximum amount else delta = d_p * csize[i] * (csize[i] - 1)/2; //Maximum amount u_p = delta - num_links_pos[i][i]; //Add as many positive links we can u_n = -num_links_neg[i][i]; //Delete as many negative links we can Q += a; } else //adhesion { if (is_directed) delta = d_n * csize[i] * csize[j]*2; //Maximum amount else delta = d_n * csize[i] * csize[j]; //Maximum amount u_p = -num_links_pos[i][j]; //Delete as many positive links we can u_n = delta - num_links_neg[i][j]; //Add as many negative links we can } if (!is_directed && i == j) max_expected = (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p)/((m_p + u_p) == 0 ? 1 : 2*(m_p + u_p)) - (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n)/((m_n + u_n) == 0 ? 1 : 2*(m_n + u_n)); else max_expected = (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p)/((m_p + u_p) == 0 ? 1 : m_p + u_p) - (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n)/((m_n + u_n) == 0 ? 1 : m_n + u_n); //printf("%f/%f %d/%d\t", num_links_pos[i][j], num_links_neg[i][j], csize[i], csize[j]); //printf("%f/%f - %f(%f)\t", u_p, u_n, expected, max_expected); max_a = ((num_links_pos[i][j] + u_p) - (num_links_neg[i][j] + u_n)) - max_expected; //In cases where we haven't actually found a ground state //the adhesion/cohesion *might* not be negative/positive, //hence the maximum adhesion and cohesion might behave quite //strangely. In order to prevent that, we limit them to 1 in //absolute value, and prevent from dividing by zero (even if //chuck norris would). if (i == j) normal_a = a/(max_a == 0 ? a : max_a); else normal_a = -a/(max_a == 0 ? a : max_a); if (normal_a > 1) normal_a = 1; else if (normal_a < -1) normal_a = -1; MATRIX(*adhesion, i - 1, j - 1) = a; MATRIX(*normalised_adhesion, i - 1, j - 1) = normal_a; } //for j //printf("\n"); } //for i //free the allocated memory for( unsigned int i = 0 ; i < q+1 ; i++ ) { delete [] num_links_pos[i] ; delete [] num_links_neg[i]; } delete [] num_links_pos ; delete [] num_links_neg ; } //adhesion if (modularity) { if (is_directed) *modularity=Q/(m_p + m_n); else *modularity=2*Q/(m_p + m_n); //Correction for the way m_p and m_n are counted. Modularity is 1/m, not 1/2m } if (polarization) { double sum_ad = 0.0; for (unsigned int i = 0; i < q; i++) { for (unsigned int j = 0; j < q; j++) { if (i != j) { sum_ad -= MATRIX(*normalised_adhesion, i, j); } } } *polarization= sum_ad/(q*q - q); } #ifdef DEBUG printf("Finished writing cluster.\n"); #endif return num_nodes; } igraph/src/igraph_vector_ptr.h0000644000176000001440000001056612325527073016253 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_VECTOR_PTR_H #define IGRAPH_VECTOR_PTR_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_vector.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Flexible vector, storing pointers */ /* -------------------------------------------------- */ /** * Vector, storing pointers efficiently * \ingroup internal * */ typedef struct s_vector_ptr { void** stor_begin; void** stor_end; void** end; igraph_finally_func_t* item_destructor; } igraph_vector_ptr_t; #define IGRAPH_VECTOR_PTR_NULL { 0,0,0,0 } #define IGRAPH_VECTOR_PTR_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_vector_ptr_init(v, size)); \ IGRAPH_FINALLY(igraph_vector_ptr_destroy, v); } while (0) int igraph_vector_ptr_init (igraph_vector_ptr_t* v, long int size); int igraph_vector_ptr_init_copy (igraph_vector_ptr_t* v, void** data, long int length); const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, void *const *data, long int length); void igraph_vector_ptr_destroy (igraph_vector_ptr_t* v); void igraph_vector_ptr_free_all (igraph_vector_ptr_t* v); void igraph_vector_ptr_destroy_all (igraph_vector_ptr_t* v); int igraph_vector_ptr_reserve (igraph_vector_ptr_t* v, long int size); igraph_bool_t igraph_vector_ptr_empty (const igraph_vector_ptr_t* v); long int igraph_vector_ptr_size (const igraph_vector_ptr_t* v); void igraph_vector_ptr_clear (igraph_vector_ptr_t* v); void igraph_vector_ptr_null (igraph_vector_ptr_t* v); int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e); int igraph_vector_ptr_append (igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v); int igraph_vector_ptr_insert(igraph_vector_ptr_t *v, long int pos, void* e); void* igraph_vector_ptr_e (const igraph_vector_ptr_t* v, long int pos); void igraph_vector_ptr_set (igraph_vector_ptr_t* v, long int pos, void* value); int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize); void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to); int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos); void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(const void*, const void*)); int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v, const igraph_vector_int_t *idx); igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v); igraph_finally_func_t* igraph_vector_ptr_set_item_destructor(igraph_vector_ptr_t *v, igraph_finally_func_t *func); /** * \define IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR * \brief Sets the item destructor for this pointer vector (macro version). * * This macro is expanded to \ref igraph_vector_ptr_set_item_destructor(), the * only difference is that the second argument is automatically cast to an * \c igraph_finally_func_t*. The cast is necessary in most cases as the * destructor functions we use (such as \ref igraph_vector_destroy()) take a * pointer to some concrete igraph data type, while \c igraph_finally_func_t * expects \c void* */ #define IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(v, func) \ igraph_vector_ptr_set_item_destructor((v), (igraph_finally_func_t*)(func)) __END_DECLS #endif igraph/src/foreign-lgl-parser.y0000644000176000001440000001044412325372071016241 0ustar ripleyusers/* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include "igraph_hacks_internal.h" #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_math.h" #include "igraph_memory.h" #include "igraph_error.h" #include "config.h" #include "foreign-lgl-header.h" #include "foreign-lgl-parser.h" #define yyscan_t void* int igraph_lgl_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, char *s); char *igraph_lgl_yyget_text (yyscan_t yyscanner ); int igraph_lgl_yyget_leng (yyscan_t yyscanner ); igraph_real_t igraph_lgl_get_number(const char *str, long int len); #define scanner context->scanner %} %pure-parser %output="y.tab.c" %name-prefix="igraph_lgl_yy" %defines %locations %error-verbose %parse-param { igraph_i_lgl_parsedata_t* context } %lex-param { void *scanner } %union { long int edgenum; double weightnum; } %type edgeid %type weight %token ALNUM %token NEWLINE %token HASH %% input : /* empty */ | input NEWLINE | input vertex ; vertex : vertexdef edges ; vertexdef : HASH edgeid NEWLINE { context->actvertex=$2; } ; edges : /* empty */ | edges edge ; edge : edgeid NEWLINE { igraph_vector_push_back(context->vector, context->actvertex); igraph_vector_push_back(context->vector, $1); igraph_vector_push_back(context->weights, 0); } | edgeid weight NEWLINE { igraph_vector_push_back(context->vector, context->actvertex); igraph_vector_push_back(context->vector, $1); igraph_vector_push_back(context->weights, $2); context->has_weights = 1; } ; edgeid : ALNUM { igraph_trie_get2(context->trie, igraph_lgl_yyget_text(scanner), igraph_lgl_yyget_leng(scanner), &$$); }; weight : ALNUM { $$=igraph_lgl_get_number(igraph_lgl_yyget_text(scanner), igraph_lgl_yyget_leng(scanner)); } ; %% int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char), "Parse error in LGL file, line %i (%s)", locp->first_line, s); return 0; } igraph_real_t igraph_lgl_get_number(const char *str, long int length) { igraph_real_t num; char *tmp=igraph_Calloc(length+1, char); strncpy(tmp, str, length); tmp[length]='\0'; sscanf(tmp, "%lf", &num); igraph_Free(tmp); return num; } igraph/src/glpapi08.c0000644000176000001440000003030112325527073014136 0ustar ripleyusers/* glpapi08.c (interior-point method routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" #include "glpipm.h" #include "glpnpp.h" /*********************************************************************** * NAME * * glp_interior - solve LP problem with the interior-point method * * SYNOPSIS * * int glp_interior(glp_prob *P, const glp_iptcp *parm); * * The routine glp_interior is a driver to the LP solver based on the * interior-point method. * * The interior-point solver has a set of control parameters. Values of * the control parameters can be passed in a structure glp_iptcp, which * the parameter parm points to. * * Currently this routine implements an easy variant of the primal-dual * interior-point method based on Mehrotra's technique. * * This routine transforms the original LP problem to an equivalent LP * problem in the standard formulation (all constraints are equalities, * all variables are non-negative), calls the routine ipm_main to solve * the transformed problem, and then transforms an obtained solution to * the solution of the original problem. * * RETURNS * * 0 The LP problem instance has been successfully solved. This code * does not necessarily mean that the solver has found optimal * solution. It only means that the solution process was successful. * * GLP_EFAIL * The problem has no rows/columns. * * GLP_ENOCVG * Very slow convergence or divergence. * * GLP_EITLIM * Iteration limit exceeded. * * GLP_EINSTAB * Numerical instability on solving Newtonian system. */ static void transform(NPP *npp) { /* transform LP to the standard formulation */ NPPROW *row, *prev_row; NPPCOL *col, *prev_col; for (row = npp->r_tail; row != NULL; row = prev_row) { prev_row = row->prev; if (row->lb == -DBL_MAX && row->ub == +DBL_MAX) npp_free_row(npp, row); else if (row->lb == -DBL_MAX) npp_leq_row(npp, row); else if (row->ub == +DBL_MAX) npp_geq_row(npp, row); else if (row->lb != row->ub) { if (fabs(row->lb) < fabs(row->ub)) npp_geq_row(npp, row); else npp_leq_row(npp, row); } } for (col = npp->c_tail; col != NULL; col = prev_col) { prev_col = col->prev; if (col->lb == -DBL_MAX && col->ub == +DBL_MAX) npp_free_col(npp, col); else if (col->lb == -DBL_MAX) npp_ubnd_col(npp, col); else if (col->ub == +DBL_MAX) { if (col->lb != 0.0) npp_lbnd_col(npp, col); } else if (col->lb != col->ub) { if (fabs(col->lb) < fabs(col->ub)) { if (col->lb != 0.0) npp_lbnd_col(npp, col); } else npp_ubnd_col(npp, col); npp_dbnd_col(npp, col); } else npp_fixed_col(npp, col); } for (row = npp->r_head; row != NULL; row = row->next) xassert(row->lb == row->ub); for (col = npp->c_head; col != NULL; col = col->next) xassert(col->lb == 0.0 && col->ub == +DBL_MAX); return; } int glp_interior(glp_prob *P, const glp_iptcp *parm) { glp_iptcp _parm; GLPROW *row; GLPCOL *col; NPP *npp = NULL; glp_prob *prob = NULL; int i, j, ret; /* check control parameters */ if (parm == NULL) glp_init_iptcp(&_parm), parm = &_parm; if (!(parm->msg_lev == GLP_MSG_OFF || parm->msg_lev == GLP_MSG_ERR || parm->msg_lev == GLP_MSG_ON || parm->msg_lev == GLP_MSG_ALL)) xerror("glp_interior: msg_lev = %d; invalid parameter\n", parm->msg_lev); if (!(parm->ord_alg == GLP_ORD_NONE || parm->ord_alg == GLP_ORD_QMD || parm->ord_alg == GLP_ORD_AMD || parm->ord_alg == GLP_ORD_SYMAMD)) xerror("glp_interior: ord_alg = %d; invalid parameter\n", parm->ord_alg); /* interior-point solution is currently undefined */ P->ipt_stat = GLP_UNDEF; P->ipt_obj = 0.0; /* check bounds of double-bounded variables */ for (i = 1; i <= P->m; i++) { row = P->row[i]; if (row->type == GLP_DB && row->lb >= row->ub) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_interior: row %d: lb = %g, ub = %g; incorre" "ct bounds\n", i, row->lb, row->ub); ret = GLP_EBOUND; goto done; } } for (j = 1; j <= P->n; j++) { col = P->col[j]; if (col->type == GLP_DB && col->lb >= col->ub) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_interior: column %d: lb = %g, ub = %g; inco" "rrect bounds\n", j, col->lb, col->ub); ret = GLP_EBOUND; goto done; } } /* transform LP to the standard formulation */ if (parm->msg_lev >= GLP_MSG_ALL) xprintf("Original LP has %d row(s), %d column(s), and %d non-z" "ero(s)\n", P->m, P->n, P->nnz); npp = npp_create_wksp(); npp_load_prob(npp, P, GLP_OFF, GLP_IPT, GLP_ON); transform(npp); prob = glp_create_prob(); npp_build_prob(npp, prob); if (parm->msg_lev >= GLP_MSG_ALL) xprintf("Working LP has %d row(s), %d column(s), and %d non-ze" "ro(s)\n", prob->m, prob->n, prob->nnz); #if 1 /* currently empty problem cannot be solved */ if (!(prob->m > 0 && prob->n > 0)) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_interior: unable to solve empty problem\n"); ret = GLP_EFAIL; goto done; } #endif /* scale the resultant LP */ { ENV *env = get_env_ptr(); int term_out = env->term_out; env->term_out = GLP_OFF; glp_scale_prob(prob, GLP_SF_EQ); env->term_out = term_out; } /* warn about dense columns */ if (parm->msg_lev >= GLP_MSG_ON && prob->m >= 200) { int len, cnt = 0; for (j = 1; j <= prob->n; j++) { len = glp_get_mat_col(prob, j, NULL, NULL); if ((double)len >= 0.20 * (double)prob->m) cnt++; } if (cnt == 1) xprintf("WARNING: PROBLEM HAS ONE DENSE COLUMN\n"); else if (cnt > 0) xprintf("WARNING: PROBLEM HAS %d DENSE COLUMNS\n", cnt); } /* solve the transformed LP */ ret = ipm_solve(prob, parm); /* postprocess solution from the transformed LP */ npp_postprocess(npp, prob); /* and store solution to the original LP */ npp_unload_sol(npp, P); done: /* free working program objects */ if (npp != NULL) npp_delete_wksp(npp); if (prob != NULL) glp_delete_prob(prob); /* return to the application program */ return ret; } /*********************************************************************** * NAME * * glp_init_iptcp - initialize interior-point solver control parameters * * SYNOPSIS * * void glp_init_iptcp(glp_iptcp *parm); * * DESCRIPTION * * The routine glp_init_iptcp initializes control parameters, which are * used by the interior-point solver, with default values. * * Default values of the control parameters are stored in the glp_iptcp * structure, which the parameter parm points to. */ void glp_init_iptcp(glp_iptcp *parm) { parm->msg_lev = GLP_MSG_ALL; parm->ord_alg = GLP_ORD_AMD; return; } /*********************************************************************** * NAME * * glp_ipt_status - retrieve status of interior-point solution * * SYNOPSIS * * int glp_ipt_status(glp_prob *lp); * * RETURNS * * The routine glp_ipt_status reports the status of solution found by * the interior-point solver as follows: * * GLP_UNDEF - interior-point solution is undefined; * GLP_OPT - interior-point solution is optimal; * GLP_INFEAS - interior-point solution is infeasible; * GLP_NOFEAS - no feasible solution exists. */ int glp_ipt_status(glp_prob *lp) { int ipt_stat = lp->ipt_stat; return ipt_stat; } /*********************************************************************** * NAME * * glp_ipt_obj_val - retrieve objective value (interior point) * * SYNOPSIS * * double glp_ipt_obj_val(glp_prob *lp); * * RETURNS * * The routine glp_ipt_obj_val returns value of the objective function * for interior-point solution. */ double glp_ipt_obj_val(glp_prob *lp) { /*struct LPXCPS *cps = lp->cps;*/ double z; z = lp->ipt_obj; /*if (cps->round && fabs(z) < 1e-9) z = 0.0;*/ return z; } /*********************************************************************** * NAME * * glp_ipt_row_prim - retrieve row primal value (interior point) * * SYNOPSIS * * double glp_ipt_row_prim(glp_prob *lp, int i); * * RETURNS * * The routine glp_ipt_row_prim returns primal value of the auxiliary * variable associated with i-th row. */ double glp_ipt_row_prim(glp_prob *lp, int i) { /*struct LPXCPS *cps = lp->cps;*/ double pval; if (!(1 <= i && i <= lp->m)) xerror("glp_ipt_row_prim: i = %d; row number out of range\n", i); pval = lp->row[i]->pval; /*if (cps->round && fabs(pval) < 1e-9) pval = 0.0;*/ return pval; } /*********************************************************************** * NAME * * glp_ipt_row_dual - retrieve row dual value (interior point) * * SYNOPSIS * * double glp_ipt_row_dual(glp_prob *lp, int i); * * RETURNS * * The routine glp_ipt_row_dual returns dual value (i.e. reduced cost) * of the auxiliary variable associated with i-th row. */ double glp_ipt_row_dual(glp_prob *lp, int i) { /*struct LPXCPS *cps = lp->cps;*/ double dval; if (!(1 <= i && i <= lp->m)) xerror("glp_ipt_row_dual: i = %d; row number out of range\n", i); dval = lp->row[i]->dval; /*if (cps->round && fabs(dval) < 1e-9) dval = 0.0;*/ return dval; } /*********************************************************************** * NAME * * glp_ipt_col_prim - retrieve column primal value (interior point) * * SYNOPSIS * * double glp_ipt_col_prim(glp_prob *lp, int j); * * RETURNS * * The routine glp_ipt_col_prim returns primal value of the structural * variable associated with j-th column. */ double glp_ipt_col_prim(glp_prob *lp, int j) { /*struct LPXCPS *cps = lp->cps;*/ double pval; if (!(1 <= j && j <= lp->n)) xerror("glp_ipt_col_prim: j = %d; column number out of range\n" , j); pval = lp->col[j]->pval; /*if (cps->round && fabs(pval) < 1e-9) pval = 0.0;*/ return pval; } /*********************************************************************** * NAME * * glp_ipt_col_dual - retrieve column dual value (interior point) * * SYNOPSIS * * #include "glplpx.h" * double glp_ipt_col_dual(glp_prob *lp, int j); * * RETURNS * * The routine glp_ipt_col_dual returns dual value (i.e. reduced cost) * of the structural variable associated with j-th column. */ double glp_ipt_col_dual(glp_prob *lp, int j) { /*struct LPXCPS *cps = lp->cps;*/ double dval; if (!(1 <= j && j <= lp->n)) xerror("glp_ipt_col_dual: j = %d; column number out of range\n" , j); dval = lp->col[j]->dval; /*if (cps->round && fabs(dval) < 1e-9) dval = 0.0;*/ return dval; } /* eof */ igraph/src/Triangle.h0000755000176000001440000000073512325527072014276 0ustar ripleyusers/** Triangle.h */ #ifndef TRIANGLE_H #define TRIANGLE_H #include "Shape.h" namespace igraph { class Triangle : public Shape { public: Triangle(); Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3); ~Triangle(); virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; private: Point mPoint1, mPoint2, mPoint3; }; } // namespace igraph #endif igraph/src/bliss_timer.hh0000644000176000001440000000172112325372072015204 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_TIMER_HH #define BLISS_TIMER_HH namespace igraph { class Timer { double start_time, end_time; public: Timer(); void start(); void stop(); double get_intermediate(); double get_duration(); }; } #endif igraph/src/bliss_eqrefhash.cc0000644000176000001440000001150212325527072016020 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #include "bliss_eqrefhash.hh" using namespace std; namespace igraph { /* * Random bits generated by * http://www.fourmilab.ch/hotbits/ */ const static unsigned int rtab[256] = { 0xAEAA35B8, 0x65632E16, 0x155EDBA9, 0x01349B39, 0x8EB8BD97, 0x8E4C5367, 0x8EA78B35, 0x2B1B4072, 0xC1163893, 0x269A8642, 0xC79D7F6D, 0x6A32DEA0, 0xD4D2DA56, 0xD96D4F47, 0x47B5F48A, 0x2587C6BF, 0x642B71D8, 0x5DBBAF58, 0x5C178169, 0xA16D9279, 0x75CDA063, 0x291BC48B, 0x01AC2F47, 0x5416DF7C, 0x45307514, 0xB3E1317B, 0xE1C7A8DE, 0x3ACDAC96, 0x11B96831, 0x32DE22DD, 0x6A1DA93B, 0x58B62381, 0x283810E2, 0xBC30E6A6, 0x8EE51705, 0xB06E8DFB, 0x729AB12A, 0xA9634922, 0x1A6E8525, 0x49DD4E19, 0xE5DB3D44, 0x8C5B3A02, 0xEBDE2864, 0xA9146D9F, 0x736D2CB4, 0xF5229F42, 0x712BA846, 0x20631593, 0x89C02603, 0xD5A5BF6A, 0x823F4E18, 0x5BE5DEFF, 0x1C4EBBFA, 0x5FAB8490, 0x6E559B0C, 0x1FE528D6, 0xB3198066, 0x4A965EB5, 0xFE8BB3D5, 0x4D2F6234, 0x5F125AA4, 0xBCC640FA, 0x4F8BC191, 0xA447E537, 0xAC474D3C, 0x703BFA2C, 0x617DC0E7, 0xF26299D7, 0xC90FD835, 0x33B71C7B, 0x6D83E138, 0xCBB1BB14, 0x029CF5FF, 0x7CBD093D, 0x4C9825EF, 0x845C4D6D, 0x124349A5, 0x53942D21, 0x800E60DA, 0x2BA6EB7F, 0xCEBF30D3, 0xEB18D449, 0xE281F724, 0x58B1CB09, 0xD469A13D, 0x9C7495C3, 0xE53A7810, 0xA866C08E, 0x832A038B, 0xDDDCA484, 0xD5FE0DDE, 0x0756002B, 0x2FF51342, 0x60FEC9C8, 0x061A53E3, 0x47B1884E, 0xDC17E461, 0xA17A6A37, 0x3158E7E2, 0xA40D873B, 0x45AE2140, 0xC8F36149, 0x63A4EE2D, 0xD7107447, 0x6F90994F, 0x5006770F, 0xC1F3CA9A, 0x91B317B2, 0xF61B4406, 0xA8C9EE8F, 0xC6939B75, 0xB28BBC3B, 0x36BF4AEF, 0x3B12118D, 0x4D536ECF, 0x9CF4B46B, 0xE8AB1E03, 0x8225A360, 0x7AE4A130, 0xC4EE8B50, 0x50651797, 0x5BB4C59F, 0xD120EE47, 0x24F3A386, 0xBE579B45, 0x3A378EFC, 0xC5AB007B, 0x3668942B, 0x2DBDCC3A, 0x6F37F64C, 0xC24F862A, 0xB6F97FCF, 0x9E4FA23D, 0x551AE769, 0x46A8A5A6, 0xDC1BCFDD, 0x8F684CF9, 0x501D811B, 0x84279F80, 0x2614E0AC, 0x86445276, 0xAEA0CE71, 0x0812250F, 0xB586D18A, 0xC68D721B, 0x44514E1D, 0x37CDB99A, 0x24731F89, 0xFA72E589, 0x81E6EBA2, 0x15452965, 0x55523D9D, 0x2DC47E14, 0x2E7FA107, 0xA7790F23, 0x40EBFDBB, 0x77E7906B, 0x6C1DB960, 0x1A8B9898, 0x65FA0D90, 0xED28B4D8, 0x34C3ED75, 0x768FD2EC, 0xFAB60BCB, 0x962C75F4, 0x304F0498, 0x0A41A36B, 0xF7DE2A4A, 0xF4770FE2, 0x73C93BBB, 0xD21C82C5, 0x6C387447, 0x8CDB4CB9, 0x2CC243E8, 0x41859E3D, 0xB667B9CB, 0x89681E8A, 0x61A0526C, 0x883EDDDC, 0x539DE9A4, 0xC29E1DEC, 0x97C71EC5, 0x4A560A66, 0xBD7ECACF, 0x576AE998, 0x31CE5616, 0x97172A6C, 0x83D047C4, 0x274EA9A8, 0xEB31A9DA, 0x327209B5, 0x14D1F2CB, 0x00FE1D96, 0x817DBE08, 0xD3E55AED, 0xF2D30AFC, 0xFB072660, 0x866687D6, 0x92552EB9, 0xEA8219CD, 0xF7927269, 0xF1948483, 0x694C1DF5, 0xB7D8B7BF, 0xFFBC5D2F, 0x2E88B849, 0x883FD32B, 0xA0331192, 0x8CB244DF, 0x41FAF895, 0x16902220, 0x97FB512A, 0x2BEA3CC4, 0xAF9CAE61, 0x41ACD0D5, 0xFD2F28FF, 0xE780ADFA, 0xB3A3A76E, 0x7112AD87, 0x7C3D6058, 0x69E64FFF, 0xE5F8617C, 0x8580727C, 0x41F54F04, 0xD72BE498, 0x653D1795, 0x1275A327, 0x14B499D4, 0x4E34D553, 0x4687AA39, 0x68B64292, 0x5C18ABC3, 0x41EABFCC, 0x92A85616, 0x82684CF8, 0x5B9F8A4E, 0x35382FFE, 0xFB936318, 0x52C08E15, 0x80918B2E, 0x199EDEE0, 0xA9470163, 0xEC44ACDD, 0x612D6735, 0x8F88EA7D, 0x759F5EA4, 0xE5CC7240, 0x68CFEB8B, 0x04725601, 0x0C22C23E, 0x5BC97174, 0x89965841, 0x5D939479, 0x690F338A, 0x3C2D4380, 0xDAE97F2B }; void BuzzHash::update(unsigned int i) { i++; while(i > 0) { h ^= rtab[i & 0xff]; const unsigned int b = h & 0x80000000; h = h << 1; if(b != 0) h++; i = i >> 8; } } int BuzzHash::cmp(const BuzzHash &other) { if(h < other.h) return -1; if(h == other.h) return 0; return 1; } int PerfectHash::cmp(const PerfectHash &other) { if(h.size() < other.h.size()) return -1; if(h.size() > other.h.size()) return 1; std::vector::const_iterator i1 = h.begin(); std::vector::const_iterator i2 = other.h.begin(); while(i1 != h.end()) { const unsigned int v1 = *i1; const unsigned int v2 = *i2; if(v1 < v2) return -1; if(v1 > v2) return 1; i1++; i2++; } return 0; } } igraph/src/glet.c0000644000176000001440000006732212325527073013462 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2013 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_graphlets.h" #include "igraph_memory.h" #include "igraph_constructors.h" #include "igraph_cliques.h" #include "igraph_structural.h" #include "igraph_qsort.h" #include "igraph_conversion.h" /** * \section graphlets_intro Introduction * * * Graphlet decomposition models a weighted undirected graph * via the union of potentially overlapping dense social groups. * This is done by a two-step algorithm. In the first step a candidate * set of groups (a candidate basis) is created by finding cliques * if the thresholded input graph. In the second step these * the graph is projected on the candidate basis, resulting a * weight coefficient for each clique in the candidate basis. * * * * igraph contains three functions for performing the graph * decomponsition of a graph. The first is \ref igraph_graphlets(), which * performed both steps on the method and returns a list of subgraphs, * with their corresponding weights. The second and third functions * correspond to the first and second steps of the algorithm, and they are * useful if the user wishes to perform them individually: * \ref igraph_graphlets_candidate_basis() and * \ref igraph_graphlets_project(). * */ typedef struct { igraph_vector_int_t *newidvectors; igraph_t *newgraphs; igraph_vector_t *newweights; int nc; } igraph_i_subclique_next_free_t; void igraph_i_subclique_next_free(void *ptr) { igraph_i_subclique_next_free_t *data=ptr; int i; if (data->newidvectors) { for (i=0; inc; i++) { if (data->newidvectors+i) { igraph_vector_int_destroy(data->newidvectors+i); } } igraph_Free(data->newidvectors); } if (data->newgraphs) { for (i=0; inc; i++) { if (data->newgraphs+i) { igraph_destroy(data->newgraphs+i); } } igraph_Free(data->newgraphs); } if (data->newweights) { for (i=0; inc; i++) { if (data->newweights+i) { igraph_vector_destroy(data->newweights+i); } } igraph_Free(data->newweights); } } /** * \function igraph_subclique_next * Calculate subcliques of the cliques found at the previous level * * \param graph Input graph. * \param weight Edge weights. * \param ids The ids of the vertices in the input graph. * \param cliques A list of vectors, vertex ids for cliques. * \param result The result is stored here, a list of graphs is stored * here. * \param resultids The ids of the vertices in the result graphs is * stored here. * \param clique_thr The thresholds for the cliques are stored here, * if not a null pointer. * \param next_thr The next thresholds for the cliques are stored * here, if not a null pointer. * */ int igraph_subclique_next(const igraph_t *graph, const igraph_vector_t *weights, const igraph_vector_int_t *ids, const igraph_vector_ptr_t *cliques, igraph_vector_ptr_t *result, igraph_vector_ptr_t *resultweights, igraph_vector_ptr_t *resultids, igraph_vector_t *clique_thr, igraph_vector_t *next_thr) { /* The input is a set of cliques, that were found at a previous level. For each clique, we calculate the next threshold, drop the isolate vertices, and create a new graph from them. */ igraph_vector_int_t mark, map; igraph_vector_int_t edges; igraph_vector_t neis, newedges; igraph_integer_t c, nc=igraph_vector_ptr_size(cliques); igraph_integer_t no_of_nodes=igraph_vcount(graph); igraph_integer_t no_of_edges=igraph_ecount(graph); igraph_vector_int_t *newidvectors=0; igraph_t *newgraphs=0; igraph_vector_t *newweights=0; igraph_i_subclique_next_free_t freedata={ newidvectors, newgraphs, newweights, nc }; if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL); } if (igraph_vector_int_size(ids) != no_of_nodes) { IGRAPH_ERROR("Invalid length of ID vector", IGRAPH_EINVAL); } if (igraph_vector_ptr_size(result) != nc) { IGRAPH_ERROR("Invalid graph list size", IGRAPH_EINVAL); } if (igraph_vector_ptr_size(resultweights) != nc) { IGRAPH_ERROR("Invalid weight list size", IGRAPH_EINVAL); } if (igraph_vector_ptr_size(resultids) != nc) { IGRAPH_ERROR("Invalid id vector size", IGRAPH_EINVAL); } IGRAPH_FINALLY(igraph_i_subclique_next_free, &freedata); newidvectors=igraph_Calloc(nc, igraph_vector_int_t); if (!newidvectors) { IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); } freedata.newidvectors = newidvectors; newweights=igraph_Calloc(nc, igraph_vector_t); if (!newweights) { IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); } freedata.newweights = newweights; newgraphs=igraph_Calloc(nc, igraph_t); if (!newgraphs) { IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); } freedata.newgraphs = newgraphs; igraph_vector_init(&newedges, 100); IGRAPH_FINALLY(igraph_vector_destroy, &newedges); igraph_vector_int_init(&mark, no_of_nodes); IGRAPH_FINALLY(igraph_vector_destroy, &mark); igraph_vector_int_init(&map, no_of_nodes); IGRAPH_FINALLY(igraph_vector_destroy, &map); igraph_vector_int_init(&edges, 100); IGRAPH_FINALLY(igraph_vector_int_destroy, &edges); igraph_vector_init(&neis, 10); IGRAPH_FINALLY(igraph_vector_destroy, &neis); if (clique_thr) { igraph_vector_resize(clique_thr, nc); } if (next_thr) { igraph_vector_resize(next_thr, nc); } /* Iterate over all cliques. We will create graphs for all subgraphs defined by the cliques. */ for (c=0; c minweight && w < nextweight) { nextweight=w; } } } } /* v < clsize */ /* --------------------------------------------------- */ /* OK, we have stored the edges and found the weight of the clique and the next weight to consider */ if (clique_thr) { VECTOR(*clique_thr)[c] = minweight; } if (next_thr) { VECTOR(*next_thr )[c] = nextweight; } /* --------------------------------------------------- */ /* Now we create the subgraph from the edges above the next threshold, and their incident vertices. */ igraph_vector_int_init(newids, 0); VECTOR(*resultids)[c] = newids; igraph_vector_init(neww, 0); VECTOR(*resultweights)[c] = neww; /* We use mark[] to denote the vertices already mapped to the new graph. If this is -(c+1), then the vertex was mapped, otherwise it was not. The mapping itself is in map[]. */ noe=igraph_vector_int_size(&edges); for (e=0; e= nextweight) { if (VECTOR(mark)[from] == c+1) { VECTOR(map)[from] = nov++; VECTOR(mark)[from] = -(c+1); igraph_vector_int_push_back(newids, VECTOR(*ids)[from]); } if (VECTOR(mark)[to] == c+1) { VECTOR(map)[to] = nov++; VECTOR(mark)[to] = -(c+1); igraph_vector_int_push_back(newids, VECTOR(*ids)[to]); } igraph_vector_push_back(neww, w); igraph_vector_push_back(&newedges, VECTOR(map)[from]); igraph_vector_push_back(&newedges, VECTOR(map)[to]); } } igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED); VECTOR(*result)[c] = newgraph; /* --------------------------------------------------- */ } /* c < nc */ igraph_vector_destroy(&neis); igraph_vector_int_destroy(&edges); igraph_vector_int_destroy(&mark); igraph_vector_int_destroy(&map); igraph_vector_destroy(&newedges); IGRAPH_FINALLY_CLEAN(6); /* +1 for the result */ return 0; } void igraph_i_graphlets_destroy_vectorlist(igraph_vector_ptr_t *vl) { int i, n=igraph_vector_ptr_size(vl); for (i=0; i= startthr) { IGRAPH_CHECK(igraph_vector_push_back(&subv, i)); } } igraph_subgraph_edges(graph, &subg, igraph_ess_vector(&subv), /*delete_vertices=*/ 0); igraph_maximal_cliques(&subg, &mycliques, /*min_size=*/ 0, /*max_size=*/ 0); nocliques=igraph_vector_ptr_size(&mycliques); igraph_vector_destroy(&subv); IGRAPH_FINALLY_CLEAN(1); /* Get the next cliques and thresholds */ igraph_vector_ptr_init(&newgraphs, nocliques); IGRAPH_FINALLY(igraph_i_graphlets_destroy_graphlist, &newgraphs); igraph_vector_ptr_init(&newweights, nocliques); IGRAPH_FINALLY(igraph_i_graphlets_destroy_vectorlist, &newweights); igraph_vector_ptr_init(&newids, nocliques); IGRAPH_FINALLY(igraph_i_graphlets_destroy_intvectorlist, &newids); IGRAPH_VECTOR_INIT_FINALLY(&next_thr, 0); IGRAPH_VECTOR_INIT_FINALLY(&clique_thr, 0); igraph_subclique_next(graph, weights, ids, &mycliques, &newgraphs, &newweights, &newids, &clique_thr, &next_thr); /* Store cliques at the current level */ igraph_vector_append(thresholds, &clique_thr); for (i=0; i 1) { igraph_vector_t *w=VECTOR(newweights)[i]; igraph_vector_int_t *ids=VECTOR(newids)[i]; igraph_i_graphlets(g, w, cliques, thresholds, ids, VECTOR(next_thr)[i]); } } igraph_vector_destroy(&clique_thr); igraph_vector_destroy(&next_thr); igraph_i_graphlets_destroy_intvectorlist(&newids); igraph_i_graphlets_destroy_vectorlist(&newweights); igraph_i_graphlets_destroy_graphlist(&newgraphs); igraph_vector_ptr_destroy(&mycliques); /* contents was copied over */ IGRAPH_FINALLY_CLEAN(6); return 0; } typedef struct { const igraph_vector_ptr_t *cliques; const igraph_vector_t *thresholds; } igraph_i_graphlets_filter_t; int igraph_i_graphlets_filter_cmp(void *data, const void *a, const void *b) { igraph_i_graphlets_filter_t *ddata=(igraph_i_graphlets_filter_t *) data; int *aa=(int*) a; int *bb=(int*) b; igraph_real_t t_a=VECTOR(*ddata->thresholds)[*aa]; igraph_real_t t_b=VECTOR(*ddata->thresholds)[*bb]; igraph_vector_t *v_a, *v_b; int s_a, s_b; if (t_a < t_b) { return -1; } else if (t_a > t_b) { return 1; } v_a=(igraph_vector_t*) VECTOR(*ddata->cliques)[*aa]; v_b=(igraph_vector_t*) VECTOR(*ddata->cliques)[*bb]; s_a=igraph_vector_size(v_a); s_b=igraph_vector_size(v_b); if (s_a < s_b) { return -1; } else if (s_a > s_b) { return 1; } else { return 0; } } int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, igraph_vector_t *thresholds) { /* Filter out non-maximal cliques. Every non-maximal clique is part of a maximal clique, at the same threshold. First we order the cliques, according to their threshold, and then according to their size. So when we look for a candidate superset, we only need to check the cliques next in the list, until their threshold is different. */ int i, iptr, nocliques=igraph_vector_ptr_size(cliques); igraph_vector_int_t order; igraph_i_graphlets_filter_t sortdata = { cliques, thresholds }; igraph_vector_int_init(&order, nocliques); IGRAPH_FINALLY(igraph_vector_int_destroy, &order); for (i=0; i n_j) { continue; } /* Check if hay is a superset */ while (pi < n_i && pj < n_j && n_i-pi <= n_j-pj) { int ei=VECTOR(*needle)[pi]; int ej=VECTOR(*hay)[pj]; if (ei < ej) { break; } else if (ei > ej) { pj++; } else { pi++; pj++; } } if (pi == n_i) { /* Found, delete immediately */ igraph_vector_destroy(needle); VECTOR(*cliques)[ri]=0; break; } } } /* Remove null pointers from the list of cliques */ for (i=0, iptr=0; iMu)[*aa]; igraph_real_t Mu_b = VECTOR(*ddata->Mu)[*bb]; if (Mu_a < Mu_b) { return 1; } else if (Mu_a > Mu_b) { return -1; } else { return 0; } } /** * \function igraph_graphlets * Calculate graphlets basis and project the graph on it * * This function simply calls \ref igraph_graphlets_candidate_basis() * and \ref igraph_graphlets_project(), and then orders the graphlets * according to decreasing weights. * \param graph The input graph, it must be a simple graph, edge directions are * ignored. * \param weights Weights of the edges, a vector. * \param cliques An initialized vector of pointers. * The graphlet basis is stored here. Each element of the pointer * vector will be a vector of vertex ids. * \param Mu An initialized vector, the weights of the graphlets will * be stored here. * \param niter Integer scalar, the number of iterations to perform * for the projection step. * \return Error code. * * See also: \ref igraph_graphlets_candidate_basis() and * \ref igraph_graphlets_project(). */ int igraph_graphlets(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_ptr_t *cliques, igraph_vector_t *Mu, int niter) { int i, nocliques; igraph_vector_t thresholds; igraph_vector_int_t order; igraph_i_graphlets_order_t sortdata={ cliques, Mu }; igraph_vector_init(&thresholds, 0); IGRAPH_FINALLY(igraph_vector_destroy, &thresholds); igraph_graphlets_candidate_basis(graph, weights, cliques, &thresholds); igraph_vector_destroy(&thresholds); IGRAPH_FINALLY_CLEAN(1); igraph_graphlets_project(graph, weights, cliques, Mu, /*startMu=*/ 0, niter); nocliques=igraph_vector_ptr_size(cliques); igraph_vector_int_init(&order, nocliques); IGRAPH_FINALLY(igraph_vector_int_destroy, &order); for (i=0; i. * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpqmd.h" /*********************************************************************** * NAME * * genqmd - GENeral Quotient Minimum Degree algorithm * * SYNOPSIS * * #include "glpqmd.h" * void genqmd(int *neqns, int xadj[], int adjncy[], int perm[], * int invp[], int deg[], int marker[], int rchset[], int nbrhd[], * int qsize[], int qlink[], int *nofsub); * * PURPOSE * * This routine implements the minimum degree algorithm. It makes use * of the implicit representation of the elimination graph by quotient * graphs, and the notion of indistinguishable nodes. * * CAUTION * * The adjancy vector adjncy will be destroyed. * * INPUT PARAMETERS * * neqns - number of equations; * (xadj, adjncy) - * the adjancy structure. * * OUTPUT PARAMETERS * * perm - the minimum degree ordering; * invp - the inverse of perm. * * WORKING PARAMETERS * * deg - the degree vector. deg[i] is negative means node i has been * numbered; * marker - a marker vector, where marker[i] is negative means node i * has been merged with another nodeand thus can be ignored; * rchset - vector used for the reachable set; * nbrhd - vector used for neighborhood set; * qsize - vector used to store the size of indistinguishable * supernodes; * qlink - vector used to store indistinguishable nodes, i, qlink[i], * qlink[qlink[i]], ... are the members of the supernode * represented by i. * * PROGRAM SUBROUTINES * * qmdrch, qmdqt, qmdupd. ***********************************************************************/ void genqmd(int *_neqns, int xadj[], int adjncy[], int perm[], int invp[], int deg[], int marker[], int rchset[], int nbrhd[], int qsize[], int qlink[], int *_nofsub) { int inode, ip, irch, j, mindeg, ndeg, nhdsze, node, np, num, nump1, nxnode, rchsze, search, thresh; # define neqns (*_neqns) # define nofsub (*_nofsub) /* Initialize degree vector and other working variables. */ mindeg = neqns; nofsub = 0; for (node = 1; node <= neqns; node++) { perm[node] = node; invp[node] = node; marker[node] = 0; qsize[node] = 1; qlink[node] = 0; ndeg = xadj[node+1] - xadj[node]; deg[node] = ndeg; if (ndeg < mindeg) mindeg = ndeg; } num = 0; /* Perform threshold search to get a node of min degree. Variable search point to where search should start. */ s200: search = 1; thresh = mindeg; mindeg = neqns; s300: nump1 = num + 1; if (nump1 > search) search = nump1; for (j = search; j <= neqns; j++) { node = perm[j]; if (marker[node] >= 0) { ndeg = deg[node]; if (ndeg <= thresh) goto s500; if (ndeg < mindeg) mindeg = ndeg; } } goto s200; /* Node has minimum degree. Find its reachable sets by calling qmdrch. */ s500: search = j; nofsub += deg[node]; marker[node] = 1; qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, &nhdsze, nbrhd); /* Eliminate all nodes indistinguishable from node. They are given by node, qlink[node], ... . */ nxnode = node; s600: num++; np = invp[nxnode]; ip = perm[num]; perm[np] = ip; invp[ip] = np; perm[num] = nxnode; invp[nxnode] = num; deg[nxnode] = -1; nxnode = qlink[nxnode]; if (nxnode > 0) goto s600; if (rchsze > 0) { /* Update the degrees of the nodes in the reachable set and identify indistinguishable nodes. */ qmdupd(xadj, adjncy, &rchsze, rchset, deg, qsize, qlink, marker, &rchset[rchsze+1], &nbrhd[nhdsze+1]); /* Reset marker value of nodes in reach set. Update threshold value for cyclic search. Also call qmdqt to form new quotient graph. */ marker[node] = 0; for (irch = 1; irch <= rchsze; irch++) { inode = rchset[irch]; if (marker[inode] >= 0) { marker[inode] = 0; ndeg = deg[inode]; if (ndeg < mindeg) mindeg = ndeg; if (ndeg <= thresh) { mindeg = thresh; thresh = ndeg; search = invp[inode]; } } } if (nhdsze > 0) qmdqt(&node, xadj, adjncy, marker, &rchsze, rchset, nbrhd); } if (num < neqns) goto s300; return; # undef neqns # undef nofsub } /*********************************************************************** * NAME * * qmdrch - Quotient MD ReaCHable set * * SYNOPSIS * * #include "glpqmd.h" * void qmdrch(int *root, int xadj[], int adjncy[], int deg[], * int marker[], int *rchsze, int rchset[], int *nhdsze, * int nbrhd[]); * * PURPOSE * * This subroutine determines the reachable set of a node through a * given subset. The adjancy structure is assumed to be stored in a * quotient graph format. * * INPUT PARAMETERS * * root - the given node not in the subset; * (xadj, adjncy) - * the adjancy structure pair; * deg - the degree vector. deg[i] < 0 means the node belongs to the * given subset. * * OUTPUT PARAMETERS * * (rchsze, rchset) - * the reachable set; * (nhdsze, nbrhd) - * the neighborhood set. * * UPDATED PARAMETERS * * marker - the marker vector for reach and nbrhd sets. > 0 means the * node is in reach set. < 0 means the node has been merged * with others in the quotient or it is in nbrhd set. ***********************************************************************/ void qmdrch(int *_root, int xadj[], int adjncy[], int deg[], int marker[], int *_rchsze, int rchset[], int *_nhdsze, int nbrhd[]) { int i, istop, istrt, j, jstop, jstrt, nabor, node; # define root (*_root) # define rchsze (*_rchsze) # define nhdsze (*_nhdsze) /* Loop through the neighbors of root in the quotient graph. */ nhdsze = 0; rchsze = 0; istrt = xadj[root]; istop = xadj[root+1] - 1; if (istop < istrt) return; for (i = istrt; i <= istop; i++) { nabor = adjncy[i]; if (nabor == 0) return; if (marker[nabor] == 0) { if (deg[nabor] >= 0) { /* Include nabor into the reachable set. */ rchsze++; rchset[rchsze] = nabor; marker[nabor] = 1; goto s600; } /* nabor has been eliminated. Find nodes reachable from it. */ marker[nabor] = -1; nhdsze++; nbrhd[nhdsze] = nabor; s300: jstrt = xadj[nabor]; jstop = xadj[nabor+1] - 1; for (j = jstrt; j <= jstop; j++) { node = adjncy[j]; nabor = - node; if (node < 0) goto s300; if (node == 0) goto s600; if (marker[node] == 0) { rchsze++; rchset[rchsze] = node; marker[node] = 1; } } } s600: ; } return; # undef root # undef rchsze # undef nhdsze } /*********************************************************************** * NAME * * qmdqt - Quotient MD Quotient graph Transformation * * SYNOPSIS * * #include "glpqmd.h" * void qmdqt(int *root, int xadj[], int adjncy[], int marker[], * int *rchsze, int rchset[], int nbrhd[]); * * PURPOSE * * This subroutine performs the quotient graph transformation after a * node has been eliminated. * * INPUT PARAMETERS * * root - the node just eliminated. It becomes the representative of * the new supernode; * (xadj, adjncy) - * the adjancy structure; * (rchsze, rchset) - * the reachable set of root in the old quotient graph; * nbrhd - the neighborhood set which will be merged with root to form * the new supernode; * marker - the marker vector. * * UPDATED PARAMETERS * * adjncy - becomes the adjncy of the quotient graph. ***********************************************************************/ void qmdqt(int *_root, int xadj[], int adjncy[], int marker[], int *_rchsze, int rchset[], int nbrhd[]) { int inhd, irch, j, jstop, jstrt, link, nabor, node; # define root (*_root) # define rchsze (*_rchsze) irch = 0; inhd = 0; node = root; s100: jstrt = xadj[node]; jstop = xadj[node+1] - 2; if (jstop >= jstrt) { /* Place reach nodes into the adjacent list of node. */ for (j = jstrt; j <= jstop; j++) { irch++; adjncy[j] = rchset[irch]; if (irch >= rchsze) goto s400; } } /* Link to other space provided by the nbrhd set. */ link = adjncy[jstop+1]; node = - link; if (link >= 0) { inhd++; node = nbrhd[inhd]; adjncy[jstop+1] = - node; } goto s100; /* All reachable nodes have been saved. End the adjacent list. Add root to the neighborhood list of each node in the reach set. */ s400: adjncy[j+1] = 0; for (irch = 1; irch <= rchsze; irch++) { node = rchset[irch]; if (marker[node] >= 0) { jstrt = xadj[node]; jstop = xadj[node+1] - 1; for (j = jstrt; j <= jstop; j++) { nabor = adjncy[j]; if (marker[nabor] < 0) { adjncy[j] = root; goto s600; } } } s600: ; } return; # undef root # undef rchsze } /*********************************************************************** * NAME * * qmdupd - Quotient MD UPDate * * SYNOPSIS * * #include "glpqmd.h" * void qmdupd(int xadj[], int adjncy[], int *nlist, int list[], * int deg[], int qsize[], int qlink[], int marker[], int rchset[], * int nbrhd[]); * * PURPOSE * * This routine performs degree update for a set of nodes in the minimum * degree algorithm. * * INPUT PARAMETERS * * (xadj, adjncy) - * the adjancy structure; * (nlist, list) - * the list of nodes whose degree has to be updated. * * UPDATED PARAMETERS * * deg - the degree vector; * qsize - size of indistinguishable supernodes; * qlink - linked list for indistinguishable nodes; * marker - used to mark those nodes in reach/nbrhd sets. * * WORKING PARAMETERS * * rchset - the reachable set; * nbrhd - the neighborhood set. * * PROGRAM SUBROUTINES * * qmdmrg. ***********************************************************************/ void qmdupd(int xadj[], int adjncy[], int *_nlist, int list[], int deg[], int qsize[], int qlink[], int marker[], int rchset[], int nbrhd[]) { int deg0, deg1, il, inhd, inode, irch, j, jstop, jstrt, mark, nabor, nhdsze, node, rchsze; # define nlist (*_nlist) /* Find all eliminated supernodes that are adjacent to some nodes in the given list. Put them into (nhdsze, nbrhd). deg0 contains the number of nodes in the list. */ if (nlist <= 0) return; deg0 = 0; nhdsze = 0; for (il = 1; il <= nlist; il++) { node = list[il]; deg0 += qsize[node]; jstrt = xadj[node]; jstop = xadj[node+1] - 1; for (j = jstrt; j <= jstop; j++) { nabor = adjncy[j]; if (marker[nabor] == 0 && deg[nabor] < 0) { marker[nabor] = -1; nhdsze++; nbrhd[nhdsze] = nabor; } } } /* Merge indistinguishable nodes in the list by calling the subroutine qmdmrg. */ if (nhdsze > 0) qmdmrg(xadj, adjncy, deg, qsize, qlink, marker, °0, &nhdsze, nbrhd, rchset, &nbrhd[nhdsze+1]); /* Find the new degrees of the nodes that have not been merged. */ for (il = 1; il <= nlist; il++) { node = list[il]; mark = marker[node]; if (mark == 0 || mark == 1) { marker[node] = 2; qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, &nhdsze, nbrhd); deg1 = deg0; if (rchsze > 0) { for (irch = 1; irch <= rchsze; irch++) { inode = rchset[irch]; deg1 += qsize[inode]; marker[inode] = 0; } } deg[node] = deg1 - 1; if (nhdsze > 0) { for (inhd = 1; inhd <= nhdsze; inhd++) { inode = nbrhd[inhd]; marker[inode] = 0; } } } } return; # undef nlist } /*********************************************************************** * NAME * * qmdmrg - Quotient MD MeRGe * * SYNOPSIS * * #include "qmdmrg.h" * void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], * int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[], * int rchset[], int ovrlp[]); * * PURPOSE * * This routine merges indistinguishable nodes in the minimum degree * ordering algorithm. It also computes the new degrees of these new * supernodes. * * INPUT PARAMETERS * * (xadj, adjncy) - * the adjancy structure; * deg0 - the number of nodes in the given set; * (nhdsze, nbrhd) - * the set of eliminated supernodes adjacent to some nodes in * the set. * * UPDATED PARAMETERS * * deg - the degree vector; * qsize - size of indistinguishable nodes; * qlink - linked list for indistinguishable nodes; * marker - the given set is given by those nodes with marker value set * to 1. Those nodes with degree updated will have marker value * set to 2. * * WORKING PARAMETERS * * rchset - the reachable set; * ovrlp - temp vector to store the intersection of two reachable sets. ***********************************************************************/ void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], int qlink[], int marker[], int *_deg0, int *_nhdsze, int nbrhd[], int rchset[], int ovrlp[]) { int deg1, head, inhd, iov, irch, j, jstop, jstrt, link, lnode, mark, mrgsze, nabor, node, novrlp, rchsze, root; # define deg0 (*_deg0) # define nhdsze (*_nhdsze) /* Initialization. */ if (nhdsze <= 0) return; for (inhd = 1; inhd <= nhdsze; inhd++) { root = nbrhd[inhd]; marker[root] = 0; } /* Loop through each eliminated supernode in the set (nhdsze, nbrhd). */ for (inhd = 1; inhd <= nhdsze; inhd++) { root = nbrhd[inhd]; marker[root] = -1; rchsze = 0; novrlp = 0; deg1 = 0; s200: jstrt = xadj[root]; jstop = xadj[root+1] - 1; /* Determine the reachable set and its intersection with the input reachable set. */ for (j = jstrt; j <= jstop; j++) { nabor = adjncy[j]; root = - nabor; if (nabor < 0) goto s200; if (nabor == 0) break; mark = marker[nabor]; if (mark == 0) { rchsze++; rchset[rchsze] = nabor; deg1 += qsize[nabor]; marker[nabor] = 1; } else if (mark == 1) { novrlp++; ovrlp[novrlp] = nabor; marker[nabor] = 2; } } /* From the overlapped set, determine the nodes that can be merged together. */ head = 0; mrgsze = 0; for (iov = 1; iov <= novrlp; iov++) { node = ovrlp[iov]; jstrt = xadj[node]; jstop = xadj[node+1] - 1; for (j = jstrt; j <= jstop; j++) { nabor = adjncy[j]; if (marker[nabor] == 0) { marker[node] = 1; goto s1100; } } /* Node belongs to the new merged supernode. Update the vectors qlink and qsize. */ mrgsze += qsize[node]; marker[node] = -1; lnode = node; s900: link = qlink[lnode]; if (link > 0) { lnode = link; goto s900; } qlink[lnode] = head; head = node; s1100: ; } if (head > 0) { qsize[head] = mrgsze; deg[head] = deg0 + deg1 - 1; marker[head] = 2; } /* Reset marker values. */ root = nbrhd[inhd]; marker[root] = 0; if (rchsze > 0) { for (irch = 1; irch <= rchsze; irch++) { node = rchset[irch]; marker[node] = 0; } } } return; # undef deg0 # undef nhdsze } /* eof */ igraph/src/fast_community.c0000644000176000001440000010563412325527073015567 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_community.h" #include "igraph_memory.h" #include "igraph_iterators.h" #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_interrupt_internal.h" #include "igraph_structural.h" #include "igraph_vector_ptr.h" #include "config.h" /* #define IGRAPH_FASTCOMM_DEBUG */ #ifdef _MSC_VER /* MSVC does not support variadic macros */ #include void debug(const char* fmt, ...) { va_list args; va_start(args, fmt); #ifdef IGRAPH_FASTCOMM_DEBUG vfprintf(stderr, fmt, args); #endif va_end(args); } #else # ifdef IGRAPH_FASTCOMM_DEBUG # define debug(...) fprintf(stderr, __VA_ARGS__) # else # define debug(...) # endif #endif /* * Implementation of the community structure algorithm originally published * by Clauset et al in: * * A. Clauset, M.E.J. Newman and C. Moore, "Finding community structure in * very large networks.". Phys. Rev. E 70, 066111 (2004). * * The data structures being used are slightly different and they are described * most closely in: * * K. Wakita, T. Tsurumi, "Finding community structure in mega-scale social * networks.". arXiv:cs/0702048v1. * * We maintain a vector of communities, each of which containing a list of * pointers to their neighboring communities along with the increase in the * modularity score that could be achieved by joining the two communities. * Each community has a pointer to one of its neighbors - the one which would * result in the highest increase in modularity after a join. The local * (community-level) maximums are also stored in an indexed max-heap. The * max-heap itself stores its elements in an array which satisfies the heap * property, but to allow us to access any of the elements in the array based * on the community index (and not based on the array index - which depends on * the element's actual position in the heap), we also maintain an index * vector in the heap: the ith element of the index vector contains the * position of community i in the array of the max-heap. When we perform * sifting operations on the heap to restore the heap property, we also maintain * the index vector. */ /* Structure storing a pair of communities along with their dQ values */ typedef struct s_igraph_i_fastgreedy_commpair { long int first; /* first member of the community pair */ long int second; /* second member of the community pair */ igraph_real_t *dq; /* pointer to a member of the dq vector storing the */ /* increase in modularity achieved when joining */ struct s_igraph_i_fastgreedy_commpair *opposite; } igraph_i_fastgreedy_commpair; /* Structure storing a community */ typedef struct { igraph_integer_t id; /* Identifier of the community (for merges matrix) */ igraph_integer_t size; /* Size of the community */ igraph_vector_ptr_t neis; /* references to neighboring communities */ igraph_i_fastgreedy_commpair* maxdq; /* community pair with maximal dq */ } igraph_i_fastgreedy_community; /* Global community list structure */ typedef struct { long int no_of_communities, n; /* number of communities, number of vertices */ igraph_i_fastgreedy_community* e; /* list of communities */ igraph_i_fastgreedy_community** heap; /* heap of communities */ igraph_integer_t *heapindex; /* heap index to speed up lookup by community idx */ } igraph_i_fastgreedy_community_list; /* Scans the community neighborhood list for the new maximal dq value. * Returns 1 if the maximum is different from the previous one, * 0 otherwise. */ int igraph_i_fastgreedy_community_rescan_max( igraph_i_fastgreedy_community* comm) { long int i, n; igraph_i_fastgreedy_commpair *p, *oldmax; n = igraph_vector_ptr_size(&comm->neis); if (n==0) { comm->maxdq = 0; return 1; } oldmax = comm->maxdq; comm->maxdq = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[0]; for (i=1; ineis)[i]; if (*p->dq > *comm->maxdq->dq) comm->maxdq=p; } if (oldmax == comm->maxdq) return 0; return 1; } /* Destroys the global community list object */ void igraph_i_fastgreedy_community_list_destroy( igraph_i_fastgreedy_community_list* list) { long int i; for (i=0; in; i++) { igraph_vector_ptr_destroy(&list->e[i].neis); } free(list->e); if (list->heapindex != 0) free(list->heapindex); if (list->heap != 0) free(list->heap); } /* Community list heap maintenance: sift down */ void igraph_i_fastgreedy_community_list_sift_down( igraph_i_fastgreedy_community_list* list, long int idx) { long int root, child, c1, c2; igraph_i_fastgreedy_community* dummy; igraph_integer_t dummy2; root = idx; while (root*2+1 < list->no_of_communities) { child = root*2+1; if (child+1 < list->no_of_communities && *list->heap[child]->maxdq->dq < *list->heap[child+1]->maxdq->dq) child++; if (*list->heap[root]->maxdq->dq < *list->heap[child]->maxdq->dq) { c1 = list->heap[root]->maxdq->first; c2 = list->heap[child]->maxdq->first; dummy = list->heap[root]; list->heap[root] = list->heap[child]; list->heap[child] = dummy; dummy2 = list->heapindex[c1]; list->heapindex[c1] = list->heapindex[c2]; list->heapindex[c2] = dummy2; root = child; } else break; } } /* Community list heap maintenance: sift up */ void igraph_i_fastgreedy_community_list_sift_up( igraph_i_fastgreedy_community_list* list, long int idx) { long int root, parent, c1, c2; igraph_i_fastgreedy_community* dummy; igraph_integer_t dummy2; root = idx; while (root>0) { parent = (root-1)/2; if (*list->heap[parent]->maxdq->dq < *list->heap[root]->maxdq->dq) { c1 = list->heap[root]->maxdq->first; c2 = list->heap[parent]->maxdq->first; dummy = list->heap[parent]; list->heap[parent] = list->heap[root]; list->heap[root] = dummy; dummy2 = list->heapindex[c1]; list->heapindex[c1] = list->heapindex[c2]; list->heapindex[c2] = dummy2; root = parent; } else break; } } /* Builds the community heap for the first time */ void igraph_i_fastgreedy_community_list_build_heap( igraph_i_fastgreedy_community_list* list) { long int i; for (i=list->no_of_communities/2-1; i>=0; i--) igraph_i_fastgreedy_community_list_sift_down(list, i); } /* Finds the element belonging to a given community in the heap and return its * index in the heap array */ #define igraph_i_fastgreedy_community_list_find_in_heap(list, idx) (list)->heapindex[idx] /* Dumps the heap - for debugging purposes */ void igraph_i_fastgreedy_community_list_dump_heap( igraph_i_fastgreedy_community_list* list) { long int i; debug("Heap:\n"); for (i=0; ino_of_communities; i++) { debug("(%ld, %p, %p)", i, list->heap[i], list->heap[i]->maxdq); if (list->heap[i]->maxdq) { debug(" (%ld, %ld, %.7f)", list->heap[i]->maxdq->first, list->heap[i]->maxdq->second, *list->heap[i]->maxdq->dq); } debug("\n"); } debug("Heap index:\n"); for (i=0; ino_of_communities; i++) debug("%ld ", (long)list->heapindex[i]); debug("\nEND\n"); } /* Checks if the community heap satisfies the heap property. * Only useful for debugging. */ void igraph_i_fastgreedy_community_list_check_heap( igraph_i_fastgreedy_community_list* list) { long int i; for (i=0; ino_of_communities/2; i++) { if ((2*i+1no_of_communities && *list->heap[i]->maxdq->dq < *list->heap[2*i+1]->maxdq->dq) || (2*i+2no_of_communities && *list->heap[i]->maxdq->dq < *list->heap[2*i+2]->maxdq->dq)) { IGRAPH_WARNING("Heap property violated"); debug("Position: %ld, %ld and %ld\n", i, 2*i+1, 2*i+2); igraph_i_fastgreedy_community_list_dump_heap(list); } } } /* Removes a given element from the heap */ void igraph_i_fastgreedy_community_list_remove( igraph_i_fastgreedy_community_list* list, long int idx) { igraph_real_t old; long int commidx; /* First adjust the index */ commidx=list->heap[list->no_of_communities-1]->maxdq->first; list->heapindex[commidx] = (igraph_integer_t) idx; commidx=list->heap[idx]->maxdq->first; list->heapindex[commidx] = -1; /* Now remove the element */ old=*list->heap[idx]->maxdq->dq; list->heap[idx] = list->heap[list->no_of_communities-1]; list->no_of_communities--; /* Recover heap property */ if (old > *list->heap[idx]->maxdq->dq) igraph_i_fastgreedy_community_list_sift_down(list, idx); else igraph_i_fastgreedy_community_list_sift_up(list, idx); } /* Removes a given element from the heap when there are no more neighbors * for it (comm->maxdq is NULL) */ void igraph_i_fastgreedy_community_list_remove2( igraph_i_fastgreedy_community_list* list, long int idx, long int comm) { long int i; if (idx == list->no_of_communities-1) { /* We removed the rightmost element on the bottom level, no problem, * there's nothing to be done */ list->heapindex[comm] = -1; list->no_of_communities--; return; } /* First adjust the index */ i=list->heap[list->no_of_communities-1]->maxdq->first; list->heapindex[i] = (igraph_integer_t) idx; list->heapindex[comm] = -1; /* Now remove the element */ list->heap[idx] = list->heap[list->no_of_communities-1]; list->no_of_communities--; /* Recover heap property */ for (i=list->no_of_communities/2-1; i>=0; i--) igraph_i_fastgreedy_community_list_sift_down(list, i); } /* Removes the pair belonging to community k from the neighborhood list * of community c (that is, clist[c]) and recalculates maxdq */ void igraph_i_fastgreedy_community_remove_nei( igraph_i_fastgreedy_community_list* list, long int c, long int k) { long int i, n; igraph_bool_t rescan=0; igraph_i_fastgreedy_commpair *p; igraph_i_fastgreedy_community *comm; igraph_real_t olddq; comm=&list->e[c]; n=igraph_vector_ptr_size(&comm->neis); for (i=0; ineis)[i]; if (p->second == k) { /* Check current maxdq */ if (comm->maxdq == p) rescan=1; break; } } if (imaxdq->dq; igraph_vector_ptr_remove(&comm->neis, i); if (rescan) { igraph_i_fastgreedy_community_rescan_max(comm); i=igraph_i_fastgreedy_community_list_find_in_heap(list, c); if (comm->maxdq) { if (*comm->maxdq->dq > olddq) igraph_i_fastgreedy_community_list_sift_up(list, i); else igraph_i_fastgreedy_community_list_sift_down(list, i); } else { /* no more neighbors for this community. we should remove this * community from the heap and restore the heap property */ debug("REMOVING (NO MORE NEIS): %ld\n", i); igraph_i_fastgreedy_community_list_remove2(list, i, c); } } } } /* Updates the dq value of community pair p in the community with index p->first * of the community list clist to newdq and restores the heap property * in community c if necessary. Returns 1 if the maximum in the row had * to be updated, zero otherwise */ int igraph_i_fastgreedy_community_update_dq( igraph_i_fastgreedy_community_list* list, igraph_i_fastgreedy_commpair* p, igraph_real_t newdq) { long int i,j,to,from; igraph_real_t olddq; igraph_i_fastgreedy_community *comm_to, *comm_from; to=p->first; from=p->second; comm_to=&list->e[to]; comm_from=&list->e[from]; if (comm_to->maxdq == p && newdq >= *p->dq) { /* If we are adjusting the current maximum and it is increased, we don't * have to re-scan for the new maximum */ *p->dq = newdq; /* The maximum was increased, so perform a sift-up in the heap */ i = igraph_i_fastgreedy_community_list_find_in_heap(list, to); igraph_i_fastgreedy_community_list_sift_up(list, i); /* Let's check the opposite side. If the pair was not the maximal in * the opposite side (the other community list)... */ if (comm_from->maxdq != p->opposite) { if (*comm_from->maxdq->dq < newdq) { /* ...and it will become the maximal, we need to adjust and sift up */ comm_from->maxdq = p->opposite; j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_up(list, j); } else { /* The pair was not the maximal in the opposite side and it will * NOT become the maximal, there's nothing to do there */ } } else { /* The pair was maximal in the opposite side, so we need to sift it up * with the new value */ j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_up(list, j); } return 1; } else if (comm_to->maxdq != p && (newdq <= *comm_to->maxdq->dq)) { /* If we are modifying an item which is not the current maximum, and the * new value is less than the current maximum, we don't * have to re-scan for the new maximum */ olddq = *p->dq; *p->dq = newdq; /* However, if the item was the maximum on the opposite side, we'd better * re-scan it */ if (comm_from->maxdq == p->opposite) { if (olddq>newdq) { /* Decreased the maximum on the other side, we have to re-scan for the * new maximum */ igraph_i_fastgreedy_community_rescan_max(comm_from); j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_down(list, j); } else { /* Increased the maximum on the other side, we don't have to re-scan * but we might have to sift up */ j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_up(list, j); } } return 0; } else { /* We got here in two cases: (1) the pair we are modifying right now is the maximum in the given community and we are decreasing it (2) the pair we are modifying right now is NOT the maximum in the given community, but we increase it so much that it will become the new maximum */ *p->dq = newdq; if (comm_to->maxdq != p) { /* case (2) */ comm_to->maxdq = p; /* The maximum was increased, so perform a sift-up in the heap */ i = igraph_i_fastgreedy_community_list_find_in_heap(list, to); igraph_i_fastgreedy_community_list_sift_up(list, i); /* Opposite side. Chances are that the new value became the maximum * in the opposite side, but check it first */ if (comm_from->maxdq != p->opposite) { if (*comm_from->maxdq->dq < newdq) { /* Yes, it will become the new maximum */ comm_from->maxdq = p->opposite; j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_up(list, j); } else { /* No, nothing to do there */ } } else { /* Already increased the maximum on the opposite side, so sift it up */ j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_up(list, j); } } else { /* case (1) */ /* This is the worst, we have to re-scan the whole community to find * the new maximum and update the global maximum as well if necessary */ igraph_i_fastgreedy_community_rescan_max(comm_to); /* The maximum was decreased, so perform a sift-down in the heap */ i = igraph_i_fastgreedy_community_list_find_in_heap(list, to); igraph_i_fastgreedy_community_list_sift_down(list, i); if (comm_from->maxdq != p->opposite) { /* The one that we decreased on the opposite side is not the * maximal one. Nothing to do. */ } else { /* We decreased the maximal on the opposite side as well. Re-scan * and sift down */ igraph_i_fastgreedy_community_rescan_max(comm_from); j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_down(list, j); } } } return 1; } /* Auxiliary function to sort a community pair list with respect to the * `second` field */ int igraph_i_fastgreedy_commpair_cmp(const void* p1, const void* p2) { igraph_i_fastgreedy_commpair *cp1, *cp2; cp1=*(igraph_i_fastgreedy_commpair**)p1; cp2=*(igraph_i_fastgreedy_commpair**)p2; return (int) (cp1->second - cp2->second); } /** * \function igraph_community_fastgreedy * \brief Finding community structure by greedy optimization of modularity * * This function implements the fast greedy modularity optimization * algorithm for finding community structure, see * A Clauset, MEJ Newman, C Moore: Finding community structure in very * large networks, http://www.arxiv.org/abs/cond-mat/0408187 for the * details. * * * Some improvements proposed in K Wakita, T Tsurumi: Finding community * structure in mega-scale social networks, * http://www.arxiv.org/abs/cs.CY/0702048v1 have also been implemented. * * \param graph The input graph. It must be a graph without multiple edges. * This is checked and an error message is given for graphs with multiple * edges. * \param weights Potentially a numeric vector containing edge * weights. Supply a null pointer here for unweighted graphs. The * weights are expected to be non-negative. * \param merges Pointer to an initialized matrix or NULL, the result of the * computation is stored here. The matrix has two columns and each * merge corresponds to one merge, the ids of the two merged * components are stored. The component ids are numbered from zero and * the first \c n components are the individual vertices, \c n is * the number of vertices in the graph. Component \c n is created * in the first merge, component \c n+1 in the second merge, etc. * The matrix will be resized as needed. If this argument is NULL * then it is ignored completely. * \param modularity Pointer to an initialized vector or NULL pointer, * in the former case the modularity scores along the stages of the * computation are recorded here. The vector will be resized as * needed. * \param membership Pointer to a vector. If not a null pointer, then * the membership vector corresponding to the best split (in terms * of modularity) is stored here. * \return Error code. * * \sa \ref igraph_community_walktrap(), \ref * igraph_community_edge_betweenness() for other community detection * algorithms, \ref igraph_community_to_membership() to convert the * dendrogram to a membership vector. * * Time complexity: O(|E||V|log|V|) in the worst case, * O(|E|+|V|log^2|V|) typically, |V| is the number of vertices, |E| is * the number of edges. * * \example examples/simple/igraph_community_fastgreedy.c */ int igraph_community_fastgreedy(const igraph_t *graph, const igraph_vector_t *weights, igraph_matrix_t *merges, igraph_vector_t *modularity, igraph_vector_t *membership) { long int no_of_edges, no_of_nodes, no_of_joins, total_joins; long int i, j, k, n, m, from, to, dummy; igraph_integer_t ffrom, fto; igraph_eit_t edgeit; igraph_i_fastgreedy_commpair *pairs, *p1, *p2; igraph_i_fastgreedy_community_list communities; igraph_vector_t a; igraph_real_t q, *dq, weight_sum, loop_weight_sum; igraph_bool_t has_multiple; /*long int join_order[] = { 16,5, 5,6, 6,0, 4,0, 10,0, 26,29, 29,33, 23,33, 27,33, 25,24, 24,31, 12,3, 21,1, 30,8, 8,32, 9,2, 17,1, 11,0, 7,3, 3,2, 13,2, 1,2, 28,31, 31,33, 22,32, 18,32, 20,32, 32,33, 15,33, 14,33, 0,19, 19,2, -1,-1 };*/ /*long int join_order[] = { 43,42, 42,41, 44,41, 41,36, 35,36, 37,36, 36,29, 38,29, 34,29, 39,29, 33,29, 40,29, 32,29, 14,29, 30,29, 31,29, 6,18, 18,4, 23,4, 21,4, 19,4, 27,4, 20,4, 22,4, 26,4, 25,4, 24,4, 17,4, 0,13, 13,2, 1,2, 11,2, 8,2, 5,2, 3,2, 10,2, 9,2, 7,2, 2,28, 28,15, 12,15, 29,16, 4,15, -1,-1 };*/ no_of_nodes = igraph_vcount(graph); no_of_edges = igraph_ecount(graph); if (igraph_is_directed(graph)) { IGRAPH_ERROR("fast greedy community detection works for undirected graphs only", IGRAPH_UNIMPLEMENTED); } total_joins=no_of_nodes-1; if (weights != 0) { if (igraph_vector_size(weights) < igraph_ecount(graph)) IGRAPH_ERROR("fast greedy community detection: weight vector too short", IGRAPH_EINVAL); if (igraph_vector_any_smaller(weights, 0)) IGRAPH_ERROR("weights must be positive", IGRAPH_EINVAL); weight_sum = igraph_vector_sum(weights); } else weight_sum = no_of_edges; IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple)); if (has_multiple) { IGRAPH_ERROR("fast-greedy community finding works only on graphs without multiple edges", IGRAPH_EINVAL); } if (merges != 0) { IGRAPH_CHECK(igraph_matrix_resize(merges, total_joins, 2)); igraph_matrix_null(merges); } if (modularity != 0) { IGRAPH_CHECK(igraph_vector_resize(modularity, total_joins+1)); } /* Create degree vector */ IGRAPH_VECTOR_INIT_FINALLY(&a, no_of_nodes); if (weights) { debug("Calculating weighted degrees\n"); for (i=0; i < no_of_edges; i++) { VECTOR(a)[(long int)IGRAPH_FROM(graph, i)] += VECTOR(*weights)[i]; VECTOR(a)[(long int)IGRAPH_TO(graph, i)] += VECTOR(*weights)[i]; } } else { debug("Calculating degrees\n"); IGRAPH_CHECK(igraph_degree(graph, &a, igraph_vss_all(), IGRAPH_ALL, 1)); } /* Create list of communities */ debug("Creating community list\n"); communities.n = no_of_nodes; communities.no_of_communities = no_of_nodes; communities.e = (igraph_i_fastgreedy_community*)calloc((size_t) no_of_nodes, sizeof(igraph_i_fastgreedy_community)); if (communities.e == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, communities.e); communities.heap = (igraph_i_fastgreedy_community**)calloc((size_t) no_of_nodes, sizeof(igraph_i_fastgreedy_community*)); if (communities.heap == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, communities.heap); communities.heapindex = (igraph_integer_t*)calloc((size_t)no_of_nodes, sizeof(igraph_integer_t)); if (communities.heapindex == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY_CLEAN(2); IGRAPH_FINALLY(igraph_i_fastgreedy_community_list_destroy, &communities); for (i=0; ito) { dummy=from; from=to; to=dummy; } if (weights) { dq[j]=2*(VECTOR(*weights)[eidx]/(weight_sum*2.0) - VECTOR(a)[from]*VECTOR(a)[to]/(4.0*weight_sum*weight_sum)); } else { dq[j]=2*(1.0/(no_of_edges*2.0) - VECTOR(a)[from]*VECTOR(a)[to]/(4.0*no_of_edges*no_of_edges)); } pairs[i].first = from; pairs[i].second = to; pairs[i].dq = &dq[j]; pairs[i].opposite = &pairs[i+1]; pairs[i+1].first = to; pairs[i+1].second = from; pairs[i+1].dq = pairs[i].dq; pairs[i+1].opposite = &pairs[i]; /* Link the pair to the communities */ igraph_vector_ptr_push_back(&communities.e[from].neis, &pairs[i]); igraph_vector_ptr_push_back(&communities.e[to].neis, &pairs[i+1]); /* Update maximums */ if (communities.e[from].maxdq==0 || *communities.e[from].maxdq->dq < *pairs[i].dq) communities.e[from].maxdq = &pairs[i]; if (communities.e[to].maxdq==0 || *communities.e[to].maxdq->dq < *pairs[i+1].dq) communities.e[to].maxdq = &pairs[i+1]; } igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(1); /* Sorting community neighbor lists by community IDs */ debug("Sorting community neighbor lists\n"); for (i=0, j=0; ifirst, p1->second, *p1->dq); } p1=communities.e[i].maxdq; debug("\n Maxdq: (%ld,%ld,%.4f)\n", p1->first, p1->second, *p1->dq); } debug("Global maxdq is: (%ld,%ld,%.4f)\n", communities.heap[0]->maxdq->first, communities.heap[0]->maxdq->second, *communities.heap[0]->maxdq->dq); for (i=0; imaxdq->first, communities.heap[i]->maxdq->second, *communities.heap[0]->maxdq->dq); debug("\n"); #endif if (communities.heap[0] == 0) break; /* no more communities */ if (communities.heap[0]->maxdq == 0) break; /* there are only isolated comms */ to=communities.heap[0]->maxdq->second; from=communities.heap[0]->maxdq->first; debug("Q[%ld] = %.7f\tdQ = %.7f\t |H| = %ld\n", no_of_joins, q, *communities.heap[0]->maxdq->dq, no_of_nodes-no_of_joins-1); /* DEBUG */ /* from=join_order[no_of_joins*2]; to=join_order[no_of_joins*2+1]; if (to == -1) break; for (i=0; isecond == from) communities.maxdq = p1; } */ n = igraph_vector_ptr_size(&communities.e[to].neis); m = igraph_vector_ptr_size(&communities.e[from].neis); /*if (n>m) { dummy=n; n=m; m=dummy; dummy=to; to=from; from=dummy; }*/ debug(" joining: %ld <- %ld\n", to, from); q += *communities.heap[0]->maxdq->dq; /* Merge the second community into the first */ i = j = 0; while (ifirst, p1->second, p2->first, p2->second); if (p1->second < p2->second) { /* Considering p1 from now on */ debug(" Considering: %ld-%ld\n", p1->first, p1->second); if (p1->second == from) { debug(" WILL REMOVE: %ld-%ld\n", to, from); } else { /* chain, case 1 */ debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", to, p1->second, from, *p1->dq, -2*VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq-2*VECTOR(a)[from]*VECTOR(a)[p1->second]); igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2*VECTOR(a)[from]*VECTOR(a)[p1->second]); } i++; } else if (p1->second == p2->second) { /* p1->first, p1->second and p2->first form a triangle */ debug(" Considering: %ld-%ld and %ld-%ld\n", p1->first, p1->second, p2->first, p2->second); /* Update dq value */ debug(" TRIANGLE: %ld-%ld-%ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", to, p1->second, from, *p1->dq, *p2->dq, p1->first, p1->second, *p1->dq+*p2->dq); igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq + *p2->dq); igraph_i_fastgreedy_community_remove_nei(&communities, p1->second, from); i++; j++; } else { debug(" Considering: %ld-%ld\n", p2->first, p2->second); if (p2->second == to) { debug(" WILL REMOVE: %ld-%ld\n", p2->second, p2->first); } else { /* chain, case 2 */ debug(" CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n", to, p2->second, from, to, p2->second, *p2->dq-2*VECTOR(a)[to]*VECTOR(a)[p2->second]); p2->opposite->second=to; /* need to re-sort community nei list `p2->second` */ /* TODO: quicksort is O(n*logn), although we could do a deletion and * insertion which can be done in O(logn) if deletion is O(1) */ debug(" Re-sorting community %ld\n", p2->second); igraph_vector_ptr_sort(&communities.e[p2->second].neis, igraph_i_fastgreedy_commpair_cmp); /* link from.neis[j] to the current place in to.neis if * from.neis[j] != to */ p2->first=to; IGRAPH_CHECK(igraph_vector_ptr_insert(&communities.e[to].neis,i,p2)); n++; i++; if (*p2->dq > *communities.e[to].maxdq->dq) { communities.e[to].maxdq = p2; k=igraph_i_fastgreedy_community_list_find_in_heap(&communities, to); igraph_i_fastgreedy_community_list_sift_up(&communities, k); } igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq - 2*VECTOR(a)[to]*VECTOR(a)[p2->second]); } j++; } } while (isecond == from) { debug(" WILL REMOVE: %ld-%ld\n", p1->first, from); } else { /* chain, case 1 */ debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", to, p1->second, from, *p1->dq, -2*VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq-2*VECTOR(a)[from]*VECTOR(a)[p1->second]); igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2*VECTOR(a)[from]*VECTOR(a)[p1->second]); } i++; } while (jsecond) { j++; continue; } /* chain, case 2 */ debug(" CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n", to, p2->second, from, p1->first, p2->second, *p2->dq-2*VECTOR(a)[to]*VECTOR(a)[p2->second]); p2->opposite->second=to; /* need to re-sort community nei list `p2->second` */ /* TODO: quicksort is O(n*logn), although we could do a deletion and * insertion which can be done in O(logn) if deletion is O(1) */ debug(" Re-sorting community %ld\n", p2->second); igraph_vector_ptr_sort(&communities.e[p2->second].neis, igraph_i_fastgreedy_commpair_cmp); /* link from.neis[j] to the current place in to.neis if * from.neis[j] != to */ p2->first=to; IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[to].neis,p2)); if (*p2->dq > *communities.e[to].maxdq->dq) { communities.e[to].maxdq = p2; k=igraph_i_fastgreedy_community_list_find_in_heap(&communities, to); igraph_i_fastgreedy_community_list_sift_up(&communities, k); } igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq-2*VECTOR(a)[to]*VECTOR(a)[p2->second]); j++; } /* Now, remove community `from` from the neighbors of community `to` */ if (communities.no_of_communities > 2) { debug(" REMOVING: %ld-%ld\n", to, from); igraph_i_fastgreedy_community_remove_nei(&communities, to, from); i=igraph_i_fastgreedy_community_list_find_in_heap(&communities, from); igraph_i_fastgreedy_community_list_remove(&communities, i); } communities.e[from].maxdq=0; /* Update community sizes */ communities.e[to].size += communities.e[from].size; communities.e[from].size = 0; /* record what has been merged */ /* igraph_vector_ptr_clear is not enough here as it won't free * the memory consumed by communities.e[from].neis. Thanks * to Tom Gregorovic for pointing that out. */ igraph_vector_ptr_destroy(&communities.e[from].neis); if (merges) { MATRIX(*merges, no_of_joins, 0) = communities.e[to].id; MATRIX(*merges, no_of_joins, 1) = communities.e[from].id; communities.e[to].id = (igraph_integer_t) (no_of_nodes+no_of_joins); } /* Update vector a */ VECTOR(a)[to] += VECTOR(a)[from]; VECTOR(a)[from] = 0.0; no_of_joins++; } /* TODO: continue merging when some isolated communities remained. Always * joining the communities with the least number of nodes results in the * smallest decrease in modularity every step. Now we're simply deleting * the excess rows from the merge matrix */ if (no_of_joins < total_joins) { long int *ivec; ivec=igraph_Calloc(igraph_matrix_nrow(merges), long int); if (ivec == 0) IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, ivec); for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_PROGRESS_H #define IGRAPH_PROGRESS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" __BEGIN_DECLS /** * \section about_progress_handlers About progress handlers * * It is often useful to report the progress of some long * calculation, to allow the user to follow the computation and * guess the total running time. A couple of igraph functions * support this at the time of writing, hopefully more will support it * in the future. * * * * To see the progress of a computation, the user has to install a * progress handler, as there is none installed by default. * If an igraph function supports progress reporting, then it * calls the installed progress handler periodically, and passes a * percentage value to it, the percentage of computation already * performed. To install a progress handler, you need to call * \ref igraph_set_progress_handler(). Currently there is a single * pre-defined progress handler, called \ref * igraph_progress_handler_stderr(). * */ /** * \section writing_progress_handlers Writing progress handlers * * * To write a new progress handler, one needs to create a function of * type \ref igraph_progress_handler_t. The new progress handler * can then be installed with the \ref igraph_set_progress_handler() * function. * * * * One can assume that the first progress handler call from a * calculation will be call with zero as the \p percentage argument, * and the last call from a function will have 100 as the \p * percentage argument. Note, however, that if an error happens in the * middle of a computation, then the 100 percent call might be * omitted. * */ /** * \section igraph_functions_with_progress Writing igraph functions with progress reporting * * * If you want to write a function that uses igraph and supports * progress reporting, you need to include \ref igraph_progress() * calls in your function, usually via the \ref IGRAPH_PROGRESS() * macro. * * * * It is good practice to always include a call to \ref * igraph_progress() with a zero \p percentage argument, before the * computation; and another call with 100 \p percentage value * after the computation is completed. * * * * It is also good practice \em not to call \ref igraph_progress() too * often, as this would slow down the computation. It might not be * worth to support progress reporting in functions with linear or * log-linear time complexity, as these are fast, even with a large * amount of data. For functions with quadratic or higher time * complexity make sure that the time complexity of the progress * reporting is constant or at least linear. In practice this means * having at most O(n) progress checks and at most 100 \reg * igraph_progress() calls. * */ /** * \section progress_and_threads Multi-threaded programs * * * In multi-threaded programs, each thread has its own progress * handler, if thread-local storage is supported and igraph is * thread-safe. See the \ref IGRAPH_THREAD_SAFE macro for checking * whether an igraph build is thread-safe. * */ /* -------------------------------------------------- */ /* Progress handlers */ /* -------------------------------------------------- */ /** * \typedef igraph_progress_handler_t * \brief Type of progress handler functions * * This is the type of the igraph progress handler functions. * There is currently one such predefined function, * \ref igraph_progress_handler_stderr(), but the user can * write and set up more sophisticated ones. * \param message A string describing the function or algorithm * that is reporting the progress. Current igraph functions * always use the name \p message argument if reporting from the * same function. * \param percent Numeric, the percentage that was completed by the * algorithm or function. * \param data User-defined data. Current igraph functions that * report progress pass a null pointer here. Users can * write their own progress handlers and functions with progress * reporting, and then pass some meaningfull context here. * \return If the return value of the progress handler is not * IGRAPH_SUCCESS (=0), then \ref igraph_progress() returns the * error code \c IGRAPH_INTERRUPTED. The \ref IGRAPH_PROGRESS() * macro frees all memory and finishes the igraph function with * error code \c IGRAPH_INTERRUPTED in this case. */ typedef int igraph_progress_handler_t(const char *message, igraph_real_t percent, void *data); extern igraph_progress_handler_t igraph_progress_handler_stderr; igraph_progress_handler_t * igraph_set_progress_handler(igraph_progress_handler_t new_handler); int igraph_progress(const char *message, igraph_real_t percent, void *data); int igraph_progressf(const char *message, igraph_real_t percent, void *data, ...); /** * \define IGRAPH_PROGRESS * \brief Report progress. * * The standard way to report progress from an igraph function * \param message A string, a textual message that references the * calculation under progress. * \param percent Numeric scalar, the percentage that is complete. * \param data User-defined data, this can be used in user-defined * progress handler functions, from user-written igraph functions. * \return If the progress handler returns with \c IGRAPH_INTERRUPTED, * then this macro frees up the igraph allocated memory for * temporary data and returns to the caller with \c * IGRAPH_INTERRUPTED. */ #define IGRAPH_PROGRESS(message, percent, data) \ do { \ if (igraph_progress((message), (percent), (data)) != IGRAPH_SUCCESS) { \ IGRAPH_FINALLY_FREE(); \ return IGRAPH_INTERRUPTED; \ } \ } while (0) __END_DECLS #endif igraph/src/gengraph_degree_sequence.cpp0000644000176000001440000002361012325527073020055 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gengraph_definitions.h" #include "gengraph_random.h" #include "gengraph_powerlaw.h" #include "gengraph_degree_sequence.h" #include "gengraph_hash.h" #include "igraph_statusbar.h" #include #include #include #include #include // using namespace __gnu_cxx; using namespace std; namespace gengraph { // shuffle an int[] randomly void random_permute(int *a, int n); // sort an array of positive integers in time & place O(n + max) void cumul_sort(int *q, int n); void degree_sequence::detach() { deg=NULL; } degree_sequence::~degree_sequence() { if(deg!=NULL) delete[] deg; deg = NULL; } void degree_sequence::make_even(int mini, int maxi) { if(total%2==0) return; if(maxi<0) maxi=0x7FFFFFFF; int i; for(i=0; imini) { deg[i]--; total--; break; } else if(deg[i] degree; // if(!DISTRIB) { // // Input is a 'raw' degree sequence d0 d1 d2 d3 ... // while(fgets(buff, FBUFF_SIZE, f)) { // int d = strtol(buff, &c, 10); // if(c == buff) continue; // degree.push_back(d); // total += d; // } // n = int(degree.size()); // deg = new int[n]; // int *yo = deg; // vector::iterator end = degree.end(); // for(vector::iterator it=degree.begin(); it!=end; *(yo++) = *(it++)); // } // else { // // Input is a degree distribution : d0 #(degree=d0), d1 #(degree=d1), ... // vector n_with_degree; // int line = 0; // int syntax = 0; // int ignored = 0; // int first_syntax = 0; // int first_ignored = 0; // while(fgets(buff, FBUFF_SIZE, f)) { // line++; // int d = strtol(buff, &c, 10); // if(c == buff) { ignored++; first_ignored = line; continue; } // char *cc; // int i = strtol(c, &cc, 10); // if(cc == c) { syntax++; first_syntax = line; continue; } // n += i; // total += i*d; // degree.push_back(d); // n_with_degree.push_back(i); // if( cc != c) { syntax++; first_syntax = line; } // } // if(VERBOSE()) { // if(ignored > 0) fprintf(stderr,"Ignored %d lines (first was line #%d)\n", ignored, first_ignored); // if(syntax > 0) fprintf(stderr,"Found %d probable syntax errors (first was line #%d)\n", syntax, first_syntax); // } // deg = new int[n]; // int *yo = deg; // vector::iterator it_n = n_with_degree.begin(); // for(vector::iterator it = degree.begin(); it != degree.end(); it++) // for(int k = *(it_n++); k--; *yo++ = *it); // } // if(VERBOSE()) { // if(total % 2 != 0) fprintf(stderr,"Warning: degree sequence is odd\n"); // fprintf(stderr,"Degree sequence created. N=%d, 2M=%d\n", n, total); // } // } // n vertices, exponent, min degree, max degree, average degree (optional, default is -1) degree_sequence:: degree_sequence(int _n, double exp, int degmin, int degmax, double z) { n=_n; if(exp==0.0) { // Binomial distribution if(z<0) { igraph_error("Fatal error in degree_sequence Ctor: " "positive average degree must be specified", __FILE__, __LINE__, IGRAPH_EINVAL); } if(degmax<0) degmax=n-1; total = int(floor(double(n)*z+0.5)); deg = new int[n]; KW_RNG::RNG myrand; double p = (z-double(degmin))/double(n); total=0; for(int i=0; idegmax); total+=deg[i]; } } else { // Power-law distribution igraph_status("Creating powerlaw sampler...", 0); powerlaw pw(exp, degmin, degmax); if(z==-1.0) { pw.init(); igraph_statusf("done. Mean=%f\n", 0, pw.mean()); } else { double offset = pw.init_to_mean(z); igraph_statusf("done. Offset=%f, Mean=%f\n", 0, offset, pw.mean()); } deg = new int[n]; total = 0; int i; igraph_statusf("Sampling %d random numbers...", 0, n); for(i=0; iwanted_total; i++) { total-=deg[i]; if(total+degmin<=wanted_total) deg[i]=wanted_total-total; else deg[i]=pw.sample(); total += deg[i]; } iterations += i; for(i=n-1; i>0 && total>1)>=wanted_total) deg[i]=wanted_total-total; else deg[i]=pw.sample(); total += deg[i]; } iterations += n-1-i; } igraph_statusf("done(%d iterations).", 0, iterations); igraph_statusf(" Now, degmax = %d\n", 0, dmax()); } shuffle(); } } // void degree_sequence::print() { // for(int i=0; ideg[i]) dmin=deg[i]; // int *dd = new int[dmax-dmin+1]; // for(i=dmin; i<=dmax; i++) dd[i-dmin]=0; // if(VERBOSE()) fprintf(stderr,"Computing cumulative distribution..."); // for(i=0; i0) printf("%d %d\n",i,dd[i-dmin]); // delete[] dd; // } bool degree_sequence::havelhakimi() { int i; int dm = dmax()+1; // Sort vertices using basket-sort, in descending degrees int *nb = new int[dm]; int *sorted = new int[n]; // init basket for(i=0; i=0; i--) { int t=nb[i]; nb[i]=c; c+=t; } // sort for(i=0; i0; ) { // We design by 'v' the vertex of highest degree (indexed by first) // look for current degree of v while(nb[d]<=first) d--; // store it in dv int dv = d; // bind it ! c -= dv; int dc = d; // residual degree of vertices we bind to int fc = ++first; // position of the first vertex with degree dc while(dv>0 && dc>0) { int lc = nb[dc]; if(lc!=fc) { while(dv>0 && lc>fc) { // binds v with sorted[--lc] dv--; lc--; } fc = nb[dc]; nb[dc] = lc; } dc--; } if(dv != 0) { // We couldn't bind entirely v delete[] nb; delete[] sorted; return false; } } delete[] nb; delete[] sorted; return true; } //************************* // Subroutines definitions //************************* inline int int_adjust(double x) { return(int(floor(x+random_float()))); } void random_permute(int *a, int n) { int j,tmp; for(int i=0; iqmax) qmax=q[i]; for(i=0; i0;i--) nb[i-1]+=nb[i]; // sort by q[i] int last_q; int tmp; int modifier = qmax-qmin+1; for(int current=0; current=qmin && tmp<=qmax) { last_q=qmin; do { q[current] = last_q+modifier; last_q = tmp; current = --nb[last_q-qmin]; } while((tmp=q[current])>=qmin && tmp<=qmax); q[current]=last_q+modifier; } } delete[] nb; for(i=0; i #include #include #include #include #include using namespace prpack; using namespace std; void prpack_base_graph::initialize() { heads = NULL; tails = NULL; vals = NULL; } prpack_base_graph::prpack_base_graph() { initialize(); num_vs = num_es = 0; } prpack_base_graph::prpack_base_graph(const prpack_csc* g) { initialize(); num_vs = g->num_vs; num_es = g->num_es; // fill in heads and tails num_self_es = 0; int* hs = g->heads; int* ts = g->tails; tails = new int[num_vs]; memset(tails, 0, num_vs*sizeof(tails[0])); for (int h = 0; h < num_vs; ++h) { const int start_ti = hs[h]; const int end_ti = (h + 1 != num_vs) ? hs[h + 1] : num_es; for (int ti = start_ti; ti < end_ti; ++ti) { const int t = ts[ti]; ++tails[t]; if (h == t) ++num_self_es; } } for (int i = 0, sum = 0; i < num_vs; ++i) { const int temp = sum; sum += tails[i]; tails[i] = temp; } heads = new int[num_es]; int* osets = new int[num_vs]; memset(osets, 0, num_vs*sizeof(osets[0])); for (int h = 0; h < num_vs; ++h) { const int start_ti = hs[h]; const int end_ti = (h + 1 != num_vs) ? hs[h + 1] : num_es; for (int ti = start_ti; ti < end_ti; ++ti) { const int t = ts[ti]; heads[tails[t] + osets[t]++] = h; } } // clean up delete[] osets; } prpack_base_graph::prpack_base_graph(const prpack_int64_csc* g) { initialize(); // TODO remove the assert and add better behavior assert(num_vs <= std::numeric_limits::max()); num_vs = (int)g->num_vs; num_es = (int)g->num_es; // fill in heads and tails num_self_es = 0; int64_t* hs = g->heads; int64_t* ts = g->tails; tails = new int[num_vs]; memset(tails, 0, num_vs*sizeof(tails[0])); for (int h = 0; h < num_vs; ++h) { const int start_ti = (int)hs[h]; const int end_ti = (h + 1 != num_vs) ? (int)hs[h + 1] : num_es; for (int ti = start_ti; ti < end_ti; ++ti) { const int t = (int)ts[ti]; ++tails[t]; if (h == t) ++num_self_es; } } for (int i = 0, sum = 0; i < num_vs; ++i) { const int temp = sum; sum += tails[i]; tails[i] = temp; } heads = new int[num_es]; int* osets = new int[num_vs]; memset(osets, 0, num_vs*sizeof(osets[0])); for (int h = 0; h < num_vs; ++h) { const int start_ti = (int)hs[h]; const int end_ti = (h + 1 != num_vs) ? (int)hs[h + 1] : num_es; for (int ti = start_ti; ti < end_ti; ++ti) { const int t = (int)ts[ti]; heads[tails[t] + osets[t]++] = h; } } // clean up delete[] osets; } prpack_base_graph::prpack_base_graph(const prpack_csr* g) { initialize(); assert(false); // TODO } prpack_base_graph::prpack_base_graph(const prpack_edge_list* g) { initialize(); num_vs = g->num_vs; num_es = g->num_es; // fill in heads and tails num_self_es = 0; int* hs = g->heads; int* ts = g->tails; tails = new int[num_vs]; memset(tails, 0, num_vs*sizeof(tails[0])); for (int i = 0; i < num_es; ++i) { ++tails[ts[i]]; if (hs[i] == ts[i]) ++num_self_es; } for (int i = 0, sum = 0; i < num_vs; ++i) { const int temp = sum; sum += tails[i]; tails[i] = temp; } heads = new int[num_es]; int* osets = new int[num_vs]; memset(osets, 0, num_vs*sizeof(osets[0])); for (int i = 0; i < num_es; ++i) heads[tails[ts[i]] + osets[ts[i]]++] = hs[i]; // clean up delete[] osets; } prpack_base_graph::prpack_base_graph(const char* filename, const char* format, const bool weighted) { initialize(); FILE* f = fopen(filename, "r"); const string s(filename); const string t(format); const string ext = (t == "") ? s.substr(s.rfind('.') + 1) : t; if (ext == "smat") { read_smat(f, weighted); } else { prpack_utils::validate(!weighted, "Error: graph format is not compatible with weighted option."); if (ext == "edges" || ext == "eg2") { read_edges(f); } else if (ext == "graph-txt") { read_ascii(f); } else { prpack_utils::validate(false, "Error: invalid graph format."); } } fclose(f); } prpack_base_graph::~prpack_base_graph() { delete[] heads; delete[] tails; delete[] vals; } void prpack_base_graph::read_smat(FILE* f, const bool weighted) { // read in header double ignore = 0.0; assert(fscanf(f, "%d %lf %d", &num_vs, &ignore, &num_es) == 3); // fill in heads and tails num_self_es = 0; int* hs = new int[num_es]; int* ts = new int[num_es]; heads = new int[num_es]; tails = new int[num_vs]; double* vs = NULL; if (weighted) { vs = new double[num_es]; vals = new double[num_es]; } memset(tails, 0, num_vs*sizeof(tails[0])); for (int i = 0; i < num_es; ++i) { assert(fscanf(f, "%d %d %lf", &hs[i], &ts[i], &((weighted) ? vs[i] : ignore)) == 3); ++tails[ts[i]]; if (hs[i] == ts[i]) ++num_self_es; } for (int i = 0, sum = 0; i < num_vs; ++i) { const int temp = sum; sum += tails[i]; tails[i] = temp; } int* osets = new int[num_vs]; memset(osets, 0, num_vs*sizeof(osets[0])); for (int i = 0; i < num_es; ++i) { const int idx = tails[ts[i]] + osets[ts[i]]++; heads[idx] = hs[i]; if (weighted) vals[idx] = vs[i]; } // clean up delete[] hs; delete[] ts; delete[] vs; delete[] osets; } void prpack_base_graph::read_edges(FILE* f) { vector > al; int h, t; num_es = num_self_es = 0; while (fscanf(f, "%d %d", &h, &t) == 2) { const int m = (h < t) ? t : h; if ((int) al.size() < m + 1) al.resize(m + 1); al[t].push_back(h); ++num_es; if (h == t) ++num_self_es; } num_vs = al.size(); heads = new int[num_es]; tails = new int[num_vs]; for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { tails[tails_i] = heads_i; for (int j = 0; j < (int) al[tails_i].size(); ++j) heads[heads_i++] = al[tails_i][j]; } } void prpack_base_graph::read_ascii(FILE* f) { assert(fscanf(f, "%d", &num_vs) == 1); while (getc(f) != '\n'); vector* al = new vector[num_vs]; num_es = num_self_es = 0; char s[32]; for (int h = 0; h < num_vs; ++h) { bool line_ended = false; while (!line_ended) { for (int i = 0; ; ++i) { s[i] = getc(f); if ('9' < s[i] || s[i] < '0') { line_ended = s[i] == '\n'; if (i != 0) { s[i] = '\0'; const int t = atoi(s); al[t].push_back(h); ++num_es; if (h == t) ++num_self_es; } break; } } } } heads = new int[num_es]; tails = new int[num_vs]; for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { tails[tails_i] = heads_i; for (int j = 0; j < (int) al[tails_i].size(); ++j) heads[heads_i++] = al[tails_i][j]; } delete[] al; } prpack_base_graph::prpack_base_graph(int nverts, int nedges, std::pair* edges) { initialize(); num_vs = nverts; num_es = nedges; // fill in heads and tails num_self_es = 0; int* hs = new int[num_es]; int* ts = new int[num_es]; tails = new int[num_vs]; memset(tails, 0, num_vs*sizeof(tails[0])); for (int i = 0; i < num_es; ++i) { assert(edges[i].first >= 0 && edges[i].first < num_vs); assert(edges[i].second >= 0 && edges[i].second < num_vs); hs[i] = edges[i].first; ts[i] = edges[i].second; ++tails[ts[i]]; if (hs[i] == ts[i]) ++num_self_es; } for (int i = 0, sum = 0; i < num_vs; ++i) { int temp = sum; sum += tails[i]; tails[i] = temp; } heads = new int[num_es]; int* osets = new int[num_vs]; memset(osets, 0, num_vs*sizeof(osets[0])); for (int i = 0; i < num_es; ++i) heads[tails[ts[i]] + osets[ts[i]]++] = hs[i]; // clean up delete[] hs; delete[] ts; delete[] osets; } /** Normalize the edge weights to sum to one. */ void prpack_base_graph::normalize_weights() { if (!vals) { // skip normalizing weights if not using values return; } std::vector rowsums(num_vs,0.); // the graph is in a compressed in-edge list. for (int i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_datatype.h" #include "igraph_interface.h" #include "igraph_attributes.h" #include "igraph_memory.h" #include /* memset & co. */ #include "config.h" /* Internal functions */ int igraph_i_create_start(igraph_vector_t *res, igraph_vector_t *el, igraph_vector_t *index, igraph_integer_t nodes); /** * \section about_basic_interface * * This is the very minimal API in \a igraph. All the other * functions use this minimal set for creating and manipulating * graphs. * * This is a very important principle since it makes possible to * implement other data representations by implementing only this * minimal set. */ /** * \ingroup interface * \function igraph_empty * \brief Creates an empty graph with some vertices and no edges. * * * The most basic constructor, all the other constructors should call * this to create a minimal graph object. Our use of the term "empty graph" * in the above description should be distinguished from the mathematical * definition of the empty or null graph. Strictly speaking, the empty or null * graph in graph theory is the graph with no vertices and no edges. However * by "empty graph" as used in \c igraph we mean a graph having zero or more * vertices, but no edges. * \param graph Pointer to a not-yet initialized graph object. * \param n The number of vertices in the graph, a non-negative * integer number is expected. * \param directed Boolean; whether the graph is directed or not. Supported * values are: * \clist * \cli IGRAPH_DIRECTED * The graph will be \em directed. * \cli IGRAPH_UNDIRECTED * The graph will be \em undirected. * \endclist * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * * Time complexity: O(|V|) for a graph with * |V| vertices (and no edges). * * \example examples/simple/igraph_empty.c */ int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { return igraph_empty_attrs(graph, n, directed, 0); } /** * \ingroup interface * \function igraph_empty_attrs * \brief Creates an empty graph with some vertices, no edges and some graph attributes. * * * Use this instead of \ref igraph_empty() if you wish to add some graph * attributes right after initialization. This function is currently * not very interesting for the ordinary user. Just supply 0 here or * use \ref igraph_empty(). * \param graph Pointer to a not-yet initialized graph object. * \param n The number of vertices in the graph; a non-negative * integer number is expected. * \param directed Boolean; whether the graph is directed or not. Supported * values are: * \clist * \cli IGRAPH_DIRECTED * Create a \em directed graph. * \cli IGRAPH_UNDIRECTED * Create an \em undirected graph. * \endclist * \param attr The attributes. * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * * Time complexity: O(|V|) for a graph with * |V| vertices (and no edges). */ int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void* attr) { if (n<0) { IGRAPH_ERROR("cannot create empty graph with negative number of vertices", IGRAPH_EINVAL); } if (!IGRAPH_FINITE(n)) { IGRAPH_ERROR("number of vertices is not finite (NA, NaN or Inf)", IGRAPH_EINVAL); } graph->n=0; graph->directed=directed; IGRAPH_VECTOR_INIT_FINALLY(&graph->from, 0); IGRAPH_VECTOR_INIT_FINALLY(&graph->to, 0); IGRAPH_VECTOR_INIT_FINALLY(&graph->oi, 0); IGRAPH_VECTOR_INIT_FINALLY(&graph->ii, 0); IGRAPH_VECTOR_INIT_FINALLY(&graph->os, 1); IGRAPH_VECTOR_INIT_FINALLY(&graph->is, 1); VECTOR(graph->os)[0]=0; VECTOR(graph->is)[0]=0; /* init attributes */ graph->attr=0; IGRAPH_CHECK(igraph_i_attribute_init(graph, attr)); /* add the vertices */ IGRAPH_CHECK(igraph_add_vertices(graph, n, 0)); IGRAPH_FINALLY_CLEAN(6); return 0; } /** * \ingroup interface * \function igraph_destroy * \brief Frees the memory allocated for a graph object. * * * This function should be called for every graph object exactly once. * * * This function invalidates all iterators (of course), but the * iterators of a graph should be destroyed before the graph itself * anyway. * \param graph Pointer to the graph to free. * \return Error code. * * Time complexity: operating system specific. */ int igraph_destroy(igraph_t *graph) { IGRAPH_I_ATTRIBUTE_DESTROY(graph); igraph_vector_destroy(&graph->from); igraph_vector_destroy(&graph->to); igraph_vector_destroy(&graph->oi); igraph_vector_destroy(&graph->ii); igraph_vector_destroy(&graph->os); igraph_vector_destroy(&graph->is); return 0; } /** * \ingroup interface * \function igraph_copy * \brief Creates an exact (deep) copy of a graph. * * * This function deeply copies a graph object to create an exact * replica of it. The new replica should be destroyed by calling * \ref igraph_destroy() on it when not needed any more. * * * You can also create a shallow copy of a graph by simply using the * standard assignment operator, but be careful and do \em not * destroy a shallow replica. To avoid this mistake, creating shallow * copies is not recommended. * \param to Pointer to an uninitialized graph object. * \param from Pointer to the graph object to copy. * \return Error code. * * Time complexity: O(|V|+|E|) for a * graph with |V| vertices and * |E| edges. * * \example examples/simple/igraph_copy.c */ int igraph_copy(igraph_t *to, const igraph_t *from) { to->n=from->n; to->directed=from->directed; IGRAPH_CHECK(igraph_vector_copy(&to->from, &from->from)); IGRAPH_FINALLY(igraph_vector_destroy, &to->from); IGRAPH_CHECK(igraph_vector_copy(&to->to, &from->to)); IGRAPH_FINALLY(igraph_vector_destroy, &to->to); IGRAPH_CHECK(igraph_vector_copy(&to->oi, &from->oi)); IGRAPH_FINALLY(igraph_vector_destroy, &to->oi); IGRAPH_CHECK(igraph_vector_copy(&to->ii, &from->ii)); IGRAPH_FINALLY(igraph_vector_destroy, &to->ii); IGRAPH_CHECK(igraph_vector_copy(&to->os, &from->os)); IGRAPH_FINALLY(igraph_vector_destroy, &to->os); IGRAPH_CHECK(igraph_vector_copy(&to->is, &from->is)); IGRAPH_FINALLY(igraph_vector_destroy, &to->is); IGRAPH_I_ATTRIBUTE_COPY(to, from, 1,1,1); /* does IGRAPH_CHECK */ IGRAPH_FINALLY_CLEAN(6); return 0; } /** * \ingroup interface * \function igraph_add_edges * \brief Adds edges to a graph object. * * * The edges are given in a vector, the * first two elements define the first edge (the order is * from, to for directed * graphs). The vector * should contain even number of integer numbers between zero and the * number of vertices in the graph minus one (inclusive). If you also * want to add new vertices, call igraph_add_vertices() first. * \param graph The graph to which the edges will be added. * \param edges The edges themselves. * \param attr The attributes of the new edges, only used by high level * interfaces currently, you can supply 0 here. * \return Error code: * \c IGRAPH_EINVEVECTOR: invalid (odd) * edges vector length, \c IGRAPH_EINVVID: * invalid vertex id in edges vector. * * This function invalidates all iterators. * * * Time complexity: O(|V|+|E|) where * |V| is the number of vertices and * |E| is the number of * edges in the \em new, extended graph. * * \example examples/simple/igraph_add_edges.c */ int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, void *attr) { long int no_of_edges=igraph_vector_size(&graph->from); long int edges_to_add=igraph_vector_size(edges)/2; long int i=0; igraph_error_handler_t *oldhandler; int ret1, ret2; igraph_vector_t newoi, newii; igraph_bool_t directed=igraph_is_directed(graph); if (igraph_vector_size(edges) % 2 != 0) { IGRAPH_ERROR("invalid (odd) length of edges vector", IGRAPH_EINVEVECTOR); } if (!igraph_vector_isininterval(edges, 0, igraph_vcount(graph)-1)) { IGRAPH_ERROR("cannot add edges", IGRAPH_EINVVID); } /* from & to */ IGRAPH_CHECK(igraph_vector_reserve(&graph->from, no_of_edges+edges_to_add)); IGRAPH_CHECK(igraph_vector_reserve(&graph->to , no_of_edges+edges_to_add)); while (i VECTOR(*edges)[i+1]) { igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ igraph_vector_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ } else { igraph_vector_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ } } /* disable the error handler temporarily */ oldhandler=igraph_set_error_handler(igraph_error_handler_ignore); /* oi & ii */ ret1=igraph_vector_init(&newoi, no_of_edges); ret2=igraph_vector_init(&newii, no_of_edges); if (ret1 != 0 || ret2 != 0) { igraph_vector_resize(&graph->from, no_of_edges); /* gets smaller */ igraph_vector_resize(&graph->to, no_of_edges); /* gets smaller */ igraph_set_error_handler(oldhandler); IGRAPH_ERROR("cannot add edges", IGRAPH_ERROR_SELECT_2(ret1, ret2)); } ret1=igraph_vector_order(&graph->from, &graph->to, &newoi, graph->n); ret2=igraph_vector_order(&graph->to , &graph->from, &newii, graph->n); if (ret1 != 0 || ret2 != 0) { igraph_vector_resize(&graph->from, no_of_edges); igraph_vector_resize(&graph->to, no_of_edges); igraph_vector_destroy(&newoi); igraph_vector_destroy(&newii); igraph_set_error_handler(oldhandler); IGRAPH_ERROR("cannot add edges", IGRAPH_ERROR_SELECT_2(ret1, ret2)); } /* Attributes */ if (graph->attr) { igraph_set_error_handler(oldhandler); ret1=igraph_i_attribute_add_edges(graph, edges, attr); igraph_set_error_handler(igraph_error_handler_ignore); if (ret1 != 0) { igraph_vector_resize(&graph->from, no_of_edges); igraph_vector_resize(&graph->to, no_of_edges); igraph_vector_destroy(&newoi); igraph_vector_destroy(&newii); igraph_set_error_handler(oldhandler); IGRAPH_ERROR("cannot add edges", ret1); } } /* os & is, its length does not change, error safe */ igraph_i_create_start(&graph->os, &graph->from, &newoi, graph->n); igraph_i_create_start(&graph->is, &graph->to , &newii, graph->n); /* everything went fine */ igraph_vector_destroy(&graph->oi); igraph_vector_destroy(&graph->ii); graph->oi=newoi; graph->ii=newii; igraph_set_error_handler(oldhandler); return 0; } /** * \ingroup interface * \function igraph_add_vertices * \brief Adds vertices to a graph. * * * This function invalidates all iterators. * * \param graph The graph object to extend. * \param nv Non-negative integer giving the number of * vertices to add. * \param attr The attributes of the new vertices, only used by * high level interfaces, you can supply 0 here. * \return Error code: * \c IGRAPH_EINVAL: invalid number of new * vertices. * * Time complexity: O(|V|) where * |V| is * the number of vertices in the \em new, extended graph. * * \example examples/simple/igraph_add_vertices.c */ int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { long int ec=igraph_ecount(graph); long int i; if (nv < 0) { IGRAPH_ERROR("cannot add negative number of vertices", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_reserve(&graph->os, graph->n+nv+1)); IGRAPH_CHECK(igraph_vector_reserve(&graph->is, graph->n+nv+1)); igraph_vector_resize(&graph->os, graph->n+nv+1); /* reserved */ igraph_vector_resize(&graph->is, graph->n+nv+1); /* reserved */ for (i=graph->n+1; in+nv+1; i++) { VECTOR(graph->os)[i]=ec; VECTOR(graph->is)[i]=ec; } graph->n += nv; if (graph->attr) { IGRAPH_CHECK(igraph_i_attribute_add_vertices(graph, nv, attr)); } return 0; } /** * \ingroup interface * \function igraph_delete_edges * \brief Removes edges from a graph. * * * The edges to remove are given as an edge selector. * * * This function cannot remove vertices, they will be kept, even if * they lose all their edges. * * * This function invalidates all iterators. * \param graph The graph to work on. * \param edges The edges to remove. * \return Error code. * * Time complexity: O(|V|+|E|) where * |V| * and |E| are the number of vertices * and edges in the \em original graph, respectively. * * \example examples/simple/igraph_delete_edges.c */ int igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { long int no_of_edges=igraph_ecount(graph); long int no_of_nodes=igraph_vcount(graph); long int edges_to_remove=0; long int remaining_edges; igraph_eit_t eit; igraph_vector_t newfrom, newto, newoi; int *mark; long int i, j; mark=igraph_Calloc(no_of_edges, int); if (mark==0) { IGRAPH_ERROR("Cannot delete edges", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, mark); IGRAPH_CHECK(igraph_eit_create(graph, edges, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); for (IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { long int e=IGRAPH_EIT_GET(eit); if (mark[e]==0) { edges_to_remove++; mark[e]++; } } remaining_edges=no_of_edges-edges_to_remove; /* We don't need the iterator any more */ igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); IGRAPH_VECTOR_INIT_FINALLY(&newfrom, remaining_edges); IGRAPH_VECTOR_INIT_FINALLY(&newto, remaining_edges); /* Actually remove the edges, move from pos i to pos j in newfrom/newto */ for (i=0,j=0; jfrom)[i]; VECTOR(newto)[j] = VECTOR(graph->to)[i]; j++; } } /* Create index, this might require additional memory */ IGRAPH_VECTOR_INIT_FINALLY(&newoi, remaining_edges); IGRAPH_CHECK(igraph_vector_order(&newfrom, &newto, &newoi, no_of_nodes)); IGRAPH_CHECK(igraph_vector_order(&newto, &newfrom, &graph->ii, no_of_nodes)); /* Edge attributes, we need an index that gives the ids of the original edges for every new edge. */ if (graph->attr) { igraph_vector_t idx; IGRAPH_VECTOR_INIT_FINALLY(&idx, remaining_edges); for (i=0, j=0; ifrom); igraph_vector_destroy(&graph->to); igraph_vector_destroy(&graph->oi); graph->from=newfrom; graph->to=newto; graph->oi=newoi; IGRAPH_FINALLY_CLEAN(3); igraph_Free(mark); IGRAPH_FINALLY_CLEAN(1); /* Create start vectors, no memory is needed for this */ igraph_i_create_start(&graph->os, &graph->from, &graph->oi, (igraph_integer_t) no_of_nodes); igraph_i_create_start(&graph->is, &graph->to, &graph->ii, (igraph_integer_t) no_of_nodes); /* Nothing to deallocate... */ return 0; } /** * \ingroup interface * \function igraph_delete_vertices * \brief Removes vertices (with all their edges) from the graph. * * * This function changes the ids of the vertices (except in some very * special cases, but these should not be relied on anyway). * * * This function invalidates all iterators. * * \param graph The graph to work on. * \param vertices The ids of the vertices to remove in a * vector. The vector may contain the same id more * than once. * \return Error code: * \c IGRAPH_EINVVID: invalid vertex id. * * Time complexity: O(|V|+|E|), * |V| and * |E| are the number of vertices and * edges in the original graph. * * \example examples/simple/igraph_delete_vertices.c */ int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) { return igraph_delete_vertices_idx(graph, vertices, /* idx= */ 0, /* invidx= */ 0); } int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, igraph_vector_t *idx, igraph_vector_t *invidx) { long int no_of_edges=igraph_ecount(graph); long int no_of_nodes=igraph_vcount(graph); igraph_vector_t edge_recoding, vertex_recoding; igraph_vector_t *my_vertex_recoding=&vertex_recoding; igraph_vit_t vit; igraph_t newgraph; long int i, j; long int remaining_vertices, remaining_edges; if (idx) { my_vertex_recoding=idx; IGRAPH_CHECK(igraph_vector_resize(idx, no_of_nodes)); igraph_vector_null(idx); } else { IGRAPH_VECTOR_INIT_FINALLY(&vertex_recoding, no_of_nodes); } IGRAPH_VECTOR_INIT_FINALLY(&edge_recoding, no_of_edges); IGRAPH_CHECK(igraph_vit_create(graph, vertices, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); /* mark the vertices to delete */ for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit) ) { long int vertex=IGRAPH_VIT_GET(vit); if (vertex < 0 || vertex >= no_of_nodes) { IGRAPH_ERROR("Cannot delete vertices", IGRAPH_EINVVID); } VECTOR(*my_vertex_recoding)[vertex]=1; } /* create vertex recoding vector */ for (remaining_vertices=0, i=0; ifrom)[i]; long int to=(long int) VECTOR(graph->to)[i]; if (VECTOR(*my_vertex_recoding)[from] != 0 && VECTOR(*my_vertex_recoding)[to ] != 0) { VECTOR(edge_recoding)[i]=remaining_edges+1; remaining_edges++; } } /* start creating the graph */ newgraph.n=(igraph_integer_t) remaining_vertices; newgraph.directed=graph->directed; /* allocate vectors */ IGRAPH_VECTOR_INIT_FINALLY(&newgraph.from, remaining_edges); IGRAPH_VECTOR_INIT_FINALLY(&newgraph.to, remaining_edges); IGRAPH_VECTOR_INIT_FINALLY(&newgraph.oi, remaining_edges); IGRAPH_VECTOR_INIT_FINALLY(&newgraph.ii, remaining_edges); IGRAPH_VECTOR_INIT_FINALLY(&newgraph.os, remaining_vertices+1); IGRAPH_VECTOR_INIT_FINALLY(&newgraph.is, remaining_vertices+1); /* Add the edges */ for (i=0, j=0; j0) { long int from=(long int) VECTOR(graph->from)[i]; long int to=(long int) VECTOR(graph->to )[i]; VECTOR(newgraph.from)[j]=VECTOR(*my_vertex_recoding)[from]-1; VECTOR(newgraph.to )[j]=VECTOR(*my_vertex_recoding)[to]-1; j++; } } /* update oi & ii */ IGRAPH_CHECK(igraph_vector_order(&newgraph.from, &newgraph.to, &newgraph.oi, remaining_vertices)); IGRAPH_CHECK(igraph_vector_order(&newgraph.to, &newgraph.from, &newgraph.ii, remaining_vertices)); IGRAPH_CHECK(igraph_i_create_start(&newgraph.os, &newgraph.from, &newgraph.oi, (igraph_integer_t) remaining_vertices)); IGRAPH_CHECK(igraph_i_create_start(&newgraph.is, &newgraph.to, &newgraph.ii, (igraph_integer_t) remaining_vertices)); /* attributes */ IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, /*graph=*/ 1, /*vertex=*/0, /*edge=*/0); IGRAPH_FINALLY_CLEAN(6); IGRAPH_FINALLY(igraph_destroy, &newgraph); if (newgraph.attr) { igraph_vector_t iidx; IGRAPH_VECTOR_INIT_FINALLY(&iidx, remaining_vertices); for (i=0; in; } /** * \ingroup interface * \function igraph_ecount * \brief The number of edges in a graph. * * \param graph The graph. * \return Number of edges. * * Time complexity: O(1) */ igraph_integer_t igraph_ecount(const igraph_t *graph) { return (igraph_integer_t) igraph_vector_size(&graph->from); } /** * \ingroup interface * \function igraph_neighbors * \brief Adjacent vertices to a vertex. * * \param graph The graph to work on. * \param neis This vector will contain the result. The vector should * be initialized beforehand and will be resized. Starting from igraph * version 0.4 this vector is always sorted, the vertex ids are * in increasing order. * \param pnode The id of the node for which the adjacent vertices are * to be searched. * \param mode Defines the way adjacent vertices are searched in * directed graphs. It can have the following values: * \c IGRAPH_OUT, vertices reachable by an * edge from the specified vertex are searched; * \c IGRAPH_IN, vertices from which the * specified vertex is reachable are searched; * \c IGRAPH_ALL, both kinds of vertices are * searched. * This parameter is ignored for undirected graphs. * \return Error code: * \c IGRAPH_EINVVID: invalid vertex id. * \c IGRAPH_EINVMODE: invalid mode argument. * \c IGRAPH_ENOMEM: not enough memory. * * Time complexity: O(d), * d is the number * of adjacent vertices to the queried vertex. * * \example examples/simple/igraph_neighbors.c */ int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t pnode, igraph_neimode_t mode) { long int length=0, idx=0; long int i, j; long int node=pnode; if (node<0 || node>igraph_vcount(graph)-1) { IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVMODE); } if (! graph->directed) { mode=IGRAPH_ALL; } /* Calculate needed space first & allocate it*/ if (mode & IGRAPH_OUT) { length += (VECTOR(graph->os)[node+1] - VECTOR(graph->os)[node]); } if (mode & IGRAPH_IN) { length += (VECTOR(graph->is)[node+1] - VECTOR(graph->is)[node]); } IGRAPH_CHECK(igraph_vector_resize(neis, length)); if (!igraph_is_directed(graph) || mode != IGRAPH_ALL) { if (mode & IGRAPH_OUT) { j=(long int) VECTOR(graph->os)[node+1]; for (i=(long int) VECTOR(graph->os)[node]; ito)[ (long int)VECTOR(graph->oi)[i] ]; } } if (mode & IGRAPH_IN) { j=(long int) VECTOR(graph->is)[node+1]; for (i=(long int) VECTOR(graph->is)[node]; ifrom)[ (long int)VECTOR(graph->ii)[i] ]; } } } else { /* both in- and out- neighbors in a directed graph, we need to merge the two 'vectors' */ long int jj1=(long int) VECTOR(graph->os)[node+1]; long int j2=(long int) VECTOR(graph->is)[node+1]; long int i1=(long int) VECTOR(graph->os)[node]; long int i2=(long int) VECTOR(graph->is)[node]; while (i1 < jj1 && i2 < j2) { long int n1=(long int) VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[i1] ]; long int n2=(long int) VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[i2] ]; if (n1n2) { VECTOR(*neis)[idx++]=n2; i2++; } else { VECTOR(*neis)[idx++]=n1; VECTOR(*neis)[idx++]=n2; i1++; i2++; } } while (i1 < jj1) { long int n1=(long int) VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[i1] ]; VECTOR(*neis)[idx++]=n1; i1++; } while (i2 < j2) { long int n2=(long int) VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[i2] ]; VECTOR(*neis)[idx++]=n2; i2++; } } return 0; } /** * \ingroup internal * */ int igraph_i_create_start(igraph_vector_t *res, igraph_vector_t *el, igraph_vector_t *iindex, igraph_integer_t nodes) { # define EDGE(i) (VECTOR(*el)[ (long int) VECTOR(*iindex)[(i)] ]) long int no_of_nodes; long int no_of_edges; long int i, j, idx; no_of_nodes=nodes; no_of_edges=igraph_vector_size(el); /* result */ IGRAPH_CHECK(igraph_vector_resize(res, nodes+1)); /* create the index */ if (igraph_vector_size(el)==0) { /* empty graph */ igraph_vector_null(res); } else { idx=-1; for (i=0; i<=EDGE(0); i++) { idx++; VECTOR(*res)[idx]=0; } for (i=1; iTRUE if the graph is directed, * FALSE otherwise. * * Time complexity: O(1) * * \example examples/simple/igraph_is_directed.c */ igraph_bool_t igraph_is_directed(const igraph_t *graph) { return graph->directed; } /** * \ingroup interface * \function igraph_degree * \brief The degree of some vertices in a graph. * * * This function calculates the in-, out- or total degree of the * specified vertices. * \param graph The graph. * \param res Vector, this will contain the result. It should be * initialized and will be resized to be the appropriate size. * \param vids Vector, giving the vertex ids of which the degree will * be calculated. * \param mode Defines the type of the degree. Valid modes are: * \c IGRAPH_OUT, out-degree; * \c IGRAPH_IN, in-degree; * \c IGRAPH_ALL, total degree (sum of the * in- and out-degree). * This parameter is ignored for undirected graphs. * \param loops Boolean, gives whether the self-loops should be * counted. * \return Error code: * \c IGRAPH_EINVVID: invalid vertex id. * \c IGRAPH_EINVMODE: invalid mode argument. * * Time complexity: O(v) if * loops is * TRUE, and * O(v*d) * otherwise. v is the number of * vertices for which the degree will be calculated, and * d is their (average) degree. * * \sa \ref igraph_strength() for the version that takes into account * edge weights. * * \example examples/simple/igraph_degree.c */ int igraph_degree(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) { long int nodes_to_calc; long int i, j; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("degree calculation failed", IGRAPH_EINVMODE); } nodes_to_calc=IGRAPH_VIT_SIZE(vit); if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); igraph_vector_null(res); if (loops) { if (mode & IGRAPH_OUT) { for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int vid=IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->os)[vid+1]-VECTOR(graph->os)[vid]); } } if (mode & IGRAPH_IN) { for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int vid=IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->is)[vid+1]-VECTOR(graph->is)[vid]); } } } else { /* no loops */ if (mode & IGRAPH_OUT) { for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int vid=IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->os)[vid+1]-VECTOR(graph->os)[vid]); for (j=(long int) VECTOR(graph->os)[vid]; jos)[vid+1]; j++) { if (VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[j] ]==vid) { VECTOR(*res)[i] -= 1; } } } } if (mode & IGRAPH_IN) { for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { long int vid=IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->is)[vid+1]-VECTOR(graph->is)[vid]); for (j=(long int) VECTOR(graph->is)[vid]; jis)[vid+1]; j++) { if (VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[j] ]==vid) { VECTOR(*res)[i] -= 1; } } } } } /* loops */ igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_edge * \brief Gives the head and tail vertices of an edge. * * \param graph The graph object. * \param eid The edge id. * \param from Pointer to an \type igraph_integer_t. The tail of the edge * will be placed here. * \param to Pointer to an \type igraph_integer_t. The head of the edge * will be placed here. * \return Error code. The current implementation always returns with * success. * \sa \ref igraph_get_eid() for the opposite operation. * * Added in version 0.2. * * Time complexity: O(1). */ int igraph_edge(const igraph_t *graph, igraph_integer_t eid, igraph_integer_t *from, igraph_integer_t *to) { *from = (igraph_integer_t) VECTOR(graph->from)[(long int)eid]; *to = (igraph_integer_t) VECTOR(graph->to )[(long int)eid]; if (! igraph_is_directed(graph) && *from > *to) { igraph_integer_t tmp=*from; *from=*to; *to=tmp; } return 0; } int igraph_edges(const igraph_t *graph, igraph_es_t eids, igraph_vector_t *edges) { igraph_eit_t eit; long int n, ptr=0; IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); n=IGRAPH_EIT_SIZE(eit); IGRAPH_CHECK(igraph_vector_resize(edges, n*2)); for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { long int e=IGRAPH_EIT_GET(eit); VECTOR(*edges)[ptr++]=IGRAPH_FROM(graph, e); VECTOR(*edges)[ptr++]=IGRAPH_TO(graph, e); } igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); return 0; } /* This is an unsafe macro. Only supply variable names, i.e. no expressions as parameters, otherwise nasty things can happen */ #define BINSEARCH(start,end,value,iindex,edgelist,N,pos) \ do { \ while ((start) < (end)) { \ long int mid=(start)+((end)-(start))/2; \ long int e=(long int) VECTOR((iindex))[mid]; \ if (VECTOR((edgelist))[e] < (value)) { \ (start)=mid+1; \ } else { \ (end)=mid; \ } \ } \ if ((start)<(N)) { \ long int e=(long int) VECTOR((iindex))[(start)]; \ if (VECTOR((edgelist))[e] == (value)) { \ *(pos)=(igraph_integer_t) e; \ } \ } } while(0) #define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid) \ do { \ long int start=(long int) VECTOR(graph->os)[xfrom]; \ long int end=(long int) VECTOR(graph->os)[xfrom+1]; \ long int N=end; \ long int start2=(long int) VECTOR(graph->is)[xto]; \ long int end2=(long int) VECTOR(graph->is)[xto+1]; \ long int N2=end2; \ if (end-startoi,graph->to,N,eid); \ } else { \ BINSEARCH(start2,end2,xfrom,graph->ii,graph->from,N2,eid); \ } \ } while (0) #define FIND_UNDIRECTED_EDGE(graph,from,to,eid) \ do { \ long int xfrom1= from > to ? from : to; \ long int xto1= from > to ? to : from; \ FIND_DIRECTED_EDGE(graph,xfrom1,xto1,eid); \ } while (0) /** * \function igraph_get_eid * \brief Get the edge id from the end points of an edge. * * For undirected graphs \c pfrom and \c pto are exchangeable. * * \param graph The graph object. * \param eid Pointer to an integer, the edge id will be stored here. * \param pfrom The starting point of the edge. * \param pto The end point of the edge. * \param directed Logical constant, whether to search for directed * edges in a directed graph. Ignored for undirected graphs. * \param error Logical scalar, whether to report an error if the edge * was not found. If it is false, then -1 will be assigned to \p eid. * \return Error code. * \sa \ref igraph_edge() for the opposite operation. * * Time complexity: O(log (d)), where d is smaller of the out-degree * of \c pfrom and in-degree of \c pto if \p directed is true. If \p directed * is false, then it is O(log(d)+log(d2)), where d is the same as before and * d2 is the minimum of the out-degree of \c pto and the in-degree of \c pfrom. * * \example examples/simple/igraph_get_eid.c * * Added in version 0.2. */ int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, igraph_integer_t pfrom, igraph_integer_t pto, igraph_bool_t directed, igraph_bool_t error) { long int from=pfrom, to=pto; long int nov=igraph_vcount(graph); if (from < 0 || to < 0 || from > nov-1 || to > nov-1) { IGRAPH_ERROR("cannot get edge id", IGRAPH_EINVVID); } *eid=-1; if (igraph_is_directed(graph)) { /* Directed graph */ FIND_DIRECTED_EDGE(graph,from,to,eid); if (!directed && *eid < 0) { FIND_DIRECTED_EDGE(graph,to,from,eid); } } else { /* Undirected graph, they only have one mode */ FIND_UNDIRECTED_EDGE(graph,from,to,eid); } if (*eid < 0) { if (error) { IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); } } return IGRAPH_SUCCESS; } int igraph_get_eids_pairs(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, igraph_bool_t directed, igraph_bool_t error); int igraph_get_eids_path(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t error); int igraph_get_eids_pairs(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, igraph_bool_t directed, igraph_bool_t error) { long int n=igraph_vector_size(pairs); long int no_of_nodes=igraph_vcount(graph); long int i; igraph_integer_t eid=-1; if (n % 2 != 0) { IGRAPH_ERROR("Cannot get edge ids, invalid length of edge ids", IGRAPH_EINVAL); } if (!igraph_vector_isininterval(pairs, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID); } IGRAPH_CHECK(igraph_vector_resize(eids, n/2)); if (igraph_is_directed(graph)) { for (i=0; iVECTOR(pairs)[0] and * VECTOR(pairs)[1] give the first * pair, VECTOR(pairs)[2] and * VECTOR(pairs)[3] the second pair, etc. * * * If the \c pairs argument is a null pointer, and \c path is not a * null pointer, then the \c path is interpreted as a path given by * vertex ids and the edges along the path are returned. * * * If neither \c pairs nor \c path are null pointers, then both are * considered (first \c pairs and then \c path), and the results are * concatenated. * * * If the \c error argument is true, then it is an error to give pairs * of vertices that are not connected. Otherwise -1 is * reported for not connected vertices. * * * If there are multiple edges in the graph, then these are ignored; * i.e. for a given pair of vertex ids, always the same edge id is * returned, even if the pair is given multiple time in \c pairs or in * \c path. See \ref igraph_get_eids_multi() for a similar function * that works differently in case of multiple edges. * * \param graph The input graph. * \param eids Pointer to an initialized vector, the result is stored * here. It will be resized as needed. * \param pairs Vector giving pairs of vertices, or a null pointer. * \param path Vector giving vertex ids along a path, or a null * pointer. * \param directed Logical scalar, whether to consider edge directions * in directed graphs. This is ignored for undirected graphs. * \param error Logical scalar, whether it is an error to supply * non-connected vertices. If false, then -1 is * returned for non-connected pairs. * \return Error code. * * Time complexity: O(n log(d)), where n is the number of queried * edges and d is the average degree of the vertices. * * \sa \ref igraph_get_eid() for a single edge, \ref * igraph_get_eids_multi() for a version that handles multiple edges * better (at a cost). * * \example examples/simple/igraph_get_eids.c */ int igraph_get_eids(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, const igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t error) { if (!pairs && !path) { igraph_vector_clear(eids); return 0; } else if (pairs && !path) { return igraph_get_eids_pairs(graph, eids, pairs, directed, error); } else if (!pairs && path) { return igraph_get_eids_path(graph, eids, path, directed, error); } else { /* both */ igraph_vector_t tmp; IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_get_eids_pairs(graph, eids, pairs, directed, error)); IGRAPH_CHECK(igraph_get_eids_path(graph, &tmp, path, directed, error)); IGRAPH_CHECK(igraph_vector_append(eids, &tmp)); igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return 0; } } #undef BINSEARCH #undef FIND_DIRECTED_EDGE #undef FIND_UNDIRECTED_EDGE #define BINSEARCH(start,end,value,iindex,edgelist,N,pos,seen) \ do { \ while ((start) < (end)) { \ long int mid=(start)+((end)-(start))/2; \ long int e=(long int) VECTOR((iindex))[mid]; \ if (VECTOR((edgelist))[e] < (value)) { \ (start)=mid+1; \ } else { \ (end)=mid; \ } \ } \ if ((start)<(N)) { \ long int e=(long int) VECTOR((iindex))[(start)]; \ while ((start)<(N) && seen[e] && VECTOR(edgelist)[e] == (value)) { \ (start)++; \ e=(long int) VECTOR(iindex)[(start)]; \ } \ if ((start)<(N) && !(seen[e]) && VECTOR(edgelist)[e] == (value)) { \ *(pos)=(igraph_integer_t) e; \ } \ } } while(0) #define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid,seen) \ do { \ long int start=(long int) VECTOR(graph->os)[xfrom]; \ long int end=(long int) VECTOR(graph->os)[xfrom+1]; \ long int N=end; \ long int start2=(long int) VECTOR(graph->is)[xto]; \ long int end2=(long int) VECTOR(graph->is)[xto+1]; \ long int N2=end2; \ if (end-startoi,graph->to,N,eid,seen); \ } else { \ BINSEARCH(start2,end2,xfrom,graph->ii,graph->from,N2,eid,seen); \ } \ } while (0) #define FIND_UNDIRECTED_EDGE(graph,from,to,eid,seen) \ do { \ long int xfrom1= from > to ? from : to; \ long int xto1= from > to ? to : from; \ FIND_DIRECTED_EDGE(graph,xfrom1,xto1,eid,seen); \ } while (0) int igraph_get_eids_multipairs(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, igraph_bool_t directed, igraph_bool_t error); int igraph_get_eids_multipath(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t error); int igraph_get_eids_multipairs(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, igraph_bool_t directed, igraph_bool_t error) { long int n=igraph_vector_size(pairs); long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_bool_t *seen; long int i; igraph_integer_t eid=-1; if (n % 2 != 0) { IGRAPH_ERROR("Cannot get edge ids, invalid length of edge ids", IGRAPH_EINVAL); } if (!igraph_vector_isininterval(pairs, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID); } seen=igraph_Calloc(no_of_edges, igraph_bool_t); if (seen==0) { IGRAPH_ERROR("Cannot get edge ids", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, seen); IGRAPH_CHECK(igraph_vector_resize(eids, n/2)); if (igraph_is_directed(graph)) { for (i=0; i= 0) { seen[(long int)(eid)]=1; } else if (error) { IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); } } } else { for (i=0; i= 0) { seen[(long int)(eid)]=1; } else if (error) { IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); } } } igraph_Free(seen); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_get_eids_multipath(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t error) { long int n=igraph_vector_size(path); long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_bool_t *seen; long int i; igraph_integer_t eid=-1; if (!igraph_vector_isininterval(path, 0, no_of_nodes-1)) { IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID); } seen=igraph_Calloc(no_of_edges, igraph_bool_t); if (!seen) { IGRAPH_ERROR("Cannot get edge ids", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, seen); IGRAPH_CHECK(igraph_vector_resize(eids, n==0 ? 0 : n-1)); if (igraph_is_directed(graph)) { for (i=0; i= 0) { seen[(long int)(eid)]=1; } else if (error) { IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); } } } else { for (i=0; i= 0) { seen[(long int)(eid)]=1; } else if (error) { IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); } } } igraph_Free(seen); IGRAPH_FINALLY_CLEAN(1); return 0; } #undef BINSEARCH #undef FIND_DIRECTED_EDGE #undef FIND_UNDIRECTED_EDGE /** * \function igraph_get_eids_multi * \brief Query edge ids based on their adjacent vertices, handle multiple edges. * * This function operates in two modes. If the \c pairs argument is * not a null pointer, but the \c path argument is, then it searches * for the edge ids of all pairs of vertices given in \c pairs. The * pairs of vertex ids are taken consecutively from the vector, * i.e. VECTOR(pairs)[0] and * VECTOR(pairs)[1] give the first pair, * VECTOR(pairs)[2] and VECTOR(pairs)[3] the * second pair, etc. * * * If the \c pairs argument is a null pointer, and \c path is not a * null pointer, then the \c path is interpreted as a path given by * vertex ids and the edges along the path are returned. * * * If the \c error argument is true, then it is an error to give pairs of * vertices that are not connected. Otherwise -1 is * returned for not connected vertex pairs. * * * An error is triggered if both \c pairs and \c path are non-null * pointers. * * * This function handles multiple edges properly, i.e. if the same * pair is given multiple times and they are indeed connected by * multiple edges, then each time a different edge id is reported. * * \param graph The input graph. * \param eids Pointer to an initialized vector, the result is stored * here. It will be resized as needed. * \param pairs Vector giving pairs of vertices, or a null pointer. * \param path Vector giving vertex ids along a path, or a null * pointer. * \param directed Logical scalar, whether to consider edge directions * in directed graphs. This is ignored for undirected graphs. * \param error Logical scalar, whether to report an error if * non-connected vertices are specified. If false, then -1 * is returned for non-connected vertex pairs. * \return Error code. * * Time complexity: O(|E|+n log(d)), where |E| is the number of edges * in the graph, n is the number of queried edges and d is the average * degree of the vertices. * * \sa \ref igraph_get_eid() for a single edge, \ref * igraph_get_eids() for a faster version that does not handle * multiple edges. */ int igraph_get_eids_multi(const igraph_t *graph, igraph_vector_t *eids, const igraph_vector_t *pairs, const igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t error) { if (!pairs && !path) { igraph_vector_clear(eids); return 0; } else if (pairs && !path) { return igraph_get_eids_multipairs(graph, eids, pairs, directed, error); } else if (!pairs && path) { return igraph_get_eids_multipath(graph, eids, path, directed, error); } else { /* both */ IGRAPH_ERROR("Give `pairs' or `path' but not both", IGRAPH_EINVAL); } } /** * \function igraph_adjacent * \brief Gives the incident edges of a vertex. * * This function was superseded by \ref igraph_incident() in igraph 0.6. * Please use \ref igraph_incident() instead of this function. * * * Added in version 0.2, deprecated in version 0.6. */ int igraph_adjacent(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t pnode, igraph_neimode_t mode) { IGRAPH_WARNING("igraph_adjacent is deprecated, use igraph_incident"); return igraph_incident(graph, eids, pnode, mode); } /** * \function igraph_incident * \brief Gives the incident edges of a vertex. * * \param graph The graph object. * \param eids An initialized \type vector_t object. It will be resized * to hold the result. * \param pnode A vertex id. * \param mode Specifies what kind of edges to include for directed * graphs. \c IGRAPH_OUT means only outgoing edges, \c IGRAPH_IN only * incoming edges, \c IGRAPH_ALL both. This parameter is ignored for * undirected graphs. * \return Error code. \c IGRAPH_EINVVID: invalid \p pnode argument, * \c IGRAPH_EINVMODE: invalid \p mode argument. * * Added in version 0.2. * * Time complexity: O(d), the number of incident edges to \p pnode. */ int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t pnode, igraph_neimode_t mode) { long int length=0, idx=0; long int i, j; long int node=pnode; if (node<0 || node>igraph_vcount(graph)-1) { IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVMODE); } if (! graph->directed) { mode=IGRAPH_ALL; } /* Calculate needed space first & allocate it*/ if (mode & IGRAPH_OUT) { length += (VECTOR(graph->os)[node+1] - VECTOR(graph->os)[node]); } if (mode & IGRAPH_IN) { length += (VECTOR(graph->is)[node+1] - VECTOR(graph->is)[node]); } IGRAPH_CHECK(igraph_vector_resize(eids, length)); if (mode & IGRAPH_OUT) { j=(long int) VECTOR(graph->os)[node+1]; for (i=(long int) VECTOR(graph->os)[node]; ioi)[i]; } } if (mode & IGRAPH_IN) { j=(long int) VECTOR(graph->is)[node+1]; for (i=(long int) VECTOR(graph->is)[node]; iii)[i]; } } return 0; } igraph/src/amd_1.c0000644000176000001440000001504312325527072013500 0ustar ripleyusers/* ========================================================================= */ /* === AMD_1 =============================================================== */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* AMD_1: Construct A+A' for a sparse matrix A and perform the AMD ordering. * * The n-by-n sparse matrix A can be unsymmetric. It is stored in MATLAB-style * compressed-column form, with sorted row indices in each column, and no * duplicate entries. Diagonal entries may be present, but they are ignored. * Row indices of column j of A are stored in Ai [Ap [j] ... Ap [j+1]-1]. * Ap [0] must be zero, and nz = Ap [n] is the number of entries in A. The * size of the matrix, n, must be greater than or equal to zero. * * This routine must be preceded by a call to AMD_aat, which computes the * number of entries in each row/column in A+A', excluding the diagonal. * Len [j], on input, is the number of entries in row/column j of A+A'. This * routine constructs the matrix A+A' and then calls AMD_2. No error checking * is performed (this was done in AMD_valid). */ #include "amd_internal.h" GLOBAL void AMD_1 ( Int n, /* n > 0 */ const Int Ap [ ], /* input of size n+1, not modified */ const Int Ai [ ], /* input of size nz = Ap [n], not modified */ Int P [ ], /* size n output permutation */ Int Pinv [ ], /* size n output inverse permutation */ Int Len [ ], /* size n input, undefined on output */ Int slen, /* slen >= sum (Len [0..n-1]) + 7n, * ideally slen = 1.2 * sum (Len) + 8n */ Int S [ ], /* size slen workspace */ double Control [ ], /* input array of size AMD_CONTROL */ double Info [ ] /* output array of size AMD_INFO */ ) { Int i, j, k, p, pfree, iwlen, pj, p1, p2, pj2, *Iw, *Pe, *Nv, *Head, *Elen, *Degree, *s, *W, *Sp, *Tp ; /* --------------------------------------------------------------------- */ /* construct the matrix for AMD_2 */ /* --------------------------------------------------------------------- */ ASSERT (n > 0) ; iwlen = slen - 6*n ; s = S ; Pe = s ; s += n ; Nv = s ; s += n ; Head = s ; s += n ; Elen = s ; s += n ; Degree = s ; s += n ; W = s ; s += n ; Iw = s ; s += iwlen ; ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; /* construct the pointers for A+A' */ Sp = Nv ; /* use Nv and W as workspace for Sp and Tp [ */ Tp = W ; pfree = 0 ; for (j = 0 ; j < n ; j++) { Pe [j] = pfree ; Sp [j] = pfree ; pfree += Len [j] ; } /* Note that this restriction on iwlen is slightly more restrictive than * what is strictly required in AMD_2. AMD_2 can operate with no elbow * room at all, but it will be very slow. For better performance, at * least size-n elbow room is enforced. */ ASSERT (iwlen >= pfree + n) ; #ifndef NDEBUG for (p = 0 ; p < iwlen ; p++) Iw [p] = EMPTY ; #endif for (k = 0 ; k < n ; k++) { AMD_DEBUG1 (("Construct row/column k= "ID" of A+A'\n", k)) ; p1 = Ap [k] ; p2 = Ap [k+1] ; /* construct A+A' */ for (p = p1 ; p < p2 ; ) { /* scan the upper triangular part of A */ j = Ai [p] ; ASSERT (j >= 0 && j < n) ; if (j < k) { /* entry A (j,k) in the strictly upper triangular part */ ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; ASSERT (Sp [k] < (k == n-1 ? pfree : Pe [k+1])) ; Iw [Sp [j]++] = k ; Iw [Sp [k]++] = j ; p++ ; } else if (j == k) { /* skip the diagonal */ p++ ; break ; } else /* j > k */ { /* first entry below the diagonal */ break ; } /* scan lower triangular part of A, in column j until reaching * row k. Start where last scan left off. */ ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; pj2 = Ap [j+1] ; for (pj = Tp [j] ; pj < pj2 ; ) { i = Ai [pj] ; ASSERT (i >= 0 && i < n) ; if (i < k) { /* A (i,j) is only in the lower part, not in upper */ ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; Iw [Sp [i]++] = j ; Iw [Sp [j]++] = i ; pj++ ; } else if (i == k) { /* entry A (k,j) in lower part and A (j,k) in upper */ pj++ ; break ; } else /* i > k */ { /* consider this entry later, when k advances to i */ break ; } } Tp [j] = pj ; } Tp [k] = p ; } /* clean up, for remaining mismatched entries */ for (j = 0 ; j < n ; j++) { for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) { i = Ai [pj] ; ASSERT (i >= 0 && i < n) ; /* A (i,j) is only in the lower part, not in upper */ ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; Iw [Sp [i]++] = j ; Iw [Sp [j]++] = i ; } } #ifndef NDEBUG for (j = 0 ; j < n-1 ; j++) ASSERT (Sp [j] == Pe [j+1]) ; ASSERT (Sp [n-1] == pfree) ; #endif /* Tp and Sp no longer needed ] */ /* --------------------------------------------------------------------- */ /* order the matrix */ /* --------------------------------------------------------------------- */ AMD_2 (n, Pe, Iw, Len, iwlen, pfree, Nv, Pinv, P, Head, Elen, Degree, W, Control, Info) ; } igraph/src/prpack_preprocessed_schur_graph.cpp0000644000176000001440000001001512325527074021476 0ustar ripleyusers#include "prpack_preprocessed_schur_graph.h" #include #include using namespace prpack; using namespace std; void prpack_preprocessed_schur_graph::initialize() { heads = NULL; tails = NULL; vals = NULL; ii = NULL; d = NULL; num_outlinks = NULL; encoding = NULL; decoding = NULL; } void prpack_preprocessed_schur_graph::initialize_weighted(const prpack_base_graph* bg) { // permute d ii = d; d = new double[num_vs]; for (int i = 0; i < num_vs; ++i) d[encoding[i]] = ii[i]; // convert bg to head/tail format for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { ii[tails_i] = 0; tails[tails_i] = heads_i; const int decoded = decoding[tails_i]; const int start_i = bg->tails[decoded]; const int end_i = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; for (int i = start_i; i < end_i; ++i) { if (decoded == bg->heads[i]) ii[tails_i] += bg->vals[i]; else { heads[heads_i] = encoding[bg->heads[i]]; vals[heads_i] = bg->vals[i]; ++heads_i; } } } } void prpack_preprocessed_schur_graph::initialize_unweighted(const prpack_base_graph* bg) { // permute num_outlinks ii = num_outlinks; num_outlinks = new double[num_vs]; for (int i = 0; i < num_vs; ++i) num_outlinks[encoding[i]] = (ii[i] == 0) ? -1 : ii[i]; // convert bg to head/tail format for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { ii[tails_i] = 0; tails[tails_i] = heads_i; const int decoded = decoding[tails_i]; const int start_i = bg->tails[decoded]; const int end_i = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; for (int i = start_i; i < end_i; ++i) { if (decoded == bg->heads[i]) ++ii[tails_i]; else heads[heads_i++] = encoding[bg->heads[i]]; } if (ii[tails_i] > 0) ii[tails_i] /= num_outlinks[tails_i]; } } prpack_preprocessed_schur_graph::prpack_preprocessed_schur_graph(const prpack_base_graph* bg) { initialize(); // initialize instance variables num_vs = bg->num_vs; num_es = bg->num_es - bg->num_self_es; tails = new int[num_vs]; heads = new int[num_es]; const bool weighted = bg->vals != NULL; if (weighted) { vals = new double[num_vs]; d = new double[num_vs]; fill(d, d + num_vs, 1); for (int i = 0; i < bg->num_es; ++i) d[bg->heads[i]] -= bg->vals[i]; } else { num_outlinks = new double[num_vs]; fill(num_outlinks, num_outlinks + num_vs, 0); for (int i = 0; i < bg->num_es; ++i) ++num_outlinks[bg->heads[i]]; } // permute no-inlink vertices to the beginning, and no-outlink vertices to the end encoding = new int[num_vs]; decoding = new int[num_vs]; num_no_in_vs = num_no_out_vs = 0; for (int i = 0; i < num_vs; ++i) { if (bg->tails[i] == ((i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es)) { decoding[encoding[i] = num_no_in_vs] = i; ++num_no_in_vs; } else if ((weighted) ? (d[i] == 1) : (num_outlinks[i] == 0)) { decoding[encoding[i] = num_vs - 1 - num_no_out_vs] = i; ++num_no_out_vs; } } // permute everything else for (int i = 0, p = num_no_in_vs; i < num_vs; ++i) if (bg->tails[i] < ((i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es) && ((weighted) ? (d[i] < 1) : (num_outlinks[i] > 0))) decoding[encoding[i] = p++] = i; // continue initialization based off of weightedness if (weighted) initialize_weighted(bg); else initialize_unweighted(bg); } prpack_preprocessed_schur_graph::~prpack_preprocessed_schur_graph() { delete[] heads; delete[] tails; delete[] vals; delete[] ii; delete[] d; delete[] num_outlinks; delete[] encoding; delete[] decoding; } igraph/src/amd_2.c0000644000176000001440000023056212325527072013506 0ustar ripleyusers/* ========================================================================= */ /* === AMD_2 =============================================================== */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* AMD_2: performs the AMD ordering on a symmetric sparse matrix A, followed * by a postordering (via depth-first search) of the assembly tree using the * AMD_postorder routine. */ #pragma clang diagnostic ignored "-Wconversion" #include "amd_internal.h" /* ========================================================================= */ /* === clear_flag ========================================================== */ /* ========================================================================= */ static Int clear_flag (Int wflg, Int wbig, Int W [ ], Int n) { Int x ; if (wflg < 2 || wflg >= wbig) { for (x = 0 ; x < n ; x++) { if (W [x] != 0) W [x] = 1 ; } wflg = 2 ; } /* at this point, W [0..n-1] < wflg holds */ return (wflg) ; } /* ========================================================================= */ /* === AMD_2 =============================================================== */ /* ========================================================================= */ GLOBAL void AMD_2 ( Int n, /* A is n-by-n, where n > 0 */ Int Pe [ ], /* Pe [0..n-1]: index in Iw of row i on input */ Int Iw [ ], /* workspace of size iwlen. Iw [0..pfree-1] * holds the matrix on input */ Int Len [ ], /* Len [0..n-1]: length for row/column i on input */ Int iwlen, /* length of Iw. iwlen >= pfree + n */ Int pfree, /* Iw [pfree ... iwlen-1] is empty on input */ /* 7 size-n workspaces, not defined on input: */ Int Nv [ ], /* the size of each supernode on output */ Int Next [ ], /* the output inverse permutation */ Int Last [ ], /* the output permutation */ Int Head [ ], Int Elen [ ], /* the size columns of L for each supernode */ Int Degree [ ], Int W [ ], /* control parameters and output statistics */ double Control [ ], /* array of size AMD_CONTROL */ double Info [ ] /* array of size AMD_INFO */ ) { /* * Given a representation of the nonzero pattern of a symmetric matrix, A, * (excluding the diagonal) perform an approximate minimum (UMFPACK/MA38-style) * degree ordering to compute a pivot order such that the introduction of * nonzeros (fill-in) in the Cholesky factors A = LL' is kept low. At each * step, the pivot selected is the one with the minimum UMFAPACK/MA38-style * upper-bound on the external degree. This routine can optionally perform * aggresive absorption (as done by MC47B in the Harwell Subroutine * Library). * * The approximate degree algorithm implemented here is the symmetric analog of * the degree update algorithm in MA38 and UMFPACK (the Unsymmetric-pattern * MultiFrontal PACKage, both by Davis and Duff). The routine is based on the * MA27 minimum degree ordering algorithm by Iain Duff and John Reid. * * This routine is a translation of the original AMDBAR and MC47B routines, * in Fortran, with the following modifications: * * (1) dense rows/columns are removed prior to ordering the matrix, and placed * last in the output order. The presence of a dense row/column can * increase the ordering time by up to O(n^2), unless they are removed * prior to ordering. * * (2) the minimum degree ordering is followed by a postordering (depth-first * search) of the assembly tree. Note that mass elimination (discussed * below) combined with the approximate degree update can lead to the mass * elimination of nodes with lower exact degree than the current pivot * element. No additional fill-in is caused in the representation of the * Schur complement. The mass-eliminated nodes merge with the current * pivot element. They are ordered prior to the current pivot element. * Because they can have lower exact degree than the current element, the * merger of two or more of these nodes in the current pivot element can * lead to a single element that is not a "fundamental supernode". The * diagonal block can have zeros in it. Thus, the assembly tree used here * is not guaranteed to be the precise supernodal elemination tree (with * "funadmental" supernodes), and the postordering performed by this * routine is not guaranteed to be a precise postordering of the * elimination tree. * * (3) input parameters are added, to control aggressive absorption and the * detection of "dense" rows/columns of A. * * (4) additional statistical information is returned, such as the number of * nonzeros in L, and the flop counts for subsequent LDL' and LU * factorizations. These are slight upper bounds, because of the mass * elimination issue discussed above. * * (5) additional routines are added to interface this routine to MATLAB * to provide a simple C-callable user-interface, to check inputs for * errors, compute the symmetry of the pattern of A and the number of * nonzeros in each row/column of A+A', to compute the pattern of A+A', * to perform the assembly tree postordering, and to provide debugging * ouput. Many of these functions are also provided by the Fortran * Harwell Subroutine Library routine MC47A. * * (6) both int and UF_long versions are provided. In the descriptions below * and integer is and int or UF_long depending on which version is * being used. ********************************************************************** ***** CAUTION: ARGUMENTS ARE NOT CHECKED FOR ERRORS ON INPUT. ****** ********************************************************************** ** If you want error checking, a more versatile input format, and a ** ** simpler user interface, use amd_order or amd_l_order instead. ** ** This routine is not meant to be user-callable. ** ********************************************************************** * ---------------------------------------------------------------------------- * References: * ---------------------------------------------------------------------------- * * [1] Timothy A. Davis and Iain Duff, "An unsymmetric-pattern multifrontal * method for sparse LU factorization", SIAM J. Matrix Analysis and * Applications, vol. 18, no. 1, pp. 140-158. Discusses UMFPACK / MA38, * which first introduced the approximate minimum degree used by this * routine. * * [2] Patrick Amestoy, Timothy A. Davis, and Iain S. Duff, "An approximate * minimum degree ordering algorithm," SIAM J. Matrix Analysis and * Applications, vol. 17, no. 4, pp. 886-905, 1996. Discusses AMDBAR and * MC47B, which are the Fortran versions of this routine. * * [3] Alan George and Joseph Liu, "The evolution of the minimum degree * ordering algorithm," SIAM Review, vol. 31, no. 1, pp. 1-19, 1989. * We list below the features mentioned in that paper that this code * includes: * * mass elimination: * Yes. MA27 relied on supervariable detection for mass elimination. * * indistinguishable nodes: * Yes (we call these "supervariables"). This was also in the MA27 * code - although we modified the method of detecting them (the * previous hash was the true degree, which we no longer keep track * of). A supervariable is a set of rows with identical nonzero * pattern. All variables in a supervariable are eliminated together. * Each supervariable has as its numerical name that of one of its * variables (its principal variable). * * quotient graph representation: * Yes. We use the term "element" for the cliques formed during * elimination. This was also in the MA27 code. The algorithm can * operate in place, but it will work more efficiently if given some * "elbow room." * * element absorption: * Yes. This was also in the MA27 code. * * external degree: * Yes. The MA27 code was based on the true degree. * * incomplete degree update and multiple elimination: * No. This was not in MA27, either. Our method of degree update * within MC47B is element-based, not variable-based. It is thus * not well-suited for use with incomplete degree update or multiple * elimination. * * Authors, and Copyright (C) 2004 by: * Timothy A. Davis, Patrick Amestoy, Iain S. Duff, John K. Reid. * * Acknowledgements: This work (and the UMFPACK package) was supported by the * National Science Foundation (ASC-9111263, DMS-9223088, and CCR-0203270). * The UMFPACK/MA38 approximate degree update algorithm, the unsymmetric analog * which forms the basis of AMD, was developed while Tim Davis was supported by * CERFACS (Toulouse, France) in a post-doctoral position. This C version, and * the etree postorder, were written while Tim Davis was on sabbatical at * Stanford University and Lawrence Berkeley National Laboratory. * ---------------------------------------------------------------------------- * INPUT ARGUMENTS (unaltered): * ---------------------------------------------------------------------------- * n: The matrix order. Restriction: n >= 1. * * iwlen: The size of the Iw array. On input, the matrix is stored in * Iw [0..pfree-1]. However, Iw [0..iwlen-1] should be slightly larger * than what is required to hold the matrix, at least iwlen >= pfree + n. * Otherwise, excessive compressions will take place. The recommended * value of iwlen is 1.2 * pfree + n, which is the value used in the * user-callable interface to this routine (amd_order.c). The algorithm * will not run at all if iwlen < pfree. Restriction: iwlen >= pfree + n. * Note that this is slightly more restrictive than the actual minimum * (iwlen >= pfree), but AMD_2 will be very slow with no elbow room. * Thus, this routine enforces a bare minimum elbow room of size n. * * pfree: On input the tail end of the array, Iw [pfree..iwlen-1], is empty, * and the matrix is stored in Iw [0..pfree-1]. During execution, * additional data is placed in Iw, and pfree is modified so that * Iw [pfree..iwlen-1] is always the unused part of Iw. * * Control: A double array of size AMD_CONTROL containing input parameters * that affect how the ordering is computed. If NULL, then default * settings are used. * * Control [AMD_DENSE] is used to determine whether or not a given input * row is "dense". A row is "dense" if the number of entries in the row * exceeds Control [AMD_DENSE] times sqrt (n), except that rows with 16 or * fewer entries are never considered "dense". To turn off the detection * of dense rows, set Control [AMD_DENSE] to a negative number, or to a * number larger than sqrt (n). The default value of Control [AMD_DENSE] * is AMD_DEFAULT_DENSE, which is defined in amd.h as 10. * * Control [AMD_AGGRESSIVE] is used to determine whether or not aggressive * absorption is to be performed. If nonzero, then aggressive absorption * is performed (this is the default). * ---------------------------------------------------------------------------- * INPUT/OUPUT ARGUMENTS: * ---------------------------------------------------------------------------- * * Pe: An integer array of size n. On input, Pe [i] is the index in Iw of * the start of row i. Pe [i] is ignored if row i has no off-diagonal * entries. Thus Pe [i] must be in the range 0 to pfree-1 for non-empty * rows. * * During execution, it is used for both supervariables and elements: * * Principal supervariable i: index into Iw of the description of * supervariable i. A supervariable represents one or more rows of * the matrix with identical nonzero pattern. In this case, * Pe [i] >= 0. * * Non-principal supervariable i: if i has been absorbed into another * supervariable j, then Pe [i] = FLIP (j), where FLIP (j) is defined * as (-(j)-2). Row j has the same pattern as row i. Note that j * might later be absorbed into another supervariable j2, in which * case Pe [i] is still FLIP (j), and Pe [j] = FLIP (j2) which is * < EMPTY, where EMPTY is defined as (-1) in amd_internal.h. * * Unabsorbed element e: the index into Iw of the description of element * e, if e has not yet been absorbed by a subsequent element. Element * e is created when the supervariable of the same name is selected as * the pivot. In this case, Pe [i] >= 0. * * Absorbed element e: if element e is absorbed into element e2, then * Pe [e] = FLIP (e2). This occurs when the pattern of e (which we * refer to as Le) is found to be a subset of the pattern of e2 (that * is, Le2). In this case, Pe [i] < EMPTY. If element e is "null" * (it has no nonzeros outside its pivot block), then Pe [e] = EMPTY, * and e is the root of an assembly subtree (or the whole tree if * there is just one such root). * * Dense variable i: if i is "dense", then Pe [i] = EMPTY. * * On output, Pe holds the assembly tree/forest, which implicitly * represents a pivot order with identical fill-in as the actual order * (via a depth-first search of the tree), as follows. If Nv [i] > 0, * then i represents a node in the assembly tree, and the parent of i is * Pe [i], or EMPTY if i is a root. If Nv [i] = 0, then (i, Pe [i]) * represents an edge in a subtree, the root of which is a node in the * assembly tree. Note that i refers to a row/column in the original * matrix, not the permuted matrix. * * Info: A double array of size AMD_INFO. If present, (that is, not NULL), * then statistics about the ordering are returned in the Info array. * See amd.h for a description. * ---------------------------------------------------------------------------- * INPUT/MODIFIED (undefined on output): * ---------------------------------------------------------------------------- * * Len: An integer array of size n. On input, Len [i] holds the number of * entries in row i of the matrix, excluding the diagonal. The contents * of Len are undefined on output. * * Iw: An integer array of size iwlen. On input, Iw [0..pfree-1] holds the * description of each row i in the matrix. The matrix must be symmetric, * and both upper and lower triangular parts must be present. The * diagonal must not be present. Row i is held as follows: * * Len [i]: the length of the row i data structure in the Iw array. * Iw [Pe [i] ... Pe [i] + Len [i] - 1]: * the list of column indices for nonzeros in row i (simple * supervariables), excluding the diagonal. All supervariables * start with one row/column each (supervariable i is just row i). * If Len [i] is zero on input, then Pe [i] is ignored on input. * * Note that the rows need not be in any particular order, and there * may be empty space between the rows. * * During execution, the supervariable i experiences fill-in. This is * represented by placing in i a list of the elements that cause fill-in * in supervariable i: * * Len [i]: the length of supervariable i in the Iw array. * Iw [Pe [i] ... Pe [i] + Elen [i] - 1]: * the list of elements that contain i. This list is kept short * by removing absorbed elements. * Iw [Pe [i] + Elen [i] ... Pe [i] + Len [i] - 1]: * the list of supervariables in i. This list is kept short by * removing nonprincipal variables, and any entry j that is also * contained in at least one of the elements (j in Le) in the list * for i (e in row i). * * When supervariable i is selected as pivot, we create an element e of * the same name (e=i): * * Len [e]: the length of element e in the Iw array. * Iw [Pe [e] ... Pe [e] + Len [e] - 1]: * the list of supervariables in element e. * * An element represents the fill-in that occurs when supervariable i is * selected as pivot (which represents the selection of row i and all * non-principal variables whose principal variable is i). We use the * term Le to denote the set of all supervariables in element e. Absorbed * supervariables and elements are pruned from these lists when * computationally convenient. * * CAUTION: THE INPUT MATRIX IS OVERWRITTEN DURING COMPUTATION. * The contents of Iw are undefined on output. * ---------------------------------------------------------------------------- * OUTPUT (need not be set on input): * ---------------------------------------------------------------------------- * * Nv: An integer array of size n. During execution, ABS (Nv [i]) is equal to * the number of rows that are represented by the principal supervariable * i. If i is a nonprincipal or dense variable, then Nv [i] = 0. * Initially, Nv [i] = 1 for all i. Nv [i] < 0 signifies that i is a * principal variable in the pattern Lme of the current pivot element me. * After element me is constructed, Nv [i] is set back to a positive * value. * * On output, Nv [i] holds the number of pivots represented by super * row/column i of the original matrix, or Nv [i] = 0 for non-principal * rows/columns. Note that i refers to a row/column in the original * matrix, not the permuted matrix. * * Elen: An integer array of size n. See the description of Iw above. At the * start of execution, Elen [i] is set to zero for all rows i. During * execution, Elen [i] is the number of elements in the list for * supervariable i. When e becomes an element, Elen [e] = FLIP (esize) is * set, where esize is the size of the element (the number of pivots, plus * the number of nonpivotal entries). Thus Elen [e] < EMPTY. * Elen (i) = EMPTY set when variable i becomes nonprincipal. * * For variables, Elen (i) >= EMPTY holds until just before the * postordering and permutation vectors are computed. For elements, * Elen [e] < EMPTY holds. * * On output, Elen [i] is the degree of the row/column in the Cholesky * factorization of the permuted matrix, corresponding to the original row * i, if i is a super row/column. It is equal to EMPTY if i is * non-principal. Note that i refers to a row/column in the original * matrix, not the permuted matrix. * * Note that the contents of Elen on output differ from the Fortran * version (Elen holds the inverse permutation in the Fortran version, * which is instead returned in the Next array in this C version, * described below). * * Last: In a degree list, Last [i] is the supervariable preceding i, or EMPTY * if i is the head of the list. In a hash bucket, Last [i] is the hash * key for i. * * Last [Head [hash]] is also used as the head of a hash bucket if * Head [hash] contains a degree list (see the description of Head, * below). * * On output, Last [0..n-1] holds the permutation. That is, if * i = Last [k], then row i is the kth pivot row (where k ranges from 0 to * n-1). Row Last [k] of A is the kth row in the permuted matrix, PAP'. * * Next: Next [i] is the supervariable following i in a link list, or EMPTY if * i is the last in the list. Used for two kinds of lists: degree lists * and hash buckets (a supervariable can be in only one kind of list at a * time). * * On output Next [0..n-1] holds the inverse permutation. That is, if * k = Next [i], then row i is the kth pivot row. Row i of A appears as * the (Next[i])-th row in the permuted matrix, PAP'. * * Note that the contents of Next on output differ from the Fortran * version (Next is undefined on output in the Fortran version). * ---------------------------------------------------------------------------- * LOCAL WORKSPACE (not input or output - used only during execution): * ---------------------------------------------------------------------------- * * Degree: An integer array of size n. If i is a supervariable, then * Degree [i] holds the current approximation of the external degree of * row i (an upper bound). The external degree is the number of nonzeros * in row i, minus ABS (Nv [i]), the diagonal part. The bound is equal to * the exact external degree if Elen [i] is less than or equal to two. * * We also use the term "external degree" for elements e to refer to * |Le \ Lme|. If e is an element, then Degree [e] is |Le|, which is the * degree of the off-diagonal part of the element e (not including the * diagonal part). * * Head: An integer array of size n. Head is used for degree lists. * Head [deg] is the first supervariable in a degree list. All * supervariables i in a degree list Head [deg] have the same approximate * degree, namely, deg = Degree [i]. If the list Head [deg] is empty then * Head [deg] = EMPTY. * * During supervariable detection Head [hash] also serves as a pointer to * a hash bucket. If Head [hash] >= 0, there is a degree list of degree * hash. The hash bucket head pointer is Last [Head [hash]]. If * Head [hash] = EMPTY, then the degree list and hash bucket are both * empty. If Head [hash] < EMPTY, then the degree list is empty, and * FLIP (Head [hash]) is the head of the hash bucket. After supervariable * detection is complete, all hash buckets are empty, and the * (Last [Head [hash]] = EMPTY) condition is restored for the non-empty * degree lists. * * W: An integer array of size n. The flag array W determines the status of * elements and variables, and the external degree of elements. * * for elements: * if W [e] = 0, then the element e is absorbed. * if W [e] >= wflg, then W [e] - wflg is the size of the set * |Le \ Lme|, in terms of nonzeros (the sum of ABS (Nv [i]) for * each principal variable i that is both in the pattern of * element e and NOT in the pattern of the current pivot element, * me). * if wflg > W [e] > 0, then e is not absorbed and has not yet been * seen in the scan of the element lists in the computation of * |Le\Lme| in Scan 1 below. * * for variables: * during supervariable detection, if W [j] != wflg then j is * not in the pattern of variable i. * * The W array is initialized by setting W [i] = 1 for all i, and by * setting wflg = 2. It is reinitialized if wflg becomes too large (to * ensure that wflg+n does not cause integer overflow). * ---------------------------------------------------------------------------- * LOCAL INTEGERS: * ---------------------------------------------------------------------------- */ Int deg, degme, dext, lemax, e, elenme, eln, i, ilast, inext, j, jlast, jnext, k, knt1, knt2, knt3, lenj, ln, me, mindeg, nel, nleft, nvi, nvj, nvpiv, slenme, wbig, we, wflg, wnvi, ok, ndense, ncmpa, dense, aggressive ; unsigned Int hash ; /* unsigned, so that hash % n is well defined.*/ /* * deg: the degree of a variable or element * degme: size, |Lme|, of the current element, me (= Degree [me]) * dext: external degree, |Le \ Lme|, of some element e * lemax: largest |Le| seen so far (called dmax in Fortran version) * e: an element * elenme: the length, Elen [me], of element list of pivotal variable * eln: the length, Elen [...], of an element list * hash: the computed value of the hash function * i: a supervariable * ilast: the entry in a link list preceding i * inext: the entry in a link list following i * j: a supervariable * jlast: the entry in a link list preceding j * jnext: the entry in a link list, or path, following j * k: the pivot order of an element or variable * knt1: loop counter used during element construction * knt2: loop counter used during element construction * knt3: loop counter used during compression * lenj: Len [j] * ln: length of a supervariable list * me: current supervariable being eliminated, and the current * element created by eliminating that supervariable * mindeg: current minimum degree * nel: number of pivots selected so far * nleft: n - nel, the number of nonpivotal rows/columns remaining * nvi: the number of variables in a supervariable i (= Nv [i]) * nvj: the number of variables in a supervariable j (= Nv [j]) * nvpiv: number of pivots in current element * slenme: number of variables in variable list of pivotal variable * wbig: = INT_MAX - n for the int version, UF_long_max - n for the * UF_long version. wflg is not allowed to be >= wbig. * we: W [e] * wflg: used for flagging the W array. See description of Iw. * wnvi: wflg - Nv [i] * x: either a supervariable or an element * * ok: true if supervariable j can be absorbed into i * ndense: number of "dense" rows/columns * dense: rows/columns with initial degree > dense are considered "dense" * aggressive: true if aggressive absorption is being performed * ncmpa: number of garbage collections * ---------------------------------------------------------------------------- * LOCAL DOUBLES, used for statistical output only (except for alpha): * ---------------------------------------------------------------------------- */ double f, r, ndiv, s, nms_lu, nms_ldl, dmax, alpha, lnz, lnzme ; /* * f: nvpiv * r: degme + nvpiv * ndiv: number of divisions for LU or LDL' factorizations * s: number of multiply-subtract pairs for LU factorization, for the * current element me * nms_lu number of multiply-subtract pairs for LU factorization * nms_ldl number of multiply-subtract pairs for LDL' factorization * dmax: the largest number of entries in any column of L, including the * diagonal * alpha: "dense" degree ratio * lnz: the number of nonzeros in L (excluding the diagonal) * lnzme: the number of nonzeros in L (excl. the diagonal) for the * current element me * ---------------------------------------------------------------------------- * LOCAL "POINTERS" (indices into the Iw array) * ---------------------------------------------------------------------------- */ Int p, p1, p2, p3, p4, pdst, pend, pj, pme, pme1, pme2, pn, psrc ; /* * Any parameter (Pe [...] or pfree) or local variable starting with "p" (for * Pointer) is an index into Iw, and all indices into Iw use variables starting * with "p." The only exception to this rule is the iwlen input argument. * * p: pointer into lots of things * p1: Pe [i] for some variable i (start of element list) * p2: Pe [i] + Elen [i] - 1 for some variable i * p3: index of first supervariable in clean list * p4: * pdst: destination pointer, for compression * pend: end of memory to compress * pj: pointer into an element or variable * pme: pointer into the current element (pme1...pme2) * pme1: the current element, me, is stored in Iw [pme1...pme2] * pme2: the end of the current element * pn: pointer into a "clean" variable, also used to compress * psrc: source pointer, for compression */ /* ========================================================================= */ /* INITIALIZATIONS */ /* ========================================================================= */ /* Note that this restriction on iwlen is slightly more restrictive than * what is actually required in AMD_2. AMD_2 can operate with no elbow * room at all, but it will be slow. For better performance, at least * size-n elbow room is enforced. */ ASSERT (iwlen >= pfree + n) ; ASSERT (n > 0) ; /* initialize output statistics */ lnz = 0 ; ndiv = 0 ; nms_lu = 0 ; nms_ldl = 0 ; dmax = 1 ; me = EMPTY ; mindeg = 0 ; ncmpa = 0 ; nel = 0 ; lemax = 0 ; /* get control parameters */ if (Control != (double *) NULL) { alpha = Control [AMD_DENSE] ; aggressive = (Control [AMD_AGGRESSIVE] != 0) ; } else { alpha = AMD_DEFAULT_DENSE ; aggressive = AMD_DEFAULT_AGGRESSIVE ; } /* Note: if alpha is NaN, this is undefined: */ if (alpha < 0) { /* only remove completely dense rows/columns */ dense = n-2 ; } else { dense = alpha * sqrt ((double) n) ; } dense = MAX (16, dense) ; dense = MIN (n, dense) ; AMD_DEBUG1 (("\n\nAMD (debug), alpha %g, aggr. "ID"\n", alpha, aggressive)) ; for (i = 0 ; i < n ; i++) { Last [i] = EMPTY ; Head [i] = EMPTY ; Next [i] = EMPTY ; /* if separate Hhead array is used for hash buckets: * Hhead [i] = EMPTY ; */ Nv [i] = 1 ; W [i] = 1 ; Elen [i] = 0 ; Degree [i] = Len [i] ; } #ifndef NDEBUG AMD_DEBUG1 (("\n======Nel "ID" initial\n", nel)) ; AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, Last, Head, Elen, Degree, W, -1) ; #endif /* initialize wflg */ wbig = Int_MAX - n ; wflg = clear_flag (0, wbig, W, n) ; /* --------------------------------------------------------------------- */ /* initialize degree lists and eliminate dense and empty rows */ /* --------------------------------------------------------------------- */ ndense = 0 ; for (i = 0 ; i < n ; i++) { deg = Degree [i] ; ASSERT (deg >= 0 && deg < n) ; if (deg == 0) { /* ------------------------------------------------------------- * we have a variable that can be eliminated at once because * there is no off-diagonal non-zero in its row. Note that * Nv [i] = 1 for an empty variable i. It is treated just * the same as an eliminated element i. * ------------------------------------------------------------- */ Elen [i] = FLIP (1) ; nel++ ; Pe [i] = EMPTY ; W [i] = 0 ; } else if (deg > dense) { /* ------------------------------------------------------------- * Dense variables are not treated as elements, but as unordered, * non-principal variables that have no parent. They do not take * part in the postorder, since Nv [i] = 0. Note that the Fortran * version does not have this option. * ------------------------------------------------------------- */ AMD_DEBUG1 (("Dense node "ID" degree "ID"\n", i, deg)) ; ndense++ ; Nv [i] = 0 ; /* do not postorder this node */ Elen [i] = EMPTY ; nel++ ; Pe [i] = EMPTY ; } else { /* ------------------------------------------------------------- * place i in the degree list corresponding to its degree * ------------------------------------------------------------- */ inext = Head [deg] ; ASSERT (inext >= EMPTY && inext < n) ; if (inext != EMPTY) Last [inext] = i ; Next [i] = inext ; Head [deg] = i ; } } /* ========================================================================= */ /* WHILE (selecting pivots) DO */ /* ========================================================================= */ while (nel < n) { #ifndef NDEBUG AMD_DEBUG1 (("\n======Nel "ID"\n", nel)) ; if (AMD_debug >= 2) { AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, Last, Head, Elen, Degree, W, nel) ; } #endif /* ========================================================================= */ /* GET PIVOT OF MINIMUM DEGREE */ /* ========================================================================= */ /* ----------------------------------------------------------------- */ /* find next supervariable for elimination */ /* ----------------------------------------------------------------- */ ASSERT (mindeg >= 0 && mindeg < n) ; for (deg = mindeg ; deg < n ; deg++) { me = Head [deg] ; if (me != EMPTY) break ; } mindeg = deg ; ASSERT (me >= 0 && me < n) ; AMD_DEBUG1 (("=================me: "ID"\n", me)) ; /* ----------------------------------------------------------------- */ /* remove chosen variable from link list */ /* ----------------------------------------------------------------- */ inext = Next [me] ; ASSERT (inext >= EMPTY && inext < n) ; if (inext != EMPTY) Last [inext] = EMPTY ; Head [deg] = inext ; /* ----------------------------------------------------------------- */ /* me represents the elimination of pivots nel to nel+Nv[me]-1. */ /* place me itself as the first in this set. */ /* ----------------------------------------------------------------- */ elenme = Elen [me] ; nvpiv = Nv [me] ; ASSERT (nvpiv > 0) ; nel += nvpiv ; /* ========================================================================= */ /* CONSTRUCT NEW ELEMENT */ /* ========================================================================= */ /* ----------------------------------------------------------------- * At this point, me is the pivotal supervariable. It will be * converted into the current element. Scan list of the pivotal * supervariable, me, setting tree pointers and constructing new list * of supervariables for the new element, me. p is a pointer to the * current position in the old list. * ----------------------------------------------------------------- */ /* flag the variable "me" as being in Lme by negating Nv [me] */ Nv [me] = -nvpiv ; degme = 0 ; ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; if (elenme == 0) { /* ------------------------------------------------------------- */ /* construct the new element in place */ /* ------------------------------------------------------------- */ pme1 = Pe [me] ; pme2 = pme1 - 1 ; for (p = pme1 ; p <= pme1 + Len [me] - 1 ; p++) { i = Iw [p] ; ASSERT (i >= 0 && i < n && Nv [i] >= 0) ; nvi = Nv [i] ; if (nvi > 0) { /* ----------------------------------------------------- */ /* i is a principal variable not yet placed in Lme. */ /* store i in new list */ /* ----------------------------------------------------- */ /* flag i as being in Lme by negating Nv [i] */ degme += nvi ; Nv [i] = -nvi ; Iw [++pme2] = i ; /* ----------------------------------------------------- */ /* remove variable i from degree list. */ /* ----------------------------------------------------- */ ilast = Last [i] ; inext = Next [i] ; ASSERT (ilast >= EMPTY && ilast < n) ; ASSERT (inext >= EMPTY && inext < n) ; if (inext != EMPTY) Last [inext] = ilast ; if (ilast != EMPTY) { Next [ilast] = inext ; } else { /* i is at the head of the degree list */ ASSERT (Degree [i] >= 0 && Degree [i] < n) ; Head [Degree [i]] = inext ; } } } } else { /* ------------------------------------------------------------- */ /* construct the new element in empty space, Iw [pfree ...] */ /* ------------------------------------------------------------- */ p = Pe [me] ; pme1 = pfree ; slenme = Len [me] - elenme ; for (knt1 = 1 ; knt1 <= elenme + 1 ; knt1++) { if (knt1 > elenme) { /* search the supervariables in me. */ e = me ; pj = p ; ln = slenme ; AMD_DEBUG2 (("Search sv: "ID" "ID" "ID"\n", me,pj,ln)) ; } else { /* search the elements in me. */ e = Iw [p++] ; ASSERT (e >= 0 && e < n) ; pj = Pe [e] ; ln = Len [e] ; AMD_DEBUG2 (("Search element e "ID" in me "ID"\n", e,me)) ; ASSERT (Elen [e] < EMPTY && W [e] > 0 && pj >= 0) ; } ASSERT (ln >= 0 && (ln == 0 || (pj >= 0 && pj < iwlen))) ; /* --------------------------------------------------------- * search for different supervariables and add them to the * new list, compressing when necessary. this loop is * executed once for each element in the list and once for * all the supervariables in the list. * --------------------------------------------------------- */ for (knt2 = 1 ; knt2 <= ln ; knt2++) { i = Iw [pj++] ; ASSERT (i >= 0 && i < n && (i == me || Elen [i] >= EMPTY)); nvi = Nv [i] ; AMD_DEBUG2 ((": "ID" "ID" "ID" "ID"\n", i, Elen [i], Nv [i], wflg)) ; if (nvi > 0) { /* ------------------------------------------------- */ /* compress Iw, if necessary */ /* ------------------------------------------------- */ if (pfree >= iwlen) { AMD_DEBUG1 (("GARBAGE COLLECTION\n")) ; /* prepare for compressing Iw by adjusting pointers * and lengths so that the lists being searched in * the inner and outer loops contain only the * remaining entries. */ Pe [me] = p ; Len [me] -= knt1 ; /* check if nothing left of supervariable me */ if (Len [me] == 0) Pe [me] = EMPTY ; Pe [e] = pj ; Len [e] = ln - knt2 ; /* nothing left of element e */ if (Len [e] == 0) Pe [e] = EMPTY ; ncmpa++ ; /* one more garbage collection */ /* store first entry of each object in Pe */ /* FLIP the first entry in each object */ for (j = 0 ; j < n ; j++) { pn = Pe [j] ; if (pn >= 0) { ASSERT (pn >= 0 && pn < iwlen) ; Pe [j] = Iw [pn] ; Iw [pn] = FLIP (j) ; } } /* psrc/pdst point to source/destination */ psrc = 0 ; pdst = 0 ; pend = pme1 - 1 ; while (psrc <= pend) { /* search for next FLIP'd entry */ j = FLIP (Iw [psrc++]) ; if (j >= 0) { AMD_DEBUG2 (("Got object j: "ID"\n", j)) ; Iw [pdst] = Pe [j] ; Pe [j] = pdst++ ; lenj = Len [j] ; /* copy from source to destination */ for (knt3 = 0 ; knt3 <= lenj - 2 ; knt3++) { Iw [pdst++] = Iw [psrc++] ; } } } /* move the new partially-constructed element */ p1 = pdst ; for (psrc = pme1 ; psrc <= pfree-1 ; psrc++) { Iw [pdst++] = Iw [psrc] ; } pme1 = p1 ; pfree = pdst ; pj = Pe [e] ; p = Pe [me] ; } /* ------------------------------------------------- */ /* i is a principal variable not yet placed in Lme */ /* store i in new list */ /* ------------------------------------------------- */ /* flag i as being in Lme by negating Nv [i] */ degme += nvi ; Nv [i] = -nvi ; Iw [pfree++] = i ; AMD_DEBUG2 ((" s: "ID" nv "ID"\n", i, Nv [i])); /* ------------------------------------------------- */ /* remove variable i from degree link list */ /* ------------------------------------------------- */ ilast = Last [i] ; inext = Next [i] ; ASSERT (ilast >= EMPTY && ilast < n) ; ASSERT (inext >= EMPTY && inext < n) ; if (inext != EMPTY) Last [inext] = ilast ; if (ilast != EMPTY) { Next [ilast] = inext ; } else { /* i is at the head of the degree list */ ASSERT (Degree [i] >= 0 && Degree [i] < n) ; Head [Degree [i]] = inext ; } } } if (e != me) { /* set tree pointer and flag to indicate element e is * absorbed into new element me (the parent of e is me) */ AMD_DEBUG1 ((" Element "ID" => "ID"\n", e, me)) ; Pe [e] = FLIP (me) ; W [e] = 0 ; } } pme2 = pfree - 1 ; } /* ----------------------------------------------------------------- */ /* me has now been converted into an element in Iw [pme1..pme2] */ /* ----------------------------------------------------------------- */ /* degme holds the external degree of new element */ Degree [me] = degme ; Pe [me] = pme1 ; Len [me] = pme2 - pme1 + 1 ; ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; Elen [me] = FLIP (nvpiv + degme) ; /* FLIP (Elen (me)) is now the degree of pivot (including * diagonal part). */ #ifndef NDEBUG AMD_DEBUG2 (("New element structure: length= "ID"\n", pme2-pme1+1)) ; for (pme = pme1 ; pme <= pme2 ; pme++) AMD_DEBUG3 ((" "ID"", Iw[pme])); AMD_DEBUG3 (("\n")) ; #endif /* ----------------------------------------------------------------- */ /* make sure that wflg is not too large. */ /* ----------------------------------------------------------------- */ /* With the current value of wflg, wflg+n must not cause integer * overflow */ wflg = clear_flag (wflg, wbig, W, n) ; /* ========================================================================= */ /* COMPUTE (W [e] - wflg) = |Le\Lme| FOR ALL ELEMENTS */ /* ========================================================================= */ /* ----------------------------------------------------------------- * Scan 1: compute the external degrees of previous elements with * respect to the current element. That is: * (W [e] - wflg) = |Le \ Lme| * for each element e that appears in any supervariable in Lme. The * notation Le refers to the pattern (list of supervariables) of a * previous element e, where e is not yet absorbed, stored in * Iw [Pe [e] + 1 ... Pe [e] + Len [e]]. The notation Lme * refers to the pattern of the current element (stored in * Iw [pme1..pme2]). If aggressive absorption is enabled, and * (W [e] - wflg) becomes zero, then the element e will be absorbed * in Scan 2. * ----------------------------------------------------------------- */ AMD_DEBUG2 (("me: ")) ; for (pme = pme1 ; pme <= pme2 ; pme++) { i = Iw [pme] ; ASSERT (i >= 0 && i < n) ; eln = Elen [i] ; AMD_DEBUG3 ((""ID" Elen "ID": \n", i, eln)) ; if (eln > 0) { /* note that Nv [i] has been negated to denote i in Lme: */ nvi = -Nv [i] ; ASSERT (nvi > 0 && Pe [i] >= 0 && Pe [i] < iwlen) ; wnvi = wflg - nvi ; for (p = Pe [i] ; p <= Pe [i] + eln - 1 ; p++) { e = Iw [p] ; ASSERT (e >= 0 && e < n) ; we = W [e] ; AMD_DEBUG4 ((" e "ID" we "ID" ", e, we)) ; if (we >= wflg) { /* unabsorbed element e has been seen in this loop */ AMD_DEBUG4 ((" unabsorbed, first time seen")) ; we -= nvi ; } else if (we != 0) { /* e is an unabsorbed element */ /* this is the first we have seen e in all of Scan 1 */ AMD_DEBUG4 ((" unabsorbed")) ; we = Degree [e] + wnvi ; } AMD_DEBUG4 (("\n")) ; W [e] = we ; } } } AMD_DEBUG2 (("\n")) ; /* ========================================================================= */ /* DEGREE UPDATE AND ELEMENT ABSORPTION */ /* ========================================================================= */ /* ----------------------------------------------------------------- * Scan 2: for each i in Lme, sum up the degree of Lme (which is * degme), plus the sum of the external degrees of each Le for the * elements e appearing within i, plus the supervariables in i. * Place i in hash list. * ----------------------------------------------------------------- */ for (pme = pme1 ; pme <= pme2 ; pme++) { i = Iw [pme] ; ASSERT (i >= 0 && i < n && Nv [i] < 0 && Elen [i] >= 0) ; AMD_DEBUG2 (("Updating: i "ID" "ID" "ID"\n", i, Elen[i], Len [i])); p1 = Pe [i] ; p2 = p1 + Elen [i] - 1 ; pn = p1 ; hash = 0 ; deg = 0 ; ASSERT (p1 >= 0 && p1 < iwlen && p2 >= -1 && p2 < iwlen) ; /* ------------------------------------------------------------- */ /* scan the element list associated with supervariable i */ /* ------------------------------------------------------------- */ /* UMFPACK/MA38-style approximate degree: */ if (aggressive) { for (p = p1 ; p <= p2 ; p++) { e = Iw [p] ; ASSERT (e >= 0 && e < n) ; we = W [e] ; if (we != 0) { /* e is an unabsorbed element */ /* dext = | Le \ Lme | */ dext = we - wflg ; if (dext > 0) { deg += dext ; Iw [pn++] = e ; hash += e ; AMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; } else { /* external degree of e is zero, absorb e into me*/ AMD_DEBUG1 ((" Element "ID" =>"ID" (aggressive)\n", e, me)) ; ASSERT (dext == 0) ; Pe [e] = FLIP (me) ; W [e] = 0 ; } } } } else { for (p = p1 ; p <= p2 ; p++) { e = Iw [p] ; ASSERT (e >= 0 && e < n) ; we = W [e] ; if (we != 0) { /* e is an unabsorbed element */ dext = we - wflg ; ASSERT (dext >= 0) ; deg += dext ; Iw [pn++] = e ; hash += e ; AMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; } } } /* count the number of elements in i (including me): */ Elen [i] = pn - p1 + 1 ; /* ------------------------------------------------------------- */ /* scan the supervariables in the list associated with i */ /* ------------------------------------------------------------- */ /* The bulk of the AMD run time is typically spent in this loop, * particularly if the matrix has many dense rows that are not * removed prior to ordering. */ p3 = pn ; p4 = p1 + Len [i] ; for (p = p2 + 1 ; p < p4 ; p++) { j = Iw [p] ; ASSERT (j >= 0 && j < n) ; nvj = Nv [j] ; if (nvj > 0) { /* j is unabsorbed, and not in Lme. */ /* add to degree and add to new list */ deg += nvj ; Iw [pn++] = j ; hash += j ; AMD_DEBUG4 ((" s: "ID" hash "ID" Nv[j]= "ID"\n", j, hash, nvj)) ; } } /* ------------------------------------------------------------- */ /* update the degree and check for mass elimination */ /* ------------------------------------------------------------- */ /* with aggressive absorption, deg==0 is identical to the * Elen [i] == 1 && p3 == pn test, below. */ ASSERT (IMPLIES (aggressive, (deg==0) == (Elen[i]==1 && p3==pn))) ; if (Elen [i] == 1 && p3 == pn) { /* --------------------------------------------------------- */ /* mass elimination */ /* --------------------------------------------------------- */ /* There is nothing left of this node except for an edge to * the current pivot element. Elen [i] is 1, and there are * no variables adjacent to node i. Absorb i into the * current pivot element, me. Note that if there are two or * more mass eliminations, fillin due to mass elimination is * possible within the nvpiv-by-nvpiv pivot block. It is this * step that causes AMD's analysis to be an upper bound. * * The reason is that the selected pivot has a lower * approximate degree than the true degree of the two mass * eliminated nodes. There is no edge between the two mass * eliminated nodes. They are merged with the current pivot * anyway. * * No fillin occurs in the Schur complement, in any case, * and this effect does not decrease the quality of the * ordering itself, just the quality of the nonzero and * flop count analysis. It also means that the post-ordering * is not an exact elimination tree post-ordering. */ AMD_DEBUG1 ((" MASS i "ID" => parent e "ID"\n", i, me)) ; Pe [i] = FLIP (me) ; nvi = -Nv [i] ; degme -= nvi ; nvpiv += nvi ; nel += nvi ; Nv [i] = 0 ; Elen [i] = EMPTY ; } else { /* --------------------------------------------------------- */ /* update the upper-bound degree of i */ /* --------------------------------------------------------- */ /* the following degree does not yet include the size * of the current element, which is added later: */ Degree [i] = MIN (Degree [i], deg) ; /* --------------------------------------------------------- */ /* add me to the list for i */ /* --------------------------------------------------------- */ /* move first supervariable to end of list */ Iw [pn] = Iw [p3] ; /* move first element to end of element part of list */ Iw [p3] = Iw [p1] ; /* add new element, me, to front of list. */ Iw [p1] = me ; /* store the new length of the list in Len [i] */ Len [i] = pn - p1 + 1 ; /* --------------------------------------------------------- */ /* place in hash bucket. Save hash key of i in Last [i]. */ /* --------------------------------------------------------- */ /* NOTE: this can fail if hash is negative, because the ANSI C * standard does not define a % b when a and/or b are negative. * That's why hash is defined as an unsigned Int, to avoid this * problem. */ hash = hash % n ; ASSERT (((Int) hash) >= 0 && ((Int) hash) < n) ; /* if the Hhead array is not used: */ j = Head [hash] ; if (j <= EMPTY) { /* degree list is empty, hash head is FLIP (j) */ Next [i] = FLIP (j) ; Head [hash] = FLIP (i) ; } else { /* degree list is not empty, use Last [Head [hash]] as * hash head. */ Next [i] = Last [j] ; Last [j] = i ; } /* if a separate Hhead array is used: * Next [i] = Hhead [hash] ; Hhead [hash] = i ; */ Last [i] = hash ; } } Degree [me] = degme ; /* ----------------------------------------------------------------- */ /* Clear the counter array, W [...], by incrementing wflg. */ /* ----------------------------------------------------------------- */ /* make sure that wflg+n does not cause integer overflow */ lemax = MAX (lemax, degme) ; wflg += lemax ; wflg = clear_flag (wflg, wbig, W, n) ; /* at this point, W [0..n-1] < wflg holds */ /* ========================================================================= */ /* SUPERVARIABLE DETECTION */ /* ========================================================================= */ AMD_DEBUG1 (("Detecting supervariables:\n")) ; for (pme = pme1 ; pme <= pme2 ; pme++) { i = Iw [pme] ; ASSERT (i >= 0 && i < n) ; AMD_DEBUG2 (("Consider i "ID" nv "ID"\n", i, Nv [i])) ; if (Nv [i] < 0) { /* i is a principal variable in Lme */ /* --------------------------------------------------------- * examine all hash buckets with 2 or more variables. We do * this by examing all unique hash keys for supervariables in * the pattern Lme of the current element, me * --------------------------------------------------------- */ /* let i = head of hash bucket, and empty the hash bucket */ ASSERT (Last [i] >= 0 && Last [i] < n) ; hash = Last [i] ; /* if Hhead array is not used: */ j = Head [hash] ; if (j == EMPTY) { /* hash bucket and degree list are both empty */ i = EMPTY ; } else if (j < EMPTY) { /* degree list is empty */ i = FLIP (j) ; Head [hash] = EMPTY ; } else { /* degree list is not empty, restore Last [j] of head j */ i = Last [j] ; Last [j] = EMPTY ; } /* if separate Hhead array is used: * i = Hhead [hash] ; Hhead [hash] = EMPTY ; */ ASSERT (i >= EMPTY && i < n) ; AMD_DEBUG2 (("----i "ID" hash "ID"\n", i, hash)) ; while (i != EMPTY && Next [i] != EMPTY) { /* ----------------------------------------------------- * this bucket has one or more variables following i. * scan all of them to see if i can absorb any entries * that follow i in hash bucket. Scatter i into w. * ----------------------------------------------------- */ ln = Len [i] ; eln = Elen [i] ; ASSERT (ln >= 0 && eln >= 0) ; ASSERT (Pe [i] >= 0 && Pe [i] < iwlen) ; /* do not flag the first element in the list (me) */ for (p = Pe [i] + 1 ; p <= Pe [i] + ln - 1 ; p++) { ASSERT (Iw [p] >= 0 && Iw [p] < n) ; W [Iw [p]] = wflg ; } /* ----------------------------------------------------- */ /* scan every other entry j following i in bucket */ /* ----------------------------------------------------- */ jlast = i ; j = Next [i] ; ASSERT (j >= EMPTY && j < n) ; while (j != EMPTY) { /* ------------------------------------------------- */ /* check if j and i have identical nonzero pattern */ /* ------------------------------------------------- */ AMD_DEBUG3 (("compare i "ID" and j "ID"\n", i,j)) ; /* check if i and j have the same Len and Elen */ ASSERT (Len [j] >= 0 && Elen [j] >= 0) ; ASSERT (Pe [j] >= 0 && Pe [j] < iwlen) ; ok = (Len [j] == ln) && (Elen [j] == eln) ; /* skip the first element in the list (me) */ for (p = Pe [j] + 1 ; ok && p <= Pe [j] + ln - 1 ; p++) { ASSERT (Iw [p] >= 0 && Iw [p] < n) ; if (W [Iw [p]] != wflg) ok = 0 ; } if (ok) { /* --------------------------------------------- */ /* found it! j can be absorbed into i */ /* --------------------------------------------- */ AMD_DEBUG1 (("found it! j "ID" => i "ID"\n", j,i)); Pe [j] = FLIP (i) ; /* both Nv [i] and Nv [j] are negated since they */ /* are in Lme, and the absolute values of each */ /* are the number of variables in i and j: */ Nv [i] += Nv [j] ; Nv [j] = 0 ; Elen [j] = EMPTY ; /* delete j from hash bucket */ ASSERT (j != Next [j]) ; j = Next [j] ; Next [jlast] = j ; } else { /* j cannot be absorbed into i */ jlast = j ; ASSERT (j != Next [j]) ; j = Next [j] ; } ASSERT (j >= EMPTY && j < n) ; } /* ----------------------------------------------------- * no more variables can be absorbed into i * go to next i in bucket and clear flag array * ----------------------------------------------------- */ wflg++ ; i = Next [i] ; ASSERT (i >= EMPTY && i < n) ; } } } AMD_DEBUG2 (("detect done\n")) ; /* ========================================================================= */ /* RESTORE DEGREE LISTS AND REMOVE NONPRINCIPAL SUPERVARIABLES FROM ELEMENT */ /* ========================================================================= */ p = pme1 ; nleft = n - nel ; for (pme = pme1 ; pme <= pme2 ; pme++) { i = Iw [pme] ; ASSERT (i >= 0 && i < n) ; nvi = -Nv [i] ; AMD_DEBUG3 (("Restore i "ID" "ID"\n", i, nvi)) ; if (nvi > 0) { /* i is a principal variable in Lme */ /* restore Nv [i] to signify that i is principal */ Nv [i] = nvi ; /* --------------------------------------------------------- */ /* compute the external degree (add size of current element) */ /* --------------------------------------------------------- */ deg = Degree [i] + degme - nvi ; deg = MIN (deg, nleft - nvi) ; ASSERT (IMPLIES (aggressive, deg > 0) && deg >= 0 && deg < n) ; /* --------------------------------------------------------- */ /* place the supervariable at the head of the degree list */ /* --------------------------------------------------------- */ inext = Head [deg] ; ASSERT (inext >= EMPTY && inext < n) ; if (inext != EMPTY) Last [inext] = i ; Next [i] = inext ; Last [i] = EMPTY ; Head [deg] = i ; /* --------------------------------------------------------- */ /* save the new degree, and find the minimum degree */ /* --------------------------------------------------------- */ mindeg = MIN (mindeg, deg) ; Degree [i] = deg ; /* --------------------------------------------------------- */ /* place the supervariable in the element pattern */ /* --------------------------------------------------------- */ Iw [p++] = i ; } } AMD_DEBUG2 (("restore done\n")) ; /* ========================================================================= */ /* FINALIZE THE NEW ELEMENT */ /* ========================================================================= */ AMD_DEBUG2 (("ME = "ID" DONE\n", me)) ; Nv [me] = nvpiv ; /* save the length of the list for the new element me */ Len [me] = p - pme1 ; if (Len [me] == 0) { /* there is nothing left of the current pivot element */ /* it is a root of the assembly tree */ Pe [me] = EMPTY ; W [me] = 0 ; } if (elenme != 0) { /* element was not constructed in place: deallocate part of */ /* it since newly nonprincipal variables may have been removed */ pfree = p ; } /* The new element has nvpiv pivots and the size of the contribution * block for a multifrontal method is degme-by-degme, not including * the "dense" rows/columns. If the "dense" rows/columns are included, * the frontal matrix is no larger than * (degme+ndense)-by-(degme+ndense). */ if (Info != (double *) NULL) { f = nvpiv ; r = degme + ndense ; dmax = MAX (dmax, f + r) ; /* number of nonzeros in L (excluding the diagonal) */ lnzme = f*r + (f-1)*f/2 ; lnz += lnzme ; /* number of divide operations for LDL' and for LU */ ndiv += lnzme ; /* number of multiply-subtract pairs for LU */ s = f*r*r + r*(f-1)*f + (f-1)*f*(2*f-1)/6 ; nms_lu += s ; /* number of multiply-subtract pairs for LDL' */ nms_ldl += (s + lnzme)/2 ; } #ifndef NDEBUG AMD_DEBUG2 (("finalize done nel "ID" n "ID"\n ::::\n", nel, n)) ; for (pme = Pe [me] ; pme <= Pe [me] + Len [me] - 1 ; pme++) { AMD_DEBUG3 ((" "ID"", Iw [pme])) ; } AMD_DEBUG3 (("\n")) ; #endif } /* ========================================================================= */ /* DONE SELECTING PIVOTS */ /* ========================================================================= */ if (Info != (double *) NULL) { /* count the work to factorize the ndense-by-ndense submatrix */ f = ndense ; dmax = MAX (dmax, (double) ndense) ; /* number of nonzeros in L (excluding the diagonal) */ lnzme = (f-1)*f/2 ; lnz += lnzme ; /* number of divide operations for LDL' and for LU */ ndiv += lnzme ; /* number of multiply-subtract pairs for LU */ s = (f-1)*f*(2*f-1)/6 ; nms_lu += s ; /* number of multiply-subtract pairs for LDL' */ nms_ldl += (s + lnzme)/2 ; /* number of nz's in L (excl. diagonal) */ Info [AMD_LNZ] = lnz ; /* number of divide ops for LU and LDL' */ Info [AMD_NDIV] = ndiv ; /* number of multiply-subtract pairs for LDL' */ Info [AMD_NMULTSUBS_LDL] = nms_ldl ; /* number of multiply-subtract pairs for LU */ Info [AMD_NMULTSUBS_LU] = nms_lu ; /* number of "dense" rows/columns */ Info [AMD_NDENSE] = ndense ; /* largest front is dmax-by-dmax */ Info [AMD_DMAX] = dmax ; /* number of garbage collections in AMD */ Info [AMD_NCMPA] = ncmpa ; /* successful ordering */ Info [AMD_STATUS] = AMD_OK ; } /* ========================================================================= */ /* POST-ORDERING */ /* ========================================================================= */ /* ------------------------------------------------------------------------- * Variables at this point: * * Pe: holds the elimination tree. The parent of j is FLIP (Pe [j]), * or EMPTY if j is a root. The tree holds both elements and * non-principal (unordered) variables absorbed into them. * Dense variables are non-principal and unordered. * * Elen: holds the size of each element, including the diagonal part. * FLIP (Elen [e]) > 0 if e is an element. For unordered * variables i, Elen [i] is EMPTY. * * Nv: Nv [e] > 0 is the number of pivots represented by the element e. * For unordered variables i, Nv [i] is zero. * * Contents no longer needed: * W, Iw, Len, Degree, Head, Next, Last. * * The matrix itself has been destroyed. * * n: the size of the matrix. * No other scalars needed (pfree, iwlen, etc.) * ------------------------------------------------------------------------- */ /* restore Pe */ for (i = 0 ; i < n ; i++) { Pe [i] = FLIP (Pe [i]) ; } /* restore Elen, for output information, and for postordering */ for (i = 0 ; i < n ; i++) { Elen [i] = FLIP (Elen [i]) ; } /* Now the parent of j is Pe [j], or EMPTY if j is a root. Elen [e] > 0 * is the size of element e. Elen [i] is EMPTY for unordered variable i. */ #ifndef NDEBUG AMD_DEBUG2 (("\nTree:\n")) ; for (i = 0 ; i < n ; i++) { AMD_DEBUG2 ((" "ID" parent: "ID" ", i, Pe [i])) ; ASSERT (Pe [i] >= EMPTY && Pe [i] < n) ; if (Nv [i] > 0) { /* this is an element */ e = i ; AMD_DEBUG2 ((" element, size is "ID"\n", Elen [i])) ; ASSERT (Elen [e] > 0) ; } AMD_DEBUG2 (("\n")) ; } AMD_DEBUG2 (("\nelements:\n")) ; for (e = 0 ; e < n ; e++) { if (Nv [e] > 0) { AMD_DEBUG3 (("Element e= "ID" size "ID" nv "ID" \n", e, Elen [e], Nv [e])) ; } } AMD_DEBUG2 (("\nvariables:\n")) ; for (i = 0 ; i < n ; i++) { Int cnt ; if (Nv [i] == 0) { AMD_DEBUG3 (("i unordered: "ID"\n", i)) ; j = Pe [i] ; cnt = 0 ; AMD_DEBUG3 ((" j: "ID"\n", j)) ; if (j == EMPTY) { AMD_DEBUG3 ((" i is a dense variable\n")) ; } else { ASSERT (j >= 0 && j < n) ; while (Nv [j] == 0) { AMD_DEBUG3 ((" j : "ID"\n", j)) ; j = Pe [j] ; AMD_DEBUG3 ((" j:: "ID"\n", j)) ; cnt++ ; if (cnt > n) break ; } e = j ; AMD_DEBUG3 ((" got to e: "ID"\n", e)) ; } } } #endif /* ========================================================================= */ /* compress the paths of the variables */ /* ========================================================================= */ for (i = 0 ; i < n ; i++) { if (Nv [i] == 0) { /* ------------------------------------------------------------- * i is an un-ordered row. Traverse the tree from i until * reaching an element, e. The element, e, was the principal * supervariable of i and all nodes in the path from i to when e * was selected as pivot. * ------------------------------------------------------------- */ AMD_DEBUG1 (("Path compression, i unordered: "ID"\n", i)) ; j = Pe [i] ; ASSERT (j >= EMPTY && j < n) ; AMD_DEBUG3 ((" j: "ID"\n", j)) ; if (j == EMPTY) { /* Skip a dense variable. It has no parent. */ AMD_DEBUG3 ((" i is a dense variable\n")) ; continue ; } /* while (j is a variable) */ while (Nv [j] == 0) { AMD_DEBUG3 ((" j : "ID"\n", j)) ; j = Pe [j] ; AMD_DEBUG3 ((" j:: "ID"\n", j)) ; ASSERT (j >= 0 && j < n) ; } /* got to an element e */ e = j ; AMD_DEBUG3 (("got to e: "ID"\n", e)) ; /* ------------------------------------------------------------- * traverse the path again from i to e, and compress the path * (all nodes point to e). Path compression allows this code to * compute in O(n) time. * ------------------------------------------------------------- */ j = i ; /* while (j is a variable) */ while (Nv [j] == 0) { jnext = Pe [j] ; AMD_DEBUG3 (("j "ID" jnext "ID"\n", j, jnext)) ; Pe [j] = e ; j = jnext ; ASSERT (j >= 0 && j < n) ; } } } /* ========================================================================= */ /* postorder the assembly tree */ /* ========================================================================= */ AMD_postorder (n, Pe, Nv, Elen, W, /* output order */ Head, Next, Last) ; /* workspace */ /* ========================================================================= */ /* compute output permutation and inverse permutation */ /* ========================================================================= */ /* W [e] = k means that element e is the kth element in the new * order. e is in the range 0 to n-1, and k is in the range 0 to * the number of elements. Use Head for inverse order. */ for (k = 0 ; k < n ; k++) { Head [k] = EMPTY ; Next [k] = EMPTY ; } for (e = 0 ; e < n ; e++) { k = W [e] ; ASSERT ((k == EMPTY) == (Nv [e] == 0)) ; if (k != EMPTY) { ASSERT (k >= 0 && k < n) ; Head [k] = e ; } } /* construct output inverse permutation in Next, * and permutation in Last */ nel = 0 ; for (k = 0 ; k < n ; k++) { e = Head [k] ; if (e == EMPTY) break ; ASSERT (e >= 0 && e < n && Nv [e] > 0) ; Next [e] = nel ; nel += Nv [e] ; } ASSERT (nel == n - ndense) ; /* order non-principal variables (dense, & those merged into supervar's) */ for (i = 0 ; i < n ; i++) { if (Nv [i] == 0) { e = Pe [i] ; ASSERT (e >= EMPTY && e < n) ; if (e != EMPTY) { /* This is an unordered variable that was merged * into element e via supernode detection or mass * elimination of i when e became the pivot element. * Place i in order just before e. */ ASSERT (Next [i] == EMPTY && Nv [e] > 0) ; Next [i] = Next [e] ; Next [e]++ ; } else { /* This is a dense unordered variable, with no parent. * Place it last in the output order. */ Next [i] = nel++ ; } } } ASSERT (nel == n) ; AMD_DEBUG2 (("\n\nPerm:\n")) ; for (i = 0 ; i < n ; i++) { k = Next [i] ; ASSERT (k >= 0 && k < n) ; Last [k] = i ; AMD_DEBUG2 ((" perm ["ID"] = "ID"\n", k, i)) ; } } igraph/src/options.c0000644000176000001440000000272112325527073014212 0ustar ripleyusers/* options.c * * Copyright (C) 2012 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "error.h" #include "plfit.h" const plfit_continuous_options_t plfit_continuous_default_options = { /* .finite_size_correction = */ 0, /* .xmin_method = */ PLFIT_GSS_OR_LINEAR }; const plfit_discrete_options_t plfit_discrete_default_options = { /* .finite_size_correction = */ 0, /* .alpha_method = */ PLFIT_LBFGS, /* .alpha = */ { /* .min = */ 1.01, /* .max = */ 5, /* .step = */ 0.01 } }; int plfit_continuous_options_init(plfit_continuous_options_t* options) { *options = plfit_continuous_default_options; return PLFIT_SUCCESS; } int plfit_discrete_options_init(plfit_discrete_options_t* options) { *options = plfit_discrete_default_options; return PLFIT_SUCCESS; } igraph/src/dneupd.f0000644000176000001440000012567712325527073014021 0ustar ripleyusersc\BeginDoc c c\Name: igraphdneupd c c\Description: c c This subroutine returns the converged approximations to eigenvalues c of A*z = lambda*B*z and (optionally): c c (1) The corresponding approximate eigenvectors; c c (2) An orthonormal basis for the associated approximate c invariant subspace; c c (3) Both. c c There is negligible additional cost to obtain eigenvectors. An orthonormal c basis is always computed. There is an additional storage cost of n*nev c if both are requested (in this case a separate array Z must be supplied). c c The approximate eigenvalues and eigenvectors of A*z = lambda*B*z c are derived from approximate eigenvalues and eigenvectors of c of the linear operator OP prescribed by the MODE selection in the c call to DNAUPD. DNAUPD must be called before this routine is called. c These approximate eigenvalues and vectors are commonly called Ritz c values and Ritz vectors respectively. They are referred to as such c in the comments that follow. The computed orthonormal basis for the c invariant subspace corresponding to these Ritz values is referred to as a c Schur basis. c c See documentation in the header of the subroutine DNAUPD for c definition of OP as well as other terms and the relation of computed c Ritz values and Ritz vectors of OP with respect to the given problem c A*z = lambda*B*z. For a brief description, see definitions of c IPARAM(7), MODE and WHICH in the documentation of DNAUPD. c c\Usage: c call igraphdneupd c ( RVEC, HOWMNY, SELECT, DR, DI, Z, LDZ, SIGMAR, SIGMAI, WORKEV, BMAT, c N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, c LWORKL, INFO ) c c\Arguments: c RVEC LOGICAL (INPUT) c Specifies whether a basis for the invariant subspace corresponding c to the converged Ritz value approximations for the eigenproblem c A*z = lambda*B*z is computed. c c RVEC = .FALSE. Compute Ritz values only. c c RVEC = .TRUE. Compute the Ritz vectors or Schur vectors. c See Remarks below. c c HOWMNY Character*1 (INPUT) c Specifies the form of the basis for the invariant subspace c corresponding to the converged Ritz values that is to be computed. c c = 'A': Compute NEV Ritz vectors; c = 'P': Compute NEV Schur vectors; c = 'S': compute some of the Ritz vectors, specified c by the logical array SELECT. c c SELECT Logical array of dimension NCV. (INPUT) c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be c computed. To select the Ritz vector corresponding to a c Ritz value (DR(j), DI(j)), SELECT(j) must be set to .TRUE.. c If HOWMNY = 'A' or 'P', SELECT is used as internal workspace. c c DR Double precision array of dimension NEV+1. (OUTPUT) c If IPARAM(7) = 1,2 or 3 and SIGMAI=0.0 then on exit: DR contains c the real part of the Ritz approximations to the eigenvalues of c A*z = lambda*B*z. c If IPARAM(7) = 3, 4 and SIGMAI is not equal to zero, then on exit: c DR contains the real part of the Ritz values of OP computed by c DNAUPD. A further computation must be performed by the user c to transform the Ritz values computed for OP by DNAUPD to those c of the original system A*z = lambda*B*z. See remark 3 below. c c DI Double precision array of dimension NEV+1. (OUTPUT) c On exit, DI contains the imaginary part of the Ritz value c approximations to the eigenvalues of A*z = lambda*B*z associated c with DR. c c NOTE: When Ritz values are complex, they will come in complex c conjugate pairs. If eigenvectors are requested, the c corresponding Ritz vectors will also come in conjugate c pairs and the real and imaginary parts of these are c represented in two consecutive columns of the array Z c (see below). c c Z Double precision N by NEV+1 array if RVEC = .TRUE. and HOWMNY = 'A'. (OUTPUT) c On exit, if RVEC = .TRUE. and HOWMNY = 'A', then the columns of c Z represent approximate eigenvectors (Ritz vectors) corresponding c to the NCONV=IPARAM(5) Ritz values for eigensystem c A*z = lambda*B*z. c c The complex Ritz vector associated with the Ritz value c with positive imaginary part is stored in two consecutive c columns. The first column holds the real part of the Ritz c vector and the igraphsecond column holds the imaginary part. The c Ritz vector associated with the Ritz value with negative c imaginary part is simply the complex conjugate of the Ritz vector c associated with the positive imaginary part. c c If RVEC = .FALSE. or HOWMNY = 'P', then Z is not referenced. c c NOTE: If if RVEC = .TRUE. and a Schur basis is not required, c the array Z may be set equal to first NEV+1 columns of the Arnoldi c basis array V computed by DNAUPD. In this case the Arnoldi basis c will be destroyed and overwritten with the eigenvector basis. c c LDZ Integer. (INPUT) c The leading dimension of the array Z. If Ritz vectors are c desired, then LDZ >= max( 1, N ). In any case, LDZ >= 1. c c SIGMAR Double precision (INPUT) c If IPARAM(7) = 3 or 4, represents the real part of the shift. c Not referenced if IPARAM(7) = 1 or 2. c c SIGMAI Double precision (INPUT) c If IPARAM(7) = 3 or 4, represents the imaginary part of the shift. c Not referenced if IPARAM(7) = 1 or 2. See remark 3 below. c c WORKEV Double precision work array of dimension 3*NCV. (WORKSPACE) c c **** The remaining arguments MUST be the same as for the **** c **** call to DNAUPD that was just completed. **** c c NOTE: The remaining arguments c c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, c WORKD, WORKL, LWORKL, INFO c c must be passed directly to DNEUPD following the last call c to DNAUPD. These arguments MUST NOT BE MODIFIED between c the the last call to DNAUPD and the call to DNEUPD. c c Three of these parameters (V, WORKL, INFO) are also output parameters: c c V Double precision N by NCV array. (INPUT/OUTPUT) c c Upon INPUT: the NCV columns of V contain the Arnoldi basis c vectors for OP as constructed by DNAUPD . c c Upon OUTPUT: If RVEC = .TRUE. the first NCONV=IPARAM(5) columns c contain approximate Schur vectors that span the c desired invariant subspace. See Remark 2 below. c c NOTE: If the array Z has been set equal to first NEV+1 columns c of the array V and RVEC=.TRUE. and HOWMNY= 'A', then the c Arnoldi basis held by V has been overwritten by the desired c Ritz vectors. If a separate array Z has been passed then c the first NCONV=IPARAM(5) columns of V will contain approximate c Schur vectors that span the desired invariant subspace. c c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) c WORKL(1:ncv*ncv+3*ncv) contains information obtained in c igraphdnaupd. They are not changed by igraphdneupd. c WORKL(ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) holds the c real and imaginary part of the untransformed Ritz values, c the upper quasi-triangular matrix for H, and the c associated matrix representation of the invariant subspace for H. c c Note: IPNTR(9:13) contains the pointer into WORKL for addresses c of the above information computed by igraphdneupd. c ------------------------------------------------------------- c IPNTR(9): pointer to the real part of the NCV RITZ values of the c original system. c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of c the original system. c IPNTR(11): pointer to the NCV corresponding error bounds. c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular c Schur matrix for H. c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors c of the upper Hessenberg matrix H. Only referenced by c igraphdneupd if RVEC = .TRUE. See Remark 2 below. c ------------------------------------------------------------- c c INFO Integer. (OUTPUT) c Error flag on output. c c = 0: Normal exit. c c = 1: The Schur form computed by LAPACK routine dlahqr c could not be reordered by LAPACK routine dtrsen. c Re-enter subroutine igraphdneupd with IPARAM(5)=NCV and c increase the size of the arrays DR and DI to have c dimension at least dimension NCV and allocate at least NCV c columns for Z. NOTE: Not necessary if Z and V share c the same space. Please notify the authors if this error c occurs. c c = -1: N must be positive. c = -2: NEV must be positive. c = -3: NCV-NEV >= 2 and less than or equal to N. c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' c = -6: BMAT must be one of 'I' or 'G'. c = -7: Length of private work WORKL array is not sufficient. c = -8: Error return from calculation of a real Schur form. c Informational error from LAPACK routine dlahqr. c = -9: Error return from calculation of eigenvectors. c Informational error from LAPACK routine dtrevc. c = -10: IPARAM(7) must be 1,2,3,4. c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. c = -12: HOWMNY = 'S' not yet implemented c = -13: HOWMNY must be one of 'A' or 'P' if RVEC = .true. c = -14: DNAUPD did not find any eigenvalues to sufficient c accuracy. c c\BeginLib c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly c Restarted Arnoldi Iteration", Rice University Technical Report c TR95-13, Department of Computational and Applied Mathematics. c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for c Real Matrices", Linear Algebra and its Applications, vol 88/89, c pp 575-595, (1987). c c\Routines called: c igraphivout ARPACK utility routine that prints integers. c igraphdmout ARPACK utility routine that prints matrices c igraphdvout ARPACK utility routine that prints vectors. c dgeqr2 LAPACK routine that computes the QR factorization of c a matrix. c dlacpy LAPACK matrix copy routine. c dlahqr LAPACK routine to compute the real Schur form of an c upper Hessenberg matrix. c dlamch LAPACK routine that determines machine constants. c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. c dlaset LAPACK matrix initialization routine. c dorm2r LAPACK routine that applies an orthogonal matrix in c factored form. c dtrevc LAPACK routine to compute the eigenvectors of a matrix c in upper quasi-triangular form. c dtrsen LAPACK routine that re-orders the Schur form. c dtrmm Level 3 BLAS matrix times an upper triangular matrix. c dger Level 2 BLAS rank one update to a matrix. c dcopy Level 1 BLAS that copies one vector to another . c ddot Level 1 BLAS that computes the scalar product of two vectors. c dnrm2 Level 1 BLAS that computes the norm of a vector. c dscal Level 1 BLAS that scales a vector. c c\Remarks c c 1. Currently only HOWMNY = 'A' and 'P' are implemented. c c Let X' denote the transpose of X. c c 2. Schur vectors are an orthogonal representation for the basis of c Ritz vectors. Thus, their numerical properties are often superior. c If RVEC = .TRUE. then the relationship c A * V(:,1:IPARAM(5)) = V(:,1:IPARAM(5)) * T, and c V(:,1:IPARAM(5))' * V(:,1:IPARAM(5)) = I are approximately satisfied. c Here T is the leading submatrix of order IPARAM(5) of the real c upper quasi-triangular matrix stored workl(ipntr(12)). That is, c T is block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; c each 2-by-2 diagonal block has its diagonal elements equal and its c off-diagonal elements of opposite sign. Corresponding to each 2-by-2 c diagonal block is a complex conjugate pair of Ritz values. The real c Ritz values are stored on the diagonal of T. c c 3. If IPARAM(7) = 3 or 4 and SIGMAI is not equal zero, then the user must c form the IPARAM(5) Rayleigh quotients in order to transform the Ritz c values computed by DNAUPD for OP to those of A*z = lambda*B*z. c Set RVEC = .true. and HOWMNY = 'A', and c compute c Z(:,I)' * A * Z(:,I) if DI(I) = 0. c If DI(I) is not equal to zero and DI(I+1) = - D(I), c then the desired real and imaginary parts of the Ritz value are c Z(:,I)' * A * Z(:,I) + Z(:,I+1)' * A * Z(:,I+1), c Z(:,I)' * A * Z(:,I+1) - Z(:,I+1)' * A * Z(:,I), respectively. c Another possibility is to set RVEC = .true. and HOWMNY = 'P' and c compute V(:,1:IPARAM(5))' * A * V(:,1:IPARAM(5)) and then an upper c quasi-triangular matrix of order IPARAM(5) is computed. See remark c 2 above. c c\Authors c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Chao Yang Houston, Texas c Dept. of Computational & c Applied Mathematics c Rice University c Houston, Texas c c\SCCS Information: @(#) c FILE: neupd.F SID: 2.5 DATE OF SID: 7/31/96 RELEASE: 2 c c\EndLib c c----------------------------------------------------------------------- subroutine igraphdneupd (rvec, howmny, select, dr, di, z, ldz, & sigmar, sigmai, workev, bmat, n, which, nev, tol, & resid, ncv, v, ldv, iparam, ipntr, workd, & workl, lworkl, info) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c character bmat, howmny, which*2 logical rvec integer info, ldz, ldv, lworkl, n, ncv, nev Double precision & sigmar, sigmai, tol c c %-----------------% c | Array Arguments | c %-----------------% c integer iparam(11), ipntr(14) logical select(ncv) Double precision & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), & workd(3*n), workl(lworkl), workev(3*ncv) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c character type*6 integer bounds, ierr, ih, ihbds, iheigr, iheigi, iconj, nconv, & invsub, iuptri, iwev, iwork(1), j, k, ktrord, & ldh, ldq, mode, msglvl, outncv, ritzr, ritzi, wri, wrr, & irr, iri, ibd logical reord Double precision & conds, rnorm, sep, temp, thres, vl(1,1), temp1, eps23 c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, dger, dgeqr2, dlacpy, dlahqr, dlaset, & igraphdmout, dorm2r, dtrevc, dtrmm, dtrsen, dscal, & igraphdvout, igraphivout c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlapy2, dnrm2, dlamch, ddot external dlapy2, dnrm2, dlamch, ddot c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic abs, min, sqrt c c %-----------------------% c | Executable Statements | c %-----------------------% c c %------------------------% c | Set default parameters | c %------------------------% c msglvl = mneupd mode = iparam(7) nconv = iparam(5) info = 0 c c %---------------------------------% c | Get machine dependent constant. | c %---------------------------------% c eps23 = dlamch('Epsilon-Machine') eps23 = eps23**(2.0D+0 / 3.0D+0) c c %--------------% c | Quick return | c %--------------% c ierr = 0 c if (nconv .le. 0) then ierr = -14 else if (n .le. 0) then ierr = -1 else if (nev .le. 0) then ierr = -2 else if (ncv .le. nev+1 .or. ncv .gt. n) then ierr = -3 else if (which .ne. 'LM' .and. & which .ne. 'SM' .and. & which .ne. 'LR' .and. & which .ne. 'SR' .and. & which .ne. 'LI' .and. & which .ne. 'SI') then ierr = -5 else if (bmat .ne. 'I' .and. bmat .ne. 'G') then ierr = -6 else if (lworkl .lt. 3*ncv**2 + 6*ncv) then ierr = -7 else if ( (howmny .ne. 'A' .and. & howmny .ne. 'P' .and. & howmny .ne. 'S') .and. rvec ) then ierr = -13 else if (howmny .eq. 'S' ) then ierr = -12 end if c if (mode .eq. 1 .or. mode .eq. 2) then type = 'REGULR' else if (mode .eq. 3 .and. sigmai .eq. zero) then type = 'SHIFTI' else if (mode .eq. 3 ) then type = 'REALPT' else if (mode .eq. 4 ) then type = 'IMAGPT' else ierr = -10 end if if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 c c %------------% c | Error Exit | c %------------% c if (ierr .ne. 0) then info = ierr go to 9000 end if c c %--------------------------------------------------------% c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | c | etc... and the remaining workspace. | c | Also update pointer to be used on output. | c | Memory is laid out as follows: | c | workl(1:ncv*ncv) := generated Hessenberg matrix | c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | c | parts of ritz values | c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | c %--------------------------------------------------------% c c %-----------------------------------------------------------% c | The following is used and set by DNEUPD. | c | workl(ncv*ncv+3*ncv+1:ncv*ncv+4*ncv) := The untransformed | c | real part of the Ritz values. | c | workl(ncv*ncv+4*ncv+1:ncv*ncv+5*ncv) := The untransformed | c | imaginary part of the Ritz values. | c | workl(ncv*ncv+5*ncv+1:ncv*ncv+6*ncv) := The untransformed | c | error bounds of the Ritz values | c | workl(ncv*ncv+6*ncv+1:2*ncv*ncv+6*ncv) := Holds the upper | c | quasi-triangular matrix for H | c | workl(2*ncv*ncv+6*ncv+1: 3*ncv*ncv+6*ncv) := Holds the | c | associated matrix representation of the invariant | c | subspace for H. | c | GRAND total of NCV * ( 3 * NCV + 6 ) locations. | c %-----------------------------------------------------------% c ih = ipntr(5) ritzr = ipntr(6) ritzi = ipntr(7) bounds = ipntr(8) ldh = ncv ldq = ncv iheigr = bounds + ldh iheigi = iheigr + ldh ihbds = iheigi + ldh iuptri = ihbds + ldh invsub = iuptri + ldh*ncv ipntr(9) = iheigr ipntr(10) = iheigi ipntr(11) = ihbds ipntr(12) = iuptri ipntr(13) = invsub wrr = 1 wri = ncv + 1 iwev = wri + ncv c c %-----------------------------------------% c | irr points to the REAL part of the Ritz | c | values computed by _neigh before | c | exiting _naup2. | c | iri points to the IMAGINARY part of the | c | Ritz values computed by _neigh | c | before exiting _naup2. | c | ibd points to the Ritz estimates | c | computed by _neigh before exiting | c | _naup2. | c %-----------------------------------------% c irr = ipntr(14)+ncv*ncv iri = irr+ncv ibd = iri+ncv c c %------------------------------------% c | RNORM is B-norm of the RESID(1:N). | c %------------------------------------% c rnorm = workl(ih+2) workl(ih+2) = zero c if (rvec) then c c %-------------------------------------------% c | Get converged Ritz value on the boundary. | c | Note: converged Ritz values have been | c | placed in the first NCONV locations in | c | workl(ritzr) and workl(ritzi). They have | c | been sorted (in _naup2) according to the | c | WHICH selection criterion. | c %-------------------------------------------% c if (which .eq. 'LM' .or. which .eq. 'SM') then thres = dlapy2( workl(ritzr), workl(ritzi) ) else if (which .eq. 'LR' .or. which .eq. 'SR') then thres = workl(ritzr) else if (which .eq. 'LI' .or. which .eq. 'SI') then thres = abs( workl(ritzi) ) end if c if (msglvl .gt. 2) then call igraphdvout(logfil, 1, thres, ndigit, & '_neupd: Threshold eigenvalue used for re-ordering') end if c c %----------------------------------------------------------% c | Check to see if all converged Ritz values appear at the | c | top of the upper quasi-triangular matrix computed by | c | _neigh in _naup2. This is done in the following way: | c | | c | 1) For each Ritz value obtained from _neigh, compare it | c | with the threshold Ritz value computed above to | c | determine whether it is a wanted one. | c | | c | 2) If it is wanted, then check the corresponding Ritz | c | estimate to see if it has converged. If it has, set | c | correponding entry in the logical array SELECT to | c | .TRUE.. | c | | c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | c | converged Ritz value that does not appear at the top of | c | the upper quasi-triangular matrix computed by _neigh in | c | _naup2. Reordering is needed. | c %----------------------------------------------------------% c reord = .false. ktrord = 0 do 10 j = 0, ncv-1 select(j+1) = .false. if (which .eq. 'LM') then if (dlapy2(workl(irr+j), workl(iri+j)) & .ge. thres) then temp1 = max( eps23, & dlapy2( workl(irr+j), workl(iri+j) ) ) if (workl(ibd+j) .le. tol*temp1) & select(j+1) = .true. end if else if (which .eq. 'SM') then if (dlapy2(workl(irr+j), workl(iri+j)) & .le. thres) then temp1 = max( eps23, & dlapy2( workl(irr+j), workl(iri+j) ) ) if (workl(ibd+j) .le. tol*temp1) & select(j+1) = .true. end if else if (which .eq. 'LR') then if (workl(irr+j) .ge. thres) then temp1 = max( eps23, & dlapy2( workl(irr+j), workl(iri+j) ) ) if (workl(ibd+j) .le. tol*temp1) & select(j+1) = .true. end if else if (which .eq. 'SR') then if (workl(irr+j) .le. thres) then temp1 = max( eps23, & dlapy2( workl(irr+j), workl(iri+j) ) ) if (workl(ibd+j) .le. tol*temp1) & select(j+1) = .true. end if else if (which .eq. 'LI') then if (abs(workl(iri+j)) .ge. thres) then temp1 = max( eps23, & dlapy2( workl(irr+j), workl(iri+j) ) ) if (workl(ibd+j) .le. tol*temp1) & select(j+1) = .true. end if else if (which .eq. 'SI') then if (abs(workl(iri+j)) .le. thres) then temp1 = max( eps23, & dlapy2( workl(irr+j), workl(iri+j) ) ) if (workl(ibd+j) .le. tol*temp1) & select(j+1) = .true. end if end if if (j+1 .gt. nconv ) reord = ( select(j+1) .or. reord ) if (select(j+1)) ktrord = ktrord + 1 10 continue c if (msglvl .gt. 2) then call igraphivout(logfil, 1, ktrord, ndigit, & '_neupd: Number of specified eigenvalues') call igraphivout(logfil, 1, nconv, ndigit, & '_neupd: Number of "converged" eigenvalues') end if c c %-----------------------------------------------------------% c | Call LAPACK routine dlahqr to compute the real Schur form | c | of the upper Hessenberg matrix returned by DNAUPD. | c | Make a copy of the upper Hessenberg matrix. | c | Initialize the Schur vector matrix Q to the identity. | c %-----------------------------------------------------------% c call dcopy (ldh*ncv, workl(ih), 1, workl(iuptri), 1) call dlaset ('All', ncv, ncv, zero, one, workl(invsub), ldq) call dlahqr (.true., .true., ncv, 1, ncv, workl(iuptri), ldh, & workl(iheigr), workl(iheigi), 1, ncv, & workl(invsub), ldq, ierr) call dcopy (ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) c if (ierr .ne. 0) then info = -8 go to 9000 end if c if (msglvl .gt. 1) then call igraphdvout (logfil, ncv, workl(iheigr), ndigit, & '_neupd: Real part of the eigenvalues of H') call igraphdvout (logfil, ncv, workl(iheigi), ndigit, & '_neupd: Imaginary part of the Eigenvalues of H') call igraphdvout (logfil, ncv, workl(ihbds), ndigit, & '_neupd: Last row of the Schur vector matrix') if (msglvl .gt. 3) then call igraphdmout (logfil, ncv, ncv, workl(iuptri), ldh, & ndigit, & '_neupd: The upper quasi-triangular matrix ') end if end if c if (reord) then c c %-----------------------------------------------------% c | Reorder the computed upper quasi-triangular matrix. | c %-----------------------------------------------------% c call dtrsen ('None', 'V', select, ncv, workl(iuptri), ldh, & workl(invsub), ldq, workl(iheigr), workl(iheigi), & nconv, conds, sep, workl(ihbds), ncv, iwork, 1, ierr) c if (ierr .eq. 1) then info = 1 go to 9000 end if c if (msglvl .gt. 2) then call igraphdvout (logfil, ncv, workl(iheigr), ndigit, & '_neupd: Real part of the eigenvalues of H--reordered') call igraphdvout (logfil, ncv, workl(iheigi), ndigit, & '_neupd: Imag part of the eigenvalues of H--reordered') if (msglvl .gt. 3) then call igraphdmout (logfil, ncv, ncv, workl(iuptri), & ldq, ndigit, & '_neupd: Quasi-triangular matrix after re-ordering') end if end if c end if c c %---------------------------------------% c | Copy the last row of the Schur vector | c | into workl(ihbds). This will be used | c | to compute the Ritz estimates of | c | converged Ritz values. | c %---------------------------------------% c call dcopy(ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) c c %----------------------------------------------------% c | Place the computed eigenvalues of H into DR and DI | c | if a spectral transformation was not used. | c %----------------------------------------------------% c if (type .eq. 'REGULR') then call dcopy (nconv, workl(iheigr), 1, dr, 1) call dcopy (nconv, workl(iheigi), 1, di, 1) end if c c %----------------------------------------------------------% c | Compute the QR factorization of the matrix representing | c | the wanted invariant subspace located in the first NCONV | c | columns of workl(invsub,ldq). | c %----------------------------------------------------------% c call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, & workev(ncv+1), ierr) c c %---------------------------------------------------------% c | * Postmultiply V by Q using dorm2r. | c | * Copy the first NCONV columns of VQ into Z. | c | * Postmultiply Z by R. | c | The N by NCONV matrix Z is now a matrix representation | c | of the approximate invariant subspace associated with | c | the Ritz values in workl(iheigr) and workl(iheigi) | c | The first NCONV columns of V are now approximate Schur | c | vectors associated with the real upper quasi-triangular | c | matrix of order NCONV in workl(iuptri) | c %---------------------------------------------------------% c call dorm2r ('Right', 'Notranspose', n, ncv, nconv, & workl(invsub), ldq, workev, v, ldv, workd(n+1), ierr) call dlacpy ('All', n, nconv, v, ldv, z, ldz) c do 20 j=1, nconv c c %---------------------------------------------------% c | Perform both a column and row scaling if the | c | diagonal element of workl(invsub,ldq) is negative | c | I'm lazy and don't take advantage of the upper | c | quasi-triangular form of workl(iuptri,ldq) | c | Note that since Q is orthogonal, R is a diagonal | c | matrix consisting of plus or minus ones | c %---------------------------------------------------% c if (workl(invsub+(j-1)*ldq+j-1) .lt. zero) then call dscal (nconv, -one, workl(iuptri+j-1), ldq) call dscal (nconv, -one, workl(iuptri+(j-1)*ldq), 1) end if c 20 continue c if (howmny .eq. 'A') then c c %--------------------------------------------% c | Compute the NCONV wanted eigenvectors of T | c | located in workl(iuptri,ldq). | c %--------------------------------------------% c do 30 j=1, ncv if (j .le. nconv) then select(j) = .true. else select(j) = .false. end if 30 continue c call dtrevc ('Right', 'Select', select, ncv, workl(iuptri), & ldq, vl, 1, workl(invsub), ldq, ncv, outncv, workev, & ierr) c if (ierr .ne. 0) then info = -9 go to 9000 end if c c %------------------------------------------------% c | Scale the returning eigenvectors so that their | c | Euclidean norms are all one. LAPACK subroutine | c | dtrevc returns each eigenvector normalized so | c | that the element of largest magnitude has | c | magnitude 1; | c %------------------------------------------------% c iconj = 0 do 40 j=1, nconv c if ( workl(iheigi+j-1) .eq. zero ) then c c %----------------------% c | real eigenvalue case | c %----------------------% c temp = dnrm2( ncv, workl(invsub+(j-1)*ldq), 1 ) call dscal ( ncv, one / temp, & workl(invsub+(j-1)*ldq), 1 ) c else c c %-------------------------------------------% c | Complex conjugate pair case. Note that | c | since the real and imaginary part of | c | the eigenvector are stored in consecutive | c | columns, we further normalize by the | c | square root of two. | c %-------------------------------------------% c if (iconj .eq. 0) then temp = dlapy2( dnrm2( ncv, workl(invsub+(j-1)*ldq), & 1 ), dnrm2( ncv, workl(invsub+j*ldq), 1) ) call dscal ( ncv, one / temp, & workl(invsub+(j-1)*ldq), 1 ) call dscal ( ncv, one / temp, & workl(invsub+j*ldq), 1 ) iconj = 1 else iconj = 0 end if c end if c 40 continue c call dgemv('T', ncv, nconv, one, workl(invsub), & ldq, workl(ihbds), 1, zero, workev, 1) c iconj = 0 do 45 j=1, nconv if (workl(iheigi+j-1) .ne. zero) then c c %-------------------------------------------% c | Complex conjugate pair case. Note that | c | since the real and imaginary part of | c | the eigenvector are stored in consecutive | c %-------------------------------------------% c if (iconj .eq. 0) then workev(j) = dlapy2(workev(j), workev(j+1)) workev(j+1) = workev(j) iconj = 1 else iconj = 0 end if end if 45 continue c if (msglvl .gt. 2) then call dcopy(ncv, workl(invsub+ncv-1), ldq, & workl(ihbds), 1) call igraphdvout (logfil, ncv, workl(ihbds), ndigit, & '_neupd: Last row of the eigenvector matrix for T') if (msglvl .gt. 3) then call igraphdmout (logfil, ncv, ncv, workl(invsub), & ldq, ndigit, & '_neupd: The eigenvector matrix for T') end if end if c c %---------------------------------------% c | Copy Ritz estimates into workl(ihbds) | c %---------------------------------------% c call dcopy(nconv, workev, 1, workl(ihbds), 1) c c %---------------------------------------------------------% c | Compute the QR factorization of the eigenvector matrix | c | associated with leading portion of T in the first NCONV | c | columns of workl(invsub,ldq). | c %---------------------------------------------------------% c call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, & workev(ncv+1), ierr) c c %----------------------------------------------% c | * Postmultiply Z by Q. | c | * Postmultiply Z by R. | c | The N by NCONV matrix Z is now contains the | c | Ritz vectors associated with the Ritz values | c | in workl(iheigr) and workl(iheigi). | c %----------------------------------------------% c call dorm2r ('Right', 'Notranspose', n, ncv, nconv, & workl(invsub), ldq, workev, z, ldz, workd(n+1), ierr) c call dtrmm ('Right', 'Upper', 'No transpose', 'Non-unit', & n, nconv, one, workl(invsub), ldq, z, ldz) c end if c else c c %------------------------------------------------------% c | An approximate invariant subspace is not needed. | c | Place the Ritz values computed DNAUPD into DR and DI | c %------------------------------------------------------% c call dcopy (nconv, workl(ritzr), 1, dr, 1) call dcopy (nconv, workl(ritzi), 1, di, 1) call dcopy (nconv, workl(ritzr), 1, workl(iheigr), 1) call dcopy (nconv, workl(ritzi), 1, workl(iheigi), 1) call dcopy (nconv, workl(bounds), 1, workl(ihbds), 1) end if c c %------------------------------------------------% c | Transform the Ritz values and possibly vectors | c | and corresponding error bounds of OP to those | c | of A*x = lambda*B*x. | c %------------------------------------------------% c if (type .eq. 'REGULR') then c if (rvec) & call dscal (ncv, rnorm, workl(ihbds), 1) c else c c %---------------------------------------% c | A spectral transformation was used. | c | * Determine the Ritz estimates of the | c | Ritz values in the original system. | c %---------------------------------------% c if (type .eq. 'SHIFTI') then c if (rvec) & call dscal (ncv, rnorm, workl(ihbds), 1) c do 50 k=1, ncv temp = dlapy2( workl(iheigr+k-1), & workl(iheigi+k-1) ) workl(ihbds+k-1) = abs( workl(ihbds+k-1) ) & / temp / temp 50 continue c else if (type .eq. 'REALPT') then c do 60 k=1, ncv 60 continue c else if (type .eq. 'IMAGPT') then c do 70 k=1, ncv 70 continue c end if c c %-----------------------------------------------------------% c | * Transform the Ritz values back to the original system. | c | For TYPE = 'SHIFTI' the transformation is | c | lambda = 1/theta + sigma | c | For TYPE = 'REALPT' or 'IMAGPT' the user must from | c | Rayleigh quotients or a projection. See remark 3 above.| c | NOTES: | c | *The Ritz vectors are not affected by the transformation. | c %-----------------------------------------------------------% c if (type .eq. 'SHIFTI') then c do 80 k=1, ncv temp = dlapy2( workl(iheigr+k-1), & workl(iheigi+k-1) ) workl(iheigr+k-1) = workl(iheigr+k-1) / temp / temp & + sigmar workl(iheigi+k-1) = -workl(iheigi+k-1) / temp / temp & + sigmai 80 continue c call dcopy (nconv, workl(iheigr), 1, dr, 1) call dcopy (nconv, workl(iheigi), 1, di, 1) c else if (type .eq. 'REALPT' .or. type .eq. 'IMAGPT') then c call dcopy (nconv, workl(iheigr), 1, dr, 1) call dcopy (nconv, workl(iheigi), 1, di, 1) c end if c end if c if (type .eq. 'SHIFTI' .and. msglvl .gt. 1) then call igraphdvout (logfil, nconv, dr, ndigit, & '_neupd: Untransformed real part of the Ritz valuess.') call igraphdvout (logfil, nconv, di, ndigit, & '_neupd: Untransformed imag part of the Ritz valuess.') call igraphdvout (logfil, nconv, workl(ihbds), ndigit, & '_neupd: Ritz estimates of untransformed Ritz values.') else if (type .eq. 'REGULR' .and. msglvl .gt. 1) then call igraphdvout (logfil, nconv, dr, ndigit, & '_neupd: Real parts of converged Ritz values.') call igraphdvout (logfil, nconv, di, ndigit, & '_neupd: Imag parts of converged Ritz values.') call igraphdvout (logfil, nconv, workl(ihbds), ndigit, & '_neupd: Associated Ritz estimates.') end if c c %-------------------------------------------------% c | Eigenvector Purification step. Formally perform | c | one of inverse subspace iteration. Only used | c | for MODE = 2. | c %-------------------------------------------------% c if (rvec .and. howmny .eq. 'A' .and. type .eq. 'SHIFTI') then c c %------------------------------------------------% c | Purify the computed Ritz vectors by adding a | c | little bit of the residual vector: | c | T | c | resid(:)*( e s ) / theta | c | NCV | c | where H s = s theta. Remember that when theta | c | has nonzero imaginary part, the corresponding | c | Ritz vector is stored across two columns of Z. | c %------------------------------------------------% c iconj = 0 do 110 j=1, nconv if (workl(iheigi+j-1) .eq. zero) then workev(j) = workl(invsub+(j-1)*ldq+ncv-1) / & workl(iheigr+j-1) else if (iconj .eq. 0) then temp = dlapy2( workl(iheigr+j-1), workl(iheigi+j-1) ) workev(j) = ( workl(invsub+(j-1)*ldq+ncv-1) * & workl(iheigr+j-1) + & workl(invsub+j*ldq+ncv-1) * & workl(iheigi+j-1) ) / temp / temp workev(j+1) = ( workl(invsub+j*ldq+ncv-1) * & workl(iheigr+j-1) - & workl(invsub+(j-1)*ldq+ncv-1) * & workl(iheigi+j-1) ) / temp / temp iconj = 1 else iconj = 0 end if 110 continue c c %---------------------------------------% c | Perform a rank one update to Z and | c | purify all the Ritz vectors together. | c %---------------------------------------% c call dger (n, nconv, one, resid, 1, workev, 1, z, ldz) c end if c 9000 continue c return c c %---------------% c | End of DNEUPD | c %---------------% c end igraph/src/foreign-dl-lexer.c0000644000176000001440000020064212325527073015664 0ustar ripleyusers#line 2 "lex.yy.c" #line 4 "lex.yy.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE igraph_dl_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via igraph_dl_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void igraph_dl_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void igraph_dl_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_dl_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void igraph_dl_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_dl_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void igraph_dl_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void igraph_dl_yypop_buffer_state (yyscan_t yyscanner ); static void igraph_dl_yyensure_buffer_stack (yyscan_t yyscanner ); static void igraph_dl_yy_load_buffer_state (yyscan_t yyscanner ); static void igraph_dl_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER igraph_dl_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE igraph_dl_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_dl_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE igraph_dl_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *igraph_dl_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *igraph_dl_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void igraph_dl_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer igraph_dl_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ igraph_dl_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ igraph_dl_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) #define igraph_dl_yywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (yy_size_t) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 24 #define YY_END_OF_BUFFER 25 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[129] = { 0, 0, 0, 0, 0, 0, 0, 18, 18, 21, 21, 25, 23, 22, 1, 1, 4, 23, 23, 23, 23, 12, 11, 12, 12, 14, 15, 13, 17, 18, 17, 16, 20, 21, 19, 22, 1, 4, 0, 0, 0, 0, 0, 3, 12, 12, 12, 12, 14, 13, 17, 18, 16, 17, 17, 20, 21, 19, 0, 2, 0, 0, 3, 12, 12, 16, 17, 16, 0, 0, 0, 12, 12, 5, 0, 0, 5, 12, 0, 0, 12, 0, 0, 0, 6, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 0, 10, 7, 7, 9, 8, 10, 8, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 7, 8, 9, 1, 10, 11, 10, 10, 10, 10, 10, 10, 10, 10, 12, 1, 1, 13, 1, 1, 1, 14, 15, 1, 16, 17, 18, 19, 1, 20, 1, 1, 21, 22, 23, 24, 1, 1, 25, 26, 27, 28, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, 14, 15, 1, 16, 17, 18, 19, 1, 20, 1, 1, 21, 22, 23, 24, 1, 1, 25, 26, 27, 28, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[30] = { 0, 1, 2, 3, 3, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int16_t yy_base[138] = { 0, 0, 22, 44, 64, 84, 94, 104, 114, 124, 134, 287, 288, 4, 282, 282, 2, 1, 260, 269, 15, 29, 288, 39, 50, 0, 288, 34, 0, 52, 19, 64, 0, 54, 51, 74, 288, 67, 255, 88, 256, 265, 138, 98, 108, 118, 128, 144, 0, 145, 0, 151, 151, 72, 159, 0, 152, 153, 265, 169, 256, 260, 170, 171, 175, 171, 168, 173, 264, 261, 253, 184, 185, 288, 246, 246, 189, 193, 195, 197, 199, 205, 218, 209, 288, 210, 0, 255, 242, 245, 246, 248, 245, 249, 231, 228, 217, 211, 200, 184, 181, 172, 150, 138, 138, 128, 126, 106, 75, 66, 67, 45, 45, 36, 42, 39, 22, 26, 219, 211, 6, 220, 227, 228, 232, 237, 238, 242, 288, 247, 250, 253, 256, 259, 262, 7, 6, 0 } ; static yyconst flex_int16_t yy_def[138] = { 0, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 134, 128, 134, 134, 135, 128, 135, 136, 128, 136, 136, 137, 128, 137, 128, 128, 128, 128, 128, 128, 128, 128, 128, 134, 128, 134, 134, 135, 128, 136, 128, 136, 136, 136, 137, 128, 137, 128, 128, 128, 128, 128, 134, 134, 136, 136, 136, 128, 128, 128, 134, 134, 128, 128, 128, 134, 134, 128, 128, 134, 128, 128, 128, 128, 128, 82, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128 } ; static yyconst flex_int16_t yy_nxt[318] = { 0, 55, 13, 14, 15, 13, 35, 50, 48, 35, 16, 16, 37, 37, 128, 38, 17, 42, 18, 128, 42, 19, 39, 20, 13, 14, 15, 13, 43, 52, 52, 45, 16, 16, 45, 125, 49, 121, 17, 49, 18, 45, 120, 19, 45, 20, 12, 14, 15, 22, 119, 22, 45, 46, 51, 45, 56, 51, 118, 56, 23, 57, 57, 117, 47, 24, 12, 14, 15, 22, 116, 22, 115, 53, 52, 52, 35, 37, 37, 35, 23, 54, 65, 65, 114, 24, 26, 14, 15, 26, 59, 12, 113, 59, 27, 27, 26, 14, 15, 26, 62, 12, 112, 62, 27, 27, 29, 14, 15, 29, 45, 12, 30, 45, 31, 31, 29, 14, 15, 29, 45, 12, 30, 45, 31, 31, 33, 14, 15, 33, 45, 12, 111, 45, 34, 34, 33, 14, 15, 33, 42, 12, 110, 42, 34, 34, 45, 49, 109, 45, 49, 43, 108, 51, 56, 63, 51, 56, 107, 64, 53, 52, 52, 57, 57, 66, 106, 66, 54, 67, 67, 59, 62, 45, 59, 62, 45, 45, 67, 67, 45, 65, 65, 67, 67, 71, 45, 45, 54, 45, 45, 45, 72, 105, 45, 45, 76, 81, 45, 83, 81, 85, 83, 104, 85, 103, 77, 81, 82, 84, 81, 83, 85, 124, 83, 85, 124, 102, 82, 80, 86, 122, 126, 86, 122, 126, 90, 90, 101, 122, 122, 123, 122, 122, 124, 87, 88, 124, 100, 127, 126, 89, 127, 126, 127, 99, 98, 127, 12, 12, 12, 21, 21, 21, 25, 25, 25, 28, 28, 28, 32, 32, 32, 44, 44, 97, 96, 95, 94, 93, 92, 91, 79, 78, 75, 74, 73, 70, 69, 68, 61, 60, 58, 41, 40, 36, 36, 128, 11, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } ; static yyconst flex_int16_t yy_chk[318] = { 0, 137, 1, 1, 1, 1, 13, 136, 135, 13, 1, 1, 16, 16, 0, 17, 1, 20, 1, 0, 20, 1, 17, 1, 2, 2, 2, 2, 20, 30, 30, 21, 2, 2, 21, 120, 27, 117, 2, 27, 2, 23, 116, 2, 23, 2, 3, 3, 3, 3, 115, 3, 24, 23, 29, 24, 33, 29, 114, 33, 3, 34, 34, 113, 24, 3, 4, 4, 4, 4, 112, 4, 111, 31, 31, 31, 35, 37, 37, 35, 4, 31, 53, 53, 110, 4, 5, 5, 5, 5, 39, 5, 109, 39, 5, 5, 6, 6, 6, 6, 43, 6, 108, 43, 6, 6, 7, 7, 7, 7, 44, 7, 7, 44, 7, 7, 8, 8, 8, 8, 45, 8, 8, 45, 8, 8, 9, 9, 9, 9, 46, 9, 107, 46, 9, 9, 10, 10, 10, 10, 42, 10, 106, 42, 10, 10, 47, 49, 105, 47, 49, 42, 104, 51, 56, 46, 51, 56, 103, 47, 52, 52, 52, 57, 57, 54, 102, 54, 52, 54, 54, 59, 62, 63, 59, 62, 63, 64, 66, 66, 64, 65, 65, 67, 67, 63, 71, 72, 65, 71, 72, 76, 64, 101, 76, 77, 71, 78, 77, 79, 78, 80, 79, 100, 80, 99, 72, 81, 78, 79, 81, 83, 85, 119, 83, 85, 119, 98, 81, 77, 82, 118, 121, 82, 118, 121, 83, 85, 97, 122, 123, 118, 122, 123, 124, 82, 82, 124, 96, 125, 126, 82, 125, 126, 127, 95, 94, 127, 129, 129, 129, 130, 130, 130, 131, 131, 131, 132, 132, 132, 133, 133, 133, 134, 134, 93, 92, 91, 90, 89, 88, 87, 75, 74, 70, 69, 68, 61, 60, 58, 41, 40, 38, 19, 18, 15, 14, 11, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "igraph/src/foreign-dl-lexer.l" /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #line 24 "igraph/src/foreign-dl-lexer.l" /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include #include #include "foreign-dl-header.h" #include "foreign-dl-parser.h" #define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* #define YY_USER_ACTION yylloc->first_line = yylineno; /* We assume that 'file' is 'stderr' here. */ #define fprintf(file, msg, ...) \ igraph_warningf(msg, __FILE__, __LINE__, 0, __VA_ARGS__) #ifdef stdout # undef stdout #endif #define stdout 0 #define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \ __LINE__, IGRAPH_PARSEERROR); #define YY_NO_INPUT 1 #line 610 "lex.yy.c" #define INITIAL 0 #define LABELM 1 #define FULLMATRIX 2 #define EDGELIST 3 #define NODELIST 4 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; yy_size_t yy_n_chars; yy_size_t yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r int igraph_dl_yylex_init (yyscan_t* scanner); int igraph_dl_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int igraph_dl_yylex_destroy (yyscan_t yyscanner ); int igraph_dl_yyget_debug (yyscan_t yyscanner ); void igraph_dl_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE igraph_dl_yyget_extra (yyscan_t yyscanner ); void igraph_dl_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *igraph_dl_yyget_in (yyscan_t yyscanner ); void igraph_dl_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *igraph_dl_yyget_out (yyscan_t yyscanner ); void igraph_dl_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t igraph_dl_yyget_leng (yyscan_t yyscanner ); char *igraph_dl_yyget_text (yyscan_t yyscanner ); int igraph_dl_yyget_lineno (yyscan_t yyscanner ); void igraph_dl_yyset_lineno (int line_number ,yyscan_t yyscanner ); YYSTYPE * igraph_dl_yyget_lval (yyscan_t yyscanner ); void igraph_dl_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *igraph_dl_yyget_lloc (yyscan_t yyscanner ); void igraph_dl_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int igraph_dl_yywrap (yyscan_t yyscanner ); #else extern int igraph_dl_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ yy_size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int igraph_dl_yylex \ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int igraph_dl_yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; #line 84 "igraph/src/foreign-dl-lexer.l" #line 856 "lex.yy.c" yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { igraph_dl_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_dl_yy_load_buffer_state(yyscanner ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 129 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 288 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: /* rule 1 can match eol */ YY_RULE_SETUP #line 86 "igraph/src/foreign-dl-lexer.l" { return NEWLINE; } YY_BREAK case 2: YY_RULE_SETUP #line 88 "igraph/src/foreign-dl-lexer.l" { return DL; } YY_BREAK case 3: YY_RULE_SETUP #line 89 "igraph/src/foreign-dl-lexer.l" { return NEQ; } YY_BREAK case 4: YY_RULE_SETUP #line 91 "igraph/src/foreign-dl-lexer.l" { return NUM; } YY_BREAK case 5: YY_RULE_SETUP #line 93 "igraph/src/foreign-dl-lexer.l" { switch (yyextra->mode) { case 0: BEGIN(FULLMATRIX); break; case 1: BEGIN(EDGELIST); break; case 2: BEGIN(NODELIST); break; } return DATA; } YY_BREAK case 6: YY_RULE_SETUP #line 104 "igraph/src/foreign-dl-lexer.l" { BEGIN(LABELM); return LABELS; } YY_BREAK case 7: YY_RULE_SETUP #line 105 "igraph/src/foreign-dl-lexer.l" { return LABELSEMBEDDED; } YY_BREAK case 8: YY_RULE_SETUP #line 107 "igraph/src/foreign-dl-lexer.l" { yyextra->mode=0; return FORMATFULLMATRIX; } YY_BREAK case 9: YY_RULE_SETUP #line 109 "igraph/src/foreign-dl-lexer.l" { yyextra->mode=1; return FORMATEDGELIST1; } YY_BREAK case 10: YY_RULE_SETUP #line 111 "igraph/src/foreign-dl-lexer.l" { yyextra->mode=2; return FORMATNODELIST1; } YY_BREAK case 11: YY_RULE_SETUP #line 114 "igraph/src/foreign-dl-lexer.l" { /* eaten up */ } YY_BREAK case 12: YY_RULE_SETUP #line 115 "igraph/src/foreign-dl-lexer.l" { return LABEL; } YY_BREAK case 13: YY_RULE_SETUP #line 117 "igraph/src/foreign-dl-lexer.l" { return DIGIT; } YY_BREAK case 14: YY_RULE_SETUP #line 118 "igraph/src/foreign-dl-lexer.l" { return LABEL; } YY_BREAK case 15: YY_RULE_SETUP #line 119 "igraph/src/foreign-dl-lexer.l" { } YY_BREAK case 16: YY_RULE_SETUP #line 121 "igraph/src/foreign-dl-lexer.l" { return NUM; } YY_BREAK case 17: YY_RULE_SETUP #line 122 "igraph/src/foreign-dl-lexer.l" { return LABEL; } YY_BREAK case 18: YY_RULE_SETUP #line 123 "igraph/src/foreign-dl-lexer.l" { } YY_BREAK case 19: YY_RULE_SETUP #line 125 "igraph/src/foreign-dl-lexer.l" { return NUM; } YY_BREAK case 20: YY_RULE_SETUP #line 126 "igraph/src/foreign-dl-lexer.l" { return LABEL; } YY_BREAK case 21: YY_RULE_SETUP #line 127 "igraph/src/foreign-dl-lexer.l" { } YY_BREAK case 22: YY_RULE_SETUP #line 129 "igraph/src/foreign-dl-lexer.l" { /* eaten up */ } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(LABELM): case YY_STATE_EOF(FULLMATRIX): case YY_STATE_EOF(EDGELIST): case YY_STATE_EOF(NODELIST): #line 131 "igraph/src/foreign-dl-lexer.l" { if (yyextra->eof) { yyterminate(); } else { yyextra->eof=1; BEGIN(INITIAL); return EOFF; } } YY_BREAK case 23: YY_RULE_SETUP #line 141 "igraph/src/foreign-dl-lexer.l" { return 0; } YY_BREAK case 24: YY_RULE_SETUP #line 143 "igraph/src/foreign-dl-lexer.l" ECHO; YY_BREAK #line 1094 "lex.yy.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * igraph_dl_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( igraph_dl_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of igraph_dl_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { yy_size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { yy_size_t new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ igraph_dl_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; igraph_dl_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_dl_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 129 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register char *yy_cp = yyg->yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 129 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 128); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ igraph_dl_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( igraph_dl_yywrap(yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void igraph_dl_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ igraph_dl_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } igraph_dl_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); igraph_dl_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void igraph_dl_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * igraph_dl_yypop_buffer_state(); * igraph_dl_yypush_buffer_state(new_buffer); */ igraph_dl_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; igraph_dl_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (igraph_dl_yywrap()) processing, but the only time this flag * is looked at is after igraph_dl_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void igraph_dl_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE igraph_dl_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) igraph_dl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) igraph_dl_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_create_buffer()" ); b->yy_is_our_buffer = 1; igraph_dl_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with igraph_dl_yy_create_buffer() * @param yyscanner The scanner object. */ void igraph_dl_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) igraph_dl_yyfree((void *) b->yy_ch_buf ,yyscanner ); igraph_dl_yyfree((void *) b ,yyscanner ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a igraph_dl_yyrestart() or at EOF. */ static void igraph_dl_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; igraph_dl_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then igraph_dl_yy_init_buffer was _probably_ * called from igraph_dl_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void igraph_dl_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) igraph_dl_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void igraph_dl_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; igraph_dl_yyensure_buffer_stack(yyscanner); /* This block is copied from igraph_dl_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from igraph_dl_yy_switch_to_buffer. */ igraph_dl_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void igraph_dl_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; igraph_dl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { igraph_dl_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void igraph_dl_yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_dl_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_dl_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_dl_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) igraph_dl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; igraph_dl_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to igraph_dl_yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * igraph_dl_yy_scan_bytes() instead. */ YY_BUFFER_STATE igraph_dl_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return igraph_dl_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to igraph_dl_yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE igraph_dl_yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n, i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) igraph_dl_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = igraph_dl_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in igraph_dl_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE igraph_dl_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int igraph_dl_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int igraph_dl_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *igraph_dl_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *igraph_dl_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ yy_size_t igraph_dl_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *igraph_dl_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void igraph_dl_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void igraph_dl_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_dl_yyset_lineno called with no buffer" , yyscanner); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void igraph_dl_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) yy_fatal_error( "igraph_dl_yyset_column called with no buffer" , yyscanner); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see igraph_dl_yy_switch_to_buffer */ void igraph_dl_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void igraph_dl_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int igraph_dl_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void igraph_dl_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * igraph_dl_yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void igraph_dl_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *igraph_dl_yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void igraph_dl_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* igraph_dl_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int igraph_dl_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_dl_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* igraph_dl_yylex_init_extra has the same functionality as igraph_dl_yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to igraph_dl_yyalloc in * the yyextra field. */ int igraph_dl_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; igraph_dl_yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) igraph_dl_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); igraph_dl_yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from igraph_dl_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * igraph_dl_yylex_init() */ return 0; } /* igraph_dl_yylex_destroy is for both reentrant and non-reentrant scanners. */ int igraph_dl_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ igraph_dl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; igraph_dl_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ igraph_dl_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ igraph_dl_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * igraph_dl_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ igraph_dl_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *igraph_dl_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *igraph_dl_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void igraph_dl_yyfree (void * ptr , yyscan_t yyscanner) { free( (char *) ptr ); /* see igraph_dl_yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 143 "igraph/src/foreign-dl-lexer.l" igraph/src/glpapi18.c0000644000176000001440000001033712325527073014146 0ustar ripleyusers/* glpapi18.c (maximum clique problem) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpapi.h" #include "glpnet.h" static void set_edge(int nv, unsigned char a[], int i, int j) { int k; xassert(1 <= j && j < i && i <= nv); k = ((i - 1) * (i - 2)) / 2 + (j - 1); a[k / CHAR_BIT] |= (unsigned char)(1 << ((CHAR_BIT - 1) - k % CHAR_BIT)); return; } int glp_wclique_exact(glp_graph *G, int v_wgt, double *sol, int v_set) { /* find maximum weight clique with exact algorithm */ glp_arc *e; int i, j, k, len, x, *w, *ind, ret = 0; unsigned char *a; double s, t; if (v_wgt >= 0 && v_wgt > G->v_size - (int)sizeof(double)) xerror("glp_wclique_exact: v_wgt = %d; invalid parameter\n", v_wgt); if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) xerror("glp_wclique_exact: v_set = %d; invalid parameter\n", v_set); if (G->nv == 0) { /* empty graph has only empty clique */ if (sol != NULL) *sol = 0.0; return 0; } /* allocate working arrays */ w = xcalloc(1+G->nv, sizeof(int)); ind = xcalloc(1+G->nv, sizeof(int)); len = G->nv; /* # vertices */ len = len * (len - 1) / 2; /* # entries in lower triangle */ len = (len + (CHAR_BIT - 1)) / CHAR_BIT; /* # bytes needed */ a = xcalloc(len, sizeof(char)); memset(a, 0, len * sizeof(char)); /* determine vertex weights */ s = 0.0; for (i = 1; i <= G->nv; i++) { if (v_wgt >= 0) { memcpy(&t, (char *)G->v[i]->data + v_wgt, sizeof(double)); if (!(0.0 <= t && t <= (double)INT_MAX && t == floor(t))) { ret = GLP_EDATA; goto done; } w[i] = (int)t; } else w[i] = 1; s += (double)w[i]; } if (s > (double)INT_MAX) { ret = GLP_EDATA; goto done; } /* build the adjacency matrix */ for (i = 1; i <= G->nv; i++) { for (e = G->v[i]->in; e != NULL; e = e->h_next) { j = e->tail->i; /* there exists edge (j,i) in the graph */ if (i > j) set_edge(G->nv, a, i, j); } for (e = G->v[i]->out; e != NULL; e = e->t_next) { j = e->head->i; /* there exists edge (i,j) in the graph */ if (i > j) set_edge(G->nv, a, i, j); } } /* find maximum weight clique in the graph */ len = wclique(G->nv, w, a, ind); /* compute the clique weight */ s = 0.0; for (k = 1; k <= len; k++) { i = ind[k]; xassert(1 <= i && i <= G->nv); s += (double)w[i]; } if (sol != NULL) *sol = s; /* mark vertices included in the clique */ if (v_set >= 0) { x = 0; for (i = 1; i <= G->nv; i++) memcpy((char *)G->v[i]->data + v_set, &x, sizeof(int)); x = 1; for (k = 1; k <= len; k++) { i = ind[k]; memcpy((char *)G->v[i]->data + v_set, &x, sizeof(int)); } } done: /* free working arrays */ xfree(w); xfree(ind); xfree(a); return ret; } /* eof */ igraph/src/cs_tdfs.c0000644000176000001440000000345312325527073014147 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* depth-first search and postorder of a tree rooted at node j */ CS_INT cs_tdfs (CS_INT j, CS_INT k, CS_INT *head, const CS_INT *next, CS_INT *post, CS_INT *stack) { CS_INT i, p, top = 0 ; if (!head || !next || !post || !stack) return (-1) ; /* check inputs */ stack [0] = j ; /* place j on the stack */ while (top >= 0) /* while (stack is not empty) */ { p = stack [top] ; /* p = top of stack */ i = head [p] ; /* i = youngest child of p */ if (i == -1) { top-- ; /* p has no unordered children left */ post [k++] = p ; /* node p is the kth postordered node */ } else { head [p] = next [i] ; /* remove i from children of p */ stack [++top] = i ; /* start dfs on child node i */ } } return (k) ; } igraph/src/cs_amd.c0000644000176000001440000004215712325527073013754 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #include "cs.h" /* clear w */ static CS_INT cs_wclear (CS_INT mark, CS_INT lemax, CS_INT *w, CS_INT n) { CS_INT k ; if (mark < 2 || (mark + lemax < 0)) { for (k = 0 ; k < n ; k++) if (w [k] != 0) w [k] = 1 ; mark = 2 ; } return (mark) ; /* at this point, w [0..n-1] < mark holds */ } /* keep off-diagonal entries; drop diagonal entries */ static CS_INT cs_diag (CS_INT i, CS_INT j, CS_ENTRY aij, void *other) { return (i != j) ; } /* p = amd(A+A') if symmetric is true, or amd(A'A) otherwise */ CS_INT *cs_amd (CS_INT order, const cs *A) /* order 0:natural, 1:Chol, 2:LU, 3:QR */ { cs *C, *A2, *AT ; CS_INT *Cp, *Ci, *last, *W, *len, *nv, *next, *P, *head, *elen, *degree, *w, *hhead, *ATp, *ATi, d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1, k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi, ok, cnz, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, n, m, t ; unsigned CS_INT h ; /* --- Construct matrix C ----------------------------------------------- */ if (!CS_CSC (A) || order <= 0 || order > 3) return (NULL) ; /* check */ AT = cs_transpose (A, 0) ; /* compute A' */ if (!AT) return (NULL) ; m = A->m ; n = A->n ; dense = CS_MAX (16, 10 * sqrt ((double) n)) ; /* find dense threshold */ dense = CS_MIN (n-2, dense) ; if (order == 1 && n == m) { C = cs_add (A, AT, 0, 0) ; /* C = A+A' */ } else if (order == 2) { ATp = AT->p ; /* drop dense columns from AT */ ATi = AT->i ; for (p2 = 0, j = 0 ; j < m ; j++) { p = ATp [j] ; /* column j of AT starts here */ ATp [j] = p2 ; /* new column j starts here */ if (ATp [j+1] - p > dense) continue ; /* skip dense col j */ for ( ; p < ATp [j+1] ; p++) ATi [p2++] = ATi [p] ; } ATp [m] = p2 ; /* finalize AT */ A2 = cs_transpose (AT, 0) ; /* A2 = AT' */ C = A2 ? cs_multiply (AT, A2) : NULL ; /* C=A'*A with no dense rows */ cs_spfree (A2) ; } else { C = cs_multiply (AT, A) ; /* C=A'*A */ } cs_spfree (AT) ; if (!C) return (NULL) ; cs_fkeep (C, &cs_diag, NULL) ; /* drop diagonal entries */ Cp = C->p ; cnz = Cp [n] ; P = cs_malloc (n+1, sizeof (CS_INT)) ; /* allocate result */ W = cs_malloc (8*(n+1), sizeof (CS_INT)) ; /* get workspace */ t = cnz + cnz/5 + 2*n ; /* add elbow room to C */ if (!P || !W || !cs_sprealloc (C, t)) return (cs_idone (P, C, W, 0)) ; len = W ; nv = W + (n+1) ; next = W + 2*(n+1) ; head = W + 3*(n+1) ; elen = W + 4*(n+1) ; degree = W + 5*(n+1) ; w = W + 6*(n+1) ; hhead = W + 7*(n+1) ; last = P ; /* use P as workspace for last */ /* --- Initialize quotient graph ---------------------------------------- */ for (k = 0 ; k < n ; k++) len [k] = Cp [k+1] - Cp [k] ; len [n] = 0 ; nzmax = C->nzmax ; Ci = C->i ; for (i = 0 ; i <= n ; i++) { head [i] = -1 ; /* degree list i is empty */ last [i] = -1 ; next [i] = -1 ; hhead [i] = -1 ; /* hash list i is empty */ nv [i] = 1 ; /* node i is just one node */ w [i] = 1 ; /* node i is alive */ elen [i] = 0 ; /* Ek of node i is empty */ degree [i] = len [i] ; /* degree of node i */ } mark = cs_wclear (0, 0, w, n) ; /* clear w */ elen [n] = -2 ; /* n is a dead element */ Cp [n] = -1 ; /* n is a root of assembly tree */ w [n] = 0 ; /* n is a dead element */ /* --- Initialize degree lists ------------------------------------------ */ for (i = 0 ; i < n ; i++) { d = degree [i] ; if (d == 0) /* node i is empty */ { elen [i] = -2 ; /* element i is dead */ nel++ ; Cp [i] = -1 ; /* i is a root of assembly tree */ w [i] = 0 ; } else if (d > dense) /* node i is dense */ { nv [i] = 0 ; /* absorb i into element n */ elen [i] = -1 ; /* node i is dead */ nel++ ; Cp [i] = CS_FLIP (n) ; nv [n]++ ; } else { if (head [d] != -1) last [head [d]] = i ; next [i] = head [d] ; /* put node i in degree list d */ head [d] = i ; } } while (nel < n) /* while (selecting pivots) do */ { /* --- Select node of minimum approximate degree -------------------- */ for (k = -1 ; mindeg < n && (k = head [mindeg]) == -1 ; mindeg++) ; if (next [k] != -1) last [next [k]] = -1 ; head [mindeg] = next [k] ; /* remove k from degree list */ elenk = elen [k] ; /* elenk = |Ek| */ nvk = nv [k] ; /* # of nodes k represents */ nel += nvk ; /* nv[k] nodes of A eliminated */ /* --- Garbage collection ------------------------------------------- */ if (elenk > 0 && cnz + mindeg >= nzmax) { for (j = 0 ; j < n ; j++) { if ((p = Cp [j]) >= 0) /* j is a live node or element */ { Cp [j] = Ci [p] ; /* save first entry of object */ Ci [p] = CS_FLIP (j) ; /* first entry is now CS_FLIP(j) */ } } for (q = 0, p = 0 ; p < cnz ; ) /* scan all of memory */ { if ((j = CS_FLIP (Ci [p++])) >= 0) /* found object j */ { Ci [q] = Cp [j] ; /* restore first entry of object */ Cp [j] = q++ ; /* new pointer to object j */ for (k3 = 0 ; k3 < len [j]-1 ; k3++) Ci [q++] = Ci [p++] ; } } cnz = q ; /* Ci [cnz...nzmax-1] now free */ } /* --- Construct new element ---------------------------------------- */ dk = 0 ; nv [k] = -nvk ; /* flag k as in Lk */ p = Cp [k] ; pk1 = (elenk == 0) ? p : cnz ; /* do in place if elen[k] == 0 */ pk2 = pk1 ; for (k1 = 1 ; k1 <= elenk + 1 ; k1++) { if (k1 > elenk) { e = k ; /* search the nodes in k */ pj = p ; /* list of nodes starts at Ci[pj]*/ ln = len [k] - elenk ; /* length of list of nodes in k */ } else { e = Ci [p++] ; /* search the nodes in e */ pj = Cp [e] ; ln = len [e] ; /* length of list of nodes in e */ } for (k2 = 1 ; k2 <= ln ; k2++) { i = Ci [pj++] ; if ((nvi = nv [i]) <= 0) continue ; /* node i dead, or seen */ dk += nvi ; /* degree[Lk] += size of node i */ nv [i] = -nvi ; /* negate nv[i] to denote i in Lk*/ Ci [pk2++] = i ; /* place i in Lk */ if (next [i] != -1) last [next [i]] = last [i] ; if (last [i] != -1) /* remove i from degree list */ { next [last [i]] = next [i] ; } else { head [degree [i]] = next [i] ; } } if (e != k) { Cp [e] = CS_FLIP (k) ; /* absorb e into k */ w [e] = 0 ; /* e is now a dead element */ } } if (elenk != 0) cnz = pk2 ; /* Ci [cnz...nzmax] is free */ degree [k] = dk ; /* external degree of k - |Lk\i| */ Cp [k] = pk1 ; /* element k is in Ci[pk1..pk2-1] */ len [k] = pk2 - pk1 ; elen [k] = -2 ; /* k is now an element */ /* --- Find set differences ----------------------------------------- */ mark = cs_wclear (mark, lemax, w, n) ; /* clear w if necessary */ for (pk = pk1 ; pk < pk2 ; pk++) /* scan 1: find |Le\Lk| */ { i = Ci [pk] ; if ((eln = elen [i]) <= 0) continue ;/* skip if elen[i] empty */ nvi = -nv [i] ; /* nv [i] was negated */ wnvi = mark - nvi ; for (p = Cp [i] ; p <= Cp [i] + eln - 1 ; p++) /* scan Ei */ { e = Ci [p] ; if (w [e] >= mark) { w [e] -= nvi ; /* decrement |Le\Lk| */ } else if (w [e] != 0) /* ensure e is a live element */ { w [e] = degree [e] + wnvi ; /* 1st time e seen in scan 1 */ } } } /* --- Degree update ------------------------------------------------ */ for (pk = pk1 ; pk < pk2 ; pk++) /* scan2: degree update */ { i = Ci [pk] ; /* consider node i in Lk */ p1 = Cp [i] ; p2 = p1 + elen [i] - 1 ; pn = p1 ; for (h = 0, d = 0, p = p1 ; p <= p2 ; p++) /* scan Ei */ { e = Ci [p] ; if (w [e] != 0) /* e is an unabsorbed element */ { dext = w [e] - mark ; /* dext = |Le\Lk| */ if (dext > 0) { d += dext ; /* sum up the set differences */ Ci [pn++] = e ; /* keep e in Ei */ h += e ; /* compute the hash of node i */ } else { Cp [e] = CS_FLIP (k) ; /* aggressive absorb. e->k */ w [e] = 0 ; /* e is a dead element */ } } } elen [i] = pn - p1 + 1 ; /* elen[i] = |Ei| */ p3 = pn ; p4 = p1 + len [i] ; for (p = p2 + 1 ; p < p4 ; p++) /* prune edges in Ai */ { j = Ci [p] ; if ((nvj = nv [j]) <= 0) continue ; /* node j dead or in Lk */ d += nvj ; /* degree(i) += |j| */ Ci [pn++] = j ; /* place j in node list of i */ h += j ; /* compute hash for node i */ } if (d == 0) /* check for mass elimination */ { Cp [i] = CS_FLIP (k) ; /* absorb i into k */ nvi = -nv [i] ; dk -= nvi ; /* |Lk| -= |i| */ nvk += nvi ; /* |k| += nv[i] */ nel += nvi ; nv [i] = 0 ; elen [i] = -1 ; /* node i is dead */ } else { degree [i] = CS_MIN (degree [i], d) ; /* update degree(i) */ Ci [pn] = Ci [p3] ; /* move first node to end */ Ci [p3] = Ci [p1] ; /* move 1st el. to end of Ei */ Ci [p1] = k ; /* add k as 1st element in of Ei */ len [i] = pn - p1 + 1 ; /* new len of adj. list of node i */ h %= n ; /* finalize hash of i */ next [i] = hhead [h] ; /* place i in hash bucket */ hhead [h] = i ; last [i] = h ; /* save hash of i in last[i] */ } } /* scan2 is done */ degree [k] = dk ; /* finalize |Lk| */ lemax = CS_MAX (lemax, dk) ; mark = cs_wclear (mark+lemax, lemax, w, n) ; /* clear w */ /* --- Supernode detection ------------------------------------------ */ for (pk = pk1 ; pk < pk2 ; pk++) { i = Ci [pk] ; if (nv [i] >= 0) continue ; /* skip if i is dead */ h = last [i] ; /* scan hash bucket of node i */ i = hhead [h] ; hhead [h] = -1 ; /* hash bucket will be empty */ for ( ; i != -1 && next [i] != -1 ; i = next [i], mark++) { ln = len [i] ; eln = elen [i] ; for (p = Cp [i]+1 ; p <= Cp [i] + ln-1 ; p++) w [Ci [p]] = mark; jlast = i ; for (j = next [i] ; j != -1 ; ) /* compare i with all j */ { ok = (len [j] == ln) && (elen [j] == eln) ; for (p = Cp [j] + 1 ; ok && p <= Cp [j] + ln - 1 ; p++) { if (w [Ci [p]] != mark) ok = 0 ; /* compare i and j*/ } if (ok) /* i and j are identical */ { Cp [j] = CS_FLIP (i) ; /* absorb j into i */ nv [i] += nv [j] ; nv [j] = 0 ; elen [j] = -1 ; /* node j is dead */ j = next [j] ; /* delete j from hash bucket */ next [jlast] = j ; } else { jlast = j ; /* j and i are different */ j = next [j] ; } } } } /* --- Finalize new element------------------------------------------ */ for (p = pk1, pk = pk1 ; pk < pk2 ; pk++) /* finalize Lk */ { i = Ci [pk] ; if ((nvi = -nv [i]) <= 0) continue ;/* skip if i is dead */ nv [i] = nvi ; /* restore nv[i] */ d = degree [i] + dk - nvi ; /* compute external degree(i) */ d = CS_MIN (d, n - nel - nvi) ; if (head [d] != -1) last [head [d]] = i ; next [i] = head [d] ; /* put i back in degree list */ last [i] = -1 ; head [d] = i ; mindeg = CS_MIN (mindeg, d) ; /* find new minimum degree */ degree [i] = d ; Ci [p++] = i ; /* place i in Lk */ } nv [k] = nvk ; /* # nodes absorbed into k */ if ((len [k] = p-pk1) == 0) /* length of adj list of element k*/ { Cp [k] = -1 ; /* k is a root of the tree */ w [k] = 0 ; /* k is now a dead element */ } if (elenk != 0) cnz = p ; /* free unused space in Lk */ } /* --- Postordering ----------------------------------------------------- */ for (i = 0 ; i < n ; i++) Cp [i] = CS_FLIP (Cp [i]) ;/* fix assembly tree */ for (j = 0 ; j <= n ; j++) head [j] = -1 ; for (j = n ; j >= 0 ; j--) /* place unordered nodes in lists */ { if (nv [j] > 0) continue ; /* skip if j is an element */ next [j] = head [Cp [j]] ; /* place j in list of its parent */ head [Cp [j]] = j ; } for (e = n ; e >= 0 ; e--) /* place elements in lists */ { if (nv [e] <= 0) continue ; /* skip unless e is an element */ if (Cp [e] != -1) { next [e] = head [Cp [e]] ; /* place e in list of its parent */ head [Cp [e]] = e ; } } for (k = 0, i = 0 ; i <= n ; i++) /* postorder the assembly tree */ { if (Cp [i] == -1) k = cs_tdfs (i, k, head, next, P, w) ; } return (cs_idone (P, C, W, 1)) ; } igraph/src/foreign-ncol-parser.h0000644000176000001440000000477612325527073016414 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton interface for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { ALNUM = 258, NEWLINE = 259 }; #endif /* Tokens. */ #define ALNUM 258 #define NEWLINE 259 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 87 "igraph/src/foreign-ncol-parser.y" { long int edgenum; double weightnum; } /* Line 1529 of yacc.c. */ #line 62 "y.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif igraph/src/glpsdf.c0000644000176000001440000001610212325527073013774 0ustar ripleyusers/* glpsdf.c (plain data file reading routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif #define GLPSDF_H #define GLP_DATA_DEFINED typedef struct glp_data glp_data; #include "glpapi.h" struct glp_data { /* plain data file */ char *fname; /* name of data file */ XFILE *fp; /* stream assigned to data file */ void *jump; /* jmp_buf jump; */ /* label for go to in case of error */ int count; /* line count */ int c; /* current character of XEOF */ char item[255+1]; /* current data item */ }; static void next_char(glp_data *data); glp_data *glp_sdf_open_file(const char *fname) { /* open plain data file */ glp_data *data = NULL; XFILE *fp; jmp_buf jump; fp = xfopen(fname, "r"); if (fp == NULL) { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); goto done; } data = xmalloc(sizeof(glp_data)); data->fname = xmalloc(strlen(fname)+1); strcpy(data->fname, fname); data->fp = fp; data->jump = NULL; data->count = 0; data->c = '\n'; data->item[0] = '\0'; /* read the very first character */ if (setjmp(jump)) { glp_sdf_close_file(data); data = NULL; goto done; } data->jump = jump; next_char(data); data->jump = NULL; done: return data; } void glp_sdf_set_jump(glp_data *data, void *jump) { /* set up error handling */ data->jump = jump; return; } void glp_sdf_error(glp_data *data, const char *fmt, ...) { /* print error message */ va_list arg; xprintf("%s:%d: ", data->fname, data->count); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); if (data->jump == NULL) xerror(""); else longjmp(data->jump, 1); /* no return */ } void glp_sdf_warning(glp_data *data, const char *fmt, ...) { /* print warning message */ va_list arg; xprintf("%s:%d: warning: ", data->fname, data->count); va_start(arg, fmt); xvprintf(fmt, arg); va_end(arg); return; } static void next_char(glp_data *data) { /* read next character */ int c; if (data->c == XEOF) glp_sdf_error(data, "unexpected end of file\n"); else if (data->c == '\n') data->count++; c = xfgetc(data->fp); if (c < 0) { if (xferror(data->fp)) glp_sdf_error(data, "read error - %s\n", xerrmsg()); else if (data->c == '\n') c = XEOF; else { glp_sdf_warning(data, "missing final end of line\n"); c = '\n'; } } else if (c == '\n') ; else if (isspace(c)) c = ' '; else if (iscntrl(c)) glp_sdf_error(data, "invalid control character 0x%02X\n", c); data->c = c; return; } static void skip_pad(glp_data *data) { /* skip uninteresting characters and comments */ loop: while (data->c == ' ' || data->c == '\n') next_char(data); if (data->c == '/') { next_char(data); if (data->c != '*') glp_sdf_error(data, "invalid use of slash\n"); next_char(data); for (;;) { if (data->c == '*') { next_char(data); if (data->c == '/') { next_char(data); break; } } next_char(data); } goto loop; } return; } static void next_item(glp_data *data) { /* read next item */ int len; skip_pad(data); len = 0; while (!(data->c == ' ' || data->c == '\n')) { data->item[len++] = (char)data->c; if (len == sizeof(data->item)) glp_sdf_error(data, "data item `%.31s...' too long\n", data->item); next_char(data); } data->item[len] = '\0'; return; } int glp_sdf_read_int(glp_data *data) { /* read integer number */ int x; next_item(data); switch (str2int(data->item, &x)) { case 0: break; case 1: glp_sdf_error(data, "integer `%s' out of range\n", data->item); case 2: glp_sdf_error(data, "cannot convert `%s' to integer\n", data->item); default: xassert(data != data); } return x; } double glp_sdf_read_num(glp_data *data) { /* read floating-point number */ double x; next_item(data); switch (str2num(data->item, &x)) { case 0: break; case 1: glp_sdf_error(data, "number `%s' out of range\n", data->item); case 2: glp_sdf_error(data, "cannot convert `%s' to number\n", data->item); default: xassert(data != data); } return x; } const char *glp_sdf_read_item(glp_data *data) { /* read data item */ next_item(data); return data->item; } const char *glp_sdf_read_text(glp_data *data) { /* read text until end of line */ int c, len = 0; for (;;) { c = data->c; next_char(data); if (c == ' ') { /* ignore initial spaces */ if (len == 0) continue; /* and multiple ones */ if (data->item[len-1] == ' ') continue; } else if (c == '\n') { /* remove trailing space */ if (len > 0 && data->item[len-1] == ' ') len--; /* and stop reading */ break; } /* add current character to the buffer */ data->item[len++] = (char)c; if (len == sizeof(data->item)) glp_sdf_error(data, "line too long\n", data->item); } data->item[len] = '\0'; return data->item; } int glp_sdf_line(glp_data *data) { /* determine current line number */ return data->count; } void glp_sdf_close_file(glp_data *data) { /* close plain data file */ xfclose(data->fp); xfree(data->fname); xfree(data); return; } /* eof */ igraph/src/bliss_utils.hh0000644000176000001440000000144112325372072015223 0ustar ripleyusers/* Copyright (C) 2003-2006 Tommi Junttila This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ #ifndef BLISS_UTILS_HH #define BLISS_UTILS_HH #endif igraph/src/DensityGrid.h0000644000176000001440000000533612325527072014755 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __DENSITY_GRID_H__ #define __DENSITY_GRID_H__ // Compile time adjustable parameters #include using namespace std; #include "drl_layout.h" #include "drl_Node.h" #ifdef MUSE_MPI #include #endif namespace drl { class DensityGrid { public: // Methods void Init(); void Subtract(Node &n, bool first_add, bool fine_first_add, bool fineDensity); void Add(Node &n, bool fineDensity ); float GetDensity(float Nx, float Ny, bool fineDensity); // Contructor/Destructor DensityGrid() {}; ~DensityGrid(); private: // Private Members void Subtract( Node &N ); void Add( Node &N ); void fineSubtract( Node &N ); void fineAdd( Node &N ); // new dynamic variables -- SBM float (*fall_off)[RADIUS*2+1]; float (*Density)[GRID_SIZE]; deque* Bins; // old static variables //float fall_off[RADIUS*2+1][RADIUS*2+1]; //float Density[GRID_SIZE][GRID_SIZE]; //deque Bins[GRID_SIZE][GRID_SIZE]; }; } // namespace drl #endif // __DENSITY_GRID_H__ igraph/src/gengraph_graph_molloy_optimized.h0000644000176000001440000002556212325527073021167 0ustar ripleyusers/* * * gengraph - generation of random simple connected graphs with prescribed * degree sequence * * Copyright (C) 2006 Fabien Viger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GRAPH_MOLLOY_OPT_H #define GRAPH_MOLLOY_OPT_H #include "gengraph_definitions.h" #include "gengraph_degree_sequence.h" #include "gengraph_qsort.h" #include #include "gengraph_random.h" namespace gengraph { // This class handles graphs with a constant degree sequence. class graph_molloy_opt { private: // Random generator KW_RNG::RNG rng; // Number of vertices int n; //Number of arcs ( = #edges * 2 ) int a; // The degree sequence of the graph int *deg; // The array containing all links int *links; // The array containing pointers to adjacency list of every vertices int **neigh; // Allocate memory according to degree_sequence (for constructor use only!!) void alloc(degree_sequence &); // Compute #edges inline void refresh_nbarcs() { a=0; for(int* d=deg+n; d!=deg; ) a += *(--d); } // Build neigh with deg and links void compute_neigh(); // Swap edges. The swap MUST be valid !!! inline void swap_edges(int from1, int to1, int from2, int to2) { fast_rpl(neigh[from1],to1,to2); fast_rpl(neigh[from2],to2,to1); fast_rpl(neigh[to1],from1,from2); fast_rpl(neigh[to2],from2,from1); } // Swap edges only if they are simple. return false if unsuccessful. bool swap_edges_simple(int ,int ,int, int); // Test if vertex is in an isolated component of size dmax. void depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited); // breadth-first search. Store the distance (modulo 3) in dist[]. Returns eplorated component size. int width_search(unsigned char *dist, int *buff, int v0=0, int toclear=-1); // depth-first search. int depth_search(bool *visited, int *buff, int v0=0); // breadth-first search that count the number of shortest paths going from src to each vertex int breadth_path_search(int src, int *buff, double *paths, unsigned char *dist); // Used by traceroute_sample() ONLY void add_traceroute_edge(int, int, int*, double** red=NULL, double t=1.0); // Used by traceroute() and betweenness(). if newdeg[]=NULL, do not discover edges. // breadth_path_search() must have been called to give the corresponding buff[],dist[],paths[] and nb_vertices void explore_usp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg=NULL, double **edge_redudancy=NULL); void explore_asp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg=NULL, double **edge_redudancy=NULL); void explore_rsp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg=NULL, double **edge_redudancy=NULL); // Return component indexes where vertices belong to, starting from 0, // sorted by size (biggest component has index 0) int *components(int *comp=NULL); // pick k random vertices of degree > 0. int *pick_random_vertices(int &k, int *output=NULL, int nb_v=-1, int *among=NULL); public: // neigh[] inline int** neighbors() { return neigh; }; // deg[] inline int* degrees() { return deg; }; //adjacency list of v inline int* operator[](const int v) { return neigh[v]; }; //degree of v inline int degree(const int v) { return deg[v]; }; //compare adjacency lists inline int compare(const int v, const int w) { return deg[v]==deg[w] ? lex_comp(neigh[v],neigh[w],deg[v]) : (deg[v]>deg[w] ? -1 : 1); }; // Detach deg[] and neigh[] void detach(); // Destroy deg and links ~graph_molloy_opt(); // Create graph from file (stdin not supported unless rewind() possible) graph_molloy_opt(FILE *f); // Allocate memory for the graph. Create deg and links. No edge is created. graph_molloy_opt(degree_sequence &); // Create graph from hard copy graph_molloy_opt(int *); // Create hard copy of graph int *hard_copy(); // Remove unused edges, updates neigh[], recreate links[] void clean(); // nb arcs inline int nbarcs() { return a; }; // last degree inline int last_degree() { return deg[n-1]; }; // nb vertices inline int nbvertices() { return n; }; // nb vertices having degree > 0 inline int nbvertices_real() { int s=0; for(int *d=deg+n; d--!=deg; ) if(*d) s++; return s; }; // return list of vertices with degree > 0. Compute #vertices, if not given. int *vertices_real(int &nb_v); // Keep only giant component void giant_comp(); // nb vertices in giant component int nbvertices_comp(); // nb arcs in giant component int nbarcs_comp(); // print graph in SUCC_LIST mode, in stdout void print(FILE *f=stdout, bool NOZERO=true); // Bind the graph avoiding multiple edges or self-edges (return false if fail) bool havelhakimi(); // Get the graph connected (return false if fail) bool make_connected(); // Test if graph is connected bool is_connected(); // Maximum degree int max_degree(); // breadth-first search. Store the distance (modulo 3) in dist[]. void breadth_search(int *dist, int v0=0, int* buff=NULL); // is edge ? inline bool is_edge(const int a, const int b) { if(deg[b] 0. If k \in [0,1[, k is understood as a density. int *pick_random_src(double k, int *nb=NULL, int* buff=NULL, int nb_v=-1, int* among=NULL); // pick k random vertices of degree > 0. If k \in [0,1], k is understood as a density. int *pick_random_dst(double k, int *nb=NULL, int* buff=NULL, int nb_v=-1, int* among=NULL); // For debug purposes : verify validity of the graph (symetry, simplicity) #define VERIFY_NORMAL 0 #define VERIFY_NONEIGH 1 #define VERIFY_NOARCS 2 bool verify(int mode=VERIFY_NORMAL); /*___________________________________________________________________________________ Not to use anymore : use graph_molloy_hash class instead public: // Shuffle. returns number of swaps done. void shuffle(long); // Connected Shuffle long connected_shuffle(long); // Get caracteristic K double eval_K(int quality = 100); // Get effective K double effective_K(int K, int quality = 10000); // Test window double window(int K, double ratio); // Try to shuffle n times. Return true if at the end, the graph was still connected. bool try_shuffle(int T, int K); //___________________________________________________________________________________ //*/ /*___________________________________________________________________________________ Not to use anymore : replaced by vertex_betweenness() 22/04/2005 // shortest paths where vertex is an extremity long long *vertex_betweenness_usp(bool trivial_path); // shortest paths where vertex is an extremity long long *vertex_betweenness_rsp(bool trivial_path); // same, but when multiple shortest path are possible, average the weights. double *vertex_betweenness_asp(bool trivial_path); //___________________________________________________________________________________ //*/ }; } // namespace gengraph #endif //GRAPH_MOLLOY_OPT_H igraph/src/adjlist.c0000644000176000001440000006301512325527072014153 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_adjlist.h" #include "igraph_memory.h" #include "igraph_interface.h" #include "igraph_interrupt_internal.h" #include "config.h" #include /* memset */ #include /** * \section about_adjlists * Sometimes it is easier to work with a graph which is in * adjacency list format: a list of vectors; each vector contains the * neighbor vertices or incident edges of a given vertex. Typically, * this representation is good if we need to iterate over the neighbors * of all vertices many times. E.g. when finding the shortest paths * between every pairs of vertices or calculating closeness centrality * for all the vertices. * * The igraph_adjlist_t stores the adjacency lists * of a graph. After creation it is independent of the original graph, * it can be modified freely with the usual vector operations, the * graph is not affected. E.g. the adjacency list can be used to * rewire the edges of a graph efficiently. If one used the * straightforward \ref igraph_delete_edges() and \ref * igraph_add_edges() combination for this that needs O(|V|+|E|) time * for every single deletion and insertion operation, it is thus very * slow if many edges are rewired. Extracting the graph into an * adjacency list, do all the rewiring operations on the vectors of * the adjacency list and then creating a new graph needs (depending * on how exactly the rewiring is done) typically O(|V|+|E|) time for * the whole rewiring process. * * Lazy adjacency lists are a bit different. When creating a * lazy adjacency list, the neighbors of the vertices are not queried, * only some memory is allocated for the vectors. When \ref * igraph_lazy_adjlist_get() is called for vertex v the first time, * the neighbors of v are queried and stored in a vector of the * adjacency list, so they don't need to be queried again. Lazy * adjacency lists are handy if you have an at least linear operation * (because initialization is generally linear in terms of number of * vertices), but you don't know how many vertices you will visit * during the computation. * * * * \example examples/simple/adjlist.c * */ /** * \function igraph_adjlist_init * Initialize an adjacency list of vertices from a given graph * * Create a list of vectors containing the neighbors of all vertices * in a graph. The adjacency list is independent of the graph after * creation, e.g. the graph can be destroyed and modified, the * adjacency list contains the state of the graph at the time of its * initialization. * \param graph The input graph. * \param al Pointer to an uninitialized igraph_adjlist_t object. * \param mode Constant specifying whether outgoing * (IGRAPH_OUT), incoming (IGRAPH_IN), * or both (IGRAPH_ALL) types of neighbors to include * in the adjacency list. It is ignored for undirected networks. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. */ int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode) { igraph_integer_t i; igraph_vector_t tmp; if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_EINVMODE); } igraph_vector_init(&tmp, 0); IGRAPH_FINALLY(igraph_vector_destroy, &tmp); if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } al->length=igraph_vcount(graph); al->adjs=igraph_Calloc(al->length, igraph_vector_int_t); if (al->adjs == 0) { IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_adjlist_destroy, al); for (i=0; ilength; i++) { int j, n; IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_CHECK(igraph_neighbors(graph, &tmp, i, mode)); n=igraph_vector_size(&tmp); IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n)); for (j=0; jadjs[i])[j] = VECTOR(tmp)[j]; } } igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_adjlist_init_empty * Initialize an empty adjacency list * * Creates a list of vectors, one for each vertex. This is useful when you * are \em constructing a graph using an adjacency list representation as * it does not require your graph to exist yet. * \param no_of_nodes The number of vertices * \param al Pointer to an uninitialized igraph_adjlist_t object. * \return Error code. * * Time complexity: O(|V|), linear in the number of vertices. */ int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes) { long int i; al->length=no_of_nodes; al->adjs=igraph_Calloc(al->length, igraph_vector_int_t); if (al->adjs == 0) { IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_adjlist_destroy, al); for (i=0; ilength; i++) { IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], 0)); } IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_adjlist_init_complementer * Adjacency lists for the complementer graph * * This function creates adjacency lists for the complementer * of the input graph. In the complementer graph all edges are present * which are not present in the original graph. Multiple edges in the * input graph are ignored. * \param graph The input graph. * \param al Pointer to a not yet initialized adjacency list. * \param mode Constant specifying whether outgoing * (IGRAPH_OUT), incoming (IGRAPH_IN), * or both (IGRAPH_ALL) types of neighbors (in the * complementer graph) to include in the adjacency list. It is * ignored for undirected networks. * \param loops Whether to consider loop edges. * \return Error code. * * Time complexity: O(|V|^2+|E|), quadratic in the number of vertices. */ int igraph_adjlist_init_complementer(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode, igraph_bool_t loops) { igraph_integer_t i, j, k, n; igraph_bool_t* seen; igraph_vector_t vec; if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } al->length=igraph_vcount(graph); al->adjs=igraph_Calloc(al->length, igraph_vector_int_t); if (al->adjs == 0) { IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_adjlist_destroy, al); n=al->length; seen=igraph_Calloc(n, igraph_bool_t); if (seen==0) { IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, seen); IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); for (i=0; ilength; i++) { IGRAPH_ALLOW_INTERRUPTION(); igraph_neighbors(graph, &vec, i, mode); memset(seen, 0, sizeof(igraph_bool_t)*(unsigned) al->length); n=al->length; if (!loops) { seen[i] = 1; n--; } for (j=0; jadjs[i], n)); for (j=0, k=0; kadjs[i])[k++] = j; } } } igraph_Free(seen); igraph_vector_destroy(&vec); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \function igraph_adjlist_destroy * Deallocate memory * * Free all memory allocated for an adjacency list. * \param al The adjacency list to destroy. * * Time complexity: depends on memory management. */ void igraph_adjlist_destroy(igraph_adjlist_t *al) { long int i; for (i=0; ilength; i++) { if (&al->adjs[i]) { igraph_vector_int_destroy(&al->adjs[i]); } } igraph_Free(al->adjs); } /** * \function igraph_adjlist_clear * Removes all edges from an adjacency list. * * \param al The adjacency list. * Time complexity: depends on memory management, typically O(n), where n is * the total number of elements in the adjacency list. */ void igraph_adjlist_clear(igraph_adjlist_t *al) { long int i; for (i=0; ilength; i++) { igraph_vector_int_clear(&al->adjs[i]); } } /** * \function igraph_adjlist_size * Number of vertices in an adjacency list. * * \param al The adjacency list. * \return The number of elements. * * Time complexity: O(1). */ igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al) { return al->length; } /* igraph_vector_int_t *igraph_adjlist_get(igraph_adjlist_t *al, igraph_integer_t no) { */ /* return &al->adjs[(long int)no]; */ /* } */ /** * \function igraph_adjlist_sort * Sort each vector in an adjacency list. * * Sorts every vector of the adjacency list. * \param al The adjacency list. * * Time complexity: O(n log n), n is the total number of elements in * the adjacency list. */ void igraph_adjlist_sort(igraph_adjlist_t *al) { long int i; for (i=0; ilength; i++) igraph_vector_int_sort(&al->adjs[i]); } /** * \function igraph_adjlist_simplify * Simplify * * Simplify an adjacency list, ie. remove loop and multiple edges. * \param al The adjacency list. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of edges and * vertices. */ int igraph_adjlist_simplify(igraph_adjlist_t *al) { long int i; long int n=al->length; igraph_vector_int_t mark; igraph_vector_int_init(&mark, n); IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); for (i=0; iadjs[i]; long int j, l=igraph_vector_int_size(v); VECTOR(mark)[i] = i+1; for (j=0; jlength; IGRAPH_UNUSED(graph); for (i=0; iadjs[i]; long int j, p=1, l=igraph_vector_int_size(v); for (j=1; jlength; for (i=0; iadjs[i]; igraph_vector_int_print(v); } return 0; } #endif int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile) { long int i; long int n=al->length; for (i=0; iadjs[i]; igraph_vector_int_fprint(v, outfile); } return 0; } int igraph_adjedgelist_remove_duplicate(const igraph_t *graph, igraph_inclist_t *al) { IGRAPH_WARNING("igraph_adjedgelist_remove_duplicate() is deprecated, use " "igraph_inclist_remove_duplicate() instead"); return igraph_inclist_remove_duplicate(graph, al); } #ifndef USING_R int igraph_adjedgelist_print(const igraph_inclist_t *al, FILE *outfile) { IGRAPH_WARNING("igraph_adjedgelist_print() is deprecated, use " "igraph_inclist_print() instead"); return igraph_inclist_fprint(al, outfile); } #endif /** * \function igraph_adjedgelist_init * Initialize an incidence list of edges * * This function was superseded by \ref igraph_inclist_init() in igraph 0.6. * Please use \ref igraph_inclist_init() instead of this function. * * * Deprecated in version 0.6. */ int igraph_adjedgelist_init(const igraph_t *graph, igraph_inclist_t *il, igraph_neimode_t mode) { IGRAPH_WARNING("igraph_adjedgelist_init() is deprecated, use " "igraph_inclist_init() instead"); return igraph_inclist_init(graph, il, mode); } /** * \function igraph_adjedgelist_destroy * Frees all memory allocated for an incidence list. * * This function was superseded by \ref igraph_inclist_destroy() in igraph 0.6. * Please use \ref igraph_inclist_destroy() instead of this function. * * * Deprecated in version 0.6. */ void igraph_adjedgelist_destroy(igraph_inclist_t *il) { IGRAPH_WARNING("igraph_adjedgelist_destroy() is deprecated, use " "igraph_inclist_destroy() instead"); igraph_inclist_destroy(il); } int igraph_inclist_remove_duplicate(const igraph_t *graph, igraph_inclist_t *al) { long int i; long int n=al->length; for (i=0; iincs[i]; long int j, p=1, l=igraph_vector_size(v); for (j=1; jlength; for (i=0; iincs[i]; igraph_vector_print(v); } return 0; } #endif int igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { long int i; long int n=al->length; for (i=0; iincs[i]; igraph_vector_fprint(v, outfile); } return 0; } /** * \function igraph_inclist_init * Initialize an incidence list of edges * * Create a list of vectors containing the incident edges for all * vertices. The incidence list is independent of the graph after * creation, subsequent changes of the graph object do not update the * incidence list, and changes to the incidence list do not update the * graph. * \param graph The input graph. * \param il Pointer to an uninitialized incidence list. * \param mode Constant specifying whether incoming edges * (IGRAPH_IN), outgoing edges (IGRAPH_OUT) or * both (IGRAPH_ALL) to include in the incidence lists * of directed graphs. It is ignored for undirected graphs. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. */ int igraph_inclist_init(const igraph_t *graph, igraph_inclist_t *il, igraph_neimode_t mode) { igraph_integer_t i; if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } il->length=igraph_vcount(graph); il->incs=igraph_Calloc(il->length, igraph_vector_t); if (il->incs == 0) { IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_inclist_destroy, il); for (i=0; ilength; i++) { IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_CHECK(igraph_vector_init(&il->incs[i], 0)); IGRAPH_CHECK(igraph_incident(graph, &il->incs[i], i, mode)); } IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_inclist_init_empty * \brief Initialize an incidence list corresponding to an empty graph. * * This function essentially creates a list of empty vectors that may * be treated as an incidence list for a graph with a given number of * vertices. * * \param il Pointer to an uninitialized incidence list. * \param n The number of vertices in the incidence list. * \return Error code. * * Time complexity: O(|V|), linear in the number of vertices. */ int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) { long int i; il->length=n; il->incs=igraph_Calloc(il->length, igraph_vector_t); if (il->incs == 0) { IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_inclist_destroy, il); for (i=0; iincs[i], 0)); } IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_inclist_destroy * Frees all memory allocated for an incidence list. * * \param eal The incidence list to destroy. * * Time complexity: depends on memory management. */ void igraph_inclist_destroy(igraph_inclist_t *il) { long int i; for (i=0; ilength; i++) { /* This works if some igraph_vector_t's are 0, because igraph_vector_destroy can handle this. */ igraph_vector_destroy(&il->incs[i]); } igraph_Free(il->incs); } /** * \function igraph_inclist_clear * Removes all edges from an incidence list. * * \param il The incidence list. * Time complexity: depends on memory management, typically O(n), where n is * the total number of elements in the incidence list. */ void igraph_inclist_clear(igraph_inclist_t *il) { long int i; for (i=0; ilength; i++) { igraph_vector_clear(&il->incs[i]); } } /** * \function igraph_lazy_adjlist_init * Constructor * * Create a lazy adjacency list for vertices. This function only * allocates some memory for storing the vectors of an adjacency list, * but the neighbor vertices are not queried, only at the \ref * igraph_lazy_adjlist_get() calls. * \param graph The input graph. * \param al Pointer to an uninitialized adjacency list object. * \param mode Constant, it gives whether incoming edges * (IGRAPH_IN), outgoing edges * (IGRPAH_OUT) or both types of edges * (IGRAPH_ALL) are considered. It is ignored for * undirected graphs. * \param simplify Constant, it gives whether to simplify the vectors * in the adjacency list (IGRAPH_SIMPLIFY) or not * (IGRAPH_DONT_SIMPLIFY). * \return Error code. * * Time complexity: O(|V|), the number of vertices, possibly, but * depends on the underlying memory management too. */ int igraph_lazy_adjlist_init(const igraph_t *graph, igraph_lazy_adjlist_t *al, igraph_neimode_t mode, igraph_lazy_adlist_simplify_t simplify) { if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { IGRAPH_ERROR("Cannor create adjlist view", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } al->mode=mode; al->simplify=simplify; al->graph=graph; al->length=igraph_vcount(graph); al->adjs=igraph_Calloc(al->length, igraph_vector_t*); if (al->adjs == 0) { IGRAPH_ERROR("Cannot create lazy adjlist view", IGRAPH_ENOMEM); } return 0; } /** * \function igraph_lazy_adjlist_destroy * Deallocate memory * * Free all allocated memory for a lazy adjacency list. * \param al The adjacency list to deallocate. * * Time complexity: depends on the memory management. */ void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al) { igraph_lazy_adjlist_clear(al); igraph_Free(al->adjs); } /** * \function igraph_lazy_adjlist_clear * Removes all edges from a lazy adjacency list. * * \param al The lazy adjacency list. * Time complexity: depends on memory management, typically O(n), where n is * the total number of elements in the adjacency list. */ void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al) { long int i, n=al->length; for (i=0; iadjs[i] != 0) { igraph_vector_destroy(al->adjs[i]); igraph_Free(al->adjs[i]); } } } igraph_vector_t *igraph_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, igraph_integer_t pno) { igraph_integer_t no=pno; int ret; if (al->adjs[no] == 0) { al->adjs[no] = igraph_Calloc(1, igraph_vector_t); if (al->adjs[no] == 0) { igraph_error("Lazy adjlist failed", __FILE__, __LINE__, IGRAPH_ENOMEM); } ret=igraph_vector_init(al->adjs[no], 0); if (ret != 0) { igraph_error("", __FILE__, __LINE__, ret); } ret=igraph_neighbors(al->graph, al->adjs[no], no, al->mode); if (ret != 0) { igraph_error("", __FILE__, __LINE__, ret); } if (al->simplify == IGRAPH_SIMPLIFY) { igraph_vector_t *v=al->adjs[no]; long int i, p=0, n=igraph_vector_size(v); for (i=0; iadjs[no]; } /** * \function igraph_lazy_adjedgelist_init * Initializes a lazy incidence list of edges * * This function was superseded by \ref igraph_lazy_inclist_init() in igraph 0.6. * Please use \ref igraph_lazy_inclist_init() instead of this function. * * * Deprecated in version 0.6. */ int igraph_lazy_adjedgelist_init(const igraph_t *graph, igraph_lazy_inclist_t *il, igraph_neimode_t mode) { IGRAPH_WARNING("igraph_lazy_adjedgelist_init() is deprecated, use " "igraph_lazy_inclist_init() instead"); return igraph_lazy_inclist_init(graph, il, mode); } /** * \function igraph_lazy_adjedgelist_destroy * Frees all memory allocated for an incidence list. * * This function was superseded by \ref igraph_lazy_inclist_destroy() in igraph 0.6. * Please use \ref igraph_lazy_inclist_destroy() instead of this function. * * * Deprecated in version 0.6. */ void igraph_lazy_adjedgelist_destroy(igraph_lazy_inclist_t *il) { IGRAPH_WARNING("igraph_lazy_adjedgelist_destroy() is deprecated, use " "igraph_lazy_inclist_destroy() instead"); igraph_lazy_inclist_destroy(il); } igraph_vector_t *igraph_lazy_adjedgelist_get_real(igraph_lazy_adjedgelist_t *il, igraph_integer_t pno) { IGRAPH_WARNING("igraph_lazy_adjedgelist_get_real() is deprecated, use " "igraph_lazy_inclist_get_real() instead"); return igraph_lazy_inclist_get_real(il, pno); } /** * \function igraph_lazy_inclist_init * Initializes a lazy incidence list of edges * * Create a lazy incidence list for edges. This function only * allocates some memory for storing the vectors of an incidence list, * but the incident edges are not queried, only when \ref * igraph_lazy_inclist_get() is called. * \param graph The input graph. * \param al Pointer to an uninitialized incidence list. * \param mode Constant, it gives whether incoming edges * (IGRAPH_IN), outgoing edges * (IGRPAH_OUT) or both types of edges * (IGRAPH_ALL) are considered. It is ignored for * undirected graphs. * \return Error code. * * Time complexity: O(|V|), the number of vertices, possibly. But it * also depends on the underlying memory management. */ int igraph_lazy_inclist_init(const igraph_t *graph, igraph_lazy_inclist_t *al, igraph_neimode_t mode) { if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode=IGRAPH_ALL; } al->mode=mode; al->graph=graph; al->length=igraph_vcount(graph); al->incs=igraph_Calloc(al->length, igraph_vector_t*); if (al->incs == 0) { IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_ENOMEM); } return 0; } /** * \function igraph_lazy_inclist_destroy * Deallocates memory * * Frees all allocated memory for a lazy incidence list. * \param al The incidence list to deallocate. * * Time complexity: depends on memory management. */ void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il) { igraph_lazy_inclist_clear(il); igraph_Free(il->incs); } /** * \function igraph_lazy_inclist_clear * Removes all edges from a lazy incidence list. * * \param il The lazy incidence list. * Time complexity: depends on memory management, typically O(n), where n is * the total number of elements in the incidence list. */ void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il) { long int i, n=il->length; for (i=0; iincs[i] != 0) { igraph_vector_destroy(il->incs[i]); igraph_Free(il->incs[i]); } } } igraph_vector_t *igraph_lazy_inclist_get_real(igraph_lazy_inclist_t *il, igraph_integer_t pno) { igraph_integer_t no=pno; int ret; if (il->incs[no] == 0) { il->incs[no] = igraph_Calloc(1, igraph_vector_t); if (il->incs[no] == 0) { igraph_error("Lazy incidence list query failed", __FILE__, __LINE__, IGRAPH_ENOMEM); } ret=igraph_vector_init(il->incs[no], 0); if (ret != 0) { igraph_error("", __FILE__, __LINE__, ret); } ret=igraph_incident(il->graph, il->incs[no], no, il->mode); if (ret != 0) { igraph_error("", __FILE__, __LINE__, ret); } } return il->incs[no]; } igraph/src/NetDataTypes.cpp0000644000176000001440000001472512325527072015432 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Jörg Reichardt The original copyright notice follows here */ /*************************************************************************** NetDataTypes.cpp - description ------------------- begin : Mon Oct 6 2003 copyright : (C) 2003 by Joerg Reichardt email : reichardt@mitte ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "NetDataTypes.h" //################################################################################# //############################################################################### //Constructor NNode::NNode(unsigned long ind, unsigned long c_ind, DLList *ll, char* n, int states) { index=ind; cluster_index=c_ind; neighbours = new DLList(); n_links = new DLList(); global_link_list=ll; strcpy(name,n); color.red=0; color.green=0; color.blue=0; strcpy(color.pajek_c,"Green"); clustering=0.0; marker=0; affiliations=0; weight=0.0; affinity=0.0; distance=0; max_states=states; state_history=new unsigned long[states+1]; } //Destructor NNode::~NNode() { Disconnect_From_All(); delete neighbours; delete n_links; delete [] state_history; neighbours=NULL; n_links=NULL; state_history=NULL; } void NNode::Add_StateHistory(unsigned int state) { if (max_states>=state) { state_history[state]++; } } void NNode::Set_Color(RGBcolor c) { color.red=c.red; color.blue=c.blue; color.green=c.green; strcpy(color.pajek_c,c.pajek_c); } int NNode::Connect_To(NNode* neighbour, double weight) { NLink *link; //sollen doppelte Links erlaubt sein?? NEIN if (!neighbour) return 0; if (!(neighbours->Is_In_List(neighbour)) && (neighbour!=this)) { neighbours->Push(neighbour); // nachbar hier eintragen neighbour->neighbours->Push(this); // diesen knoten beim nachbarn eintragen link=new NLink(this,neighbour, weight); //link erzeugen global_link_list->Push(link); // in globaler liste eintragen n_links->Push(link); // bei diesem Knoten eintragen neighbour->n_links->Push(link); // beim nachbarn eintragen return(1); } return(0); } NLink *NNode::Get_LinkToNeighbour(NNode* neighbour) { DLList_Iter iter; NLink *l_cur, *link=0; bool found=false; // finde einen bestimmten Link aus der Liste der links eines Knotens l_cur=iter.First(n_links); while (!iter.End() && !found) { if (((l_cur->Get_Start()==this) && (l_cur->Get_End()==neighbour)) || ((l_cur->Get_End()==this) && (l_cur->Get_Start()==neighbour))) { found=true; link=l_cur; } l_cur=iter.Next(); } if (found) return link; else return NULL; } int NNode::Disconnect_From(NNode* neighbour) { //sollen doppelte Links erlaubt sein?? s.o. if (!neighbours) return 0; neighbours->fDelete(neighbour); n_links->fDelete(Get_LinkToNeighbour(neighbour)); neighbour->n_links->fDelete(neighbour->Get_LinkToNeighbour(this)); neighbour->neighbours->fDelete(this); return 1; } int NNode::Disconnect_From_All() { int number_of_neighbours=0; while (neighbours->Size()) { Disconnect_From(neighbours->Pop()); number_of_neighbours++; } return(number_of_neighbours) ; } /* int NNode::Disconnect_From_All_Grandchildren() { int n_l=links->Size(); unsigned long pos=0; while ((n_l--)>1) { //alle bis auf das erste loeschen pos=(links->Get(n_l+1))->links->Is_In_List(this); // printf("%d %d\n",n_l,pos); (links->Get(n_l+1))->links->Delete(pos); } return(pos) ; } */ double NNode::Get_Links_Among_Neigbours(void) { // long neighbours1, neighbours2; double lam=0; DLList_Iter iter1, iter2; // neighbours1=neighbours->Size(); //so viele Nachbarn hat die Betrachtete Node NNode *step1,*step2; step1=iter1.First(neighbours); while (!iter1.End()) // for (int n1=1;n1<=neighbours1; n1++) { //step1=neighbours->Get(n1); //neighbours2=step1->neighbours->Size(); //so viele Nachbarn hat der n1-ste Nachbar step2=iter2.First(step1->Get_Neighbours()); while (!iter2.End()) //for (int n2=1;n2<=neighbours2; n2++) { //step2=step1->neighbours->Get(n2); if (step2->Get_Neighbours()->Is_In_List(this)) {lam++;} step2=iter2.Next(); } step1=iter1.Next(); } return(lam/2.0); } double NNode::Get_Clustering() { double c; unsigned long k; k=neighbours->Size(); if (k<=1) return(0); c=2.0*Get_Links_Among_Neigbours()/double(k*k-k); return(c); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++ //Constructor NLink::NLink(NNode *s, NNode *e, double w) { start=s; end=e; weight=w; old_weight=0; marker=0; } //Destructor NLink::~NLink() { if (start && end) start->Disconnect_From(end); } igraph/src/dnapps.f0000644000176000001440000005614112325527073014014 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdnapps c c\Description: c Given the Arnoldi factorization c c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, c c apply NP implicit shifts resulting in c c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q c c where Q is an orthogonal matrix which is the product of rotations c and reflections resulting from the NP bulge chage sweeps. c The updated Arnoldi factorization becomes: c c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. c c\Usage: c call igraphdnapps c ( N, KEV, NP, SHIFTR, SHIFTI, V, LDV, H, LDH, RESID, Q, LDQ, c WORKL, WORKD ) c c\Arguments c N Integer. (INPUT) c Problem size, i.e. size of matrix A. c c KEV Integer. (INPUT/OUTPUT) c KEV+NP is the size of the input matrix H. c KEV is the size of the updated matrix HNEW. KEV is only c updated on ouput when fewer than NP shifts are applied in c order to keep the conjugate pair together. c c NP Integer. (INPUT) c Number of implicit shifts to be applied. c c SHIFTR, Double precision array of length NP. (INPUT) c SHIFTI Real and imaginary part of the shifts to be applied. c Upon, entry to igraphdnapps, the shifts must be sorted so that the c conjugate pairs are in consecutive locations. c c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) c On INPUT, V contains the current KEV+NP Arnoldi vectors. c On OUTPUT, V contains the updated KEV Arnoldi vectors c in the first KEV columns of V. c c LDV Integer. (INPUT) c Leading dimension of V exactly as declared in the calling c program. c c H Double precision (KEV+NP) by (KEV+NP) array. (INPUT/OUTPUT) c On INPUT, H contains the current KEV+NP by KEV+NP upper c Hessenber matrix of the Arnoldi factorization. c On OUTPUT, H contains the updated KEV by KEV upper Hessenberg c matrix in the KEV leading submatrix. c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c RESID Double precision array of length N. (INPUT/OUTPUT) c On INPUT, RESID contains the the residual vector r_{k+p}. c On OUTPUT, RESID is the update residual vector rnew_{k} c in the first KEV locations. c c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) c Work array used to accumulate the rotations and reflections c during the bulge chase sweep. c c LDQ Integer. (INPUT) c Leading dimension of Q exactly as declared in the calling c program. c c WORKL Double precision work array of length (KEV+NP). (WORKSPACE) c Private (replicated) array on each PE or array allocated on c the front end. c c WORKD Double precision work array of length 2*N. (WORKSPACE) c Distributed array used in the application of the accumulated c orthogonal matrix Q. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\References: c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), c pp 357-385. c c\Routines called: c igraphivout ARPACK utility routine that prints integers. c igraphsecond ARPACK utility routine for timing. c igraphdmout ARPACK utility routine that prints matrices. c igraphdvout ARPACK utility routine that prints vectors. c dlabad LAPACK routine that computes machine constants. c dlacpy LAPACK matrix copy routine. c dlamch LAPACK routine that determines machine constants. c dlanhs LAPACK routine that computes various norms of a matrix. c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. c dlarf LAPACK routine that applies Householder reflection to c a matrix. c dlarfg LAPACK Householder reflection construction routine. c dlartg LAPACK Givens rotation construction routine. c dlaset LAPACK matrix initialization routine. c dgemv Level 2 BLAS routine for matrix vector multiplication. c daxpy Level 1 BLAS that computes a vector triad. c dcopy Level 1 BLAS that copies one vector to another . c dscal Level 1 BLAS that scales a vector. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.1' c c\SCCS Information: @(#) c FILE: napps.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 c c\Remarks c 1. In this version, each shift is applied to all the sublocks of c the Hessenberg matrix H and not just to the submatrix that it c comes from. Deflation as in LAPACK routine dlahqr (QR algorithm c for upper Hessenberg matrices ) is used. c The subdiagonals of H are enforced to be non-negative. c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdnapps & ( n, kev, np, shiftr, shifti, v, ldv, h, ldh, resid, q, ldq, & workl, workd ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c integer kev, ldh, ldq, ldv, n, np c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & h(ldh,kev+np), resid(n), shifti(np), shiftr(np), & v(ldv,kev+np), q(ldq,kev+np), workd(2*n), workl(kev+np) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %------------------------% c | Local Scalars & Arrays | c %------------------------% c integer i, iend, ir, istart, j, jj, kplusp, msglvl, nr logical cconj, first Double precision & c, f, g, h11, h12, h21, h22, h32, ovfl, r, s, sigmai, & sigmar, smlnum, ulp, unfl, u(3), t, tau, tst1 save first, ovfl, smlnum, ulp, unfl c c %----------------------% c | External Subroutines | c %----------------------% c external daxpy, dcopy, dscal, dlacpy, dlarfg, dlarf, & dlaset, dlabad, igraphsecond, dlartg c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlamch, dlanhs, dlapy2 external dlamch, dlanhs, dlapy2 c c %----------------------% c | Intrinsics Functions | c %----------------------% c intrinsic abs, max, min c c %----------------% c | Data statments | c %----------------% c data first / .true. / c c %-----------------------% c | Executable Statements | c %-----------------------% c if (first) then c c %-----------------------------------------------% c | Set machine-dependent constants for the | c | stopping criterion. If norm(H) <= sqrt(OVFL), | c | overflow should not occur. | c | REFERENCE: LAPACK subroutine dlahqr | c %-----------------------------------------------% c unfl = dlamch( 'safe minimum' ) ovfl = one / unfl call dlabad( unfl, ovfl ) ulp = dlamch( 'precision' ) smlnum = unfl*( n / ulp ) first = .false. end if c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = mnapps kplusp = kev + np c c %--------------------------------------------% c | Initialize Q to the identity to accumulate | c | the rotations and reflections | c %--------------------------------------------% c call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) c c %----------------------------------------------% c | Quick return if there are no shifts to apply | c %----------------------------------------------% c if (np .eq. 0) go to 9000 c c %----------------------------------------------% c | Chase the bulge with the application of each | c | implicit shift. Each shift is applied to the | c | whole matrix including each block. | c %----------------------------------------------% c cconj = .false. do 110 jj = 1, np sigmar = shiftr(jj) sigmai = shifti(jj) c if (msglvl .gt. 2 ) then call igraphivout (logfil, 1, jj, ndigit, & '_napps: shift number.') call igraphdvout (logfil, 1, sigmar, ndigit, & '_napps: The real part of the shift ') call igraphdvout (logfil, 1, sigmai, ndigit, & '_napps: The imaginary part of the shift ') end if c c %-------------------------------------------------% c | The following set of conditionals is necessary | c | in order that complex conjugate pairs of shifts | c | are applied together or not at all. | c %-------------------------------------------------% c if ( cconj ) then c c %-----------------------------------------% c | cconj = .true. means the previous shift | c | had non-zero imaginary part. | c %-----------------------------------------% c cconj = .false. go to 110 else if ( jj .lt. np .and. abs( sigmai ) .gt. zero ) then c c %------------------------------------% c | Start of a complex conjugate pair. | c %------------------------------------% c cconj = .true. else if ( jj .eq. np .and. abs( sigmai ) .gt. zero ) then c c %----------------------------------------------% c | The last shift has a nonzero imaginary part. | c | Don't apply it; thus the order of the | c | compressed H is order KEV+1 since only np-1 | c | were applied. | c %----------------------------------------------% c kev = kev + 1 go to 110 end if istart = 1 20 continue c c %--------------------------------------------------% c | if sigmai = 0 then | c | Apply the jj-th shift ... | c | else | c | Apply the jj-th and (jj+1)-th together ... | c | (Note that jj < np at this point in the code) | c | end | c | to the current block of H. The next do loop | c | determines the current block ; | c %--------------------------------------------------% c do 30 i = istart, kplusp-1 c c %----------------------------------------% c | Check for splitting and deflation. Use | c | a standard test as in the QR algorithm | c | REFERENCE: LAPACK subroutine dlahqr | c %----------------------------------------% c tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) if( tst1.eq.zero ) & tst1 = dlanhs( '1', kplusp-jj+1, h, ldh, workl ) if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) then if (msglvl .gt. 0) then call igraphivout (logfil, 1, i, ndigit, & '_napps: matrix splitting at row/column no.') call igraphivout (logfil, 1, jj, ndigit, & '_napps: matrix splitting with shift number.') call igraphdvout (logfil, 1, h(i+1,i), ndigit, & '_napps: off diagonal element.') end if iend = i h(i+1,i) = zero go to 40 end if 30 continue iend = kplusp 40 continue c if (msglvl .gt. 2) then call igraphivout (logfil, 1, istart, ndigit, & '_napps: Start of current block ') call igraphivout (logfil, 1, iend, ndigit, & '_napps: End of current block ') end if c c %------------------------------------------------% c | No reason to apply a shift to block of order 1 | c %------------------------------------------------% c if ( istart .eq. iend ) go to 100 c c %------------------------------------------------------% c | If istart + 1 = iend then no reason to apply a | c | complex conjugate pair of shifts on a 2 by 2 matrix. | c %------------------------------------------------------% c if ( istart + 1 .eq. iend .and. abs( sigmai ) .gt. zero ) & go to 100 c h11 = h(istart,istart) h21 = h(istart+1,istart) if ( abs( sigmai ) .le. zero ) then c c %---------------------------------------------% c | Real-valued shift ==> apply single shift QR | c %---------------------------------------------% c f = h11 - sigmar g = h21 c do 80 i = istart, iend-1 c c %-----------------------------------------------------% c | Contruct the plane rotation G to zero out the bulge | c %-----------------------------------------------------% c call dlartg (f, g, c, s, r) if (i .gt. istart) then c c %-------------------------------------------% c | The following ensures that h(1:iend-1,1), | c | the first iend-2 off diagonal of elements | c | H, remain non negative. | c %-------------------------------------------% c if (r .lt. zero) then r = -r c = -c s = -s end if h(i,i-1) = r h(i+1,i-1) = zero end if c c %---------------------------------------------% c | Apply rotation to the left of H; H <- G'*H | c %---------------------------------------------% c do 50 j = i, kplusp t = c*h(i,j) + s*h(i+1,j) h(i+1,j) = -s*h(i,j) + c*h(i+1,j) h(i,j) = t 50 continue c c %---------------------------------------------% c | Apply rotation to the right of H; H <- H*G | c %---------------------------------------------% c do 60 j = 1, min(i+2,iend) t = c*h(j,i) + s*h(j,i+1) h(j,i+1) = -s*h(j,i) + c*h(j,i+1) h(j,i) = t 60 continue c c %----------------------------------------------------% c | Accumulate the rotation in the matrix Q; Q <- Q*G | c %----------------------------------------------------% c do 70 j = 1, min( j+jj, kplusp ) t = c*q(j,i) + s*q(j,i+1) q(j,i+1) = - s*q(j,i) + c*q(j,i+1) q(j,i) = t 70 continue c c %---------------------------% c | Prepare for next rotation | c %---------------------------% c if (i .lt. iend-1) then f = h(i+1,i) g = h(i+2,i) end if 80 continue c c %-----------------------------------% c | Finished applying the real shift. | c %-----------------------------------% c else c c %----------------------------------------------------% c | Complex conjugate shifts ==> apply double shift QR | c %----------------------------------------------------% c h12 = h(istart,istart+1) h22 = h(istart+1,istart+1) h32 = h(istart+2,istart+1) c c %---------------------------------------------------------% c | Compute 1st column of (H - shift*I)*(H - conj(shift)*I) | c %---------------------------------------------------------% c s = 2.0*sigmar t = dlapy2 ( sigmar, sigmai ) u(1) = ( h11 * (h11 - s) + t * t ) / h21 + h12 u(2) = h11 + h22 - s u(3) = h32 c do 90 i = istart, iend-1 c nr = min ( 3, iend-i+1 ) c c %-----------------------------------------------------% c | Construct Householder reflector G to zero out u(1). | c | G is of the form I - tau*( 1 u )' * ( 1 u' ). | c %-----------------------------------------------------% c call dlarfg ( nr, u(1), u(2), 1, tau ) c if (i .gt. istart) then h(i,i-1) = u(1) h(i+1,i-1) = zero if (i .lt. iend-1) h(i+2,i-1) = zero end if u(1) = one c c %--------------------------------------% c | Apply the reflector to the left of H | c %--------------------------------------% c call dlarf ('Left', nr, kplusp-i+1, u, 1, tau, & h(i,i), ldh, workl) c c %---------------------------------------% c | Apply the reflector to the right of H | c %---------------------------------------% c ir = min ( i+3, iend ) call dlarf ('Right', ir, nr, u, 1, tau, & h(1,i), ldh, workl) c c %-----------------------------------------------------% c | Accumulate the reflector in the matrix Q; Q <- Q*G | c %-----------------------------------------------------% c call dlarf ('Right', kplusp, nr, u, 1, tau, & q(1,i), ldq, workl) c c %----------------------------% c | Prepare for next reflector | c %----------------------------% c if (i .lt. iend-1) then u(1) = h(i+1,i) u(2) = h(i+2,i) if (i .lt. iend-2) u(3) = h(i+3,i) end if c 90 continue c c %--------------------------------------------% c | Finished applying a complex pair of shifts | c | to the current block | c %--------------------------------------------% c end if c 100 continue c c %---------------------------------------------------------% c | Apply the same shift to the next block if there is any. | c %---------------------------------------------------------% c istart = iend + 1 if (iend .lt. kplusp) go to 20 c c %---------------------------------------------% c | Loop back to the top to get the next shift. | c %---------------------------------------------% c 110 continue c c %--------------------------------------------------% c | Perform a similarity transformation that makes | c | sure that H will have non negative sub diagonals | c %--------------------------------------------------% c do 120 j=1,kev if ( h(j+1,j) .lt. zero ) then call dscal( kplusp-j+1, -one, h(j+1,j), ldh ) call dscal( min(j+2, kplusp), -one, h(1,j+1), 1 ) call dscal( min(j+np+1,kplusp), -one, q(1,j+1), 1 ) end if 120 continue c do 130 i = 1, kev c c %--------------------------------------------% c | Final check for splitting and deflation. | c | Use a standard test as in the QR algorithm | c | REFERENCE: LAPACK subroutine dlahqr | c %--------------------------------------------% c tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) if( tst1.eq.zero ) & tst1 = dlanhs( '1', kev, h, ldh, workl ) if( h( i+1,i ) .le. max( ulp*tst1, smlnum ) ) & h(i+1,i) = zero 130 continue c c %-------------------------------------------------% c | Compute the (kev+1)-st column of (V*Q) and | c | temporarily store the result in WORKD(N+1:2*N). | c | This is needed in the residual update since we | c | cannot GUARANTEE that the corresponding entry | c | of H would be zero as in exact arithmetic. | c %-------------------------------------------------% c if (h(kev+1,kev) .gt. zero) & call dgemv ('N', n, kplusp, one, v, ldv, q(1,kev+1), 1, zero, & workd(n+1), 1) c c %----------------------------------------------------------% c | Compute column 1 to kev of (V*Q) in backward order | c | taking advantage of the upper Hessenberg structure of Q. | c %----------------------------------------------------------% c do 140 i = 1, kev call dgemv ('N', n, kplusp-i+1, one, v, ldv, & q(1,kev-i+1), 1, zero, workd, 1) call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) 140 continue c c %-------------------------------------------------% c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | c %-------------------------------------------------% c call dlacpy ('A', n, kev, v(1,kplusp-kev+1), ldv, v, ldv) c c %--------------------------------------------------------------% c | Copy the (kev+1)-st column of (V*Q) in the appropriate place | c %--------------------------------------------------------------% c if (h(kev+1,kev) .gt. zero) & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) c c %-------------------------------------% c | Update the residual vector: | c | r <- sigmak*r + betak*v(:,kev+1) | c | where | c | sigmak = (e_{kplusp}'*Q)*e_{kev} | c | betak = e_{kev+1}'*H*e_{kev} | c %-------------------------------------% c call dscal (n, q(kplusp,kev), resid, 1) if (h(kev+1,kev) .gt. zero) & call daxpy (n, h(kev+1,kev), v(1,kev+1), 1, resid, 1) c if (msglvl .gt. 1) then call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, & '_napps: sigmak = (e_{kev+p}^T*Q)*e_{kev}') call igraphdvout (logfil, 1, h(kev+1,kev), ndigit, & '_napps: betak = e_{kev+1}^T*H*e_{kev}') call igraphivout (logfil, 1, kev, ndigit, & '_napps: Order of the final Hessenberg matrix ') if (msglvl .gt. 2) then call igraphdmout (logfil, kev, kev, h, ldh, ndigit, & '_napps: updated Hessenberg matrix H for next iteration') end if c end if c 9000 continue call igraphsecond (t1) tnapps = tnapps + (t1 - t0) c return c c %---------------% c | End of igraphdnapps | c %---------------% c end igraph/src/drl_layout.h0000644000176000001440000000561312325527073014705 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file contains compile time parameters which affect the entire // DrL program. #define DRL_VERSION "3.2 5/5/2006" // compile time parameters for MPI message passing #define MAX_PROCS 256 // maximum number of processors #define MAX_FILE_NAME 250 // max length of filename #define MAX_INT_LENGTH 4 // max length of integer suffix of intermediate .coord file // Compile time adjustable parameters for the Density grid #define GRID_SIZE 1000 // size of Density grid #define VIEW_SIZE 4000.0 // actual physical size of layout plane // these values use more memory but have // little effect on performance or layout #define RADIUS 10 // radius for density fall-off: // larger values tends to slow down // the program and clump the data #define HALF_VIEW 2000 // 1/2 of VIEW_SIZE #define VIEW_TO_GRID .25 // ratio of GRID_SIZE to VIEW_SIZE /* // original values for VxOrd #define GRID_SIZE 400 // size of VxOrd Density grid #define VIEW_SIZE 1600.0 // actual physical size of VxOrd plane #define RADIUS 10 // radius for density fall-off #define HALF_VIEW 800 // 1/2 of VIEW_SIZE #define VIEW_TO_GRID .25 // ratio of GRID_SIZE to VIEW_SIZE */ igraph/src/foreign-ncol-header.h0000644000176000001440000000213712325527073016335 0ustar ripleyusers/* IGraph library. Copyright (C) 2011-2012 Gabor Csardi 334 Harvard street, Cambridge MA, 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_vector.h" #include "igraph_types_internal.h" typedef struct { void *scanner; int eof; char errmsg[300]; int has_weights; igraph_vector_t *vector; igraph_vector_t *weights; igraph_trie_t *trie; } igraph_i_ncol_parsedata_t; igraph/src/lapack.c0000644000176000001440000007713112325527073013761 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_lapack.h" #include "igraph_lapack_internal.h" /** * \function igraph_lapack_dgetrf * LU factorization of a general M-by-N matrix * * The factorization has the form * A = P * L * U * where P is a permutation matrix, L is lower triangular with unit * diagonal elements (lower trapezoidal if m > n), and U is upper * triangular (upper trapezoidal if m < n). * \param a The input/output matrix. On entry, the M-by-N matrix to be * factored. On exit, the factors L and U from the factorization * A = P * L * U; the unit diagonal elements of L are not * stored. * \param ipiv An integer vector, the pivot indices are stored here, * unless it is a null pointer. Row i of the matrix was * interchanged with row ipiv[i]. * \param info LAPACK error code. Zero on successful exit. If positive * and i, then U(i,i) is exactly zero. The factorization has been * completed, but the factor U is exactly singular, and division * by zero will occur if it is used to solve a system of * equations. If LAPACK returns an error, i.e. a negative info * value, then an igraph error is generated as well. * \return Error code. * * Time complexity: TODO. */ int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, int *info) { int m=(int) igraph_matrix_nrow(a); int n=(int) igraph_matrix_ncol(a); int lda=m > 0 ? m : 1; igraph_vector_int_t *myipiv=ipiv, vipiv; if (!ipiv) { IGRAPH_CHECK(igraph_vector_int_init(&vipiv, mdata), &lda, VECTOR(*myipiv), info); if (*info > 0) { IGRAPH_WARNING("LU: factor is exactly singular"); } else if (*info < 0) { switch(*info) { case -1: IGRAPH_ERROR("Invalid number of rows", IGRAPH_ELAPACK); break; case -2: IGRAPH_ERROR("Invalid number of columns", IGRAPH_ELAPACK); break; case -3: IGRAPH_ERROR("Invalid input matrix", IGRAPH_ELAPACK); break; case -4: IGRAPH_ERROR("Invalid LDA parameter", IGRAPH_ELAPACK); break; case -5: IGRAPH_ERROR("Invalid pivot vector", IGRAPH_ELAPACK); break; case -6: IGRAPH_ERROR("Invalid info argument", IGRAPH_ELAPACK); break; default: IGRAPH_ERROR("Unknown LAPACK error", IGRAPH_ELAPACK); break; } } if (!ipiv) { igraph_vector_int_destroy(&vipiv); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_lapack_dgetrs * Solve general system of linear equations using LU factorization * * This function calls LAPACK to solve a system of linear equations * A * X = B or A' * X = B * with a general N-by-N matrix A using the LU factorization * computed by \ref igraph_lapack_dgetrf. * \param transpose Logical scalar, whether to transpose the input * matrix. * \param a A matrix containing the L and U factors from the * factorization A = P*L*U. * \param ipiv An integer vector, the pivot indices from \ref * igraph_lapack_dgetrf must be given here. * \param b The right hand side matrix must be given here. * \return Error code. * * Time complexity: TODO. */ int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, igraph_vector_int_t *ipiv, igraph_matrix_t *b) { char trans = transpose ? 'T' : 'N'; int n=(int) igraph_matrix_nrow(a); int nrhs=(int) igraph_matrix_ncol(b); int lda= n > 0 ? n : 1; int ldb= n > 0 ? n : 1; int info; if (n != igraph_matrix_ncol(a)) { IGRAPH_ERROR("Cannot LU solve matrix", IGRAPH_NONSQUARE); } if (n != igraph_matrix_nrow(b)) { IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size", IGRAPH_EINVAL); } igraphdgetrs_(&trans, &n, &nrhs, VECTOR(a->data), &lda, VECTOR(*ipiv), VECTOR(b->data), &ldb, &info); if (info < 0) { switch(info) { case -1: IGRAPH_ERROR("Invalid transpose argument", IGRAPH_ELAPACK); break; case -2: IGRAPH_ERROR("Invalid number of rows/columns", IGRAPH_ELAPACK); break; case -3: IGRAPH_ERROR("Invalid number of RHS vectors", IGRAPH_ELAPACK); break; case -4: IGRAPH_ERROR("Invalid LU matrix", IGRAPH_ELAPACK); break; case -5: IGRAPH_ERROR("Invalid LDA parameter", IGRAPH_ELAPACK); break; case -6: IGRAPH_ERROR("Invalid pivot vector", IGRAPH_ELAPACK); break; case -7: IGRAPH_ERROR("Invalid RHS matrix", IGRAPH_ELAPACK); break; case -8: IGRAPH_ERROR("Invalid LDB parameter", IGRAPH_ELAPACK); break; case -9: IGRAPH_ERROR("Invalid info argument", IGRAPH_ELAPACK); break; default: IGRAPH_ERROR("Unknown LAPACK error", IGRAPH_ELAPACK); break; } } return 0; } /** * \function igraph_lapack_dgesv * Solve system of linear equations with LU factorization * * This function computes the solution to a real system of linear * equations A * X = B, where A is an N-by-N matrix and X and B are * N-by-NRHS matrices. * * The LU decomposition with partial pivoting and row * interchanges is used to factor A as * A = P * L * U, * where P is a permutation matrix, L is unit lower triangular, and U is * upper triangular. The factored form of A is then used to solve the * system of equations A * X = B. * \param a Matrix. On entry the N-by-N coefficient matrix, on exit, * the factors L and U from the factorization A=P*L*U; the unit * diagonal elements of L are not stored. * \param ipiv An integer vector or a null pointer. If not a null * pointer, then the pivot indices that define the permutation * matrix P, are stored here. Row i of the matrix was * interchanged with row IPIV(i). * \param b Matrix, on entry the right hand side matrix should be * stored here. On exit, if there was no error, and the info * argument is zero, then it contains the solution matrix X. * \param info The LAPACK info code. If it is positive, then * U(info,info) is exactly zero. In this case the factorization * has been completed, but the factor U is exactly * singular, so the solution could not be computed. * \return Error code. * * Time complexity: TODO. * * \example examples/simple/igraph_lapack_dgesv.c */ int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, igraph_matrix_t *b, int *info) { int n=(int) igraph_matrix_nrow(a); int nrhs=(int) igraph_matrix_ncol(b); int lda= n > 0 ? n : 1; int ldb= n > 0 ? n : 1; igraph_vector_int_t *myipiv=ipiv, vipiv; if (n != igraph_matrix_ncol(a)) { IGRAPH_ERROR("Cannot LU solve matrix", IGRAPH_NONSQUARE); } if (n != igraph_matrix_nrow(b)) { IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size", IGRAPH_EINVAL); } if (!ipiv) { IGRAPH_CHECK(igraph_vector_int_init(&vipiv, n)); IGRAPH_FINALLY(igraph_vector_int_destroy, &vipiv); myipiv=&vipiv; } igraphdgesv_(&n, &nrhs, VECTOR(a->data), &lda, VECTOR(*myipiv), VECTOR(b->data), &ldb, info); if (*info > 0) { IGRAPH_WARNING("LU: factor is exactly singular"); } else if (*info < 0) { switch(*info) { case -1: IGRAPH_ERROR("Invalid number of rows/column", IGRAPH_ELAPACK); break; case -2: IGRAPH_ERROR("Invalid number of RHS vectors", IGRAPH_ELAPACK); break; case -3: IGRAPH_ERROR("Invalid input matrix", IGRAPH_ELAPACK); break; case -4: IGRAPH_ERROR("Invalid LDA parameter", IGRAPH_ELAPACK); break; case -5: IGRAPH_ERROR("Invalid pivot vector", IGRAPH_ELAPACK); break; case -6: IGRAPH_ERROR("Invalid RHS matrix", IGRAPH_ELAPACK); break; case -7: IGRAPH_ERROR("Invalid LDB parameter", IGRAPH_ELAPACK); break; case -8: IGRAPH_ERROR("Invalid info argument", IGRAPH_ELAPACK); break; default: IGRAPH_ERROR("Unknown LAPACK error", IGRAPH_ELAPACK); break; } } if (!ipiv) { igraph_vector_int_destroy(&vipiv); IGRAPH_FINALLY_CLEAN(1); } return 0; } /** * \function igraph_lapack_dsyevr * Selected eigenvalues and optionally eigenvectors of a symmetric matrix * * Calls the DSYEVR LAPACK function to compute selected eigenvalues * and, optionally, eigenvectors of a real symmetric matrix A. * Eigenvalues and eigenvectors can be selected by specifying either * a range of values or a range of indices for the desired eigenvalues. * * See more in the LAPACK documentation. * \param A Matrix, on entry it contains the symmetric input * matrix. Only the leading N-by-N upper triangular part is * used for the computation. * \param which Constant that gives which eigenvalues (and possibly * the corresponding eigenvectors) to calculate. Possible * values are \c IGRAPH_LAPACK_DSYEV_ALL, all eigenvalues; * \c IGRAPH_LAPACK_DSYEV_INTERVAL, all eigenvalues in the * half-open interval (vl,vu]; * \c IGRAPH_LAPACK_DSYEV_SELECT, the il-th through iu-th * eigenvalues. * \param vl If \p which is \c IGRAPH_LAPACK_DSYEV_INTERVAL, then * this is the lower bound of the interval to be searched for * eigenvalues. See also the \p vestimate argument. * \param vu If \p which is \c IGRAPH_LAPACK_DSYEV_INTERVAL, then * this is the upper bound of the interval to be searched for * eigenvalues. See also the \p vestimate argument. * \param vestimate An upper bound for the number of eigenvalues in * the (vl,vu] interval, if \p which is \c * IGRAPH_LAPACK_DSYEV_INTERVAL. Memory is allocated only for * the given number of eigenvalues (and eigenvectors), so this * upper bound must be correct. * \param il The index of the smallest eigenvalue to return, if \p * which is \c IGRAPH_LAPACK_DSYEV_SELECT. * \param iu The index of the largets eigenvalue to return, if \p * which is \c IGRAPH_LAPACK_DSYEV_SELECT. * \param abstol The absolute error tolerance for the eigevalues. An * approximate eigenvalue is accepted as converged when it is * determined to lie in an interval [a,b] of width less than or * equal to abstol + EPS * max(|a|,|b|), where EPS is the * machine precision. * \param values An initialized vector, the eigenvalues are stored * here, unless it is a null pointer. It will be resized as * needed. * \param vectors An initialized matrix, the eigenvectors are stored * in its columns, unless it is a null pointer. It will be * resized as needed. * \param support An integer vector. If not a null pointer, then it * will be resized to (2*max(1,M)) (M is a the total number of * eigenvalues found). Then the support of the eigenvectors in * \p vectors is stored here, i.e., the indices * indicating the nonzero elements in \p vectors. * The i-th eigenvector is nonzero only in elements * support(2*i-1) through support(2*i). * \return Error code. * * Time complexity: TODO. * * \example examples/simple/igraph_lapack_dsyevr.c */ int igraph_lapack_dsyevr(const igraph_matrix_t *A, igraph_lapack_dsyev_which_t which, igraph_real_t vl, igraph_real_t vu, int vestimate, int il, int iu, igraph_real_t abstol, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_int_t *support) { igraph_matrix_t Acopy; char jobz = vectors ? 'V' : 'N', range, uplo='U'; int n=(int) igraph_matrix_nrow(A), lda=n, ldz=n; int m, info; igraph_vector_t *myvalues=values, vvalues; igraph_vector_int_t *mysupport=support, vsupport; igraph_vector_t work; igraph_vector_int_t iwork; int lwork=-1, liwork=-1; if (n != igraph_matrix_ncol(A)) { IGRAPH_ERROR("Cannot find eigenvalues/vectors", IGRAPH_NONSQUARE); } if (which==IGRAPH_LAPACK_DSYEV_INTERVAL && (vestimate < 1 || vestimate > n)) { IGRAPH_ERROR("Estimated (upper bound) number of eigenvalues must be " "between 1 and n", IGRAPH_EINVAL); } if (which==IGRAPH_LAPACK_DSYEV_SELECT && iu-il < 0) { IGRAPH_ERROR("Invalid 'il' and/or 'iu' values", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&work, 1); IGRAPH_CHECK(igraph_vector_int_init(&iwork, 1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &iwork); if (!values) { IGRAPH_VECTOR_INIT_FINALLY(&vvalues, 0); myvalues=&vvalues; } if (!support) { IGRAPH_CHECK(igraph_vector_int_init(&vsupport, 0)); IGRAPH_FINALLY(igraph_vector_int_destroy, &vsupport); mysupport=&vsupport; } switch (which) { case IGRAPH_LAPACK_DSYEV_ALL: range = 'A'; IGRAPH_CHECK(igraph_vector_resize(myvalues, n)); IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2*n)); if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, n)); } break; case IGRAPH_LAPACK_DSYEV_INTERVAL: range = 'V'; IGRAPH_CHECK(igraph_vector_resize(myvalues, vestimate)); IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2*vestimate)); if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors,n, vestimate)); } break; case IGRAPH_LAPACK_DSYEV_SELECT: range = 'I'; IGRAPH_CHECK(igraph_vector_resize(myvalues, iu-il+1)); IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2*(iu-il+1))); if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, iu-il+1)); } break; } igraphdsyevr_(&jobz, &range, &uplo, &n, &MATRIX(Acopy,0,0), &lda, &vl, &vu, &il, &iu, &abstol, &m, VECTOR(*myvalues), vectors ? &MATRIX(*vectors,0,0) : 0, &ldz, VECTOR(*mysupport), VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); lwork=(int) VECTOR(work)[0]; liwork=VECTOR(iwork)[0]; IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); IGRAPH_CHECK(igraph_vector_int_resize(&iwork, liwork)); igraphdsyevr_(&jobz, &range, &uplo, &n, &MATRIX(Acopy,0,0), &lda, &vl, &vu, &il, &iu, &abstol, &m, VECTOR(*myvalues), vectors ? &MATRIX(*vectors,0,0) : 0, &ldz, VECTOR(*mysupport), VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); if (values) { IGRAPH_CHECK(igraph_vector_resize(values, m)); } if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, m)); } if (support) { IGRAPH_CHECK(igraph_vector_int_resize(support, m)); } if (!support) { igraph_vector_int_destroy(&vsupport); IGRAPH_FINALLY_CLEAN(1); } if (!values) { igraph_vector_destroy(&vvalues); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_int_destroy(&iwork); igraph_vector_destroy(&work); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \function igraph_lapack_dgeev * Eigenvalues and optionally eigenvectors of a non-symmetric matrix * * This function calls LAPACK to compute, for an N-by-N real * nonsymmetric matrix A, the eigenvalues and, optionally, the left * and/or right eigenvectors. * * * The right eigenvector v(j) of A satisfies * A * v(j) = lambda(j) * v(j) * where lambda(j) is its eigenvalue. * The left eigenvector u(j) of A satisfies * u(j)**H * A = lambda(j) * u(j)**H * where u(j)**H denotes the conjugate transpose of u(j). * * * The computed eigenvectors are normalized to have Euclidean norm * equal to 1 and largest component real. * * \param A matrix. On entry it contains the N-by-N input matrix. * \param valuesreal Pointer to an initialized vector, or a null * pointer. If not a null pointer, then the real parts of the * eigenvalues are stored here. The vector will be resized as * needed. * \param valuesimag Pointer to an initialized vector, or a null * pointer. If not a null pointer, then the imaginary parts of * the eigenvalues are stored here. The vector will be resized * as needed. * \param vectorsleft Pointer to an initialized matrix, or a null * pointer. If not a null pointer, then the left eigenvectors * are stored in the columns of the matrix. The matrix will be * resized as needed. * \param vectorsright Pointer to an initialized matrix, or a null * pointer. If not a null pointer, then the right eigenvectors * are stored in the columns of the matrix. The matrix will be * resized as needed. * \param info This argument is used for two purposes. As an input * argument it gives whether an igraph error should be * generated if the QR algorithm fails to compute all * eigenvalues. If \p info is non-zero, then an error is * generated, otherwise only a warning is given. * On exit it contains the LAPACK error code. * Zero means successful exit. * A negative values means that some of the arguments had an * illegal value, this always triggers an igraph error. An i * positive value means that the QR algorithm failed to * compute all the eigenvalues, and no eigenvectors have been * computed; element i+1:N of \p valuesreal and \p valuesimag * contain eigenvalues which have converged. This case only * generates an igraph error, if \p info was non-zero on entry. * \return Error code. * * Time complexity: TODO. * * \example examples/simple/igraph_lapack_dgeev.c */ int igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, igraph_matrix_t *vectorsright, int *info) { char jobvl= vectorsleft ? 'V' : 'N'; char jobvr= vectorsright ? 'V' : 'N'; int n=(int) igraph_matrix_nrow(A); int lda=n, ldvl=n, ldvr=n, lwork=-1; igraph_vector_t work; igraph_vector_t *myreal=valuesreal, *myimag=valuesimag, vreal, vimag; igraph_matrix_t Acopy; int error=*info; if (igraph_matrix_ncol(A) != n) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_NONSQUARE); } IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&work, 1); if (!valuesreal) { IGRAPH_VECTOR_INIT_FINALLY(&vreal, n); myreal=&vreal; } else { IGRAPH_CHECK(igraph_vector_resize(myreal, n)); } if (!valuesimag) { IGRAPH_VECTOR_INIT_FINALLY(&vimag, n); myimag=&vimag; } else { IGRAPH_CHECK(igraph_vector_resize(myimag, n)); } if (vectorsleft) { IGRAPH_CHECK(igraph_matrix_resize(vectorsleft, n, n)); } if (vectorsright) { IGRAPH_CHECK(igraph_matrix_resize(vectorsright, n, n)); } igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy,0,0), &lda, VECTOR(*myreal), VECTOR(*myimag), vectorsleft ? &MATRIX(*vectorsleft ,0,0) : 0, &ldvl, vectorsright ? &MATRIX(*vectorsright,0,0) : 0, &ldvr, VECTOR(work), &lwork, info); lwork=(int) VECTOR(work)[0]; IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy,0,0), &lda, VECTOR(*myreal), VECTOR(*myimag), vectorsleft ? &MATRIX(*vectorsleft ,0,0) : 0, &ldvl, vectorsright ? &MATRIX(*vectorsright,0,0) : 0, &ldvr, VECTOR(work), &lwork, info); if (*info < 0) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK); } else if (*info > 0) { if (error) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK); } else { IGRAPH_WARNING("Cannot calculate eigenvalues (dgeev)"); } } if (!valuesimag) { igraph_vector_destroy(&vimag); IGRAPH_FINALLY_CLEAN(1); } if (!valuesreal) { igraph_vector_destroy(&vreal); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&work); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_lapack_dgeevx * Eigenvalues/vectors of nonsymmetric matrices, expert mode * * This function calculates the eigenvalues and optionally the left * and/or right eigenvectors of a nonsymmetric N-by-N real matrix. * * * Optionally also, it computes a balancing transformation to improve * the conditioning of the eigenvalues and eigenvectors (\p ilo, \pihi, * \p scale, and \p abnrm), reciprocal condition numbers for the * eigenvalues (\p rconde), and reciprocal condition numbers for the * right eigenvectors (\p rcondv). * * * The right eigenvector v(j) of A satisfies * A * v(j) = lambda(j) * v(j) * where lambda(j) is its eigenvalue. * The left eigenvector u(j) of A satisfies * u(j)**H * A = lambda(j) * u(j)**H * where u(j)**H denotes the conjugate transpose of u(j). * * * The computed eigenvectors are normalized to have Euclidean norm * equal to 1 and largest component real. * * * Balancing a matrix means permuting the rows and columns to make it * more nearly upper triangular, and applying a diagonal similarity * transformation D * A * D**(-1), where D is a diagonal matrix, to * make its rows and columns closer in norm and the condition numbers * of its eigenvalues and eigenvectors smaller. The computed * reciprocal condition numbers correspond to the balanced matrix. * Permuting rows and columns will not change the condition numbers * (in exact arithmetic) but diagonal scaling will. For further * explanation of balancing, see section 4.10.2 of the LAPACK * Users' Guide. * * \param balance Scalar that indicated, whether the input matrix * should be balanced. Possible values: * \clist * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_NONE * no not diagonally scale or permute. * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_PERM * perform permutations to make the matrix more nearly upper * triangular. Do not diagonally scale. * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE * diagonally scale the matrix, i.e. replace A by * D*A*D**(-1), where D is a diagonal matrix, chosen to make * the rows and columns of A more equal in norm. Do not * permute. * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH * both diagonally scale and permute A. * \endclist * \param A The input matrix, must be square. * \param valuesreal An initialized vector, or a NULL pointer. If not * a NULL pointer, then the real parts of the eigenvalues are stored * here. The vector will be resized, as needed. * \param valuesimag An initialized vector, or a NULL pointer. If not * a NULL pointer, then the imaginary parts of the eigenvalues are stored * here. The vector will be resized, as needed. * \param vectorsleft An initialized matrix or a NULL pointer. If not * a null pointer, then the left eigenvectors are stored here. The * order corresponds to the eigenvalues and the eigenvectors are * stored in a compressed form. If the j-th eigenvalue is real then * column j contains the corresponding eigenvector. If the j-th and * (j+1)-th eigenvalues form a complex conjugate pair, then the j-th * and (j+1)-th columns contain their corresponding eigenvectors. * \param vectorsright An initialized matrix or a NULL pointer. If not * a null pointer, then the right eigenvectors are stored here. The * format is the same, as for the \p vectorsleft argument. * \param ilo * \param ihi \p ilo and \p ihi are integer values determined when A was * balanced. The balanced A(i,j) = 0 if I>J and * J=1,...,ilo-1 or I=ihi+1,...,N. * \param scale Pointer to an initialized vector or a NULL pointer. If * not a NULL pointer, then details of the permutations and scaling * factors applied when balancing \param A, are stored here. * If P(j) is the index of the row and column * interchanged with row and column j, and D(j) is the scaling * factor applied to row and column j, then * \clist * \cli scale(J) = P(J), for J = 1,...,ilo-1 * \cli scale(J) = D(J), for J = ilo,...,ihi * \cli scale(J) = P(J) for J = ihi+1,...,N. * \endclist * The order in which the interchanges are made is N to \p ihi+1, * then 1 to \p ilo-1. * \param abnrm Pointer to a real variable, the one-norm of the * balanced matrix is stored here. (The one-norm is the maximum of * the sum of absolute values of elements in any column.) * \param rconde An initialized vector or a NULL pointer. If not a * null pointer, then the reciprocal condition numbers of the * eigenvalues are stored here. * \param rcondv An initialized vector or a NULL pointer. If not a * null pointer, then the reciprocal condition numbers of the right * eigenvectors are stored here. * \param info This argument is used for two purposes. As an input * argument it gives whether an igraph error should be * generated if the QR algorithm fails to compute all * eigenvalues. If \p info is non-zero, then an error is * generated, otherwise only a warning is given. * On exit it contains the LAPACK error code. * Zero means successful exit. * A negative values means that some of the arguments had an * illegal value, this always triggers an igraph error. An i * positive value means that the QR algorithm failed to * compute all the eigenvalues, and no eigenvectors have been * computed; element i+1:N of \p valuesreal and \p valuesimag * contain eigenvalues which have converged. This case only * generated an igraph error, if \p info was non-zero on entry. * \return Error code. * * Time complexity: TODO * * \example examples/simple/igraph_lapack_dgeevx.c */ int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, igraph_matrix_t *vectorsright, int *ilo, int *ihi, igraph_vector_t *scale, igraph_real_t *abnrm, igraph_vector_t *rconde, igraph_vector_t *rcondv, int *info) { char balanc; char jobvl= vectorsleft ? 'V' : 'N'; char jobvr= vectorsright ? 'V' : 'N'; char sense; int n=(int) igraph_matrix_nrow(A); int lda=n, ldvl=n, ldvr=n, lwork=-1; igraph_vector_t work; igraph_vector_int_t iwork; igraph_matrix_t Acopy; int error=*info; igraph_vector_t *myreal=valuesreal, *myimag=valuesimag, vreal, vimag; igraph_vector_t *myscale=scale, vscale; if (igraph_matrix_ncol(A) != n) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeevx)", IGRAPH_NONSQUARE); } switch (balance) { case IGRAPH_LAPACK_DGEEVX_BALANCE_NONE: balanc='N'; break; case IGRAPH_LAPACK_DGEEVX_BALANCE_PERM: balanc='P'; break; case IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE: balanc='S'; break; case IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH: balanc='B'; break; default: IGRAPH_ERROR("Invalid 'balance' argument", IGRAPH_EINVAL); break; } if (!rconde && !rcondv) { sense='N'; } else if (rconde && !rcondv) { sense='E'; } else if (!rconde && rcondv) { sense='V'; } else { sense='B'; } IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&work, 1); IGRAPH_CHECK(igraph_vector_int_init(&iwork, n)); IGRAPH_FINALLY(igraph_vector_int_destroy, &iwork); if (!valuesreal) { IGRAPH_VECTOR_INIT_FINALLY(&vreal, n); myreal=&vreal; } else { IGRAPH_CHECK(igraph_vector_resize(myreal, n)); } if (!valuesimag) { IGRAPH_VECTOR_INIT_FINALLY(&vimag, n); myimag=&vimag; } else { IGRAPH_CHECK(igraph_vector_resize(myimag, n)); } if (!scale) { IGRAPH_VECTOR_INIT_FINALLY(&vscale, n); myscale=&vscale; } else { IGRAPH_CHECK(igraph_vector_resize(scale, n)); } if (vectorsleft) { IGRAPH_CHECK(igraph_matrix_resize(vectorsleft, n, n)); } if (vectorsright) { IGRAPH_CHECK(igraph_matrix_resize(vectorsright, n, n)); } igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy,0,0), &lda, VECTOR(*myreal), VECTOR(*myimag), vectorsleft ? &MATRIX(*vectorsleft ,0,0) : 0, &ldvl, vectorsright ? &MATRIX(*vectorsright,0,0) : 0, &ldvr, ilo, ihi, VECTOR(*myscale), abnrm, rconde ? VECTOR(*rconde) : 0, rcondv ? VECTOR(*rcondv) : 0, VECTOR(work), &lwork, VECTOR(iwork), info); lwork=(int) VECTOR(work)[0]; IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy,0,0), &lda, VECTOR(*myreal), VECTOR(*myimag), vectorsleft ? &MATRIX(*vectorsleft ,0,0) : 0, &ldvl, vectorsright ? &MATRIX(*vectorsright,0,0) : 0, &ldvr, ilo, ihi, VECTOR(*myscale), abnrm, rconde ? VECTOR(*rconde) : 0, rcondv ? VECTOR(*rcondv) : 0, VECTOR(work), &lwork, VECTOR(iwork), info); if (*info < 0) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK); } else if (*info > 0) { if (error) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK); } else { IGRAPH_WARNING("Cannot calculate eigenvalues (dgeev)"); } } if (!scale) { igraph_vector_destroy(&vscale); IGRAPH_FINALLY_CLEAN(1); } if (!valuesimag) { igraph_vector_destroy(&vimag); IGRAPH_FINALLY_CLEAN(1); } if (!valuesreal) { igraph_vector_destroy(&vreal); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_int_destroy(&iwork); igraph_vector_destroy(&work); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(3); return 0; } int igraph_lapack_dgehrd(const igraph_matrix_t *A, int ilo, int ihi, igraph_matrix_t *result) { int n=(int) igraph_matrix_nrow(A); int lda=n; int lwork=-1; igraph_vector_t work; igraph_real_t optwork; igraph_vector_t tau; igraph_matrix_t Acopy; int info=0; int i; if (igraph_matrix_ncol(A) != n) { IGRAPH_ERROR("Hessenberg reduction failed", IGRAPH_NONSQUARE); } if (ilo < 1 || ihi > n || ilo > ihi) { IGRAPH_ERROR("Invalid `ilo' and/or `ihi'", IGRAPH_EINVAL); } if (n <= 1) { IGRAPH_CHECK(igraph_matrix_update(result, A)); return 0; } IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&tau, n-1); igraphdgehrd_(&n, &ilo, &ihi, &MATRIX(Acopy, 0, 0), &lda, VECTOR(tau), &optwork, &lwork, &info); if (info != 0) { IGRAPH_ERROR("Internal Hessenberg transformation error", IGRAPH_EINTERNAL); } lwork=(int) optwork; IGRAPH_VECTOR_INIT_FINALLY(&work, lwork); igraphdgehrd_(&n, &ilo, &ihi, &MATRIX(Acopy, 0, 0), &lda, VECTOR(tau), VECTOR(work), &lwork, &info); if (info != 0) { IGRAPH_ERROR("Internal Hessenberg transformation error", IGRAPH_EINTERNAL); } igraph_vector_destroy(&work); igraph_vector_destroy(&tau); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_matrix_update(result, &Acopy)); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(1); for (i=0; i 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS const igraph_real_t igraph_i_atlas_edges[]={ 0,0, 1,0, 2,0, 2,1,0,1, 3,0, 3,1,1,2, 3,2,0,1,0,2, 3,3,0,1,0,2,1,2, 4,0, 4,1,3,2, 4,2,3,2,3,1, 4,2,0,1,3,2, 4,3,3,2,1,2,3,1, 4,3,3,0,3,1,3,2, 4,3,0,1,1,2,0,3, 4,4,3,2,1,2,3,1,3,0, 4,4,0,1,1,2,2,3,0,3, 4,5,0,1,0,2,0,3,1,2,2,3, 4,6,0,1,1,2,0,2,3,0,3,1,3,2, 5,0, 5,1,4,3, 5,2,1,2,0,1, 5,2,0,2,4,3, 5,3,1,2,0,1,2,0, 5,3,4,3,3,2,3,1, 5,3,3,2,4,3,0,4, 5,3,1,2,0,1,4,3, 5,4,4,3,1,2,3,1,3,2, 5,4,0,3,1,0,2,1,3,2, 5,4,4,3,4,0,4,1,4,2, 5,4,4,0,3,1,4,3,3,2, 5,4,2,3,1,2,0,1,4,0, 5,4,1,2,0,1,2,0,4,3, 5,5,0,3,2,0,3,2,1,0,2,1, 5,5,4,2,4,3,2,3,4,1,4,0, 5,5,0,1,1,2,2,3,0,4,0,2, 5,5,4,0,1,2,4,3,3,2,3,1, 5,5,1,0,4,1,2,4,3,2,1,3, 5,5,0,1,1,2,2,3,3,4,0,4, 5,6,1,0,4,1,4,0,0,3,1,3,3,4, 5,6,1,0,4,1,2,4,3,2,1,3,2,1, 5,6,1,0,4,1,2,4,3,2,1,3,3,4, 5,6,0,1,4,3,2,3,4,2,4,0,4,1, 5,6,0,4,3,0,4,3,2,3,1,2,0,1, 5,6,2,1,0,2,3,0,1,3,4,1,0,4, 5,7,4,0,1,2,4,3,3,2,3,1,4,1,2,4, 5,7,4,1,2,4,3,2,1,3,3,4,0,3,4,0, 5,7,0,1,1,2,2,3,3,4,0,4,1,3,4,1, 5,7,2,1,0,2,3,0,1,3,4,1,0,4,2,4, 5,8,1,0,4,1,2,4,3,2,1,3,4,0,3,4,0,3, 5,8,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3, 5,9,0,1,3,4,0,3,0,4,1,2,1,3,1,4,2,3,2,4, 5,10,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4, 6,0, 6,1,5,4, 6,2,0,3,5,4, 6,2,1,3,1,2, 6,3,1,3,2,1,3,2, 6,3,0,3,5,0,4,0, 6,3,4,3,5,4,0,5, 6,3,4,3,5,1,5,2, 6,3,1,2,3,0,5,4, 6,4,0,3,4,0,5,4,0,5, 6,4,3,0,5,3,4,5,0,4, 6,4,5,1,5,3,5,2,0,5, 6,4,4,3,3,1,4,0,3,2, 6,4,0,2,1,3,2,1,5,3, 6,4,1,3,2,1,3,2,0,5, 6,4,1,2,0,3,5,0,4,0, 6,4,4,5,1,2,0,5,3,4, 6,4,0,2,4,0,3,1,5,3, 6,5,3,0,5,3,4,5,0,4,5,0, 6,5,5,3,3,1,3,2,4,3,4,5, 6,5,5,3,5,4,2,3,3,4,0,4, 6,5,4,3,1,2,4,0,3,2,3,1, 6,5,1,4,3,4,4,0,2,1,3,2, 6,5,0,1,1,2,2,3,3,4,0,4, 6,5,5,3,5,4,5,0,5,1,5,2, 6,5,1,4,5,1,1,0,2,1,2,3, 6,5,0,1,3,4,0,2,3,0,5,3, 6,5,1,0,2,1,2,4,1,3,5,3, 6,5,4,3,0,5,4,0,3,2,3,1, 6,5,1,2,0,1,4,5,1,3,2,3, 6,5,0,1,0,5,2,3,3,4,4,5, 6,5,4,3,5,1,5,2,0,3,4,0, 6,5,1,2,3,0,5,3,4,5,0,4, 6,6,0,3,5,0,4,5,3,4,5,3,4,0, 6,6,1,4,2,4,4,0,2,3,3,1,3,4, 6,6,1,4,2,4,4,0,2,1,3,1,2,3, 6,6,2,0,5,4,4,3,5,3,4,0,2,4, 6,6,3,2,4,3,0,4,1,0,2,1,0,3, 6,6,4,1,3,1,4,2,3,2,2,0,1,0, 6,6,5,2,5,3,5,4,3,4,5,1,5,0, 6,6,4,3,4,2,4,0,1,4,3,0,5,3, 6,6,4,3,3,5,5,4,5,1,3,2,4,0, 6,6,4,2,1,2,4,3,4,1,4,0,0,5, 6,6,1,2,3,1,0,3,2,0,4,0,5,0, 6,6,2,0,4,2,1,4,2,1,3,1,5,3, 6,6,1,2,3,1,0,3,2,0,4,0,5,3, 6,6,5,3,2,5,2,0,4,2,4,3,3,1, 6,6,0,2,3,4,1,0,5,3,4,5,3,0, 6,6,1,2,3,0,5,3,4,5,0,4,5,0, 6,6,4,3,1,2,4,0,3,2,3,1,5,0, 6,6,1,4,2,4,4,0,0,5,3,1,2,3, 6,6,0,1,1,2,2,3,3,4,0,4,1,5, 6,6,0,1,1,2,2,3,3,4,4,5,0,5, 6,6,1,3,2,1,3,2,0,4,5,0,4,5, 6,7,0,1,1,2,0,2,3,0,3,1,3,2,0,5, 6,7,1,4,2,4,2,1,3,1,2,3,2,0,0,1, 6,7,0,1,1,2,2,3,3,4,0,4,1,3,4,1, 6,7,0,1,3,2,0,2,3,0,3,1,5,1,5,2, 6,7,1,4,2,4,2,3,0,4,3,1,4,5,3,4, 6,7,1,0,4,1,2,4,3,2,5,1,2,5,1,2, 6,7,0,4,2,0,1,2,3,1,5,3,3,0,2,3, 6,7,1,4,2,4,2,3,2,1,3,1,4,5,0,4, 6,7,1,0,4,1,2,4,3,2,5,1,2,5,4,5, 6,7,0,1,1,2,0,2,3,0,3,1,3,2,5,4, 6,7,0,5,4,0,5,4,0,2,3,0,3,2,0,1, 6,7,0,1,1,2,2,3,3,4,0,4,1,5,4,1, 6,7,0,1,4,0,1,4,0,2,3,0,3,2,3,5, 6,7,1,4,2,4,4,0,0,5,3,1,2,3,3,4, 6,7,2,0,3,2,4,3,5,4,2,5,1,2,4,1, 6,7,1,5,0,1,4,0,3,4,2,3,1,2,0,3, 6,7,1,4,2,4,4,0,0,5,3,1,2,3,2,1, 6,7,0,1,1,2,2,3,3,4,0,4,0,2,5,1, 6,7,2,0,4,1,1,2,5,4,2,5,3,1,5,3, 6,7,5,0,3,5,2,3,0,2,1,3,4,1,3,4, 6,7,1,3,2,1,0,2,5,0,4,5,3,4,2,3, 6,7,0,1,1,2,2,3,3,4,4,5,0,5,0,3, 6,7,4,3,0,4,1,0,2,1,3,2,0,5,5,3, 6,7,1,2,0,1,2,0,3,0,4,3,5,4,3,5, 6,8,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1, 6,8,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3, 6,8,0,1,1,2,0,2,3,0,3,1,3,2,5,0,0,4, 6,8,1,2,3,1,0,3,1,0,2,0,3,2,5,3,4,0, 6,8,0,1,2,4,0,2,5,2,3,1,3,2,2,1,4,1, 6,8,0,1,1,2,2,3,3,4,0,4,1,3,4,1,1,5, 6,8,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4, 6,8,0,1,2,5,0,2,4,0,3,1,3,2,2,1,5,1, 6,8,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,0, 6,8,0,1,2,5,0,2,4,0,3,1,3,2,3,0,5,1, 6,8,2,0,3,2,4,3,5,4,2,5,1,2,4,1,5,3, 6,8,0,1,1,2,0,2,3,0,3,1,3,2,0,5,5,4, 6,8,0,1,2,5,0,2,4,0,3,1,3,2,5,1,5,3, 6,8,1,4,2,4,2,3,0,4,3,1,4,5,0,5,3,4, 6,8,0,1,1,2,2,3,3,4,0,4,5,0,5,2,0,2, 6,8,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3, 6,8,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2, 6,8,1,3,2,1,0,2,5,0,4,5,3,4,1,4,0,1, 6,8,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0, 6,8,1,4,2,4,2,3,0,4,3,1,4,5,0,5,2,1, 6,8,0,1,1,2,2,3,3,4,0,4,4,5,5,3,1,5, 6,8,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,1, 6,8,0,1,1,2,2,3,3,4,0,4,1,5,5,2,5,0, 6,8,0,1,1,2,2,3,3,4,4,5,0,5,4,1,5,2, 6,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3, 6,9,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,2, 6,9,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,0,4, 6,9,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,4,5, 6,9,2,0,4,1,1,2,5,4,2,5,3,1,5,3,3,2,4,3, 6,9,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,5, 6,9,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5, 6,9,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,3,0, 6,9,1,3,2,1,0,2,5,0,4,5,3,4,0,4,1,0,4,1, 6,9,1,3,2,1,0,2,5,0,4,5,3,4,4,1,1,0,5,1, 6,9,0,1,1,2,0,2,3,0,3,1,3,2,5,4,4,0,5,0, 6,9,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5, 6,9,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0, 6,9,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4, 6,9,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1, 6,9,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,2,0, 6,9,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3, 6,9,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,5,1, 6,9,0,1,1,2,2,3,3,4,4,5,0,5,0,3,4,2,5,2, 6,9,2,3,0,2,3,0,4,3,1,4,5,1,4,5,1,0,5,2, 6,9,0,1,1,2,2,3,3,4,4,5,0,5,0,3,5,2,4,1, 6,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,2, 6,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,4,5, 6,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,5, 6,10,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5,1,0, 6,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,3,5,1,5, 6,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0,2,4, 6,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,2, 6,10,1,0,4,1,0,4,5,0,4,5,3,4,1,3,5,1,2,3,1,2, 6,10,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,5,2, 6,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,4,1, 6,10,0,1,2,4,0,2,4,5,3,1,3,2,4,1,5,1,5,2,5,3, 6,10,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,5,3,5,4, 6,10,0,1,1,2,2,3,3,4,4,5,0,5,2,4,0,2,1,3,5,1, 6,10,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,3,2,0,3, 6,10,1,3,2,1,0,2,5,0,4,5,3,4,4,1,5,3,2,5,1,0, 6,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,2,1,5, 6,11,0,1,2,4,0,2,2,1,3,1,3,2,4,1,5,1,5,2,5,3,0,3, 6,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0,4,5, 6,11,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,0,2, 6,11,1,3,2,1,0,2,5,0,4,5,3,4,4,1,5,3,2,5,1,0,5,1, 6,11,1,3,4,1,3,4,2,3,0,2,4,0,5,4,2,5,4,2,0,5,1,5, 6,11,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,1,4,0,1, 6,11,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2, 6,11,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,0,3, 6,12,0,1,1,2,0,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,4,3, 6,12,3,2,1,3,2,1,0,2,5,0,2,5,2,4,5,1,0,3,1,4,0,1,0,4, 6,12,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2,4,5, 6,12,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,1,4,0,1,2,3, 6,12,0,1,1,2,0,2,3,2,3,1,4,0,2,4,5,1,0,5,4,5,3,4,5,3, 6,13,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,1,4,0,1,2,3,0,4, 6,13,0,1,1,2,0,2,3,2,3,1,4,0,2,4,5,1,0,5,4,5,3,4,5,3,3,0, 6,14,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,1,3,2,0,4,0,5,3, 6,15,0,1,0,2,0,3,0,4,0,5,1,2,1,3,1,4,1,5,2,3,2,4,2,5,3,4,3,5,4,5, 7,0, 7,1,6,5, 7,2,2,3,1,2, 7,2,5,4,6,0, 7,3,0,4,4,2,2,0, 7,3,0,1,0,6,0,5, 7,3,5,4,6,0,5,6, 7,3,3,2,1,2,5,6, 7,3,3,1,5,6,0,4, 7,4,2,5,6,2,5,6,1,2, 7,4,1,2,4,1,5,4,2,5, 7,4,1,0,5,1,1,2,4,1, 7,4,1,0,2,1,5,2,6,2, 7,4,3,4,2,3,1,2,0,1, 7,4,4,2,0,4,2,0,5,6, 7,4,0,1,6,0,0,5,4,2, 7,4,3,1,5,4,6,5,0,6, 7,4,0,4,3,0,2,5,6,2, 7,4,2,3,1,2,6,0,5,4, 7,5,0,4,3,0,1,3,4,1,1,0, 7,5,2,5,6,2,5,6,4,2,3,2, 7,5,4,2,4,0,2,0,5,4,6,0, 7,5,2,5,6,2,5,6,1,2,0,1, 7,5,4,1,0,4,3,0,1,3,2,1, 7,5,1,2,0,1,4,0,3,4,2,3, 7,5,5,1,5,0,2,5,3,5,4,5, 7,5,1,5,6,1,1,0,2,1,3,2, 7,5,1,5,4,1,2,3,6,2,2,1, 7,5,1,5,6,1,1,2,2,3,4,3, 7,5,2,1,3,2,4,3,5,4,3,6, 7,5,6,5,2,6,1,2,5,2,3,4, 7,5,4,3,5,4,6,5,0,6,1,0, 7,5,0,4,3,0,2,5,6,2,5,6, 7,5,4,1,5,2,6,5,3,6,2,3, 7,5,1,4,3,1,1,0,2,1,6,5, 7,5,0,4,3,0,1,0,2,1,6,5, 7,5,0,4,3,0,2,1,5,2,6,2, 7,5,6,5,3,4,2,3,1,2,0,1, 7,5,2,3,1,2,6,0,5,6,5,4, 7,5,0,1,4,6,5,4,3,2,6,5, 7,6,1,5,6,1,5,6,2,5,1,2,6,2, 7,6,1,4,3,1,2,3,4,2,1,0,2,1, 7,6,0,4,3,0,1,3,2,1,1,4,3,4, 7,6,5,2,4,5,2,4,3,2,6,3,2,6, 7,6,1,2,4,1,5,4,2,5,0,1,4,0, 7,6,1,2,5,1,4,5,2,4,0,2,5,0, 7,6,2,5,6,2,5,6,2,4,1,2,3,2, 7,6,1,4,3,1,2,3,1,2,2,5,6,2, 7,6,5,4,6,5,1,6,5,1,3,6,0,1, 7,6,6,5,1,6,5,1,3,1,0,3,1,4, 7,6,0,4,3,0,2,3,4,2,2,5,6,2, 7,6,1,4,3,1,2,3,1,2,2,5,6,5, 7,6,2,3,1,2,3,6,5,4,6,5,5,2, 7,6,2,5,6,2,5,6,1,4,3,1,2,1, 7,6,4,5,0,4,3,0,2,3,4,2,6,3, 7,6,0,4,3,0,1,3,6,5,1,4,1,0, 7,6,1,4,3,1,2,3,5,2,6,5,2,6, 7,6,6,3,5,6,4,5,1,4,2,1,5,2, 7,6,1,0,3,1,6,3,5,6,4,5,1,4, 7,6,0,1,1,2,2,3,3,4,4,5,0,5, 7,6,0,4,3,0,4,3,2,5,6,2,5,6, 7,6,6,3,0,6,6,2,5,6,6,1,4,6, 7,6,2,4,5,2,2,3,6,2,1,2,1,0, 7,6,1,0,2,1,5,2,1,4,3,1,6,2, 7,6,1,0,2,1,3,6,1,3,4,1,5,4, 7,6,1,0,2,1,5,2,6,5,1,4,3,1, 7,6,1,0,2,4,5,2,6,5,2,6,3,2, 7,6,4,0,1,4,3,1,2,1,5,2,6,2, 7,6,6,5,1,2,0,1,2,0,3,2,0,4, 7,6,0,4,3,0,1,0,2,1,5,2,6,2, 7,6,1,0,3,1,6,3,2,6,4,1,5,4, 7,6,2,5,6,2,4,2,1,4,3,1,0,3, 7,6,0,4,3,0,2,3,4,2,1,2,6,5, 7,6,0,4,3,0,2,1,5,2,6,5,2,6, 7,6,3,4,1,0,2,1,5,2,6,5,2,6, 7,6,4,5,0,4,3,0,6,3,1,0,2,1, 7,6,2,5,6,2,5,6,1,4,3,1,1,0, 7,6,4,5,3,4,2,3,1,2,0,1,6,0, 7,6,6,4,5,6,4,5,2,3,1,2,0,1, 7,6,0,1,4,0,2,3,5,2,6,5,3,6, 7,6,1,2,0,1,4,0,3,4,2,3,6,5, 7,7,1,4,3,1,2,3,4,2,1,0,2,1,3,4, 7,7,1,2,5,1,4,5,2,4,0,2,5,0,5,2, 7,7,0,1,1,2,2,3,3,4,0,4,1,3,4,1, 7,7,1,2,5,1,4,5,2,4,0,2,5,0,1,0, 7,7,0,4,3,0,2,3,4,2,2,5,6,2,2,0, 7,7,1,4,3,1,2,3,4,2,1,0,2,1,2,6, 7,7,1,4,3,1,2,3,4,2,1,0,3,4,6,3, 7,7,0,4,3,0,2,3,4,2,2,5,6,2,3,4, 7,7,0,4,3,0,1,3,3,6,1,4,1,0,5,4, 7,7,0,4,3,0,1,3,6,5,1,4,1,0,3,4, 7,7,5,2,4,5,2,4,3,2,6,3,2,6,2,1, 7,7,0,1,1,2,2,3,3,4,0,4,0,2,2,5, 7,7,5,2,4,5,2,4,3,2,6,3,2,6,3,1, 7,7,1,4,3,1,2,3,4,2,2,0,2,1,6,0, 7,7,1,2,5,1,4,5,2,4,0,2,5,0,3,5, 7,7,0,1,1,2,2,3,3,4,0,4,0,2,3,5, 7,7,0,1,1,2,2,3,3,4,0,4,0,2,1,5, 7,7,3,2,4,3,3,5,2,4,5,2,6,1,6,4, 7,7,1,2,5,1,4,5,2,4,0,2,5,0,0,3, 7,7,3,4,1,3,2,1,6,2,5,6,1,5,4,1, 7,7,0,1,4,0,1,4,2,1,3,2,5,3,4,5, 7,7,6,3,5,6,1,5,2,1,3,2,4,2,5,4, 7,7,1,2,4,1,5,4,6,5,3,6,2,3,5,2, 7,7,4,1,3,4,1,3,2,1,6,2,5,6,2,5, 7,7,3,0,6,3,0,6,1,0,0,2,5,0,0,4, 7,7,1,5,6,1,1,2,3,1,4,3,1,4,4,0, 7,7,5,0,6,5,0,6,5,2,1,5,6,3,4,6, 7,7,4,1,0,4,1,0,2,1,0,3,6,0,4,5, 7,7,5,2,6,5,2,6,2,4,3,2,1,0,2,1, 7,7,4,1,0,4,3,0,1,3,2,1,1,5,6,1, 7,7,1,0,4,1,0,4,5,4,2,1,3,2,6,1, 7,7,0,1,4,0,1,4,2,1,3,2,5,4,6,4, 7,7,2,3,5,2,6,5,3,6,1,2,4,5,0,5, 7,7,0,4,3,0,1,3,4,1,1,0,2,1,6,5, 7,7,2,5,6,2,5,6,4,2,1,2,0,1,3,1, 7,7,2,5,6,2,4,2,1,4,3,1,2,3,0,1, 7,7,6,2,5,6,2,5,1,2,0,1,4,1,3,1, 7,7,0,4,3,0,1,3,4,1,5,4,2,1,6,3, 7,7,2,5,6,2,5,6,4,5,3,6,1,2,0,1, 7,7,2,5,6,2,1,4,1,2,0,1,4,0,0,3, 7,7,6,5,1,2,4,1,0,4,3,0,1,3,3,4, 7,7,4,1,0,4,1,0,3,6,2,3,0,2,5,0, 7,7,4,1,0,4,3,0,1,3,2,1,5,2,6,1, 7,7,4,1,0,4,1,0,2,3,0,2,5,0,6,5, 7,7,0,1,5,0,6,5,3,6,2,3,0,2,4,0, 7,7,1,0,4,1,2,4,3,2,4,3,0,4,6,5, 7,7,3,6,2,3,1,2,0,1,4,0,1,4,5,4, 7,7,1,0,5,1,6,5,2,6,1,2,3,2,4,3, 7,7,2,3,1,2,0,1,4,0,5,4,6,5,4,1, 7,7,5,2,6,5,2,6,1,2,4,1,0,4,3,1, 7,7,2,3,1,2,0,1,4,0,5,4,6,5,5,2, 7,7,1,4,0,1,2,0,3,2,5,3,0,5,6,3, 7,7,2,1,3,2,6,3,5,6,0,5,2,0,5,4, 7,7,5,2,6,5,2,6,1,2,0,1,4,0,3,0, 7,7,4,1,0,4,3,0,1,3,2,1,5,2,6,2, 7,7,1,0,2,1,5,2,4,5,0,4,4,1,6,3, 7,7,2,5,6,2,0,4,3,0,1,3,4,1,1,0, 7,7,6,5,0,4,3,0,1,3,4,1,2,4,3,2, 7,7,2,1,5,2,4,5,0,4,3,0,6,3,2,6, 7,7,4,0,3,4,1,3,2,1,5,2,6,5,2,6, 7,7,6,5,2,6,1,2,4,1,0,4,3,0,1,3, 7,7,4,1,0,4,2,0,3,2,6,3,5,6,0,5, 7,7,0,4,3,0,4,3,2,1,5,2,6,5,2,6, 7,7,0,1,1,2,2,3,3,4,4,5,5,6,0,6, 7,7,1,0,4,1,0,4,5,2,6,5,3,6,2,3, 7,8,0,1,4,0,5,4,2,5,1,2,5,1,4,1,2,4, 7,8,4,1,5,4,2,5,1,2,0,1,5,0,0,4,2,0, 7,8,0,4,3,0,1,3,4,1,1,0,3,4,5,1,6,1, 7,8,4,1,5,4,2,5,1,2,5,1,6,5,2,4,3,2, 7,8,1,3,0,1,4,0,2,4,1,2,4,1,5,4,1,5, 7,8,2,0,3,2,6,3,5,6,0,5,3,0,0,6,4,0, 7,8,1,0,2,1,5,2,4,5,0,4,2,0,5,0,6,5, 7,8,1,0,2,1,3,2,1,3,4,3,2,4,5,2,3,5, 7,8,2,0,3,2,6,3,5,6,0,5,3,0,6,0,4,5, 7,8,1,0,2,1,4,3,1,5,4,1,2,4,5,2,3,5, 7,8,3,5,2,1,4,3,1,5,4,1,2,4,5,2,4,6, 7,8,0,4,3,0,1,3,4,1,1,0,3,4,2,1,5,2, 7,8,3,5,2,1,4,3,1,5,4,1,2,4,5,2,0,3, 7,8,4,0,2,4,0,2,3,0,2,3,5,2,6,5,2,6, 7,8,3,2,6,3,5,6,2,5,0,2,5,0,4,5,2,4, 7,8,0,5,4,0,2,4,5,2,1,5,4,1,3,4,5,3, 7,8,2,3,1,2,4,1,5,4,1,5,5,2,6,5,3,6, 7,8,5,2,4,5,0,4,3,0,6,3,2,6,4,2,3,2, 7,8,0,4,3,0,1,3,4,1,2,4,3,2,5,4,2,5, 7,8,5,6,2,5,6,2,3,2,4,3,0,4,3,0,2,4, 7,8,1,0,5,0,3,2,1,3,5,2,6,1,6,2,6,5, 7,8,5,4,6,5,3,6,0,3,4,0,2,4,3,2,0,2, 7,8,0,1,1,2,2,3,3,4,4,5,0,5,4,2,1,5, 7,8,5,0,6,2,0,6,1,0,2,1,5,2,4,5,4,6, 7,8,0,4,3,0,1,3,4,1,1,0,2,1,1,5,6,1, 7,8,0,2,4,0,1,4,0,1,3,0,1,3,5,1,6,1, 7,8,4,2,0,4,3,0,1,3,4,1,1,0,1,5,6,1, 7,8,0,4,3,0,4,3,1,4,3,1,1,5,2,1,6,1, 7,8,2,1,0,2,3,0,5,3,2,5,3,2,4,3,6,5, 7,8,4,2,0,4,3,0,1,3,4,1,3,4,1,5,6,1, 7,8,2,1,0,2,3,0,5,3,2,5,6,5,4,3,5,0, 7,8,1,0,2,1,3,2,1,3,4,2,3,4,4,5,6,4, 7,8,6,5,1,2,4,1,0,4,3,0,1,3,0,1,3,4, 7,8,0,1,6,5,2,3,6,4,6,3,6,2,6,0,6,1, 7,8,6,4,1,2,2,3,6,5,4,5,6,2,6,0,6,1, 7,8,0,1,1,2,2,3,6,5,6,4,6,3,6,0,6,2, 7,8,0,4,3,0,1,3,4,1,1,0,6,1,5,1,2,5, 7,8,3,0,2,3,4,2,0,4,1,0,2,1,5,2,6,2, 7,8,2,1,3,2,6,3,5,6,0,5,2,0,5,2,4,5, 7,8,1,0,2,1,3,2,4,3,5,2,1,5,6,1,2,6, 7,8,2,5,4,2,1,4,3,1,0,3,1,0,2,1,6,2, 7,8,4,5,0,4,3,0,2,3,4,2,1,4,3,1,6,3, 7,8,0,1,4,0,1,4,2,1,4,2,5,4,1,5,6,3, 7,8,0,1,2,0,3,2,4,3,1,4,2,1,1,6,5,0, 7,8,4,5,0,4,1,0,4,1,3,0,1,3,6,1,2,6, 7,8,2,5,4,2,0,4,1,0,4,1,3,0,1,3,6,1, 7,8,1,6,2,1,0,2,1,0,4,1,3,4,2,3,4,5, 7,8,0,1,2,0,3,2,4,3,1,4,2,1,1,6,5,3, 7,8,0,4,3,0,4,3,1,4,3,1,5,1,6,2,1,6, 7,8,2,3,1,2,0,1,5,0,4,5,0,4,2,0,6,5, 7,8,4,5,0,4,3,0,1,3,4,1,2,4,3,2,6,2, 7,8,2,3,1,2,0,1,4,0,5,4,4,1,2,6,5,2, 7,8,0,1,1,2,2,3,6,3,4,5,6,2,6,0,6,1, 7,8,4,1,0,4,3,0,1,3,0,1,2,1,5,2,6,2, 7,8,0,1,1,2,2,3,6,5,4,5,6,2,6,4,6,1, 7,8,0,1,4,0,0,2,5,0,6,5,3,6,2,3,5,2, 7,8,0,4,3,0,2,3,4,2,1,4,3,1,2,5,6,2, 7,8,4,5,3,4,1,3,2,1,6,2,4,6,3,2,0,1, 7,8,1,0,2,6,3,2,4,3,5,2,1,5,6,1,6,5, 7,8,2,3,1,2,0,1,4,0,5,4,6,5,5,2,4,1, 7,8,4,1,0,4,3,0,1,3,3,4,2,1,2,5,6,2, 7,8,0,6,4,0,1,4,3,1,0,3,2,4,3,2,5,2, 7,8,0,4,3,0,1,3,4,1,2,4,3,2,1,0,6,5, 7,8,0,1,4,0,3,2,6,3,5,6,2,5,6,2,3,5, 7,8,5,2,6,5,2,6,4,2,0,4,3,0,2,3,1,2, 7,8,2,0,1,2,0,1,5,0,4,5,0,4,6,0,3,6, 7,8,0,1,2,0,3,2,2,1,1,4,5,4,5,3,1,6, 7,8,1,6,2,1,0,2,1,0,4,1,3,4,2,3,5,6, 7,8,6,1,0,6,1,0,5,1,0,5,2,1,3,2,4,3, 7,8,6,5,2,6,1,2,4,1,3,4,0,3,4,0,2,4, 7,8,1,6,0,1,5,0,1,5,3,0,4,3,2,4,0,2, 7,8,2,6,4,2,0,4,1,0,4,1,3,4,5,3,2,5, 7,8,1,0,2,1,6,2,5,6,1,5,4,1,3,4,2,3, 7,8,6,1,4,3,1,0,5,1,3,2,2,1,4,6,5,4, 7,8,4,2,0,4,1,0,4,1,3,4,6,3,5,6,3,5, 7,8,4,1,2,4,0,2,6,0,3,6,0,3,5,0,4,5, 7,8,5,6,4,5,0,4,3,0,2,3,4,2,1,4,3,1, 7,8,6,3,5,6,4,5,0,4,1,0,2,1,5,2,4,1, 7,8,0,1,2,0,3,2,2,1,1,4,5,4,5,3,4,6, 7,8,4,0,3,4,2,3,6,2,5,6,1,5,4,1,2,1, 7,8,6,1,0,6,4,3,5,1,0,5,2,1,3,2,5,6, 7,8,6,2,5,6,3,5,6,3,4,3,0,4,1,0,4,1, 7,8,0,1,1,2,2,3,3,4,4,5,0,5,0,6,5,1, 7,8,0,1,1,2,2,3,3,4,4,5,0,5,0,6,4,2, 7,8,0,1,2,0,3,2,4,3,1,4,2,1,5,6,0,5, 7,8,4,0,2,4,3,2,6,3,5,6,4,5,1,2,5,1, 7,8,5,1,2,4,3,2,0,3,5,0,4,5,1,2,0,6, 7,8,5,6,2,5,4,2,0,4,3,0,2,3,1,4,3,1, 7,8,0,4,1,0,4,1,3,4,5,3,6,5,2,6,4,2, 7,8,0,1,6,5,2,3,3,4,6,4,0,5,6,2,6,1, 7,8,1,2,0,1,4,0,5,4,2,5,3,2,6,3,5,6, 7,8,0,1,1,2,2,3,3,4,4,5,0,5,2,6,1,6, 7,8,6,2,5,6,2,5,1,2,4,1,0,4,3,0,1,3, 7,8,0,1,1,2,2,3,3,4,4,5,0,5,6,5,6,1, 7,8,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,3, 7,8,0,4,1,0,3,2,1,4,2,5,5,3,6,4,6,3, 7,8,0,4,3,0,1,3,4,1,1,0,6,2,5,6,2,5, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3, 7,9,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,2, 7,9,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,0,4, 7,9,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,4,5, 7,9,2,0,4,1,1,2,5,4,2,5,3,1,5,3,3,2,4,3, 7,9,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,5, 7,9,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5, 7,9,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,3,0, 7,9,1,3,2,1,0,2,5,0,4,5,3,4,0,4,1,0,4,1, 7,9,1,3,2,1,0,2,5,0,4,5,3,4,4,1,1,0,5,1, 7,9,0,1,1,2,0,2,3,0,3,1,3,2,5,4,4,0,5,0, 7,9,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5, 7,9,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0, 7,9,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4, 7,9,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1, 7,9,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,2,0, 7,9,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3, 7,9,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,5,1, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,0,3,4,2,5,2, 7,9,2,3,0,2,3,0,4,3,1,4,5,1,4,5,1,0,5,2, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,0,3,5,2,4,1, 7,9,0,1,1,2,0,2,3,0,3,1,3,2,5,0,0,4,0,6, 7,9,0,1,1,2,0,2,3,0,3,1,3,2,5,0,0,4,2,6, 7,9,1,2,3,1,0,3,1,0,2,0,3,2,5,3,4,0,1,6, 7,9,0,1,2,4,0,2,3,1,3,2,2,1,4,1,5,2,2,6, 7,9,0,1,2,4,0,2,5,2,3,1,3,2,2,1,4,1,1,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,1,5,1,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,1,5,4,6, 7,9,0,1,2,5,0,2,4,0,3,1,3,2,2,1,5,1,1,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,4,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,3,6, 7,9,0,1,2,5,0,2,4,0,3,1,3,2,2,1,5,1,0,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,0,1,6, 7,9,2,0,3,2,4,3,5,4,2,5,1,2,4,1,5,3,2,6, 7,9,0,1,2,5,0,2,4,0,3,1,3,2,3,0,5,1,0,6, 7,9,0,1,1,2,0,2,3,0,3,1,3,2,5,0,0,4,5,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,0,4,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,2,6, 7,9,2,0,3,2,4,3,5,4,2,5,1,2,4,1,5,3,5,6, 7,9,1,2,3,1,0,3,1,0,2,0,3,2,4,0,6,5,6,3, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,0,0,6, 7,9,0,1,1,2,2,3,3,4,0,4,3,6,5,4,0,3,2,4, 7,9,0,1,2,5,0,2,4,0,3,1,3,2,2,1,5,1,5,6, 7,9,2,0,3,2,4,3,5,4,2,5,1,2,4,1,5,3,4,6, 7,9,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,0,2,6, 7,9,0,1,2,5,0,2,4,0,3,1,3,2,3,0,5,1,5,6, 7,9,0,1,2,5,0,2,5,4,3,1,3,2,3,0,5,1,2,6, 7,9,0,1,2,5,0,2,4,0,3,1,3,2,5,1,5,3,0,6, 7,9,0,1,1,2,0,2,3,0,3,1,3,2,0,5,5,4,5,6, 7,9,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,6, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,3,4,4,6, 7,9,0,1,1,2,2,3,3,4,0,4,5,0,5,2,0,2,0,6, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,3,4,3,6, 7,9,0,1,2,4,0,2,5,2,3,1,3,2,2,1,4,1,5,6, 7,9,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,2,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,6,5,6,1, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,3,4,2,6, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,3,4,0,6, 7,9,1,3,2,1,0,2,5,0,6,5,3,6,1,6,0,1,1,4, 7,9,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,0,6, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,2,1,4,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,5,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,4,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,5,6, 7,9,1,3,2,1,0,2,5,0,6,5,3,6,1,6,0,1,0,4, 7,9,0,1,1,2,2,3,3,4,0,4,5,0,5,2,0,2,1,6, 7,9,0,1,1,2,2,3,3,4,0,4,5,0,5,2,0,2,4,6, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,2,1,2,6, 7,9,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,3,6, 7,9,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,2,6, 7,9,0,1,2,5,0,2,5,1,3,1,3,2,2,1,6,0,6,4, 7,9,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,1,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,0,5,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,0,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,3,6, 7,9,1,3,2,1,0,2,5,0,6,5,3,6,1,6,0,1,2,4, 7,9,0,1,1,2,2,3,3,4,0,4,4,5,5,3,1,5,3,6, 7,9,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,1,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,1,4,6, 7,9,0,1,2,5,0,2,5,1,3,1,3,2,3,0,6,4,6,0, 7,9,0,1,1,2,2,3,3,4,0,4,1,5,5,2,5,0,1,6, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,2,1,6,0, 7,9,5,3,3,2,4,3,5,4,2,5,1,2,4,1,6,0,6,2, 7,9,0,1,1,2,2,3,3,4,0,4,1,5,5,2,5,0,0,6, 7,9,0,1,1,2,2,3,3,4,0,4,4,5,5,3,1,5,5,6, 7,9,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,4,6, 7,9,1,3,2,1,0,2,5,0,6,5,3,6,1,6,0,1,5,4, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,4,1,5,2,5,6, 7,9,0,1,1,2,2,3,3,4,0,4,4,5,5,3,1,5,1,6, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,2,1,3,6, 7,9,0,1,1,2,0,2,3,0,3,1,3,2,0,5,5,4,4,6, 7,9,0,1,1,2,2,3,3,4,0,4,4,5,5,3,1,5,0,6, 7,9,0,1,1,2,2,3,3,4,0,4,1,5,5,2,5,0,4,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,1,0,6, 7,9,0,1,2,5,0,2,5,3,3,1,3,2,5,1,6,4,6,0, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,4,1,5,2,0,6, 7,9,6,3,1,2,6,5,3,4,6,4,0,5,6,0,6,1,6,2, 7,9,0,1,2,0,3,2,4,3,1,4,2,1,6,2,5,6,2,5, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,3,4,6,5,6,0, 7,9,1,4,2,4,2,3,0,4,3,1,4,5,0,5,6,4,6,3, 7,9,4,1,5,4,6,5,3,6,2,3,1,2,5,2,0,5,2,0, 7,9,4,1,3,1,2,3,4,0,5,0,5,2,5,4,6,4,5,6, 7,9,1,0,2,1,6,2,3,6,5,3,4,5,3,4,2,3,0,2, 7,9,0,2,5,0,1,5,2,1,4,2,5,4,6,5,3,6,2,3, 7,9,0,1,1,2,2,3,3,4,4,5,5,6,0,6,1,3,4,1, 7,9,0,1,1,2,2,3,3,4,0,4,5,4,5,1,6,1,0,6, 7,9,0,4,1,0,4,1,3,4,2,3,6,2,5,6,1,5,2,1, 7,9,0,1,2,0,3,2,4,3,1,4,2,1,5,3,6,5,3,6, 7,9,6,5,3,6,2,3,0,4,0,5,1,0,2,0,1,2,5,4, 7,9,0,1,1,2,2,3,3,4,0,4,5,3,5,1,6,1,0,6, 7,9,5,2,6,5,3,6,2,3,0,2,4,0,5,4,1,5,0,1, 7,9,2,4,1,2,4,1,5,4,0,5,1,0,6,4,3,6,2,3, 7,9,6,2,5,6,2,5,1,2,0,1,4,0,1,4,3,1,0,3, 7,9,0,5,6,0,1,6,4,1,2,4,3,2,1,3,5,1,6,5, 7,9,6,5,3,6,2,3,5,2,0,5,1,0,4,1,0,4,2,0, 7,9,0,4,3,0,1,3,4,1,2,4,6,2,5,6,2,5,3,2, 7,9,1,0,4,1,5,4,0,5,6,0,3,6,2,3,0,2,3,4, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,1,6,3, 7,9,0,1,1,2,2,3,0,3,4,1,5,4,5,3,6,0,6,4, 7,9,0,1,4,0,1,4,2,1,5,2,4,5,6,5,3,6,2,3, 7,9,1,0,6,3,0,4,5,0,3,5,5,6,1,2,1,4,6,2, 7,9,6,2,5,6,2,5,1,2,0,3,4,0,1,4,3,1,3,4, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,6,1,5,6,6,0, 7,9,0,4,1,0,2,1,3,2,0,3,2,4,5,4,6,5,3,6, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,4,2,6,1,5,6, 7,9,0,1,1,2,2,3,3,4,0,4,5,3,5,4,6,1,6,5, 7,9,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,4,6,2, 7,9,0,4,3,0,4,3,6,1,5,6,1,5,2,1,5,2,6,2, 7,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,2, 7,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,4, 7,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5,1,0, 7,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,3,5,1,5, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0,2,4, 7,10,1,0,4,1,0,4,5,0,4,5,3,4,1,3,5,1,2,3,1,2, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,2, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,4,1, 7,10,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,5,2, 7,10,0,1,2,4,0,2,4,5,3,1,3,2,4,1,5,1,5,2,5,3, 7,10,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,5,3,5,4, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,2,4,0,2,1,3,5,1, 7,10,0,1,1,2,3,4,0,2,3,0,2,4,5,2,1,5,4,1,3,5, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,4,1,5,3,2,5,1,0, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,2,2,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,2,1,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,0,4,1,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,0,4,0,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,0,4,3,6, 7,10,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,4,5,4,6, 7,10,2,0,4,1,1,2,5,4,2,5,3,1,5,3,3,2,4,3,3,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,5,2,6, 7,10,2,0,4,1,1,2,5,4,2,5,3,1,5,3,3,2,4,3,2,6, 7,10,2,3,1,2,4,1,5,4,2,5,0,2,4,0,0,1,5,0,6,5, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,0,4,5,6, 7,10,2,0,4,1,1,2,5,4,2,5,3,1,5,3,3,2,4,3,4,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,4,5,6,5, 7,10,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,6, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5,6,5, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,3,0,0,6, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,3,0,2,6, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5,1,6, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,3,0,3,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,0,4,1,0,4,1,0,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,4,1,1,0,5,1,1,6, 7,10,0,1,1,2,0,2,3,0,3,1,3,2,5,4,4,0,5,0,0,6, 7,10,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,3,6, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,3,0,5,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0,3,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,4,1,1,0,5,1,0,6, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,2,0,5,3,2,3,6,2, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,6,4,6,2, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,2,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,4,1,1,0,5,1,5,6, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,3,0,4,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,3,6, 7,10,0,1,1,2,0,2,3,0,3,1,3,2,5,4,4,0,5,0,2,6, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,2,0,5,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0,2,6, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,2,0,5,3,2,3,0,6, 7,10,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,1,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,4,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,6,4,6,0, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,5,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,0,4,1,0,4,1,5,6, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,2,0,0,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,4,1,1,0,5,1,2,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,0,3,4,2,5,2,2,6, 7,10,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3,5,6, 7,10,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,5,1,0,6, 7,10,0,1,1,2,0,2,3,0,3,1,3,2,5,4,4,0,5,0,5,6, 7,10,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,6,5,6,4, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0,1,6, 7,10,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,4,6, 7,10,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3,4,6, 7,10,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,2,6, 7,10,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3,0,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,0,3,4,2,5,2,5,6, 7,10,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,5,1,1,6, 7,10,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,3,6, 7,10,4,3,4,1,1,2,5,4,2,5,3,1,5,3,3,2,6,0,6,2, 7,10,1,0,2,1,3,2,4,3,5,4,1,5,6,1,4,6,2,6,5,2, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,0,3,4,2,5,2,0,6, 7,10,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,5,1,3,6, 7,10,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,5,1,2,6, 7,10,0,1,2,5,0,2,3,0,3,1,3,2,2,1,5,1,6,5,6,4, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,1,6, 7,10,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,6, 7,10,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,2,0,1,6, 7,10,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3,1,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,0,3,4,2,5,2,1,6, 7,10,0,1,1,2,2,3,3,4,0,4,3,0,5,2,5,0,5,1,4,6, 7,10,2,3,0,2,3,0,4,3,1,4,5,1,4,5,1,0,5,2,4,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,0,3,5,2,4,1,0,6, 7,10,4,0,1,4,3,1,2,3,1,2,6,1,0,6,5,0,1,5,0,1, 7,10,3,2,6,3,5,6,0,5,2,0,5,2,1,5,2,1,4,2,5,4, 7,10,2,0,1,2,3,1,0,3,6,0,1,6,5,1,0,5,4,0,1,4, 7,10,6,4,1,2,6,5,3,4,4,5,0,5,6,0,6,1,6,2,6,3, 7,10,0,1,6,5,2,3,3,4,6,4,0,5,6,0,6,1,6,2,6,3, 7,10,0,1,2,0,3,2,4,3,1,4,2,1,0,5,5,2,6,1,2,6, 7,10,0,1,2,0,3,2,4,3,1,4,2,1,5,0,5,2,6,2,0,6, 7,10,6,4,1,2,6,5,3,4,4,5,0,5,6,3,6,1,6,2,0,4, 7,10,1,0,2,1,0,2,3,2,4,3,2,4,5,2,4,5,6,4,1,6, 7,10,0,1,0,3,0,4,0,5,0,6,1,2,1,3,1,4,2,5,2,6, 7,10,0,2,5,0,4,5,2,4,1,2,5,1,6,5,3,6,2,3,2,6, 7,10,0,1,2,0,3,2,4,3,1,4,2,1,5,1,0,5,6,0,2,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,2,6,4,0,2,4,0, 7,10,0,4,3,0,2,3,5,2,6,5,2,6,4,2,1,4,3,1,4,3, 7,10,1,6,2,1,0,2,1,0,4,1,3,4,2,3,5,6,4,5,3,1, 7,10,6,5,1,2,2,3,3,4,4,5,0,5,6,0,6,1,6,2,6,4, 7,10,0,1,6,5,2,3,3,4,6,4,0,5,6,0,6,1,6,2,5,3, 7,10,0,1,1,2,2,3,5,4,0,4,5,0,5,3,5,2,6,5,6,1, 7,10,0,3,2,0,1,2,3,1,4,3,2,4,0,4,6,0,5,6,0,5, 7,10,0,3,2,0,1,2,3,1,4,3,0,5,0,4,6,0,5,6,1,4, 7,10,0,1,6,5,2,3,3,4,6,4,0,5,6,0,6,1,6,2,4,2, 7,10,1,2,5,1,6,5,2,6,1,6,5,2,4,1,0,4,3,0,1,3, 7,10,4,2,6,2,5,3,4,1,2,0,6,3,5,2,0,1,0,4,6,0, 7,10,4,2,3,6,5,3,5,1,2,0,6,0,5,2,1,4,0,4,5,4, 7,10,4,0,5,4,4,1,2,1,3,2,0,3,3,4,5,3,6,1,6,5, 7,10,0,4,1,0,2,1,4,2,3,4,5,3,4,5,5,2,6,3,2,6, 7,10,1,6,2,1,0,2,1,0,4,1,3,4,2,3,5,6,4,5,4,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,3,6,1,6,5,5,1, 7,10,1,0,4,1,0,4,2,0,3,2,6,3,5,6,0,5,5,2,6,2, 7,10,0,1,1,2,2,3,3,4,0,4,5,3,5,1,5,4,6,1,5,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,2,4,4,1,6,1,6,5, 7,10,0,1,2,0,3,2,4,3,1,4,2,1,5,3,2,5,6,1,4,6, 7,10,0,1,1,2,2,3,3,4,0,4,5,2,5,4,6,5,6,0,0,2, 7,10,2,0,5,2,1,5,0,1,3,0,5,3,6,5,4,6,0,4,4,3, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,4,2,1,5,6,2,6,5, 7,10,5,0,6,5,2,6,3,2,0,3,4,0,2,4,4,3,1,4,3,1, 7,10,0,1,1,2,2,3,3,4,0,4,6,3,5,6,3,5,4,5,4,6, 7,10,5,2,2,1,3,2,4,3,1,4,5,0,6,1,6,0,1,5,2,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,2,6,4,4,2,5,1, 7,10,4,2,2,3,4,1,0,1,3,0,6,4,0,6,5,0,4,5,1,5, 7,10,2,1,5,2,3,5,0,3,4,0,6,4,3,6,1,3,4,1,5,4, 7,10,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,6,5,6,3, 7,10,0,1,4,0,1,4,2,1,5,2,4,5,6,5,3,6,2,3,5,3, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,5,6,1,6,2,4,2, 7,10,0,1,1,2,2,3,3,4,0,4,5,0,5,3,5,2,6,5,6,4, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,3,6,2,4,0, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,4,6,5,6,3, 7,10,0,5,6,0,1,6,0,1,1,5,2,1,3,2,4,3,6,4,4,5, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,5,6,4,2,0, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,1,6,5,6,3, 7,10,2,1,2,0,3,2,4,3,1,4,5,1,5,0,6,0,1,6,6,5, 7,10,0,1,1,2,2,3,3,4,0,4,5,3,5,1,6,1,6,4,6,5, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,5,2,4,1,6,0,5,6, 7,10,3,1,0,3,5,0,1,5,2,1,6,2,0,6,0,4,4,2,4,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,4,6,1,6,2,6,5, 7,10,0,3,2,0,1,2,3,1,4,3,2,4,5,4,5,0,6,4,6,1, 7,10,0,1,6,5,2,3,3,4,6,4,0,5,6,2,6,1,4,2,5,1, 7,10,5,2,6,5,2,6,4,2,0,4,3,0,2,3,1,0,1,3,4,1, 7,10,3,4,1,3,4,1,0,4,3,0,1,0,2,1,5,2,6,5,2,6, 7,10,5,6,2,5,6,2,3,6,0,3,4,0,5,4,1,4,3,1,2,1, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,1,6,5,4,2, 7,10,1,0,2,1,3,2,0,3,5,0,1,5,4,5,6,4,3,6,2,6, 7,10,0,1,1,2,2,3,3,4,4,5,0,5,4,1,2,5,6,0,3,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,2,1,5, 7,11,0,1,2,4,0,2,2,1,3,1,3,2,4,1,5,1,5,2,5,3,0,3, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0,4,5, 7,11,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,0,2, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,5,1, 7,11,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,6,4,5,4,6,5,6, 7,11,3,6,1,3,2,1,0,2,5,0,6,5,2,6,5,1,0,3,1,6,0,1, 7,11,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,0,3, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,4,6,4, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,3,6,4, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,2,4,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,2,5,6,2, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,2,0,6, 7,11,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,6,5, 7,11,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5,1,0,4,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,3,5,1,5,1,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,3,5,1,5,6,4, 7,11,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,4,5,1,0,1,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0,2,4,2,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,3,5,1,5,5,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,2,2,6, 7,11,1,0,4,1,0,4,5,0,4,5,3,4,1,3,5,1,2,3,1,2,6,1, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,2,3,6, 7,11,1,0,4,1,0,4,5,0,4,5,3,4,1,3,5,1,2,3,1,2,6,4, 7,11,0,4,1,5,1,6,2,3,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,11,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,5,2,0,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,2,0,6, 7,11,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,4,1,1,6, 7,11,1,0,4,1,0,4,5,0,4,5,3,4,1,3,5,1,2,3,1,2,5,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,5,4,3,5,1,5,0,6, 7,11,0,1,2,4,0,2,4,5,3,1,3,2,4,1,5,1,5,2,5,3,2,6, 7,11,1,0,4,1,0,4,5,0,4,5,3,4,1,3,5,1,2,3,1,2,3,6, 7,11,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,4,1,2,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,2,5,6, 7,11,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,5,2,5,6, 7,11,0,1,2,4,0,2,4,5,3,1,3,2,4,1,5,1,5,2,5,3,5,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,4,5,6, 7,11,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,5,2,1,6, 7,11,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,4,1,4,6, 7,11,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,4,1,5,6, 7,11,0,1,2,4,0,2,4,5,3,1,3,2,4,1,5,1,5,2,5,3,4,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,4,0,2,4,1,6, 7,11,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,6,5,6,2, 7,11,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,5,3,5,4,5,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,2,4,0,2,1,3,5,1,1,6, 7,11,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,5,3,5,4,1,6, 7,11,1,0,4,1,0,4,5,0,4,5,3,4,1,3,5,1,2,3,1,2,2,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,3,2,0,3,2,4,5,2,1,6, 7,11,0,6,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,5,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,4,1,5,3,2,5,1,0,1,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,2,4,0,2,1,3,5,1,5,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,2,4,0,2,1,3,5,1,6,3, 7,11,4,3,0,4,1,0,2,1,3,2,0,5,5,3,0,3,1,5,5,2,4,6, 7,11,0,1,1,2,2,3,3,4,0,4,2,5,0,5,2,0,5,1,4,1,6,3, 7,11,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,3,2,0,3,1,6, 7,11,1,3,2,1,0,2,5,0,4,5,3,4,4,1,5,3,2,5,1,0,6,2, 7,11,0,1,2,4,0,2,4,5,3,1,3,2,4,1,5,1,5,2,5,3,0,6, 7,11,0,6,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5, 7,11,6,5,0,6,5,0,1,5,6,1,2,6,5,2,3,5,6,3,4,6,5,4, 7,11,0,1,2,0,3,2,4,3,1,4,2,1,5,1,2,5,6,2,1,6,3,1, 7,11,0,1,1,2,2,3,3,4,0,4,5,1,3,5,6,1,4,6,1,4,3,1, 7,11,1,4,2,3,4,2,0,6,4,5,6,5,3,1,6,4,3,0,3,6,4,3, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,4,2,6,4,2,6,5,2,0,2, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,4,0,3,0,2,0,6,0,3,6, 7,11,0,1,2,0,5,2,6,5,2,6,1,2,4,1,2,4,3,2,4,3,3,1, 7,11,4,5,1,4,2,1,3,2,6,3,5,6,2,5,4,2,3,5,0,5,2,0, 7,11,0,1,1,2,2,3,3,4,0,4,4,2,5,2,5,0,6,2,4,6,5,4, 7,11,0,1,1,2,2,3,3,4,0,4,6,1,2,6,0,2,6,0,5,2,0,5, 7,11,0,5,6,0,1,6,5,1,2,5,6,2,4,3,3,2,4,5,6,4,6,5, 7,11,0,5,6,0,1,6,5,1,2,5,6,2,3,6,5,3,4,5,6,4,4,3, 7,11,0,5,0,6,1,2,1,6,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,11,0,1,1,2,2,3,3,4,0,4,0,2,2,4,5,2,4,5,6,5,6,0, 7,11,0,1,1,2,2,3,3,4,4,5,5,6,0,6,4,2,0,4,2,0,6,4, 7,11,0,1,2,0,3,2,4,3,1,4,5,1,5,2,6,1,2,6,4,2,5,4, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,5,1,2,5,4,2,6,2,4,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,4,6,2,0,2,4,0, 7,11,0,1,1,2,2,3,3,4,0,4,3,1,5,3,4,5,1,4,6,5,6,1, 7,11,0,4,3,0,4,3,2,4,3,2,1,3,2,1,4,1,5,2,6,5,2,6, 7,11,0,5,0,6,1,4,1,6,2,3,2,5,3,4,3,5,3,6,4,5,4,6, 7,11,0,1,4,0,5,4,6,5,3,6,2,3,1,2,4,1,2,4,5,2,1,5, 7,11,0,4,3,0,4,3,2,4,6,2,1,6,5,1,2,5,3,2,1,3,4,1, 7,11,0,1,6,5,2,3,3,4,4,5,0,5,6,0,6,1,6,2,6,3,6,4, 7,11,4,1,0,4,1,0,3,1,0,3,5,1,6,5,1,6,2,1,5,2,6,2, 7,11,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,4,6,5,4,6, 7,11,1,0,2,1,3,2,4,3,0,4,2,0,5,2,6,5,3,6,6,0,0,5, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,0,2,4,0,6,4,0,6,3,6, 7,11,0,1,1,2,2,3,3,4,0,4,2,0,5,2,6,5,4,6,0,5,6,0, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,6,0,2,6,3,6,0,3,4,0, 7,11,4,6,5,4,6,5,3,6,5,3,2,5,3,2,5,0,6,0,1,0,2,1, 7,11,2,0,4,2,5,4,3,5,1,3,0,1,2,1,3,2,6,3,5,6,4,3, 7,11,4,3,4,2,1,4,3,1,0,3,1,0,3,2,3,5,2,5,6,0,4,6, 7,11,0,1,0,2,2,3,5,1,1,3,5,2,6,3,6,0,5,3,4,5,3,4, 7,11,4,0,1,4,6,1,0,6,3,0,1,3,5,1,0,5,6,5,2,3,0,2, 7,11,0,1,5,0,4,5,1,4,2,1,3,2,4,3,4,2,6,4,2,6,3,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,5,1,3,5,6,3,4,6,5,6, 7,11,6,3,5,6,2,5,3,2,5,3,4,5,2,4,1,2,5,1,0,1,4,0, 7,11,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,1,6,5,4,6, 7,11,0,4,3,0,2,3,5,2,6,5,2,6,4,2,1,4,3,1,1,0,2,1, 7,11,5,0,0,1,3,0,5,3,2,5,6,2,4,6,5,4,1,5,6,1,3,6, 7,11,0,1,2,0,3,2,4,3,1,4,2,1,5,1,2,5,6,4,6,2,3,6, 7,11,3,2,6,3,5,6,0,5,2,0,1,2,0,1,5,1,2,5,4,2,6,4, 7,11,0,1,1,2,2,3,3,4,0,4,4,1,5,3,5,1,6,4,6,5,3,6, 7,11,0,1,1,2,2,3,3,4,0,4,5,0,5,3,6,0,6,2,6,3,5,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,5,1,6,5,4,6,3,6,1,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,5,1,6,5,1,6,2,6,4,2, 7,11,0,1,1,2,2,3,0,3,4,0,4,3,4,2,6,1,6,4,5,2,3,5, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,5,1,6,1,4,6,6,5,2,6, 7,11,0,3,0,6,1,2,1,5,2,4,2,6,3,4,3,5,4,5,4,6,5,6, 7,11,5,1,6,5,4,6,3,4,2,3,0,2,1,0,5,0,6,0,2,6,4,2, 7,11,1,0,2,1,3,2,4,3,0,4,5,2,3,5,6,0,6,5,6,2,3,6, 7,11,0,3,4,0,2,4,3,2,1,3,4,1,5,1,6,0,6,1,5,3,4,5, 7,11,0,5,0,6,1,3,1,4,2,3,2,5,2,6,3,4,4,5,4,6,5,6, 7,11,0,2,1,0,2,1,0,3,3,1,5,4,5,3,6,4,6,2,6,0,1,6, 7,11,4,1,5,4,2,5,1,2,0,1,5,0,6,5,3,6,2,3,0,2,4,0, 7,11,0,1,2,0,3,2,4,3,1,4,6,1,6,2,5,1,3,5,5,2,4,5, 7,11,0,5,0,6,1,4,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,6,3,6,4,6,2,2,0,4,0, 7,11,0,2,1,0,2,1,0,3,3,1,5,4,5,3,6,4,6,2,4,0,1,4, 7,11,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,6,2,0,6,1,6, 7,11,0,3,4,0,1,4,3,1,4,3,0,1,2,4,6,2,5,6,2,5,1,2, 7,11,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3,6,1,6,5, 7,11,4,1,5,4,2,5,1,2,0,1,4,0,5,0,6,5,3,6,2,3,5,3, 7,11,0,1,1,2,2,3,0,3,4,0,4,3,4,2,5,1,5,4,6,5,4,6, 7,11,0,1,1,2,2,3,5,4,0,4,5,0,5,1,5,2,5,3,6,3,6,4, 7,11,0,4,3,0,1,3,4,1,3,4,5,1,6,5,1,6,2,1,5,2,6,2, 7,11,4,1,0,4,3,0,2,5,5,4,6,5,2,6,1,2,3,1,6,3,2,3, 7,11,0,1,1,2,2,3,0,3,4,0,4,2,5,4,5,3,6,0,6,5,3,6, 7,11,5,2,2,4,5,3,4,1,5,4,0,1,3,0,0,2,6,2,6,3,0,6, 7,11,0,1,1,2,2,3,3,4,0,4,5,0,5,4,5,2,5,3,6,1,0,6, 7,11,0,3,0,4,1,2,1,5,1,6,2,4,2,6,3,5,3,6,4,5,5,6, 7,11,4,0,3,4,5,3,0,5,1,0,2,1,3,2,4,1,5,2,6,4,5,6, 7,11,2,3,4,2,0,4,5,0,1,5,4,1,3,4,5,3,1,0,6,5,6,2, 7,11,4,1,0,4,3,0,4,3,5,4,6,5,2,6,1,2,3,1,6,3,2,5, 7,11,0,3,4,0,2,4,3,2,1,3,0,1,6,0,5,6,2,5,1,5,4,1, 7,11,0,3,0,4,1,4,1,5,1,6,2,3,2,5,2,6,3,6,4,5,5,6, 7,11,0,1,1,2,2,3,0,3,4,0,4,3,4,2,5,1,5,4,6,1,5,6, 7,11,4,1,5,4,6,5,3,6,2,3,1,2,0,1,5,0,4,0,5,2,6,2, 7,11,0,1,1,2,2,3,3,4,0,4,4,2,3,5,4,5,3,0,6,5,6,1, 7,11,0,4,1,0,4,1,3,4,2,3,1,2,6,1,5,6,3,5,5,4,2,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,5,1,4,2,6,2,3,6,4,6, 7,11,0,1,1,2,2,3,3,4,4,5,0,5,6,2,1,6,5,2,4,1,0,3, 7,11,0,3,0,4,1,2,1,5,1,6,2,5,2,6,3,5,3,6,4,5,4,6, 7,11,0,1,1,2,2,3,5,4,0,4,5,3,5,1,6,3,6,4,4,2,0,3, 7,11,0,4,0,5,0,6,1,3,1,5,1,6,2,3,2,4,2,6,3,6,4,5, 7,11,4,3,2,4,3,2,0,3,2,1,5,4,5,0,6,4,6,1,1,5,0,6, 7,11,6,4,3,6,1,3,4,1,0,4,2,0,3,2,0,1,5,0,6,5,5,2, 7,11,6,1,2,6,1,2,0,1,3,0,4,3,5,4,3,5,2,0,4,0,5,6, 7,12,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,4,3,0,2, 7,12,3,6,1,3,2,1,0,2,5,0,6,5,2,6,5,1,0,3,1,6,0,1,0,6, 7,12,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,5,1,2,4, 7,12,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,1,4,0,1,2,3, 7,12,0,1,1,2,0,2,3,2,3,1,4,0,2,4,5,1,0,5,4,5,3,4,5,3, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,2,1,5,6,1, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,2,4,6,5,3, 7,12,0,1,2,4,0,2,2,1,3,1,3,2,4,1,5,1,5,2,5,3,0,3,1,6, 7,12,0,1,2,4,0,2,2,1,3,1,3,2,4,1,5,1,5,2,5,3,0,3,3,6, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0,4,5,4,6, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0,4,5,6,1, 7,12,0,1,2,4,0,2,2,1,3,1,3,2,4,1,5,1,5,2,5,3,0,3,0,6, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0,4,5,0,6, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0,4,5,2,6, 7,12,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,0,2,1,6, 7,12,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,0,2,4,6, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,0,2,6,1,6,5, 7,12,1,3,2,1,0,2,5,0,4,5,3,4,5,1,5,3,2,5,1,0,4,1,1,6, 7,12,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,3,5,0,3,5,6, 7,12,3,6,1,3,2,1,0,2,5,0,6,5,2,6,5,1,0,3,1,6,0,1,1,4, 7,12,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,3,5,0,3,3,6, 7,12,0,1,2,4,0,2,2,1,3,1,3,2,4,1,5,1,5,2,5,3,0,3,4,6, 7,12,1,3,2,1,0,2,5,0,4,5,3,4,5,1,5,3,2,5,1,0,4,1,2,6, 7,12,3,6,1,3,2,1,0,2,5,0,6,5,2,6,5,1,0,3,1,6,0,1,0,4, 7,12,1,3,4,1,3,4,2,3,0,2,4,0,5,4,2,5,4,2,0,5,1,5,3,6, 7,12,1,3,4,1,3,4,2,3,0,2,4,0,5,4,2,5,4,2,0,5,1,5,0,6, 7,12,0,1,1,2,2,3,3,4,0,4,1,3,4,1,2,4,0,3,5,0,4,5,5,6, 7,12,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,0,3,5,6, 7,12,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2,4,6, 7,12,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2,0,6, 7,12,3,6,1,3,2,1,0,2,5,0,6,5,2,6,5,1,0,3,1,6,0,1,4,5, 7,12,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2,3,6, 7,12,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,0,3,0,6, 7,12,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,0,2,5,6, 7,12,0,3,6,0,5,6,3,5,1,3,6,1,4,6,3,4,2,3,6,2,3,6,5,4, 7,12,0,1,4,0,5,4,1,5,4,1,2,4,1,2,6,1,4,6,2,6,3,2,4,3, 7,12,4,1,3,2,3,0,4,2,5,1,5,0,3,5,4,3,5,4,6,5,3,6,4,6, 7,12,0,1,1,2,0,2,3,0,3,1,3,2,4,2,3,4,5,3,0,5,6,3,1,6, 7,12,0,1,1,2,0,2,3,0,3,1,3,2,6,3,0,6,5,0,1,5,4,1,2,4, 7,12,6,2,5,6,3,5,2,3,1,2,4,1,5,4,2,5,4,2,0,4,1,0,5,1, 7,12,5,4,6,5,3,6,4,3,0,4,3,0,1,3,0,1,4,1,3,5,2,3,4,2, 7,12,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,5,0,1,5,6,1,0,6, 7,12,1,2,0,1,2,0,3,2,0,3,1,3,4,2,0,4,5,4,2,5,6,2,1,6, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,2,4,5,6,4,3,6, 7,12,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,5,0,1,5,6,1,2,6, 7,12,1,2,0,1,2,0,3,2,0,3,1,3,4,2,0,4,6,4,2,6,5,2,4,5, 7,12,1,0,2,1,0,2,3,0,4,3,0,4,5,0,3,5,4,5,6,4,3,6,6,0, 7,12,0,1,1,2,0,2,3,0,3,1,3,2,4,1,0,4,5,0,2,5,6,5,2,6, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,6,4,0,6,5,0,3,5, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,1,5,6,1,0,6,5,0,4,5, 7,12,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,6,1,0,6,5,0,2,5, 7,12,5,4,3,5,4,3,6,4,3,6,2,3,4,2,0,4,3,0,1,0,2,1,6,2, 7,12,4,1,3,2,3,0,4,2,5,1,5,0,3,5,4,3,5,4,6,5,0,6,3,6, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,6,1,0,6,5,0,1,5, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,3,1,5,1,6,5,4,6, 7,12,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,5,4,3,5,6,3,4,6, 7,12,1,0,2,1,3,2,0,3,4,3,1,4,4,0,5,4,0,5,3,5,6,0,1,6, 7,12,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,6,2,4,6,5,0,1,5, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,5,4,2,5,1,5,6,1,5,6, 7,12,0,2,1,0,2,1,0,3,3,1,4,2,5,3,6,1,6,4,4,0,3,4,1,5, 7,12,0,1,1,2,0,2,3,0,3,2,4,3,4,1,0,4,5,2,5,4,6,0,3,6, 7,12,5,0,2,5,6,2,3,6,2,3,1,2,0,1,4,0,1,4,5,1,6,5,0,2, 7,12,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,6,4,2,6,5,2,3,5, 7,12,0,2,1,0,5,1,3,5,6,3,2,6,4,2,3,4,5,4,2,5,1,2,4,1, 7,12,0,2,1,0,2,1,0,3,3,1,4,0,1,4,4,2,3,4,5,4,6,5,3,6, 7,12,0,1,1,2,0,2,3,0,3,1,3,2,6,1,2,6,4,6,3,4,5,3,6,5, 7,12,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,5,0,6,5,0,6,3,4, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,1,2,5,6,0,3,6, 7,12,0,1,1,2,2,3,3,4,0,4,5,3,5,1,5,4,6,3,4,6,1,6,6,5, 7,12,0,5,0,6,1,3,1,4,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6, 7,12,0,1,1,2,2,3,3,4,0,4,5,2,6,5,0,6,6,2,0,5,1,5,6,1, 7,12,0,1,6,5,2,3,3,4,4,5,0,5,6,0,6,1,6,2,6,3,6,4,5,1, 7,12,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,5,3,5,4,6,1,5,6, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,1,4,5,6,4,5,6, 7,12,2,1,0,2,5,0,4,5,3,4,5,1,5,3,2,5,1,0,4,1,6,3,6,1, 7,12,0,4,0,6,1,3,1,5,1,6,2,3,2,5,2,6,3,5,4,5,4,6,5,6, 7,12,0,5,0,6,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,6,4,5,5,6, 7,12,0,1,2,4,0,2,2,1,4,6,4,1,5,1,5,2,5,3,0,3,6,1,2,6, 7,12,0,1,1,2,2,3,5,4,0,4,5,3,5,1,6,3,6,4,4,2,0,3,4,3, 7,12,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,6,1,6,5, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,1,6,5,0,6,1,6, 7,12,0,3,4,0,2,4,3,2,1,3,4,1,1,0,5,1,5,2,6,1,2,6,4,6, 7,12,0,1,1,2,2,3,0,3,4,2,5,0,5,4,4,1,1,6,5,3,1,5,6,2, 7,12,0,1,1,2,2,3,0,3,4,2,5,0,5,4,4,1,1,5,5,3,6,1,4,6, 7,12,0,1,1,2,2,3,0,3,4,2,5,0,5,4,4,1,1,5,5,3,6,1,0,6, 7,12,4,3,1,2,0,1,0,3,4,0,6,4,4,2,5,4,6,2,6,3,3,2,5,1, 7,12,2,3,4,2,0,4,6,0,6,5,4,5,1,3,5,1,0,5,6,1,3,6,5,3, 7,12,0,3,0,5,1,2,1,5,1,6,2,4,2,6,3,4,3,6,4,5,4,6,5,6, 7,12,0,3,0,6,1,4,1,5,1,6,2,3,2,4,2,5,3,6,4,5,4,6,5,6, 7,12,0,1,1,2,2,3,4,5,0,4,4,3,5,3,6,1,3,6,6,2,0,6,4,6, 7,12,0,1,2,4,0,2,6,1,3,1,3,2,4,1,5,1,5,2,5,3,0,3,4,6, 7,12,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,6,3,1,6, 7,12,4,3,1,2,5,0,0,3,4,0,4,1,4,2,5,1,6,2,6,3,3,2,6,4, 7,12,2,3,4,2,5,4,0,5,6,0,1,6,3,1,6,3,5,3,1,5,4,0,3,0, 7,12,0,3,0,5,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,6,4,6,5,6, 7,12,3,2,1,2,5,0,0,3,4,0,4,1,6,4,5,1,6,2,6,3,6,1,0,6, 7,12,0,5,0,6,1,3,1,4,1,6,2,3,2,4,2,6,3,5,4,5,4,6,5,6, 7,12,0,3,0,5,1,2,1,4,1,6,2,4,2,6,3,5,3,6,4,5,4,6,5,6, 7,12,0,3,0,6,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,4,6,5,6, 7,12,0,5,0,6,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,6, 7,12,0,1,1,2,2,3,0,3,4,2,5,0,5,4,4,1,3,4,5,3,2,6,6,1, 7,12,0,1,1,2,2,3,0,3,4,2,5,0,5,4,4,1,3,4,5,3,6,1,6,5, 7,12,0,2,0,6,1,4,1,5,1,6,2,3,2,5,3,4,3,5,3,6,4,5,4,6, 7,12,0,5,0,6,1,3,1,4,1,6,2,3,2,4,2,5,3,4,3,6,4,5,5,6, 7,12,0,1,1,2,2,3,0,3,4,1,2,4,5,2,0,5,4,3,6,5,6,4,3,5, 7,12,0,2,0,6,1,2,1,4,1,5,2,3,3,4,3,5,3,6,4,5,4,6,5,6, 7,12,0,2,0,6,1,3,1,4,1,5,2,4,2,5,3,4,3,5,3,6,4,6,5,6, 7,12,0,2,0,6,1,3,1,4,1,5,2,5,2,6,3,4,3,5,3,6,4,5,4,6, 7,12,0,5,0,6,1,3,1,4,1,6,2,3,2,4,2,6,3,4,3,5,4,5,5,6, 7,12,0,5,0,6,1,2,1,5,1,6,2,3,2,4,3,4,3,5,3,6,4,5,4,6, 7,12,3,0,2,3,4,2,0,4,5,1,5,2,6,1,6,0,3,6,5,3,4,5,6,4, 7,12,0,5,0,6,1,2,1,3,1,4,2,3,2,4,3,5,3,6,4,5,4,6,5,6, 7,12,0,1,0,2,1,5,1,6,2,3,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,12,3,0,2,3,4,2,0,4,5,1,5,2,6,1,6,0,3,6,5,3,6,4,1,3, 7,12,0,4,0,5,0,6,1,3,1,5,1,6,2,3,2,4,2,5,3,6,4,6,5,6, 7,12,2,3,4,2,5,2,4,1,6,0,3,0,3,1,6,3,5,6,1,5,4,0,3,4, 7,12,2,3,4,2,4,1,2,5,6,0,6,4,3,1,6,3,0,3,1,5,4,0,5,3, 7,12,0,4,0,5,0,6,1,2,1,3,1,6,2,3,2,6,3,5,4,5,4,6,5,6, 7,12,3,0,2,3,4,2,0,4,5,1,5,2,6,1,6,0,3,6,1,3,6,4,5,4, 7,12,6,3,1,2,2,3,0,3,4,2,5,0,0,6,4,1,3,4,6,5,5,1,0,4, 7,12,0,3,0,5,0,6,1,2,1,5,1,6,2,4,2,6,3,4,3,5,4,5,4,6, 7,12,0,3,0,5,0,6,1,2,1,4,1,6,2,3,2,5,3,4,4,5,4,6,5,6, 7,12,0,3,0,5,0,6,1,2,1,5,1,6,2,3,2,4,3,4,4,5,4,6,5,6, 7,12,0,4,3,0,1,3,4,1,1,0,4,5,2,4,6,2,5,6,2,5,3,2,6,3, 7,12,0,1,1,2,2,3,0,3,4,0,4,1,5,2,5,4,6,4,6,3,6,2,5,3, 7,12,0,4,0,5,0,6,1,3,1,5,1,6,2,3,2,5,2,6,3,4,4,5,4,6, 7,12,3,0,4,2,3,1,4,0,5,2,5,1,4,3,5,4,3,5,6,1,0,6,2,6, 7,12,1,0,4,1,0,4,5,0,6,5,1,6,3,4,5,3,2,3,5,2,6,3,2,6, 7,12,0,1,2,0,2,3,3,4,0,4,0,5,6,1,4,6,6,5,2,6,3,1,5,3, 7,12,0,1,1,2,2,3,3,4,4,5,0,5,6,0,6,1,6,2,6,3,6,4,6,5, 7,12,3,6,1,2,0,6,0,3,4,0,4,1,4,2,4,3,5,1,2,5,4,5,6,4, 7,13,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,13,0,6,1,4,1,5,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,4,3,0,2,6,4, 7,13,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,1,4,0,1,0,4,1,6, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,2,3,2,4,2,5,2,6,3,6,4,5,5,6, 7,13,0,6,1,4,1,5,1,6,2,3,2,4,2,5,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,0,3,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,2,5,2,6,3,4,3,5,3,6,4,5,4,6, 7,13,0,6,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,5,6, 7,13,0,6,1,3,1,4,1,5,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,13,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2,4,5,1,6, 7,13,0,1,1,2,2,3,4,5,0,4,1,3,4,1,2,4,0,3,5,3,4,3,0,2,5,6, 7,13,0,5,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,6,4,6,5,6, 7,13,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6, 7,13,5,6,0,5,6,0,4,6,5,4,1,5,6,1,3,6,5,3,2,5,6,2,1,0,2,1, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,2,3,2,4,2,6, 7,13,3,4,0,3,4,0,1,4,3,1,2,3,4,2,1,0,2,1,6,0,3,6,5,3,4,5, 7,13,3,4,0,3,4,0,1,4,3,1,2,3,4,2,1,0,2,1,6,0,3,6,5,3,0,5, 7,13,3,4,0,3,4,0,1,4,3,1,2,3,4,2,1,0,2,1,6,4,0,6,5,0,3,5, 7,13,0,5,0,6,1,3,1,4,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,2,3,2,4,3,5,4,6, 7,13,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,5,1,6,5,1,6, 7,13,0,4,0,6,1,3,1,5,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,13,0,5,0,6,1,4,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,5,6, 7,13,0,5,0,6,1,3,1,4,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,13,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,5,3,6,5,4,6, 7,13,0,5,0,6,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6, 7,13,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,6,1,6,5,5,1, 7,13,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,4,0,5,6,0,3,6,6,4, 7,13,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,6,1,5,1,2,6, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,2,3,2,4,5,6, 7,13,1,0,2,1,4,2,1,4,3,1,0,3,2,3,5,2,1,5,0,5,6,0,1,6,2,6, 7,13,2,5,6,2,5,6,4,5,3,4,0,3,4,0,1,4,3,1,6,3,2,1,4,2,3,2, 7,13,0,3,0,6,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,4,5,4,6,5,6, 7,13,2,4,3,2,1,3,4,1,0,4,3,0,6,3,1,6,5,1,4,5,6,4,3,5,1,2, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,2,4,2,5,3,5,3,6, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,2,3,2,4,3,5,5,6, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,2,3,2,5,3,6,4,5, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,6,2,4,2,5,3,4,3,5, 7,13,0,2,0,6,1,4,1,5,1,6,2,3,2,5,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,2,5,1,2,5,1,4,5,3,4,0,3,4,0,3,2,4,2,1,3,6,3,2,6,1,6, 7,13,0,4,0,6,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,5,3,6,4,5,5,6, 7,13,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,0,4,6,1,4,6, 7,13,2,3,0,2,3,0,4,3,1,4,5,1,4,5,1,0,5,2,5,0,6,5,1,6,4,0, 7,13,0,1,1,2,0,2,3,0,1,3,3,2,4,0,2,5,3,4,5,3,0,5,6,4,6,1, 7,13,2,3,0,2,3,0,4,3,1,4,5,1,4,5,1,0,5,2,6,2,5,6,0,5,1,2, 7,13,5,4,6,2,6,4,4,3,5,0,3,1,3,2,6,3,5,6,4,0,1,4,5,1,0,3, 7,13,0,2,0,6,1,3,1,4,1,5,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,1,5,4,1,0,4,5,0,2,5,4,2,3,4,5,3,0,1,2,0,3,2,6,5,6,4, 7,13,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,0,4,0,6,4,6, 7,13,0,4,3,0,2,3,4,2,1,4,3,1,1,0,5,0,4,5,1,5,6,1,0,6,3,6, 7,13,0,5,0,6,1,2,1,5,1,6,2,3,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,1,3,2,1,0,2,5,0,4,5,3,4,4,1,5,3,2,5,1,0,6,3,4,6,5,1, 7,13,5,2,0,2,3,0,4,3,1,4,5,1,4,5,1,0,6,2,6,3,1,2,3,1,4,0, 7,13,1,0,2,1,0,2,0,3,3,2,6,2,1,6,5,1,6,5,4,6,5,4,0,5,4,0, 7,13,0,5,0,6,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,6, 7,13,0,5,0,6,1,3,1,4,1,6,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6, 7,13,0,1,1,2,4,1,3,1,0,4,2,3,0,3,2,0,5,0,6,5,4,6,3,5,4,2, 7,13,0,5,0,6,1,2,1,3,1,4,2,4,2,5,2,6,3,4,3,5,3,6,4,6,5,6, 7,13,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,0,4,2,3,6,5,0,6, 7,13,5,2,0,2,3,0,4,3,1,4,5,1,4,5,1,0,6,2,6,3,2,3,5,0,4,0, 7,13,0,1,1,2,2,3,5,4,0,4,5,0,5,1,5,2,5,3,6,3,6,4,4,2,0,3, 7,13,0,1,0,6,1,4,1,5,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,6,5,6, 7,13,0,1,0,6,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6, 7,13,0,1,1,2,2,3,3,4,0,4,6,2,0,6,5,0,2,5,5,3,4,5,6,4,3,6, 7,13,0,1,1,2,2,3,3,4,0,4,6,5,0,6,0,2,2,5,5,3,4,5,6,4,3,6, 7,13,3,4,0,3,4,0,3,6,3,1,2,3,4,2,1,0,2,1,5,2,3,5,6,2,5,6, 7,13,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,5,3,5,4,6,5,3,6,4,6, 7,13,1,0,2,1,6,0,1,4,3,1,0,3,2,3,1,6,1,5,2,6,4,3,5,4,6,5, 7,13,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,2,5,2,6,3,5,4,6, 7,13,0,6,1,2,2,3,0,3,4,2,5,0,6,3,4,1,3,4,6,5,1,5,3,5,1,3, 7,13,0,1,1,2,2,3,0,3,4,0,4,1,6,4,4,3,5,4,5,2,6,0,3,6,3,5, 7,13,0,1,1,2,2,3,3,6,0,4,6,5,0,6,6,4,2,5,5,3,4,5,1,5,6,1, 7,13,0,4,0,5,0,6,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,6,4,5,5,6, 7,13,0,4,0,5,0,6,1,3,1,5,1,6,2,3,2,5,2,6,3,4,4,5,4,6,5,6, 7,13,2,3,5,2,6,5,3,6,4,3,5,4,0,5,2,0,1,2,0,1,5,1,4,2,6,4, 7,13,2,1,0,5,6,0,4,6,5,4,1,5,6,1,3,6,5,3,2,5,6,2,1,0,3,4, 7,13,0,1,2,0,2,3,3,4,0,4,0,5,6,1,4,6,6,5,2,6,3,1,5,3,6,0, 7,13,0,4,0,5,0,6,1,2,1,5,1,6,2,3,2,6,3,4,3,5,3,6,4,5,4,6, 7,13,2,3,0,2,3,0,4,3,4,6,5,1,4,5,3,1,5,2,6,0,6,1,2,1,4,1, 7,13,0,1,1,2,2,3,0,3,4,0,4,1,4,3,5,4,5,2,5,1,6,5,1,6,2,6, 7,13,0,4,0,5,0,6,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,5,3,6,4,6, 7,13,3,0,2,3,4,2,0,4,5,1,5,2,6,1,6,0,3,6,1,3,6,4,5,4,5,3, 7,13,0,4,0,5,0,6,1,2,1,4,1,5,2,3,2,6,3,4,3,5,3,6,4,6,5,6, 7,13,0,4,0,5,0,6,1,3,1,5,1,6,2,3,2,4,2,5,3,4,3,6,4,6,5,6, 7,13,0,2,0,5,0,6,1,2,1,4,1,6,2,3,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,3,4,5,0,3,5,0,6,6,4,2,3,4,2,1,0,2,1,1,6,5,1,2,5,6,2, 7,13,0,2,0,5,0,6,1,2,1,3,1,4,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,13,3,0,2,3,4,2,0,4,5,3,5,2,5,4,1,3,1,0,4,1,6,0,3,6,1,6, 7,13,2,3,0,2,3,0,4,3,1,4,5,1,4,5,6,0,1,6,6,3,4,6,1,2,5,0, 7,13,0,4,0,5,0,6,1,2,1,3,1,6,2,3,2,5,2,6,3,4,3,5,4,5,4,6, 7,13,0,4,0,5,0,6,1,2,1,3,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5, 7,13,0,1,1,2,2,3,0,3,4,0,4,1,5,3,5,4,6,4,6,2,6,5,5,2,3,6, 7,13,0,1,0,5,0,6,1,3,1,4,2,3,2,4,2,5,2,6,3,4,3,6,4,5,5,6, 7,13,0,1,0,5,0,6,1,3,1,4,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6, 7,13,0,1,2,0,2,3,3,4,0,4,0,5,6,1,4,6,6,5,2,6,3,1,5,3,2,1, 7,14,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,1,3,2,0,4,0,5,3, 7,14,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,1,4,0,1,2,3,0,4,1,6, 7,14,0,6,1,3,1,4,1,5,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,2,4,2,5,2,6,3,4,3,5,3,6,4,6,5,6, 7,14,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,4,5,4,6,5,6, 7,14,0,3,1,2,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,14,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,3,6,5,3,4,5,6,4, 7,14,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,6,2,1,6,5,1,0,5, 7,14,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,5,2,3,5,6,4,0,6, 7,14,3,4,1,3,2,1,0,2,5,0,4,5,2,4,5,1,0,3,1,4,0,1,0,4,6,4,0,6, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,3,4,3,5,4,6, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,6,2,5,2,6,3,4,3,5,4,5,4,6,5,6, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,4,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,14,3,4,1,3,2,1,0,2,5,0,4,5,0,1,5,1,0,4,1,4,5,3,2,5,6,4,0,6, 7,14,1,3,2,1,0,2,5,0,4,5,3,4,5,3,2,5,1,0,4,1,6,1,5,1,2,6,0,4, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,6,2,5,2,6,3,4,3,5,3,6,4,5,4,6, 7,14,2,3,4,2,6,3,4,0,6,0,3,4,3,1,5,4,5,0,0,3,1,5,5,3,6,4,6,1, 7,14,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,5,3,6,5,4,6,5,4, 7,14,3,1,1,4,2,3,3,4,0,4,1,5,0,1,0,2,2,5,5,3,4,5,1,2,6,2,5,6, 7,14,0,3,0,6,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,4,5,4,6,5,6, 7,14,0,3,0,6,1,2,1,4,1,5,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,14,0,5,0,6,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6, 7,14,0,1,0,6,1,4,1,5,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,14,0,5,0,6,1,2,1,3,1,4,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,14,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,4,6,5,2,1,5,0,5,6,3, 7,14,0,1,1,2,2,3,3,4,0,4,5,0,5,1,5,2,5,3,5,4,4,2,3,0,6,1,5,6, 7,14,0,4,0,6,1,2,1,3,1,5,1,6,2,3,2,5,2,6,3,4,3,5,4,5,4,6,5,6, 7,14,0,4,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,5,3,6,4,5,5,6, 7,14,0,5,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,6,4,5,5,6, 7,14,2,3,0,2,3,0,4,3,1,4,5,1,1,2,5,2,4,0,3,1,5,0,6,5,6,4,0,1, 7,14,2,3,0,2,3,0,4,3,1,4,5,1,4,5,5,2,6,0,6,1,5,0,1,2,3,1,4,0, 7,14,5,6,0,5,6,0,4,6,5,4,1,5,6,1,3,6,5,3,2,5,6,2,1,0,2,1,3,4, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,5,1,6,2,5,2,6,3,4,3,6,4,5,5,6, 7,14,0,1,2,0,2,3,3,4,0,4,0,5,6,1,4,6,6,5,2,6,3,1,5,3,6,0,3,6, 7,14,3,1,4,2,4,5,4,0,1,4,0,3,5,0,5,2,6,1,3,6,6,0,5,6,6,2,4,6, 7,14,0,4,3,0,2,3,4,2,1,4,3,1,1,0,2,1,5,4,1,5,6,1,3,6,0,5,6,0, 7,14,3,4,4,2,1,5,4,0,1,4,5,3,3,0,5,2,6,4,0,6,3,6,2,6,5,6,1,6, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,4,1,5,2,3,2,6,3,6,4,5,4,6,5,6, 7,14,2,3,4,2,6,3,4,0,4,5,3,4,3,1,5,2,1,6,5,6,6,0,5,3,6,4,0,1, 7,14,0,4,0,5,0,6,1,3,1,5,1,6,2,3,2,4,2,6,3,4,3,5,4,5,4,6,5,6, 7,14,0,4,0,5,0,6,1,2,1,5,1,6,2,3,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,14,2,3,4,2,6,3,4,0,1,4,6,0,3,1,5,2,4,5,5,6,1,5,5,3,6,4,3,0, 7,14,3,1,4,2,0,3,4,0,1,4,5,3,5,0,5,2,6,4,1,6,6,3,0,6,6,5,2,6, 7,14,0,1,4,2,3,0,4,0,4,5,5,3,1,3,5,2,6,4,2,6,6,5,3,6,6,1,0,6, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,6,2,5,3,4,3,5,3,6,4,5,4,6, 7,14,0,1,0,2,0,3,0,4,0,5,0,6,1,5,1,6,2,3,2,4,3,5,3,6,4,5,4,6, 7,14,2,3,4,2,6,3,4,0,1,4,6,1,3,1,5,2,5,0,5,6,4,5,5,3,6,0,3,0, 7,14,2,3,4,2,3,0,4,0,4,5,3,4,3,1,5,2,0,1,5,6,6,0,5,3,6,4,1,6, 7,14,0,4,0,5,0,6,1,3,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,6,4,5,5,6, 7,14,0,3,0,4,0,6,1,2,1,4,1,5,2,3,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,14,0,4,0,5,0,6,1,2,1,3,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5,5,6, 7,14,0,1,0,5,0,6,1,4,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,5,6, 7,14,0,1,0,4,0,6,1,3,1,5,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,14,2,3,4,2,6,3,4,0,1,4,4,5,3,1,5,2,5,0,6,1,0,6,5,3,6,4,3,0, 7,14,0,1,0,5,0,6,1,3,1,4,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,14,0,4,0,5,0,6,1,2,1,3,1,4,2,3,2,5,2,6,3,5,3,6,4,5,4,6,5,6, 7,14,0,4,0,5,0,6,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,5,6, 7,14,2,3,4,2,6,3,4,0,4,5,3,5,3,1,5,2,3,0,1,4,6,0,1,6,6,4,0,1, 7,14,0,4,0,5,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,5,3,6,4,6, 7,14,0,4,0,5,0,6,1,2,1,3,1,5,1,6,2,3,2,5,2,6,3,4,3,6,4,5,4,6, 7,14,0,4,0,5,0,6,1,2,1,3,1,5,1,6,2,3,2,4,2,6,3,4,3,5,4,6,5,6, 7,14,0,3,0,4,0,5,1,2,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,6,4,6,5,6, 7,14,0,3,0,4,0,5,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,6,4,6,5,6, 7,14,0,1,1,2,2,3,3,4,4,5,5,6,0,6,0,2,5,0,3,5,1,3,6,1,4,6,2,4, 7,14,0,3,0,4,0,5,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,6,4,5, 7,15,0,1,0,2,0,3,0,4,0,5,1,2,1,3,1,4,1,5,2,3,2,4,2,5,3,4,3,5,4,5, 7,15,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,1,3,2,0,4,0,5,3,1,6, 7,15,0,1,1,2,2,3,3,4,4,5,0,5,2,4,5,2,1,5,1,4,1,3,2,0,4,0,5,3,0,6, 7,15,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,3,4,3,5,3,6,5,6, 7,15,0,1,0,2,0,3,0,4,0,5,0,6,1,5,1,6,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,15,3,4,4,5,0,3,0,4,0,5,0,6,3,6,4,6,5,6,1,5,3,5,2,3,2,4,1,6,0,1, 7,15,3,4,4,5,0,3,0,4,0,5,0,6,3,6,1,3,1,4,1,5,3,5,2,3,2,4,6,1,4,6, 7,15,0,1,1,2,2,3,0,3,4,0,4,1,4,2,4,3,5,1,0,5,5,2,3,5,4,5,6,1,5,6, 7,15,4,3,4,5,5,3,0,1,0,5,0,3,2,4,1,5,1,3,6,5,3,6,6,0,1,6,6,2,4,6, 7,15,3,4,4,5,5,6,0,4,0,5,0,6,3,6,1,3,4,6,1,5,3,5,2,3,2,4,1,6,0,1, 7,15,0,2,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,3,4,3,5,3,6,4,5,4,6,5,6, 7,15,6,1,4,5,0,3,0,4,0,5,0,6,3,6,1,3,1,4,1,5,3,5,2,3,2,4,5,6,4,6, 7,15,3,4,4,5,0,3,0,4,0,5,4,6,3,6,1,3,1,4,1,5,3,5,2,3,2,4,5,6,5,2, 7,15,3,4,4,5,0,3,0,4,6,0,4,6,3,6,1,3,1,4,1,5,3,5,2,3,2,4,5,6,5,2, 7,15,0,1,1,2,0,2,3,0,1,3,2,3,5,1,3,5,4,3,2,4,6,2,3,6,6,1,0,5,4,0, 7,15,0,1,2,0,3,2,4,3,1,4,3,1,4,2,0,4,3,0,6,3,4,6,5,4,3,5,6,2,5,1, 7,15,0,1,0,2,0,3,0,4,1,2,1,3,1,4,2,3,2,4,3,4,5,3,4,5,6,4,5,6,6,3, 7,15,0,1,1,2,2,3,0,3,4,0,4,3,4,2,6,1,2,6,5,2,4,5,6,4,0,6,6,5,3,6, 7,15,0,1,5,3,1,3,0,4,3,0,4,3,2,4,5,2,4,5,6,4,2,6,6,5,3,6,6,1,0,6, 7,15,5,2,4,5,3,1,0,4,0,5,0,3,2,4,1,5,1,4,6,3,1,6,6,0,5,6,6,2,4,6, 7,15,0,1,1,2,2,3,3,4,4,5,0,5,0,3,2,0,3,1,6,4,5,6,6,3,0,6,6,2,1,6, 7,15,5,2,3,0,5,3,0,4,0,5,4,3,2,4,1,5,1,4,6,4,2,6,6,0,5,6,6,3,1,6, 7,15,0,4,0,5,0,6,1,2,1,3,1,6,2,3,2,4,2,5,3,4,3,5,3,6,4,5,4,6,5,6, 7,15,6,1,4,5,0,3,0,4,0,5,4,6,3,6,1,3,1,4,0,6,3,5,2,3,2,4,5,6,5,2, 7,15,3,4,0,1,0,3,0,4,0,5,4,6,3,6,1,3,1,4,6,0,1,6,2,3,2,4,5,6,5,2, 7,15,0,1,0,2,0,3,0,4,0,5,0,6,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,6, 7,15,5,2,4,5,5,3,0,4,0,1,1,3,2,4,3,0,1,4,6,4,1,6,6,0,3,6,6,2,5,6, 7,15,5,0,4,3,5,3,5,2,0,1,1,3,2,4,3,0,1,4,6,2,5,6,6,4,3,6,6,0,1,6, 7,15,3,4,4,5,0,3,4,6,0,1,1,6,3,6,1,3,1,4,6,0,0,5,2,3,2,4,5,6,5,2, 7,15,0,2,0,3,0,6,1,3,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,4,5,4,6,5,6, 7,15,0,4,0,5,0,6,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6, 7,15,3,4,5,0,0,3,0,4,4,6,1,6,3,6,1,3,1,4,6,0,1,5,2,3,2,4,5,6,5,2, 7,15,6,4,5,2,0,3,0,4,2,4,1,6,3,6,1,3,1,4,6,0,3,5,2,3,0,1,5,6,4,5, 7,15,0,4,0,5,0,6,1,2,1,3,1,5,1,6,2,3,2,4,2,6,3,4,3,5,4,5,4,6,5,6, 7,15,0,1,0,2,0,3,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,15,2,3,0,2,3,0,4,3,1,4,5,1,4,5,1,0,5,2,6,2,5,6,6,1,0,6,6,4,3,6, 7,15,3,0,3,5,3,4,2,0,2,5,2,4,1,4,1,5,1,0,6,0,1,6,6,5,3,6,6,4,2,6, 7,15,0,3,0,4,0,5,0,6,1,2,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,6,4,5,5,6, 7,15,3,4,6,2,0,3,0,4,5,0,1,6,3,6,1,3,1,4,6,0,4,5,2,3,2,4,5,1,5,2, 7,15,3,4,6,2,0,3,0,4,5,0,5,6,3,6,1,3,1,4,0,1,4,6,2,3,2,4,5,1,5,2, 7,15,0,1,1,2,2,3,3,4,0,4,6,2,1,6,6,0,4,6,5,4,0,5,3,5,6,3,5,2,1,5, 7,16,0,1,0,2,0,3,0,4,0,5,1,2,1,3,1,4,1,5,2,3,2,4,2,5,3,4,3,5,4,5,2,6, 7,16,3,0,4,1,4,3,1,3,4,0,2,5,6,2,5,6,1,5,4,5,3,5,0,5,0,6,3,6,4,6,6,1, 7,16,0,1,0,2,0,3,0,4,0,5,0,6,1,6,2,3,2,4,2,5,3,4,3,5,3,6,4,5,4,6,5,6, 7,16,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6, 7,16,3,4,5,1,0,3,0,4,5,0,4,6,3,6,1,3,1,4,6,0,3,5,2,3,2,4,5,6,4,5,2,5, 7,16,2,4,3,1,3,0,4,3,4,0,5,2,4,5,5,0,3,5,5,1,6,5,1,6,3,6,6,0,4,6,6,2, 7,16,0,1,0,2,0,3,0,4,0,5,0,6,1,5,1,6,2,3,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,16,2,4,4,1,3,0,3,1,4,0,5,2,4,5,5,0,3,5,6,5,1,5,6,1,3,6,4,6,6,2,6,0, 7,16,0,1,0,3,0,5,0,6,1,3,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,16,2,5,0,1,4,5,1,3,5,0,4,3,5,3,2,4,1,4,3,0,6,3,2,6,6,4,5,6,6,1,0,6, 7,16,0,1,0,2,0,3,0,4,0,5,0,6,1,3,1,6,2,4,2,5,2,6,3,4,3,5,4,5,4,6,5,6, 7,16,2,5,5,1,3,1,0,4,5,0,4,3,5,3,2,4,1,4,3,0,6,2,4,6,5,6,6,1,0,6,3,6, 7,16,1,6,0,1,0,3,0,4,5,0,4,6,3,6,1,3,1,4,6,0,3,5,2,3,2,4,5,6,4,5,2,5, 7,16,3,4,5,1,0,3,0,4,5,0,4,6,3,6,1,3,1,4,6,0,1,6,2,3,2,4,5,6,0,1,2,5, 7,16,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,5,2,6,3,4,3,6,4,5, 7,16,0,1,0,2,0,3,0,4,0,5,0,6,1,4,1,5,1,6,2,3,2,5,2,6,3,4,3,6,4,5,5,6, 7,16,0,1,0,2,0,3,0,4,0,5,0,6,1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6,5,6, 7,16,2,5,5,1,3,5,0,4,0,1,4,3,3,2,2,4,1,4,0,5,6,4,2,6,6,3,5,6,6,1,0,6, 7,16,5,6,5,1,0,3,0,4,0,1,4,6,3,6,1,3,1,4,6,0,3,5,2,3,2,4,6,2,4,5,2,5, 7,16,3,4,5,1,0,3,0,4,0,1,4,6,3,6,1,3,1,4,6,0,5,0,2,3,2,4,6,2,6,5,2,5, 7,16,5,0,5,1,0,3,0,4,6,1,4,6,3,6,1,3,1,4,6,0,3,5,2,3,2,4,6,2,4,5,2,5, 7,17,0,1,0,2,0,3,0,4,0,5,1,2,1,3,1,4,1,5,2,3,2,4,2,5,3,4,3,5,4,5,6,2,1,6, 7,17,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,4,5,4,6, 7,17,4,0,4,3,0,1,3,0,2,4,3,1,5,3,4,5,5,2,6,5,5,0,1,5,6,1,0,6,6,4,2,6,3,6, 7,17,0,1,5,1,5,3,0,4,5,0,4,3,3,1,2,5,1,4,3,0,2,4,6,2,5,6,6,3,1,6,6,0,4,6, 7,17,3,4,5,1,0,3,0,4,4,5,4,6,3,6,1,3,1,4,0,1,3,5,2,3,2,4,2,5,5,0,5,6,6,2, 7,17,3,2,4,1,0,1,3,0,2,4,4,3,5,1,4,5,5,2,0,5,5,3,6,5,2,6,6,3,0,6,1,6,4,6, 7,17,3,2,4,1,4,0,3,0,2,4,3,1,5,2,4,5,5,0,3,5,5,1,6,5,2,6,6,0,3,6,6,4,1,6, 7,17,3,2,5,1,5,0,0,4,0,1,4,3,5,3,2,5,1,4,3,0,2,4,6,4,5,6,6,2,3,6,6,0,1,6, 7,17,3,2,5,1,5,0,0,4,4,5,0,1,3,1,2,5,1,4,3,0,2,4,6,0,3,6,6,1,5,6,6,2,4,6, 7,17,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,5,3,6,4,5,4,6, 7,18,0,1,0,2,0,3,0,4,0,5,1,2,1,3,1,4,1,5,2,3,2,4,2,5,3,4,3,5,4,5,6,1,0,6,5,6, 7,18,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6, 7,18,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,4,1,5,2,3,2,4,2,5,2,6,3,4,3,6,4,5,4,6,5,6, 7,18,0,1,0,2,0,3,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,18,4,0,4,5,3,0,3,5,2,0,2,5,1,3,1,4,1,5,1,0,2,3,2,4,6,0,5,6,6,1,2,6,6,4,3,6, 7,19,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,3,4,3,5,3,6,4,5,4,6,5,6, 7,19,0,1,0,2,0,3,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,6,4,5,4,6,5,6, 7,20,0,1,0,2,0,3,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, 7,21,0,1,0,2,0,3,0,4,0,5,0,6,1,2,1,3,1,4,1,5,1,6,2,3,2,4,2,5,2,6,3,4,3,5,3,6,4,5,4,6,5,6, }; const long int igraph_i_atlas_edges_pos[]={0, 2, 4, 6, 10, 12, 16, 22, 30, 32, 36, 42, 48, 56, 64, 72, 82, 92, 104, 118, 120, 124, 130, 136, 144, 152, 160, 168, 178, 188, 198, 208, 218, 228, 240, 252, 264, 276, 288, 300, 314, 328, 342, 356, 370, 384, 400, 416, 432, 448, 466, 484, 504, 526, 528, 532, 538, 544, 552, 560, 568, 576, 584, 594, 604, 614, 624, 634, 644, 654, 664, 674, 686, 698, 710, 722, 734, 746, 758, 770, 782, 794, 806, 818, 830, 842, 854, 868, 882, 896, 910, 924, 938, 952, 966, 980, 994, 1008, 1022, 1036, 1050, 1064, 1078, 1092, 1106, 1120, 1134, 1148, 1164, 1180, 1196, 1212, 1228, 1244, 1260, 1276, 1292, 1308, 1324, 1340, 1356, 1372, 1388, 1404, 1420, 1436, 1452, 1468, 1484, 1500, 1516, 1532, 1550, 1568, 1586, 1604, 1622, 1640, 1658, 1676, 1694, 1712, 1730, 1748, 1766, 1784, 1802, 1820, 1838, 1856, 1874, 1892, 1910, 1928, 1946, 1964, 1984, 2004, 2024, 2044, 2064, 2084, 2104, 2124, 2144, 2164, 2184, 2204, 2224, 2244, 2264, 2284, 2304, 2324, 2344, 2364, 2384, 2406, 2428, 2450, 2472, 2494, 2516, 2538, 2560, 2582, 2604, 2626, 2648, 2670, 2692, 2714, 2738, 2762, 2786, 2810, 2834, 2858, 2882, 2906, 2930, 2956, 2982, 3008, 3034, 3060, 3088, 3116, 3146, 3178, 3180, 3184, 3190, 3196, 3204, 3212, 3220, 3228, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316, 3326, 3336, 3348, 3360, 3372, 3384, 3396, 3408, 3420, 3432, 3444, 3456, 3468, 3480, 3492, 3504, 3516, 3528, 3540, 3552, 3564, 3576, 3588, 3602, 3616, 3630, 3644, 3658, 3672, 3686, 3700, 3714, 3728, 3742, 3756, 3770, 3784, 3798, 3812, 3826, 3840, 3854, 3868, 3882, 3896, 3910, 3924, 3938, 3952, 3966, 3980, 3994, 4008, 4022, 4036, 4050, 4064, 4078, 4092, 4106, 4120, 4134, 4148, 4162, 4178, 4194, 4210, 4226, 4242, 4258, 4274, 4290, 4306, 4322, 4338, 4354, 4370, 4386, 4402, 4418, 4434, 4450, 4466, 4482, 4498, 4514, 4530, 4546, 4562, 4578, 4594, 4610, 4626, 4642, 4658, 4674, 4690, 4706, 4722, 4738, 4754, 4770, 4786, 4802, 4818, 4834, 4850, 4866, 4882, 4898, 4914, 4930, 4946, 4962, 4978, 4994, 5010, 5026, 5042, 5058, 5074, 5090, 5106, 5122, 5138, 5154, 5170, 5186, 5202, 5220, 5238, 5256, 5274, 5292, 5310, 5328, 5346, 5364, 5382, 5400, 5418, 5436, 5454, 5472, 5490, 5508, 5526, 5544, 5562, 5580, 5598, 5616, 5634, 5652, 5670, 5688, 5706, 5724, 5742, 5760, 5778, 5796, 5814, 5832, 5850, 5868, 5886, 5904, 5922, 5940, 5958, 5976, 5994, 6012, 6030, 6048, 6066, 6084, 6102, 6120, 6138, 6156, 6174, 6192, 6210, 6228, 6246, 6264, 6282, 6300, 6318, 6336, 6354, 6372, 6390, 6408, 6426, 6444, 6462, 6480, 6498, 6516, 6534, 6552, 6570, 6588, 6606, 6624, 6642, 6660, 6678, 6696, 6714, 6732, 6750, 6768, 6786, 6804, 6822, 6840, 6858, 6876, 6894, 6912, 6930, 6948, 6968, 6988, 7008, 7028, 7048, 7068, 7088, 7108, 7128, 7148, 7168, 7188, 7208, 7228, 7248, 7268, 7288, 7308, 7328, 7348, 7368, 7388, 7408, 7428, 7448, 7468, 7488, 7508, 7528, 7548, 7568, 7588, 7608, 7628, 7648, 7668, 7688, 7708, 7728, 7748, 7768, 7788, 7808, 7828, 7848, 7868, 7888, 7908, 7928, 7948, 7968, 7988, 8008, 8028, 8048, 8068, 8088, 8108, 8128, 8148, 8168, 8188, 8208, 8228, 8248, 8268, 8288, 8308, 8328, 8348, 8368, 8388, 8408, 8428, 8448, 8468, 8488, 8508, 8528, 8548, 8568, 8588, 8608, 8628, 8648, 8668, 8688, 8708, 8728, 8748, 8768, 8788, 8808, 8828, 8848, 8868, 8888, 8908, 8928, 8948, 8968, 8988, 9008, 9028, 9048, 9068, 9088, 9108, 9128, 9148, 9168, 9188, 9208, 9228, 9248, 9268, 9288, 9308, 9328, 9348, 9368, 9388, 9408, 9428, 9448, 9468, 9488, 9508, 9528, 9548, 9568, 9590, 9612, 9634, 9656, 9678, 9700, 9722, 9744, 9766, 9788, 9810, 9832, 9854, 9876, 9898, 9920, 9942, 9964, 9986, 10008, 10030, 10052, 10074, 10096, 10118, 10140, 10162, 10184, 10206, 10228, 10250, 10272, 10294, 10316, 10338, 10360, 10382, 10404, 10426, 10448, 10470, 10492, 10514, 10536, 10558, 10580, 10602, 10624, 10646, 10668, 10690, 10712, 10734, 10756, 10778, 10800, 10822, 10844, 10866, 10888, 10910, 10932, 10954, 10976, 10998, 11020, 11042, 11064, 11086, 11108, 11130, 11152, 11174, 11196, 11218, 11240, 11262, 11284, 11306, 11328, 11350, 11372, 11394, 11416, 11438, 11460, 11482, 11504, 11526, 11548, 11570, 11592, 11614, 11636, 11658, 11680, 11702, 11724, 11746, 11768, 11790, 11812, 11834, 11856, 11878, 11900, 11922, 11944, 11966, 11988, 12010, 12032, 12054, 12076, 12098, 12120, 12142, 12164, 12186, 12208, 12230, 12252, 12274, 12296, 12318, 12340, 12362, 12384, 12406, 12428, 12450, 12472, 12494, 12516, 12538, 12560, 12582, 12604, 12626, 12648, 12670, 12692, 12714, 12736, 12758, 12780, 12802, 12824, 12848, 12872, 12896, 12920, 12944, 12968, 12992, 13016, 13040, 13064, 13088, 13112, 13136, 13160, 13184, 13208, 13232, 13256, 13280, 13304, 13328, 13352, 13376, 13400, 13424, 13448, 13472, 13496, 13520, 13544, 13568, 13592, 13616, 13640, 13664, 13688, 13712, 13736, 13760, 13784, 13808, 13832, 13856, 13880, 13904, 13928, 13952, 13976, 14000, 14024, 14048, 14072, 14096, 14120, 14144, 14168, 14192, 14216, 14240, 14264, 14288, 14312, 14336, 14360, 14384, 14408, 14432, 14456, 14480, 14504, 14528, 14552, 14576, 14600, 14624, 14648, 14672, 14696, 14720, 14744, 14768, 14792, 14816, 14840, 14864, 14888, 14912, 14936, 14960, 14984, 15008, 15032, 15056, 15080, 15104, 15128, 15152, 15176, 15200, 15224, 15248, 15272, 15296, 15320, 15344, 15368, 15392, 15416, 15440, 15464, 15488, 15512, 15536, 15560, 15584, 15608, 15632, 15656, 15680, 15704, 15728, 15752, 15776, 15800, 15824, 15848, 15872, 15896, 15920, 15944, 15968, 15992, 16016, 16040, 16064, 16088, 16112, 16136, 16160, 16184, 16208, 16232, 16256, 16280, 16304, 16328, 16352, 16376, 16402, 16428, 16454, 16480, 16506, 16532, 16558, 16584, 16610, 16636, 16662, 16688, 16714, 16740, 16766, 16792, 16818, 16844, 16870, 16896, 16922, 16948, 16974, 17000, 17026, 17052, 17078, 17104, 17130, 17156, 17182, 17208, 17234, 17260, 17286, 17312, 17338, 17364, 17390, 17416, 17442, 17468, 17494, 17520, 17546, 17572, 17598, 17624, 17650, 17676, 17702, 17728, 17754, 17780, 17806, 17832, 17858, 17884, 17910, 17936, 17962, 17988, 18014, 18040, 18066, 18092, 18118, 18144, 18170, 18196, 18222, 18248, 18274, 18300, 18326, 18352, 18378, 18404, 18430, 18456, 18482, 18508, 18534, 18560, 18586, 18612, 18638, 18664, 18690, 18716, 18742, 18768, 18794, 18820, 18846, 18872, 18898, 18924, 18950, 18976, 19002, 19028, 19054, 19080, 19106, 19132, 19158, 19184, 19210, 19236, 19262, 19288, 19314, 19340, 19366, 19392, 19418, 19444, 19470, 19496, 19522, 19548, 19574, 19600, 19626, 19652, 19678, 19704, 19730, 19756, 19782, 19810, 19838, 19866, 19894, 19922, 19950, 19978, 20006, 20034, 20062, 20090, 20118, 20146, 20174, 20202, 20230, 20258, 20286, 20314, 20342, 20370, 20398, 20426, 20454, 20482, 20510, 20538, 20566, 20594, 20622, 20650, 20678, 20706, 20734, 20762, 20790, 20818, 20846, 20874, 20902, 20930, 20958, 20986, 21014, 21042, 21070, 21098, 21126, 21154, 21182, 21210, 21238, 21266, 21294, 21322, 21350, 21378, 21406, 21434, 21462, 21490, 21518, 21546, 21574, 21602, 21630, 21658, 21686, 21714, 21742, 21770, 21798, 21826, 21854, 21882, 21910, 21938, 21966, 21994, 22022, 22050, 22078, 22106, 22134, 22162, 22190, 22218, 22246, 22274, 22302, 22330, 22358, 22386, 22414, 22442, 22470, 22498, 22528, 22558, 22588, 22618, 22648, 22678, 22708, 22738, 22768, 22798, 22828, 22858, 22888, 22918, 22948, 22978, 23008, 23038, 23068, 23098, 23128, 23158, 23188, 23218, 23248, 23278, 23308, 23338, 23368, 23398, 23428, 23458, 23488, 23518, 23548, 23578, 23608, 23638, 23668, 23698, 23728, 23758, 23788, 23818, 23848, 23878, 23908, 23938, 23968, 23998, 24028, 24058, 24088, 24118, 24148, 24178, 24208, 24238, 24268, 24298, 24328, 24358, 24388, 24418, 24448, 24480, 24512, 24544, 24576, 24608, 24640, 24672, 24704, 24736, 24768, 24800, 24832, 24864, 24896, 24928, 24960, 24992, 25024, 25056, 25088, 25120, 25152, 25184, 25216, 25248, 25280, 25312, 25344, 25376, 25408, 25440, 25472, 25504, 25536, 25568, 25600, 25632, 25664, 25696, 25728, 25760, 25794, 25828, 25862, 25896, 25930, 25964, 25998, 26032, 26066, 26100, 26134, 26168, 26202, 26236, 26270, 26304, 26338, 26372, 26406, 26440, 26474, 26510, 26546, 26582, 26618, 26654, 26690, 26726, 26762, 26798, 26834, 26872, 26910, 26948, 26986, 27024, 27064, 27104, 27146}; __END_DECLS igraph/src/glpenv04.c0000644000176000001440000000725712325527073014167 0ustar ripleyusers/* glpenv04.c (error handling) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpapi.h" #include "igraph_error.h" /*********************************************************************** * NAME * * glp_error - display error message and terminate execution * * SYNOPSIS * * void glp_error(const char *fmt, ...); * * DESCRIPTION * * The routine glp_error (implemented as a macro) formats its * parameters using the format control string fmt, writes the formatted * message to the terminal, and abnormally terminates the program. */ static void error(const char *fmt, ...) { ENV *env = get_env_ptr(); va_list arg; env->term_out = GLP_ON; va_start(arg, fmt); igraph_errorvf(fmt, env->err_file, env->err_line, IGRAPH_EGLP, arg); /* no return */ } _glp_error glp_error_(const char *file, int line) { ENV *env = get_env_ptr(); env->err_file = file; env->err_line = line; return error; } /*********************************************************************** * NAME * * glp_assert - check for logical condition * * SYNOPSIS * * #include "glplib.h" * void glp_assert(int expr); * * DESCRIPTION * * The routine glp_assert (implemented as a macro) checks for a logical * condition specified by the parameter expr. If the condition is false * (i.e. the value of expr is zero), the routine writes a message to * the terminal and abnormally terminates the program. */ void glp_assert_(const char *expr, const char *file, int line) { glp_error_(file, line)("Assertion failed: %s\n", expr); /* no return */ } /*********************************************************************** * NAME * * glp_error_hook - install hook to intercept abnormal termination * * SYNOPSIS * * void glp_error_hook(void (*func)(void *info), void *info); * * DESCRIPTION * * The routine glp_error_hook installs a user-defined hook routine to * intercept abnormal termination. * * The parameter func specifies the user-defined hook routine. It is * called from the routine glp_error before the latter calls the abort * function to abnormally terminate the application program because of * fatal error. The parameter info is a transit pointer, specified in * the corresponding call to the routine glp_error_hook; it may be used * to pass some information to the hook routine. * * To uninstall the hook routine the parameters func and info should be * specified as NULL. */ void glp_error_hook(void (*func)(void *info), void *info) { ENV *env = get_env_ptr(); if (func == NULL) { env->err_hook = NULL; env->err_info = NULL; } else { env->err_hook = func; env->err_info = info; } return; } /* eof */ igraph/src/dsconv.f0000644000176000001440000000665412325527073014027 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdsconv c c\Description: c Convergence testing for the symmetric Arnoldi eigenvalue routine. c c\Usage: c call igraphdsconv c ( N, RITZ, BOUNDS, TOL, NCONV ) c c\Arguments c N Integer. (INPUT) c Number of Ritz values to check for convergence. c c RITZ Double precision array of length N. (INPUT) c The Ritz values to be checked for convergence. c c BOUNDS Double precision array of length N. (INPUT) c Ritz estimates associated with the Ritz values in RITZ. c c TOL Double precision scalar. (INPUT) c Desired relative accuracy for a Ritz value to be considered c "converged". c c NCONV Integer scalar. (OUTPUT) c Number of "converged" Ritz values. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Routines called: c igraphsecond ARPACK utility routine for timing. c dlamch LAPACK routine that determines machine constants. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\SCCS Information: @(#) c FILE: sconv.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 c c\Remarks c 1. Starting with version 2.4, this routine no longer uses the c Parlett strategy using the gap conditions. c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdsconv (n, ritz, bounds, tol, nconv) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c integer n, nconv Double precision & tol c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & ritz(n), bounds(n) c c %---------------% c | Local Scalars | c %---------------% c integer i Double precision & temp, eps23 c c %-------------------% c | External routines | c %-------------------% c Double precision & dlamch external dlamch c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic abs c c %-----------------------% c | Executable Statements | c %-----------------------% c call igraphsecond (t0) c eps23 = dlamch('Epsilon-Machine') eps23 = eps23**(2.0D+0 / 3.0D+0) c nconv = 0 do 10 i = 1, n c c %-----------------------------------------------------% c | The i-th Ritz value is considered "converged" | c | when: bounds(i) .le. TOL*max(eps23, abs(ritz(i))) | c %-----------------------------------------------------% c temp = max( eps23, abs(ritz(i)) ) if ( bounds(i) .le. tol*temp ) then nconv = nconv + 1 end if c 10 continue c call igraphsecond (t1) tsconv = tsconv + (t1 - t0) c return c c %---------------% c | End of igraphdsconv | c %---------------% c end igraph/src/glplib01.c0000644000176000001440000002257712325527073014144 0ustar ripleyusers/* glplib01.c (bignum arithmetic) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpenv.h" #include "glplib.h" /*********************************************************************** * Two routines below are intended to multiply and divide unsigned * integer numbers of arbitrary precision. * * The routines assume that an unsigned integer number is represented in * the positional numeral system with the base 2^16 = 65536, i.e. each * "digit" of the number is in the range [0, 65535] and represented as * a 16-bit value of the unsigned short type. In other words, a number x * has the following representation: * * n-1 * x = sum d[j] * 65536^j, * j=0 * * where n is the number of places (positions), and d[j] is j-th "digit" * of x, 0 <= d[j] <= 65535. ***********************************************************************/ /*********************************************************************** * NAME * * bigmul - multiply unsigned integer numbers of arbitrary precision * * SYNOPSIS * * #include "glplib.h" * void bigmul(int n, int m, unsigned short x[], unsigned short y[]); * * DESCRIPTION * * The routine bigmul multiplies unsigned integer numbers of arbitrary * precision. * * n is the number of digits of multiplicand, n >= 1; * * m is the number of digits of multiplier, m >= 1; * * x is an array containing digits of the multiplicand in elements * x[m], x[m+1], ..., x[n+m-1]. Contents of x[0], x[1], ..., x[m-1] are * ignored on entry. * * y is an array containing digits of the multiplier in elements y[0], * y[1], ..., y[m-1]. * * On exit digits of the product are stored in elements x[0], x[1], ..., * x[n+m-1]. The array y is not changed. */ void bigmul(int n, int m, unsigned short x[], unsigned short y[]) { int i, j; unsigned int t; xassert(n >= 1); xassert(m >= 1); for (j = 0; j < m; j++) x[j] = 0; for (i = 0; i < n; i++) { if (x[i+m]) { t = 0; for (j = 0; j < m; j++) { t += (unsigned int)x[i+m] * (unsigned int)y[j] + (unsigned int)x[i+j]; x[i+j] = (unsigned short)t; t >>= 16; } x[i+m] = (unsigned short)t; } } return; } /*********************************************************************** * NAME * * bigdiv - divide unsigned integer numbers of arbitrary precision * * SYNOPSIS * * #include "glplib.h" * void bigdiv(int n, int m, unsigned short x[], unsigned short y[]); * * DESCRIPTION * * The routine bigdiv divides one unsigned integer number of arbitrary * precision by another with the algorithm described in [1]. * * n is the difference between the number of digits of dividend and the * number of digits of divisor, n >= 0. * * m is the number of digits of divisor, m >= 1. * * x is an array containing digits of the dividend in elements x[0], * x[1], ..., x[n+m-1]. * * y is an array containing digits of the divisor in elements y[0], * y[1], ..., y[m-1]. The highest digit y[m-1] must be non-zero. * * On exit n+1 digits of the quotient are stored in elements x[m], * x[m+1], ..., x[n+m], and m digits of the remainder are stored in * elements x[0], x[1], ..., x[m-1]. The array y is changed but then * restored. * * REFERENCES * * 1. D. Knuth. The Art of Computer Programming. Vol. 2: Seminumerical * Algorithms. Stanford University, 1969. */ void bigdiv(int n, int m, unsigned short x[], unsigned short y[]) { int i, j; unsigned int t; unsigned short d, q, r; xassert(n >= 0); xassert(m >= 1); xassert(y[m-1] != 0); /* special case when divisor has the only digit */ if (m == 1) { d = 0; for (i = n; i >= 0; i--) { t = ((unsigned int)d << 16) + (unsigned int)x[i]; x[i+1] = (unsigned short)(t / y[0]); d = (unsigned short)(t % y[0]); } x[0] = d; goto done; } /* multiply dividend and divisor by a normalizing coefficient in order to provide the condition y[m-1] >= base / 2 */ d = (unsigned short)(0x10000 / ((unsigned int)y[m-1] + 1)); if (d == 1) x[n+m] = 0; else { t = 0; for (i = 0; i < n+m; i++) { t += (unsigned int)x[i] * (unsigned int)d; x[i] = (unsigned short)t; t >>= 16; } x[n+m] = (unsigned short)t; t = 0; for (j = 0; j < m; j++) { t += (unsigned int)y[j] * (unsigned int)d; y[j] = (unsigned short)t; t >>= 16; } } /* main loop */ for (i = n; i >= 0; i--) { /* estimate and correct the current digit of quotient */ if (x[i+m] < y[m-1]) { t = ((unsigned int)x[i+m] << 16) + (unsigned int)x[i+m-1]; q = (unsigned short)(t / (unsigned int)y[m-1]); r = (unsigned short)(t % (unsigned int)y[m-1]); if (q == 0) goto putq; else goto test; } q = 0; r = x[i+m-1]; decr: q--; /* if q = 0 then q-- = 0xFFFF */ t = (unsigned int)r + (unsigned int)y[m-1]; r = (unsigned short)t; if (t > 0xFFFF) goto msub; test: t = (unsigned int)y[m-2] * (unsigned int)q; if ((unsigned short)(t >> 16) > r) goto decr; if ((unsigned short)(t >> 16) < r) goto msub; if ((unsigned short)t > x[i+m-2]) goto decr; msub: /* now subtract divisor multiplied by the current digit of quotient from the current dividend */ if (q == 0) goto putq; t = 0; for (j = 0; j < m; j++) { t += (unsigned int)y[j] * (unsigned int)q; if (x[i+j] < (unsigned short)t) t += 0x10000; x[i+j] -= (unsigned short)t; t >>= 16; } if (x[i+m] >= (unsigned short)t) goto putq; /* perform correcting addition, because the current digit of quotient is greater by one than its correct value */ q--; t = 0; for (j = 0; j < m; j++) { t += (unsigned int)x[i+j] + (unsigned int)y[j]; x[i+j] = (unsigned short)t; t >>= 16; } putq: /* store the current digit of quotient */ x[i+m] = q; } /* divide divisor and remainder by the normalizing coefficient in order to restore their original values */ if (d > 1) { t = 0; for (i = m-1; i >= 0; i--) { t = (t << 16) + (unsigned int)x[i]; x[i] = (unsigned short)(t / (unsigned int)d); t %= (unsigned int)d; } t = 0; for (j = m-1; j >= 0; j--) { t = (t << 16) + (unsigned int)y[j]; y[j] = (unsigned short)(t / (unsigned int)d); t %= (unsigned int)d; } } done: return; } /**********************************************************************/ #if 0 #include #include #include #include "glprng.h" #define N_MAX 7 /* maximal number of digits in multiplicand */ #define M_MAX 5 /* maximal number of digits in multiplier */ #define N_TEST 1000000 /* number of tests */ int main(void) { RNG *rand; int d, j, n, m, test; unsigned short x[N_MAX], y[M_MAX], z[N_MAX+M_MAX]; rand = rng_create_rand(); for (test = 1; test <= N_TEST; test++) { /* x[0,...,n-1] := multiplicand */ n = 1 + rng_unif_rand(rand, N_MAX-1); assert(1 <= n && n <= N_MAX); for (j = 0; j < n; j++) { d = rng_unif_rand(rand, 65536); assert(0 <= d && d <= 65535); x[j] = (unsigned short)d; } /* y[0,...,m-1] := multiplier */ m = 1 + rng_unif_rand(rand, M_MAX-1); assert(1 <= m && m <= M_MAX); for (j = 0; j < m; j++) { d = rng_unif_rand(rand, 65536); assert(0 <= d && d <= 65535); y[j] = (unsigned short)d; } if (y[m-1] == 0) y[m-1] = 1; /* z[0,...,n+m-1] := x * y */ for (j = 0; j < n; j++) z[m+j] = x[j]; bigmul(n, m, z, y); /* z[0,...,m-1] := z mod y, z[m,...,n+m-1] := z div y */ bigdiv(n, m, z, y); /* z mod y must be 0 */ for (j = 0; j < m; j++) assert(z[j] == 0); /* z div y must be x */ for (j = 0; j < n; j++) assert(z[m+j] == x[j]); } fprintf(stderr, "%d tests successfully passed\n", N_TEST); rng_delete_rand(rand); return 0; } #endif /* eof */ igraph/src/cs_scatter.c0000644000176000001440000000340312325527073014647 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* x = x + beta * A(:,j), where x is a dense vector and A(:,j) is sparse */ CS_INT cs_scatter (const cs *A, CS_INT j, CS_ENTRY beta, CS_INT *w, CS_ENTRY *x, CS_INT mark, cs *C, CS_INT nz) { CS_INT i, p, *Ap, *Ai, *Ci ; CS_ENTRY *Ax ; if (!CS_CSC (A) || !w || !CS_CSC (C)) return (-1) ; /* check inputs */ Ap = A->p ; Ai = A->i ; Ax = A->x ; Ci = C->i ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; /* A(i,j) is nonzero */ if (w [i] < mark) { w [i] = mark ; /* i is new entry in column j */ Ci [nz++] = i ; /* add i to pattern of C(:,j) */ if (x) x [i] = beta * Ax [p] ; /* x(i) = beta*A(i,j) */ } else if (x) x [i] += beta * Ax [p] ; /* i exists in C(:,j) already */ } return (nz) ; } igraph/src/igraph_interrupt.h0000644000176000001440000001167712325527073016124 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_INTERRUPT_H #define IGRAPH_INTERRUPT_H #include "igraph_error.h" #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* This file contains the igraph interruption handling. */ /** * \section interrupthandlers Interruption handlers * * * \a igraph is designed to be embeddable into several higher level * languages (R and Python interfaces are included in the original * package). Since most higher level languages consider internal \a igraph * calls as atomic, interruption requests (like Ctrl-C in Python) must * be handled differently depending on the environment \a igraph embeds * into. * * An \emb interruption handler \eme is a function which is called regularly * by \a igraph during long calculations. A typical usage of the interruption * handler is to check whether the user tried to interrupt the calculation * and return an appropriate value to signal this condition. For example, * in R, one must call an internal R function regularly to check for * interruption requests, and the \a igraph interruption handler is the * perfect place to do that. * * If you are using the plain C interface of \a igraph or if you are * allowed to replace the operating system's interruption handler (like * SIGINT in Un*x systems), these calls are not of much use to you. * * The default interruption handler is empty. * The \ref igraph_set_interruption_handler() function can be used to set a * new interruption handler function of type * \ref igraph_interruption_handler_t, see the * documentation of this type for details. * */ /** * \section writing_interruption_handlers Writing interruption handlers * * * You can write and install interruption handlers simply by defining a * function of type \ref igraph_interruption_handler_t and calling * \ref igraph_set_interruption_handler(). This feature is useful for * interface writers, because usually this is the only way to allow handling * of Ctrl-C and similar keypresses properly. * * * Your interruption handler will be called regularly during long operations * (so it is not guaranteed to be called during operations which tend to be * short, like adding single edges). An interruption handler accepts no * parameters and must return \c IGRAPH_SUCCESS if the calculation should go on. All * other return values are considered to be a request for interruption, * and the caller function would return a special error code, \c IGRAPH_INTERRUPTED. * It is up to your error handler function to handle this error properly. * */ /** * \section writing_functions_interruption_handling Writing \a igraph functions with * proper interruption handling * * * There is practically a simple rule that should be obeyed when writing * \a igraph functions. If the calculation is expected to take a long time * in large graphs (a simple rule of thumb is to assume this for every * function with a time complexity of at least O(n^2)), call * \ref IGRAPH_ALLOW_INTERRUPTION in regular intervals like every 10th * iteration or so. * */ /** * \typedef igraph_interruption_handler_t * * This is the type of the interruption handler functions. * * \param data reserved for possible future use * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise. */ typedef int igraph_interruption_handler_t (void* data); /** * \function igraph_allow_interruption * * This is the function which is called (usually via the * \ref IGRAPH_INTERRUPTION macro) if \a igraph is checking for interruption * requests. * * \param data reserved for possible future use, now it is always \c NULL * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise. */ int igraph_allow_interruption(void* data); igraph_interruption_handler_t * igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler); __END_DECLS #endif igraph/src/rinterface.c0000644000176000001440000211131712325527074014646 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library R interface. Copyright (C) 2005-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph.h" #include "igraph_error.h" #include "config.h" #define USE_RINTERNALS #include #include #include #include int igraph_free(void *p); SEXP R_igraph_vector_to_SEXP(const igraph_vector_t *v); SEXP R_igraph_vector_int_to_SEXP(const igraph_vector_int_t *v); SEXP R_igraph_vector_bool_to_SEXP(const igraph_vector_bool_t *v); SEXP R_igraph_vector_long_to_SEXP(const igraph_vector_long_t *v); SEXP R_igraph_vector_complex_to_SEXP(const igraph_vector_complex_t* v); SEXP R_igraph_0orvector_to_SEXP(const igraph_vector_t *v); SEXP R_igraph_0orvector_bool_to_SEXP(const igraph_vector_bool_t *v); SEXP R_igraph_0orvector_long_to_SEXP(const igraph_vector_long_t *v); SEXP R_igraph_0orvector_complex_to_SEXP(const igraph_vector_complex_t *v); SEXP R_igraph_matrix_to_SEXP(const igraph_matrix_t *m); SEXP R_igraph_matrix_complex_to_SEXP(const igraph_matrix_complex_t *m); SEXP R_igraph_0ormatrix_complex_to_SEXP(const igraph_matrix_complex_t *m); SEXP R_igraph_strvector_to_SEXP(const igraph_strvector_t *m); SEXP R_igraph_to_SEXP(const igraph_t *graph); SEXP R_igraph_vectorlist_to_SEXP(const igraph_vector_ptr_t *ptr); SEXP R_igraph_vectorlist_int_to_SEXP(const igraph_vector_ptr_t *ptr); void R_igraph_vectorlist_int_destroy(igraph_vector_ptr_t *ptr); SEXP R_igraph_0orvectorlist_to_SEXP(const igraph_vector_ptr_t *ptr); void R_igraph_vectorlist_destroy(igraph_vector_ptr_t *ptr); SEXP R_igraph_graphlist_to_SEXP(const igraph_vector_ptr_t *ptr); void R_igraph_graphlist_destroy(igraph_vector_ptr_t *ptr); SEXP R_igraph_hrg_to_SEXP(const igraph_hrg_t *hrg); SEXP R_igraph_plfit_result_to_SEXP(const igraph_plfit_result_t *plfit); SEXP R_igraph_sparsemat_to_SEXP(const igraph_sparsemat_t *sp); SEXP R_igraph_0orsparsemat_to_SEXP(const igraph_sparsemat_t *sp); SEXP R_igraph_maxflow_stats_to_SEXP(const igraph_maxflow_stats_t *st); SEXP R_igraph_sirlist_to_SEXP(const igraph_vector_ptr_t *sl); void R_igraph_sirlist_destroy(igraph_vector_ptr_t *sl); int R_igraph_SEXP_to_strvector(SEXP rval, igraph_strvector_t *sv); int R_igraph_SEXP_to_strvector_copy(SEXP rval, igraph_strvector_t *sv); int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v); int R_SEXP_to_vector_copy(SEXP sv, igraph_vector_t *v); int R_SEXP_to_matrix(SEXP pakl, igraph_matrix_t *akl); int R_SEXP_to_matrix_complex(SEXP pakl, igraph_matrix_complex_t *akl); int R_SEXP_to_igraph_matrix_copy(SEXP pakl, igraph_matrix_t *akl); int R_SEXP_to_igraph(SEXP graph, igraph_t *res); int R_SEXP_to_igraph_copy(SEXP graph, igraph_t *res); int R_SEXP_to_igraph_vs(SEXP rit, igraph_t *graph, igraph_vs_t *it); int R_SEXP_to_igraph_es(SEXP rit, igraph_t *graph, igraph_es_t *it); int R_SEXP_to_igraph_adjlist(SEXP vectorlist, igraph_adjlist_t *ptr); int R_igraph_SEXP_to_0orvectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr); int R_igraph_SEXP_to_vectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr); int R_SEXP_to_vector_bool(SEXP sv, igraph_vector_bool_t *v); int R_SEXP_to_vector_bool_copy(SEXP sv, igraph_vector_bool_t *v); int R_SEXP_to_vector_int(SEXP sv, igraph_vector_int_t *v); int R_SEXP_to_vector_long_copy(SEXP sv, igraph_vector_long_t *v); int R_SEXP_to_hrg(SEXP shrg, igraph_hrg_t *hrg); int R_SEXP_to_hrg_copy(SEXP shrg, igraph_hrg_t *hrg); int R_SEXP_to_sparsemat(SEXP pakl, igraph_sparsemat_t *akl); int R_SEXP_to_pagerank_power_options(SEXP popt, igraph_pagerank_power_options_t *opt); SEXP R_igraph_i_lang7(SEXP s, SEXP t, SEXP u, SEXP v, SEXP w, SEXP x, SEXP y) { PROTECT(s); PROTECT(t); PROTECT(u); s = LCONS(s, LCONS(t, LCONS(u, list4(v, w, x, y)))); UNPROTECT(3); return s; } /* get the list element named str, or return NULL */ /* from the R Manual */ SEXP R_igraph_getListElement(SEXP list, const char *str) { SEXP elmt = R_NilValue, names = getAttrib(list, R_NamesSymbol); int i; for (i = 0; i < length(list); i++) if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { elmt = VECTOR_ELT(list, i); break; } return elmt; } /****************************************************** * Attributes * *****************************************************/ SEXP R_igraph_get_attr_mode(SEXP graph, SEXP pwhich) { int which=INTEGER(pwhich)[0]-1; SEXP obj=VECTOR_ELT(VECTOR_ELT(graph, 8), which); int i, len=GET_LENGTH(obj); SEXP result; PROTECT(result=NEW_CHARACTER(len)); for (i=0; iattr=result; /* Add graph attributes */ attrno= attr==NULL ? 0 : igraph_vector_ptr_size(attr); SET_VECTOR_ELT(result, 1, NEW_LIST(attrno)); gal=VECTOR_ELT(result, 1); PROTECT(names=NEW_CHARACTER(attrno)); for (i=0; iname)); SET_VECTOR_ELT(gal, i, R_NilValue); switch (rec->type) { case IGRAPH_ATTRIBUTE_NUMERIC: vec=(igraph_vector_t*) rec->value; if (igraph_vector_size(vec) > 0) { SET_VECTOR_ELT(gal, i, NEW_NUMERIC(1)); REAL(VECTOR_ELT(gal, i))[0]=VECTOR(*vec)[0]; } break; case IGRAPH_ATTRIBUTE_BOOLEAN: log=(igraph_vector_bool_t*) rec->value; if (igraph_vector_bool_size(log) > 0) { SET_VECTOR_ELT(gal, i, NEW_LOGICAL(1)); LOGICAL(VECTOR_ELT(gal, i))[0]=VECTOR(*log)[0]; } break; case IGRAPH_ATTRIBUTE_STRING: strvec=(igraph_strvector_t*) rec->value; if (igraph_strvector_size(strvec) > 0) { SET_VECTOR_ELT(gal, i, NEW_CHARACTER(1)); SET_STRING_ELT(VECTOR_ELT(gal,i), 0, mkChar(STR(*strvec, 0))); } break; case IGRAPH_ATTRIBUTE_R_OBJECT: IGRAPH_ERROR("R_objects not implemented yet", IGRAPH_UNIMPLEMENTED); break; case IGRAPH_ATTRIBUTE_DEFAULT: case IGRAPH_ATTRIBUTE_PY_OBJECT: default: IGRAPH_ERROR("Unknown attribute type, this should not happen", IGRAPH_EINTERNAL); break; } } SET_NAMES(gal, names); UNPROTECT(1); return 0; } void R_igraph_attribute_destroy(igraph_t *graph) { SEXP attr=graph->attr; REAL(VECTOR_ELT(attr, 0))[1] -= 1; /* refcount for igraph_t */ if (!R_igraph_attribute_protected && REAL(VECTOR_ELT(attr, 0))[1]==0 && REAL(VECTOR_ELT(attr, 0))[2]==1) { UNPROTECT_PTR(attr); } graph->attr=0; } /* If not copying all three attribute kinds are requested, then we don't refcount, but really copy the requested ones, because 1) we can only refcount all three at the same time, and 2) the not-copied attributes will be set up by subsequent calls to permute_vertices and/or permute/edges anyway. */ int R_igraph_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { SEXP fromattr=from->attr; if (ga && va && ea) { to->attr=from->attr; REAL(VECTOR_ELT(fromattr, 0))[1] += 1; /* refcount only */ if (!R_igraph_attribute_protected && REAL(VECTOR_ELT(fromattr, 0))[1] == 1) { PROTECT(to->attr); } } else { R_igraph_attribute_init(to,0); /* Sets up many things */ SEXP toattr=to->attr; if (ga) { SET_VECTOR_ELT(toattr, 1, duplicate(VECTOR_ELT(fromattr, 1))); } if (va) { SET_VECTOR_ELT(toattr, 2, duplicate(VECTOR_ELT(fromattr, 2))); } if (ea) { SET_VECTOR_ELT(toattr, 3, duplicate(VECTOR_ELT(fromattr, 3))); } } return 0; } int R_igraph_attribute_add_vertices(igraph_t *graph, long int nv, igraph_vector_ptr_t *nattr) { SEXP attr=graph->attr; SEXP val, rep=0, names, newnames; igraph_vector_t news; long int valno, i, origlen, nattrno, newattrs; if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { SEXP newattr=duplicate(attr); if (!R_igraph_attribute_protected) { PROTECT(newattr); } REAL(VECTOR_ELT(attr, 0))[1] -= 1; if (!R_igraph_attribute_protected && REAL(VECTOR_ELT(attr, 0))[1] == 0) { UNPROTECT_PTR(attr); } REAL(VECTOR_ELT(newattr, 0))[0] = 0; REAL(VECTOR_ELT(newattr, 0))[1] = 1; if (R_igraph_attribute_protected) { long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); if (alen == 4) { pos=REAL(VECTOR_ELT(attr, 0))[3]; SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } else { SEXP tmp=NEW_NUMERIC(4); PROTECT(tmp); REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; R_igraph_attribute_protected_size += 1; SET_VECTOR_ELT(newattr, 0, tmp); UNPROTECT(1); } SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } attr=graph->attr=newattr; } val=VECTOR_ELT(attr, 2); valno=GET_LENGTH(val); names=GET_NAMES(val); if (nattr==NULL) { nattrno=0; } else { nattrno=igraph_vector_ptr_size(nattr); } origlen=igraph_vcount(graph)-nv; /* First add the new attributes, if any */ newattrs=0; IGRAPH_VECTOR_INIT_FINALLY(&news, 0); for (i=0; iname; long int j; igraph_bool_t l=0; for (j=0; !l && jname)); } UNPROTECT(1); /* rep */ PROTECT(newval=EVAL(lang3(install("c"), val, app))); PROTECT(newnames=EVAL(lang3(install("c"), names, newnames))); SET_NAMES(newval, newnames); SET_VECTOR_ELT(attr, 2, newval); val=VECTOR_ELT(attr, 2); valno=GET_LENGTH(val); names=GET_NAMES(val); UNPROTECT(4); rep=0; } igraph_vector_destroy(&news); IGRAPH_FINALLY_CLEAN(1); /* news */ /* Now append the new values */ for (i=0; iname); } if (l) { /* This attribute is present in nattr */ SEXP app=0; igraph_attribute_record_t *tmprec=VECTOR(*nattr)[j-1]; switch (tmprec->type) { case IGRAPH_ATTRIBUTE_NUMERIC: if (nv != igraph_vector_size(tmprec->value)) { IGRAPH_ERROR("Invalid attribute length", IGRAPH_EINVAL); } PROTECT(app=NEW_NUMERIC(nv)); igraph_vector_copy_to(tmprec->value, REAL(app)); break; case IGRAPH_ATTRIBUTE_BOOLEAN: if (nv != igraph_vector_bool_size(tmprec->value)) { IGRAPH_ERROR("Invalid attribute length", IGRAPH_EINVAL); } PROTECT(app=R_igraph_vector_bool_to_SEXP(tmprec->value)); break; case IGRAPH_ATTRIBUTE_STRING: if (nv != igraph_strvector_size(tmprec->value)) { IGRAPH_ERROR("Invalid attribute length", IGRAPH_EINVAL); } PROTECT(app=R_igraph_strvector_to_SEXP(tmprec->value)); break; case IGRAPH_ATTRIBUTE_R_OBJECT: /* TODO */ IGRAPH_ERROR("R_objects not implemented yet", IGRAPH_UNIMPLEMENTED); break; default: warning("Ignoring unknown attribute type"); break; } if (app!=0) { PROTECT(newva=EVAL(lang3(install("c"), oldva, app))); SET_VECTOR_ELT(val, i, newva); UNPROTECT(2); /* app & newva */ } } else { /* No such attribute, append NA's */ if (rep==0) { PROTECT(rep=EVAL(lang3(install("rep"), ScalarLogical(NA_LOGICAL), ScalarInteger((int) nv)))); } PROTECT(newva=EVAL(lang3(install("c"), oldva, rep))); SET_VECTOR_ELT(val, i, newva); UNPROTECT(1); /* newva */ } } if (rep != 0) { UNPROTECT(1); } return 0; } /* void R_igraph_attribute_delete_vertices(igraph_t *graph, */ /* const igraph_vector_t *eidx, */ /* const igraph_vector_t *vidx) { */ /* SEXP attr=graph->attr; */ /* SEXP eal, val; */ /* long int valno, ealno, i; */ /* if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { */ /* SEXP newattr; */ /* PROTECT(newattr=duplicate(attr)); */ /* REAL(VECTOR_ELT(attr, 0))[1] -= 1; */ /* if (REAL(VECTOR_ELT(attr, 0))[1] == 0) { */ /* UNPROTECT_PTR(attr); */ /* } */ /* REAL(VECTOR_ELT(newattr, 0))[0] = 0; */ /* REAL(VECTOR_ELT(newattr, 0))[1] = 1; */ /* attr=graph->attr=newattr; */ /* } */ /* /\* Vertices *\/ */ /* val=VECTOR_ELT(attr, 2); */ /* valno=GET_LENGTH(val); */ /* for (i=0; i 0) { */ /* newlen++; */ /* } */ /* } */ /* PROTECT(ss=NEW_NUMERIC(newlen)); */ /* for (j=0; j0) { */ /* REAL(ss)[(long int)VECTOR(*vidx)[j]-1]=j+1; */ /* } */ /* } */ /* PROTECT(newva=EVAL(lang3(install("["), oldva, ss))); */ /* SET_VECTOR_ELT(val, i, newva); */ /* UNPROTECT(2); */ /* } */ /* /\* Edges *\/ */ /* eal=VECTOR_ELT(attr, 3); */ /* ealno=GET_LENGTH(eal); */ /* for (i=0; i 0) { */ /* newlen++; */ /* } */ /* } */ /* PROTECT(ss=NEW_NUMERIC(newlen)); */ /* for (j=0; j0) { */ /* REAL(ss)[(long int)VECTOR(*eidx)[j]-1]=j+1; */ /* } */ /* } */ /* PROTECT(newea=EVAL(lang3(install("["), oldea, ss))); */ /* SET_VECTOR_ELT(eal, i, newea); */ /* UNPROTECT(2); */ /* } */ /* } */ int R_igraph_attribute_permute_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx) { if (graph == newgraph) { SEXP attr=newgraph->attr; SEXP val; long int i, valno; long int idxlen=igraph_vector_size(idx); SEXP ss; /* We copy if we need to */ if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { SEXP newattr=duplicate(attr); if (!R_igraph_attribute_protected) { PROTECT(newattr); } REAL(VECTOR_ELT(attr, 0))[1] -= 1; if (!R_igraph_attribute_protected && REAL(VECTOR_ELT(attr, 0))[1] == 0) { UNPROTECT_PTR(attr); } REAL(VECTOR_ELT(newattr, 0))[0] = 0; REAL(VECTOR_ELT(newattr, 0))[1] = 1; if (R_igraph_attribute_protected) { long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); if (alen == 4) { pos=REAL(VECTOR_ELT(attr, 0))[3]; SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } else { SEXP tmp=NEW_NUMERIC(4); PROTECT(tmp); REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; R_igraph_attribute_protected_size += 1; SET_VECTOR_ELT(newattr, 0, tmp); UNPROTECT(1); } SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } attr=newgraph->attr=newattr; } val=VECTOR_ELT(attr,2); valno=GET_LENGTH(val); /* If we have no vertex attributes, then we don't need to do anything */ if (valno==0) { return 0; } /* Convert idx to an R object, we will use this for indexing */ PROTECT(ss=NEW_INTEGER(idxlen)); for (i=0; iattr; SEXP toattr=newgraph->attr; SEXP val, toval; SEXP names; long int i, valno; long int idxlen=igraph_vector_size(idx); SEXP ss; val=VECTOR_ELT(attr,2); valno=GET_LENGTH(val); /* If we have no vertex attributes, then we don't need to do anything */ if (valno==0) { return 0; } /* Convert idx to an R object, we will use this for indexing */ PROTECT(ss=NEW_INTEGER(idxlen)); for (i=0; iattr; SEXP eal, rep=0, names, newnames; igraph_vector_t news; long int ealno, i, origlen, nattrno, newattrs; long int ne=igraph_vector_size(edges)/2; if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { SEXP newattr=duplicate(attr); if (!R_igraph_attribute_protected) { PROTECT(newattr); } REAL(VECTOR_ELT(attr, 0))[1] -= 1; if (!R_igraph_attribute_protected && REAL(VECTOR_ELT(attr, 0))[1] == 0) { UNPROTECT_PTR(attr); } REAL(VECTOR_ELT(newattr, 0))[0] = 0; REAL(VECTOR_ELT(newattr, 0))[1] = 1; if (R_igraph_attribute_protected) { long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); if (alen == 4) { pos=REAL(VECTOR_ELT(attr, 0))[3]; SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } else { SEXP tmp=NEW_NUMERIC(4); PROTECT(tmp); REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; R_igraph_attribute_protected_size += 1; SET_VECTOR_ELT(newattr, 0, tmp); UNPROTECT(1); } SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } attr=graph->attr=newattr; } eal=VECTOR_ELT(attr, 3); ealno=GET_LENGTH(eal); names=GET_NAMES(eal); if (nattr==NULL) { nattrno=0; } else { nattrno=igraph_vector_ptr_size(nattr); } origlen=igraph_ecount(graph)-ne; /* First add the new attributes, if any */ newattrs=0; IGRAPH_VECTOR_INIT_FINALLY(&news, 0); for (i=0; iname; long int j; igraph_bool_t l=0; for (j=0; !l && jname)); } UNPROTECT(1); /* rep */ PROTECT(neweal=EVAL(lang3(install("c"), eal, app))); PROTECT(newnames=EVAL(lang3(install("c"), names, newnames))); SET_NAMES(neweal, newnames); SET_VECTOR_ELT(attr, 3, neweal); eal=VECTOR_ELT(attr, 3); ealno=GET_LENGTH(eal); names=GET_NAMES(eal); UNPROTECT(4); rep=0; } igraph_vector_destroy(&news); IGRAPH_FINALLY_CLEAN(1); /* Now append the new values */ for (i=0; iname); } if (l) { /* This attribute is present in nattr */ SEXP app=0; igraph_attribute_record_t *tmprec=VECTOR(*nattr)[j-1]; switch (tmprec->type) { case IGRAPH_ATTRIBUTE_NUMERIC: if (ne != igraph_vector_size(tmprec->value)) { IGRAPH_ERROR("Invalid attribute length", IGRAPH_EINVAL); } PROTECT(app=NEW_NUMERIC(ne)); igraph_vector_copy_to(tmprec->value, REAL(app)); break; case IGRAPH_ATTRIBUTE_BOOLEAN: if (ne != igraph_vector_bool_size(tmprec->value)) { IGRAPH_ERROR("Invalid attribute length", IGRAPH_EINVAL); } PROTECT(app=R_igraph_vector_bool_to_SEXP(tmprec->value)); break; case IGRAPH_ATTRIBUTE_STRING: if (ne != igraph_strvector_size(tmprec->value)) { IGRAPH_ERROR("Invalid attribute length", IGRAPH_EINVAL); } PROTECT(app=R_igraph_strvector_to_SEXP(tmprec->value)); break; case IGRAPH_ATTRIBUTE_R_OBJECT: /* TODO */ IGRAPH_ERROR("R objects not implemented yet", IGRAPH_UNIMPLEMENTED); break; default: warning("Ignoring unknown attribute type"); break; } if (app!=0) { PROTECT(newea=EVAL(lang3(install("c"), oldea, app))); SET_VECTOR_ELT(eal, i, newea); UNPROTECT(2); /* app & newea */ } } else { /* No such attribute, append NA's */ if (rep==0) { PROTECT(rep=EVAL(lang3(install("rep"), ScalarLogical(NA_LOGICAL), ScalarInteger((int) ne)))); } PROTECT(newea=EVAL(lang3(install("c"), oldea, rep))); SET_VECTOR_ELT(eal, i, newea); UNPROTECT(1); /* newea */ } } if (rep != 0) { UNPROTECT(1); } return 0; } /* void R_igraph_attribute_delete_edges(igraph_t *graph, */ /* const igraph_vector_t *idx) { */ /* SEXP attr=graph->attr; */ /* SEXP eal; */ /* long int ealno, i; */ /* if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { */ /* SEXP newattr; */ /* PROTECT(newattr=duplicate(attr)); */ /* REAL(VECTOR_ELT(attr, 0))[1] -= 1; */ /* if (REAL(VECTOR_ELT(attr, 0))[1] == 0) { */ /* UNPROTECT_PTR(attr); */ /* } */ /* REAL(VECTOR_ELT(newattr, 0))[0] = 0; */ /* REAL(VECTOR_ELT(newattr, 0))[1] = 1; */ /* attr=graph->attr=newattr; */ /* } */ /* eal=VECTOR_ELT(attr, 3); */ /* ealno=GET_LENGTH(eal); */ /* for (i=0; i 0) { */ /* newlen++; */ /* } */ /* } */ /* PROTECT(ss=NEW_NUMERIC(newlen)); */ /* for (j=0; j 0) { */ /* REAL(ss)[(long int)VECTOR(*idx)[j]-1] = j+1; */ /* } */ /* } */ /* PROTECT(newea=EVAL(lang3(install("["), oldea, ss))); */ /* SET_VECTOR_ELT(eal, i, newea); */ /* UNPROTECT(2); */ /* } */ /* } */ int R_igraph_attribute_permute_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx) { if (graph==newgraph) { SEXP attr=newgraph->attr; SEXP eal; long int i, ealno; long int idxlen=igraph_vector_size(idx); SEXP ss; /* We copy if we need to */ if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { SEXP newattr=duplicate(attr); if (!R_igraph_attribute_protected) { PROTECT(newattr); } REAL(VECTOR_ELT(attr, 0))[1] -= 1; if (!R_igraph_attribute_protected && REAL(VECTOR_ELT(attr, 0))[1] == 0) { UNPROTECT_PTR(attr); } REAL(VECTOR_ELT(newattr, 0))[0] = 0; REAL(VECTOR_ELT(newattr, 0))[1] = 1; if (R_igraph_attribute_protected) { long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); if (alen == 4) { pos=REAL(VECTOR_ELT(attr, 0))[3]; SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } else { SEXP tmp=NEW_NUMERIC(4); PROTECT(tmp); REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; R_igraph_attribute_protected_size += 1; SET_VECTOR_ELT(newattr, 0, tmp); UNPROTECT(1); } SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); } attr=newgraph->attr=newattr; } eal=VECTOR_ELT(attr,3); ealno=GET_LENGTH(eal); /* If we have no edge attributes, then we don't need to do anything */ if (ealno==0) { return 0; } /* Convert idx to an R object, we will use this for indexing */ PROTECT(ss=NEW_INTEGER(idxlen)); for (i=0; iattr; SEXP toattr=newgraph->attr; SEXP eal, toeal; SEXP names; long int i, ealno; long int idxlen=igraph_vector_size(idx); SEXP ss; eal=VECTOR_ELT(attr,3); ealno=GET_LENGTH(eal); /* If we have no vertex attributes, then we don't need to do anything */ if (ealno==0) { return 0; } /* Convert idx to an R object, we will use this for indexing */ PROTECT(ss=NEW_INTEGER(idxlen)); for (i=0; iattr; for (i=0; i<3; i++) { igraph_strvector_t *n=names[i]; igraph_vector_t *t=types[i]; SEXP al=VECTOR_ELT(attr, i+1); if (n) { /* return names */ SEXP names=GET_NAMES(al); R_igraph_SEXP_to_strvector_copy(names, n); } if (t) { /* return types */ igraph_vector_resize(t, GET_LENGTH(al)); for (j=0; jattr, attrnum), name); return res != R_NilValue; } int R_igraph_attribute_gettype(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name) { long int attrnum; SEXP res; switch (elemtype) { case IGRAPH_ATTRIBUTE_GRAPH: attrnum=1; break; case IGRAPH_ATTRIBUTE_VERTEX: attrnum=2; break; case IGRAPH_ATTRIBUTE_EDGE: attrnum=3; break; default: IGRAPH_ERROR("Unkwown attribute element type", IGRAPH_EINVAL); break; } res=R_igraph_getListElement(VECTOR_ELT(graph->attr, attrnum), name); if (IS_NUMERIC(res) || IS_INTEGER(res)) { *type=IGRAPH_ATTRIBUTE_NUMERIC; } else if (IS_LOGICAL(res)) { *type=IGRAPH_ATTRIBUTE_BOOLEAN; } else if (IS_CHARACTER(res)) { *type=IGRAPH_ATTRIBUTE_STRING; } else { *type=IGRAPH_ATTRIBUTE_R_OBJECT; } return 0; } int R_igraph_attribute_get_numeric_graph_attr(const igraph_t *graph, const char *name, igraph_vector_t *value) { SEXP gal=VECTOR_ELT(graph->attr, 1); SEXP ga=R_igraph_getListElement(gal, name); if (ga == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_NUMERIC(ga) && !IS_INTEGER(ga)) { IGRAPH_ERROR("Attribute not numeric", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_resize(value, 1)); if (IS_NUMERIC(ga)) { VECTOR(*value)[0]=REAL(ga)[0]; } else { /* INTEGER */ VECTOR(*value)[0]=INTEGER(ga)[0]; } return 0; } int R_igraph_attribute_get_bool_graph_attr(const igraph_t *graph, const char *name, igraph_vector_bool_t *value) { SEXP gal=VECTOR_ELT(graph->attr, 1); SEXP ga=R_igraph_getListElement(gal, name); if (ga == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_LOGICAL(ga)) { IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); VECTOR(*value)[0]=LOGICAL(ga)[0]; return 0; } int R_igraph_attribute_get_string_graph_attr(const igraph_t *graph, const char *name, igraph_strvector_t *value) { /* TODO: serialization */ SEXP gal=VECTOR_ELT(graph->attr, 1); SEXP ga=R_igraph_getListElement(gal, name); if (ga == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_CHARACTER(ga)) { IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_strvector_resize(value, 1)); IGRAPH_CHECK(igraph_strvector_set(value, 0, CHAR(STRING_ELT(ga, 0)))); return 0; } int R_igraph_attribute_get_numeric_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_t *value) { /* TODO: serialization */ SEXP val=VECTOR_ELT(graph->attr, 2); SEXP va=R_igraph_getListElement(val, name); igraph_vector_t newvalue; if (va == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_NUMERIC(va) && !IS_INTEGER(va)) { IGRAPH_ERROR("Attribute not numeric", IGRAPH_EINVAL); } if (igraph_vs_is_all(&vs)) { R_SEXP_to_vector_copy(AS_NUMERIC(va), &newvalue); igraph_vector_destroy(value); *value=newvalue; } else { igraph_vit_t it; long int i=0; IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); IGRAPH_FINALLY(igraph_vit_destroy, &it); IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_VIT_SIZE(it))); if (IS_NUMERIC(va)) { while (!IGRAPH_VIT_END(it)) { long int v=IGRAPH_VIT_GET(it); VECTOR(*value)[i]=REAL(va)[v]; IGRAPH_VIT_NEXT(it); i++; } } else if (IS_INTEGER(va)) { while (!IGRAPH_VIT_END(it)) { long int v=IGRAPH_VIT_GET(it); VECTOR(*value)[i]=INTEGER(va)[v]; IGRAPH_VIT_NEXT(it); i++; } } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int R_igraph_attribute_get_bool_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_bool_t *value) { /* TODO: serialization */ SEXP val=VECTOR_ELT(graph->attr, 2); SEXP va=R_igraph_getListElement(val, name); igraph_vector_bool_t newvalue; if (va == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_LOGICAL(va)) { IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); } if (igraph_vs_is_all(&vs)) { R_SEXP_to_vector_bool_copy(va, &newvalue); igraph_vector_bool_destroy(value); *value=newvalue; } else { igraph_vit_t it; long int i=0; IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); IGRAPH_FINALLY(igraph_vit_destroy, &it); IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_VIT_SIZE(it))); while (!IGRAPH_VIT_END(it)) { long int v=IGRAPH_VIT_GET(it); VECTOR(*value)[i]=LOGICAL(va)[v]; IGRAPH_VIT_NEXT(it); i++; } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int R_igraph_attribute_get_string_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_strvector_t *value) { /* TODO: serialization */ SEXP val, va; val=VECTOR_ELT(graph->attr, 2); va=R_igraph_getListElement(val, name); if (va == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_CHARACTER(va)) { IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); } if (igraph_vs_is_all(&vs)) { R_igraph_SEXP_to_strvector_copy(va, value); } else { igraph_vit_t it; long int i=0; IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); IGRAPH_FINALLY(igraph_vit_destroy, &it); IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_VIT_SIZE(it))); while (!IGRAPH_VIT_END(it)) { long int v=IGRAPH_VIT_GET(it); const char *str=CHAR(STRING_ELT(va, v)); IGRAPH_CHECK(igraph_strvector_set(value, i, str)); IGRAPH_VIT_NEXT(it); i++; } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int R_igraph_attribute_get_numeric_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value) { /* TODO: serialization */ SEXP eal=VECTOR_ELT(graph->attr, 3); SEXP ea=R_igraph_getListElement(eal, name); igraph_vector_t newvalue; if (ea == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_NUMERIC(ea) && !IS_INTEGER(ea)) { IGRAPH_ERROR("Attribute is not numeric", IGRAPH_EINVAL); } if (igraph_es_is_all(&es)) { R_SEXP_to_vector_copy(AS_NUMERIC(ea), &newvalue); igraph_vector_destroy(value); *value=newvalue; } else { igraph_eit_t it; long int i=0; IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_EIT_SIZE(it))); if (IS_NUMERIC(ea)) { while (!IGRAPH_EIT_END(it)) { long int e=IGRAPH_EIT_GET(it); VECTOR(*value)[i]=REAL(ea)[e]; IGRAPH_EIT_NEXT(it); i++; } } else { /* INTEGER */ while (!IGRAPH_EIT_END(it)) { long int e=IGRAPH_EIT_GET(it); VECTOR(*value)[i]=INTEGER(ea)[e]; IGRAPH_EIT_NEXT(it); i++; } } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int R_igraph_attribute_get_bool_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value) { /* TODO: serialization */ SEXP eal=VECTOR_ELT(graph->attr, 3); SEXP ea=R_igraph_getListElement(eal, name); igraph_vector_bool_t newvalue; if (ea == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_LOGICAL(ea)) { IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); } if (igraph_es_is_all(&es)) { R_SEXP_to_vector_bool_copy(ea, &newvalue); igraph_vector_bool_destroy(value); *value=newvalue; } else { igraph_eit_t it; long int i=0; IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_EIT_SIZE(it))); while (!IGRAPH_EIT_END(it)) { long int e=IGRAPH_EIT_GET(it); VECTOR(*value)[i]=LOGICAL(ea)[e]; IGRAPH_EIT_NEXT(it); i++; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } int R_igraph_attribute_get_string_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_strvector_t *value) { /* TODO: serialization */ SEXP eal=VECTOR_ELT(graph->attr, 3); SEXP ea=R_igraph_getListElement(eal, name); if (ea == R_NilValue) { IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); } if (!IS_CHARACTER(ea)) { IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); } if (igraph_es_is_all(&es)) { R_igraph_SEXP_to_strvector_copy(ea, value); } else { igraph_eit_t it; long int i=0; IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); IGRAPH_FINALLY(igraph_eit_destroy, &it); IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_EIT_SIZE(it))); while (!IGRAPH_EIT_END(it)) { long int e=IGRAPH_EIT_GET(it); const char *str=CHAR(STRING_ELT(ea, e)); IGRAPH_CHECK(igraph_strvector_set(value, i, str)); IGRAPH_EIT_NEXT(it); i++; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } return 0; } SEXP R_igraph_ac_sum_numeric(SEXP attr, const igraph_vector_ptr_t *merges) { SEXP res; SEXP attr2; long int i, len=igraph_vector_ptr_size(merges); PROTECT(attr2=AS_NUMERIC(attr)); PROTECT(res=NEW_NUMERIC(len)); for (i=0; i 0 ? REAL(attr2)[(long) VECTOR(*v)[0] ] : NA_REAL; for (j=1; j 0 ? REAL(attr2)[(long) VECTOR(*v)[0] ] : NA_REAL; for (j=1; j m) { m=val; } } REAL(res)[i] = m; } UNPROTECT(2); return res; } SEXP R_igraph_ac_random_numeric(SEXP attr, const igraph_vector_ptr_t *merges) { SEXP res; SEXP attr2; long int i, len=igraph_vector_ptr_size(merges); PROTECT(attr2=AS_NUMERIC(attr)); PROTECT(res=NEW_NUMERIC(len)); RNG_BEGIN(); for (i=0; i0 ? 0.0 : NA_REAL; for (j=0; j0) { s=s/n; } REAL(res)[i] = s; } UNPROTECT(2); return res; } SEXP R_igraph_ac_median_numeric(SEXP attr, const igraph_vector_ptr_t *merges) { SEXP res; SEXP attr2; long int i, len=igraph_vector_ptr_size(merges); PROTECT(attr2=AS_NUMERIC(attr)); PROTECT(res=NEW_NUMERIC(len)); for (i=0; iattr; SEXP toattr=newgraph->attr; SEXP val=VECTOR_ELT(attr, 2); long int i, j, valno=GET_LENGTH(val); SEXP names, newnames; SEXP res; int keepno=0; int *TODO; void **funcs; /* Create the TODO list first */ PROTECT(names=GET_NAMES(val)); TODO=igraph_Calloc(valno, int); if (!TODO) { IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, TODO); funcs=igraph_Calloc(valno, void*); if (!funcs) { IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, funcs); for (i=0; iattr; SEXP toattr=newgraph->attr; SEXP eal=VECTOR_ELT(attr, 3); long int i, j, ealno=GET_LENGTH(eal); SEXP names, newnames; SEXP res; int keepno=0; int *TODO; void **funcs; /* Create the TODO list first */ PROTECT(names=GET_NAMES(eal)); TODO=igraph_Calloc(ealno, int); if (!TODO) { IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, TODO); funcs=igraph_Calloc(ealno, void*); if (!funcs) { IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, funcs); for (i=0; idata, REAL(result)); PROTECT(dim=NEW_INTEGER(3)); INTEGER(dim)[0]=(int) igraph_array3_n(a, 1); INTEGER(dim)[1]=(int) igraph_array3_n(a, 2); INTEGER(dim)[2]=(int) igraph_array3_n(a, 3); SET_DIM(result, dim); UNPROTECT(2); return result; } SEXP R_igraph_0orarray3_to_SEXP(const igraph_array3_t *a) { SEXP result; if (a) { PROTECT(result=R_igraph_array3_to_SEXP(a)); } else { PROTECT(result=R_NilValue); } UNPROTECT(1); return result; } SEXP R_igraph_strvector_to_SEXP(const igraph_strvector_t *m) { SEXP result; long int i; char *str; long int len; len=igraph_strvector_size(m); PROTECT(result=NEW_CHARACTER(len)); for (i=0; idirected; memcpy(REAL(VECTOR_ELT(result, 2)), graph->from.stor_begin, sizeof(igraph_real_t)*(size_t) no_of_edges); memcpy(REAL(VECTOR_ELT(result, 3)), graph->to.stor_begin, sizeof(igraph_real_t)*(size_t) no_of_edges); memcpy(REAL(VECTOR_ELT(result, 4)), graph->oi.stor_begin, sizeof(igraph_real_t)*(size_t) no_of_edges); memcpy(REAL(VECTOR_ELT(result, 5)), graph->ii.stor_begin, sizeof(igraph_real_t)*(size_t) no_of_edges); memcpy(REAL(VECTOR_ELT(result, 6)), graph->os.stor_begin, sizeof(igraph_real_t)*(size_t) (no_of_nodes+1)); memcpy(REAL(VECTOR_ELT(result, 7)), graph->is.stor_begin, sizeof(igraph_real_t)*(size_t) (no_of_nodes+1)); SET_CLASS(result, ScalarString(CREATE_STRING_VECTOR("igraph"))); /* Attributes */ SET_VECTOR_ELT(result, 8, graph->attr); REAL(VECTOR_ELT(graph->attr, 0))[0] += 1; UNPROTECT(1); return result; } SEXP R_igraph_vectorlist_to_SEXP(const igraph_vector_ptr_t *ptr) { SEXP result; long int i, n=igraph_vector_ptr_size(ptr); PROTECT(result=NEW_LIST(n)); for (i=0; ileft)); SET_VECTOR_ELT(result, 1, R_igraph_vector_to_SEXP(&hrg->right)); SET_VECTOR_ELT(result, 2, R_igraph_vector_to_SEXP(&hrg->prob)); SET_VECTOR_ELT(result, 3, R_igraph_vector_to_SEXP(&hrg->edges)); SET_VECTOR_ELT(result, 4, R_igraph_vector_to_SEXP(&hrg->vertices)); PROTECT(names=NEW_CHARACTER(5)); SET_STRING_ELT(names, 0, mkChar("left")); SET_STRING_ELT(names, 1, mkChar("right")); SET_STRING_ELT(names, 2, mkChar("prob")); SET_STRING_ELT(names, 3, mkChar("edges")); SET_STRING_ELT(names, 4, mkChar("vertices")); SET_NAMES(result, names); UNPROTECT(2); return result; } int R_SEXP_to_hrg(SEXP shrg, igraph_hrg_t *hrg) { R_SEXP_to_vector(VECTOR_ELT(shrg, 0), &hrg->left); R_SEXP_to_vector(VECTOR_ELT(shrg, 1), &hrg->right); R_SEXP_to_vector(VECTOR_ELT(shrg, 2), &hrg->prob); R_SEXP_to_vector(VECTOR_ELT(shrg, 3), &hrg->edges); R_SEXP_to_vector(VECTOR_ELT(shrg, 4), &hrg->vertices); return 0; } int R_SEXP_to_hrg_copy(SEXP shrg, igraph_hrg_t *hrg) { R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 0), &hrg->left); R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 1), &hrg->right); R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 2), &hrg->prob); R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 3), &hrg->edges); R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 4), &hrg->vertices); return 0; } SEXP R_igraph_plfit_result_to_SEXP(const igraph_plfit_result_t *plfit) { SEXP result, names; PROTECT(result=NEW_LIST(6)); SET_VECTOR_ELT(result, 0, ScalarLogical(plfit->continuous)); SET_VECTOR_ELT(result, 1, ScalarReal(plfit->alpha)); SET_VECTOR_ELT(result, 2, ScalarReal(plfit->xmin)); SET_VECTOR_ELT(result, 3, ScalarReal(plfit->L)); SET_VECTOR_ELT(result, 4, ScalarReal(plfit->D)); SET_VECTOR_ELT(result, 5, ScalarReal(plfit->p)); PROTECT(names=NEW_CHARACTER(6)); SET_STRING_ELT(names, 0, mkChar("continuous")); SET_STRING_ELT(names, 1, mkChar("alpha")); SET_STRING_ELT(names, 2, mkChar("xmin")); SET_STRING_ELT(names, 3, mkChar("logLik")); SET_STRING_ELT(names, 4, mkChar("KS.stat")); SET_STRING_ELT(names, 5, mkChar("KS.p")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_maxflow_stats_to_SEXP(const igraph_maxflow_stats_t *st) { SEXP result, names; PROTECT(result=NEW_LIST(5)); SET_VECTOR_ELT(result, 0, ScalarInteger(st->nopush)); SET_VECTOR_ELT(result, 1, ScalarInteger(st->norelabel)); SET_VECTOR_ELT(result, 2, ScalarInteger(st->nogap)); SET_VECTOR_ELT(result, 3, ScalarInteger(st->nogapnodes)); SET_VECTOR_ELT(result, 4, ScalarInteger(st->nobfs)); PROTECT(names=NEW_CHARACTER(5)); SET_STRING_ELT(names, 0, mkChar("nopush")); SET_STRING_ELT(names, 1, mkChar("norelabel")); SET_STRING_ELT(names, 2, mkChar("nogap")); SET_STRING_ELT(names, 3, mkChar("nogapnodes")); SET_STRING_ELT(names, 4, mkChar("nobfs")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_sirlist_to_SEXP(const igraph_vector_ptr_t *sl) { SEXP result, names; int i, n=igraph_vector_ptr_size(sl); PROTECT(result=NEW_LIST(n)); PROTECT(names=NEW_CHARACTER(4)); SET_STRING_ELT(names, 0, mkChar("times")); SET_STRING_ELT(names, 1, mkChar("NS")); SET_STRING_ELT(names, 2, mkChar("NI")); SET_STRING_ELT(names, 3, mkChar("NR")); for (i=0; itimes)); SET_VECTOR_ELT(tmp, 1, R_igraph_vector_int_to_SEXP(&sir->no_s)); SET_VECTOR_ELT(tmp, 2, R_igraph_vector_int_to_SEXP(&sir->no_i)); SET_VECTOR_ELT(tmp, 3, R_igraph_vector_int_to_SEXP(&sir->no_r)); SET_VECTOR_ELT(result, i, tmp); SET_NAMES(tmp, names); UNPROTECT(1); } UNPROTECT(2); return result; } void R_igraph_sirlist_destroy(igraph_vector_ptr_t *sl) { int i, n=igraph_vector_ptr_size(sl); for (i=0; itimes); igraph_vector_int_destroy(&sir->no_s); igraph_vector_int_destroy(&sir->no_i); igraph_vector_int_destroy(&sir->no_r); igraph_free(sir); } igraph_vector_ptr_destroy(sl); } int R_SEXP_to_sparsemat(SEXP pakl, igraph_sparsemat_t *akl) { SEXP Dim=GET_SLOT(pakl, install("Dim")); SEXP i=GET_SLOT(pakl, install("i")); SEXP p=GET_SLOT(pakl, install("p")); SEXP x=GET_SLOT(pakl, install("x")); igraph_i_sparsemat_view(akl, /*nzmax=*/ GET_LENGTH(x), /*m=*/ INTEGER(Dim)[0], /*n=*/ INTEGER(Dim)[1], /*p=*/ INTEGER(p), /*i=*/ INTEGER(i), /*x=*/ REAL(x), /*nz=*/ -1); return 0; } int R_SEXP_to_pagerank_power_options(SEXP popt, igraph_pagerank_power_options_t *opt) { opt->niter=INTEGER(AS_INTEGER(R_igraph_getListElement(popt, "niter")))[0]; opt->eps=REAL(R_igraph_getListElement(popt, "eps"))[0]; return 0; } SEXP R_igraph_sparsemat_to_SEXP_triplet(const igraph_sparsemat_t *sp) { SEXP res, names; int nz=igraph_sparsemat_nonzero_storage(sp); PROTECT(res=NEW_LIST(5)); SET_VECTOR_ELT(res, 0, ScalarString(CREATE_STRING_VECTOR("triplet"))); SET_VECTOR_ELT(res, 1, NEW_INTEGER(2)); INTEGER(VECTOR_ELT(res, 1))[0] = (int) igraph_sparsemat_nrow(sp); INTEGER(VECTOR_ELT(res, 1))[1] = (int) igraph_sparsemat_ncol(sp); SET_VECTOR_ELT(res, 2, NEW_INTEGER(nz)); SET_VECTOR_ELT(res, 3, NEW_INTEGER(nz)); SET_VECTOR_ELT(res, 4, NEW_NUMERIC(nz)); if (nz > 0) { igraph_vector_int_t i, j; igraph_vector_t x; igraph_vector_int_view(&i, INTEGER(VECTOR_ELT(res, 2)), nz); igraph_vector_int_view(&j, INTEGER(VECTOR_ELT(res, 3)), nz); igraph_vector_view(&x, REAL(VECTOR_ELT(res, 4)), nz); igraph_sparsemat_getelements(sp, &j, &i, &x); } PROTECT(names=NEW_CHARACTER(5)); SET_STRING_ELT(names, 0, mkChar("type")); SET_STRING_ELT(names, 1, mkChar("dim")); SET_STRING_ELT(names, 2, mkChar("p")); SET_STRING_ELT(names, 3, mkChar("i")); SET_STRING_ELT(names, 4, mkChar("x")); SET_NAMES(res, names); SET_CLASS(res, ScalarString(CREATE_STRING_VECTOR("igraph.tmp.sparse"))); UNPROTECT(2); return res; } SEXP R_igraph_sparsemat_to_SEXP_cc(const igraph_sparsemat_t *sp) { SEXP res, names; int nz=igraph_sparsemat_nonzero_storage(sp); int m=(int) igraph_sparsemat_nrow(sp); int n=(int) igraph_sparsemat_ncol(sp); PROTECT(res=NEW_LIST(5)); SET_VECTOR_ELT(res, 0, ScalarString(CREATE_STRING_VECTOR("cc"))); SET_VECTOR_ELT(res, 1, NEW_INTEGER(2)); INTEGER(VECTOR_ELT(res, 1))[0] = m; INTEGER(VECTOR_ELT(res, 1))[1] = n; SET_VECTOR_ELT(res, 2, NEW_INTEGER(n+1)); SET_VECTOR_ELT(res, 3, NEW_INTEGER(nz)); SET_VECTOR_ELT(res, 4, NEW_NUMERIC(nz)); if (nz > 0) { igraph_vector_int_t i, p; igraph_vector_t x; igraph_vector_int_view(&p, INTEGER(VECTOR_ELT(res, 2)), n+1); igraph_vector_int_view(&i, INTEGER(VECTOR_ELT(res, 3)), nz); igraph_vector_view(&x, REAL(VECTOR_ELT(res, 4)), nz); igraph_sparsemat_getelements_sorted(sp, &i, &p, &x); } PROTECT(names=NEW_CHARACTER(5)); SET_STRING_ELT(names, 0, mkChar("type")); SET_STRING_ELT(names, 1, mkChar("dim")); SET_STRING_ELT(names, 2, mkChar("p")); SET_STRING_ELT(names, 3, mkChar("i")); SET_STRING_ELT(names, 4, mkChar("x")); SET_NAMES(res, names); SET_CLASS(res, ScalarString(CREATE_STRING_VECTOR("igraph.tmp.sparse"))); UNPROTECT(2); return res; } SEXP R_igraph_sparsemat_to_SEXP(const igraph_sparsemat_t *sp) { if (igraph_sparsemat_is_triplet(sp)) { return R_igraph_sparsemat_to_SEXP_triplet(sp); } else { return R_igraph_sparsemat_to_SEXP_cc(sp); } } SEXP R_igraph_0orsparsemat_to_SEXP(const igraph_sparsemat_t *sp) { if (!sp) { return R_NilValue; } else { return R_igraph_sparsemat_to_SEXP(sp); } } int R_SEXP_to_igraph_adjlist(SEXP vectorlist, igraph_adjlist_t *ptr) { int length=GET_LENGTH(vectorlist); int i; ptr->length=length; ptr->adjs = (igraph_vector_int_t*) R_alloc((size_t) length, sizeof(igraph_vector_int_t)); for (i=0; iadjs[i], INTEGER(vec), GET_LENGTH(vec)); } return 0; } int R_igraph_SEXP_to_0orvectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr) { if (!isNull(vectorlist)) { return R_igraph_SEXP_to_vectorlist(vectorlist, ptr); } return 0; } int R_igraph_SEXP_to_vectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr) { int length=GET_LENGTH(vectorlist); int i; igraph_vector_t *vecs; igraph_vector_t **vecsptr; vecs = (igraph_vector_t *) R_alloc((size_t) length, sizeof(igraph_vector_t)); vecsptr = (igraph_vector_t **) R_alloc((size_t) length, sizeof(igraph_vector_t*)); igraph_vector_ptr_view(ptr, (void**) vecsptr, length); for (i=0; ilen=GET_LENGTH(rval); sv->data=(char**) R_alloc((size_t) (sv->len), sizeof(char*)); for (i=0; ilen; i++) { sv->data[i]=(char*) CHAR(STRING_ELT(rval, i)); } return 0; } int R_igraph_SEXP_to_strvector_copy(SEXP rval, igraph_strvector_t *sv) { long int i; igraph_strvector_init(sv, GET_LENGTH(rval)); for (i=0; ilen; i++) { igraph_strvector_set(sv, i, CHAR(STRING_ELT(rval, i))); } return 0; } int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v) { v->stor_begin=REAL(sv); v->stor_end=v->stor_begin+GET_LENGTH(sv); v->end=v->stor_end; return 0; } int R_SEXP_to_vector_copy(SEXP sv, igraph_vector_t *v) { return igraph_vector_init_copy(v, REAL(sv), GET_LENGTH(sv)); } int R_SEXP_to_vector_bool(SEXP sv, igraph_vector_bool_t *v) { v->stor_begin=LOGICAL(sv); v->stor_end=v->stor_begin+GET_LENGTH(sv); v->end=v->stor_end; return 0; } int R_SEXP_to_vector_bool_copy(SEXP sv, igraph_vector_bool_t *v) { long int i, n=GET_LENGTH(sv); int *svv=LOGICAL(sv); igraph_vector_bool_init(v, n); for (i=0; istor_begin=(int*) INTEGER(sv); v->stor_end=v->stor_begin+GET_LENGTH(sv); v->end=v->stor_end; return 0; } int R_SEXP_to_vector_long_copy(SEXP sv, igraph_vector_long_t *v) { long int i, n=GET_LENGTH(sv); double *svv=REAL(sv); igraph_vector_long_init(v, n); for (i=0; idata); akl->nrow=INTEGER(GET_DIM(pakl))[0]; akl->ncol=INTEGER(GET_DIM(pakl))[1]; return 0; } int R_SEXP_to_igraph_matrix_copy(SEXP pakl, igraph_matrix_t *akl) { igraph_vector_init_copy(&akl->data, REAL(pakl), GET_LENGTH(pakl)); akl->nrow=INTEGER(GET_DIM(pakl))[0]; akl->ncol=INTEGER(GET_DIM(pakl))[1]; return 0; } int R_SEXP_to_vector_complex(SEXP pv, igraph_vector_complex_t *v) { v->stor_begin=(igraph_complex_t*) COMPLEX(pv); v->stor_end=v->stor_begin+GET_LENGTH(pv); v->end=v->stor_end; return 0; } int R_SEXP_to_vector_complex_copy(SEXP pv, igraph_vector_complex_t *v) { igraph_vector_complex_init_copy(v, (igraph_complex_t*) COMPLEX(pv), GET_LENGTH(pv)); return 0; } int R_SEXP_to_matrix_complex(SEXP pakl, igraph_matrix_complex_t *akl) { R_SEXP_to_vector_complex(pakl, &akl->data); akl->nrow=INTEGER(GET_DIM(pakl))[0]; akl->ncol=INTEGER(GET_DIM(pakl))[1]; return 0; } int R_SEXP_to_matrix_complex_copy(SEXP pakl, igraph_matrix_complex_t *akl) { igraph_vector_complex_init_copy(&akl->data, (igraph_complex_t*) COMPLEX(pakl), GET_LENGTH(pakl)); akl->nrow=INTEGER(GET_DIM(pakl))[0]; akl->ncol=INTEGER(GET_DIM(pakl))[1]; return 0; } int R_igraph_SEXP_to_array3(SEXP rval, igraph_array3_t *a) { R_SEXP_to_vector(rval, &a->data); a->n1=INTEGER(GET_DIM(rval))[0]; a->n2=INTEGER(GET_DIM(rval))[1]; a->n3=INTEGER(GET_DIM(rval))[2]; a->n1n2=(a->n1) * (a->n2); return 0; } int R_igraph_SEXP_to_array3_copy(SEXP rval, igraph_array3_t *a) { igraph_vector_init_copy(&a->data, REAL(rval), GET_LENGTH(rval)); a->n1=INTEGER(GET_DIM(rval))[0]; a->n2=INTEGER(GET_DIM(rval))[1]; a->n3=INTEGER(GET_DIM(rval))[2]; a->n1n2=(a->n1) * (a->n2); return 0; } int R_SEXP_to_igraph(SEXP graph, igraph_t *res) { res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; R_SEXP_to_vector(VECTOR_ELT(graph, 2), &res->from); R_SEXP_to_vector(VECTOR_ELT(graph, 3), &res->to); R_SEXP_to_vector(VECTOR_ELT(graph, 4), &res->oi); R_SEXP_to_vector(VECTOR_ELT(graph, 5), &res->ii); R_SEXP_to_vector(VECTOR_ELT(graph, 6), &res->os); R_SEXP_to_vector(VECTOR_ELT(graph, 7), &res->is); /* attributes */ REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects refcount */ REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 0; /* igraph_t objects */ res->attr=VECTOR_ELT(graph, 8); return 0; } int R_SEXP_to_igraph_copy(SEXP graph, igraph_t *res) { res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; igraph_vector_init_copy(&res->from, REAL(VECTOR_ELT(graph, 2)), GET_LENGTH(VECTOR_ELT(graph, 2))); igraph_vector_init_copy(&res->to, REAL(VECTOR_ELT(graph, 3)), GET_LENGTH(VECTOR_ELT(graph, 3))); igraph_vector_init_copy(&res->oi, REAL(VECTOR_ELT(graph, 4)), GET_LENGTH(VECTOR_ELT(graph, 4))); igraph_vector_init_copy(&res->ii, REAL(VECTOR_ELT(graph, 5)), GET_LENGTH(VECTOR_ELT(graph, 5))); igraph_vector_init_copy(&res->os, REAL(VECTOR_ELT(graph, 6)), GET_LENGTH(VECTOR_ELT(graph, 6))); igraph_vector_init_copy(&res->is, REAL(VECTOR_ELT(graph, 7)), GET_LENGTH(VECTOR_ELT(graph, 7))); /* attributes */ REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects */ REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 1; /* igraph_t objects */ PROTECT(res->attr=VECTOR_ELT(graph, 8)); return 0; } /* * We have only vector type */ int R_SEXP_to_igraph_vs(SEXP rit, igraph_t *graph, igraph_vs_t *it) { igraph_vector_t *tmpv=(igraph_vector_t*)R_alloc(1,sizeof(igraph_vector_t)); igraph_vs_vector(it, igraph_vector_view(tmpv, REAL(rit), GET_LENGTH(rit))); return 0; } /* * We have only vector type */ int R_SEXP_to_igraph_es(SEXP rit, igraph_t *graph, igraph_es_t *it) { igraph_vector_t *tmpv=(igraph_vector_t*)R_alloc(1,sizeof(igraph_vector_t)); igraph_es_vector(it, igraph_vector_view(tmpv, REAL(rit), GET_LENGTH(rit))); return 0; } int R_SEXP_to_igraph_layout_drl_options(SEXP in, igraph_layout_drl_options_t *opt) { opt->edge_cut = REAL(AS_NUMERIC(R_igraph_getListElement(in, "edge.cut")))[0]; opt->init_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.iterations")))[0]; opt->init_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.temperature")))[0]; opt->init_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.attraction")))[0]; opt->init_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.damping.mult")))[0]; opt->liquid_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.iterations")))[0]; opt->liquid_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.temperature")))[0]; opt->liquid_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.attraction")))[0]; opt->liquid_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.damping.mult")))[0]; opt->expansion_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.iterations")))[0]; opt->expansion_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.temperature")))[0]; opt->expansion_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.attraction")))[0]; opt->expansion_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.damping.mult")))[0]; opt->cooldown_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.iterations")))[0]; opt->cooldown_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.temperature")))[0]; opt->cooldown_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.attraction")))[0]; opt->cooldown_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.damping.mult")))[0]; opt->crunch_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.iterations")))[0]; opt->crunch_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.temperature")))[0]; opt->crunch_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.attraction")))[0]; opt->crunch_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.damping.mult")))[0]; opt->simmer_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.iterations")))[0]; opt->simmer_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.temperature")))[0]; opt->simmer_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.attraction")))[0]; opt->simmer_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.damping.mult")))[0]; return 0; } int R_SEXP_to_igraph_arpack_options(SEXP in, igraph_arpack_options_t *opt) { const char *tmpstr; igraph_arpack_options_init(opt); opt -> bmat[0] = CHAR(STRING_ELT(AS_CHARACTER (R_igraph_getListElement(in, "bmat")), 0))[0]; opt -> n = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "n")))[0]; tmpstr=CHAR(STRING_ELT(AS_CHARACTER(R_igraph_getListElement(in, "which")), 0)); opt -> which[0]=tmpstr[0]; opt -> which[1]=tmpstr[1]; opt -> nev = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "nev")))[0]; opt -> tol = REAL(AS_NUMERIC(R_igraph_getListElement(in, "tol")))[0]; opt -> ncv = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ncv")))[0]; opt -> ldv = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ldv")))[0]; opt -> ishift = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ishift")))[0]; opt -> mxiter = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "maxiter")))[0]; opt -> nb = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "nb")))[0]; opt -> mode = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "mode")))[0]; opt -> start = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "start")))[0]; opt -> lworkl = 0; opt -> sigma = REAL(AS_NUMERIC(R_igraph_getListElement(in, "sigma")))[0]; opt -> sigmai = REAL(AS_NUMERIC(R_igraph_getListElement(in, "sigmai")))[0]; opt -> info = opt -> start; opt->iparam[0]=opt->ishift; opt->iparam[2]=opt->mxiter; opt->iparam[3]=opt->nb; opt->iparam[6]=opt->mode; return 0; } SEXP R_igraph_arpack_options_to_SEXP(const igraph_arpack_options_t *opt) { SEXP result, names; char bmat[2], which[3]; bmat[0]=opt->bmat[0]; bmat[1]='\0'; which[0]=opt->which[0]; which[1]=opt->which[1]; which[2]='\0'; PROTECT(result = NEW_LIST(20)); SET_VECTOR_ELT(result, 0, ScalarString(CREATE_STRING_VECTOR(bmat))); SET_VECTOR_ELT(result, 1, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 1))[0]=opt->n; SET_VECTOR_ELT(result, 2, ScalarString(CREATE_STRING_VECTOR(which))); SET_VECTOR_ELT(result, 3, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 3))[0]=opt->nev; SET_VECTOR_ELT(result, 4, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 4))[0]=opt->tol; SET_VECTOR_ELT(result, 5, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 5))[0]=opt->ncv; SET_VECTOR_ELT(result, 6, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 6))[0]=opt->ldv; SET_VECTOR_ELT(result, 7, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 7))[0]=opt->ishift; SET_VECTOR_ELT(result, 8, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 8))[0]=opt->mxiter; SET_VECTOR_ELT(result, 9, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 9))[0]=opt->nb; SET_VECTOR_ELT(result, 10, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 10))[0]=opt->mode; SET_VECTOR_ELT(result, 11, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 11))[0]=opt->start; SET_VECTOR_ELT(result, 12, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 12))[0]=opt->sigma; SET_VECTOR_ELT(result, 13, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 13))[0]=opt->sigmai; SET_VECTOR_ELT(result, 14, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 14))[0]=opt->info; SET_VECTOR_ELT(result, 15, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 15))[0]=opt->iparam[2];/* mxiter */ SET_VECTOR_ELT(result, 16, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 16))[0]=opt->iparam[4];/* nconv */ SET_VECTOR_ELT(result, 17, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 17))[0]=opt->iparam[8];/* numop */ SET_VECTOR_ELT(result, 18, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 18))[0]=opt->iparam[9];/* numopb */ SET_VECTOR_ELT(result, 19, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 19))[0]=opt->iparam[10];/* numreo */ PROTECT(names=NEW_CHARACTER(20)); SET_STRING_ELT(names, 0, mkChar("bmat")); SET_STRING_ELT(names, 1, mkChar("n")); SET_STRING_ELT(names, 2, mkChar("which")); SET_STRING_ELT(names, 3, mkChar("nev")); SET_STRING_ELT(names, 4, mkChar("tol")); SET_STRING_ELT(names, 5, mkChar("ncv")); SET_STRING_ELT(names, 6, mkChar("ldv")); SET_STRING_ELT(names, 7, mkChar("ishift")); SET_STRING_ELT(names, 8, mkChar("maxiter")); SET_STRING_ELT(names, 9, mkChar("nb")); SET_STRING_ELT(names, 10, mkChar("mode")); SET_STRING_ELT(names, 11, mkChar("start")); SET_STRING_ELT(names, 12, mkChar("sigma")); SET_STRING_ELT(names, 13, mkChar("sigmai")); SET_STRING_ELT(names, 14, mkChar("info")); SET_STRING_ELT(names, 15, mkChar("iter")); SET_STRING_ELT(names, 16, mkChar("nconv")); SET_STRING_ELT(names, 17, mkChar("numop")); SET_STRING_ELT(names, 18, mkChar("numopb")); SET_STRING_ELT(names, 19, mkChar("numreo")); SET_NAMES(result, names); UNPROTECT(2); return result; } int R_SEXP_to_igraph_eigen_which(SEXP in, igraph_eigen_which_t *out) { SEXP pos=PROTECT(AS_CHARACTER(R_igraph_getListElement(in, "pos"))); SEXP balance=PROTECT(AS_CHARACTER(R_igraph_getListElement (in, "balance"))); if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "lm")) { out->pos=IGRAPH_EIGEN_LM; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sm")) { out->pos=IGRAPH_EIGEN_SM; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "la")) { out->pos=IGRAPH_EIGEN_LA; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sa")) { out->pos=IGRAPH_EIGEN_SA; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "be")) { out->pos=IGRAPH_EIGEN_BE; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "lr")) { out->pos=IGRAPH_EIGEN_LR; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sr")) { out->pos=IGRAPH_EIGEN_SR; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "li")) { out->pos=IGRAPH_EIGEN_LI; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "si")) { out->pos=IGRAPH_EIGEN_SI; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "all")) { out->pos=IGRAPH_EIGEN_ALL; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "interval")) { out->pos=IGRAPH_EIGEN_INTERVAL; } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "select")) { out->pos=IGRAPH_EIGEN_SELECT; } else { UNPROTECT(2); IGRAPH_ERROR("Unknown eigenvalue position specification", IGRAPH_EINVAL); } out->howmany=INTEGER(AS_INTEGER(R_igraph_getListElement (in, "howmany")))[0]; out->il=INTEGER(AS_INTEGER(R_igraph_getListElement(in, "il")))[0]; out->iu=INTEGER(AS_INTEGER(R_igraph_getListElement(in, "iu")))[0]; out->vl=REAL(AS_NUMERIC(R_igraph_getListElement(in, "vl")))[0]; out->vu=REAL(AS_NUMERIC(R_igraph_getListElement(in, "vu")))[0]; out->vestimate=INTEGER(AS_INTEGER(R_igraph_getListElement (in, "vestimate")))[0]; if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "none")) { out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_NONE; } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "perm")) { out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_PERM; } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "scale")) { out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE; } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "both")) { out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH; } else { UNPROTECT(2); IGRAPH_ERROR("Unknown balance specification", IGRAPH_EINVAL); } UNPROTECT(2); return 0; } SEXP R_igraph_bliss_info_to_SEXP(const igraph_bliss_info_t *info) { SEXP result, names; PROTECT(result=NEW_LIST(6)); SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 0))[0]=info->nof_nodes; SET_VECTOR_ELT(result, 1, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 1))[0]=info->nof_leaf_nodes; SET_VECTOR_ELT(result, 2, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 2))[0]=info->nof_bad_nodes; SET_VECTOR_ELT(result, 3, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 3))[0]=info->nof_canupdates; SET_VECTOR_ELT(result, 4, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 4))[0]=info->max_level; if (info->group_size) { SET_VECTOR_ELT(result, 5, NEW_CHARACTER(1)); SET_STRING_ELT(VECTOR_ELT(result, 5), 0, mkChar(info->group_size)); } else { SET_VECTOR_ELT(result, 5, R_NilValue); } PROTECT(names=NEW_CHARACTER(6)); SET_STRING_ELT(names, 0, mkChar("nof_nodes")); SET_STRING_ELT(names, 1, mkChar("nof_leaf_nodes")); SET_STRING_ELT(names, 2, mkChar("nof_bad_nodes")); SET_STRING_ELT(names, 3, mkChar("nof_canupdates")); SET_STRING_ELT(names, 4, mkChar("max_level")); SET_STRING_ELT(names, 5, mkChar("group_size")); SET_NAMES(result, names); UNPROTECT(2); return result; } /*******************************************************************/ SEXP R_igraph_mybracket(SEXP graph, SEXP pidx) { int idx=INTEGER(pidx)[0]-1; return duplicate(VECTOR_ELT(graph, idx)); } SEXP R_igraph_mybracket2(SEXP graph, SEXP pidx1, SEXP pidx2) { int idx1=INTEGER(pidx1)[0]-1; int idx2=INTEGER(pidx2)[0]-1; return duplicate(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2)); } SEXP R_igraph_mybracket2_names(SEXP graph, SEXP pidx1, SEXP pidx2) { SEXP result; int idx1=INTEGER(pidx1)[0]-1; int idx2=INTEGER(pidx2)[0]-1; result=duplicate(GET_NAMES(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2))); return result; } SEXP R_igraph_mybracket2_copy(SEXP graph, SEXP pidx1, SEXP pidx2) { int idx1=INTEGER(pidx1)[0]-1; int idx2=INTEGER(pidx2)[0]-1; return duplicate(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2)); } SEXP R_igraph_mybracket2_set(SEXP graph, SEXP pidx1, SEXP pidx2, SEXP value) { SEXP newgraph; int idx1=INTEGER(pidx1)[0]-1; int idx2=INTEGER(pidx2)[0]-1; PROTECT(newgraph=duplicate(graph)); SET_VECTOR_ELT(VECTOR_ELT(newgraph, idx1), idx2, value); UNPROTECT(1); return newgraph; } SEXP R_igraph_mybracket3_set(SEXP graph, SEXP pidx1, SEXP pidx2, SEXP pname, SEXP value) { SEXP newgraph; int idx1=INTEGER(pidx1)[0]-1; int idx2=INTEGER(pidx2)[0]-1; const char *name=CHAR(STRING_ELT(pname, 0)); SEXP attrs, names; int i, n; PROTECT(newgraph=duplicate(graph)); attrs=VECTOR_ELT(VECTOR_ELT(newgraph, idx1), idx2); names=getAttrib(attrs, R_NamesSymbol); n=length(attrs); for (i=0; i100) { igraph_shortest_paths_johnson(&g, &res, vs, to, pw); } else if (negw) { igraph_shortest_paths_bellman_ford(&g, &res, vs, to, pw, (igraph_neimode_t) mode); } else { /* This one chooses 'unweighted' if there are no weights */ igraph_shortest_paths_dijkstra(&g, &res, vs, to, pw, (igraph_neimode_t) mode); } break; case 1: /* unweighted */ igraph_shortest_paths(&g, &res, vs, to, (igraph_neimode_t) mode); break; case 2: /* dijkstra */ igraph_shortest_paths_dijkstra(&g, &res, vs, to, pw, (igraph_neimode_t) mode); break; case 3: /* bellman-ford */ igraph_shortest_paths_bellman_ford(&g, &res, vs, to, pw, (igraph_neimode_t) mode); break; case 4: /* johnson */ igraph_shortest_paths_johnson(&g, &res, vs, to, pw); break; } PROTECT(result=R_igraph_matrix_to_SEXP(&res)); igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); UNPROTECT(1); return result; } SEXP R_igraph_lattice(SEXP pdimvector, SEXP pnei, SEXP pdirected, SEXP pmutual, SEXP pcircular) { igraph_t g; igraph_vector_t dimvector; igraph_integer_t nei=(igraph_integer_t) REAL(pnei)[0]; igraph_bool_t directed=LOGICAL(pdirected)[0]; igraph_bool_t mutual=LOGICAL(pmutual)[0]; igraph_bool_t circular=LOGICAL(pcircular)[0]; SEXP result; R_SEXP_to_vector(pdimvector, &dimvector); igraph_lattice(&g, &dimvector, nei, directed, mutual, circular); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_barabasi_game(SEXP pn, SEXP ppower, SEXP pm, SEXP poutseq, SEXP poutpref, SEXP pA, SEXP pdirected, SEXP palgo, SEXP pstart) { igraph_t g; igraph_integer_t n=(igraph_integer_t) REAL(pn)[0]; igraph_real_t power=REAL(ppower)[0]; igraph_integer_t m=(igraph_integer_t) REAL(pm)[0]; igraph_vector_t outseq, *myoutseq=0; igraph_bool_t outpref=LOGICAL(poutpref)[0]; igraph_real_t A=REAL(pA)[0]; igraph_bool_t directed=LOGICAL(pdirected)[0]; igraph_barabasi_algorithm_t algo=(igraph_barabasi_algorithm_t) REAL(palgo)[0]; igraph_t start, *ppstart=0; SEXP result; if (!isNull(poutseq)) { R_SEXP_to_vector(poutseq, &outseq); myoutseq=&outseq; } if (!isNull(pstart)) { R_SEXP_to_igraph(pstart, &start); ppstart=&start; } igraph_barabasi_game(&g, n, power, m, &outseq, outpref, A, directed, algo, ppstart); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_recent_degree_game(SEXP pn, SEXP ppower, SEXP pwindow, SEXP pm, SEXP poutseq, SEXP poutpref, SEXP pzero_appeal, SEXP pdirected) { igraph_t g; igraph_integer_t n=(igraph_integer_t) REAL(pn)[0]; igraph_real_t power=REAL(ppower)[0]; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_integer_t m=(igraph_integer_t) REAL(pm)[0]; igraph_vector_t outseq; igraph_bool_t outpref=LOGICAL(poutpref)[0]; igraph_bool_t directed=LOGICAL(pdirected)[0]; igraph_real_t zero_appeal=REAL(pzero_appeal)[0]; SEXP result; R_SEXP_to_vector(poutseq, &outseq); igraph_recent_degree_game(&g, n, power, window, m, &outseq, outpref, zero_appeal, directed); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_layout_kamada_kawai(SEXP graph, SEXP pniter, SEXP pinitemp, SEXP pcoolexp, SEXP pkkconst, SEXP psigma, SEXP start, SEXP pfixz, SEXP pminx, SEXP pmaxx, SEXP pminy, SEXP pmaxy, SEXP pminz, SEXP pmaxz) { igraph_t g; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_real_t initemp=REAL(pinitemp)[0]; igraph_real_t coolexp=REAL(pcoolexp)[0]; igraph_real_t kkconst=REAL(pkkconst)[0]; igraph_real_t sigma=REAL(psigma)[0]; igraph_vector_t minx, maxx, *ppminx=0, *ppmaxx=0; igraph_vector_t miny, maxy, *ppminy=0, *ppmaxy=0; igraph_matrix_t res; SEXP result; R_SEXP_to_igraph(graph, &g); if (isNull(start)) { igraph_matrix_init(&res, 0, 0); } else { R_SEXP_to_igraph_matrix_copy(start, &res); } if (!isNull(pminx)) { ppminx=&minx; R_SEXP_to_vector(pminx, &minx); } if (!isNull(pmaxx)) { ppmaxx=&maxx; R_SEXP_to_vector(pmaxx, &maxx); } if (!isNull(pminy)) { ppminy=&miny; R_SEXP_to_vector(pminy, &miny); } if (!isNull(pmaxy)) { ppmaxy=&maxy; R_SEXP_to_vector(pmaxy, &maxy); } igraph_layout_kamada_kawai(&g, &res, niter, sigma, initemp, coolexp, kkconst, !isNull(start), ppminx, ppmaxx, ppminy, ppmaxy); PROTECT(result=R_igraph_matrix_to_SEXP(&res)); igraph_matrix_destroy(&res); UNPROTECT(1); return result; } SEXP R_igraph_layout_kamada_kawai_3d(SEXP graph, SEXP pniter, SEXP pinitemp, SEXP pcoolexp, SEXP pkkconst, SEXP psigma, SEXP start, SEXP pfixz, SEXP pminx, SEXP pmaxx, SEXP pminy, SEXP pmaxy, SEXP pminz, SEXP pmaxz) { igraph_t g; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_real_t initemp=REAL(pinitemp)[0]; igraph_real_t coolexp=REAL(pcoolexp)[0]; igraph_real_t kkconst=REAL(pkkconst)[0]; igraph_real_t sigma=REAL(psigma)[0]; igraph_bool_t fixz=LOGICAL(pfixz)[0]; igraph_vector_t minx, maxx, *ppminx=0, *ppmaxx=0; igraph_vector_t miny, maxy, *ppminy=0, *ppmaxy=0; igraph_vector_t minz, maxz, *ppminz=0, *ppmaxz=0; igraph_matrix_t res; SEXP result; R_SEXP_to_igraph(graph, &g); if (isNull(start)) { igraph_matrix_init(&res, 0, 0); } else { R_SEXP_to_igraph_matrix_copy(start, &res); } if (!isNull(pminx)) { ppminx=&minx; R_SEXP_to_vector(pminx, &minx); } if (!isNull(pmaxx)) { ppmaxx=&maxx; R_SEXP_to_vector(pmaxx, &maxx); } if (!isNull(pminy)) { ppminy=&miny; R_SEXP_to_vector(pminy, &miny); } if (!isNull(pmaxy)) { ppmaxy=&maxy; R_SEXP_to_vector(pmaxy, &maxy); } if (!isNull(pminz)) { ppminz=&minz; R_SEXP_to_vector(pminz, &minz); } if (!isNull(pmaxz)) { ppmaxz=&maxz; R_SEXP_to_vector(pmaxz, &maxz); } igraph_layout_kamada_kawai_3d(&g, &res, niter, sigma, initemp, coolexp, kkconst, !isNull(start), fixz, ppminx, ppmaxx, ppminy, ppmaxy, ppminz, ppmaxz); PROTECT(result=R_igraph_matrix_to_SEXP(&res)); igraph_matrix_destroy(&res); UNPROTECT(1); return result; } SEXP R_igraph_layout_graphopt(SEXP graph, SEXP pniter, SEXP pcharge, SEXP pmass, SEXP pspring_length, SEXP pspring_constant, SEXP pmax_sa_movement, SEXP start) { igraph_t g; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_real_t charge=REAL(pcharge)[0]; igraph_real_t mass=REAL(pmass)[0]; igraph_real_t spring_length=REAL(pspring_length)[0]; igraph_real_t spring_constant=REAL(pspring_constant)[0]; igraph_real_t max_sa_movement=REAL(pmax_sa_movement)[0]; igraph_matrix_t res; SEXP result; R_SEXP_to_igraph(graph, &g); if (isNull(start)) { igraph_matrix_init(&res, 0, 0); } else { R_SEXP_to_igraph_matrix_copy(start, &res); } igraph_layout_graphopt(&g, &res, niter, charge, mass, spring_length, spring_constant, max_sa_movement, !isNull(start)); PROTECT(result=R_igraph_matrix_to_SEXP(&res)); igraph_matrix_destroy(&res); UNPROTECT(1); return result; } SEXP R_igraph_layout_lgl(SEXP graph, SEXP pmaxiter, SEXP pmaxdelta, SEXP parea, SEXP pcoolexp, SEXP prepulserad, SEXP pcellsize, SEXP proot) { igraph_t g; igraph_matrix_t res; igraph_integer_t maxiter=(igraph_integer_t) REAL(pmaxiter)[0]; igraph_real_t maxdelta=REAL(pmaxdelta)[0]; igraph_real_t area=REAL(parea)[0]; igraph_real_t coolexp=REAL(pcoolexp)[0]; igraph_real_t repulserad=REAL(prepulserad)[0]; igraph_real_t cellsize=REAL(pcellsize)[0]; igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; SEXP result; R_SEXP_to_igraph(graph, &g); igraph_matrix_init(&res, 0, 0); igraph_layout_lgl(&g, &res, maxiter, maxdelta, area, coolexp, repulserad, cellsize, root); PROTECT(result=R_igraph_matrix_to_SEXP(&res)); igraph_matrix_destroy(&res); UNPROTECT(1); return result; } SEXP R_igraph_layout_fruchterman_reingold_grid(SEXP graph, SEXP pniter, SEXP pmaxdelta, SEXP parea, SEXP pcoolexp, SEXP prepulserad, SEXP pcellsize, SEXP start, SEXP pweights) { igraph_t g; igraph_matrix_t res; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_real_t maxdelta=REAL(pmaxdelta)[0]; igraph_real_t area=REAL(parea)[0]; igraph_real_t coolexp=REAL(pcoolexp)[0]; igraph_real_t repulserad=REAL(prepulserad)[0]; igraph_real_t cellsize=REAL(pcellsize)[0]; igraph_bool_t use_seed=!isNull(start); igraph_vector_t weights, *ppweights=0; SEXP result; R_SEXP_to_igraph(graph, &g); if (!isNull(pweights)) { R_SEXP_to_vector(pweights, &weights);ppweights=&weights; } if (use_seed) { R_SEXP_to_igraph_matrix_copy(start, &res); } else { igraph_matrix_init(&res, 0, 0); } igraph_layout_grid_fruchterman_reingold(&g, &res, niter, maxdelta, area, coolexp, repulserad, cellsize, use_seed, ppweights); PROTECT(result=R_igraph_matrix_to_SEXP(&res)); igraph_matrix_destroy(&res); UNPROTECT(1); return result; } SEXP R_igraph_minimum_spanning_tree_unweighted(SEXP graph) { igraph_t g; igraph_t mst; SEXP result; R_SEXP_to_igraph(graph, &g); igraph_minimum_spanning_tree_unweighted(&g, &mst); PROTECT(result=R_igraph_to_SEXP(&mst)); igraph_destroy(&mst); UNPROTECT(1); return result; } SEXP R_igraph_minimum_spanning_tree_prim(SEXP graph, SEXP pweights) { igraph_t g; igraph_t mst; igraph_vector_t weights; SEXP result; R_SEXP_to_vector(pweights, &weights); R_SEXP_to_igraph(graph, &g); igraph_minimum_spanning_tree_prim(&g, &mst, &weights); PROTECT(result=R_igraph_to_SEXP(&mst)); igraph_destroy(&mst); UNPROTECT(1); return result; } SEXP R_igraph_get_shortest_paths(SEXP graph, SEXP pfrom, SEXP pto, SEXP pmode, SEXP pno, SEXP weights, SEXP output, SEXP ppred, SEXP pinbound) { igraph_t g; igraph_integer_t from=(igraph_integer_t) REAL(pfrom)[0]; igraph_vs_t to; igraph_integer_t mode=(igraph_integer_t) REAL(pmode)[0]; igraph_vector_t *vects, *evects; long int i; igraph_vector_ptr_t ptrvec, ptrevec; igraph_vector_t w, *pw=&w; SEXP result, result1, result2, names; igraph_bool_t verts=REAL(output)[0]==0 || REAL(output)[0]==2; igraph_bool_t edges=REAL(output)[0]==1 || REAL(output)[0]==2; igraph_bool_t pred=LOGICAL(ppred)[0]; igraph_bool_t inbound=LOGICAL(pinbound)[0]; igraph_vector_long_t predvec, inboundvec; long int no=(long int) REAL(pno)[0]; R_SEXP_to_igraph(graph, &g); R_SEXP_to_igraph_vs(pto, &g, &to); if (verts) { igraph_vector_ptr_init(&ptrvec, no); vects=(igraph_vector_t*) R_alloc((size_t) GET_LENGTH(pto), sizeof(igraph_vector_t)); for (i=0; i0) { R_igraph_SEXP_to_strvector(ppredef, &predef); predefptr=&predef; } igraph_read_graph_ncol(&g, file, predefptr, names, weights, directed); fclose(file); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_write_graph_ncol(SEXP graph, SEXP file, SEXP pnames, SEXP pweights) { igraph_t g; FILE *stream; #if HAVE_OPEN_MEMSTREAM == 1 char *bp; size_t size; #endif const char *names, *weights; SEXP result; if (isNull(pnames)) { names=0; } else { names=CHAR(STRING_ELT(pnames, 0)); } if (isNull(pweights)) { weights=0; } else { weights=CHAR(STRING_ELT(pweights, 0)); } R_SEXP_to_igraph(graph, &g); #if HAVE_OPEN_MEMSTREAM == 1 stream=open_memstream(&bp, &size); #else stream=fopen(CHAR(STRING_ELT(file,0)), "w"); #endif if (stream==0) { igraph_error("Cannot write .ncol file", __FILE__, __LINE__, IGRAPH_EFILE); } igraph_write_graph_ncol(&g, stream, names, weights); fclose(stream); #if HAVE_OPEN_MEMSTREAM == 1 PROTECT(result=allocVector(RAWSXP, size)); memcpy(RAW(result), bp, sizeof(char)*size); free(bp); #else PROTECT(result=NEW_NUMERIC(0)); #endif UNPROTECT(1); return result; } SEXP R_igraph_read_graph_lgl(SEXP pvfile, SEXP pnames, SEXP pweights, SEXP pdirected) { igraph_t g; igraph_bool_t names=LOGICAL(pnames)[0]; igraph_add_weights_t weights=REAL(pweights)[0]; igraph_bool_t directed=LOGICAL(pdirected)[0]; FILE *file; SEXP result; #if HAVE_FMEMOPEN == 1 file=fmemopen(RAW(pvfile), GET_LENGTH(pvfile), "r"); #else file=fopen(CHAR(STRING_ELT(pvfile, 0)), "r"); #endif if (file==0) { igraph_error("Cannot read edgelist", __FILE__, __LINE__, IGRAPH_EFILE); } igraph_read_graph_lgl(&g, file, names, weights, directed); fclose(file); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_write_graph_lgl(SEXP graph, SEXP file, SEXP pnames, SEXP pweights, SEXP pisolates) { igraph_t g; FILE *stream; #if HAVE_OPEN_MEMSTREAM == 1 char *bp; size_t size; #endif const char *names, *weights; igraph_bool_t isolates=LOGICAL(pisolates)[0]; SEXP result; if (isNull(pnames)) { names=0; } else { names=CHAR(STRING_ELT(pnames, 0)); } if (isNull(pweights)) { weights=0; } else { weights=CHAR(STRING_ELT(pweights, 0)); } R_SEXP_to_igraph(graph, &g); #if HAVE_OPEN_MEMSTREAM == 1 stream=open_memstream(&bp, &size); #else stream=fopen(CHAR(STRING_ELT(file, 0)), "w"); #endif igraph_write_graph_lgl(&g, stream, names, weights, isolates); fclose(stream); #if HAVE_OPEN_MEMSTREAM == 1 PROTECT(result=allocVector(RAWSXP, size)); memcpy(RAW(result), bp, sizeof(char)*size); free(bp); #else PROTECT(result=NEW_NUMERIC(0)); #endif UNPROTECT(1); return result; } SEXP R_igraph_read_graph_pajek(SEXP pvfile) { igraph_t g; FILE *file; SEXP result; #if HAVE_FMEMOPEN == 1 file=fmemopen(RAW(pvfile), GET_LENGTH(pvfile), "r"); #else file=fopen(CHAR(STRING_ELT(pvfile, 0)), "r"); #endif if (file==0) { igraph_error("Cannot read Pajek file", __FILE__, __LINE__, IGRAPH_EFILE); } igraph_read_graph_pajek(&g, file); fclose(file); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_decompose(SEXP graph, SEXP pmode, SEXP pmaxcompno, SEXP pminelements) { igraph_t g; igraph_integer_t mode=(igraph_integer_t) REAL(pmode)[0]; igraph_integer_t maxcompno=(igraph_integer_t) REAL(pmaxcompno)[0]; igraph_integer_t minelements=(igraph_integer_t) REAL(pminelements)[0]; igraph_vector_ptr_t comps; SEXP result; long int i; PROTECT(R_igraph_attribute_protected=NEW_LIST(100)); R_igraph_attribute_protected_size=0; IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); R_SEXP_to_igraph(graph, &g); igraph_vector_ptr_init(&comps, 0); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &comps); igraph_decompose(&g, &comps, (igraph_connectedness_t) mode, maxcompno, minelements); PROTECT(result=NEW_LIST(igraph_vector_ptr_size(&comps))); for (i=0; ifun, s_from, data->extra)); PROTECT(s_to = eval(R_fcall, data->rho)); memcpy(to, REAL(s_to), sizeof(igraph_real_t) * (size_t) n); UNPROTECT(3); return 0; } SEXP R_igraph_arpack(SEXP function, SEXP extra, SEXP options, SEXP rho, SEXP sym) { igraph_vector_t values; igraph_matrix_t vectors, values2; R_igraph_i_arpack_data_t data; igraph_arpack_options_t c_options; SEXP result, names; if (0 != igraph_matrix_init(&vectors, 0, 0)) { igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &vectors); if (LOGICAL(sym)[0]) { if (0 != igraph_vector_init(&values, 0)) { igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &values); } else { if (0 != igraph_matrix_init(&values2, 0, 0)) { igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &values2); } data.fun=function; data.extra=extra; data.rho=rho; R_SEXP_to_igraph_arpack_options(options, &c_options); if (LOGICAL(sym)[0]) { if (0 != igraph_arpack_rssolve(R_igraph_i_arpack_callback, &data, &c_options, 0, &values, &vectors)) { igraph_error("ARPACK failed", __FILE__, __LINE__, IGRAPH_FAILURE); } } else { if (0 != igraph_arpack_rnsolve(R_igraph_i_arpack_callback, &data, &c_options, 0, &values2, &vectors)) { igraph_error("ARPACK failed", __FILE__, __LINE__, IGRAPH_FAILURE); } } PROTECT(result=NEW_LIST(3)); if (LOGICAL(sym)[0]) { SET_VECTOR_ELT(result, 0, R_igraph_vector_to_SEXP(&values)); igraph_vector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); } else { SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&values2)); igraph_matrix_destroy(&values2); IGRAPH_FINALLY_CLEAN(1); } SET_VECTOR_ELT(result, 1, R_igraph_matrix_to_SEXP(&vectors)); igraph_matrix_destroy(&vectors); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 2, R_igraph_arpack_options_to_SEXP(&c_options)); PROTECT(names=NEW_CHARACTER(3)); SET_STRING_ELT(names, 0, mkChar("values")); SET_STRING_ELT(names, 1, mkChar("vectors")); SET_STRING_ELT(names, 2, mkChar("options")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_is_chordal(SEXP graph, SEXP alpha, SEXP alpham1, SEXP pfillin, SEXP pnewgraph) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_alpha; igraph_vector_t c_alpham1; igraph_bool_t c_chordal; igraph_vector_t c_fillin; igraph_t c_newgraph; SEXP chordal; SEXP fillin; SEXP newgraph; int c_result; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(alpha)) { R_SEXP_to_vector(alpha, &c_alpha); } if (!isNull(alpham1)) { R_SEXP_to_vector(alpham1, &c_alpham1); } if (LOGICAL(pfillin)[0]) { if (0 != igraph_vector_init(&c_fillin, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_fillin); } c_result=igraph_is_chordal(&c_graph, (isNull(alpha) ? 0 : &c_alpha), (isNull(alpham1) ? 0 : &c_alpham1), &c_chordal, (LOGICAL(pfillin)[0] ? &c_fillin : 0), (LOGICAL(pnewgraph)[0] ? &c_newgraph : 0)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(chordal=NEW_LOGICAL(1)); LOGICAL(chordal)[0]=c_chordal; if (LOGICAL(pfillin)[0]) { PROTECT(fillin=R_igraph_vector_to_SEXP(&c_fillin)); igraph_vector_destroy(&c_fillin); IGRAPH_FINALLY_CLEAN(1); } else { PROTECT(fillin=R_NilValue); } if (LOGICAL(pnewgraph)[0]) { IGRAPH_FINALLY(igraph_destroy, &c_newgraph); PROTECT(newgraph=R_igraph_to_SEXP(&c_newgraph)); igraph_destroy(&c_newgraph); IGRAPH_FINALLY_CLEAN(1); } else { PROTECT(newgraph=R_NilValue); } SET_VECTOR_ELT(result, 0, chordal); SET_VECTOR_ELT(result, 1, fillin); SET_VECTOR_ELT(result, 2, newgraph); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("chordal")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("fillin")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("newgraph")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } typedef struct { SEXP graph, fun, extra, rho; } R_igraph_i_bfs_data_t; igraph_bool_t R_igraph_bfshandler(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t pred, igraph_integer_t succ, igraph_integer_t rank, igraph_integer_t dist, void *extra) { R_igraph_i_bfs_data_t *data=extra; SEXP args, R_fcall, result, names; igraph_bool_t cres; PROTECT(args=NEW_NUMERIC(5)); PROTECT(names=NEW_CHARACTER(5)); SET_STRING_ELT(names, 0, mkChar("vid")); SET_STRING_ELT(names, 1, mkChar("pred")); SET_STRING_ELT(names, 2, mkChar("succ")); SET_STRING_ELT(names, 3, mkChar("rank")); SET_STRING_ELT(names, 4, mkChar("dist")); REAL(args)[0]=vid; REAL(args)[1]=pred; REAL(args)[2]=succ; REAL(args)[3]=rank; REAL(args)[4]=dist; SET_NAMES(args, names); PROTECT(R_fcall = lang4(data->fun, data->graph, args, data->extra)); PROTECT(result = eval(R_fcall, data->rho)); cres=LOGICAL(result)[0]; UNPROTECT(4); return cres; } SEXP R_igraph_bfs(SEXP graph, SEXP proot, SEXP proots, SEXP pneimode, SEXP punreachable, SEXP prestricted, SEXP porder, SEXP prank, SEXP pfather, SEXP ppred, SEXP psucc, SEXP pdist, SEXP pcallback, SEXP pextra, SEXP prho) { igraph_t g; SEXP result, names; igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; igraph_vector_t roots; igraph_bool_t unreachable=LOGICAL(punreachable)[0]; igraph_vector_t restricted; igraph_integer_t neimode=(igraph_integer_t) REAL(pneimode)[0]; igraph_vector_t order, rank, father, pred, succ, dist; igraph_vector_t *p_order=0, *p_rank=0, *p_father=0, *p_pred=0, *p_succ=0, *p_dist=0; igraph_bfshandler_t *callback=0; R_igraph_i_bfs_data_t cb_data, *p_cb_data=0; R_SEXP_to_igraph(graph, &g); if (!isNull(proots)) { R_SEXP_to_vector(proots, &roots); } if (!isNull(prestricted)) { R_SEXP_to_vector(prestricted, &restricted); } if (LOGICAL(porder)[0]) { igraph_vector_init(&order, 0); p_order=ℴ } if (LOGICAL(prank)[0]) { igraph_vector_init(&rank, 0); p_rank=&rank; } if (LOGICAL(pfather)[0]) { igraph_vector_init(&father, 0); p_father=&father; } if (LOGICAL(ppred)[0]) { igraph_vector_init(&pred, 0); p_pred=&pred; } if (LOGICAL(psucc)[0]) { igraph_vector_init(&succ, 0); p_succ=≻ } if (LOGICAL(pdist)[0]) { igraph_vector_init(&dist, 0); p_dist=&dist; } if (!isNull(pcallback)) { cb_data.graph=graph; cb_data.fun=pcallback; cb_data.extra=pextra; cb_data.rho=prho; callback=R_igraph_bfshandler; p_cb_data = &cb_data; } igraph_bfs(&g, root, isNull(proots) ? 0 : &roots, (igraph_neimode_t) neimode, unreachable, isNull(prestricted) ? 0 : &restricted, p_order, p_rank, p_father, p_pred, p_succ, p_dist, (igraph_bfshandler_t*) callback, p_cb_data); PROTECT(result=NEW_LIST(8)); PROTECT(names=NEW_CHARACTER(8)); SET_STRING_ELT(names, 0, mkChar("root")); SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 0))[0] = root+1; SET_STRING_ELT(names, 1, mkChar("neimode")); SET_VECTOR_ELT(result, 1, NEW_CHARACTER(1)); if (neimode==1) { SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("out")); } else if (neimode==2) { SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("in")); } else { SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("all")); } SET_STRING_ELT(names, 2, mkChar("order")); SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP_d(p_order)); SET_STRING_ELT(names, 3, mkChar("rank")); SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP_d(p_rank)); SET_STRING_ELT(names, 4, mkChar("father")); SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP_d(p_father)); SET_STRING_ELT(names, 5, mkChar("pred")); SET_VECTOR_ELT(result, 5, R_igraph_0orvector_to_SEXP_d(p_pred)); SET_STRING_ELT(names, 6, mkChar("succ")); SET_VECTOR_ELT(result, 6, R_igraph_0orvector_to_SEXP_d(p_succ)); SET_STRING_ELT(names, 7, mkChar("dist")); SET_VECTOR_ELT(result, 7, R_igraph_0orvector_to_SEXP_d(p_dist)); SET_NAMES(result, names); UNPROTECT(2); return result; } typedef struct { SEXP graph, fun_in, fun_out, extra, rho; } R_igraph_i_dfs_data_t; igraph_bool_t R_igraph_dfshandler(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra, int which) { R_igraph_i_dfs_data_t *data=extra; SEXP args, R_fcall, result, names; igraph_bool_t cres; PROTECT(args=NEW_NUMERIC(2)); PROTECT(names=NEW_CHARACTER(2)); SET_STRING_ELT(names, 0, mkChar("vid")); SET_STRING_ELT(names, 1, mkChar("dist")); REAL(args)[0]=vid; REAL(args)[1]=dist; SET_NAMES(args, names); PROTECT(R_fcall = lang4(which==0 ? data->fun_in : data->fun_out, data->graph, args, data->extra)); PROTECT(result = eval(R_fcall, data->rho)); cres=LOGICAL(result)[0]; UNPROTECT(4); return cres; } igraph_bool_t R_igraph_dfshandler_in(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra) { return R_igraph_dfshandler(graph, vid, dist, extra, 0); } igraph_bool_t R_igraph_dfshandler_out(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra) { return R_igraph_dfshandler(graph, vid, dist, extra, 1); } SEXP R_igraph_dfs(SEXP graph, SEXP proot, SEXP pneimode, SEXP punreachable, SEXP porder, SEXP porder_out, SEXP pfather, SEXP pdist, SEXP pin_callback, SEXP pout_callback, SEXP pextra, SEXP prho) { igraph_t g; SEXP result, names; igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; igraph_integer_t neimode=(igraph_integer_t) REAL(pneimode)[0]; igraph_bool_t unreachable=LOGICAL(punreachable)[0]; igraph_vector_t order, order_out, father, dist; igraph_vector_t *p_order=0, *p_order_out=0, *p_father=0, *p_dist=0; igraph_dfshandler_t *in_callback=0, *out_callback=0; R_igraph_i_dfs_data_t cb_data, *p_cb_data=0; R_SEXP_to_igraph(graph, &g); if (LOGICAL(porder)[0]) { igraph_vector_init(&order, 0); p_order=ℴ } if (LOGICAL(porder_out)[0]) { igraph_vector_init(&order_out, 0); p_order_out=&order_out; } if (LOGICAL(pfather)[0]) { igraph_vector_init(&father, 0); p_father=&father; } if (LOGICAL(pdist)[0]) { igraph_vector_init(&dist, 0); p_dist=&dist; } if (!isNull(pin_callback) || !isNull(pout_callback)) { cb_data.graph=graph; cb_data.fun_in=pin_callback; cb_data.fun_out=pout_callback; cb_data.extra=pextra; cb_data.rho=prho; p_cb_data = &cb_data; } if (!isNull(pin_callback)) { in_callback=R_igraph_dfshandler_in; } if (!isNull(pout_callback)) { out_callback=R_igraph_dfshandler_out; } igraph_dfs(&g, root, (igraph_neimode_t) neimode, unreachable, p_order, p_order_out, p_father, p_dist, (igraph_dfshandler_t*) in_callback, (igraph_dfshandler_t*) out_callback, p_cb_data); PROTECT(result=NEW_LIST(6)); PROTECT(names=NEW_CHARACTER(6)); SET_STRING_ELT(names, 0, mkChar("root")); SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 0))[0] = root; SET_STRING_ELT(names, 1, mkChar("neimode")); SET_VECTOR_ELT(result, 1, NEW_CHARACTER(1)); if (neimode==1) { SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("out")); } else if (neimode==2) { SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("in")); } else { SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("all")); } SET_STRING_ELT(names, 2, mkChar("order")); SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP_d(p_order)); SET_STRING_ELT(names, 3, mkChar("order.out")); SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP_d(p_order_out)); SET_STRING_ELT(names, 4, mkChar("father")); SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP_d(p_father)); SET_STRING_ELT(names, 5, mkChar("dist")); SET_VECTOR_ELT(result, 5, R_igraph_0orvector_to_SEXP_d(p_dist)); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_cohesive_blocks(SEXP graph) { igraph_vector_ptr_t c_blocks; igraph_vector_t c_cohesion; igraph_vector_t c_parent; igraph_t c_blockTree; int c_result; igraph_t c_graph; SEXP blocks; SEXP cohesion; SEXP parent; SEXP blockTree; SEXP result; SEXP names; R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_blocks, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_blocks); if (0 != igraph_vector_init(&c_cohesion, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_cohesion); if (0 != igraph_vector_init(&c_parent, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_parent); c_result=igraph_cohesive_blocks(&c_graph, &c_blocks, &c_cohesion, &c_parent, &c_blockTree); PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(blocks=R_igraph_vectorlist_to_SEXP_p1(&c_blocks)); R_igraph_vectorlist_destroy(&c_blocks); IGRAPH_FINALLY_CLEAN(1); PROTECT(cohesion=R_igraph_vector_to_SEXP(&c_cohesion)); igraph_vector_destroy(&c_cohesion); IGRAPH_FINALLY_CLEAN(1); PROTECT(parent=R_igraph_vector_to_SEXPp1(&c_parent)); igraph_vector_destroy(&c_parent); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &c_blockTree); PROTECT(blockTree=R_igraph_to_SEXP(&c_blockTree)); igraph_destroy(&c_blockTree); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, blocks); SET_VECTOR_ELT(result, 1, cohesion); SET_VECTOR_ELT(result, 2, parent); SET_VECTOR_ELT(result, 3, blockTree); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("blocks")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cohesion")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("parent")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("blockTree")); SET_NAMES(result, names); UNPROTECT(6); return result; } SEXP R_igraph_i_levc_arp(SEXP extP, SEXP extE, SEXP pv) { igraph_arpack_function_t *fun= (igraph_arpack_function_t *) R_ExternalPtrAddr(extP); void *extra=R_ExternalPtrAddr(extE); SEXP res; PROTECT(res=NEW_NUMERIC(GET_LENGTH(pv))); fun(REAL(res), REAL(pv), GET_LENGTH(pv), extra); UNPROTECT(1); return res; } typedef struct R_igraph_i_levc_data_t { SEXP fun; SEXP extra; SEXP rho; SEXP rho2; } R_igraph_i_levc_data_t; int R_igraph_i_levc_callback(const igraph_vector_t *membership, long int comm, igraph_real_t eigenvalue, const igraph_vector_t *eigenvector, igraph_arpack_function_t *arpack_multiplier, void *arpack_extra, void *extra) { SEXP s_memb, s_comm, s_evalue, s_evector, s_multip; SEXP R_fcall, R_multip_call; SEXP res; int result; R_igraph_i_levc_data_t *data=extra; PROTECT(s_memb=R_igraph_vector_to_SEXP(membership)); PROTECT(s_comm=NEW_NUMERIC(1)); REAL(s_comm)[0]=comm; PROTECT(s_evalue=NEW_NUMERIC(1)); REAL(s_evalue)[0]=eigenvalue; PROTECT(s_evector=R_igraph_vector_to_SEXP(eigenvector)); PROTECT(R_multip_call = lang3(install("igraph.i.levc.arp"), R_MakeExternalPtr((void*) arpack_multiplier, R_NilValue, R_NilValue), R_MakeExternalPtr(arpack_extra, R_NilValue, R_NilValue))); PROTECT(s_multip = eval(R_multip_call, data->rho2)); PROTECT(R_fcall = R_igraph_i_lang7(data->fun, s_memb, s_comm, s_evalue, s_evector, s_multip, data->extra)); PROTECT(res = eval(R_fcall, data->rho)); result=(int) REAL(AS_NUMERIC(res))[0]; UNPROTECT(8); return result; } SEXP R_igraph_community_leading_eigenvector(SEXP graph, SEXP steps, SEXP weights, SEXP options, SEXP pstart, SEXP callback, SEXP callback_extra, SEXP callback_env, SEXP callback_env2) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_merges; igraph_vector_t c_membership; igraph_integer_t c_steps; igraph_vector_t v_weights, *pweights=0; igraph_bool_t c_start=!isNull(pstart); igraph_arpack_options_t c_options; igraph_real_t c_modularity; igraph_vector_t c_eigenvalues; igraph_vector_ptr_t c_eigenvectors; igraph_vector_t c_history; SEXP merges; SEXP membership; SEXP modularity; SEXP eigenvalues; SEXP eigenvectors; SEXP history; int c_result; SEXP result, names; R_igraph_i_levc_data_t callback_data = { callback, callback_extra, callback_env, callback_env2 }; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(weights)) { pweights=&v_weights; R_SEXP_to_vector(weights, &v_weights); } if (0 != igraph_matrix_init(&c_merges, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_merges); if (c_start) { R_SEXP_to_vector_copy(pstart, &c_membership); } else { if (0 != igraph_vector_init(&c_membership, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); c_steps=INTEGER(steps)[0]; R_SEXP_to_igraph_arpack_options(options, &c_options); if (0 != igraph_vector_init(&c_eigenvalues, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } if (0 != igraph_vector_ptr_init(&c_eigenvectors, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } if (0 != igraph_vector_init(&c_history, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } /* Call igraph */ c_result=igraph_community_leading_eigenvector(&c_graph, pweights, &c_merges, &c_membership, c_steps, &c_options, &c_modularity, c_start, &c_eigenvalues, &c_eigenvectors, &c_history, isNull(callback) ? 0 : R_igraph_i_levc_callback, &callback_data); /* Convert output */ PROTECT(result=NEW_LIST(7)); PROTECT(names=NEW_CHARACTER(7)); PROTECT(merges=R_igraph_matrix_to_SEXP(&c_merges)); igraph_matrix_destroy(&c_merges); IGRAPH_FINALLY_CLEAN(1); PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); PROTECT(modularity=NEW_NUMERIC(1)); REAL(modularity)[0]=c_modularity; PROTECT(eigenvalues=R_igraph_vector_to_SEXP(&c_eigenvalues)); igraph_vector_destroy(&c_eigenvalues); PROTECT(eigenvectors=R_igraph_vectorlist_to_SEXP(&c_eigenvectors)); R_igraph_vectorlist_destroy(&c_eigenvectors); PROTECT(history=R_igraph_vector_to_SEXP(&c_history)); igraph_vector_destroy(&c_history); SET_VECTOR_ELT(result, 0, merges); SET_VECTOR_ELT(result, 1, membership); SET_VECTOR_ELT(result, 2, options); SET_VECTOR_ELT(result, 3, modularity); SET_VECTOR_ELT(result, 4, eigenvalues); SET_VECTOR_ELT(result, 5, eigenvectors); SET_VECTOR_ELT(result, 6, history); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("merges")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("membership")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("modularity")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("eigenvalues")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("eigenvectors")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("history")); SET_NAMES(result, names); UNPROTECT(8); UNPROTECT(1); return(result); } SEXP R_igraph_get_eids(SEXP graph, SEXP pvp, SEXP pdirected, SEXP perror, SEXP pmulti) { igraph_t g; igraph_vector_t vp; igraph_vector_t res; igraph_bool_t directed=LOGICAL(pdirected)[0]; igraph_bool_t error=LOGICAL(perror)[0]; igraph_bool_t multi=LOGICAL(pmulti)[0]; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pvp, &vp); igraph_vector_init(&res, 0); if (multi) { igraph_get_eids_multi(&g, &res, /*pairs=*/ &vp, /*path=*/ 0, directed, error); } else { igraph_get_eids(&g, &res, /*pairs=*/ &vp, /*path=*/ 0, directed, error); } PROTECT(result=R_igraph_vector_to_SEXP(&res)); igraph_vector_destroy(&res); UNPROTECT(1); return result; } SEXP R_igraph_scg_semiprojectors(SEXP groups, SEXP matrix_type, SEXP p, SEXP norm, SEXP psparse) { /* Declarations */ igraph_vector_t c_groups; igraph_integer_t c_matrix_type; igraph_matrix_t c_L; igraph_matrix_t c_R; igraph_sparsemat_t c_Lsparse; igraph_sparsemat_t c_Rsparse; igraph_vector_t c_p; igraph_integer_t c_norm; SEXP L; SEXP R; SEXP Lsparse; SEXP Rsparse; igraph_bool_t sparse=LOGICAL(psparse)[0]; int c_result; SEXP result, names; /* Convert input */ R_SEXP_to_vector(groups, &c_groups); c_matrix_type=(igraph_integer_t) REAL(matrix_type)[0]; if (!sparse) { if (0 != igraph_matrix_init(&c_L, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_L); if (0 != igraph_matrix_init(&c_R, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_R); } else { /* Nothing to do, because igraph_scg_semiprojectors expect uninitialized sparse matrices */ } if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } c_norm=(igraph_integer_t) REAL(norm)[0]; /* Call igraph */ c_result=igraph_scg_semiprojectors(&c_groups, (igraph_scg_matrix_t) c_matrix_type, (sparse ? 0 : &c_L), (sparse ? 0 : &c_R), (sparse ? &c_Lsparse : 0), (sparse ? &c_Rsparse : 0), (isNull(p) ? 0 : &c_p), (igraph_scg_norm_t) c_norm); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); if (!sparse) { PROTECT(L=R_igraph_0ormatrix_to_SEXP(&c_L)); igraph_matrix_destroy(&c_L); IGRAPH_FINALLY_CLEAN(1); PROTECT(R=R_igraph_0ormatrix_to_SEXP(&c_R)); igraph_matrix_destroy(&c_R); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, L); SET_VECTOR_ELT(result, 1, R); } else { PROTECT(Lsparse=R_igraph_0orsparsemat_to_SEXP(&c_Lsparse)); igraph_sparsemat_destroy(&c_Lsparse); IGRAPH_FINALLY_CLEAN(1); PROTECT(Rsparse=R_igraph_0orsparsemat_to_SEXP(&c_Rsparse)); igraph_sparsemat_destroy(&c_Rsparse); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, Lsparse); SET_VECTOR_ELT(result, 1, Rsparse); } SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("L")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("R")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } SEXP R_igraph_laplacian(SEXP graph, SEXP normalized, SEXP weights, SEXP psparse) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_sparsemat_t c_sparseres; igraph_bool_t c_normalized; igraph_vector_t c_weights; igraph_bool_t c_sparse=LOGICAL(psparse)[0]; SEXP result; int c_result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!c_sparse) { if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); } if (c_sparse) { if (0 != igraph_sparsemat_init(&c_sparseres, 0, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_sparsemat_destroy, &c_sparseres); } c_normalized=LOGICAL(normalized)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ c_result=igraph_laplacian(&c_graph, (c_sparse ? 0 : &c_res), (c_sparse ? &c_sparseres : 0), c_normalized, (isNull(weights) ? 0 : &c_weights)); /* Convert output */ if (!c_sparse) { PROTECT(result=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); } else { PROTECT(result=R_igraph_sparsemat_to_SEXP(&c_sparseres)); igraph_sparsemat_destroy(&c_sparseres); IGRAPH_FINALLY_CLEAN(1); } UNPROTECT(1); return(result); } SEXP R_igraph_scg_adjacency(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, SEXP intervals_vector, SEXP algorithm, SEXP evec, SEXP groups, SEXP use_arpack, SEXP maxiter, SEXP sparse, SEXP output, SEXP semproj, SEXP epairs) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_matrix; igraph_sparsemat_t c_sparsmat; igraph_vector_t c_ev; igraph_vector_t c_intervals_vector; igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; igraph_vector_t c_eval; igraph_matrix_t c_evec; igraph_vector_t c_groups; igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; igraph_bool_t c_sparse=LOGICAL(sparse)[0]; igraph_real_t c_output=REAL(output)[0]; igraph_bool_t c_semproj=LOGICAL(semproj)[0]; igraph_bool_t c_epairs=LOGICAL(epairs)[0]; igraph_t c_scg_graph; igraph_matrix_t c_scg_matrix; igraph_sparsemat_t c_scg_sparsemat; igraph_matrix_t c_L; igraph_matrix_t c_R; igraph_sparsemat_t c_Lsparse; igraph_sparsemat_t c_Rsparse; SEXP scg_graph; SEXP scg_matrix; SEXP scg_sparsemat; SEXP L; SEXP R; SEXP Lsparse; SEXP Rsparse; int c_result; SEXP result, names; SEXP eval; /* What to return */ igraph_bool_t do_scg_graph= (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; igraph_bool_t do_scg_matrix=!c_sparse && ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); igraph_bool_t do_scg_sparsemat=c_sparse && ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); igraph_bool_t do_L=c_semproj && !c_sparse; igraph_bool_t do_R=c_semproj && !c_sparse; igraph_bool_t do_Lsparse=c_semproj && c_sparse; igraph_bool_t do_Rsparse=c_semproj && c_sparse; igraph_bool_t do_eval=c_epairs; igraph_bool_t do_evec=c_epairs; /* Convert input */ if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } R_SEXP_to_vector(ev, &c_ev); R_SEXP_to_vector(intervals_vector, &c_intervals_vector); if (do_eval) { igraph_vector_init(&c_eval, 0); } if (!isNull(evec)) { R_SEXP_to_igraph_matrix_copy(evec, &c_evec); } else if (do_evec) { igraph_matrix_init(&c_evec, 0, 0); } if (!isNull(groups)) { R_SEXP_to_vector_copy(groups, &c_groups); } else { igraph_vector_init(&c_groups, 0); } if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } if (do_L) { igraph_matrix_init(&c_L, 0, 0); } if (do_R) { igraph_matrix_init(&c_R, 0, 0); } if (do_scg_sparsemat) { igraph_sparsemat_init(&c_scg_sparsemat, 0, 0, 0); } /* Call igraph */ c_result=igraph_scg_adjacency((isNull(graph) ? 0 : &c_graph), (isNull(matrix) ? 0 : &c_matrix), (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, /*intervals=*/ 0, &c_intervals_vector, (igraph_scg_algorithm_t) c_algorithm, (do_eval ? &c_eval : 0), (!isNull(evec) || do_evec ? &c_evec : 0), &c_groups, c_use_arpack, c_maxiter, (do_scg_graph ? &c_scg_graph : 0), (do_scg_matrix ? &c_scg_matrix : 0), (do_scg_sparsemat ? &c_scg_sparsemat : 0), (do_L ? &c_L : 0), (do_R ? &c_R : 0), (do_Lsparse ? &c_Lsparse : 0), (do_Rsparse ? &c_Rsparse : 0)); if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } /* Convert output */ PROTECT(result=NEW_LIST(6)); PROTECT(names=NEW_CHARACTER(6)); if (do_eval) { PROTECT(eval=R_igraph_vector_to_SEXP(&c_eval)); igraph_vector_destroy(&c_eval); } else { PROTECT(eval=R_NilValue); } if (do_evec) { PROTECT(evec=R_igraph_matrix_to_SEXP(&c_evec)); igraph_matrix_destroy(&c_evec); } else { PROTECT(evec=R_NilValue); } PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); igraph_vector_destroy(&c_groups); if (do_scg_graph) { PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); igraph_destroy(&c_scg_graph); } else { PROTECT(scg_graph=R_NilValue); } if (do_scg_matrix) { PROTECT(scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix)); igraph_matrix_destroy(&c_scg_matrix); } else { PROTECT(scg_matrix=R_NilValue); } if (do_scg_sparsemat) { PROTECT(scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat)); igraph_sparsemat_destroy(&c_scg_sparsemat); } else { PROTECT(scg_sparsemat=R_NilValue); } if (do_L) { PROTECT(L=R_igraph_matrix_to_SEXP(&c_L)); igraph_matrix_destroy(&c_L); } else { PROTECT(L=R_NilValue); } if (do_R) { PROTECT(R=R_igraph_matrix_to_SEXP(&c_R)); igraph_matrix_destroy(&c_R); } else { PROTECT(R=R_NilValue); } if (do_Lsparse) { PROTECT(Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse)); igraph_sparsemat_destroy(&c_Lsparse); } else { PROTECT(Lsparse=R_NilValue); } if (do_Rsparse) { PROTECT(Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse)); igraph_sparsemat_destroy(&c_Rsparse); } else { PROTECT(Rsparse=R_NilValue); } if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } SET_VECTOR_ELT(result, 1, groups); if (do_L) { SET_VECTOR_ELT(result, 2, L); } if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } if (do_R) { SET_VECTOR_ELT(result, 3, R); } if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } SET_VECTOR_ELT(result, 4, eval); SET_VECTOR_ELT(result, 5, evec); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); SET_NAMES(result, names); UNPROTECT(11); UNPROTECT(1); return(result); } SEXP R_igraph_scg_stochastic(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, SEXP intervals_vector, SEXP algorithm, SEXP norm, SEXP evec, SEXP groups, SEXP p, SEXP use_arpack, SEXP maxiter, SEXP sparse, SEXP output, SEXP semproj, SEXP epairs, SEXP stat_prob) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_matrix; igraph_sparsemat_t c_sparsmat; igraph_vector_t c_ev; igraph_vector_t c_intervals_vector; igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; igraph_integer_t c_norm=(igraph_integer_t) REAL(norm)[0]; igraph_vector_complex_t c_eval; igraph_matrix_complex_t c_evec; igraph_vector_t c_groups; igraph_vector_t c_p; igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; igraph_bool_t c_sparse=LOGICAL(sparse)[0]; igraph_real_t c_output=REAL(output)[0]; igraph_bool_t c_semproj=LOGICAL(semproj)[0]; igraph_bool_t c_epairs=LOGICAL(epairs)[0]; igraph_bool_t c_stat_prob=LOGICAL(stat_prob)[0]; igraph_t c_scg_graph; igraph_matrix_t c_scg_matrix; igraph_sparsemat_t c_scg_sparsemat; igraph_matrix_t c_L; igraph_matrix_t c_R; igraph_sparsemat_t c_Lsparse; igraph_sparsemat_t c_Rsparse; SEXP scg_graph; SEXP scg_matrix; SEXP scg_sparsemat; SEXP L; SEXP R; SEXP Lsparse; SEXP Rsparse; int c_result; SEXP result, names; SEXP eval; /* What to return */ igraph_bool_t do_scg_graph= (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; igraph_bool_t do_scg_matrix=!c_sparse && ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); igraph_bool_t do_scg_sparsemat=c_sparse && ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); igraph_bool_t do_L=c_semproj && !c_sparse; igraph_bool_t do_R=c_semproj && !c_sparse; igraph_bool_t do_Lsparse=c_semproj && c_sparse; igraph_bool_t do_Rsparse=c_semproj && c_sparse; igraph_bool_t do_eval=c_epairs; igraph_bool_t do_evec=c_epairs; igraph_bool_t do_p=c_stat_prob; /* Convert input */ if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } R_SEXP_to_vector(ev, &c_ev); R_SEXP_to_vector(intervals_vector, &c_intervals_vector); if (do_eval) { igraph_vector_complex_init(&c_eval, 0); } if (!isNull(evec)) { R_SEXP_to_matrix_complex_copy(evec, &c_evec); } else if (do_evec) { igraph_matrix_complex_init(&c_evec, 0, 0); } if (!isNull(groups)) { R_SEXP_to_vector_copy(groups, &c_groups); } else { igraph_vector_init(&c_groups, 0); } if (!isNull(p)) { R_SEXP_to_vector_copy(p, &c_p); } else if (do_p) { igraph_vector_init(&c_p, 0); } if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } if (do_L) { igraph_matrix_init(&c_L, 0, 0); } if (do_R) { igraph_matrix_init(&c_R, 0, 0); } /* Call igraph */ c_result=igraph_scg_stochastic((isNull(graph) ? 0 : &c_graph), (isNull(matrix) ? 0 : &c_matrix), (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, /*intervals=*/ 0, &c_intervals_vector, (igraph_scg_algorithm_t) c_algorithm, (igraph_scg_norm_t) c_norm, (do_eval ? &c_eval : 0), (!isNull(evec) || do_evec ? &c_evec : 0), &c_groups, (!isNull(p) || do_p ? &c_p : 0), c_use_arpack, c_maxiter, (do_scg_graph ? &c_scg_graph : 0), (do_scg_matrix ? &c_scg_matrix : 0), (do_scg_sparsemat ? &c_scg_sparsemat : 0), (do_L ? &c_L : 0), (do_R ? &c_R : 0), (do_Lsparse ? &c_Lsparse : 0), (do_Rsparse ? &c_Rsparse : 0)); if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } /* Convert output */ PROTECT(result=NEW_LIST(7)); PROTECT(names=NEW_CHARACTER(7)); if (do_eval) { PROTECT(eval=R_igraph_vector_complex_to_SEXP(&c_eval)); igraph_vector_complex_destroy(&c_eval); } else { PROTECT(eval=R_NilValue); } if (do_evec) { PROTECT(evec=R_igraph_matrix_complex_to_SEXP(&c_evec)); igraph_matrix_complex_destroy(&c_evec); } else { PROTECT(evec=R_NilValue); } if (do_p) { PROTECT(p=R_igraph_vector_to_SEXP(&c_p)); igraph_vector_destroy(&c_p); } else { PROTECT(p=R_NilValue); } PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); igraph_vector_destroy(&c_groups); if (do_scg_graph) { PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); igraph_destroy(&c_scg_graph); } else { PROTECT(scg_graph=R_NilValue); } if (do_scg_matrix) { PROTECT(scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix)); igraph_matrix_destroy(&c_scg_matrix); } else { PROTECT(scg_matrix=R_NilValue); } if (do_scg_sparsemat) { PROTECT(scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat)); igraph_sparsemat_destroy(&c_scg_sparsemat); } else { PROTECT(scg_sparsemat=R_NilValue); } if (do_L) { PROTECT(L=R_igraph_matrix_to_SEXP(&c_L)); igraph_matrix_destroy(&c_L); } else { PROTECT(L=R_NilValue); } if (do_R) { PROTECT(R=R_igraph_matrix_to_SEXP(&c_R)); igraph_matrix_destroy(&c_R); } else { PROTECT(R=R_NilValue); } if (do_Lsparse) { PROTECT(Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse)); igraph_sparsemat_destroy(&c_Lsparse); } else { PROTECT(Lsparse=R_NilValue); } if (do_Rsparse) { PROTECT(Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse)); igraph_sparsemat_destroy(&c_Rsparse); } else { PROTECT(Rsparse=R_NilValue); } if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } SET_VECTOR_ELT(result, 1, groups); if (do_L) { SET_VECTOR_ELT(result, 2, L); } if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } if (do_R) { SET_VECTOR_ELT(result, 3, R); } if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } SET_VECTOR_ELT(result, 4, eval); SET_VECTOR_ELT(result, 5, evec); if (do_p) { SET_VECTOR_ELT(result, 6, p); } SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("p")); SET_NAMES(result, names); UNPROTECT(12); UNPROTECT(1); return(result); } SEXP R_igraph_scg_laplacian(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, SEXP intervals_vector, SEXP algorithm, SEXP norm, SEXP direction, SEXP evec, SEXP groups, SEXP use_arpack, SEXP maxiter, SEXP sparse, SEXP output, SEXP semproj, SEXP epairs) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_matrix; igraph_sparsemat_t c_sparsmat; igraph_vector_t c_ev; igraph_vector_t c_intervals_vector; igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; igraph_integer_t c_norm=(igraph_integer_t) REAL(norm)[0]; igraph_integer_t c_direction=(igraph_integer_t) REAL(direction)[0]; igraph_vector_complex_t c_eval; igraph_matrix_complex_t c_evec; igraph_vector_t c_groups; igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; igraph_bool_t c_sparse=LOGICAL(sparse)[0]; igraph_real_t c_output=REAL(output)[0]; igraph_bool_t c_semproj=LOGICAL(semproj)[0]; igraph_bool_t c_epairs=LOGICAL(epairs)[0]; igraph_t c_scg_graph; igraph_matrix_t c_scg_matrix; igraph_sparsemat_t c_scg_sparsemat; igraph_matrix_t c_L; igraph_matrix_t c_R; igraph_sparsemat_t c_Lsparse; igraph_sparsemat_t c_Rsparse; SEXP eval; SEXP scg_graph; SEXP scg_matrix; SEXP scg_sparsemat; SEXP L; SEXP R; SEXP Lsparse; SEXP Rsparse; int c_result; SEXP result, names; /* What to return */ igraph_bool_t do_scg_graph= (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; igraph_bool_t do_scg_matrix=!c_sparse && ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); igraph_bool_t do_scg_sparsemat=c_sparse && ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); igraph_bool_t do_L=c_semproj && !c_sparse; igraph_bool_t do_R=c_semproj && !c_sparse; igraph_bool_t do_Lsparse=c_semproj && c_sparse; igraph_bool_t do_Rsparse=c_semproj && c_sparse; igraph_bool_t do_eval=c_epairs; igraph_bool_t do_evec=c_epairs; /* Convert input */ if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } R_SEXP_to_vector(ev, &c_ev); R_SEXP_to_vector(intervals_vector, &c_intervals_vector); if (do_eval) { igraph_vector_complex_init(&c_eval, 0); } if (!isNull(evec)) { R_SEXP_to_matrix_complex_copy(evec, &c_evec); } else if (do_evec) { igraph_matrix_complex_init(&c_evec, 0, 0); } if (!isNull(groups)) { R_SEXP_to_vector_copy(groups, &c_groups); } else { igraph_vector_init(&c_groups, 0); } if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } if (do_L) { igraph_matrix_init(&c_L, 0, 0); } if (do_R) { igraph_matrix_init(&c_R, 0, 0); } /* Call igraph */ c_result=igraph_scg_laplacian((isNull(graph) ? 0 : &c_graph), (isNull(matrix) ? 0 : &c_matrix), (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, /*intervals=*/ 0, &c_intervals_vector, (igraph_scg_algorithm_t) c_algorithm, (igraph_scg_norm_t) c_norm, (igraph_scg_direction_t) c_direction, (do_eval ? &c_eval : 0), (!isNull(evec) || do_evec ? &c_evec : 0), &c_groups, c_use_arpack, c_maxiter, (do_scg_graph ? &c_scg_graph : 0), (do_scg_matrix ? &c_scg_matrix : 0), (do_scg_sparsemat ? &c_scg_sparsemat : 0), (do_L ? &c_L : 0), (do_R ? &c_R : 0), (do_Lsparse ? &c_Lsparse : 0), (do_Rsparse ? &c_Rsparse : 0)); if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } /* Convert output */ PROTECT(result=NEW_LIST(6)); PROTECT(names=NEW_CHARACTER(6)); if (do_eval) { PROTECT(eval=R_igraph_vector_complex_to_SEXP(&c_eval)); igraph_vector_complex_destroy(&c_eval); } else { PROTECT(eval=R_NilValue); } if (do_evec) { PROTECT(evec=R_igraph_matrix_complex_to_SEXP(&c_evec)); igraph_matrix_complex_destroy(&c_evec); } else { PROTECT(evec=R_NilValue); } PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); igraph_vector_destroy(&c_groups); if (do_scg_graph) { PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); igraph_destroy(&c_scg_graph); } else { PROTECT(scg_graph=R_NilValue); } if (do_scg_matrix) { PROTECT(scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix)); igraph_matrix_destroy(&c_scg_matrix); } else { PROTECT(scg_matrix=R_NilValue); } if (do_scg_sparsemat) { PROTECT(scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat)); igraph_sparsemat_destroy(&c_scg_sparsemat); } else { PROTECT(scg_sparsemat=R_NilValue); } if (do_L) { PROTECT(L=R_igraph_matrix_to_SEXP(&c_L)); igraph_matrix_destroy(&c_L); } else { PROTECT(L=R_NilValue); } if (do_R) { PROTECT(R=R_igraph_matrix_to_SEXP(&c_R)); igraph_matrix_destroy(&c_R); } else { PROTECT(R=R_NilValue); } if (do_Lsparse) { PROTECT(Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse)); igraph_sparsemat_destroy(&c_Lsparse); } else { PROTECT(Lsparse=R_NilValue); } if (do_Rsparse) { PROTECT(Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse)); igraph_sparsemat_destroy(&c_Rsparse); } else { PROTECT(Rsparse=R_NilValue); } if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } SET_VECTOR_ELT(result, 1, groups); if (do_L) { SET_VECTOR_ELT(result, 2, L); } if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } if (do_R) { SET_VECTOR_ELT(result, 3, R); } if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } SET_VECTOR_ELT(result, 4, eval); SET_VECTOR_ELT(result, 5, evec); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); SET_NAMES(result, names); UNPROTECT(11); UNPROTECT(1); return(result); } SEXP R_igraph_subisomorphic_lad(SEXP pattern, SEXP target, SEXP domains, SEXP induced, SEXP time_limit, SEXP pqmap, SEXP pqall_maps) { /* Declarations */ igraph_t c_pattern; igraph_t c_target; igraph_vector_ptr_t c_domains; igraph_bool_t c_iso; igraph_vector_t c_map; igraph_vector_ptr_t c_maps; igraph_bool_t c_induced; int c_time_limit; igraph_bool_t c_qmap; igraph_bool_t c_qall_maps; SEXP iso; SEXP map; SEXP maps; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(pattern, &c_pattern); R_SEXP_to_igraph(target, &c_target); R_igraph_SEXP_to_0orvectorlist(domains, &c_domains); c_qmap=LOGICAL(pqmap)[0]; c_qall_maps=LOGICAL(pqall_maps)[0]; if (c_qmap) { if (0 != igraph_vector_init(&c_map, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_map); map=NEW_NUMERIC(0); /* hack to have a non-NULL value */ } else { map=R_NilValue; } if (c_qall_maps) { if (0 != igraph_vector_ptr_init(&c_maps, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); maps=NEW_NUMERIC(0); /* hack to have a non-NULL value */ } else { maps=R_NilValue; } c_induced=LOGICAL(induced)[0]; c_time_limit=INTEGER(time_limit)[0]; /* Call igraph */ igraph_subisomorphic_lad(&c_pattern, &c_target, (isNull(domains) ? 0 : &c_domains), &c_iso, (isNull(map) ? 0 : &c_map), (isNull(maps) ? 0 : &c_maps), c_induced, c_time_limit); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(iso=NEW_LOGICAL(1)); LOGICAL(iso)[0]=c_iso; if (!isNull(map)) { PROTECT(map=R_igraph_0orvector_to_SEXP(&c_map)); igraph_vector_destroy(&c_map); IGRAPH_FINALLY_CLEAN(1); } else { PROTECT(map=R_NilValue); } if (!isNull(maps)) { PROTECT(maps=R_igraph_0orvectorlist_to_SEXP(&c_maps)); R_igraph_vectorlist_destroy(&c_maps); } else { PROTECT(maps=R_NilValue); } IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, iso); SET_VECTOR_ELT(result, 1, map); SET_VECTOR_ELT(result, 2, maps); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("iso")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("map")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("maps")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_graphlets / /-------------------------------------------*/ SEXP R_igraph_graphlets(SEXP graph, SEXP weights, SEXP niter) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_weights; igraph_vector_ptr_t c_cliques; igraph_vector_t c_Mu; int c_niter; SEXP cliques; SEXP Mu; SEXP result, names; PROTECT(R_igraph_attribute_protected=NEW_LIST(100)); R_igraph_attribute_protected_size=0; IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (0 != igraph_vector_ptr_init(&c_cliques, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cliques); if (0 != igraph_vector_init(&c_Mu, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_Mu); c_niter=INTEGER(niter)[0]; /* Call igraph */ igraph_graphlets(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_cliques, &c_Mu, c_niter); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(cliques=R_igraph_vectorlist_to_SEXP_p1(&c_cliques)); R_igraph_vectorlist_destroy(&c_cliques); IGRAPH_FINALLY_CLEAN(1); PROTECT(Mu=R_igraph_vector_to_SEXP(&c_Mu)); igraph_vector_destroy(&c_Mu); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, cliques); SET_VECTOR_ELT(result, 1, Mu); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("cliques")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Mu")); SET_NAMES(result, names); UNPROTECT(5); /* protected list must be on top */ IGRAPH_FINALLY_CLEAN(1); R_igraph_attribute_protected=0; R_igraph_attribute_protected_size=0; return(result); } /*-------------------------------------------/ / igraph_graphlets_candidate_basis / /-------------------------------------------*/ SEXP R_igraph_graphlets_candidate_basis(SEXP graph, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_weights; igraph_vector_ptr_t c_cliques; igraph_vector_t c_thresholds; SEXP cliques; SEXP thresholds; SEXP result, names; PROTECT(R_igraph_attribute_protected=NEW_LIST(100)); R_igraph_attribute_protected_size=0; IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (0 != igraph_vector_ptr_init(&c_cliques, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cliques); if (0 != igraph_vector_init(&c_thresholds, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_thresholds); /* Call igraph */ igraph_graphlets_candidate_basis(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_cliques, &c_thresholds); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(cliques=R_igraph_vectorlist_to_SEXP_p1(&c_cliques)); R_igraph_vectorlist_destroy(&c_cliques); IGRAPH_FINALLY_CLEAN(1); PROTECT(thresholds=R_igraph_vector_to_SEXP(&c_thresholds)); igraph_vector_destroy(&c_thresholds); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, cliques); SET_VECTOR_ELT(result, 1, thresholds); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("cliques")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("thresholds")); SET_NAMES(result, names); UNPROTECT(5); /* protected list must be on top */ IGRAPH_FINALLY_CLEAN(1); R_igraph_attribute_protected=0; R_igraph_attribute_protected_size=0; return(result); } int igraph_i_graphlets_project(const igraph_t *graph, const igraph_vector_t *weights, const igraph_vector_ptr_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, int niter, int vid1); /*-------------------------------------------/ / igraph_graphlets_project / /-------------------------------------------*/ SEXP R_igraph_graphlets_project(SEXP graph, SEXP weights, SEXP cliques, SEXP Mu, SEXP niter) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_weights; igraph_vector_ptr_t c_cliques; igraph_vector_t c_Mu; int c_niter; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (!isNull(cliques)) { R_igraph_SEXP_to_vectorlist(cliques, &c_cliques); } if (0 != R_SEXP_to_vector_copy(Mu, &c_Mu)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_Mu); c_niter=INTEGER(niter)[0]; /* Call igraph */ igraph_i_graphlets_project(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_cliques, &c_Mu, /*startMu=*/ 1, c_niter, /*vid1=*/ 1); /* Convert output */ PROTECT(Mu=R_igraph_vector_to_SEXP(&c_Mu)); igraph_vector_destroy(&c_Mu); IGRAPH_FINALLY_CLEAN(1); result=Mu; UNPROTECT(1); return(result); } SEXP R_igraph_revolver_d(SEXP graph, SEXP pniter, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_bool_t sd=LOGICAL(psd)[0]; igraph_bool_t norm=LOGICAL(pnorm)[0]; igraph_bool_t cites=LOGICAL(pcites)[0]; igraph_bool_t expected=LOGICAL(pexpected)[0]; igraph_vector_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_vector_t vsd, vnorm, vcites, vexpected; igraph_vector_t *pvsd=0, *pvnorm=0, *pvcites=0, *pvexpected=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); igraph_vector_init(&kernel, 0); if (sd) { igraph_vector_init(&vsd, 0); pvsd=&vsd; } if (norm) { igraph_vector_init(&vnorm, 0); pvnorm=&vnorm; } if (cites) { igraph_vector_init(&vcites, 0); pvcites=&vcites; } if (expected) { igraph_vector_init(&vexpected, 0); pvexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_vector(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_d(&g, niter, &kernel, pvsd, pvnorm, pvcites, pvexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_vector_to_SEXP(&kernel)); igraph_vector_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0orvector_to_SEXP(pvsd)); if (pvsd) { igraph_vector_destroy(pvsd); } SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP(pvnorm)); if (pvnorm) { igraph_vector_destroy(pvnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP(pvcites)); if (pvcites) { igraph_vector_destroy(pvcites); } SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP(pvexpected)); if (pvexpected) { igraph_vector_destroy(pvexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_d(SEXP graph, SEXP pkernel) { igraph_t g; igraph_vector_t kernel; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pkernel, &kernel); igraph_revolver_error2_d(&g, &kernel, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_ad(SEXP graph, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_bool_t sd=LOGICAL(psd)[0]; igraph_bool_t norm=LOGICAL(pnorm)[0]; igraph_bool_t cites=LOGICAL(pcites)[0]; igraph_bool_t expected=LOGICAL(pexpected)[0]; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_matrix_t vsd, vnorm, vcites, vexpected; igraph_matrix_t *pvsd=0, *pvnorm=0, *pvcites=0, *pvexpected=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); igraph_matrix_init(&kernel, 0, 0); if (sd) { igraph_matrix_init(&vsd, 0, 0); pvsd=&vsd; } if (norm) { igraph_matrix_init(&vnorm, 0, 0); pvnorm=&vnorm; } if (cites) { igraph_matrix_init(&vcites, 0, 0); pvcites=&vcites; } if (expected) { igraph_matrix_init(&vexpected, 0, 0); pvexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_ad(&g, niter, agebins, &kernel, pvsd, pvnorm, pvcites, pvexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(pvsd)); if (pvsd) { igraph_matrix_destroy(pvsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(pvnorm)); if (pvnorm) { igraph_matrix_destroy(pvnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(pvcites)); if (pvcites) { igraph_matrix_destroy(pvcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(pvexpected)); if (pvexpected) { igraph_matrix_destroy(pvexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_ad(SEXP graph, SEXP pkernel) { igraph_t g; igraph_matrix_t kernel; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_ad(&g, &kernel, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_ade(SEXP graph, SEXP pcats, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_array3_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_array3_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_array3_init(&kernel, 0, 0, 0); if (LOGICAL(psd)[0]) { igraph_array3_init(&vsd, 0, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_array3_init(&vnorm, 0, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_array3_init(&vcites, 0, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_array3_init(&vexpected, 0, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_ade(&g, niter, agebins, &cats, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_array3_to_SEXP(&kernel)); igraph_array3_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0orarray3_to_SEXP(ppsd)); if (ppsd) { igraph_array3_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0orarray3_to_SEXP(ppnorm)); if (ppnorm) { igraph_array3_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0orarray3_to_SEXP(ppcites)); if (ppcites) { igraph_array3_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0orarray3_to_SEXP(ppexpected)); if (ppexpected) { igraph_array3_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_ade(SEXP graph, SEXP pkernel, SEXP pcats) { igraph_t g; igraph_array3_t kernel; igraph_vector_t cats; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_igraph_SEXP_to_array3(pkernel, &kernel); igraph_revolver_error2_ade(&g, &kernel, &cats, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_e(SEXP graph, SEXP pcats, SEXP pniter, SEXP pst, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_vector_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_vector_t vst, *ppst=0; igraph_vector_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_vector_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_vector_init(&kernel, 0); if (LOGICAL(pst)[0]) { igraph_vector_init(&vst, 0); ppst=&vst; } if (LOGICAL(psd)[0]) { igraph_vector_init(&vsd, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_vector_init(&vnorm, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_vector_init(&vcites, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_vector_init(&vexpected, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_vector(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_e(&g, niter, &cats, &kernel, ppst, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(8)); SET_VECTOR_ELT(result, 0, R_igraph_vector_to_SEXP(&kernel)); igraph_vector_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0orvector_to_SEXP(ppst)); if (ppst) { igraph_vector_destroy(ppst); } SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP(ppsd)); if (ppsd) { igraph_vector_destroy(ppsd); } SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP(ppnorm)); if (ppnorm) { igraph_vector_destroy(ppnorm); } SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP(ppcites)); if (ppcites) { igraph_vector_destroy(ppcites); } SET_VECTOR_ELT(result, 5, R_igraph_0orvector_to_SEXP(ppexpected)); if (ppexpected) { igraph_vector_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 6, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 7, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 7))[0]=*pplogprob; REAL(VECTOR_ELT(result, 7))[1]=*pplognull; REAL(VECTOR_ELT(result, 7))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(8)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("st")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 7, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_e(SEXP graph, SEXP pkernel, SEXP pcats) { igraph_t g; igraph_vector_t kernel; igraph_vector_t cats; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_SEXP_to_vector(pkernel, &kernel); igraph_revolver_error2_e(&g, &kernel, &cats, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_de(SEXP graph, SEXP pcats, SEXP pniter, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_de(&g, niter, &cats, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_de(SEXP graph, SEXP pkernel, SEXP pcats) { igraph_t g; igraph_matrix_t kernel; igraph_vector_t cats; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_de(&g, &kernel, &cats, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_l(SEXP graph, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_vector_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_vector_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); igraph_vector_init(&kernel, 0); if (LOGICAL(psd)[0]) { igraph_vector_init(&vsd, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_vector_init(&vnorm, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_vector_init(&vcites, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_vector_init(&vexpected, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_vector(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_l(&g, niter, agebins, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_vector_to_SEXP(&kernel)); igraph_vector_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0orvector_to_SEXP(ppsd)); if (ppsd) { igraph_vector_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP(ppnorm)); if (ppnorm) { igraph_vector_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP(ppcites)); if (ppcites) { igraph_vector_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP(ppexpected)); if (ppexpected) { igraph_vector_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_l(SEXP graph, SEXP pkernel) { igraph_t g; igraph_vector_t kernel; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pkernel, &kernel); igraph_revolver_error2_l(&g, &kernel, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_dl(SEXP graph, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_dl(&g, niter, agebins, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_dl(SEXP graph, SEXP pkernel) { igraph_t g; igraph_matrix_t kernel; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_dl(&g, &kernel, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_el(SEXP graph, SEXP pcats, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_el(&g, niter, &cats, agebins, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_el(SEXP graph, SEXP pkernel, SEXP pcats) { igraph_t g; igraph_matrix_t kernel; igraph_vector_t cats; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_el(&g, &kernel, &cats, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_r(SEXP graph, SEXP pniter, SEXP pwindow, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_bool_t sd=LOGICAL(psd)[0]; igraph_bool_t norm=LOGICAL(pnorm)[0]; igraph_bool_t cites=LOGICAL(pcites)[0]; igraph_bool_t expected=LOGICAL(pexpected)[0]; igraph_vector_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_vector_t vsd, vnorm, vcites, vexpected; igraph_vector_t *pvsd=0, *pvnorm=0, *pvcites=0, *pvexpected=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); igraph_vector_init(&kernel, 0); if (sd) { igraph_vector_init(&vsd, 0); pvsd=&vsd; } if (norm) { igraph_vector_init(&vnorm, 0); pvnorm=&vnorm; } if (cites) { igraph_vector_init(&vcites, 0); pvcites=&vcites; } if (expected) { igraph_vector_init(&vexpected, 0); pvexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_vector(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_r(&g, niter, window, &kernel, pvsd, pvnorm, pvcites, pvexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_vector_to_SEXP(&kernel)); igraph_vector_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0orvector_to_SEXP(pvsd)); if (pvsd) { igraph_vector_destroy(pvsd); } SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP(pvnorm)); if (pvnorm) { igraph_vector_destroy(pvnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP(pvcites)); if (pvcites) { igraph_vector_destroy(pvcites); } SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP(pvexpected)); if (pvexpected) { igraph_vector_destroy(pvexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_r(SEXP graph, SEXP pkernel, SEXP pwindow) { igraph_t g; igraph_vector_t kernel; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pkernel, &kernel); igraph_revolver_error2_r(&g, &kernel, window, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_ar(SEXP graph, SEXP pniter, SEXP pagebins, SEXP pwindow, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_bool_t sd=LOGICAL(psd)[0]; igraph_bool_t norm=LOGICAL(pnorm)[0]; igraph_bool_t cites=LOGICAL(pcites)[0]; igraph_bool_t expected=LOGICAL(pexpected)[0]; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_matrix_t vsd, vnorm, vcites, vexpected; igraph_matrix_t *pvsd=0, *pvnorm=0, *pvcites=0, *pvexpected=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); igraph_matrix_init(&kernel, 0, 0); if (sd) { igraph_matrix_init(&vsd, 0, 0); pvsd=&vsd; } if (norm) { igraph_matrix_init(&vnorm, 0, 0); pvnorm=&vnorm; } if (cites) { igraph_matrix_init(&vcites, 0, 0); pvcites=&vcites; } if (expected) { igraph_matrix_init(&vexpected, 0, 0); pvexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_ar(&g, niter, agebins, window, &kernel, pvsd, pvnorm, pvcites, pvexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(pvsd)); if (pvsd) { igraph_matrix_destroy(pvsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(pvnorm)); if (pvnorm) { igraph_matrix_destroy(pvnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(pvcites)); if (pvcites) { igraph_matrix_destroy(pvcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(pvexpected)); if (pvexpected) { igraph_matrix_destroy(pvexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_ar(SEXP graph, SEXP pkernel, SEXP pwindow) { igraph_t g; igraph_matrix_t kernel; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_ar(&g, &kernel, window, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_di(SEXP graph, SEXP pcats, SEXP pniter, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_di(&g, niter, &cats, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_di(SEXP graph, SEXP pkernel, SEXP pcats) { igraph_t g; igraph_matrix_t kernel; igraph_vector_t cats; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_di(&g, &kernel, &cats, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_adi(SEXP graph, SEXP pcats, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_array3_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_array3_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_array3_init(&kernel, 0, 0, 0); if (LOGICAL(psd)[0]) { igraph_array3_init(&vsd, 0, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_array3_init(&vnorm, 0, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_array3_init(&vcites, 0, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_array3_init(&vexpected, 0, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_adi(&g, niter, agebins, &cats, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_array3_to_SEXP(&kernel)); igraph_array3_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0orarray3_to_SEXP(ppsd)); if (ppsd) { igraph_array3_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0orarray3_to_SEXP(ppnorm)); if (ppnorm) { igraph_array3_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0orarray3_to_SEXP(ppcites)); if (ppcites) { igraph_array3_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0orarray3_to_SEXP(ppexpected)); if (ppexpected) { igraph_array3_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_adi(SEXP graph, SEXP pkernel, SEXP pcats) { igraph_t g; igraph_array3_t kernel; igraph_vector_t cats; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_igraph_SEXP_to_array3(pkernel, &kernel); igraph_revolver_error2_adi(&g, &kernel, &cats, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_il(SEXP graph, SEXP pcats, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_il(&g, niter, agebins, &cats, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_il(SEXP graph, SEXP pkernel, SEXP pcats) { igraph_t g; igraph_matrix_t kernel; igraph_vector_t cats; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_il(&g, &kernel, &cats, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_ir(SEXP graph, SEXP pcats, SEXP pwindow, SEXP pniter, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_matrix_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_ir(&g, niter, window, &cats, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_ir(SEXP graph, SEXP pkernel, SEXP pcats, SEXP pwindow) { igraph_t g; igraph_matrix_t kernel; igraph_vector_t cats; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_SEXP_to_matrix(pkernel, &kernel); igraph_revolver_error2_ir(&g, &kernel, &cats, window, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_air(SEXP graph, SEXP pcats, SEXP pwindow, SEXP pniter, SEXP pagebins, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t cats; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_array3_t kernel; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_integer_t agebins=(igraph_integer_t) REAL(pagebins)[0]; igraph_array3_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0, rlogmax, *pplogmax=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); igraph_array3_init(&kernel, 0, 0, 0); if (LOGICAL(psd)[0]) { igraph_array3_init(&vsd, 0, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_array3_init(&vnorm, 0, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_array3_init(&vcites, 0, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_array3_init(&vexpected, 0, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; pplogmax=&rlogmax; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_air(&g, niter, window, agebins, &cats, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, pplogmax, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_array3_to_SEXP(&kernel)); igraph_array3_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0orarray3_to_SEXP(ppsd)); if (ppsd) { igraph_array3_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0orarray3_to_SEXP(ppnorm)); if (ppnorm) { igraph_array3_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0orarray3_to_SEXP(ppcites)); if (ppcites) { igraph_array3_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0orarray3_to_SEXP(ppexpected)); if (ppexpected) { igraph_array3_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(3)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; REAL(VECTOR_ELT(result, 6))[2]=*pplogmax; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_revolver_error2_air(SEXP graph, SEXP pkernel, SEXP pcats, SEXP pwindow) { igraph_t g; igraph_array3_t kernel; igraph_vector_t cats; igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; igraph_real_t logprob, lognull; SEXP result; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pcats, &cats); R_igraph_SEXP_to_array3(pkernel, &kernel); igraph_revolver_error2_air(&g, &kernel, &cats, window, &logprob, &lognull); PROTECT(result=NEW_NUMERIC(2)); REAL(result)[0]=logprob; REAL(result)[1]=lognull; UNPROTECT(1); return result; } SEXP R_igraph_revolver_d_d(SEXP graph, SEXP pniter, SEXP pvtime, SEXP petime, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t vtime, etime; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_matrix_t kernel; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pvtime, &vtime); R_SEXP_to_vector(petime, &etime); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_d_d(&g, niter, &vtime, &etime, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(2)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_evolver_d(SEXP pnodes, SEXP pkernel, SEXP poutseq, SEXP poutdist, SEXP pm, SEXP pdirected) { igraph_t g; igraph_integer_t nodes=(igraph_integer_t) REAL(pnodes)[0]; igraph_vector_t kernel; igraph_vector_t voutseq, *ppoutseq=0; igraph_vector_t voutdist, *ppoutdist=0; igraph_integer_t m=(igraph_integer_t) REAL(pm)[0]; igraph_bool_t directed=LOGICAL(pdirected)[0]; SEXP result; R_SEXP_to_vector(pkernel, &kernel); if (!isNull(poutseq)) { R_SEXP_to_vector(poutseq, &voutseq); ppoutseq=&voutseq; } if (!isNull(poutdist)) { R_SEXP_to_vector(poutdist, &voutdist); ppoutdist=&voutdist; } igraph_evolver_d(&g, nodes, &kernel, ppoutseq, ppoutdist, m, directed); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_revolver_p_p(SEXP graph, SEXP pniter, SEXP pvtime, SEXP petime, SEXP pauthors, SEXP peventsizes, SEXP psd, SEXP pnorm, SEXP pcites, SEXP pexpected, SEXP perror, SEXP pdebug) { igraph_t g; igraph_vector_t vtime, etime; igraph_vector_t authors, eventsizes; igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; igraph_matrix_t kernel; igraph_matrix_t vsd, *ppsd=0, vnorm, *ppnorm=0, vcites, *ppcites=0, vexpected, *ppexpected=0; igraph_matrix_t debug, *ppdebug=0; igraph_vector_ptr_t debugres, *ppdebugres=0; igraph_real_t rlogprob, rlognull, *pplogprob=0, *pplognull=0; SEXP result, names; R_SEXP_to_igraph(graph, &g); R_SEXP_to_vector(pvtime, &vtime); R_SEXP_to_vector(petime, &etime); R_SEXP_to_vector(pauthors, &authors); R_SEXP_to_vector(peventsizes, &eventsizes); igraph_matrix_init(&kernel, 0, 0); if (LOGICAL(psd)[0]) { igraph_matrix_init(&vsd, 0, 0); ppsd=&vsd; } if (LOGICAL(pnorm)[0]) { igraph_matrix_init(&vnorm, 0, 0); ppnorm=&vnorm; } if (LOGICAL(pcites)[0]) { igraph_matrix_init(&vcites, 0, 0); ppcites=&vcites; } if (LOGICAL(pexpected)[0]) { igraph_matrix_init(&vexpected, 0, 0); ppexpected=&vexpected; } if (LOGICAL(perror)[0]) { pplogprob=&rlogprob; pplognull=&rlognull; } if (!isNull(pdebug) && GET_LENGTH(pdebug)!=0) { R_SEXP_to_matrix(pdebug, &debug); ppdebug=&debug; igraph_vector_ptr_init(&debugres, 0); ppdebugres=&debugres; } igraph_revolver_p_p(&g, niter, &vtime, &etime, &authors, &eventsizes, &kernel, ppsd, ppnorm, ppcites, ppexpected, pplogprob, pplognull, ppdebug, ppdebugres); PROTECT(result=NEW_LIST(7)); SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&kernel)); igraph_matrix_destroy(&kernel); SET_VECTOR_ELT(result, 1, R_igraph_0ormatrix_to_SEXP(ppsd)); if (ppsd) { igraph_matrix_destroy(ppsd); } SET_VECTOR_ELT(result, 2, R_igraph_0ormatrix_to_SEXP(ppnorm)); if (ppnorm) { igraph_matrix_destroy(ppnorm); } SET_VECTOR_ELT(result, 3, R_igraph_0ormatrix_to_SEXP(ppcites)); if (ppcites) { igraph_matrix_destroy(ppcites); } SET_VECTOR_ELT(result, 4, R_igraph_0ormatrix_to_SEXP(ppexpected)); if (ppexpected) { igraph_matrix_destroy(ppexpected); } if (!isNull(pdebug) && GET_LENGTH(pdebug) != 0) { /* TODO */ } else { SET_VECTOR_ELT(result, 5, R_NilValue); } if (pplogprob) { SET_VECTOR_ELT(result, 6, NEW_NUMERIC(2)); REAL(VECTOR_ELT(result, 6))[0]=*pplogprob; REAL(VECTOR_ELT(result, 6))[1]=*pplognull; } else { SET_VECTOR_ELT(result, 6, R_NilValue); } PROTECT(names=NEW_CHARACTER(7)); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("sd")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("norm")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("expected")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("debug")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("error")); SET_NAMES(result, names); UNPROTECT(2); return result; } SEXP R_igraph_simple_interconnected_islands_game(SEXP islands_n, SEXP islands_size, SEXP islands_pin, SEXP n_inter) { igraph_t g; igraph_integer_t a=INTEGER(islands_n)[0]; igraph_integer_t b=INTEGER(islands_size)[0]; igraph_real_t c=REAL(islands_pin)[0]; igraph_integer_t d=INTEGER(n_inter)[0]; SEXP result; igraph_simple_interconnected_islands_game(&g, a, b, c, d); PROTECT(result=R_igraph_to_SEXP(&g)); igraph_destroy(&g); UNPROTECT(1); return result; } SEXP R_igraph_subclique_next(SEXP pgraph, SEXP pweights, SEXP pids, SEXP pcliques) { igraph_t graph; igraph_vector_t weights; igraph_vector_int_t ids; igraph_vector_ptr_t cliques; int nc=GET_LENGTH(pcliques); igraph_vector_ptr_t result, resultids, resultweights; igraph_vector_t clique_thr, next_thr; SEXP Rresult, Rnames; R_SEXP_to_igraph(pgraph, &graph); R_SEXP_to_vector(pweights, &weights); R_SEXP_to_vector_int(pids, &ids); R_igraph_SEXP_to_vectorlist(pcliques, &cliques); igraph_vector_ptr_init(&result, nc); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &result); igraph_vector_ptr_init(&resultweights, nc); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &resultweights); igraph_vector_ptr_init(&resultids, nc); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &resultids); igraph_vector_init(&clique_thr, nc); IGRAPH_FINALLY(igraph_vector_destroy, &clique_thr); igraph_vector_init(&next_thr, nc); IGRAPH_FINALLY(igraph_vector_destroy, &next_thr); igraph_subclique_next(&graph, &weights, &ids, &cliques, &result, &resultweights, &resultids, &clique_thr, &next_thr); PROTECT(Rresult=NEW_LIST(5)); SET_VECTOR_ELT(Rresult, 0, R_igraph_graphlist_to_SEXP(&result)); R_igraph_graphlist_destroy(&result); SET_VECTOR_ELT(Rresult, 1, R_igraph_vectorlist_int_to_SEXP(&resultids)); R_igraph_vectorlist_int_destroy2(&resultids); SET_VECTOR_ELT(Rresult, 2, R_igraph_vectorlist_to_SEXP(&resultweights)); R_igraph_vectorlist2_destroy(&resultweights); SET_VECTOR_ELT(Rresult, 3, R_igraph_vector_to_SEXP(&clique_thr)); igraph_vector_destroy(&clique_thr); SET_VECTOR_ELT(Rresult, 4, R_igraph_vector_to_SEXP(&next_thr)); igraph_vector_destroy(&next_thr); PROTECT(Rnames=NEW_CHARACTER(5)); SET_STRING_ELT(Rnames, 0, mkChar("graphs")); SET_STRING_ELT(Rnames, 1, mkChar("ids")); SET_STRING_ELT(Rnames, 2, mkChar("weights")); SET_STRING_ELT(Rnames, 3, mkChar("thr")); SET_STRING_ELT(Rnames, 4, mkChar("next_thr")); SET_NAMES(Rresult, Rnames); IGRAPH_FINALLY_CLEAN(5); UNPROTECT(2); return Rresult; } SEXP R_igraph_version() { const char *version; SEXP result; igraph_version(&version, /*major=*/ 0, /*minor=*/ 0, /*patch=*/ 0); PROTECT(result=NEW_CHARACTER(1)); SET_STRING_ELT(result, 0, mkChar(version)); UNPROTECT(1); return result; } SEXP R_igraph_bipartite_projection(SEXP graph, SEXP types, SEXP probe1, SEXP pwhich) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_t c_proj1; igraph_t c_proj2; igraph_vector_t c_multiplicity1; igraph_vector_t c_multiplicity2; igraph_integer_t c_probe1; igraph_integer_t which=INTEGER(pwhich)[0]; igraph_bool_t do_1=(which == 0 || which == 1); igraph_bool_t do_2=(which == 0 || which == 2); SEXP proj1; SEXP proj2; SEXP multiplicity1; SEXP multiplicity2; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } if (0 != igraph_vector_init(&c_multiplicity1, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_multiplicity1); multiplicity1=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_multiplicity2, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_multiplicity2); multiplicity2=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_probe1=INTEGER(probe1)[0]; /* Call igraph */ igraph_bipartite_projection(&c_graph, (isNull(types) ? 0 : &c_types), do_1 ? &c_proj1 : 0, do_2 ? &c_proj2 : 0, (isNull(multiplicity1) ? 0 : &c_multiplicity1), (isNull(multiplicity2) ? 0 : &c_multiplicity2), c_probe1); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); if (do_1) { IGRAPH_FINALLY(igraph_destroy, &c_proj1); PROTECT(proj1=R_igraph_to_SEXP(&c_proj1)); igraph_destroy(&c_proj1); IGRAPH_FINALLY_CLEAN(1); } else { PROTECT(proj1=R_NilValue); } if (do_2) { IGRAPH_FINALLY(igraph_destroy, &c_proj2); PROTECT(proj2=R_igraph_to_SEXP(&c_proj2)); igraph_destroy(&c_proj2); IGRAPH_FINALLY_CLEAN(1); } else { PROTECT(proj2=R_NilValue); } PROTECT(multiplicity1=R_igraph_0orvector_to_SEXP(&c_multiplicity1)); igraph_vector_destroy(&c_multiplicity1); IGRAPH_FINALLY_CLEAN(1); PROTECT(multiplicity2=R_igraph_0orvector_to_SEXP(&c_multiplicity2)); igraph_vector_destroy(&c_multiplicity2); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, proj1); SET_VECTOR_ELT(result, 1, proj2); SET_VECTOR_ELT(result, 2, multiplicity1); SET_VECTOR_ELT(result, 3, multiplicity2); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("proj1")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("proj2")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("multiplicity1")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("multiplicity2")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /***********************************************/ /* THE REST IS GENERATED BY inger.py */ /***********************************************/ /*-------------------------------------------/ / igraph_empty / /-------------------------------------------*/ SEXP R_igraph_empty(SEXP n, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_n; igraph_bool_t c_directed; SEXP graph; SEXP result; /* Convert input */ c_n=INTEGER(n)[0]; c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_empty(&c_graph, c_n, c_directed); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_vcount / /-------------------------------------------*/ SEXP R_igraph_vcount(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_result; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); /* Call igraph */ c_result= igraph_vcount(&c_graph); /* Convert output */ PROTECT(result=NEW_INTEGER(1)); INTEGER(result)[0]=c_result; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_full_citation / /-------------------------------------------*/ SEXP R_igraph_full_citation(SEXP n, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_n; igraph_bool_t c_directed; SEXP graph; SEXP result; /* Convert input */ c_n=INTEGER(n)[0]; c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_full_citation(&c_graph, c_n, c_directed); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_lcf_vector / /-------------------------------------------*/ SEXP R_igraph_lcf_vector(SEXP n, SEXP shifts, SEXP repeats) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_n; igraph_vector_t c_shifts; igraph_integer_t c_repeats; SEXP graph; SEXP result; /* Convert input */ c_n=INTEGER(n)[0]; R_SEXP_to_vector(shifts, &c_shifts); c_repeats=INTEGER(repeats)[0]; /* Call igraph */ igraph_lcf_vector(&c_graph, c_n, &c_shifts, c_repeats); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_adjlist / /-------------------------------------------*/ SEXP R_igraph_adjlist(SEXP adjlist, SEXP mode, SEXP duplicate) { /* Declarations */ igraph_t c_graph; igraph_adjlist_t c_adjlist; igraph_neimode_t c_mode; igraph_bool_t c_duplicate; SEXP graph; SEXP result; /* Convert input */ if (0 != R_SEXP_to_igraph_adjlist(adjlist, &c_adjlist)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } c_mode=(igraph_neimode_t) REAL(mode)[0]; c_duplicate=LOGICAL(duplicate)[0]; /* Call igraph */ igraph_adjlist(&c_graph, &c_adjlist, c_mode, c_duplicate); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_full_bipartite / /-------------------------------------------*/ SEXP R_igraph_full_bipartite(SEXP n1, SEXP n2, SEXP directed, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_integer_t c_n1; igraph_integer_t c_n2; igraph_bool_t c_directed; igraph_neimode_t c_mode; SEXP graph; SEXP types; SEXP result, names; /* Convert input */ if (0 != igraph_vector_bool_init(&c_types, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); types=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_n1=INTEGER(n1)[0]; c_n2=INTEGER(n2)[0]; c_directed=LOGICAL(directed)[0]; c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_full_bipartite(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_directed, c_mode); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); igraph_vector_bool_destroy(&c_types); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, graph); SET_VECTOR_ELT(result, 1, types); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("graph")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("types")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_forest_fire_game / /-------------------------------------------*/ SEXP R_igraph_forest_fire_game(SEXP nodes, SEXP fw_prob, SEXP bw_factor, SEXP ambs, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_nodes; igraph_real_t c_fw_prob; igraph_real_t c_bw_factor; igraph_integer_t c_ambs; igraph_bool_t c_directed; SEXP graph; SEXP result; /* Convert input */ c_nodes=INTEGER(nodes)[0]; c_fw_prob=REAL(fw_prob)[0]; c_bw_factor=REAL(bw_factor)[0]; c_ambs=INTEGER(ambs)[0]; c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_forest_fire_game(&c_graph, c_nodes, c_fw_prob, c_bw_factor, c_ambs, c_directed); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_static_fitness_game / /-------------------------------------------*/ SEXP R_igraph_static_fitness_game(SEXP no_of_edges, SEXP fitness_out, SEXP fitness_in, SEXP loops, SEXP multiple) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_no_of_edges; igraph_vector_t c_fitness_out; igraph_vector_t c_fitness_in; igraph_bool_t c_loops; igraph_bool_t c_multiple; SEXP graph; SEXP result; /* Convert input */ c_no_of_edges=INTEGER(no_of_edges)[0]; R_SEXP_to_vector(fitness_out, &c_fitness_out); if (!isNull(fitness_in)) { R_SEXP_to_vector(fitness_in, &c_fitness_in); } c_loops=LOGICAL(loops)[0]; c_multiple=LOGICAL(multiple)[0]; /* Call igraph */ igraph_static_fitness_game(&c_graph, c_no_of_edges, &c_fitness_out, (isNull(fitness_in) ? 0 : &c_fitness_in), c_loops, c_multiple); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_static_power_law_game / /-------------------------------------------*/ SEXP R_igraph_static_power_law_game(SEXP no_of_nodes, SEXP no_of_edges, SEXP exponent_out, SEXP exponent_in, SEXP loops, SEXP multiple, SEXP finite_size_correction) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_no_of_nodes; igraph_integer_t c_no_of_edges; igraph_real_t c_exponent_out; igraph_real_t c_exponent_in; igraph_bool_t c_loops; igraph_bool_t c_multiple; igraph_bool_t c_finite_size_correction; SEXP graph; SEXP result; /* Convert input */ c_no_of_nodes=INTEGER(no_of_nodes)[0]; c_no_of_edges=INTEGER(no_of_edges)[0]; c_exponent_out=REAL(exponent_out)[0]; c_exponent_in=REAL(exponent_in)[0]; c_loops=LOGICAL(loops)[0]; c_multiple=LOGICAL(multiple)[0]; c_finite_size_correction=LOGICAL(finite_size_correction)[0]; /* Call igraph */ igraph_static_power_law_game(&c_graph, c_no_of_nodes, c_no_of_edges, c_exponent_out, c_exponent_in, c_loops, c_multiple, c_finite_size_correction); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_k_regular_game / /-------------------------------------------*/ SEXP R_igraph_k_regular_game(SEXP no_of_nodes, SEXP k, SEXP directed, SEXP multiple) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_no_of_nodes; igraph_integer_t c_k; igraph_bool_t c_directed; igraph_bool_t c_multiple; SEXP graph; SEXP result; /* Convert input */ c_no_of_nodes=INTEGER(no_of_nodes)[0]; c_k=INTEGER(k)[0]; c_directed=LOGICAL(directed)[0]; c_multiple=LOGICAL(multiple)[0]; /* Call igraph */ igraph_k_regular_game(&c_graph, c_no_of_nodes, c_k, c_directed, c_multiple); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_sbm_game / /-------------------------------------------*/ SEXP R_igraph_sbm_game(SEXP n, SEXP pref_matrix, SEXP block_sizes, SEXP directed, SEXP loops) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_n; igraph_matrix_t c_pref_matrix; igraph_vector_int_t c_block_sizes; igraph_bool_t c_directed; igraph_bool_t c_loops; SEXP graph; SEXP result; /* Convert input */ c_n=INTEGER(n)[0]; R_SEXP_to_matrix(pref_matrix, &c_pref_matrix); R_SEXP_to_vector_int(block_sizes, &c_block_sizes); c_directed=LOGICAL(directed)[0]; c_loops=LOGICAL(loops)[0]; /* Call igraph */ igraph_sbm_game(&c_graph, c_n, &c_pref_matrix, &c_block_sizes, c_directed, c_loops); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_closeness / /-------------------------------------------*/ SEXP R_igraph_closeness(SEXP graph, SEXP vids, SEXP mode, SEXP weights, SEXP normalized) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_vs_t c_vids; igraph_neimode_t c_mode; igraph_vector_t c_weights; igraph_bool_t c_normalized; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_mode=(igraph_neimode_t) REAL(mode)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } c_normalized=LOGICAL(normalized)[0]; /* Call igraph */ igraph_closeness(&c_graph, &c_res, c_vids, c_mode, (isNull(weights) ? 0 : &c_weights), c_normalized); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_closeness_estimate / /-------------------------------------------*/ SEXP R_igraph_closeness_estimate(SEXP graph, SEXP vids, SEXP mode, SEXP cutoff, SEXP weights, SEXP normalized) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_vs_t c_vids; igraph_neimode_t c_mode; igraph_real_t c_cutoff; igraph_vector_t c_weights; igraph_bool_t c_normalized; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_mode=(igraph_neimode_t) REAL(mode)[0]; c_cutoff=REAL(cutoff)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } c_normalized=LOGICAL(normalized)[0]; /* Call igraph */ igraph_closeness_estimate(&c_graph, &c_res, c_vids, c_mode, c_cutoff, (isNull(weights) ? 0 : &c_weights), c_normalized); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_get_all_shortest_paths / /-------------------------------------------*/ SEXP R_igraph_get_all_shortest_paths(SEXP graph, SEXP from, SEXP to, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_vector_ptr_t c_res; igraph_vector_t c_nrgeo; igraph_integer_t c_from; igraph_vs_t c_to; igraph_neimode_t c_mode; SEXP res; SEXP nrgeo; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); if (0 != igraph_vector_init(&c_nrgeo, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_nrgeo); c_from=(igraph_integer_t) REAL(from)[0]; R_SEXP_to_igraph_vs(to, &c_graph, &c_to); c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_get_all_shortest_paths(&c_graph, &c_res, &c_nrgeo, c_from, c_to, c_mode); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); R_igraph_vectorlist_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); PROTECT(nrgeo=R_igraph_vector_to_SEXP(&c_nrgeo)); igraph_vector_destroy(&c_nrgeo); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_to); SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, nrgeo); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("nrgeo")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_get_all_shortest_paths_dijkstra / /-------------------------------------------*/ SEXP R_igraph_get_all_shortest_paths_dijkstra(SEXP graph, SEXP from, SEXP to, SEXP weights, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_vector_ptr_t c_res; igraph_vector_t c_nrgeo; igraph_integer_t c_from; igraph_vs_t c_to; igraph_vector_t c_weights; igraph_neimode_t c_mode; SEXP res; SEXP nrgeo; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); if (0 != igraph_vector_init(&c_nrgeo, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_nrgeo); c_from=(igraph_integer_t) REAL(from)[0]; R_SEXP_to_igraph_vs(to, &c_graph, &c_to); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_get_all_shortest_paths_dijkstra(&c_graph, &c_res, &c_nrgeo, c_from, c_to, (isNull(weights) ? 0 : &c_weights), c_mode); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); R_igraph_vectorlist_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); PROTECT(nrgeo=R_igraph_vector_to_SEXP(&c_nrgeo)); igraph_vector_destroy(&c_nrgeo); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_to); SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, nrgeo); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("nrgeo")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_betweenness_estimate / /-------------------------------------------*/ SEXP R_igraph_betweenness_estimate(SEXP graph, SEXP vids, SEXP directed, SEXP cutoff, SEXP weights, SEXP nobigint) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_vs_t c_vids; igraph_bool_t c_directed; igraph_real_t c_cutoff; igraph_vector_t c_weights; igraph_bool_t c_nobigint; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_directed=LOGICAL(directed)[0]; c_cutoff=REAL(cutoff)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } c_nobigint=LOGICAL(nobigint)[0]; /* Call igraph */ igraph_betweenness_estimate(&c_graph, &c_res, c_vids, c_directed, c_cutoff, (isNull(weights) ? 0 : &c_weights), c_nobigint); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_edge_betweenness / /-------------------------------------------*/ SEXP R_igraph_edge_betweenness(SEXP graph, SEXP directed, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_bool_t c_directed; igraph_vector_t c_weights; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); c_directed=LOGICAL(directed)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_edge_betweenness(&c_graph, &c_res, c_directed, (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_edge_betweenness_estimate / /-------------------------------------------*/ SEXP R_igraph_edge_betweenness_estimate(SEXP graph, SEXP directed, SEXP cutoff, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_bool_t c_directed; igraph_real_t c_cutoff; igraph_vector_t c_weights; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); c_directed=LOGICAL(directed)[0]; c_cutoff=REAL(cutoff)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_edge_betweenness_estimate(&c_graph, &c_res, c_directed, c_cutoff, (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_pagerank_old / /-------------------------------------------*/ SEXP R_igraph_pagerank_old(SEXP graph, SEXP vids, SEXP directed, SEXP niter, SEXP eps, SEXP damping, SEXP old) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_vs_t c_vids; igraph_bool_t c_directed; igraph_integer_t c_niter; igraph_real_t c_eps; igraph_real_t c_damping; igraph_bool_t c_old; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_directed=LOGICAL(directed)[0]; c_niter=INTEGER(niter)[0]; c_eps=REAL(eps)[0]; c_damping=REAL(damping)[0]; c_old=LOGICAL(old)[0]; /* Call igraph */ igraph_pagerank_old(&c_graph, &c_res, c_vids, c_directed, c_niter, c_eps, c_damping, c_old); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_personalized_pagerank / /-------------------------------------------*/ SEXP R_igraph_personalized_pagerank(SEXP graph, SEXP algo, SEXP vids, SEXP directed, SEXP damping, SEXP personalized, SEXP weights, SEXP options) { /* Declarations */ igraph_t c_graph; igraph_pagerank_algo_t c_algo; igraph_vector_t c_vector; igraph_real_t c_value; igraph_vs_t c_vids; igraph_bool_t c_directed; igraph_real_t c_damping; igraph_vector_t c_personalized; igraph_vector_t c_weights; igraph_pagerank_power_options_t c_options1; igraph_arpack_options_t c_options2; void* c_options; SEXP vector; SEXP value; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_algo=(igraph_pagerank_algo_t) INTEGER(algo)[0]; if (0 != igraph_vector_init(&c_vector, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_directed=LOGICAL(directed)[0]; c_damping=REAL(damping)[0]; if (!isNull(personalized)) { R_SEXP_to_vector(personalized, &c_personalized); } if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (c_algo == IGRAPH_PAGERANK_ALGO_POWER) { R_SEXP_to_pagerank_power_options(options, &c_options1); c_options = &c_options1; } else if (c_algo == IGRAPH_PAGERANK_ALGO_ARPACK) { R_SEXP_to_igraph_arpack_options(options, &c_options2); c_options = &c_options2; } else { c_options = 0; } /* Call igraph */ igraph_personalized_pagerank(&c_graph, c_algo, &c_vector, &c_value, c_vids, c_directed, c_damping, (isNull(personalized) ? 0 : &c_personalized), (isNull(weights) ? 0 : &c_weights), c_options); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); igraph_vector_destroy(&c_vector); IGRAPH_FINALLY_CLEAN(1); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; igraph_vs_destroy(&c_vids); if (c_algo == IGRAPH_PAGERANK_ALGO_POWER) { PROTECT(options); } else if (c_algo == IGRAPH_PAGERANK_ALGO_ARPACK) { PROTECT(options = R_igraph_arpack_options_to_SEXP(&c_options2)); } else { PROTECT(options); } SET_VECTOR_ELT(result, 0, vector); SET_VECTOR_ELT(result, 1, value); SET_VECTOR_ELT(result, 2, options); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("vector")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_induced_subgraph / /-------------------------------------------*/ SEXP R_igraph_induced_subgraph(SEXP graph, SEXP vids, SEXP impl) { /* Declarations */ igraph_t c_graph; igraph_t c_res; igraph_vs_t c_vids; igraph_subgraph_implementation_t c_impl; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_impl=(igraph_subgraph_implementation_t) REAL(impl)[0]; /* Call igraph */ igraph_induced_subgraph(&c_graph, &c_res, c_vids, c_impl); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_res); PROTECT(res=R_igraph_to_SEXP(&c_res)); igraph_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_subgraph_edges / /-------------------------------------------*/ SEXP R_igraph_subgraph_edges(SEXP graph, SEXP eids, SEXP delete_vertices) { /* Declarations */ igraph_t c_graph; igraph_t c_res; igraph_es_t c_eids; igraph_bool_t c_delete_vertices; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_igraph_es(eids, &c_graph, &c_eids); c_delete_vertices=LOGICAL(delete_vertices)[0]; /* Call igraph */ igraph_subgraph_edges(&c_graph, &c_res, c_eids, c_delete_vertices); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_res); PROTECT(res=R_igraph_to_SEXP(&c_res)); igraph_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_es_destroy(&c_eids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_path_length_hist / /-------------------------------------------*/ SEXP R_igraph_path_length_hist(SEXP graph, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_real_t c_unconnected; igraph_bool_t c_directed; SEXP res; SEXP unconnected; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_path_length_hist(&c_graph, &c_res, &c_unconnected, c_directed); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); PROTECT(unconnected=NEW_NUMERIC(1)); REAL(unconnected)[0]=c_unconnected; SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, unconnected); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("unconnected")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_simplify / /-------------------------------------------*/ SEXP R_igraph_simplify(SEXP graph, SEXP remove_multiple, SEXP remove_loops, SEXP edge_attr_comb) { /* Declarations */ igraph_t c_graph; igraph_bool_t c_remove_multiple; igraph_bool_t c_remove_loops; igraph_attribute_combination_t c_edge_attr_comb; SEXP result; /* Convert input */ R_SEXP_to_igraph_copy(graph, &c_graph); IGRAPH_FINALLY(igraph_destroy, &c_graph); c_remove_multiple=LOGICAL(remove_multiple)[0]; c_remove_loops=LOGICAL(remove_loops)[0]; R_SEXP_to_attr_comb(edge_attr_comb, &c_edge_attr_comb); /* Call igraph */ igraph_simplify(&c_graph, c_remove_multiple, c_remove_loops, &c_edge_attr_comb); /* Convert output */ PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); igraph_attribute_combination_destroy(&c_edge_attr_comb); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_dag / /-------------------------------------------*/ SEXP R_igraph_is_dag(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); /* Call igraph */ igraph_is_dag(&c_graph, &c_res); /* Convert output */ PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_simple / /-------------------------------------------*/ SEXP R_igraph_is_simple(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); /* Call igraph */ igraph_is_simple(&c_graph, &c_res); /* Convert output */ PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_has_multiple / /-------------------------------------------*/ SEXP R_igraph_has_multiple(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); /* Call igraph */ igraph_has_multiple(&c_graph, &c_res); /* Convert output */ PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_eigenvector_centrality / /-------------------------------------------*/ SEXP R_igraph_eigenvector_centrality(SEXP graph, SEXP directed, SEXP scale, SEXP weights, SEXP options) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_vector; igraph_real_t c_value; igraph_bool_t c_directed; igraph_bool_t c_scale; igraph_vector_t c_weights; igraph_arpack_options_t c_options; SEXP vector; SEXP value; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_vector, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); c_directed=LOGICAL(directed)[0]; c_scale=LOGICAL(scale)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } R_SEXP_to_igraph_arpack_options(options, &c_options); /* Call igraph */ igraph_eigenvector_centrality(&c_graph, &c_vector, &c_value, c_directed, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); igraph_vector_destroy(&c_vector); IGRAPH_FINALLY_CLEAN(1); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); SET_VECTOR_ELT(result, 0, vector); SET_VECTOR_ELT(result, 1, value); SET_VECTOR_ELT(result, 2, options); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("vector")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_hub_score / /-------------------------------------------*/ SEXP R_igraph_hub_score(SEXP graph, SEXP scale, SEXP weights, SEXP options) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_vector; igraph_real_t c_value; igraph_bool_t c_scale; igraph_vector_t c_weights; igraph_arpack_options_t c_options; SEXP vector; SEXP value; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_vector, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); c_scale=LOGICAL(scale)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } R_SEXP_to_igraph_arpack_options(options, &c_options); /* Call igraph */ igraph_hub_score(&c_graph, &c_vector, &c_value, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); igraph_vector_destroy(&c_vector); IGRAPH_FINALLY_CLEAN(1); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); SET_VECTOR_ELT(result, 0, vector); SET_VECTOR_ELT(result, 1, value); SET_VECTOR_ELT(result, 2, options); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("vector")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_authority_score / /-------------------------------------------*/ SEXP R_igraph_authority_score(SEXP graph, SEXP scale, SEXP weights, SEXP options) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_vector; igraph_real_t c_value; igraph_bool_t c_scale; igraph_vector_t c_weights; igraph_arpack_options_t c_options; SEXP vector; SEXP value; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_vector, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); c_scale=LOGICAL(scale)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } R_SEXP_to_igraph_arpack_options(options, &c_options); /* Call igraph */ igraph_authority_score(&c_graph, &c_vector, &c_value, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); igraph_vector_destroy(&c_vector); IGRAPH_FINALLY_CLEAN(1); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); SET_VECTOR_ELT(result, 0, vector); SET_VECTOR_ELT(result, 1, value); SET_VECTOR_ELT(result, 2, options); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("vector")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_arpack_unpack_complex / /-------------------------------------------*/ SEXP R_igraph_arpack_unpack_complex(SEXP vectors, SEXP values, SEXP nev) { /* Declarations */ igraph_matrix_t c_vectors; igraph_matrix_t c_values; igraph_integer_t c_nev; SEXP result, names; /* Convert input */ if (0 != R_SEXP_to_igraph_matrix_copy(vectors, &c_vectors)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_vectors); if (0 != R_SEXP_to_igraph_matrix_copy(values, &c_values)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_values); c_nev=INTEGER(nev)[0]; /* Call igraph */ igraph_arpack_unpack_complex(&c_vectors, &c_values, c_nev); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(vectors=R_igraph_matrix_to_SEXP(&c_vectors)); igraph_matrix_destroy(&c_vectors); IGRAPH_FINALLY_CLEAN(1); PROTECT(values=R_igraph_matrix_to_SEXP(&c_values)); igraph_matrix_destroy(&c_values); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, vectors); SET_VECTOR_ELT(result, 1, values); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("vectors")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("values")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_unfold_tree / /-------------------------------------------*/ SEXP R_igraph_unfold_tree(SEXP graph, SEXP mode, SEXP roots) { /* Declarations */ igraph_t c_graph; igraph_t c_tree; igraph_neimode_t c_mode; igraph_vector_t c_roots; igraph_vector_t c_vertex_index; SEXP tree; SEXP vertex_index; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_mode=(igraph_neimode_t) REAL(mode)[0]; R_SEXP_to_vector(roots, &c_roots); if (0 != igraph_vector_init(&c_vertex_index, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_vertex_index); vertex_index=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_unfold_tree(&c_graph, &c_tree, c_mode, &c_roots, (isNull(vertex_index) ? 0 : &c_vertex_index)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); IGRAPH_FINALLY(igraph_destroy, &c_tree); PROTECT(tree=R_igraph_to_SEXP(&c_tree)); igraph_destroy(&c_tree); IGRAPH_FINALLY_CLEAN(1); PROTECT(vertex_index=R_igraph_0orvector_to_SEXPp1(&c_vertex_index)); igraph_vector_destroy(&c_vertex_index); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, tree); SET_VECTOR_ELT(result, 1, vertex_index); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("tree")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("vertex_index")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_mutual / /-------------------------------------------*/ SEXP R_igraph_is_mutual(SEXP graph, SEXP es) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_res; igraph_es_t c_es; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_bool_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_res); R_SEXP_to_igraph_es(es, &c_graph, &c_es); /* Call igraph */ igraph_is_mutual(&c_graph, &c_res, c_es); /* Convert output */ PROTECT(res=R_igraph_vector_bool_to_SEXP(&c_res)); igraph_vector_bool_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_es_destroy(&c_es); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_maximum_cardinality_search / /-------------------------------------------*/ SEXP R_igraph_maximum_cardinality_search(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_alpha; igraph_vector_t c_alpham1; SEXP alpha; SEXP alpham1; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_alpha, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_alpha); if (0 != igraph_vector_init(&c_alpham1, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_alpham1); alpham1=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_maximum_cardinality_search(&c_graph, &c_alpha, (isNull(alpham1) ? 0 : &c_alpham1)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(alpha=R_igraph_vector_to_SEXPp1(&c_alpha)); igraph_vector_destroy(&c_alpha); IGRAPH_FINALLY_CLEAN(1); PROTECT(alpham1=R_igraph_0orvector_to_SEXPp1(&c_alpham1)); igraph_vector_destroy(&c_alpham1); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, alpham1); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("alpham1")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_avg_nearest_neighbor_degree / /-------------------------------------------*/ SEXP R_igraph_avg_nearest_neighbor_degree(SEXP graph, SEXP vids, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vs_t c_vids; igraph_vector_t c_knn; igraph_vector_t c_knnk; igraph_vector_t c_weights; SEXP knn; SEXP knnk; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); if (0 != igraph_vector_init(&c_knn, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_knn); if (0 != igraph_vector_init(&c_knnk, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_knnk); knnk=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_avg_nearest_neighbor_degree(&c_graph, c_vids, &c_knn, (isNull(knnk) ? 0 : &c_knnk), (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); igraph_vs_destroy(&c_vids); PROTECT(knn=R_igraph_vector_to_SEXP(&c_knn)); igraph_vector_destroy(&c_knn); IGRAPH_FINALLY_CLEAN(1); PROTECT(knnk=R_igraph_0orvector_to_SEXP(&c_knnk)); igraph_vector_destroy(&c_knnk); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, knn); SET_VECTOR_ELT(result, 1, knnk); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("knn")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("knnk")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_strength / /-------------------------------------------*/ SEXP R_igraph_strength(SEXP graph, SEXP vids, SEXP mode, SEXP loops, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_vs_t c_vids; igraph_neimode_t c_mode; igraph_bool_t c_loops; igraph_vector_t c_weights; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_mode=(igraph_neimode_t) REAL(mode)[0]; c_loops=LOGICAL(loops)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_strength(&c_graph, &c_res, c_vids, c_mode, c_loops, (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization / /-------------------------------------------*/ SEXP R_igraph_centralization(SEXP scores, SEXP theoretical_max, SEXP normalized) { /* Declarations */ igraph_vector_t c_scores; igraph_real_t c_theoretical_max; igraph_bool_t c_normalized; igraph_real_t c_result; SEXP result; /* Convert input */ R_SEXP_to_vector(scores, &c_scores); c_theoretical_max=REAL(theoretical_max)[0]; c_normalized=LOGICAL(normalized)[0]; /* Call igraph */ c_result= igraph_centralization(&c_scores, c_theoretical_max, c_normalized); /* Convert output */ PROTECT(result=NEW_NUMERIC(1)); REAL(result)[0]=c_result; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_degree / /-------------------------------------------*/ SEXP R_igraph_centralization_degree(SEXP graph, SEXP mode, SEXP loops, SEXP normalized) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_neimode_t c_mode; igraph_bool_t c_loops; igraph_real_t c_centralization; igraph_real_t c_theoretical_max; igraph_bool_t c_normalized; SEXP res; SEXP centralization; SEXP theoretical_max; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); c_mode=(igraph_neimode_t) REAL(mode)[0]; c_loops=LOGICAL(loops)[0]; c_normalized=LOGICAL(normalized)[0]; /* Call igraph */ igraph_centralization_degree(&c_graph, &c_res, c_mode, c_loops, &c_centralization, &c_theoretical_max, c_normalized); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); PROTECT(centralization=NEW_NUMERIC(1)); REAL(centralization)[0]=c_centralization; PROTECT(theoretical_max=NEW_NUMERIC(1)); REAL(theoretical_max)[0]=c_theoretical_max; SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, centralization); SET_VECTOR_ELT(result, 2, theoretical_max); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("centralization")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("theoretical_max")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_degree_tmax / /-------------------------------------------*/ SEXP R_igraph_centralization_degree_tmax(SEXP graph, SEXP nodes, SEXP mode, SEXP loops) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_nodes; igraph_neimode_t c_mode; igraph_bool_t c_loops; igraph_real_t c_res; SEXP res; SEXP result; /* Convert input */ if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } c_nodes=INTEGER(nodes)[0]; c_mode=(igraph_neimode_t) REAL(mode)[0]; c_loops=LOGICAL(loops)[0]; /* Call igraph */ igraph_centralization_degree_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_mode, c_loops, &c_res); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_betweenness / /-------------------------------------------*/ SEXP R_igraph_centralization_betweenness(SEXP graph, SEXP directed, SEXP nobigint, SEXP normalized) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_bool_t c_directed; igraph_bool_t c_nobigint; igraph_real_t c_centralization; igraph_real_t c_theoretical_max; igraph_bool_t c_normalized; SEXP res; SEXP centralization; SEXP theoretical_max; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); c_directed=LOGICAL(directed)[0]; c_nobigint=LOGICAL(nobigint)[0]; c_normalized=LOGICAL(normalized)[0]; /* Call igraph */ igraph_centralization_betweenness(&c_graph, &c_res, c_directed, c_nobigint, &c_centralization, &c_theoretical_max, c_normalized); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); PROTECT(centralization=NEW_NUMERIC(1)); REAL(centralization)[0]=c_centralization; PROTECT(theoretical_max=NEW_NUMERIC(1)); REAL(theoretical_max)[0]=c_theoretical_max; SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, centralization); SET_VECTOR_ELT(result, 2, theoretical_max); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("centralization")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("theoretical_max")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_betweenness_tmax / /-------------------------------------------*/ SEXP R_igraph_centralization_betweenness_tmax(SEXP graph, SEXP nodes, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_nodes; igraph_bool_t c_directed; igraph_real_t c_res; SEXP res; SEXP result; /* Convert input */ if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } c_nodes=INTEGER(nodes)[0]; c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_centralization_betweenness_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_directed, &c_res); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_closeness / /-------------------------------------------*/ SEXP R_igraph_centralization_closeness(SEXP graph, SEXP mode, SEXP normalized) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_neimode_t c_mode; igraph_real_t c_centralization; igraph_real_t c_theoretical_max; igraph_bool_t c_normalized; SEXP res; SEXP centralization; SEXP theoretical_max; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); c_mode=(igraph_neimode_t) REAL(mode)[0]; c_normalized=LOGICAL(normalized)[0]; /* Call igraph */ igraph_centralization_closeness(&c_graph, &c_res, c_mode, &c_centralization, &c_theoretical_max, c_normalized); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); PROTECT(centralization=NEW_NUMERIC(1)); REAL(centralization)[0]=c_centralization; PROTECT(theoretical_max=NEW_NUMERIC(1)); REAL(theoretical_max)[0]=c_theoretical_max; SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, centralization); SET_VECTOR_ELT(result, 2, theoretical_max); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("centralization")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("theoretical_max")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_closeness_tmax / /-------------------------------------------*/ SEXP R_igraph_centralization_closeness_tmax(SEXP graph, SEXP nodes, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_nodes; igraph_neimode_t c_mode; igraph_real_t c_res; SEXP res; SEXP result; /* Convert input */ if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } c_nodes=INTEGER(nodes)[0]; c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_centralization_closeness_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_mode, &c_res); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_eigenvector_centrality / /-------------------------------------------*/ SEXP R_igraph_centralization_eigenvector_centrality(SEXP graph, SEXP directed, SEXP scale, SEXP options, SEXP normalized) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_vector; igraph_real_t c_value; igraph_bool_t c_directed; igraph_bool_t c_scale; igraph_arpack_options_t c_options; igraph_real_t c_centralization; igraph_real_t c_theoretical_max; igraph_bool_t c_normalized; SEXP vector; SEXP value; SEXP centralization; SEXP theoretical_max; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_vector, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); c_directed=LOGICAL(directed)[0]; c_scale=LOGICAL(scale)[0]; R_SEXP_to_igraph_arpack_options(options, &c_options); c_normalized=LOGICAL(normalized)[0]; /* Call igraph */ igraph_centralization_eigenvector_centrality(&c_graph, &c_vector, &c_value, c_directed, c_scale, &c_options, &c_centralization, &c_theoretical_max, c_normalized); /* Convert output */ PROTECT(result=NEW_LIST(5)); PROTECT(names=NEW_CHARACTER(5)); PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); igraph_vector_destroy(&c_vector); IGRAPH_FINALLY_CLEAN(1); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); PROTECT(centralization=NEW_NUMERIC(1)); REAL(centralization)[0]=c_centralization; PROTECT(theoretical_max=NEW_NUMERIC(1)); REAL(theoretical_max)[0]=c_theoretical_max; SET_VECTOR_ELT(result, 0, vector); SET_VECTOR_ELT(result, 1, value); SET_VECTOR_ELT(result, 2, options); SET_VECTOR_ELT(result, 3, centralization); SET_VECTOR_ELT(result, 4, theoretical_max); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("vector")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("centralization")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("theoretical_max")); SET_NAMES(result, names); UNPROTECT(6); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_centralization_eigenvector_centrality_tmax / /-------------------------------------------*/ SEXP R_igraph_centralization_eigenvector_centrality_tmax(SEXP graph, SEXP nodes, SEXP directed, SEXP scale) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_nodes; igraph_bool_t c_directed; igraph_bool_t c_scale; igraph_real_t c_res; SEXP res; SEXP result; /* Convert input */ if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } c_nodes=INTEGER(nodes)[0]; c_directed=LOGICAL(directed)[0]; c_scale=LOGICAL(scale)[0]; /* Call igraph */ igraph_centralization_eigenvector_centrality_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_directed, c_scale, &c_res); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_assortativity_nominal / /-------------------------------------------*/ SEXP R_igraph_assortativity_nominal(SEXP graph, SEXP types, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_types; igraph_real_t c_res; igraph_bool_t c_directed; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(types, &c_types); c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_assortativity_nominal(&c_graph, &c_types, &c_res, c_directed); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_assortativity / /-------------------------------------------*/ SEXP R_igraph_assortativity(SEXP graph, SEXP types1, SEXP types2, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_types1; igraph_vector_t c_types2; igraph_real_t c_res; igraph_bool_t c_directed; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(types1, &c_types1); if (!isNull(types2)) { R_SEXP_to_vector(types2, &c_types2); } c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_assortativity(&c_graph, &c_types1, (isNull(types2) ? 0 : &c_types2), &c_res, c_directed); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_assortativity_degree / /-------------------------------------------*/ SEXP R_igraph_assortativity_degree(SEXP graph, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_real_t c_res; igraph_bool_t c_directed; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_assortativity_degree(&c_graph, &c_res, c_directed); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_contract_vertices / /-------------------------------------------*/ SEXP R_igraph_contract_vertices(SEXP graph, SEXP mapping, SEXP vertex_attr_comb) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_mapping; igraph_attribute_combination_t c_vertex_attr_comb; SEXP result; /* Convert input */ R_SEXP_to_igraph_copy(graph, &c_graph); IGRAPH_FINALLY(igraph_destroy, &c_graph); R_SEXP_to_vector(mapping, &c_mapping); R_SEXP_to_attr_comb(vertex_attr_comb, &c_vertex_attr_comb); /* Call igraph */ igraph_contract_vertices(&c_graph, &c_mapping, &c_vertex_attr_comb); /* Convert output */ PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); igraph_attribute_combination_destroy(&c_vertex_attr_comb); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_eccentricity / /-------------------------------------------*/ SEXP R_igraph_eccentricity(SEXP graph, SEXP vids, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_vs_t c_vids; igraph_neimode_t c_mode; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_eccentricity(&c_graph, &c_res, c_vids, c_mode); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_radius / /-------------------------------------------*/ SEXP R_igraph_radius(SEXP graph, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_real_t c_radius; igraph_neimode_t c_mode; SEXP radius; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_radius(&c_graph, &c_radius, c_mode); /* Convert output */ PROTECT(radius=NEW_NUMERIC(1)); REAL(radius)[0]=c_radius; result=radius; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_diversity / /-------------------------------------------*/ SEXP R_igraph_diversity(SEXP graph, SEXP weights, SEXP vids) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_weights; igraph_vector_t c_res; igraph_vs_t c_vids; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); /* Call igraph */ igraph_diversity(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_res, c_vids); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_degree_sequence / /-------------------------------------------*/ SEXP R_igraph_is_degree_sequence(SEXP out_deg, SEXP in_deg) { /* Declarations */ igraph_vector_t c_out_deg; igraph_vector_t c_in_deg; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_vector(out_deg, &c_out_deg); if (!isNull(in_deg)) { R_SEXP_to_vector(in_deg, &c_in_deg); } /* Call igraph */ igraph_is_degree_sequence(&c_out_deg, (isNull(in_deg) ? 0 : &c_in_deg), &c_res); /* Convert output */ PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_graphical_degree_sequence / /-------------------------------------------*/ SEXP R_igraph_is_graphical_degree_sequence(SEXP out_deg, SEXP in_deg) { /* Declarations */ igraph_vector_t c_out_deg; igraph_vector_t c_in_deg; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_vector(out_deg, &c_out_deg); if (!isNull(in_deg)) { R_SEXP_to_vector(in_deg, &c_in_deg); } /* Call igraph */ igraph_is_graphical_degree_sequence(&c_out_deg, (isNull(in_deg) ? 0 : &c_in_deg), &c_res); /* Convert output */ PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_bipartite_projection_size / /-------------------------------------------*/ SEXP R_igraph_bipartite_projection_size(SEXP graph, SEXP types) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_integer_t c_vcount1; igraph_integer_t c_ecount1; igraph_integer_t c_vcount2; igraph_integer_t c_ecount2; SEXP vcount1; SEXP ecount1; SEXP vcount2; SEXP ecount2; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } /* Call igraph */ igraph_bipartite_projection_size(&c_graph, (isNull(types) ? 0 : &c_types), &c_vcount1, &c_ecount1, &c_vcount2, &c_ecount2); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(vcount1=NEW_INTEGER(1)); INTEGER(vcount1)[0]=c_vcount1; PROTECT(ecount1=NEW_INTEGER(1)); INTEGER(ecount1)[0]=c_ecount1; PROTECT(vcount2=NEW_INTEGER(1)); INTEGER(vcount2)[0]=c_vcount2; PROTECT(ecount2=NEW_INTEGER(1)); INTEGER(ecount2)[0]=c_ecount2; SET_VECTOR_ELT(result, 0, vcount1); SET_VECTOR_ELT(result, 1, ecount1); SET_VECTOR_ELT(result, 2, vcount2); SET_VECTOR_ELT(result, 3, ecount2); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("vcount1")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("ecount1")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("vcount2")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("ecount2")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_create_bipartite / /-------------------------------------------*/ SEXP R_igraph_create_bipartite(SEXP types, SEXP edges, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_vector_t c_edges; igraph_bool_t c_directed; SEXP graph; SEXP result; /* Convert input */ R_SEXP_to_vector_bool(types, &c_types); R_SEXP_to_vector(edges, &c_edges); c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_create_bipartite(&c_graph, &c_types, &c_edges, c_directed); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_incidence / /-------------------------------------------*/ SEXP R_igraph_incidence(SEXP incidence, SEXP directed, SEXP mode, SEXP multiple) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_matrix_t c_incidence; igraph_bool_t c_directed; igraph_neimode_t c_mode; igraph_bool_t c_multiple; SEXP graph; SEXP types; SEXP result, names; /* Convert input */ if (0 != igraph_vector_bool_init(&c_types, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); R_SEXP_to_matrix(incidence, &c_incidence); c_directed=LOGICAL(directed)[0]; c_mode=(igraph_neimode_t) REAL(mode)[0]; c_multiple=LOGICAL(multiple)[0]; /* Call igraph */ igraph_incidence(&c_graph, &c_types, &c_incidence, c_directed, c_mode, c_multiple); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); PROTECT(types=R_igraph_vector_bool_to_SEXP(&c_types)); igraph_vector_bool_destroy(&c_types); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, graph); SET_VECTOR_ELT(result, 1, types); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("graph")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("types")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_get_incidence / /-------------------------------------------*/ SEXP R_igraph_get_incidence(SEXP graph, SEXP types) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_matrix_t c_res; igraph_vector_t c_row_ids; igraph_vector_t c_col_ids; SEXP res; SEXP row_ids; SEXP col_ids; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); if (0 != igraph_vector_init(&c_row_ids, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_row_ids); row_ids=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_col_ids, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_col_ids); col_ids=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_get_incidence(&c_graph, (isNull(types) ? 0 : &c_types), &c_res, (isNull(row_ids) ? 0 : &c_row_ids), (isNull(col_ids) ? 0 : &c_col_ids)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); PROTECT(row_ids=R_igraph_0orvector_to_SEXP(&c_row_ids)); igraph_vector_destroy(&c_row_ids); IGRAPH_FINALLY_CLEAN(1); PROTECT(col_ids=R_igraph_0orvector_to_SEXP(&c_col_ids)); igraph_vector_destroy(&c_col_ids); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, row_ids); SET_VECTOR_ELT(result, 2, col_ids); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("row_ids")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("col_ids")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_bipartite / /-------------------------------------------*/ SEXP R_igraph_is_bipartite(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_bool_t c_res; igraph_vector_bool_t c_type; SEXP res; SEXP type; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_bool_init(&c_type, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_type); type=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_is_bipartite(&c_graph, &c_res, (isNull(type) ? 0 : &c_type)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; PROTECT(type=R_igraph_0orvector_bool_to_SEXP(&c_type)); igraph_vector_bool_destroy(&c_type); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, type); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("type")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_bipartite_game_gnp / /-------------------------------------------*/ SEXP R_igraph_bipartite_game_gnp(SEXP n1, SEXP n2, SEXP p, SEXP directed, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_integer_t c_n1; igraph_integer_t c_n2; igraph_real_t c_p; igraph_bool_t c_directed; igraph_neimode_t c_mode; SEXP graph; SEXP types; SEXP result, names; /* Convert input */ if (0 != igraph_vector_bool_init(&c_types, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); types=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_n1=INTEGER(n1)[0]; c_n2=INTEGER(n2)[0]; c_p=REAL(p)[0]; c_directed=LOGICAL(directed)[0]; c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_bipartite_game_gnp(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_p, c_directed, c_mode); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); igraph_vector_bool_destroy(&c_types); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, graph); SET_VECTOR_ELT(result, 1, types); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("graph")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("types")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_bipartite_game_gnm / /-------------------------------------------*/ SEXP R_igraph_bipartite_game_gnm(SEXP n1, SEXP n2, SEXP m, SEXP directed, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_integer_t c_n1; igraph_integer_t c_n2; igraph_integer_t c_m; igraph_bool_t c_directed; igraph_neimode_t c_mode; SEXP graph; SEXP types; SEXP result, names; /* Convert input */ if (0 != igraph_vector_bool_init(&c_types, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); types=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_n1=INTEGER(n1)[0]; c_n2=INTEGER(n2)[0]; c_m=INTEGER(m)[0]; c_directed=LOGICAL(directed)[0]; c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_bipartite_game_gnm(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_m, c_directed, c_mode); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); igraph_vector_bool_destroy(&c_types); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, graph); SET_VECTOR_ELT(result, 1, types); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("graph")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("types")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_clusters / /-------------------------------------------*/ SEXP R_igraph_clusters(SEXP graph, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_membership; igraph_vector_t c_csize; igraph_integer_t c_no; igraph_connectedness_t c_mode; SEXP membership; SEXP csize; SEXP no; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_membership, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); if (0 != igraph_vector_init(&c_csize, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_csize); c_mode=REAL(mode)[0]; /* Call igraph */ igraph_clusters(&c_graph, &c_membership, &c_csize, &c_no, c_mode); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); PROTECT(csize=R_igraph_vector_to_SEXP(&c_csize)); igraph_vector_destroy(&c_csize); IGRAPH_FINALLY_CLEAN(1); PROTECT(no=NEW_INTEGER(1)); INTEGER(no)[0]=c_no; SET_VECTOR_ELT(result, 0, membership); SET_VECTOR_ELT(result, 1, csize); SET_VECTOR_ELT(result, 2, no); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("membership")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("csize")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("no")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_articulation_points / /-------------------------------------------*/ SEXP R_igraph_articulation_points(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); /* Call igraph */ igraph_articulation_points(&c_graph, &c_res); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXPp1(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_biconnected_components / /-------------------------------------------*/ SEXP R_igraph_biconnected_components(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_no; igraph_vector_ptr_t c_tree_edges; igraph_vector_ptr_t c_component_edges; igraph_vector_ptr_t c_components; igraph_vector_t c_articulation_points; SEXP no; SEXP tree_edges; SEXP component_edges; SEXP components; SEXP articulation_points; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_tree_edges, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_tree_edges); if (0 != igraph_vector_ptr_init(&c_component_edges, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_component_edges); if (0 != igraph_vector_ptr_init(&c_components, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_components); if (0 != igraph_vector_init(&c_articulation_points, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_articulation_points); /* Call igraph */ igraph_biconnected_components(&c_graph, &c_no, &c_tree_edges, &c_component_edges, &c_components, &c_articulation_points); /* Convert output */ PROTECT(result=NEW_LIST(5)); PROTECT(names=NEW_CHARACTER(5)); PROTECT(no=NEW_INTEGER(1)); INTEGER(no)[0]=c_no; PROTECT(tree_edges=R_igraph_vectorlist_to_SEXP_p1(&c_tree_edges)); R_igraph_vectorlist_destroy(&c_tree_edges); IGRAPH_FINALLY_CLEAN(1); PROTECT(component_edges=R_igraph_vectorlist_to_SEXP_p1(&c_component_edges)); R_igraph_vectorlist_destroy(&c_component_edges); IGRAPH_FINALLY_CLEAN(1); PROTECT(components=R_igraph_vectorlist_to_SEXP_p1(&c_components)); R_igraph_vectorlist_destroy(&c_components); IGRAPH_FINALLY_CLEAN(1); PROTECT(articulation_points=R_igraph_vector_to_SEXPp1(&c_articulation_points)); igraph_vector_destroy(&c_articulation_points); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, no); SET_VECTOR_ELT(result, 1, tree_edges); SET_VECTOR_ELT(result, 2, component_edges); SET_VECTOR_ELT(result, 3, components); SET_VECTOR_ELT(result, 4, articulation_points); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("no")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("tree_edges")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("component_edges")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("components")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("articulation_points")); SET_NAMES(result, names); UNPROTECT(6); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_star / /-------------------------------------------*/ SEXP R_igraph_layout_star(SEXP graph, SEXP center, SEXP order) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_integer_t c_center; igraph_vector_t c_order; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); c_center=(igraph_integer_t) REAL(center)[0]; if (!isNull(order)) { R_SEXP_to_vector(order, &c_order); } /* Call igraph */ igraph_layout_star(&c_graph, &c_res, c_center, (isNull(order) ? 0 : &c_order)); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_grid / /-------------------------------------------*/ SEXP R_igraph_layout_grid(SEXP graph, SEXP width) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_integer_t c_width; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); c_width=INTEGER(width)[0]; /* Call igraph */ igraph_layout_grid(&c_graph, &c_res, c_width); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_grid_3d / /-------------------------------------------*/ SEXP R_igraph_layout_grid_3d(SEXP graph, SEXP width, SEXP height) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_integer_t c_width; igraph_integer_t c_height; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); c_width=INTEGER(width)[0]; c_height=INTEGER(height)[0]; /* Call igraph */ igraph_layout_grid_3d(&c_graph, &c_res, c_width, c_height); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_drl / /-------------------------------------------*/ SEXP R_igraph_layout_drl(SEXP graph, SEXP res, SEXP use_seed, SEXP options, SEXP weights, SEXP fixed) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_bool_t c_use_seed; igraph_layout_drl_options_t c_options; igraph_vector_t c_weights; igraph_vector_bool_t c_fixed; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); c_use_seed=LOGICAL(use_seed)[0]; R_SEXP_to_igraph_layout_drl_options(options, &c_options); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } /* Call igraph */ igraph_layout_drl(&c_graph, &c_res, c_use_seed, &c_options, (isNull(weights) ? 0 : &c_weights), (isNull(fixed) ? 0 : &c_fixed)); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_drl_3d / /-------------------------------------------*/ SEXP R_igraph_layout_drl_3d(SEXP graph, SEXP res, SEXP use_seed, SEXP options, SEXP weights, SEXP fixed) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_bool_t c_use_seed; igraph_layout_drl_options_t c_options; igraph_vector_t c_weights; igraph_vector_bool_t c_fixed; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); c_use_seed=LOGICAL(use_seed)[0]; R_SEXP_to_igraph_layout_drl_options(options, &c_options); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } /* Call igraph */ igraph_layout_drl_3d(&c_graph, &c_res, c_use_seed, &c_options, (isNull(weights) ? 0 : &c_weights), (isNull(fixed) ? 0 : &c_fixed)); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_sugiyama / /-------------------------------------------*/ SEXP R_igraph_layout_sugiyama(SEXP graph, SEXP layers, SEXP hgap, SEXP vgap, SEXP maxiter, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_t c_extd_graph; igraph_vector_t c_extd_to_orig_eids; igraph_vector_t c_layers; igraph_real_t c_hgap; igraph_real_t c_vgap; igraph_integer_t c_maxiter; igraph_vector_t c_weights; SEXP res; SEXP extd_graph; SEXP extd_to_orig_eids; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); extd_graph=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_extd_to_orig_eids, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_extd_to_orig_eids); extd_to_orig_eids=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (!isNull(layers)) { R_SEXP_to_vector(layers, &c_layers); } c_hgap=REAL(hgap)[0]; c_vgap=REAL(vgap)[0]; c_maxiter=INTEGER(maxiter)[0]; if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_layout_sugiyama(&c_graph, &c_res, (isNull(extd_graph) ? 0 : &c_extd_graph), (isNull(extd_to_orig_eids) ? 0 : &c_extd_to_orig_eids), (isNull(layers) ? 0 : &c_layers), c_hgap, c_vgap, c_maxiter, (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &c_extd_graph); PROTECT(extd_graph=R_igraph_to_SEXP(&c_extd_graph)); igraph_destroy(&c_extd_graph); IGRAPH_FINALLY_CLEAN(1); PROTECT(extd_to_orig_eids=R_igraph_0orvector_to_SEXPp1(&c_extd_to_orig_eids)); igraph_vector_destroy(&c_extd_to_orig_eids); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, res); SET_VECTOR_ELT(result, 1, extd_graph); SET_VECTOR_ELT(result, 2, extd_to_orig_eids); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("res")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("extd_graph")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("extd_to_orig_eids")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_mds / /-------------------------------------------*/ SEXP R_igraph_layout_mds(SEXP graph, SEXP dist, SEXP dim) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_matrix_t c_dist; igraph_integer_t c_dim; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); if (!isNull(dist)) { R_SEXP_to_matrix(dist, &c_dist); } c_dim=INTEGER(dim)[0]; /* Call igraph */ igraph_layout_mds(&c_graph, &c_res, (isNull(dist) ? 0 : &c_dist), c_dim, 0); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_layout_bipartite / /-------------------------------------------*/ SEXP R_igraph_layout_bipartite(SEXP graph, SEXP types, SEXP hgap, SEXP vgap, SEXP maxiter) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_matrix_t c_res; igraph_real_t c_hgap; igraph_real_t c_vgap; igraph_integer_t c_maxiter; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); c_hgap=REAL(hgap)[0]; c_vgap=REAL(vgap)[0]; c_maxiter=INTEGER(maxiter)[0]; /* Call igraph */ igraph_layout_bipartite(&c_graph, (isNull(types) ? 0 : &c_types), &c_res, c_hgap, c_vgap, c_maxiter); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_similarity_jaccard / /-------------------------------------------*/ SEXP R_igraph_similarity_jaccard(SEXP graph, SEXP vids, SEXP mode, SEXP loops) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_vs_t c_vids; igraph_neimode_t c_mode; igraph_bool_t c_loops; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_mode=(igraph_neimode_t) REAL(mode)[0]; c_loops=LOGICAL(loops)[0]; /* Call igraph */ igraph_similarity_jaccard(&c_graph, &c_res, c_vids, c_mode, c_loops); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_similarity_dice / /-------------------------------------------*/ SEXP R_igraph_similarity_dice(SEXP graph, SEXP vids, SEXP mode, SEXP loops) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_vs_t c_vids; igraph_neimode_t c_mode; igraph_bool_t c_loops; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_mode=(igraph_neimode_t) REAL(mode)[0]; c_loops=LOGICAL(loops)[0]; /* Call igraph */ igraph_similarity_dice(&c_graph, &c_res, c_vids, c_mode, c_loops); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_similarity_inverse_log_weighted / /-------------------------------------------*/ SEXP R_igraph_similarity_inverse_log_weighted(SEXP graph, SEXP vids, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_vs_t c_vids; igraph_neimode_t c_mode; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_similarity_inverse_log_weighted(&c_graph, &c_res, c_vids, c_mode); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_compare_communities / /-------------------------------------------*/ SEXP R_igraph_compare_communities(SEXP comm1, SEXP comm2, SEXP method) { /* Declarations */ igraph_vector_t c_comm1; igraph_vector_t c_comm2; igraph_real_t c_res; igraph_community_comparison_t c_method; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_vector(comm1, &c_comm1); R_SEXP_to_vector(comm2, &c_comm2); c_method=(igraph_community_comparison_t) REAL(method)[0]; /* Call igraph */ igraph_compare_communities(&c_comm1, &c_comm2, &c_res, c_method); /* Convert output */ PROTECT(res=NEW_NUMERIC(1)); REAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_le_community_to_membership / /-------------------------------------------*/ SEXP R_igraph_le_community_to_membership(SEXP merges, SEXP steps, SEXP membership) { /* Declarations */ igraph_matrix_t c_merges; igraph_integer_t c_steps; igraph_vector_t c_membership; igraph_vector_t c_csize; SEXP csize; SEXP result, names; /* Convert input */ R_SEXP_to_matrix(merges, &c_merges); c_steps=INTEGER(steps)[0]; if (0 != R_SEXP_to_vector_copy(membership, &c_membership)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); if (0 != igraph_vector_init(&c_csize, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_csize); csize=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_le_community_to_membership(&c_merges, c_steps, &c_membership, (isNull(csize) ? 0 : &c_csize)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); PROTECT(csize=R_igraph_0orvector_to_SEXP(&c_csize)); igraph_vector_destroy(&c_csize); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, membership); SET_VECTOR_ELT(result, 1, csize); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("membership")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("csize")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_modularity / /-------------------------------------------*/ SEXP R_igraph_modularity(SEXP graph, SEXP membership, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_membership; igraph_real_t c_modularity; igraph_vector_t c_weights; SEXP modularity; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(membership, &c_membership); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_modularity(&c_graph, &c_membership, &c_modularity, (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(modularity=NEW_NUMERIC(1)); REAL(modularity)[0]=c_modularity; result=modularity; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_modularity_matrix / /-------------------------------------------*/ SEXP R_igraph_modularity_matrix(SEXP graph, SEXP membership, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_membership; igraph_matrix_t c_modmat; igraph_vector_t c_weights; SEXP modmat; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(membership, &c_membership); if (0 != igraph_matrix_init(&c_modmat, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_modmat); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_modularity_matrix(&c_graph, &c_membership, &c_modmat, (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(modmat=R_igraph_matrix_to_SEXP(&c_modmat)); igraph_matrix_destroy(&c_modmat); IGRAPH_FINALLY_CLEAN(1); result=modmat; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_reindex_membership / /-------------------------------------------*/ SEXP R_igraph_reindex_membership(SEXP membership) { /* Declarations */ igraph_vector_t c_membership; igraph_vector_t c_new_to_old; SEXP new_to_old; SEXP result, names; /* Convert input */ if (0 != R_SEXP_to_vector_copy(membership, &c_membership)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); if (0 != igraph_vector_init(&c_new_to_old, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_new_to_old); new_to_old=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_reindex_membership(&c_membership, (isNull(new_to_old) ? 0 : &c_new_to_old)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); PROTECT(new_to_old=R_igraph_0orvector_to_SEXP(&c_new_to_old)); igraph_vector_destroy(&c_new_to_old); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, membership); SET_VECTOR_ELT(result, 1, new_to_old); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("membership")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("new_to_old")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_community_label_propagation / /-------------------------------------------*/ SEXP R_igraph_community_label_propagation(SEXP graph, SEXP weights, SEXP initial, SEXP fixed) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_membership; igraph_vector_t c_weights; igraph_vector_t c_initial; igraph_vector_bool_t c_fixed; igraph_real_t c_modularity; SEXP membership; SEXP modularity; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_membership, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (!isNull(initial)) { R_SEXP_to_vector(initial, &c_initial); } if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } /* Call igraph */ igraph_community_label_propagation(&c_graph, &c_membership, (isNull(weights) ? 0 : &c_weights), (isNull(initial) ? 0 : &c_initial), (isNull(fixed) ? 0 : &c_fixed), &c_modularity); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); PROTECT(modularity=NEW_NUMERIC(1)); REAL(modularity)[0]=c_modularity; SET_VECTOR_ELT(result, 0, membership); SET_VECTOR_ELT(result, 1, modularity); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("membership")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("modularity")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_community_multilevel / /-------------------------------------------*/ SEXP R_igraph_community_multilevel(SEXP graph, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_weights; igraph_vector_t c_membership; igraph_matrix_t c_memberships; igraph_vector_t c_modularity; SEXP membership; SEXP memberships; SEXP modularity; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } if (0 != igraph_vector_init(&c_membership, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); if (0 != igraph_matrix_init(&c_memberships, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_memberships); memberships=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_modularity, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_modularity); modularity=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_community_multilevel(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_membership, (isNull(memberships) ? 0 : &c_memberships), (isNull(modularity) ? 0 : &c_modularity)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); PROTECT(memberships=R_igraph_0ormatrix_to_SEXP(&c_memberships)); igraph_matrix_destroy(&c_memberships); IGRAPH_FINALLY_CLEAN(1); PROTECT(modularity=R_igraph_0orvector_to_SEXP(&c_modularity)); igraph_vector_destroy(&c_modularity); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, membership); SET_VECTOR_ELT(result, 1, memberships); SET_VECTOR_ELT(result, 2, modularity); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("membership")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("memberships")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("modularity")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_community_optimal_modularity / /-------------------------------------------*/ SEXP R_igraph_community_optimal_modularity(SEXP graph, SEXP weights) { /* Declarations */ igraph_t c_graph; igraph_real_t c_modularity; igraph_vector_t c_membership; igraph_vector_t c_weights; SEXP modularity; SEXP membership; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_membership, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); membership=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } /* Call igraph */ igraph_community_optimal_modularity(&c_graph, &c_modularity, (isNull(membership) ? 0 : &c_membership), (isNull(weights) ? 0 : &c_weights)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(modularity=NEW_NUMERIC(1)); REAL(modularity)[0]=c_modularity; PROTECT(membership=R_igraph_0orvector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, modularity); SET_VECTOR_ELT(result, 1, membership); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("modularity")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("membership")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_hrg_fit / /-------------------------------------------*/ SEXP R_igraph_hrg_fit(SEXP graph, SEXP hrg, SEXP start, SEXP steps) { /* Declarations */ igraph_t c_graph; igraph_hrg_t c_hrg; igraph_bool_t c_start; int c_steps; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); c_start=LOGICAL(start)[0]; c_steps=INTEGER(steps)[0]; /* Call igraph */ igraph_hrg_fit(&c_graph, &c_hrg, c_start, c_steps); /* Convert output */ PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); igraph_hrg_destroy(&c_hrg); IGRAPH_FINALLY_CLEAN(1); result=hrg; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_hrg_game / /-------------------------------------------*/ SEXP R_igraph_hrg_game(SEXP hrg) { /* Declarations */ igraph_t c_graph; igraph_hrg_t c_hrg; SEXP graph; SEXP result; /* Convert input */ R_SEXP_to_hrg(hrg, &c_hrg); /* Call igraph */ igraph_hrg_game(&c_graph, &c_hrg); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_hrg_dendrogram / /-------------------------------------------*/ SEXP R_igraph_hrg_dendrogram(SEXP hrg) { /* Declarations */ igraph_t c_graph; igraph_hrg_t c_hrg; SEXP graph; SEXP result; /* Convert input */ R_SEXP_to_hrg(hrg, &c_hrg); /* Call igraph */ igraph_hrg_dendrogram(&c_graph, &c_hrg); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_hrg_consensus / /-------------------------------------------*/ SEXP R_igraph_hrg_consensus(SEXP graph, SEXP hrg, SEXP start, SEXP num_samples) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_parents; igraph_vector_t c_weights; igraph_hrg_t c_hrg; igraph_bool_t c_start; int c_num_samples; SEXP parents; SEXP weights; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_parents, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_parents); if (0 != igraph_vector_init(&c_weights, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_weights); if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); c_start=LOGICAL(start)[0]; c_num_samples=INTEGER(num_samples)[0]; /* Call igraph */ igraph_hrg_consensus(&c_graph, &c_parents, &c_weights, &c_hrg, c_start, c_num_samples); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(parents=R_igraph_vector_to_SEXP(&c_parents)); igraph_vector_destroy(&c_parents); IGRAPH_FINALLY_CLEAN(1); PROTECT(weights=R_igraph_vector_to_SEXP(&c_weights)); igraph_vector_destroy(&c_weights); IGRAPH_FINALLY_CLEAN(1); PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); igraph_hrg_destroy(&c_hrg); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, parents); SET_VECTOR_ELT(result, 1, weights); SET_VECTOR_ELT(result, 2, hrg); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("parents")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("weights")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("hrg")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_hrg_predict / /-------------------------------------------*/ SEXP R_igraph_hrg_predict(SEXP graph, SEXP hrg, SEXP start, SEXP num_samples, SEXP num_bins) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_edges; igraph_vector_t c_prob; igraph_hrg_t c_hrg; igraph_bool_t c_start; int c_num_samples; int c_num_bins; SEXP edges; SEXP prob; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_edges, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_edges); if (0 != igraph_vector_init(&c_prob, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_prob); if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); c_start=LOGICAL(start)[0]; c_num_samples=INTEGER(num_samples)[0]; c_num_bins=INTEGER(num_bins)[0]; /* Call igraph */ igraph_hrg_predict(&c_graph, &c_edges, &c_prob, &c_hrg, c_start, c_num_samples, c_num_bins); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(edges=R_igraph_vector_to_SEXPp1(&c_edges)); igraph_vector_destroy(&c_edges); IGRAPH_FINALLY_CLEAN(1); PROTECT(prob=R_igraph_vector_to_SEXP(&c_prob)); igraph_vector_destroy(&c_prob); IGRAPH_FINALLY_CLEAN(1); PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); igraph_hrg_destroy(&c_hrg); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, edges); SET_VECTOR_ELT(result, 1, prob); SET_VECTOR_ELT(result, 2, hrg); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("edges")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("prob")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("hrg")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_hrg_create / /-------------------------------------------*/ SEXP R_igraph_hrg_create(SEXP graph, SEXP prob) { /* Declarations */ igraph_hrg_t c_hrg; igraph_t c_graph; igraph_vector_t c_prob; SEXP hrg; SEXP result; /* Convert input */ if (0 != igraph_hrg_init(&c_hrg, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(prob, &c_prob); /* Call igraph */ igraph_hrg_create(&c_hrg, &c_graph, &c_prob); /* Convert output */ PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); igraph_hrg_destroy(&c_hrg); IGRAPH_FINALLY_CLEAN(1); result=hrg; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_community_infomap / /-------------------------------------------*/ SEXP R_igraph_community_infomap(SEXP graph, SEXP e_weights, SEXP v_weights, SEXP nb_trials) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_e_weights; igraph_vector_t c_v_weights; int c_nb_trials; igraph_vector_t c_membership; igraph_real_t c_codelength; SEXP membership; SEXP codelength; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(e_weights)) { R_SEXP_to_vector(e_weights, &c_e_weights); } if (!isNull(v_weights)) { R_SEXP_to_vector(v_weights, &c_v_weights); } c_nb_trials=INTEGER(nb_trials)[0]; if (0 != igraph_vector_init(&c_membership, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); /* Call igraph */ igraph_community_infomap(&c_graph, (isNull(e_weights) ? 0 : &c_e_weights), (isNull(v_weights) ? 0 : &c_v_weights), c_nb_trials, &c_membership, &c_codelength); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); igraph_vector_destroy(&c_membership); IGRAPH_FINALLY_CLEAN(1); PROTECT(codelength=NEW_NUMERIC(1)); REAL(codelength)[0]=c_codelength; SET_VECTOR_ELT(result, 0, membership); SET_VECTOR_ELT(result, 1, codelength); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("membership")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("codelength")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_to_undirected / /-------------------------------------------*/ SEXP R_igraph_to_undirected(SEXP graph, SEXP mode, SEXP edge_attr_comb) { /* Declarations */ igraph_t c_graph; igraph_to_undirected_t c_mode; igraph_attribute_combination_t c_edge_attr_comb; SEXP result; /* Convert input */ R_SEXP_to_igraph_copy(graph, &c_graph); IGRAPH_FINALLY(igraph_destroy, &c_graph); c_mode=(igraph_to_undirected_t) REAL(mode)[0]; R_SEXP_to_attr_comb(edge_attr_comb, &c_edge_attr_comb); /* Call igraph */ igraph_to_undirected(&c_graph, c_mode, &c_edge_attr_comb); /* Convert output */ PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); igraph_attribute_combination_destroy(&c_edge_attr_comb); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_get_stochastic / /-------------------------------------------*/ SEXP R_igraph_get_stochastic(SEXP graph, SEXP column_wise) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_res; igraph_bool_t c_column_wise; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_matrix_init(&c_res, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); c_column_wise=LOGICAL(column_wise)[0]; /* Call igraph */ igraph_get_stochastic(&c_graph, &c_res, c_column_wise); /* Convert output */ PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); igraph_matrix_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_get_stochastic_sparsemat / /-------------------------------------------*/ SEXP R_igraph_get_stochastic_sparsemat(SEXP graph, SEXP column_wise) { /* Declarations */ igraph_t c_graph; igraph_sparsemat_t c_sparsemat; igraph_bool_t c_column_wise; SEXP sparsemat; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); /* Don't need to init. */ c_column_wise=LOGICAL(column_wise)[0]; /* Call igraph */ igraph_get_stochastic_sparsemat(&c_graph, &c_sparsemat, c_column_wise); /* Convert output */ PROTECT(sparsemat=R_igraph_sparsemat_to_SEXP(&c_sparsemat)); igraph_sparsemat_destroy(&c_sparsemat); IGRAPH_FINALLY_CLEAN(1); result=sparsemat; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_dyad_census / /-------------------------------------------*/ SEXP R_igraph_dyad_census(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_mut; igraph_integer_t c_asym; igraph_integer_t c_null; SEXP mut; SEXP asym; SEXP null; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); /* Call igraph */ igraph_dyad_census(&c_graph, &c_mut, &c_asym, &c_null); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(mut=NEW_INTEGER(1)); INTEGER(mut)[0]=c_mut; PROTECT(asym=NEW_INTEGER(1)); INTEGER(asym)[0]=c_asym; PROTECT(null=NEW_INTEGER(1)); INTEGER(null)[0]=c_null; SET_VECTOR_ELT(result, 0, mut); SET_VECTOR_ELT(result, 1, asym); SET_VECTOR_ELT(result, 2, null); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("mut")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("asym")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("null")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_triad_census / /-------------------------------------------*/ SEXP R_igraph_triad_census(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); /* Call igraph */ igraph_triad_census(&c_graph, &c_res); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_adjacent_triangles / /-------------------------------------------*/ SEXP R_igraph_adjacent_triangles(SEXP graph, SEXP vids) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_res; igraph_vs_t c_vids; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_res); R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); /* Call igraph */ igraph_adjacent_triangles(&c_graph, &c_res, c_vids); /* Convert output */ PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); igraph_vector_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); igraph_vs_destroy(&c_vids); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_maxflow / /-------------------------------------------*/ SEXP R_igraph_maxflow(SEXP graph, SEXP source, SEXP target, SEXP capacity) { /* Declarations */ igraph_t c_graph; igraph_real_t c_value; igraph_vector_t c_flow; igraph_vector_t c_cut; igraph_vector_t c_partition1; igraph_vector_t c_partition2; igraph_integer_t c_source; igraph_integer_t c_target; igraph_vector_t c_capacity; igraph_maxflow_stats_t c_stats; SEXP value; SEXP flow; SEXP cut; SEXP partition1; SEXP partition2; SEXP stats; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_flow, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_flow); flow=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_cut, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_cut); cut=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_partition1, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_partition1); partition1=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_partition2, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_partition2); partition2=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_source=(igraph_integer_t) REAL(source)[0]; c_target=(igraph_integer_t) REAL(target)[0]; if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } /* Call igraph */ igraph_maxflow(&c_graph, &c_value, (isNull(flow) ? 0 : &c_flow), (isNull(cut) ? 0 : &c_cut), (isNull(partition1) ? 0 : &c_partition1), (isNull(partition2) ? 0 : &c_partition2), c_source, c_target, (isNull(capacity) ? 0 : &c_capacity), &c_stats); /* Convert output */ PROTECT(result=NEW_LIST(6)); PROTECT(names=NEW_CHARACTER(6)); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(flow=R_igraph_0orvector_to_SEXP(&c_flow)); igraph_vector_destroy(&c_flow); IGRAPH_FINALLY_CLEAN(1); PROTECT(cut=R_igraph_0orvector_to_SEXPp1(&c_cut)); igraph_vector_destroy(&c_cut); IGRAPH_FINALLY_CLEAN(1); PROTECT(partition1=R_igraph_0orvector_to_SEXPp1(&c_partition1)); igraph_vector_destroy(&c_partition1); IGRAPH_FINALLY_CLEAN(1); PROTECT(partition2=R_igraph_0orvector_to_SEXPp1(&c_partition2)); igraph_vector_destroy(&c_partition2); IGRAPH_FINALLY_CLEAN(1); PROTECT(stats=R_igraph_maxflow_stats_to_SEXP(&c_stats)); SET_VECTOR_ELT(result, 0, value); SET_VECTOR_ELT(result, 1, flow); SET_VECTOR_ELT(result, 2, cut); SET_VECTOR_ELT(result, 3, partition1); SET_VECTOR_ELT(result, 4, partition2); SET_VECTOR_ELT(result, 5, stats); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("flow")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("cut")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("partition1")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("partition2")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("stats")); SET_NAMES(result, names); UNPROTECT(7); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_dominator_tree / /-------------------------------------------*/ SEXP R_igraph_dominator_tree(SEXP graph, SEXP root, SEXP mode) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_root; igraph_vector_t c_dom; igraph_t c_domtree; igraph_vector_t c_leftout; igraph_neimode_t c_mode; SEXP dom; SEXP domtree; SEXP leftout; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_root=(igraph_integer_t) REAL(root)[0]; if (0 != igraph_vector_init(&c_dom, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_dom); domtree=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_leftout, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_leftout); c_mode=(igraph_neimode_t) REAL(mode)[0]; /* Call igraph */ igraph_dominator_tree(&c_graph, c_root, &c_dom, (isNull(domtree) ? 0 : &c_domtree), &c_leftout, c_mode); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(dom=R_igraph_vector_to_SEXPp1(&c_dom)); igraph_vector_destroy(&c_dom); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &c_domtree); PROTECT(domtree=R_igraph_to_SEXP(&c_domtree)); igraph_destroy(&c_domtree); IGRAPH_FINALLY_CLEAN(1); PROTECT(leftout=R_igraph_vector_to_SEXPp1(&c_leftout)); igraph_vector_destroy(&c_leftout); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, dom); SET_VECTOR_ELT(result, 1, domtree); SET_VECTOR_ELT(result, 2, leftout); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("dom")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("domtree")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("leftout")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_all_st_cuts / /-------------------------------------------*/ SEXP R_igraph_all_st_cuts(SEXP graph, SEXP source, SEXP target) { /* Declarations */ igraph_t c_graph; igraph_vector_ptr_t c_cuts; igraph_vector_ptr_t c_partition1s; igraph_integer_t c_source; igraph_integer_t c_target; SEXP cuts; SEXP partition1s; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_cuts, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cuts); if (0 != igraph_vector_ptr_init(&c_partition1s, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_partition1s); c_source=(igraph_integer_t) REAL(source)[0]; c_target=(igraph_integer_t) REAL(target)[0]; /* Call igraph */ igraph_all_st_cuts(&c_graph, &c_cuts, &c_partition1s, c_source, c_target); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(cuts=R_igraph_vectorlist_to_SEXP_p1(&c_cuts)); R_igraph_vectorlist_destroy(&c_cuts); IGRAPH_FINALLY_CLEAN(1); PROTECT(partition1s=R_igraph_vectorlist_to_SEXP_p1(&c_partition1s)); R_igraph_vectorlist_destroy(&c_partition1s); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, cuts); SET_VECTOR_ELT(result, 1, partition1s); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("cuts")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("partition1s")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_all_st_mincuts / /-------------------------------------------*/ SEXP R_igraph_all_st_mincuts(SEXP graph, SEXP source, SEXP target, SEXP capacity) { /* Declarations */ igraph_t c_graph; igraph_real_t c_value; igraph_vector_ptr_t c_cuts; igraph_vector_ptr_t c_partition1s; igraph_integer_t c_source; igraph_integer_t c_target; igraph_vector_t c_capacity; SEXP value; SEXP cuts; SEXP partition1s; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_cuts, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cuts); if (0 != igraph_vector_ptr_init(&c_partition1s, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_partition1s); c_source=(igraph_integer_t) REAL(source)[0]; c_target=(igraph_integer_t) REAL(target)[0]; if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } /* Call igraph */ igraph_all_st_mincuts(&c_graph, &c_value, &c_cuts, &c_partition1s, c_source, c_target, (isNull(capacity) ? 0 : &c_capacity)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(cuts=R_igraph_vectorlist_to_SEXP_p1(&c_cuts)); R_igraph_vectorlist_destroy(&c_cuts); IGRAPH_FINALLY_CLEAN(1); PROTECT(partition1s=R_igraph_vectorlist_to_SEXP_p1(&c_partition1s)); R_igraph_vectorlist_destroy(&c_partition1s); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, value); SET_VECTOR_ELT(result, 1, cuts); SET_VECTOR_ELT(result, 2, partition1s); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cuts")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("partition1s")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_separator / /-------------------------------------------*/ SEXP R_igraph_is_separator(SEXP graph, SEXP candidate) { /* Declarations */ igraph_t c_graph; igraph_vs_t c_candidate; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_igraph_vs(candidate, &c_graph, &c_candidate); /* Call igraph */ igraph_is_separator(&c_graph, c_candidate, &c_res); /* Convert output */ igraph_vs_destroy(&c_candidate); PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_minimal_separator / /-------------------------------------------*/ SEXP R_igraph_is_minimal_separator(SEXP graph, SEXP candidate) { /* Declarations */ igraph_t c_graph; igraph_vs_t c_candidate; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_igraph_vs(candidate, &c_graph, &c_candidate); /* Call igraph */ igraph_is_minimal_separator(&c_graph, c_candidate, &c_res); /* Convert output */ igraph_vs_destroy(&c_candidate); PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_all_minimal_st_separators / /-------------------------------------------*/ SEXP R_igraph_all_minimal_st_separators(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_vector_ptr_t c_separators; SEXP separators; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_separators, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_separators); /* Call igraph */ igraph_all_minimal_st_separators(&c_graph, &c_separators); /* Convert output */ PROTECT(separators=R_igraph_vectorlist_to_SEXP_p1(&c_separators)); R_igraph_vectorlist_destroy(&c_separators); IGRAPH_FINALLY_CLEAN(1); result=separators; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_minimum_size_separators / /-------------------------------------------*/ SEXP R_igraph_minimum_size_separators(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_vector_ptr_t c_separators; SEXP separators; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_ptr_init(&c_separators, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_separators); /* Call igraph */ igraph_minimum_size_separators(&c_graph, &c_separators); /* Convert output */ PROTECT(separators=R_igraph_vectorlist_to_SEXP_p1(&c_separators)); R_igraph_vectorlist_destroy(&c_separators); IGRAPH_FINALLY_CLEAN(1); result=separators; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_isoclass / /-------------------------------------------*/ SEXP R_igraph_isoclass(SEXP graph) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_isoclass; SEXP isoclass; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); /* Call igraph */ igraph_isoclass(&c_graph, &c_isoclass); /* Convert output */ PROTECT(isoclass=NEW_INTEGER(1)); INTEGER(isoclass)[0]=c_isoclass; result=isoclass; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_isomorphic / /-------------------------------------------*/ SEXP R_igraph_isomorphic(SEXP graph1, SEXP graph2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_bool_t c_iso; SEXP iso; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); /* Call igraph */ igraph_isomorphic(&c_graph1, &c_graph2, &c_iso); /* Convert output */ PROTECT(iso=NEW_LOGICAL(1)); LOGICAL(iso)[0]=c_iso; result=iso; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_isoclass_subgraph / /-------------------------------------------*/ SEXP R_igraph_isoclass_subgraph(SEXP graph, SEXP vids) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_vids; igraph_integer_t c_isoclass; SEXP isoclass; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(vids, &c_vids); /* Call igraph */ igraph_isoclass_subgraph(&c_graph, &c_vids, &c_isoclass); /* Convert output */ PROTECT(isoclass=NEW_INTEGER(1)); INTEGER(isoclass)[0]=c_isoclass; result=isoclass; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_isoclass_create / /-------------------------------------------*/ SEXP R_igraph_isoclass_create(SEXP size, SEXP number, SEXP directed) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_size; igraph_integer_t c_number; igraph_bool_t c_directed; SEXP graph; SEXP result; /* Convert input */ c_size=INTEGER(size)[0]; c_number=INTEGER(number)[0]; c_directed=LOGICAL(directed)[0]; /* Call igraph */ igraph_isoclass_create(&c_graph, c_size, c_number, c_directed); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_graph); PROTECT(graph=R_igraph_to_SEXP(&c_graph)); igraph_destroy(&c_graph); IGRAPH_FINALLY_CLEAN(1); result=graph; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_isomorphic_vf2 / /-------------------------------------------*/ SEXP R_igraph_isomorphic_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_vector_int_t c_vertex_color1; igraph_vector_int_t c_vertex_color2; igraph_vector_int_t c_edge_color1; igraph_vector_int_t c_edge_color2; igraph_bool_t c_iso; igraph_vector_t c_map12; igraph_vector_t c_map21; SEXP iso; SEXP map12; SEXP map21; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } if (0 != igraph_vector_init(&c_map12, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); map12=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_map21, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); map21=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_isomorphic_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), 0, 0, 0); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(iso=NEW_LOGICAL(1)); LOGICAL(iso)[0]=c_iso; PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); igraph_vector_destroy(&c_map12); IGRAPH_FINALLY_CLEAN(1); PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); igraph_vector_destroy(&c_map21); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, iso); SET_VECTOR_ELT(result, 1, map12); SET_VECTOR_ELT(result, 2, map21); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("iso")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("map12")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("map21")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_count_isomorphisms_vf2 / /-------------------------------------------*/ SEXP R_igraph_count_isomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_vector_int_t c_vertex_color1; igraph_vector_int_t c_vertex_color2; igraph_vector_int_t c_edge_color1; igraph_vector_int_t c_edge_color2; igraph_integer_t c_count; SEXP count; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } /* Call igraph */ igraph_count_isomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_count, 0, 0, 0); /* Convert output */ PROTECT(count=NEW_INTEGER(1)); INTEGER(count)[0]=c_count; result=count; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_get_isomorphisms_vf2 / /-------------------------------------------*/ SEXP R_igraph_get_isomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_vector_int_t c_vertex_color1; igraph_vector_int_t c_vertex_color2; igraph_vector_int_t c_edge_color1; igraph_vector_int_t c_edge_color2; igraph_vector_ptr_t c_maps; SEXP maps; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } if (0 != igraph_vector_ptr_init(&c_maps, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); /* Call igraph */ igraph_get_isomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_maps, 0, 0, 0); /* Convert output */ PROTECT(maps=R_igraph_vectorlist_to_SEXP(&c_maps)); R_igraph_vectorlist_destroy(&c_maps); IGRAPH_FINALLY_CLEAN(1); result=maps; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_subisomorphic_vf2 / /-------------------------------------------*/ SEXP R_igraph_subisomorphic_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_vector_int_t c_vertex_color1; igraph_vector_int_t c_vertex_color2; igraph_vector_int_t c_edge_color1; igraph_vector_int_t c_edge_color2; igraph_bool_t c_iso; igraph_vector_t c_map12; igraph_vector_t c_map21; SEXP iso; SEXP map12; SEXP map21; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } if (0 != igraph_vector_init(&c_map12, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); map12=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_map21, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); map21=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_subisomorphic_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), 0, 0, 0); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(iso=NEW_LOGICAL(1)); LOGICAL(iso)[0]=c_iso; PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); igraph_vector_destroy(&c_map12); IGRAPH_FINALLY_CLEAN(1); PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); igraph_vector_destroy(&c_map21); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, iso); SET_VECTOR_ELT(result, 1, map12); SET_VECTOR_ELT(result, 2, map21); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("iso")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("map12")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("map21")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_count_subisomorphisms_vf2 / /-------------------------------------------*/ SEXP R_igraph_count_subisomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_vector_int_t c_vertex_color1; igraph_vector_int_t c_vertex_color2; igraph_vector_int_t c_edge_color1; igraph_vector_int_t c_edge_color2; igraph_integer_t c_count; SEXP count; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } /* Call igraph */ igraph_count_subisomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_count, 0, 0, 0); /* Convert output */ PROTECT(count=NEW_INTEGER(1)); INTEGER(count)[0]=c_count; result=count; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_get_subisomorphisms_vf2 / /-------------------------------------------*/ SEXP R_igraph_get_subisomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_vector_int_t c_vertex_color1; igraph_vector_int_t c_vertex_color2; igraph_vector_int_t c_edge_color1; igraph_vector_int_t c_edge_color2; igraph_vector_ptr_t c_maps; SEXP maps; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } if (0 != igraph_vector_ptr_init(&c_maps, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); /* Call igraph */ igraph_get_subisomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_maps, 0, 0, 0); /* Convert output */ PROTECT(maps=R_igraph_vectorlist_to_SEXP(&c_maps)); R_igraph_vectorlist_destroy(&c_maps); IGRAPH_FINALLY_CLEAN(1); result=maps; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_isomorphic_34 / /-------------------------------------------*/ SEXP R_igraph_isomorphic_34(SEXP graph1, SEXP graph2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_bool_t c_iso; SEXP iso; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); /* Call igraph */ igraph_isomorphic_34(&c_graph1, &c_graph2, &c_iso); /* Convert output */ PROTECT(iso=NEW_LOGICAL(1)); LOGICAL(iso)[0]=c_iso; result=iso; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_canonical_permutation / /-------------------------------------------*/ SEXP R_igraph_canonical_permutation(SEXP graph, SEXP sh) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_labeling; igraph_bliss_sh_t c_sh; igraph_bliss_info_t c_info; SEXP labeling; SEXP info; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (0 != igraph_vector_init(&c_labeling, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_labeling); c_sh=REAL(sh)[0]; /* Call igraph */ igraph_canonical_permutation(&c_graph, &c_labeling, c_sh, &c_info); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(labeling=R_igraph_vector_to_SEXPp1(&c_labeling)); igraph_vector_destroy(&c_labeling); IGRAPH_FINALLY_CLEAN(1); PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); if (c_info.group_size) { free(c_info.group_size); } SET_VECTOR_ELT(result, 0, labeling); SET_VECTOR_ELT(result, 1, info); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("labeling")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("info")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_permute_vertices / /-------------------------------------------*/ SEXP R_igraph_permute_vertices(SEXP graph, SEXP permutation) { /* Declarations */ igraph_t c_graph; igraph_t c_res; igraph_vector_t c_permutation; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(permutation, &c_permutation); /* Call igraph */ igraph_permute_vertices(&c_graph, &c_res, &c_permutation); /* Convert output */ IGRAPH_FINALLY(igraph_destroy, &c_res); PROTECT(res=R_igraph_to_SEXP(&c_res)); igraph_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_isomorphic_bliss / /-------------------------------------------*/ SEXP R_igraph_isomorphic_bliss(SEXP graph1, SEXP graph2, SEXP sh1, SEXP sh2) { /* Declarations */ igraph_t c_graph1; igraph_t c_graph2; igraph_bool_t c_iso; igraph_vector_t c_map12; igraph_vector_t c_map21; igraph_bliss_sh_t c_sh1; igraph_bliss_sh_t c_sh2; igraph_bliss_info_t c_info1; igraph_bliss_info_t c_info2; SEXP iso; SEXP map12; SEXP map21; SEXP info1; SEXP info2; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph1, &c_graph1); R_SEXP_to_igraph(graph2, &c_graph2); if (0 != igraph_vector_init(&c_map12, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); map12=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_map21, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); map21=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_sh1=REAL(sh1)[0]; c_sh2=REAL(sh2)[0]; /* Call igraph */ igraph_isomorphic_bliss(&c_graph1, &c_graph2, &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), c_sh1, c_sh2, &c_info1, &c_info2); /* Convert output */ PROTECT(result=NEW_LIST(5)); PROTECT(names=NEW_CHARACTER(5)); PROTECT(iso=NEW_LOGICAL(1)); LOGICAL(iso)[0]=c_iso; PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); igraph_vector_destroy(&c_map12); IGRAPH_FINALLY_CLEAN(1); PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); igraph_vector_destroy(&c_map21); IGRAPH_FINALLY_CLEAN(1); PROTECT(info1=R_igraph_bliss_info_to_SEXP(&c_info1)); if (c_info1.group_size) { free(c_info1.group_size); } PROTECT(info2=R_igraph_bliss_info_to_SEXP(&c_info2)); if (c_info2.group_size) { free(c_info2.group_size); } SET_VECTOR_ELT(result, 0, iso); SET_VECTOR_ELT(result, 1, map12); SET_VECTOR_ELT(result, 2, map21); SET_VECTOR_ELT(result, 3, info1); SET_VECTOR_ELT(result, 4, info2); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("iso")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("map12")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("map21")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("info1")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("info2")); SET_NAMES(result, names); UNPROTECT(6); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_automorphisms / /-------------------------------------------*/ SEXP R_igraph_automorphisms(SEXP graph, SEXP sh) { /* Declarations */ igraph_t c_graph; igraph_bliss_sh_t c_sh; igraph_bliss_info_t c_info; SEXP info; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_sh=REAL(sh)[0]; /* Call igraph */ igraph_automorphisms(&c_graph, c_sh, &c_info); /* Convert output */ PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); if (c_info.group_size) { free(c_info.group_size); } result=info; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_scg_grouping / /-------------------------------------------*/ SEXP R_igraph_scg_grouping(SEXP V, SEXP nt, SEXP nt_vec, SEXP mtype, SEXP algo, SEXP p, SEXP maxiter) { /* Declarations */ igraph_matrix_t c_V; igraph_vector_t c_groups; igraph_integer_t c_nt; igraph_vector_t c_nt_vec; igraph_scg_matrix_t c_mtype; igraph_scg_algorithm_t c_algo; igraph_vector_t c_p; igraph_integer_t c_maxiter; SEXP groups; SEXP result; /* Convert input */ R_SEXP_to_matrix(V, &c_V); if (0 != igraph_vector_init(&c_groups, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_groups); c_nt=INTEGER(nt)[0]; if (!isNull(nt_vec)) { R_SEXP_to_vector(nt_vec, &c_nt_vec); } c_mtype=(igraph_scg_matrix_t) REAL(mtype)[0]; c_algo=(igraph_scg_algorithm_t) REAL(algo)[0]; if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } c_maxiter=INTEGER(maxiter)[0]; /* Call igraph */ igraph_scg_grouping(&c_V, &c_groups, c_nt, (isNull(nt_vec) ? 0 : &c_nt_vec), c_mtype, c_algo, (isNull(p) ? 0 : &c_p), c_maxiter); /* Convert output */ PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); igraph_vector_destroy(&c_groups); IGRAPH_FINALLY_CLEAN(1); result=groups; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_scg_norm_eps / /-------------------------------------------*/ SEXP R_igraph_scg_norm_eps(SEXP V, SEXP groups, SEXP mtype, SEXP p, SEXP norm) { /* Declarations */ igraph_matrix_t c_V; igraph_vector_t c_groups; igraph_vector_t c_eps; igraph_scg_matrix_t c_mtype; igraph_vector_t c_p; igraph_scg_norm_t c_norm; SEXP eps; SEXP result; /* Convert input */ R_SEXP_to_matrix(V, &c_V); R_SEXP_to_vector(groups, &c_groups); if (0 != igraph_vector_init(&c_eps, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_eps); c_mtype=(igraph_scg_matrix_t) REAL(mtype)[0]; if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } c_norm=(igraph_scg_norm_t) REAL(norm)[0]; /* Call igraph */ igraph_scg_norm_eps(&c_V, &c_groups, &c_eps, c_mtype, (isNull(p) ? 0 : &c_p), c_norm); /* Convert output */ PROTECT(eps=R_igraph_vector_to_SEXP(&c_eps)); igraph_vector_destroy(&c_eps); IGRAPH_FINALLY_CLEAN(1); result=eps; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_matching / /-------------------------------------------*/ SEXP R_igraph_is_matching(SEXP graph, SEXP types, SEXP matching) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_vector_long_t c_matching; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } R_SEXP_to_vector_long_copy(matching, &c_matching); /* Call igraph */ igraph_is_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching, &c_res); /* Convert output */ igraph_vector_long_destroy(&c_matching); PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_is_maximal_matching / /-------------------------------------------*/ SEXP R_igraph_is_maximal_matching(SEXP graph, SEXP types, SEXP matching) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_vector_long_t c_matching; igraph_bool_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } R_SEXP_to_vector_long_copy(matching, &c_matching); /* Call igraph */ igraph_is_maximal_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching, &c_res); /* Convert output */ igraph_vector_long_destroy(&c_matching); PROTECT(res=NEW_LOGICAL(1)); LOGICAL(res)[0]=c_res; result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_maximum_bipartite_matching / /-------------------------------------------*/ SEXP R_igraph_maximum_bipartite_matching(SEXP graph, SEXP types, SEXP weights, SEXP eps) { /* Declarations */ igraph_t c_graph; igraph_vector_bool_t c_types; igraph_integer_t c_matching_size; igraph_real_t c_matching_weight; igraph_vector_long_t c_matching; igraph_vector_t c_weights; igraph_real_t c_eps; SEXP matching_size; SEXP matching_weight; SEXP matching; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } if (0 != igraph_vector_long_init(&c_matching, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_long_destroy, &c_matching); if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } c_eps=REAL(eps)[0]; /* Call igraph */ igraph_maximum_bipartite_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching_size, &c_matching_weight, &c_matching, (isNull(weights) ? 0 : &c_weights), c_eps); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(matching_size=NEW_INTEGER(1)); INTEGER(matching_size)[0]=c_matching_size; PROTECT(matching_weight=NEW_NUMERIC(1)); REAL(matching_weight)[0]=c_matching_weight; PROTECT(matching=R_igraph_vector_long_to_SEXPp1(&c_matching)); igraph_vector_long_destroy(&c_matching); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, matching_size); SET_VECTOR_ELT(result, 1, matching_weight); SET_VECTOR_ELT(result, 2, matching); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("matching_size")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("matching_weight")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("matching")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_eigen_adjacency / /-------------------------------------------*/ SEXP R_igraph_eigen_adjacency(SEXP graph, SEXP algorithm, SEXP which, SEXP options) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_algorithm; igraph_eigen_which_t c_which; igraph_arpack_options_t c_options; igraph_vector_t c_values; igraph_matrix_t c_vectors; SEXP values; SEXP vectors; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_algorithm=REAL(algorithm)[0]; R_SEXP_to_igraph_eigen_which(which, &c_which); R_SEXP_to_igraph_arpack_options(options, &c_options); if (0 != igraph_vector_init(&c_values, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_values); if (0 != igraph_matrix_init(&c_vectors, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_vectors); /* Call igraph */ igraph_eigen_adjacency(&c_graph, c_algorithm, &c_which, &c_options, 0, &c_values, &c_vectors, 0, 0); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); PROTECT(values=R_igraph_vector_to_SEXP(&c_values)); igraph_vector_destroy(&c_values); IGRAPH_FINALLY_CLEAN(1); PROTECT(vectors=R_igraph_matrix_to_SEXP(&c_vectors)); igraph_matrix_destroy(&c_vectors); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, options); SET_VECTOR_ELT(result, 1, values); SET_VECTOR_ELT(result, 2, vectors); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("options")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("values")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("vectors")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_power_law_fit / /-------------------------------------------*/ SEXP R_igraph_power_law_fit(SEXP data, SEXP xmin, SEXP force_continuous) { /* Declarations */ igraph_vector_t c_data; igraph_plfit_result_t c_res; igraph_real_t c_xmin; igraph_bool_t c_force_continuous; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_vector(data, &c_data); c_xmin=REAL(xmin)[0]; c_force_continuous=LOGICAL(force_continuous)[0]; /* Call igraph */ igraph_power_law_fit(&c_data, &c_res, c_xmin, c_force_continuous); /* Convert output */ PROTECT(res=R_igraph_plfit_result_to_SEXP(&c_res)); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_sir / /-------------------------------------------*/ SEXP R_igraph_sir(SEXP graph, SEXP beta, SEXP gamma, SEXP no_sim) { /* Declarations */ igraph_t c_graph; igraph_real_t c_beta; igraph_real_t c_gamma; igraph_integer_t c_no_sim; igraph_vector_ptr_t c_res; SEXP res; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_beta=REAL(beta)[0]; c_gamma=REAL(gamma)[0]; c_no_sim=INTEGER(no_sim)[0]; if (0 != igraph_vector_ptr_init(&c_res, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(R_igraph_sirlist_destroy, &c_res); /* Call igraph */ igraph_sir(&c_graph, c_beta, c_gamma, c_no_sim, &c_res); /* Convert output */ PROTECT(res=R_igraph_sirlist_to_SEXP(&c_res)); R_igraph_sirlist_destroy(&c_res); IGRAPH_FINALLY_CLEAN(1); result=res; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_convex_hull / /-------------------------------------------*/ SEXP R_igraph_convex_hull(SEXP data) { /* Declarations */ igraph_matrix_t c_data; igraph_vector_t c_resverts; igraph_matrix_t c_rescoords; SEXP resverts; SEXP rescoords; SEXP result, names; /* Convert input */ R_SEXP_to_matrix(data, &c_data); if (0 != igraph_vector_init(&c_resverts, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_resverts); if (0 != igraph_matrix_init(&c_rescoords, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_rescoords); /* Call igraph */ igraph_convex_hull(&c_data, &c_resverts, &c_rescoords); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(resverts=R_igraph_vector_to_SEXP(&c_resverts)); igraph_vector_destroy(&c_resverts); IGRAPH_FINALLY_CLEAN(1); PROTECT(rescoords=R_igraph_matrix_to_SEXP(&c_rescoords)); igraph_matrix_destroy(&c_rescoords); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, resverts); SET_VECTOR_ELT(result, 1, rescoords); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("resverts")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("rescoords")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_d / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_d(SEXP graph, SEXP niter, SEXP delta, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_niter; igraph_vector_t c_kernel; igraph_vector_t c_cites; igraph_real_t c_delta; igraph_vector_t c_filter; igraph_real_t c_logprob; igraph_real_t c_logmax; SEXP kernel; SEXP cites; SEXP logprob; SEXP logmax; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_niter=INTEGER(niter)[0]; if (0 != igraph_vector_init(&c_kernel, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_kernel); if (0 != igraph_vector_init(&c_cites, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_cites); cites=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_delta=REAL(delta)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_d(&c_graph, c_niter, &c_kernel, (isNull(cites) ? 0 : &c_cites), c_delta, (isNull(filter) ? 0 : &c_filter), &c_logprob, &c_logmax); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(kernel=R_igraph_vector_to_SEXP(&c_kernel)); igraph_vector_destroy(&c_kernel); IGRAPH_FINALLY_CLEAN(1); PROTECT(cites=R_igraph_0orvector_to_SEXP(&c_cites)); igraph_vector_destroy(&c_cites); IGRAPH_FINALLY_CLEAN(1); PROTECT(logprob=NEW_NUMERIC(1)); REAL(logprob)[0]=c_logprob; PROTECT(logmax=NEW_NUMERIC(1)); REAL(logmax)[0]=c_logmax; SET_VECTOR_ELT(result, 0, kernel); SET_VECTOR_ELT(result, 1, cites); SET_VECTOR_ELT(result, 2, logprob); SET_VECTOR_ELT(result, 3, logmax); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logprob")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("logmax")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_probs_d / /-------------------------------------------*/ SEXP R_igraph_revolver_probs_d(SEXP graph, SEXP kernel, SEXP ntk) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_kernel; igraph_vector_t c_probs; igraph_vector_t c_citedprobs; igraph_vector_t c_citingprobs; igraph_bool_t c_ntk; SEXP probs; SEXP citedprobs; SEXP citingprobs; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(kernel, &c_kernel); if (0 != igraph_vector_init(&c_probs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_probs); probs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_citedprobs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_citedprobs); citedprobs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_citingprobs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_citingprobs); citingprobs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_ntk=LOGICAL(ntk)[0]; /* Call igraph */ igraph_revolver_probs_d(&c_graph, &c_kernel, (isNull(probs) ? 0 : &c_probs), (isNull(citedprobs) ? 0 : &c_citedprobs), (isNull(citingprobs) ? 0 : &c_citingprobs), c_ntk); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(probs=R_igraph_0orvector_to_SEXP(&c_probs)); igraph_vector_destroy(&c_probs); IGRAPH_FINALLY_CLEAN(1); PROTECT(citedprobs=R_igraph_0orvector_to_SEXP(&c_citedprobs)); igraph_vector_destroy(&c_citedprobs); IGRAPH_FINALLY_CLEAN(1); PROTECT(citingprobs=R_igraph_0orvector_to_SEXP(&c_citingprobs)); igraph_vector_destroy(&c_citingprobs); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, probs); SET_VECTOR_ELT(result, 1, citedprobs); SET_VECTOR_ELT(result, 2, citingprobs); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("probs")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("citedprobs")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("citingprobs")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_de / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_de(SEXP graph, SEXP niter, SEXP cats, SEXP delta, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_niter; igraph_matrix_t c_kernel; igraph_vector_t c_cats; igraph_matrix_t c_cites; igraph_real_t c_delta; igraph_vector_t c_filter; igraph_real_t c_logprob; igraph_real_t c_logmax; SEXP kernel; SEXP cites; SEXP logprob; SEXP logmax; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_niter=INTEGER(niter)[0]; if (0 != igraph_matrix_init(&c_kernel, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_kernel); R_SEXP_to_vector(cats, &c_cats); if (0 != igraph_matrix_init(&c_cites, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_cites); cites=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_delta=REAL(delta)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_de(&c_graph, c_niter, &c_kernel, &c_cats, (isNull(cites) ? 0 : &c_cites), c_delta, (isNull(filter) ? 0 : &c_filter), &c_logprob, &c_logmax); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(kernel=R_igraph_matrix_to_SEXP(&c_kernel)); igraph_matrix_destroy(&c_kernel); IGRAPH_FINALLY_CLEAN(1); PROTECT(cites=R_igraph_0ormatrix_to_SEXP(&c_cites)); igraph_matrix_destroy(&c_cites); IGRAPH_FINALLY_CLEAN(1); PROTECT(logprob=NEW_NUMERIC(1)); REAL(logprob)[0]=c_logprob; PROTECT(logmax=NEW_NUMERIC(1)); REAL(logmax)[0]=c_logmax; SET_VECTOR_ELT(result, 0, kernel); SET_VECTOR_ELT(result, 1, cites); SET_VECTOR_ELT(result, 2, logprob); SET_VECTOR_ELT(result, 3, logmax); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logprob")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("logmax")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_probs_de / /-------------------------------------------*/ SEXP R_igraph_revolver_probs_de(SEXP graph, SEXP kernel, SEXP cats) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_kernel; igraph_vector_t c_cats; igraph_vector_t c_logprobs; igraph_vector_t c_logcited; igraph_vector_t c_logciting; SEXP logprobs; SEXP logcited; SEXP logciting; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_matrix(kernel, &c_kernel); R_SEXP_to_vector(cats, &c_cats); if (0 != igraph_vector_init(&c_logprobs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logprobs); logprobs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_logcited, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logcited); logcited=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_logciting, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logciting); logciting=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_revolver_probs_de(&c_graph, &c_kernel, &c_cats, (isNull(logprobs) ? 0 : &c_logprobs), (isNull(logcited) ? 0 : &c_logcited), (isNull(logciting) ? 0 : &c_logciting)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(logprobs=R_igraph_0orvector_to_SEXP(&c_logprobs)); igraph_vector_destroy(&c_logprobs); IGRAPH_FINALLY_CLEAN(1); PROTECT(logcited=R_igraph_0orvector_to_SEXP(&c_logcited)); igraph_vector_destroy(&c_logcited); IGRAPH_FINALLY_CLEAN(1); PROTECT(logciting=R_igraph_0orvector_to_SEXP(&c_logciting)); igraph_vector_destroy(&c_logciting); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, logprobs); SET_VECTOR_ELT(result, 1, logcited); SET_VECTOR_ELT(result, 2, logciting); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("logprobs")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("logcited")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logciting")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_ade / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_ade(SEXP graph, SEXP niter, SEXP cats, SEXP agebins, SEXP delta, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_niter; igraph_array3_t c_kernel; igraph_vector_t c_cats; igraph_array3_t c_cites; igraph_integer_t c_agebins; igraph_real_t c_delta; igraph_vector_t c_filter; igraph_real_t c_logprob; igraph_real_t c_logmax; SEXP kernel; SEXP cites; SEXP logprob; SEXP logmax; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_niter=INTEGER(niter)[0]; if (0 != igraph_array3_init(&c_kernel, 0, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_array3_destroy, &c_kernel); R_SEXP_to_vector(cats, &c_cats); if (0 != igraph_array3_init(&c_cites, 0, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_array3_destroy, &c_cites); cites=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_agebins=INTEGER(agebins)[0]; c_delta=REAL(delta)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_ade(&c_graph, c_niter, &c_kernel, &c_cats, (isNull(cites) ? 0 : &c_cites), c_agebins, c_delta, (isNull(filter) ? 0 : &c_filter), &c_logprob, &c_logmax); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(kernel=R_igraph_array3_to_SEXP(&c_kernel)); igraph_array3_destroy(&c_kernel); IGRAPH_FINALLY_CLEAN(1); PROTECT(cites=R_igraph_0orarray3_to_SEXP(&c_cites)); igraph_array3_destroy(&c_cites); IGRAPH_FINALLY_CLEAN(1); PROTECT(logprob=NEW_NUMERIC(1)); REAL(logprob)[0]=c_logprob; PROTECT(logmax=NEW_NUMERIC(1)); REAL(logmax)[0]=c_logmax; SET_VECTOR_ELT(result, 0, kernel); SET_VECTOR_ELT(result, 1, cites); SET_VECTOR_ELT(result, 2, logprob); SET_VECTOR_ELT(result, 3, logmax); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logprob")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("logmax")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_probs_ade / /-------------------------------------------*/ SEXP R_igraph_revolver_probs_ade(SEXP graph, SEXP kernel, SEXP cats) { /* Declarations */ igraph_t c_graph; igraph_array3_t c_kernel; igraph_vector_t c_cats; igraph_vector_t c_logprobs; igraph_vector_t c_logcited; igraph_vector_t c_logciting; SEXP logprobs; SEXP logcited; SEXP logciting; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_igraph_SEXP_to_array3(kernel, &c_kernel); R_SEXP_to_vector(cats, &c_cats); if (0 != igraph_vector_init(&c_logprobs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logprobs); logprobs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_logcited, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logcited); logcited=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_logciting, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logciting); logciting=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_revolver_probs_ade(&c_graph, &c_kernel, &c_cats, (isNull(logprobs) ? 0 : &c_logprobs), (isNull(logcited) ? 0 : &c_logcited), (isNull(logciting) ? 0 : &c_logciting)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(logprobs=R_igraph_0orvector_to_SEXP(&c_logprobs)); igraph_vector_destroy(&c_logprobs); IGRAPH_FINALLY_CLEAN(1); PROTECT(logcited=R_igraph_0orvector_to_SEXP(&c_logcited)); igraph_vector_destroy(&c_logcited); IGRAPH_FINALLY_CLEAN(1); PROTECT(logciting=R_igraph_0orvector_to_SEXP(&c_logciting)); igraph_vector_destroy(&c_logciting); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, logprobs); SET_VECTOR_ELT(result, 1, logcited); SET_VECTOR_ELT(result, 2, logciting); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("logprobs")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("logcited")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logciting")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_f / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_f(SEXP graph, SEXP niter, SEXP delta) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_niter; igraph_vector_t c_kernel; igraph_vector_t c_cites; igraph_real_t c_delta; igraph_real_t c_logprob; igraph_real_t c_logmax; SEXP kernel; SEXP cites; SEXP logprob; SEXP logmax; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_niter=INTEGER(niter)[0]; if (0 != igraph_vector_init(&c_kernel, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_kernel); if (0 != igraph_vector_init(&c_cites, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_cites); cites=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_delta=REAL(delta)[0]; /* Call igraph */ igraph_revolver_ml_f(&c_graph, c_niter, &c_kernel, (isNull(cites) ? 0 : &c_cites), c_delta, &c_logprob, &c_logmax); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(kernel=R_igraph_vector_to_SEXP(&c_kernel)); igraph_vector_destroy(&c_kernel); IGRAPH_FINALLY_CLEAN(1); PROTECT(cites=R_igraph_0orvector_to_SEXP(&c_cites)); igraph_vector_destroy(&c_cites); IGRAPH_FINALLY_CLEAN(1); PROTECT(logprob=NEW_NUMERIC(1)); REAL(logprob)[0]=c_logprob; PROTECT(logmax=NEW_NUMERIC(1)); REAL(logmax)[0]=c_logmax; SET_VECTOR_ELT(result, 0, kernel); SET_VECTOR_ELT(result, 1, cites); SET_VECTOR_ELT(result, 2, logprob); SET_VECTOR_ELT(result, 3, logmax); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logprob")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("logmax")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_df / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_df(SEXP graph, SEXP niter, SEXP delta) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_niter; igraph_matrix_t c_kernel; igraph_matrix_t c_cites; igraph_real_t c_delta; igraph_real_t c_logprob; igraph_real_t c_logmax; SEXP kernel; SEXP cites; SEXP logprob; SEXP logmax; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_niter=INTEGER(niter)[0]; if (0 != igraph_matrix_init(&c_kernel, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_kernel); if (0 != igraph_matrix_init(&c_cites, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_cites); cites=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_delta=REAL(delta)[0]; /* Call igraph */ igraph_revolver_ml_df(&c_graph, c_niter, &c_kernel, (isNull(cites) ? 0 : &c_cites), c_delta, &c_logprob, &c_logmax); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(kernel=R_igraph_matrix_to_SEXP(&c_kernel)); igraph_matrix_destroy(&c_kernel); IGRAPH_FINALLY_CLEAN(1); PROTECT(cites=R_igraph_0ormatrix_to_SEXP(&c_cites)); igraph_matrix_destroy(&c_cites); IGRAPH_FINALLY_CLEAN(1); PROTECT(logprob=NEW_NUMERIC(1)); REAL(logprob)[0]=c_logprob; PROTECT(logmax=NEW_NUMERIC(1)); REAL(logmax)[0]=c_logmax; SET_VECTOR_ELT(result, 0, kernel); SET_VECTOR_ELT(result, 1, cites); SET_VECTOR_ELT(result, 2, logprob); SET_VECTOR_ELT(result, 3, logmax); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logprob")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("logmax")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_l / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_l(SEXP graph, SEXP niter, SEXP agebins, SEXP delta) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_niter; igraph_vector_t c_kernel; igraph_vector_t c_cites; igraph_integer_t c_agebins; igraph_real_t c_delta; igraph_real_t c_logprob; igraph_real_t c_logmax; SEXP kernel; SEXP cites; SEXP logprob; SEXP logmax; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_niter=INTEGER(niter)[0]; if (0 != igraph_vector_init(&c_kernel, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_kernel); if (0 != igraph_vector_init(&c_cites, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_cites); cites=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_agebins=INTEGER(agebins)[0]; c_delta=REAL(delta)[0]; /* Call igraph */ igraph_revolver_ml_l(&c_graph, c_niter, &c_kernel, (isNull(cites) ? 0 : &c_cites), c_agebins, c_delta, &c_logprob, &c_logmax); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(kernel=R_igraph_vector_to_SEXP(&c_kernel)); igraph_vector_destroy(&c_kernel); IGRAPH_FINALLY_CLEAN(1); PROTECT(cites=R_igraph_0orvector_to_SEXP(&c_cites)); igraph_vector_destroy(&c_cites); IGRAPH_FINALLY_CLEAN(1); PROTECT(logprob=NEW_NUMERIC(1)); REAL(logprob)[0]=c_logprob; PROTECT(logmax=NEW_NUMERIC(1)); REAL(logmax)[0]=c_logmax; SET_VECTOR_ELT(result, 0, kernel); SET_VECTOR_ELT(result, 1, cites); SET_VECTOR_ELT(result, 2, logprob); SET_VECTOR_ELT(result, 3, logmax); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logprob")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("logmax")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_ad / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_ad(SEXP graph, SEXP niter, SEXP agebins, SEXP delta, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_integer_t c_niter; igraph_matrix_t c_kernel; igraph_matrix_t c_cites; igraph_integer_t c_agebins; igraph_real_t c_delta; igraph_vector_t c_filter; igraph_real_t c_logprob; igraph_real_t c_logmax; SEXP kernel; SEXP cites; SEXP logprob; SEXP logmax; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_niter=INTEGER(niter)[0]; if (0 != igraph_matrix_init(&c_kernel, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_kernel); if (0 != igraph_matrix_init(&c_cites, 0, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_matrix_destroy, &c_cites); cites=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_agebins=INTEGER(agebins)[0]; c_delta=REAL(delta)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_ad(&c_graph, c_niter, &c_kernel, (isNull(cites) ? 0 : &c_cites), c_agebins, c_delta, (isNull(filter) ? 0 : &c_filter), &c_logprob, &c_logmax); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(kernel=R_igraph_matrix_to_SEXP(&c_kernel)); igraph_matrix_destroy(&c_kernel); IGRAPH_FINALLY_CLEAN(1); PROTECT(cites=R_igraph_0ormatrix_to_SEXP(&c_cites)); igraph_matrix_destroy(&c_cites); IGRAPH_FINALLY_CLEAN(1); PROTECT(logprob=NEW_NUMERIC(1)); REAL(logprob)[0]=c_logprob; PROTECT(logmax=NEW_NUMERIC(1)); REAL(logmax)[0]=c_logmax; SET_VECTOR_ELT(result, 0, kernel); SET_VECTOR_ELT(result, 1, cites); SET_VECTOR_ELT(result, 2, logprob); SET_VECTOR_ELT(result, 3, logmax); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("kernel")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cites")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logprob")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("logmax")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_probs_ad / /-------------------------------------------*/ SEXP R_igraph_revolver_probs_ad(SEXP graph, SEXP kernel, SEXP ntk) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_kernel; igraph_vector_t c_probs; igraph_vector_t c_citedprobs; igraph_vector_t c_citingprobs; igraph_bool_t c_ntk; SEXP probs; SEXP citedprobs; SEXP citingprobs; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_matrix(kernel, &c_kernel); if (0 != igraph_vector_init(&c_probs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_probs); probs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_citedprobs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_citedprobs); citedprobs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_citingprobs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_citingprobs); citingprobs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ c_ntk=LOGICAL(ntk)[0]; /* Call igraph */ igraph_revolver_probs_ad(&c_graph, &c_kernel, (isNull(probs) ? 0 : &c_probs), (isNull(citedprobs) ? 0 : &c_citedprobs), (isNull(citingprobs) ? 0 : &c_citingprobs), c_ntk); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(probs=R_igraph_0orvector_to_SEXP(&c_probs)); igraph_vector_destroy(&c_probs); IGRAPH_FINALLY_CLEAN(1); PROTECT(citedprobs=R_igraph_0orvector_to_SEXP(&c_citedprobs)); igraph_vector_destroy(&c_citedprobs); IGRAPH_FINALLY_CLEAN(1); PROTECT(citingprobs=R_igraph_0orvector_to_SEXP(&c_citingprobs)); igraph_vector_destroy(&c_citingprobs); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, probs); SET_VECTOR_ELT(result, 1, citedprobs); SET_VECTOR_ELT(result, 2, citingprobs); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("probs")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("citedprobs")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("citingprobs")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_D_alpha / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_D_alpha(SEXP graph, SEXP alpha, SEXP abstol, SEXP reltol, SEXP maxit, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_real_t c_alpha; igraph_real_t c_Fmin; igraph_real_t c_abstol; igraph_real_t c_reltol; int c_maxit; igraph_vector_t c_filter; igraph_integer_t c_fncount; igraph_integer_t c_grcount; SEXP Fmin; SEXP fncount; SEXP grcount; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_alpha=REAL(alpha)[0]; c_abstol=REAL(abstol)[0]; c_reltol=REAL(reltol)[0]; c_maxit=INTEGER(maxit)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_D_alpha(&c_graph, &c_alpha, &c_Fmin, c_abstol, c_reltol, c_maxit, (isNull(filter) ? 0 : &c_filter), &c_fncount, &c_grcount); /* Convert output */ PROTECT(result=NEW_LIST(4)); PROTECT(names=NEW_CHARACTER(4)); PROTECT(alpha=NEW_NUMERIC(1)); REAL(alpha)[0]=c_alpha; PROTECT(Fmin=NEW_NUMERIC(1)); REAL(Fmin)[0]=c_Fmin; PROTECT(fncount=NEW_INTEGER(1)); INTEGER(fncount)[0]=c_fncount; PROTECT(grcount=NEW_INTEGER(1)); INTEGER(grcount)[0]=c_grcount; SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, Fmin); SET_VECTOR_ELT(result, 2, fncount); SET_VECTOR_ELT(result, 3, grcount); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Fmin")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("fncount")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("grcount")); SET_NAMES(result, names); UNPROTECT(5); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_D_alpha_a / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_D_alpha_a(SEXP graph, SEXP alpha, SEXP a, SEXP abstol, SEXP reltol, SEXP maxit, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_real_t c_alpha; igraph_real_t c_a; igraph_real_t c_Fmin; igraph_real_t c_abstol; igraph_real_t c_reltol; int c_maxit; igraph_vector_t c_filter; igraph_integer_t c_fncount; igraph_integer_t c_grcount; SEXP Fmin; SEXP fncount; SEXP grcount; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; c_abstol=REAL(abstol)[0]; c_reltol=REAL(reltol)[0]; c_maxit=INTEGER(maxit)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_D_alpha_a(&c_graph, &c_alpha, &c_a, &c_Fmin, c_abstol, c_reltol, c_maxit, (isNull(filter) ? 0 : &c_filter), &c_fncount, &c_grcount); /* Convert output */ PROTECT(result=NEW_LIST(5)); PROTECT(names=NEW_CHARACTER(5)); PROTECT(alpha=NEW_NUMERIC(1)); REAL(alpha)[0]=c_alpha; PROTECT(a=NEW_NUMERIC(1)); REAL(a)[0]=c_a; PROTECT(Fmin=NEW_NUMERIC(1)); REAL(Fmin)[0]=c_Fmin; PROTECT(fncount=NEW_INTEGER(1)); INTEGER(fncount)[0]=c_fncount; PROTECT(grcount=NEW_INTEGER(1)); INTEGER(grcount)[0]=c_grcount; SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, a); SET_VECTOR_ELT(result, 2, Fmin); SET_VECTOR_ELT(result, 3, fncount); SET_VECTOR_ELT(result, 4, grcount); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("a")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("Fmin")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("fncount")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("grcount")); SET_NAMES(result, names); UNPROTECT(6); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_DE_alpha_a / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_DE_alpha_a(SEXP graph, SEXP cats, SEXP alpha, SEXP a, SEXP coeffs, SEXP abstol, SEXP reltol, SEXP maxit, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_cats; igraph_real_t c_alpha; igraph_real_t c_a; igraph_vector_t c_coeffs; igraph_real_t c_Fmin; igraph_real_t c_abstol; igraph_real_t c_reltol; int c_maxit; igraph_vector_t c_filter; igraph_integer_t c_fncount; igraph_integer_t c_grcount; SEXP Fmin; SEXP fncount; SEXP grcount; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(cats, &c_cats); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; if (0 != R_SEXP_to_vector_copy(coeffs, &c_coeffs)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_coeffs); c_abstol=REAL(abstol)[0]; c_reltol=REAL(reltol)[0]; c_maxit=INTEGER(maxit)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_DE_alpha_a(&c_graph, &c_cats, &c_alpha, &c_a, &c_coeffs, &c_Fmin, c_abstol, c_reltol, c_maxit, (isNull(filter) ? 0 : &c_filter), &c_fncount, &c_grcount); /* Convert output */ PROTECT(result=NEW_LIST(6)); PROTECT(names=NEW_CHARACTER(6)); PROTECT(alpha=NEW_NUMERIC(1)); REAL(alpha)[0]=c_alpha; PROTECT(a=NEW_NUMERIC(1)); REAL(a)[0]=c_a; PROTECT(coeffs=R_igraph_vector_to_SEXP(&c_coeffs)); igraph_vector_destroy(&c_coeffs); IGRAPH_FINALLY_CLEAN(1); PROTECT(Fmin=NEW_NUMERIC(1)); REAL(Fmin)[0]=c_Fmin; PROTECT(fncount=NEW_INTEGER(1)); INTEGER(fncount)[0]=c_fncount; PROTECT(grcount=NEW_INTEGER(1)); INTEGER(grcount)[0]=c_grcount; SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, a); SET_VECTOR_ELT(result, 2, coeffs); SET_VECTOR_ELT(result, 3, Fmin); SET_VECTOR_ELT(result, 4, fncount); SET_VECTOR_ELT(result, 5, grcount); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("a")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("coeffs")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("Fmin")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("fncount")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("grcount")); SET_NAMES(result, names); UNPROTECT(7); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_AD_alpha_a_beta / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_AD_alpha_a_beta(SEXP graph, SEXP alpha, SEXP a, SEXP beta, SEXP abstol, SEXP reltol, SEXP maxit, SEXP agebins, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_real_t c_alpha; igraph_real_t c_a; igraph_real_t c_beta; igraph_real_t c_Fmin; igraph_real_t c_abstol; igraph_real_t c_reltol; int c_maxit; int c_agebins; igraph_vector_t c_filter; igraph_integer_t c_fncount; igraph_integer_t c_grcount; SEXP Fmin; SEXP fncount; SEXP grcount; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; c_beta=REAL(beta)[0]; c_abstol=REAL(abstol)[0]; c_reltol=REAL(reltol)[0]; c_maxit=INTEGER(maxit)[0]; c_agebins=INTEGER(agebins)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_AD_alpha_a_beta(&c_graph, &c_alpha, &c_a, &c_beta, &c_Fmin, c_abstol, c_reltol, c_maxit, c_agebins, (isNull(filter) ? 0 : &c_filter), &c_fncount, &c_grcount); /* Convert output */ PROTECT(result=NEW_LIST(6)); PROTECT(names=NEW_CHARACTER(6)); PROTECT(alpha=NEW_NUMERIC(1)); REAL(alpha)[0]=c_alpha; PROTECT(a=NEW_NUMERIC(1)); REAL(a)[0]=c_a; PROTECT(beta=NEW_NUMERIC(1)); REAL(beta)[0]=c_beta; PROTECT(Fmin=NEW_NUMERIC(1)); REAL(Fmin)[0]=c_Fmin; PROTECT(fncount=NEW_INTEGER(1)); INTEGER(fncount)[0]=c_fncount; PROTECT(grcount=NEW_INTEGER(1)); INTEGER(grcount)[0]=c_grcount; SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, a); SET_VECTOR_ELT(result, 2, beta); SET_VECTOR_ELT(result, 3, Fmin); SET_VECTOR_ELT(result, 4, fncount); SET_VECTOR_ELT(result, 5, grcount); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("a")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("beta")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("Fmin")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("fncount")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("grcount")); SET_NAMES(result, names); UNPROTECT(7); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_AD_dpareto / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_AD_dpareto(SEXP graph, SEXP alpha, SEXP a, SEXP paralpha, SEXP parbeta, SEXP parscale, SEXP abstol, SEXP reltol, SEXP maxit, SEXP agebins, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_real_t c_alpha; igraph_real_t c_a; igraph_real_t c_paralpha; igraph_real_t c_parbeta; igraph_real_t c_parscale; igraph_real_t c_Fmin; igraph_real_t c_abstol; igraph_real_t c_reltol; int c_maxit; int c_agebins; igraph_vector_t c_filter; igraph_integer_t c_fncount; igraph_integer_t c_grcount; SEXP Fmin; SEXP fncount; SEXP grcount; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; c_paralpha=REAL(paralpha)[0]; c_parbeta=REAL(parbeta)[0]; c_parscale=REAL(parscale)[0]; c_abstol=REAL(abstol)[0]; c_reltol=REAL(reltol)[0]; c_maxit=INTEGER(maxit)[0]; c_agebins=INTEGER(agebins)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_AD_dpareto(&c_graph, &c_alpha, &c_a, &c_paralpha, &c_parbeta, &c_parscale, &c_Fmin, c_abstol, c_reltol, c_maxit, c_agebins, (isNull(filter) ? 0 : &c_filter), &c_fncount, &c_grcount); /* Convert output */ PROTECT(result=NEW_LIST(8)); PROTECT(names=NEW_CHARACTER(8)); PROTECT(alpha=NEW_NUMERIC(1)); REAL(alpha)[0]=c_alpha; PROTECT(a=NEW_NUMERIC(1)); REAL(a)[0]=c_a; PROTECT(paralpha=NEW_NUMERIC(1)); REAL(paralpha)[0]=c_paralpha; PROTECT(parbeta=NEW_NUMERIC(1)); REAL(parbeta)[0]=c_parbeta; PROTECT(parscale=NEW_NUMERIC(1)); REAL(parscale)[0]=c_parscale; PROTECT(Fmin=NEW_NUMERIC(1)); REAL(Fmin)[0]=c_Fmin; PROTECT(fncount=NEW_INTEGER(1)); INTEGER(fncount)[0]=c_fncount; PROTECT(grcount=NEW_INTEGER(1)); INTEGER(grcount)[0]=c_grcount; SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, a); SET_VECTOR_ELT(result, 2, paralpha); SET_VECTOR_ELT(result, 3, parbeta); SET_VECTOR_ELT(result, 4, parscale); SET_VECTOR_ELT(result, 5, Fmin); SET_VECTOR_ELT(result, 6, fncount); SET_VECTOR_ELT(result, 7, grcount); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("a")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("paralpha")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("parbeta")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("parscale")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("Fmin")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("fncount")); SET_STRING_ELT(names, 7, CREATE_STRING_VECTOR("grcount")); SET_NAMES(result, names); UNPROTECT(9); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_AD_dpareto_eval / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_AD_dpareto_eval(SEXP graph, SEXP alpha, SEXP a, SEXP paralpha, SEXP parbeta, SEXP parscale, SEXP agebins, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_real_t c_alpha; igraph_real_t c_a; igraph_real_t c_paralpha; igraph_real_t c_parbeta; igraph_real_t c_parscale; igraph_real_t c_value; igraph_vector_t c_deriv; int c_agebins; igraph_vector_t c_filter; SEXP value; SEXP deriv; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; c_paralpha=REAL(paralpha)[0]; c_parbeta=REAL(parbeta)[0]; c_parscale=REAL(parscale)[0]; if (0 != igraph_vector_init(&c_deriv, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_deriv); c_agebins=INTEGER(agebins)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_AD_dpareto_eval(&c_graph, c_alpha, c_a, c_paralpha, c_parbeta, c_parscale, &c_value, &c_deriv, c_agebins, (isNull(filter) ? 0 : &c_filter)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(deriv=R_igraph_vector_to_SEXP(&c_deriv)); igraph_vector_destroy(&c_deriv); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, value); SET_VECTOR_ELT(result, 1, deriv); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("deriv")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_ADE_alpha_a_beta / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_ADE_alpha_a_beta(SEXP graph, SEXP cats, SEXP alpha, SEXP a, SEXP beta, SEXP coeffs, SEXP abstol, SEXP reltol, SEXP maxit, SEXP agebins, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_cats; igraph_real_t c_alpha; igraph_real_t c_a; igraph_real_t c_beta; igraph_vector_t c_coeffs; igraph_real_t c_Fmin; igraph_real_t c_abstol; igraph_real_t c_reltol; int c_maxit; int c_agebins; igraph_vector_t c_filter; igraph_integer_t c_fncount; igraph_integer_t c_grcount; SEXP Fmin; SEXP fncount; SEXP grcount; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(cats, &c_cats); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; c_beta=REAL(beta)[0]; if (0 != R_SEXP_to_vector_copy(coeffs, &c_coeffs)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_coeffs); c_abstol=REAL(abstol)[0]; c_reltol=REAL(reltol)[0]; c_maxit=INTEGER(maxit)[0]; c_agebins=INTEGER(agebins)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_ADE_alpha_a_beta(&c_graph, &c_cats, &c_alpha, &c_a, &c_beta, &c_coeffs, &c_Fmin, c_abstol, c_reltol, c_maxit, c_agebins, (isNull(filter) ? 0 : &c_filter), &c_fncount, &c_grcount); /* Convert output */ PROTECT(result=NEW_LIST(7)); PROTECT(names=NEW_CHARACTER(7)); PROTECT(alpha=NEW_NUMERIC(1)); REAL(alpha)[0]=c_alpha; PROTECT(a=NEW_NUMERIC(1)); REAL(a)[0]=c_a; PROTECT(beta=NEW_NUMERIC(1)); REAL(beta)[0]=c_beta; PROTECT(coeffs=R_igraph_vector_to_SEXP(&c_coeffs)); igraph_vector_destroy(&c_coeffs); IGRAPH_FINALLY_CLEAN(1); PROTECT(Fmin=NEW_NUMERIC(1)); REAL(Fmin)[0]=c_Fmin; PROTECT(fncount=NEW_INTEGER(1)); INTEGER(fncount)[0]=c_fncount; PROTECT(grcount=NEW_INTEGER(1)); INTEGER(grcount)[0]=c_grcount; SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, a); SET_VECTOR_ELT(result, 2, beta); SET_VECTOR_ELT(result, 3, coeffs); SET_VECTOR_ELT(result, 4, Fmin); SET_VECTOR_ELT(result, 5, fncount); SET_VECTOR_ELT(result, 6, grcount); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("a")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("beta")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("coeffs")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("Fmin")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("fncount")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("grcount")); SET_NAMES(result, names); UNPROTECT(8); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_ADE_dpareto / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_ADE_dpareto(SEXP graph, SEXP cats, SEXP alpha, SEXP a, SEXP paralpha, SEXP parbeta, SEXP parscale, SEXP coeffs, SEXP abstol, SEXP reltol, SEXP maxit, SEXP agebins, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_cats; igraph_real_t c_alpha; igraph_real_t c_a; igraph_real_t c_paralpha; igraph_real_t c_parbeta; igraph_real_t c_parscale; igraph_vector_t c_coeffs; igraph_real_t c_Fmin; igraph_real_t c_abstol; igraph_real_t c_reltol; int c_maxit; int c_agebins; igraph_vector_t c_filter; igraph_integer_t c_fncount; igraph_integer_t c_grcount; SEXP Fmin; SEXP fncount; SEXP grcount; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(cats, &c_cats); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; c_paralpha=REAL(paralpha)[0]; c_parbeta=REAL(parbeta)[0]; c_parscale=REAL(parscale)[0]; if (0 != R_SEXP_to_vector_copy(coeffs, &c_coeffs)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_coeffs); c_abstol=REAL(abstol)[0]; c_reltol=REAL(reltol)[0]; c_maxit=INTEGER(maxit)[0]; c_agebins=INTEGER(agebins)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_ADE_dpareto(&c_graph, &c_cats, &c_alpha, &c_a, &c_paralpha, &c_parbeta, &c_parscale, &c_coeffs, &c_Fmin, c_abstol, c_reltol, c_maxit, c_agebins, (isNull(filter) ? 0 : &c_filter), &c_fncount, &c_grcount); /* Convert output */ PROTECT(result=NEW_LIST(9)); PROTECT(names=NEW_CHARACTER(9)); PROTECT(alpha=NEW_NUMERIC(1)); REAL(alpha)[0]=c_alpha; PROTECT(a=NEW_NUMERIC(1)); REAL(a)[0]=c_a; PROTECT(paralpha=NEW_NUMERIC(1)); REAL(paralpha)[0]=c_paralpha; PROTECT(parbeta=NEW_NUMERIC(1)); REAL(parbeta)[0]=c_parbeta; PROTECT(parscale=NEW_NUMERIC(1)); REAL(parscale)[0]=c_parscale; PROTECT(coeffs=R_igraph_vector_to_SEXP(&c_coeffs)); igraph_vector_destroy(&c_coeffs); IGRAPH_FINALLY_CLEAN(1); PROTECT(Fmin=NEW_NUMERIC(1)); REAL(Fmin)[0]=c_Fmin; PROTECT(fncount=NEW_INTEGER(1)); INTEGER(fncount)[0]=c_fncount; PROTECT(grcount=NEW_INTEGER(1)); INTEGER(grcount)[0]=c_grcount; SET_VECTOR_ELT(result, 0, alpha); SET_VECTOR_ELT(result, 1, a); SET_VECTOR_ELT(result, 2, paralpha); SET_VECTOR_ELT(result, 3, parbeta); SET_VECTOR_ELT(result, 4, parscale); SET_VECTOR_ELT(result, 5, coeffs); SET_VECTOR_ELT(result, 6, Fmin); SET_VECTOR_ELT(result, 7, fncount); SET_VECTOR_ELT(result, 8, grcount); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("alpha")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("a")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("paralpha")); SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("parbeta")); SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("parscale")); SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("coeffs")); SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("Fmin")); SET_STRING_ELT(names, 7, CREATE_STRING_VECTOR("fncount")); SET_STRING_ELT(names, 8, CREATE_STRING_VECTOR("grcount")); SET_NAMES(result, names); UNPROTECT(10); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_ADE_dpareto_eval / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_ADE_dpareto_eval(SEXP graph, SEXP cats, SEXP alpha, SEXP a, SEXP paralpha, SEXP parbeta, SEXP parscale, SEXP coeffs, SEXP agebins, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_cats; igraph_real_t c_alpha; igraph_real_t c_a; igraph_real_t c_paralpha; igraph_real_t c_parbeta; igraph_real_t c_parscale; igraph_vector_t c_coeffs; igraph_real_t c_value; igraph_vector_t c_deriv; int c_agebins; igraph_vector_t c_filter; SEXP value; SEXP deriv; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(cats, &c_cats); c_alpha=REAL(alpha)[0]; c_a=REAL(a)[0]; c_paralpha=REAL(paralpha)[0]; c_parbeta=REAL(parbeta)[0]; c_parscale=REAL(parscale)[0]; R_SEXP_to_vector(coeffs, &c_coeffs); if (0 != igraph_vector_init(&c_deriv, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_deriv); c_agebins=INTEGER(agebins)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_ADE_dpareto_eval(&c_graph, &c_cats, c_alpha, c_a, c_paralpha, c_parbeta, c_parscale, &c_coeffs, &c_value, &c_deriv, c_agebins, (isNull(filter) ? 0 : &c_filter)); /* Convert output */ PROTECT(result=NEW_LIST(2)); PROTECT(names=NEW_CHARACTER(2)); PROTECT(value=NEW_NUMERIC(1)); REAL(value)[0]=c_value; PROTECT(deriv=R_igraph_vector_to_SEXP(&c_deriv)); igraph_vector_destroy(&c_deriv); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, value); SET_VECTOR_ELT(result, 1, deriv); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("value")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("deriv")); SET_NAMES(result, names); UNPROTECT(3); UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_ml_ADE_dpareto_evalf / /-------------------------------------------*/ SEXP R_igraph_revolver_ml_ADE_dpareto_evalf(SEXP graph, SEXP cats, SEXP par, SEXP agebins, SEXP filter) { /* Declarations */ igraph_t c_graph; igraph_vector_t c_cats; igraph_matrix_t c_par; igraph_vector_t c_value; int c_agebins; igraph_vector_t c_filter; SEXP value; SEXP result; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_vector(cats, &c_cats); R_SEXP_to_matrix(par, &c_par); if (0 != igraph_vector_init(&c_value, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_value); c_agebins=INTEGER(agebins)[0]; if (!isNull(filter)) { R_SEXP_to_vector(filter, &c_filter); } /* Call igraph */ igraph_revolver_ml_ADE_dpareto_evalf(&c_graph, &c_cats, &c_par, &c_value, c_agebins, (isNull(filter) ? 0 : &c_filter)); /* Convert output */ PROTECT(value=R_igraph_vector_to_SEXP(&c_value)); igraph_vector_destroy(&c_value); IGRAPH_FINALLY_CLEAN(1); result=value; UNPROTECT(1); return(result); } /*-------------------------------------------/ / igraph_revolver_probs_ADE_dpareto / /-------------------------------------------*/ SEXP R_igraph_revolver_probs_ADE_dpareto(SEXP graph, SEXP par, SEXP cats, SEXP gcats, SEXP agebins) { /* Declarations */ igraph_t c_graph; igraph_matrix_t c_par; igraph_vector_t c_cats; igraph_vector_t c_gcats; int c_agebins; igraph_vector_t c_logprobs; igraph_vector_t c_logcited; igraph_vector_t c_logciting; SEXP logprobs; SEXP logcited; SEXP logciting; SEXP result, names; /* Convert input */ R_SEXP_to_igraph(graph, &c_graph); R_SEXP_to_matrix(par, &c_par); R_SEXP_to_vector(cats, &c_cats); R_SEXP_to_vector(gcats, &c_gcats); c_agebins=INTEGER(agebins)[0]; if (0 != igraph_vector_init(&c_logprobs, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logprobs); logprobs=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_logcited, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logcited); logcited=NEW_NUMERIC(0); /* hack to have a non-NULL value */ if (0 != igraph_vector_init(&c_logciting, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_vector_destroy, &c_logciting); logciting=NEW_NUMERIC(0); /* hack to have a non-NULL value */ /* Call igraph */ igraph_revolver_probs_ADE_dpareto(&c_graph, &c_par, &c_cats, &c_gcats, c_agebins, (isNull(logprobs) ? 0 : &c_logprobs), (isNull(logcited) ? 0 : &c_logcited), (isNull(logciting) ? 0 : &c_logciting)); /* Convert output */ PROTECT(result=NEW_LIST(3)); PROTECT(names=NEW_CHARACTER(3)); PROTECT(logprobs=R_igraph_0orvector_to_SEXP(&c_logprobs)); igraph_vector_destroy(&c_logprobs); IGRAPH_FINALLY_CLEAN(1); PROTECT(logcited=R_igraph_0orvector_to_SEXP(&c_logcited)); igraph_vector_destroy(&c_logcited); IGRAPH_FINALLY_CLEAN(1); PROTECT(logciting=R_igraph_0orvector_to_SEXP(&c_logciting)); igraph_vector_destroy(&c_logciting); IGRAPH_FINALLY_CLEAN(1); SET_VECTOR_ELT(result, 0, logprobs); SET_VECTOR_ELT(result, 1, logcited); SET_VECTOR_ELT(result, 2, logciting); SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("logprobs")); SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("logcited")); SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("logciting")); SET_NAMES(result, names); UNPROTECT(4); UNPROTECT(1); return(result); } igraph/src/cs/0000755000176000001440000000000012325372073012754 5ustar ripleyusersigraph/src/cs/cs.h0000644000176000001440000007620512325527073013546 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CXS_H #define _CXS_H #include #include #include #include #ifdef MATLAB_MEX_FILE #include "mex.h" #endif #ifdef __cplusplus #ifndef NCOMPLEX #include typedef std::complex cs_complex_t ; #endif extern "C" { #else #ifndef NCOMPLEX #include #define cs_complex_t double _Complex #endif #endif #define CS_VER 2 /* CXSparse Version 2.2.3 */ #define CS_SUBVER 2 #define CS_SUBSUB 3 #define CS_DATE "Mar 24, 2009" /* CXSparse release date */ #define CS_COPYRIGHT "Copyright (c) Timothy A. Davis, 2006-2009" #define CXSPARSE /* define UF_long */ #include "UFconfig.h" /* -------------------------------------------------------------------------- */ /* double/int version of CXSparse */ /* -------------------------------------------------------------------------- */ /* --- primary CSparse routines and data structures ------------------------- */ typedef struct cs_di_sparse /* matrix in compressed-column or triplet form */ { int nzmax ; /* maximum number of entries */ int m ; /* number of rows */ int n ; /* number of columns */ int *p ; /* column pointers (size n+1) or col indices (size nzmax) */ int *i ; /* row indices, size nzmax */ double *x ; /* numerical values, size nzmax */ int nz ; /* # of entries in triplet matrix, -1 for compressed-col */ } cs_di ; cs_di *cs_di_add (const cs_di *A, const cs_di *B, double alpha, double beta) ; int cs_di_cholsol (int order, const cs_di *A, double *b) ; int cs_di_dupl (cs_di *A) ; int cs_di_entry (cs_di *T, int i, int j, double x) ; int cs_di_lusol (int order, const cs_di *A, double *b, double tol) ; int cs_di_gaxpy (const cs_di *A, const double *x, double *y) ; cs_di *cs_di_multiply (const cs_di *A, const cs_di *B) ; int cs_di_qrsol (int order, const cs_di *A, double *b) ; cs_di *cs_di_transpose (const cs_di *A, int values) ; cs_di *cs_di_compress (const cs_di *T) ; double cs_di_norm (const cs_di *A) ; int cs_di_print (const cs_di *A, int brief) ; cs_di *cs_di_load (FILE *f) ; /* utilities */ void *cs_di_calloc (int n, size_t size) ; void *cs_di_free (void *p) ; void *cs_di_realloc (void *p, int n, size_t size, int *ok) ; cs_di *cs_di_spalloc (int m, int n, int nzmax, int values, int t) ; cs_di *cs_di_spfree (cs_di *A) ; int cs_di_sprealloc (cs_di *A, int nzmax) ; void *cs_di_malloc (int n, size_t size) ; /* --- secondary CSparse routines and data structures ----------------------- */ typedef struct cs_di_symbolic /* symbolic Cholesky, LU, or QR analysis */ { int *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ int *q ; /* fill-reducing column permutation for LU and QR */ int *parent ; /* elimination tree for Cholesky and QR */ int *cp ; /* column pointers for Cholesky, row counts for QR */ int *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ int m2 ; /* # of rows for QR, after adding fictitious rows */ double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ double unz ; /* # entries in U for LU; in R for QR */ } cs_dis ; typedef struct cs_di_numeric /* numeric Cholesky, LU, or QR factorization */ { cs_di *L ; /* L for LU and Cholesky, V for QR */ cs_di *U ; /* U for LU, r for QR, not used for Cholesky */ int *pinv ; /* partial pivoting for LU */ double *B ; /* beta [0..n-1] for QR */ } cs_din ; typedef struct cs_di_dmperm_results /* cs_di_dmperm or cs_di_scc output */ { int *p ; /* size m, row permutation */ int *q ; /* size n, column permutation */ int *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ int *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ int nb ; /* # of blocks in fine dmperm decomposition */ int rr [5] ; /* coarse row decomposition */ int cc [5] ; /* coarse column decomposition */ } cs_did ; int *cs_di_amd (int order, const cs_di *A) ; cs_din *cs_di_chol (const cs_di *A, const cs_dis *S) ; cs_did *cs_di_dmperm (const cs_di *A, int seed) ; int cs_di_droptol (cs_di *A, double tol) ; int cs_di_dropzeros (cs_di *A) ; int cs_di_happly (const cs_di *V, int i, double beta, double *x) ; int cs_di_ipvec (const int *p, const double *b, double *x, int n) ; int cs_di_lsolve (const cs_di *L, double *x) ; int cs_di_ltsolve (const cs_di *L, double *x) ; cs_din *cs_di_lu (const cs_di *A, const cs_dis *S, double tol) ; cs_di *cs_di_permute (const cs_di *A, const int *pinv, const int *q, int values) ; int *cs_di_pinv (const int *p, int n) ; int cs_di_pvec (const int *p, const double *b, double *x, int n) ; cs_din *cs_di_qr (const cs_di *A, const cs_dis *S) ; cs_dis *cs_di_schol (int order, const cs_di *A) ; cs_dis *cs_di_sqr (int order, const cs_di *A, int qr) ; cs_di *cs_di_symperm (const cs_di *A, const int *pinv, int values) ; int cs_di_usolve (const cs_di *U, double *x) ; int cs_di_utsolve (const cs_di *U, double *x) ; int cs_di_updown (cs_di *L, int sigma, const cs_di *C, const int *parent) ; /* utilities */ cs_dis *cs_di_sfree (cs_dis *S) ; cs_din *cs_di_nfree (cs_din *N) ; cs_did *cs_di_dfree (cs_did *D) ; /* --- tertiary CSparse routines -------------------------------------------- */ int *cs_di_counts (const cs_di *A, const int *parent, const int *post, int ata) ; double cs_di_cumsum (int *p, int *c, int n) ; int cs_di_dfs (int j, cs_di *G, int top, int *xi, int *pstack, const int *pinv) ; int *cs_di_etree (const cs_di *A, int ata) ; int cs_di_fkeep (cs_di *A, int (*fkeep) (int, int, double, void *), void *other) ; double cs_di_house (double *x, double *beta, int n) ; int *cs_di_maxtrans (const cs_di *A, int seed) ; int *cs_di_post (const int *parent, int n) ; cs_did *cs_di_scc (cs_di *A) ; int cs_di_scatter (const cs_di *A, int j, double beta, int *w, double *x, int mark, cs_di *C, int nz) ; int cs_di_tdfs (int j, int k, int *head, const int *next, int *post, int *stack) ; int cs_di_leaf (int i, int j, const int *first, int *maxfirst, int *prevleaf, int *ancestor, int *jleaf) ; int cs_di_reach (cs_di *G, const cs_di *B, int k, int *xi, const int *pinv) ; int cs_di_spsolve (cs_di *L, const cs_di *B, int k, int *xi, double *x, const int *pinv, int lo) ; int cs_di_ereach (const cs_di *A, int k, const int *parent, int *s, int *w) ; int *cs_di_randperm (int n, int seed) ; /* utilities */ cs_did *cs_di_dalloc (int m, int n) ; cs_di *cs_di_done (cs_di *C, void *w, void *x, int ok) ; int *cs_di_idone (int *p, cs_di *C, void *w, int ok) ; cs_din *cs_di_ndone (cs_din *N, cs_di *C, void *w, void *x, int ok) ; cs_did *cs_di_ddone (cs_did *D, cs_di *C, void *w, int ok) ; /* -------------------------------------------------------------------------- */ /* double/UF_long version of CXSparse */ /* -------------------------------------------------------------------------- */ /* --- primary CSparse routines and data structures ------------------------- */ typedef struct cs_dl_sparse /* matrix in compressed-column or triplet form */ { UF_long nzmax ; /* maximum number of entries */ UF_long m ; /* number of rows */ UF_long n ; /* number of columns */ UF_long *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ UF_long *i ; /* row indices, size nzmax */ double *x ; /* numerical values, size nzmax */ UF_long nz ; /* # of entries in triplet matrix, -1 for compressed-col */ } cs_dl ; cs_dl *cs_dl_add (const cs_dl *A, const cs_dl *B, double alpha, double beta) ; UF_long cs_dl_cholsol (UF_long order, const cs_dl *A, double *b) ; UF_long cs_dl_dupl (cs_dl *A) ; UF_long cs_dl_entry (cs_dl *T, UF_long i, UF_long j, double x) ; UF_long cs_dl_lusol (UF_long order, const cs_dl *A, double *b, double tol) ; UF_long cs_dl_gaxpy (const cs_dl *A, const double *x, double *y) ; cs_dl *cs_dl_multiply (const cs_dl *A, const cs_dl *B) ; UF_long cs_dl_qrsol (UF_long order, const cs_dl *A, double *b) ; cs_dl *cs_dl_transpose (const cs_dl *A, UF_long values) ; cs_dl *cs_dl_compress (const cs_dl *T) ; double cs_dl_norm (const cs_dl *A) ; UF_long cs_dl_print (const cs_dl *A, UF_long brief) ; cs_dl *cs_dl_load (FILE *f) ; /* utilities */ void *cs_dl_calloc (UF_long n, size_t size) ; void *cs_dl_free (void *p) ; void *cs_dl_realloc (void *p, UF_long n, size_t size, UF_long *ok) ; cs_dl *cs_dl_spalloc (UF_long m, UF_long n, UF_long nzmax, UF_long values, UF_long t) ; cs_dl *cs_dl_spfree (cs_dl *A) ; UF_long cs_dl_sprealloc (cs_dl *A, UF_long nzmax) ; void *cs_dl_malloc (UF_long n, size_t size) ; /* --- secondary CSparse routines and data structures ----------------------- */ typedef struct cs_dl_symbolic /* symbolic Cholesky, LU, or QR analysis */ { UF_long *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ UF_long *q ; /* fill-reducing column permutation for LU and QR */ UF_long *parent ; /* elimination tree for Cholesky and QR */ UF_long *cp ; /* column pointers for Cholesky, row counts for QR */ UF_long *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ UF_long m2 ; /* # of rows for QR, after adding fictitious rows */ double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ double unz ; /* # entries in U for LU; in R for QR */ } cs_dls ; typedef struct cs_dl_numeric /* numeric Cholesky, LU, or QR factorization */ { cs_dl *L ; /* L for LU and Cholesky, V for QR */ cs_dl *U ; /* U for LU, r for QR, not used for Cholesky */ UF_long *pinv ; /* partial pivoting for LU */ double *B ; /* beta [0..n-1] for QR */ } cs_dln ; typedef struct cs_dl_dmperm_results /* cs_dl_dmperm or cs_dl_scc output */ { UF_long *p ; /* size m, row permutation */ UF_long *q ; /* size n, column permutation */ UF_long *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ UF_long *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ UF_long nb ; /* # of blocks in fine dmperm decomposition */ UF_long rr [5] ; /* coarse row decomposition */ UF_long cc [5] ; /* coarse column decomposition */ } cs_dld ; UF_long *cs_dl_amd (UF_long order, const cs_dl *A) ; cs_dln *cs_dl_chol (const cs_dl *A, const cs_dls *S) ; cs_dld *cs_dl_dmperm (const cs_dl *A, UF_long seed) ; UF_long cs_dl_droptol (cs_dl *A, double tol) ; UF_long cs_dl_dropzeros (cs_dl *A) ; UF_long cs_dl_happly (const cs_dl *V, UF_long i, double beta, double *x) ; UF_long cs_dl_ipvec (const UF_long *p, const double *b, double *x, UF_long n) ; UF_long cs_dl_lsolve (const cs_dl *L, double *x) ; UF_long cs_dl_ltsolve (const cs_dl *L, double *x) ; cs_dln *cs_dl_lu (const cs_dl *A, const cs_dls *S, double tol) ; cs_dl *cs_dl_permute (const cs_dl *A, const UF_long *pinv, const UF_long *q, UF_long values) ; UF_long *cs_dl_pinv (const UF_long *p, UF_long n) ; UF_long cs_dl_pvec (const UF_long *p, const double *b, double *x, UF_long n) ; cs_dln *cs_dl_qr (const cs_dl *A, const cs_dls *S) ; cs_dls *cs_dl_schol (UF_long order, const cs_dl *A) ; cs_dls *cs_dl_sqr (UF_long order, const cs_dl *A, UF_long qr) ; cs_dl *cs_dl_symperm (const cs_dl *A, const UF_long *pinv, UF_long values) ; UF_long cs_dl_usolve (const cs_dl *U, double *x) ; UF_long cs_dl_utsolve (const cs_dl *U, double *x) ; UF_long cs_dl_updown (cs_dl *L, UF_long sigma, const cs_dl *C, const UF_long *parent) ; /* utilities */ cs_dls *cs_dl_sfree (cs_dls *S) ; cs_dln *cs_dl_nfree (cs_dln *N) ; cs_dld *cs_dl_dfree (cs_dld *D) ; /* --- tertiary CSparse routines -------------------------------------------- */ UF_long *cs_dl_counts (const cs_dl *A, const UF_long *parent, const UF_long *post, UF_long ata) ; double cs_dl_cumsum (UF_long *p, UF_long *c, UF_long n) ; UF_long cs_dl_dfs (UF_long j, cs_dl *G, UF_long top, UF_long *xi, UF_long *pstack, const UF_long *pinv) ; UF_long *cs_dl_etree (const cs_dl *A, UF_long ata) ; UF_long cs_dl_fkeep (cs_dl *A, UF_long (*fkeep) (UF_long, UF_long, double, void *), void *other) ; double cs_dl_house (double *x, double *beta, UF_long n) ; UF_long *cs_dl_maxtrans (const cs_dl *A, UF_long seed) ; UF_long *cs_dl_post (const UF_long *parent, UF_long n) ; cs_dld *cs_dl_scc (cs_dl *A) ; UF_long cs_dl_scatter (const cs_dl *A, UF_long j, double beta, UF_long *w, double *x, UF_long mark,cs_dl *C, UF_long nz) ; UF_long cs_dl_tdfs (UF_long j, UF_long k, UF_long *head, const UF_long *next, UF_long *post, UF_long *stack) ; UF_long cs_dl_leaf (UF_long i, UF_long j, const UF_long *first, UF_long *maxfirst, UF_long *prevleaf, UF_long *ancestor, UF_long *jleaf) ; UF_long cs_dl_reach (cs_dl *G, const cs_dl *B, UF_long k, UF_long *xi, const UF_long *pinv) ; UF_long cs_dl_spsolve (cs_dl *L, const cs_dl *B, UF_long k, UF_long *xi, double *x, const UF_long *pinv, UF_long lo) ; UF_long cs_dl_ereach (const cs_dl *A, UF_long k, const UF_long *parent, UF_long *s, UF_long *w) ; UF_long *cs_dl_randperm (UF_long n, UF_long seed) ; /* utilities */ cs_dld *cs_dl_dalloc (UF_long m, UF_long n) ; cs_dl *cs_dl_done (cs_dl *C, void *w, void *x, UF_long ok) ; UF_long *cs_dl_idone (UF_long *p, cs_dl *C, void *w, UF_long ok) ; cs_dln *cs_dl_ndone (cs_dln *N, cs_dl *C, void *w, void *x, UF_long ok) ; cs_dld *cs_dl_ddone (cs_dld *D, cs_dl *C, void *w, UF_long ok) ; /* -------------------------------------------------------------------------- */ /* complex/int version of CXSparse */ /* -------------------------------------------------------------------------- */ #ifndef NCOMPLEX /* --- primary CSparse routines and data structures ------------------------- */ typedef struct cs_ci_sparse /* matrix in compressed-column or triplet form */ { int nzmax ; /* maximum number of entries */ int m ; /* number of rows */ int n ; /* number of columns */ int *p ; /* column pointers (size n+1) or col indices (size nzmax) */ int *i ; /* row indices, size nzmax */ cs_complex_t *x ; /* numerical values, size nzmax */ int nz ; /* # of entries in triplet matrix, -1 for compressed-col */ } cs_ci ; cs_ci *cs_ci_add (const cs_ci *A, const cs_ci *B, cs_complex_t alpha, cs_complex_t beta) ; int cs_ci_cholsol (int order, const cs_ci *A, cs_complex_t *b) ; int cs_ci_dupl (cs_ci *A) ; int cs_ci_entry (cs_ci *T, int i, int j, cs_complex_t x) ; int cs_ci_lusol (int order, const cs_ci *A, cs_complex_t *b, double tol) ; int cs_ci_gaxpy (const cs_ci *A, const cs_complex_t *x, cs_complex_t *y) ; cs_ci *cs_ci_multiply (const cs_ci *A, const cs_ci *B) ; int cs_ci_qrsol (int order, const cs_ci *A, cs_complex_t *b) ; cs_ci *cs_ci_transpose (const cs_ci *A, int values) ; cs_ci *cs_ci_compress (const cs_ci *T) ; double cs_ci_norm (const cs_ci *A) ; int cs_ci_print (const cs_ci *A, int brief) ; cs_ci *cs_ci_load (FILE *f) ; /* utilities */ void *cs_ci_calloc (int n, size_t size) ; void *cs_ci_free (void *p) ; void *cs_ci_realloc (void *p, int n, size_t size, int *ok) ; cs_ci *cs_ci_spalloc (int m, int n, int nzmax, int values, int t) ; cs_ci *cs_ci_spfree (cs_ci *A) ; int cs_ci_sprealloc (cs_ci *A, int nzmax) ; void *cs_ci_malloc (int n, size_t size) ; /* --- secondary CSparse routines and data structures ----------------------- */ typedef struct cs_ci_symbolic /* symbolic Cholesky, LU, or QR analysis */ { int *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ int *q ; /* fill-reducing column permutation for LU and QR */ int *parent ; /* elimination tree for Cholesky and QR */ int *cp ; /* column pointers for Cholesky, row counts for QR */ int *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ int m2 ; /* # of rows for QR, after adding fictitious rows */ double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ double unz ; /* # entries in U for LU; in R for QR */ } cs_cis ; typedef struct cs_ci_numeric /* numeric Cholesky, LU, or QR factorization */ { cs_ci *L ; /* L for LU and Cholesky, V for QR */ cs_ci *U ; /* U for LU, r for QR, not used for Cholesky */ int *pinv ; /* partial pivoting for LU */ double *B ; /* beta [0..n-1] for QR */ } cs_cin ; typedef struct cs_ci_dmperm_results /* cs_ci_dmperm or cs_ci_scc output */ { int *p ; /* size m, row permutation */ int *q ; /* size n, column permutation */ int *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ int *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ int nb ; /* # of blocks in fine dmperm decomposition */ int rr [5] ; /* coarse row decomposition */ int cc [5] ; /* coarse column decomposition */ } cs_cid ; int *cs_ci_amd (int order, const cs_ci *A) ; cs_cin *cs_ci_chol (const cs_ci *A, const cs_cis *S) ; cs_cid *cs_ci_dmperm (const cs_ci *A, int seed) ; int cs_ci_droptol (cs_ci *A, double tol) ; int cs_ci_dropzeros (cs_ci *A) ; int cs_ci_happly (const cs_ci *V, int i, double beta, cs_complex_t *x) ; int cs_ci_ipvec (const int *p, const cs_complex_t *b, cs_complex_t *x, int n) ; int cs_ci_lsolve (const cs_ci *L, cs_complex_t *x) ; int cs_ci_ltsolve (const cs_ci *L, cs_complex_t *x) ; cs_cin *cs_ci_lu (const cs_ci *A, const cs_cis *S, double tol) ; cs_ci *cs_ci_permute (const cs_ci *A, const int *pinv, const int *q, int values) ; int *cs_ci_pinv (const int *p, int n) ; int cs_ci_pvec (const int *p, const cs_complex_t *b, cs_complex_t *x, int n) ; cs_cin *cs_ci_qr (const cs_ci *A, const cs_cis *S) ; cs_cis *cs_ci_schol (int order, const cs_ci *A) ; cs_cis *cs_ci_sqr (int order, const cs_ci *A, int qr) ; cs_ci *cs_ci_symperm (const cs_ci *A, const int *pinv, int values) ; int cs_ci_usolve (const cs_ci *U, cs_complex_t *x) ; int cs_ci_utsolve (const cs_ci *U, cs_complex_t *x) ; int cs_ci_updown (cs_ci *L, int sigma, const cs_ci *C, const int *parent) ; /* utilities */ cs_cis *cs_ci_sfree (cs_cis *S) ; cs_cin *cs_ci_nfree (cs_cin *N) ; cs_cid *cs_ci_dfree (cs_cid *D) ; /* --- tertiary CSparse routines -------------------------------------------- */ int *cs_ci_counts (const cs_ci *A, const int *parent, const int *post, int ata) ; double cs_ci_cumsum (int *p, int *c, int n) ; int cs_ci_dfs (int j, cs_ci *G, int top, int *xi, int *pstack, const int *pinv) ; int *cs_ci_etree (const cs_ci *A, int ata) ; int cs_ci_fkeep (cs_ci *A, int (*fkeep) (int, int, cs_complex_t, void *), void *other) ; cs_complex_t cs_ci_house (cs_complex_t *x, double *beta, int n) ; int *cs_ci_maxtrans (const cs_ci *A, int seed) ; int *cs_ci_post (const int *parent, int n) ; cs_cid *cs_ci_scc (cs_ci *A) ; int cs_ci_scatter (const cs_ci *A, int j, cs_complex_t beta, int *w, cs_complex_t *x, int mark,cs_ci *C, int nz) ; int cs_ci_tdfs (int j, int k, int *head, const int *next, int *post, int *stack) ; int cs_ci_leaf (int i, int j, const int *first, int *maxfirst, int *prevleaf, int *ancestor, int *jleaf) ; int cs_ci_reach (cs_ci *G, const cs_ci *B, int k, int *xi, const int *pinv) ; int cs_ci_spsolve (cs_ci *L, const cs_ci *B, int k, int *xi, cs_complex_t *x, const int *pinv, int lo) ; int cs_ci_ereach (const cs_ci *A, int k, const int *parent, int *s, int *w) ; int *cs_ci_randperm (int n, int seed) ; /* utilities */ cs_cid *cs_ci_dalloc (int m, int n) ; cs_ci *cs_ci_done (cs_ci *C, void *w, void *x, int ok) ; int *cs_ci_idone (int *p, cs_ci *C, void *w, int ok) ; cs_cin *cs_ci_ndone (cs_cin *N, cs_ci *C, void *w, void *x, int ok) ; cs_cid *cs_ci_ddone (cs_cid *D, cs_ci *C, void *w, int ok) ; /* -------------------------------------------------------------------------- */ /* complex/UF_long version of CXSparse */ /* -------------------------------------------------------------------------- */ /* --- primary CSparse routines and data structures ------------------------- */ typedef struct cs_cl_sparse /* matrix in compressed-column or triplet form */ { UF_long nzmax ; /* maximum number of entries */ UF_long m ; /* number of rows */ UF_long n ; /* number of columns */ UF_long *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ UF_long *i ; /* row indices, size nzmax */ cs_complex_t *x ; /* numerical values, size nzmax */ UF_long nz ; /* # of entries in triplet matrix, -1 for compressed-col */ } cs_cl ; cs_cl *cs_cl_add (const cs_cl *A, const cs_cl *B, cs_complex_t alpha, cs_complex_t beta) ; UF_long cs_cl_cholsol (UF_long order, const cs_cl *A, cs_complex_t *b) ; UF_long cs_cl_dupl (cs_cl *A) ; UF_long cs_cl_entry (cs_cl *T, UF_long i, UF_long j, cs_complex_t x) ; UF_long cs_cl_lusol (UF_long order, const cs_cl *A, cs_complex_t *b, double tol) ; UF_long cs_cl_gaxpy (const cs_cl *A, const cs_complex_t *x, cs_complex_t *y) ; cs_cl *cs_cl_multiply (const cs_cl *A, const cs_cl *B) ; UF_long cs_cl_qrsol (UF_long order, const cs_cl *A, cs_complex_t *b) ; cs_cl *cs_cl_transpose (const cs_cl *A, UF_long values) ; cs_cl *cs_cl_compress (const cs_cl *T) ; double cs_cl_norm (const cs_cl *A) ; UF_long cs_cl_print (const cs_cl *A, UF_long brief) ; cs_cl *cs_cl_load (FILE *f) ; /* utilities */ void *cs_cl_calloc (UF_long n, size_t size) ; void *cs_cl_free (void *p) ; void *cs_cl_realloc (void *p, UF_long n, size_t size, UF_long *ok) ; cs_cl *cs_cl_spalloc (UF_long m, UF_long n, UF_long nzmax, UF_long values, UF_long t) ; cs_cl *cs_cl_spfree (cs_cl *A) ; UF_long cs_cl_sprealloc (cs_cl *A, UF_long nzmax) ; void *cs_cl_malloc (UF_long n, size_t size) ; /* --- secondary CSparse routines and data structures ----------------------- */ typedef struct cs_cl_symbolic /* symbolic Cholesky, LU, or QR analysis */ { UF_long *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ UF_long *q ; /* fill-reducing column permutation for LU and QR */ UF_long *parent ; /* elimination tree for Cholesky and QR */ UF_long *cp ; /* column pointers for Cholesky, row counts for QR */ UF_long *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ UF_long m2 ; /* # of rows for QR, after adding fictitious rows */ double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ double unz ; /* # entries in U for LU; in R for QR */ } cs_cls ; typedef struct cs_cl_numeric /* numeric Cholesky, LU, or QR factorization */ { cs_cl *L ; /* L for LU and Cholesky, V for QR */ cs_cl *U ; /* U for LU, r for QR, not used for Cholesky */ UF_long *pinv ; /* partial pivoting for LU */ double *B ; /* beta [0..n-1] for QR */ } cs_cln ; typedef struct cs_cl_dmperm_results /* cs_cl_dmperm or cs_cl_scc output */ { UF_long *p ; /* size m, row permutation */ UF_long *q ; /* size n, column permutation */ UF_long *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ UF_long *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ UF_long nb ; /* # of blocks in fine dmperm decomposition */ UF_long rr [5] ; /* coarse row decomposition */ UF_long cc [5] ; /* coarse column decomposition */ } cs_cld ; UF_long *cs_cl_amd (UF_long order, const cs_cl *A) ; cs_cln *cs_cl_chol (const cs_cl *A, const cs_cls *S) ; cs_cld *cs_cl_dmperm (const cs_cl *A, UF_long seed) ; UF_long cs_cl_droptol (cs_cl *A, double tol) ; UF_long cs_cl_dropzeros (cs_cl *A) ; UF_long cs_cl_happly (const cs_cl *V, UF_long i, double beta, cs_complex_t *x) ; UF_long cs_cl_ipvec (const UF_long *p, const cs_complex_t *b, cs_complex_t *x, UF_long n) ; UF_long cs_cl_lsolve (const cs_cl *L, cs_complex_t *x) ; UF_long cs_cl_ltsolve (const cs_cl *L, cs_complex_t *x) ; cs_cln *cs_cl_lu (const cs_cl *A, const cs_cls *S, double tol) ; cs_cl *cs_cl_permute (const cs_cl *A, const UF_long *pinv, const UF_long *q, UF_long values) ; UF_long *cs_cl_pinv (const UF_long *p, UF_long n) ; UF_long cs_cl_pvec (const UF_long *p, const cs_complex_t *b, cs_complex_t *x, UF_long n) ; cs_cln *cs_cl_qr (const cs_cl *A, const cs_cls *S) ; cs_cls *cs_cl_schol (UF_long order, const cs_cl *A) ; cs_cls *cs_cl_sqr (UF_long order, const cs_cl *A, UF_long qr) ; cs_cl *cs_cl_symperm (const cs_cl *A, const UF_long *pinv, UF_long values) ; UF_long cs_cl_usolve (const cs_cl *U, cs_complex_t *x) ; UF_long cs_cl_utsolve (const cs_cl *U, cs_complex_t *x) ; UF_long cs_cl_updown (cs_cl *L, UF_long sigma, const cs_cl *C, const UF_long *parent) ; /* utilities */ cs_cls *cs_cl_sfree (cs_cls *S) ; cs_cln *cs_cl_nfree (cs_cln *N) ; cs_cld *cs_cl_dfree (cs_cld *D) ; /* --- tertiary CSparse routines -------------------------------------------- */ UF_long *cs_cl_counts (const cs_cl *A, const UF_long *parent, const UF_long *post, UF_long ata) ; double cs_cl_cumsum (UF_long *p, UF_long *c, UF_long n) ; UF_long cs_cl_dfs (UF_long j, cs_cl *G, UF_long top, UF_long *xi, UF_long *pstack, const UF_long *pinv) ; UF_long *cs_cl_etree (const cs_cl *A, UF_long ata) ; UF_long cs_cl_fkeep (cs_cl *A, UF_long (*fkeep) (UF_long, UF_long, cs_complex_t, void *), void *other) ; cs_complex_t cs_cl_house (cs_complex_t *x, double *beta, UF_long n) ; UF_long *cs_cl_maxtrans (const cs_cl *A, UF_long seed) ; UF_long *cs_cl_post (const UF_long *parent, UF_long n) ; cs_cld *cs_cl_scc (cs_cl *A) ; UF_long cs_cl_scatter (const cs_cl *A, UF_long j, cs_complex_t beta, UF_long *w, cs_complex_t *x, UF_long mark,cs_cl *C, UF_long nz) ; UF_long cs_cl_tdfs (UF_long j, UF_long k, UF_long *head, const UF_long *next, UF_long *post, UF_long *stack) ; UF_long cs_cl_leaf (UF_long i, UF_long j, const UF_long *first, UF_long *maxfirst, UF_long *prevleaf, UF_long *ancestor, UF_long *jleaf) ; UF_long cs_cl_reach (cs_cl *G, const cs_cl *B, UF_long k, UF_long *xi, const UF_long *pinv) ; UF_long cs_cl_spsolve (cs_cl *L, const cs_cl *B, UF_long k, UF_long *xi, cs_complex_t *x, const UF_long *pinv, UF_long lo) ; UF_long cs_cl_ereach (const cs_cl *A, UF_long k, const UF_long *parent, UF_long *s, UF_long *w) ; UF_long *cs_cl_randperm (UF_long n, UF_long seed) ; /* utilities */ cs_cld *cs_cl_dalloc (UF_long m, UF_long n) ; cs_cl *cs_cl_done (cs_cl *C, void *w, void *x, UF_long ok) ; UF_long *cs_cl_idone (UF_long *p, cs_cl *C, void *w, UF_long ok) ; cs_cln *cs_cl_ndone (cs_cln *N, cs_cl *C, void *w, void *x, UF_long ok) ; cs_cld *cs_cl_ddone (cs_cld *D, cs_cl *C, void *w, UF_long ok) ; #endif /* -------------------------------------------------------------------------- */ /* Macros for constructing each version of CSparse */ /* -------------------------------------------------------------------------- */ #ifdef CS_LONG #define CS_INT UF_long #define CS_INT_MAX UF_long_max #define CS_ID UF_long_id #ifdef CS_COMPLEX #define CS_ENTRY cs_complex_t #define CS_NAME(nm) cs_cl ## nm #define cs cs_cl #else #define CS_ENTRY double #define CS_NAME(nm) cs_dl ## nm #define cs cs_dl #endif #else #define CS_INT int #define CS_INT_MAX INT_MAX #define CS_ID "%d" #ifdef CS_COMPLEX #define CS_ENTRY cs_complex_t #define CS_NAME(nm) cs_ci ## nm #define cs cs_ci #else #define CS_ENTRY double #define CS_NAME(nm) cs_di ## nm #define cs cs_di #endif #endif #ifdef CS_COMPLEX #define CS_REAL(x) creal(x) #define CS_IMAG(x) cimag(x) #define CS_CONJ(x) conj(x) #define CS_ABS(x) cabs(x) #else #define CS_REAL(x) (x) #define CS_IMAG(x) (0.) #define CS_CONJ(x) (x) #define CS_ABS(x) fabs(x) #endif #define CS_MAX(a,b) (((a) > (b)) ? (a) : (b)) #define CS_MIN(a,b) (((a) < (b)) ? (a) : (b)) #define CS_FLIP(i) (-(i)-2) #define CS_UNFLIP(i) (((i) < 0) ? CS_FLIP(i) : (i)) #define CS_MARKED(w,j) (w [j] < 0) #define CS_MARK(w,j) { w [j] = CS_FLIP (w [j]) ; } #define CS_CSC(A) (A && (A->nz == -1)) #define CS_TRIPLET(A) (A && (A->nz >= 0)) /* --- primary CSparse routines and data structures ------------------------- */ #define cs_add CS_NAME (_add) #define cs_cholsol CS_NAME (_cholsol) #define cs_dupl CS_NAME (_dupl) #define cs_entry CS_NAME (_entry) #define cs_lusol CS_NAME (_lusol) #define cs_gaxpy CS_NAME (_gaxpy) #define cs_multiply CS_NAME (_multiply) #define cs_qrsol CS_NAME (_qrsol) #define cs_transpose CS_NAME (_transpose) #define cs_compress CS_NAME (_compress) #define cs_norm CS_NAME (_norm) #define cs_print CS_NAME (_print) #define cs_load CS_NAME (_load) /* utilities */ #define cs_calloc CS_NAME (_calloc) #define cs_free CS_NAME (_free) #define cs_realloc CS_NAME (_realloc) #define cs_spalloc CS_NAME (_spalloc) #define cs_spfree CS_NAME (_spfree) #define cs_sprealloc CS_NAME (_sprealloc) #define cs_malloc CS_NAME (_malloc) /* --- secondary CSparse routines and data structures ----------------------- */ #define css CS_NAME (s) #define csn CS_NAME (n) #define csd CS_NAME (d) #define cs_amd CS_NAME (_amd) #define cs_chol CS_NAME (_chol) #define cs_dmperm CS_NAME (_dmperm) #define cs_droptol CS_NAME (_droptol) #define cs_dropzeros CS_NAME (_dropzeros) #define cs_happly CS_NAME (_happly) #define cs_ipvec CS_NAME (_ipvec) #define cs_lsolve CS_NAME (_lsolve) #define cs_ltsolve CS_NAME (_ltsolve) #define cs_lu CS_NAME (_lu) #define cs_permute CS_NAME (_permute) #define cs_pinv CS_NAME (_pinv) #define cs_pvec CS_NAME (_pvec) #define cs_qr CS_NAME (_qr) #define cs_schol CS_NAME (_schol) #define cs_sqr CS_NAME (_sqr) #define cs_symperm CS_NAME (_symperm) #define cs_usolve CS_NAME (_usolve) #define cs_utsolve CS_NAME (_utsolve) #define cs_updown CS_NAME (_updown) /* utilities */ #define cs_sfree CS_NAME (_sfree) #define cs_nfree CS_NAME (_nfree) #define cs_dfree CS_NAME (_dfree) /* --- tertiary CSparse routines -------------------------------------------- */ #define cs_counts CS_NAME (_counts) #define cs_cumsum CS_NAME (_cumsum) #define cs_dfs CS_NAME (_dfs) #define cs_etree CS_NAME (_etree) #define cs_fkeep CS_NAME (_fkeep) #define cs_house CS_NAME (_house) #define cs_invmatch CS_NAME (_invmatch) #define cs_maxtrans CS_NAME (_maxtrans) #define cs_post CS_NAME (_post) #define cs_scc CS_NAME (_scc) #define cs_scatter CS_NAME (_scatter) #define cs_tdfs CS_NAME (_tdfs) #define cs_reach CS_NAME (_reach) #define cs_spsolve CS_NAME (_spsolve) #define cs_ereach CS_NAME (_ereach) #define cs_randperm CS_NAME (_randperm) #define cs_leaf CS_NAME (_leaf) /* utilities */ #define cs_dalloc CS_NAME (_dalloc) #define cs_done CS_NAME (_done) #define cs_idone CS_NAME (_idone) #define cs_ndone CS_NAME (_ndone) #define cs_ddone CS_NAME (_ddone) /* -------------------------------------------------------------------------- */ /* Conversion routines */ /* -------------------------------------------------------------------------- */ #ifndef NCOMPLEX cs_di *cs_i_real (cs_ci *A, int real) ; cs_ci *cs_i_complex (cs_di *A, int real) ; cs_dl *cs_l_real (cs_cl *A, UF_long real) ; cs_cl *cs_l_complex (cs_dl *A, UF_long real) ; #endif #ifdef __cplusplus } #endif #endif igraph/src/cs/UFconfig.h0000644000176000001440000001021112325527073014622 0ustar ripleyusers/* ========================================================================== */ /* === UFconfig.h =========================================================== */ /* ========================================================================== */ /* Configuration file for SuiteSparse: a Suite of Sparse matrix packages * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others). * * UFconfig.h provides the definition of the long integer. On most systems, * a C program can be compiled in LP64 mode, in which long's and pointers are * both 64-bits, and int's are 32-bits. Windows 64, however, uses the LLP64 * model, in which int's and long's are 32-bits, and long long's and pointers * are 64-bits. * * SuiteSparse packages that include long integer versions are * intended for the LP64 mode. However, as a workaround for Windows 64 * (and perhaps other systems), the long integer can be redefined. * * If _WIN64 is defined, then the __int64 type is used instead of long. * * The long integer can also be defined at compile time. For example, this * could be added to UFconfig.mk: * * CFLAGS = -O -D'UF_long=long long' -D'UF_long_max=9223372036854775801' \ * -D'UF_long_id="%lld"' * * This file defines UF_long as either long (on all but _WIN64) or * __int64 on Windows 64. The intent is that a UF_long is always a 64-bit * integer in a 64-bit code. ptrdiff_t might be a better choice than long; * it is always the same size as a pointer. * * This file also defines the SUITESPARSE_VERSION and related definitions. * * Copyright (c) 2007, University of Florida. No licensing restrictions * apply to this file or to the UFconfig directory. Author: Timothy A. Davis. */ #ifndef _UFCONFIG_H #define _UFCONFIG_H #ifdef __cplusplus extern "C" { #endif #include /* ========================================================================== */ /* === UF_long ============================================================== */ /* ========================================================================== */ #ifndef UF_long #ifdef _WIN64 #define UF_long __int64 #define UF_long_max _I64_MAX #define UF_long_id "%I64d" #else #define UF_long long #define UF_long_max LONG_MAX #define UF_long_id "%ld" #endif #endif /* ========================================================================== */ /* === SuiteSparse version ================================================== */ /* ========================================================================== */ /* SuiteSparse is not a package itself, but a collection of packages, some of * which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD, * COLAMD, CAMD, and CCOLAMD, etc). A version number is provided here for the * collection itself. The versions of packages within each version of * SuiteSparse are meant to work together. Combining one packge from one * version of SuiteSparse, with another package from another version of * SuiteSparse, may or may not work. * * SuiteSparse Version 3.3.0 contains the following packages: * * AMD version 2.2.0 * CAMD version 2.2.0 * COLAMD version 2.7.1 * CCOLAMD version 2.7.1 * CHOLMOD version 1.7.1 * CSparse version 2.2.3 * CXSparse version 2.2.3 * KLU version 1.1.0 * BTF version 1.0.1 * LDL version 2.0.1 * UFconfig version number is the same as SuiteSparse * UMFPACK version 5.3.0 * RBio version 1.1.1 * UFcollection version 1.2.0 * LINFACTOR version 1.1.0 * MESHND version 1.1.1 * SSMULT version 2.0.0 * MATLAB_Tools no specific version number * SuiteSparseQR version 1.1.1 * * Other package dependencies: * BLAS required by CHOLMOD and UMFPACK * LAPACK required by CHOLMOD * METIS 4.0.1 required by CHOLMOD (optional) and KLU (optional) */ #define SUITESPARSE_DATE "Mar 24, 2009" #define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) #define SUITESPARSE_MAIN_VERSION 3 #define SUITESPARSE_SUB_VERSION 3 #define SUITESPARSE_SUBSUB_VERSION 0 #define SUITESPARSE_VERSION \ SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) #ifdef __cplusplus } #endif #endif igraph/src/igraph_statusbar.h0000644000176000001440000001044112325527073016064 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_STATUSBAR #define IGRAPH_STATUSBAR #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /** * \section about_status_handlers Status reporting * * * In addition to the possibility of reporting the progress of an * igraph computation via \ref igraph_progress(), it is also possible * to report simple status messages from within igraph functions, * without having to judge how much of the computation was performed * already. For this one needs to install a status handler function. * * * * Status handler functions must be of type \ref igraph_status_handler_t * and they can be install by a call to \ref igraph_set_status_handler(). * Currently there is a simple predefined status handler function, * called \ref igraph_status_handler_stderr(), but the user can define * new ones. * * * * Igraph functions report their status via a call to the * \ref IGRAPH_STATUS() or the \ref IGRAPH_STATUSF() macro. * */ /** * \typedef igraph_status_handler_t * * The type of the igraph status handler functions * \param message The status message. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. */ typedef int igraph_status_handler_t(const char *message, void *data); extern igraph_status_handler_t igraph_status_handler_stderr; igraph_status_handler_t * igraph_set_status_handler(igraph_status_handler_t new_handler); int igraph_status(const char *message, void *data); /** * \define IGRAPH_STATUS * Report the status of an igraph function. * * Typically this function is called only a handful of times from * an igraph function. E.g. if an algorithm has three major * steps, then it is logical to call it three times, to * signal the three major steps. * \param message The status message. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. * \return If the status handler returns with a value other than * \c IGRAPH_SUCCESS, then the function that called this * macro returns as well, with error code * \c IGRAPH_INTERRUPTED. */ #define IGRAPH_STATUS(message, data) \ do { \ if (igraph_status((message), (data)) != IGRAPH_SUCCESS) { \ IGRAPH_FINALLY_FREE(); \ return IGRAPH_INTERRUPTED; \ } \ } while (0) int igraph_statusf(const char *message, void *data, ...); /** * \define IGRAPH_STATUSF * Report the status from an igraph function * * This is the more flexible version of \ref IGRAPH_STATUS(), * having a printf-like syntax. As this macro takes variable * number of arguments, they must be all supplied as a single * argument, enclosed in parentheses. Then \ref igraph_statusf() * is called with the given arguments. * \param args The arguments to pass to \ref igraph_statusf(). * \return If the status handler returns with a value other than * \c IGRAPH_SUCCESS, then the function that called this * macro returns as well, with error code * \c IGRAPH_INTERRUPTED. */ #define IGRAPH_STATUSF(args) \ do { \ if (igraph_statusf args != IGRAPH_SUCCESS) { \ IGRAPH_FINALLY_FREE(); \ return IGRAPH_INTERRUPTED; \ } \ } while (0) __END_DECLS #endif igraph/src/drl_graph.h0000644000176000001440000001074512325527073014473 0ustar ripleyusers/* * Copyright 2007 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains * certain rights in this software. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sandia National Laboratories nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // The graph class contains the methods necessary to draw the // graph. It calls on the density server class to obtain // position and density information #include "DensityGrid.h" #include "igraph_layout.h" namespace drl { // layout schedule information struct layout_schedule { int iterations; float temperature; float attraction; float damping_mult; time_t time_elapsed; }; class graph { public: // Methods void init_parms ( int rand_seed, float edge_cut, float real_parm ); void init_parms ( const igraph_layout_drl_options_t *options ); void read_parms ( char *parms_file ); void read_real ( char *real_file ); int read_real ( const igraph_matrix_t *real_mat, const igraph_vector_bool_t *fixed); void scan_int ( char *filename ); void read_int ( char *file_name ); void draw_graph ( int int_out, char *coord_file ); int draw_graph (igraph_matrix_t *res); void write_coord ( const char *file_name ); void write_sim ( const char *file_name ); float get_tot_energy ( ); // Con/Decon graph( int proc_id, int tot_procs, char *int_file ); ~graph( ) { } graph( const igraph_t *igraph, const igraph_layout_drl_options_t *options, const igraph_vector_t *weights); private: // Methods int ReCompute ( ); void update_nodes ( ); float Compute_Node_Energy ( int node_ind ); void Solve_Analytic ( int node_ind, float &pos_x, float &pos_y ); void get_positions ( vector &node_indices, float return_positions[2*MAX_PROCS] ); void update_density ( vector &node_indices, float old_positions[2*MAX_PROCS], float new_positions[2*MAX_PROCS] ); void update_node_pos ( int node_ind, float old_positions[2*MAX_PROCS], float new_positions[2*MAX_PROCS] ); // MPI information int myid, num_procs; // graph decomposition information int num_nodes; // number of nodes in graph float highest_sim; // highest sim for normalization map id_catalog; // id_catalog[file id] = internal id map > neighbors; // neighbors of nodes on this proc. // graph layout information vector positions; DensityGrid density_server; // original VxOrd information int STAGE, iterations; float temperature, attraction, damping_mult; float min_edges, CUT_END, cut_length_end, cut_off_length, cut_rate; bool first_add, fine_first_add, fineDensity; // scheduling variables layout_schedule liquid; layout_schedule expansion; layout_schedule cooldown; layout_schedule crunch; layout_schedule simmer; // timing statistics time_t start_time, stop_time; // online clustering information int real_iterations; // number of iterations to hold .real input fixed int tot_iterations; int tot_expected_iterations; // for progress bar bool real_fixed; }; } // namespace drl igraph/src/foreign-ncol-parser.y0000644000176000001440000001020112325372071016405 0ustar ripleyusers/* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include "igraph_hacks_internal.h" #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_math.h" #include "igraph_memory.h" #include "igraph_error.h" #include "config.h" #include "foreign-ncol-header.h" #include "foreign-ncol-parser.h" #define yyscan_t void* int igraph_ncol_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_ncol_yyerror(YYLTYPE* locp, igraph_i_ncol_parsedata_t *context, const char *s); char *igraph_ncol_yyget_text (yyscan_t yyscanner ); int igraph_ncol_yyget_leng (yyscan_t yyscanner ); igraph_real_t igraph_ncol_get_number(const char *str, long int len); #define scanner context->scanner %} %pure-parser %output="y.tab.c" %name-prefix="igraph_ncol_yy" %defines %locations %error-verbose %parse-param { igraph_i_ncol_parsedata_t* context } %lex-param { void *scanner } %union { long int edgenum; double weightnum; } %type edgeid %type weight %token ALNUM %token NEWLINE %% input : /* empty */ | input NEWLINE | input edge ; edge : edgeid edgeid NEWLINE { igraph_vector_push_back(context->vector, $1); igraph_vector_push_back(context->vector, $2); igraph_vector_push_back(context->weights, 0); } | edgeid edgeid weight NEWLINE { igraph_vector_push_back(context->vector, $1); igraph_vector_push_back(context->vector, $2); igraph_vector_push_back(context->weights, $3); context->has_weights = 1; } ; edgeid : ALNUM { igraph_trie_get2(context->trie, igraph_ncol_yyget_text(scanner), igraph_ncol_yyget_leng(scanner), &$$); }; weight : ALNUM { $$=igraph_ncol_get_number(igraph_ncol_yyget_text(scanner), igraph_ncol_yyget_leng(scanner)); } ; %% int igraph_ncol_yyerror(YYLTYPE* locp, igraph_i_ncol_parsedata_t *context, const char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "Parse error in NCOL file, line %i (%s)", locp->first_line, s); return 0; } igraph_real_t igraph_ncol_get_number(const char *str, long int length) { igraph_real_t num; char *tmp=igraph_Calloc(length+1, char); strncpy(tmp, str, length); tmp[length]='\0'; sscanf(tmp, "%lf", &num); igraph_Free(tmp); return num; } igraph/src/RayTracer.h0000755000176000001440000000255612325527072014430 0ustar ripleyusers/** RayTraceCanvas.h */ #ifndef RAY_TRACER_H #define RAY_TRACER_H #include #include "Point.h" #include "Shape.h" #include "Color.h" #include "Light.h" namespace igraph { class Image { public: int width, height; double *red, *green, *blue, *trans; }; class RayTracer { public: RayTracer(); ~RayTracer(); void RayTrace(Image &result); void AddShape(Shape* pShape); void AddLight(Light* pLight); void BackgroundColor(const Color& rBackgroundColor); void EyePoint(const Point& rEyePoint); void AmbientColor(const Color& rAmbient); void AmbientIntensity(double vAmbientIntensity); private: Color Render(const Ray& rRay, bool vIsReflecting = false, const Shape* pReflectingFrom = 0 ); // vEyeRay should be true if the ray we are tracing is a ray from the eye, otherwise it should be false Shape* QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting = false, const Shape* pReflectingFrom = 0); double Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade); double Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight); Color mBackgroundColor; Color mAmbientColor; Point mEyePoint; Color mSpecularColor; double mAmbientIntensity; ShapeList* mpShapes; LightList* mpLights; int mRecursions; int mRecursionLimit; int mAntiAliasDetail; }; } // namespace igraph #endif igraph/src/igraph_sparsemat.h0000644000176000001440000002260612325527073016061 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_SPARSEMAT_H #define IGRAPH_SPARSEMAT_H #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_datatype.h" #include "igraph_arpack.h" #include struct cs_di_sparse; struct cs_di_symbolic; struct cs_di_numeric; typedef struct { struct cs_di_sparse *cs; } igraph_sparsemat_t; typedef struct { struct cs_di_symbolic *symbolic; } igraph_sparsemat_symbolic_t; typedef struct { struct cs_di_numeric *numeric; } igraph_sparsemat_numeric_t; typedef enum { IGRAPH_SPARSEMAT_TRIPLET, IGRAPH_SPARSEMAT_CC } igraph_sparsemat_type_t; typedef struct { igraph_sparsemat_t *mat; int pos; int col; } igraph_sparsemat_iterator_t; int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax); int igraph_sparsemat_copy(igraph_sparsemat_t *to, const igraph_sparsemat_t *from); void igraph_sparsemat_destroy(igraph_sparsemat_t *A); int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax); long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A); long int igraph_sparsemat_ncol(const igraph_sparsemat_t *B); igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A); igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A); igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A); int igraph_sparsemat_permute(const igraph_sparsemat_t *A, const igraph_vector_int_t *p, const igraph_vector_int_t *q, igraph_sparsemat_t *res); int igraph_sparsemat_index(const igraph_sparsemat_t *A, const igraph_vector_int_t *p, const igraph_vector_int_t *q, igraph_sparsemat_t *res, igraph_real_t *constres); int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, igraph_real_t elem); int igraph_sparsemat_compress(const igraph_sparsemat_t *A, igraph_sparsemat_t *res); int igraph_sparsemat_transpose(const igraph_sparsemat_t *A, igraph_sparsemat_t *res, int values); igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A); int igraph_sparsemat_dupl(igraph_sparsemat_t *A); int igraph_sparsemat_fkeep(igraph_sparsemat_t *A, int (*fkeep)(int, int, igraph_real_t, void*), void *other); int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A); int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol); int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_sparsemat_t *res); int igraph_sparsemat_add(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_real_t alpha, igraph_real_t beta, igraph_sparsemat_t *res); int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, const igraph_vector_t *x, igraph_vector_t *res); int igraph_sparsemat_lsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); int igraph_sparsemat_usolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); int igraph_sparsemat_utsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, int order); int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, int order, igraph_real_t tol); int igraph_sparsemat_print(const igraph_sparsemat_t *A, FILE *outstream); int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax, igraph_real_t value, igraph_bool_t compress); int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax, const igraph_vector_t *values, igraph_bool_t compress); int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed); int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed, const char *attr, igraph_bool_t loops); int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res); int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, const igraph_matrix_t *mat, igraph_real_t tol); int igraph_sparsemat_as_matrix(igraph_matrix_t *res, const igraph_sparsemat_t *spmat); typedef enum { IGRAPH_SPARSEMAT_SOLVE_LU, IGRAPH_SPARSEMAT_SOLVE_QR } igraph_sparsemat_solve_t; int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_sparsemat_solve_t solvemethod); int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_matrix_t *values, igraph_matrix_t *vectors); int igraph_sparsemat_lu(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din, double tol); int igraph_sparsemat_qr(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din); int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, const igraph_sparsemat_numeric_t *din, const igraph_vector_t *b, igraph_vector_t *res); int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, const igraph_sparsemat_numeric_t *din, const igraph_vector_t *b, igraph_vector_t *res); int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis); int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis); void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis); void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din); igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A); igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A); int igraph_sparsemat_minmax(igraph_sparsemat_t *A, igraph_real_t *min, igraph_real_t *max); long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A); long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, igraph_real_t tol); int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, igraph_vector_t *res); int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, igraph_vector_t *res); int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by); int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n); int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n); int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, long int ncol, int nzmax); int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A); int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x); int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x); int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, const igraph_vector_t *fact); int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, const igraph_vector_t *fact); int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, const igraph_matrix_t *B, igraph_matrix_t *res); int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, const igraph_sparsemat_t *B, igraph_matrix_t *res); int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, int *p, int *i, double *x, int nz); int igraph_sparsemat_sort(const igraph_sparsemat_t *A, igraph_sparsemat_t *sorted); int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A); int igraph_sparsemat_neg(igraph_sparsemat_t *A); int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, igraph_sparsemat_t *sparsemat); int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it); igraph_bool_t igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it); int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it); int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it); int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it); igraph_real_t igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it); int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it); #endif igraph/src/dmout.f0000644000176000001440000001357512325527073013663 0ustar ripleyusers*----------------------------------------------------------------------- * Routine: DMOUT * * Purpose: Real matrix output routine. * * Usage: CALL DMOUT (LOUT, M, N, A, LDA, IDIGIT, IFMT) * * Arguments * M - Number of rows of A. (Input) * N - Number of columns of A. (Input) * A - Real M by N matrix to be printed. (Input) * LDA - Leading dimension of A exactly as specified in the * dimension statement of the calling program. (Input) * IFMT - Format to be used in printing matrix A. (Input) * IDIGIT - Print up to IABS(IDIGIT) decimal digits per number. (In) * If IDIGIT .LT. 0, printing is done with 72 columns. * If IDIGIT .GT. 0, printing is done with 132 columns. * *----------------------------------------------------------------------- * SUBROUTINE IGRAPHDMOUT( LOUT, M, N, A, LDA, IDIGIT, IFMT ) * ... * ... SPECIFICATIONS FOR ARGUMENTS * ... * ... SPECIFICATIONS FOR LOCAL VARIABLES * .. Scalar Arguments .. CHARACTER*( * ) IFMT INTEGER IDIGIT, LDA, LOUT, M, N * .. * .. Array Arguments .. DOUBLE PRECISION A( LDA, * ) * .. * .. Local Scalars .. CHARACTER*80 LINE INTEGER I, J, K1, K2, LLL, NDIGIT * .. * .. Local Arrays .. CHARACTER ICOL( 3 ) * .. * .. Intrinsic Functions .. INTRINSIC LEN, MIN, MIN0 * .. * .. Data statements .. DATA ICOL( 1 ), ICOL( 2 ), ICOL( 3 ) / 'C', 'o', $ 'l' / * .. * .. Executable Statements .. * ... * ... FIRST EXECUTABLE STATEMENT * c$$$ LLL = MIN( LEN( IFMT ), 80 ) c$$$ DO 10 I = 1, LLL c$$$ LINE( I: I ) = '-' c$$$ 10 CONTINUE c$$$* c$$$ DO 20 I = LLL + 1, 80 c$$$ LINE( I: I ) = ' ' c$$$ 20 CONTINUE c$$$* c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) c$$$* c$$$ IF( M.LE.0 .OR. N.LE.0 .OR. LDA.LE.0 ) c$$$ $ RETURN c$$$ NDIGIT = IDIGIT c$$$ IF( IDIGIT.EQ.0 ) c$$$ $ NDIGIT = 4 c$$$* c$$$*======================================================================= c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT c$$$*======================================================================= c$$$* c$$$ IF( IDIGIT.LT.0 ) THEN c$$$ NDIGIT = -IDIGIT c$$$ IF( NDIGIT.LE.4 ) THEN c$$$ DO 40 K1 = 1, N, 5 c$$$ K2 = MIN0( N, K1+4 ) c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) c$$$ DO 30 I = 1, M c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) c$$$ 30 CONTINUE c$$$ 40 CONTINUE c$$$* c$$$ ELSE IF( NDIGIT.LE.6 ) THEN c$$$ DO 60 K1 = 1, N, 4 c$$$ K2 = MIN0( N, K1+3 ) c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) c$$$ DO 50 I = 1, M c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) c$$$ 50 CONTINUE c$$$ 60 CONTINUE c$$$* c$$$ ELSE IF( NDIGIT.LE.10 ) THEN c$$$ DO 80 K1 = 1, N, 3 c$$$ K2 = MIN0( N, K1+2 ) c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) c$$$ DO 70 I = 1, M c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) c$$$ 70 CONTINUE c$$$ 80 CONTINUE c$$$* c$$$ ELSE c$$$ DO 100 K1 = 1, N, 2 c$$$ K2 = MIN0( N, K1+1 ) c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) c$$$ DO 90 I = 1, M c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) c$$$ 90 CONTINUE c$$$ 100 CONTINUE c$$$ END IF c$$$* c$$$*======================================================================= c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT c$$$*======================================================================= c$$$* c$$$ ELSE c$$$ IF( NDIGIT.LE.4 ) THEN c$$$ DO 120 K1 = 1, N, 10 c$$$ K2 = MIN0( N, K1+9 ) c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) c$$$ DO 110 I = 1, M c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) c$$$ 110 CONTINUE c$$$ 120 CONTINUE c$$$* c$$$ ELSE IF( NDIGIT.LE.6 ) THEN c$$$ DO 140 K1 = 1, N, 8 c$$$ K2 = MIN0( N, K1+7 ) c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) c$$$ DO 130 I = 1, M c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) c$$$ 130 CONTINUE c$$$ 140 CONTINUE c$$$* c$$$ ELSE IF( NDIGIT.LE.10 ) THEN c$$$ DO 160 K1 = 1, N, 6 c$$$ K2 = MIN0( N, K1+5 ) c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) c$$$ DO 150 I = 1, M c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) c$$$ 150 CONTINUE c$$$ 160 CONTINUE c$$$* c$$$ ELSE c$$$ DO 180 K1 = 1, N, 5 c$$$ K2 = MIN0( N, K1+4 ) c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) c$$$ DO 170 I = 1, M c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) c$$$ 170 CONTINUE c$$$ 180 CONTINUE c$$$ END IF c$$$ END IF c$$$ WRITE( LOUT, FMT = 9990 ) c$$$* c$$$ 9998 FORMAT( 10X, 10( 4X, 3A1, I4, 1X ) ) c$$$ 9997 FORMAT( 10X, 8( 5X, 3A1, I4, 2X ) ) c$$$ 9996 FORMAT( 10X, 6( 7X, 3A1, I4, 4X ) ) c$$$ 9995 FORMAT( 10X, 5( 9X, 3A1, I4, 6X ) ) c$$$ 9994 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 10D12.3 ) c$$$ 9993 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 8D14.5 ) c$$$ 9992 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 6D18.9 ) c$$$ 9991 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 5D22.13 ) c$$$ 9990 FORMAT( 1X, ' ' ) * RETURN END igraph/src/glpssx01.c0000644000176000001440000006636312325527073014214 0ustar ripleyusers/* glpssx01.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #pragma clang diagnostic ignored "-Wunused-value" #endif #include "glpenv.h" #include "glpssx.h" #define xfault xerror /*---------------------------------------------------------------------- // ssx_create - create simplex solver workspace. // // This routine creates the workspace used by simplex solver routines, // and returns a pointer to it. // // Parameters m, n, and nnz specify, respectively, the number of rows, // columns, and non-zero constraint coefficients. // // This routine only allocates the memory for the workspace components, // so the workspace needs to be saturated by data. */ SSX *ssx_create(int m, int n, int nnz) { SSX *ssx; int i, j, k; if (m < 1) xfault("ssx_create: m = %d; invalid number of rows\n", m); if (n < 1) xfault("ssx_create: n = %d; invalid number of columns\n", n); if (nnz < 0) xfault("ssx_create: nnz = %d; invalid number of non-zero const" "raint coefficients\n", nnz); ssx = xmalloc(sizeof(SSX)); ssx->m = m; ssx->n = n; ssx->type = xcalloc(1+m+n, sizeof(int)); ssx->lb = xcalloc(1+m+n, sizeof(mpq_t)); for (k = 1; k <= m+n; k++) mpq_init(ssx->lb[k]); ssx->ub = xcalloc(1+m+n, sizeof(mpq_t)); for (k = 1; k <= m+n; k++) mpq_init(ssx->ub[k]); ssx->coef = xcalloc(1+m+n, sizeof(mpq_t)); for (k = 0; k <= m+n; k++) mpq_init(ssx->coef[k]); ssx->A_ptr = xcalloc(1+n+1, sizeof(int)); ssx->A_ptr[n+1] = nnz+1; ssx->A_ind = xcalloc(1+nnz, sizeof(int)); ssx->A_val = xcalloc(1+nnz, sizeof(mpq_t)); for (k = 1; k <= nnz; k++) mpq_init(ssx->A_val[k]); ssx->stat = xcalloc(1+m+n, sizeof(int)); ssx->Q_row = xcalloc(1+m+n, sizeof(int)); ssx->Q_col = xcalloc(1+m+n, sizeof(int)); ssx->binv = bfx_create_binv(); ssx->bbar = xcalloc(1+m, sizeof(mpq_t)); for (i = 0; i <= m; i++) mpq_init(ssx->bbar[i]); ssx->pi = xcalloc(1+m, sizeof(mpq_t)); for (i = 1; i <= m; i++) mpq_init(ssx->pi[i]); ssx->cbar = xcalloc(1+n, sizeof(mpq_t)); for (j = 1; j <= n; j++) mpq_init(ssx->cbar[j]); ssx->rho = xcalloc(1+m, sizeof(mpq_t)); for (i = 1; i <= m; i++) mpq_init(ssx->rho[i]); ssx->ap = xcalloc(1+n, sizeof(mpq_t)); for (j = 1; j <= n; j++) mpq_init(ssx->ap[j]); ssx->aq = xcalloc(1+m, sizeof(mpq_t)); for (i = 1; i <= m; i++) mpq_init(ssx->aq[i]); mpq_init(ssx->delta); return ssx; } /*---------------------------------------------------------------------- // ssx_factorize - factorize the current basis matrix. // // This routine computes factorization of the current basis matrix B // and returns the singularity flag. If the matrix B is non-singular, // the flag is zero, otherwise non-zero. */ static int basis_col(void *info, int j, int ind[], mpq_t val[]) { /* this auxiliary routine provides row indices and numeric values of non-zero elements in j-th column of the matrix B */ SSX *ssx = info; int m = ssx->m; int n = ssx->n; int *A_ptr = ssx->A_ptr; int *A_ind = ssx->A_ind; mpq_t *A_val = ssx->A_val; int *Q_col = ssx->Q_col; int k, len, ptr; xassert(1 <= j && j <= m); k = Q_col[j]; /* x[k] = xB[j] */ xassert(1 <= k && k <= m+n); /* j-th column of the matrix B is k-th column of the augmented constraint matrix (I | -A) */ if (k <= m) { /* it is a column of the unity matrix I */ len = 1, ind[1] = k, mpq_set_si(val[1], 1, 1); } else { /* it is a column of the original constraint matrix -A */ len = 0; for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) { len++; ind[len] = A_ind[ptr]; mpq_neg(val[len], A_val[ptr]); } } return len; } int ssx_factorize(SSX *ssx) { int ret; ret = bfx_factorize(ssx->binv, ssx->m, basis_col, ssx); return ret; } /*---------------------------------------------------------------------- // ssx_get_xNj - determine value of non-basic variable. // // This routine determines the value of non-basic variable xN[j] in the // current basic solution defined as follows: // // 0, if xN[j] is free variable // lN[j], if xN[j] is on its lower bound // uN[j], if xN[j] is on its upper bound // lN[j] = uN[j], if xN[j] is fixed variable // // where lN[j] and uN[j] are lower and upper bounds of xN[j]. */ void ssx_get_xNj(SSX *ssx, int j, mpq_t x) { int m = ssx->m; int n = ssx->n; mpq_t *lb = ssx->lb; mpq_t *ub = ssx->ub; int *stat = ssx->stat; int *Q_col = ssx->Q_col; int k; xassert(1 <= j && j <= n); k = Q_col[m+j]; /* x[k] = xN[j] */ xassert(1 <= k && k <= m+n); switch (stat[k]) { case SSX_NL: /* xN[j] is on its lower bound */ mpq_set(x, lb[k]); break; case SSX_NU: /* xN[j] is on its upper bound */ mpq_set(x, ub[k]); break; case SSX_NF: /* xN[j] is free variable */ mpq_set_si(x, 0, 1); break; case SSX_NS: /* xN[j] is fixed variable */ mpq_set(x, lb[k]); break; default: xassert(stat != stat); } return; } /*---------------------------------------------------------------------- // ssx_eval_bbar - compute values of basic variables. // // This routine computes values of basic variables xB in the current // basic solution as follows: // // beta = - inv(B) * N * xN, // // where B is the basis matrix, N is the matrix of non-basic columns, // xN is a vector of current values of non-basic variables. */ void ssx_eval_bbar(SSX *ssx) { int m = ssx->m; int n = ssx->n; mpq_t *coef = ssx->coef; int *A_ptr = ssx->A_ptr; int *A_ind = ssx->A_ind; mpq_t *A_val = ssx->A_val; int *Q_col = ssx->Q_col; mpq_t *bbar = ssx->bbar; int i, j, k, ptr; mpq_t x, temp; mpq_init(x); mpq_init(temp); /* bbar := 0 */ for (i = 1; i <= m; i++) mpq_set_si(bbar[i], 0, 1); /* bbar := - N * xN = - N[1] * xN[1] - ... - N[n] * xN[n] */ for (j = 1; j <= n; j++) { ssx_get_xNj(ssx, j, x); if (mpq_sgn(x) == 0) continue; k = Q_col[m+j]; /* x[k] = xN[j] */ if (k <= m) { /* N[j] is a column of the unity matrix I */ mpq_sub(bbar[k], bbar[k], x); } else { /* N[j] is a column of the original constraint matrix -A */ for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) { mpq_mul(temp, A_val[ptr], x); mpq_add(bbar[A_ind[ptr]], bbar[A_ind[ptr]], temp); } } } /* bbar := inv(B) * bbar */ bfx_ftran(ssx->binv, bbar, 0); #if 1 /* compute value of the objective function */ /* bbar[0] := c[0] */ mpq_set(bbar[0], coef[0]); /* bbar[0] := bbar[0] + sum{i in B} cB[i] * xB[i] */ for (i = 1; i <= m; i++) { k = Q_col[i]; /* x[k] = xB[i] */ if (mpq_sgn(coef[k]) == 0) continue; mpq_mul(temp, coef[k], bbar[i]); mpq_add(bbar[0], bbar[0], temp); } /* bbar[0] := bbar[0] + sum{j in N} cN[j] * xN[j] */ for (j = 1; j <= n; j++) { k = Q_col[m+j]; /* x[k] = xN[j] */ if (mpq_sgn(coef[k]) == 0) continue; ssx_get_xNj(ssx, j, x); mpq_mul(temp, coef[k], x); mpq_add(bbar[0], bbar[0], temp); } #endif mpq_clear(x); mpq_clear(temp); return; } /*---------------------------------------------------------------------- // ssx_eval_pi - compute values of simplex multipliers. // // This routine computes values of simplex multipliers (shadow prices) // pi in the current basic solution as follows: // // pi = inv(B') * cB, // // where B' is a matrix transposed to the basis matrix B, cB is a vector // of objective coefficients at basic variables xB. */ void ssx_eval_pi(SSX *ssx) { int m = ssx->m; mpq_t *coef = ssx->coef; int *Q_col = ssx->Q_col; mpq_t *pi = ssx->pi; int i; /* pi := cB */ for (i = 1; i <= m; i++) mpq_set(pi[i], coef[Q_col[i]]); /* pi := inv(B') * cB */ bfx_btran(ssx->binv, pi); return; } /*---------------------------------------------------------------------- // ssx_eval_dj - compute reduced cost of non-basic variable. // // This routine computes reduced cost d[j] of non-basic variable xN[j] // in the current basic solution as follows: // // d[j] = cN[j] - N[j] * pi, // // where cN[j] is an objective coefficient at xN[j], N[j] is a column // of the augmented constraint matrix (I | -A) corresponding to xN[j], // pi is the vector of simplex multipliers (shadow prices). */ void ssx_eval_dj(SSX *ssx, int j, mpq_t dj) { int m = ssx->m; int n = ssx->n; mpq_t *coef = ssx->coef; int *A_ptr = ssx->A_ptr; int *A_ind = ssx->A_ind; mpq_t *A_val = ssx->A_val; int *Q_col = ssx->Q_col; mpq_t *pi = ssx->pi; int k, ptr, end; mpq_t temp; mpq_init(temp); xassert(1 <= j && j <= n); k = Q_col[m+j]; /* x[k] = xN[j] */ xassert(1 <= k && k <= m+n); /* j-th column of the matrix N is k-th column of the augmented constraint matrix (I | -A) */ if (k <= m) { /* it is a column of the unity matrix I */ mpq_sub(dj, coef[k], pi[k]); } else { /* it is a column of the original constraint matrix -A */ mpq_set(dj, coef[k]); for (ptr = A_ptr[k-m], end = A_ptr[k-m+1]; ptr < end; ptr++) { mpq_mul(temp, A_val[ptr], pi[A_ind[ptr]]); mpq_add(dj, dj, temp); } } mpq_clear(temp); return; } /*---------------------------------------------------------------------- // ssx_eval_cbar - compute reduced costs of all non-basic variables. // // This routine computes the vector of reduced costs pi in the current // basic solution for all non-basic variables, including fixed ones. */ void ssx_eval_cbar(SSX *ssx) { int n = ssx->n; mpq_t *cbar = ssx->cbar; int j; for (j = 1; j <= n; j++) ssx_eval_dj(ssx, j, cbar[j]); return; } /*---------------------------------------------------------------------- // ssx_eval_rho - compute p-th row of the inverse. // // This routine computes p-th row of the matrix inv(B), where B is the // current basis matrix. // // p-th row of the inverse is computed using the following formula: // // rho = inv(B') * e[p], // // where B' is a matrix transposed to B, e[p] is a unity vector, which // contains one in p-th position. */ void ssx_eval_rho(SSX *ssx) { int m = ssx->m; int p = ssx->p; mpq_t *rho = ssx->rho; int i; xassert(1 <= p && p <= m); /* rho := 0 */ for (i = 1; i <= m; i++) mpq_set_si(rho[i], 0, 1); /* rho := e[p] */ mpq_set_si(rho[p], 1, 1); /* rho := inv(B') * rho */ bfx_btran(ssx->binv, rho); return; } /*---------------------------------------------------------------------- // ssx_eval_row - compute pivot row of the simplex table. // // This routine computes p-th (pivot) row of the current simplex table // A~ = - inv(B) * N using the following formula: // // A~[p] = - N' * inv(B') * e[p] = - N' * rho[p], // // where N' is a matrix transposed to the matrix N, rho[p] is p-th row // of the inverse inv(B). */ void ssx_eval_row(SSX *ssx) { int m = ssx->m; int n = ssx->n; int *A_ptr = ssx->A_ptr; int *A_ind = ssx->A_ind; mpq_t *A_val = ssx->A_val; int *Q_col = ssx->Q_col; mpq_t *rho = ssx->rho; mpq_t *ap = ssx->ap; int j, k, ptr; mpq_t temp; mpq_init(temp); for (j = 1; j <= n; j++) { /* ap[j] := - N'[j] * rho (inner product) */ k = Q_col[m+j]; /* x[k] = xN[j] */ if (k <= m) mpq_neg(ap[j], rho[k]); else { mpq_set_si(ap[j], 0, 1); for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) { mpq_mul(temp, A_val[ptr], rho[A_ind[ptr]]); mpq_add(ap[j], ap[j], temp); } } } mpq_clear(temp); return; } /*---------------------------------------------------------------------- // ssx_eval_col - compute pivot column of the simplex table. // // This routine computes q-th (pivot) column of the current simplex // table A~ = - inv(B) * N using the following formula: // // A~[q] = - inv(B) * N[q], // // where N[q] is q-th column of the matrix N corresponding to chosen // non-basic variable xN[q]. */ void ssx_eval_col(SSX *ssx) { int m = ssx->m; int n = ssx->n; int *A_ptr = ssx->A_ptr; int *A_ind = ssx->A_ind; mpq_t *A_val = ssx->A_val; int *Q_col = ssx->Q_col; int q = ssx->q; mpq_t *aq = ssx->aq; int i, k, ptr; xassert(1 <= q && q <= n); /* aq := 0 */ for (i = 1; i <= m; i++) mpq_set_si(aq[i], 0, 1); /* aq := N[q] */ k = Q_col[m+q]; /* x[k] = xN[q] */ if (k <= m) { /* N[q] is a column of the unity matrix I */ mpq_set_si(aq[k], 1, 1); } else { /* N[q] is a column of the original constraint matrix -A */ for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++) mpq_neg(aq[A_ind[ptr]], A_val[ptr]); } /* aq := inv(B) * aq */ bfx_ftran(ssx->binv, aq, 1); /* aq := - aq */ for (i = 1; i <= m; i++) mpq_neg(aq[i], aq[i]); return; } /*---------------------------------------------------------------------- // ssx_chuzc - choose pivot column. // // This routine chooses non-basic variable xN[q] whose reduced cost // indicates possible improving of the objective function to enter it // in the basis. // // Currently the standard (textbook) pricing is used, i.e. that // non-basic variable is preferred which has greatest reduced cost (in // magnitude). // // If xN[q] has been chosen, the routine stores its number q and also // sets the flag q_dir that indicates direction in which xN[q] has to // change (+1 means increasing, -1 means decreasing). // // If the choice cannot be made, because the current basic solution is // dual feasible, the routine sets the number q to 0. */ void ssx_chuzc(SSX *ssx) { int m = ssx->m; int n = ssx->n; int dir = (ssx->dir == SSX_MIN ? +1 : -1); int *Q_col = ssx->Q_col; int *stat = ssx->stat; mpq_t *cbar = ssx->cbar; int j, k, s, q, q_dir; double best, temp; /* nothing is chosen so far */ q = 0, q_dir = 0, best = 0.0; /* look through the list of non-basic variables */ for (j = 1; j <= n; j++) { k = Q_col[m+j]; /* x[k] = xN[j] */ s = dir * mpq_sgn(cbar[j]); if ((stat[k] == SSX_NF || stat[k] == SSX_NL) && s < 0 || (stat[k] == SSX_NF || stat[k] == SSX_NU) && s > 0) { /* reduced cost of xN[j] indicates possible improving of the objective function */ temp = fabs(mpq_get_d(cbar[j])); xassert(temp != 0.0); if (q == 0 || best < temp) q = j, q_dir = - s, best = temp; } } ssx->q = q, ssx->q_dir = q_dir; return; } /*---------------------------------------------------------------------- // ssx_chuzr - choose pivot row. // // This routine looks through elements of q-th column of the simplex // table and chooses basic variable xB[p] which should leave the basis. // // The choice is based on the standard (textbook) ratio test. // // If xB[p] has been chosen, the routine stores its number p and also // sets its non-basic status p_stat which should be assigned to xB[p] // when it has left the basis and become xN[q]. // // Special case p < 0 means that xN[q] is double-bounded variable and // it reaches its opposite bound before any basic variable does that, // so the current basis remains unchanged. // // If the choice cannot be made, because xN[q] can infinitely change in // the feasible direction, the routine sets the number p to 0. */ void ssx_chuzr(SSX *ssx) { int m = ssx->m; int n = ssx->n; int *type = ssx->type; mpq_t *lb = ssx->lb; mpq_t *ub = ssx->ub; int *Q_col = ssx->Q_col; mpq_t *bbar = ssx->bbar; int q = ssx->q; mpq_t *aq = ssx->aq; int q_dir = ssx->q_dir; int i, k, s, t, p, p_stat; mpq_t teta, temp; mpq_init(teta); mpq_init(temp); xassert(1 <= q && q <= n); xassert(q_dir == +1 || q_dir == -1); /* nothing is chosen so far */ p = 0, p_stat = 0; /* look through the list of basic variables */ for (i = 1; i <= m; i++) { s = q_dir * mpq_sgn(aq[i]); if (s < 0) { /* xB[i] decreases */ k = Q_col[i]; /* x[k] = xB[i] */ t = type[k]; if (t == SSX_LO || t == SSX_DB || t == SSX_FX) { /* xB[i] has finite lower bound */ mpq_sub(temp, bbar[i], lb[k]); mpq_div(temp, temp, aq[i]); mpq_abs(temp, temp); if (p == 0 || mpq_cmp(teta, temp) > 0) { p = i; p_stat = (t == SSX_FX ? SSX_NS : SSX_NL); mpq_set(teta, temp); } } } else if (s > 0) { /* xB[i] increases */ k = Q_col[i]; /* x[k] = xB[i] */ t = type[k]; if (t == SSX_UP || t == SSX_DB || t == SSX_FX) { /* xB[i] has finite upper bound */ mpq_sub(temp, bbar[i], ub[k]); mpq_div(temp, temp, aq[i]); mpq_abs(temp, temp); if (p == 0 || mpq_cmp(teta, temp) > 0) { p = i; p_stat = (t == SSX_FX ? SSX_NS : SSX_NU); mpq_set(teta, temp); } } } /* if something has been chosen and the ratio test indicates exact degeneracy, the search can be finished */ if (p != 0 && mpq_sgn(teta) == 0) break; } /* if xN[q] is double-bounded, check if it can reach its opposite bound before any basic variable */ k = Q_col[m+q]; /* x[k] = xN[q] */ if (type[k] == SSX_DB) { mpq_sub(temp, ub[k], lb[k]); if (p == 0 || mpq_cmp(teta, temp) > 0) { p = -1; p_stat = -1; mpq_set(teta, temp); } } ssx->p = p; ssx->p_stat = p_stat; /* if xB[p] has been chosen, determine its actual change in the adjacent basis (it has the same sign as q_dir) */ if (p != 0) { xassert(mpq_sgn(teta) >= 0); if (q_dir > 0) mpq_set(ssx->delta, teta); else mpq_neg(ssx->delta, teta); } mpq_clear(teta); mpq_clear(temp); return; } /*---------------------------------------------------------------------- // ssx_update_bbar - update values of basic variables. // // This routine recomputes the current values of basic variables for // the adjacent basis. // // The simplex table for the current basis is the following: // // xB[i] = sum{j in 1..n} alfa[i,j] * xN[q], i = 1,...,m // // therefore // // delta xB[i] = alfa[i,q] * delta xN[q], i = 1,...,m // // where delta xN[q] = xN.new[q] - xN[q] is the change of xN[q] in the // adjacent basis, and delta xB[i] = xB.new[i] - xB[i] is the change of // xB[i]. This gives formulae for recomputing values of xB[i]: // // xB.new[p] = xN[q] + delta xN[q] // // (because xN[q] becomes xB[p] in the adjacent basis), and // // xB.new[i] = xB[i] + alfa[i,q] * delta xN[q], i != p // // for other basic variables. */ void ssx_update_bbar(SSX *ssx) { int m = ssx->m; int n = ssx->n; mpq_t *bbar = ssx->bbar; mpq_t *cbar = ssx->cbar; int p = ssx->p; int q = ssx->q; mpq_t *aq = ssx->aq; int i; mpq_t temp; mpq_init(temp); xassert(1 <= q && q <= n); if (p < 0) { /* xN[q] is double-bounded and goes to its opposite bound */ /* nop */; } else { /* xN[q] becomes xB[p] in the adjacent basis */ /* xB.new[p] = xN[q] + delta xN[q] */ xassert(1 <= p && p <= m); ssx_get_xNj(ssx, q, temp); mpq_add(bbar[p], temp, ssx->delta); } /* update values of other basic variables depending on xN[q] */ for (i = 1; i <= m; i++) { if (i == p) continue; /* xB.new[i] = xB[i] + alfa[i,q] * delta xN[q] */ if (mpq_sgn(aq[i]) == 0) continue; mpq_mul(temp, aq[i], ssx->delta); mpq_add(bbar[i], bbar[i], temp); } #if 1 /* update value of the objective function */ /* z.new = z + d[q] * delta xN[q] */ mpq_mul(temp, cbar[q], ssx->delta); mpq_add(bbar[0], bbar[0], temp); #endif mpq_clear(temp); return; } /*---------------------------------------------------------------------- -- ssx_update_pi - update simplex multipliers. -- -- This routine recomputes the vector of simplex multipliers for the -- adjacent basis. */ void ssx_update_pi(SSX *ssx) { int m = ssx->m; int n = ssx->n; mpq_t *pi = ssx->pi; mpq_t *cbar = ssx->cbar; int p = ssx->p; int q = ssx->q; mpq_t *aq = ssx->aq; mpq_t *rho = ssx->rho; int i; mpq_t new_dq, temp; mpq_init(new_dq); mpq_init(temp); xassert(1 <= p && p <= m); xassert(1 <= q && q <= n); /* compute d[q] in the adjacent basis */ mpq_div(new_dq, cbar[q], aq[p]); /* update the vector of simplex multipliers */ for (i = 1; i <= m; i++) { if (mpq_sgn(rho[i]) == 0) continue; mpq_mul(temp, new_dq, rho[i]); mpq_sub(pi[i], pi[i], temp); } mpq_clear(new_dq); mpq_clear(temp); return; } /*---------------------------------------------------------------------- // ssx_update_cbar - update reduced costs of non-basic variables. // // This routine recomputes the vector of reduced costs of non-basic // variables for the adjacent basis. */ void ssx_update_cbar(SSX *ssx) { int m = ssx->m; int n = ssx->n; mpq_t *cbar = ssx->cbar; int p = ssx->p; int q = ssx->q; mpq_t *ap = ssx->ap; int j; mpq_t temp; mpq_init(temp); xassert(1 <= p && p <= m); xassert(1 <= q && q <= n); /* compute d[q] in the adjacent basis */ /* d.new[q] = d[q] / alfa[p,q] */ mpq_div(cbar[q], cbar[q], ap[q]); /* update reduced costs of other non-basic variables */ for (j = 1; j <= n; j++) { if (j == q) continue; /* d.new[j] = d[j] - (alfa[p,j] / alfa[p,q]) * d[q] */ if (mpq_sgn(ap[j]) == 0) continue; mpq_mul(temp, ap[j], cbar[q]); mpq_sub(cbar[j], cbar[j], temp); } mpq_clear(temp); return; } /*---------------------------------------------------------------------- // ssx_change_basis - change current basis to adjacent one. // // This routine changes the current basis to the adjacent one swapping // basic variable xB[p] and non-basic variable xN[q]. */ void ssx_change_basis(SSX *ssx) { int m = ssx->m; int n = ssx->n; int *type = ssx->type; int *stat = ssx->stat; int *Q_row = ssx->Q_row; int *Q_col = ssx->Q_col; int p = ssx->p; int q = ssx->q; int p_stat = ssx->p_stat; int k, kp, kq; if (p < 0) { /* special case: xN[q] goes to its opposite bound */ xassert(1 <= q && q <= n); k = Q_col[m+q]; /* x[k] = xN[q] */ xassert(type[k] == SSX_DB); switch (stat[k]) { case SSX_NL: stat[k] = SSX_NU; break; case SSX_NU: stat[k] = SSX_NL; break; default: xassert(stat != stat); } } else { /* xB[p] leaves the basis, xN[q] enters the basis */ xassert(1 <= p && p <= m); xassert(1 <= q && q <= n); kp = Q_col[p]; /* x[kp] = xB[p] */ kq = Q_col[m+q]; /* x[kq] = xN[q] */ /* check non-basic status of xB[p] which becomes xN[q] */ switch (type[kp]) { case SSX_FR: xassert(p_stat == SSX_NF); break; case SSX_LO: xassert(p_stat == SSX_NL); break; case SSX_UP: xassert(p_stat == SSX_NU); break; case SSX_DB: xassert(p_stat == SSX_NL || p_stat == SSX_NU); break; case SSX_FX: xassert(p_stat == SSX_NS); break; default: xassert(type != type); } /* swap xB[p] and xN[q] */ stat[kp] = (char)p_stat, stat[kq] = SSX_BS; Q_row[kp] = m+q, Q_row[kq] = p; Q_col[p] = kq, Q_col[m+q] = kp; /* update factorization of the basis matrix */ if (bfx_update(ssx->binv, p)) { if (ssx_factorize(ssx)) xassert(("Internal error: basis matrix is singular", 0)); } } return; } /*---------------------------------------------------------------------- // ssx_delete - delete simplex solver workspace. // // This routine deletes the simplex solver workspace freeing all the // memory allocated to this object. */ void ssx_delete(SSX *ssx) { int m = ssx->m; int n = ssx->n; int nnz = ssx->A_ptr[n+1]-1; int i, j, k; xfree(ssx->type); for (k = 1; k <= m+n; k++) mpq_clear(ssx->lb[k]); xfree(ssx->lb); for (k = 1; k <= m+n; k++) mpq_clear(ssx->ub[k]); xfree(ssx->ub); for (k = 0; k <= m+n; k++) mpq_clear(ssx->coef[k]); xfree(ssx->coef); xfree(ssx->A_ptr); xfree(ssx->A_ind); for (k = 1; k <= nnz; k++) mpq_clear(ssx->A_val[k]); xfree(ssx->A_val); xfree(ssx->stat); xfree(ssx->Q_row); xfree(ssx->Q_col); bfx_delete_binv(ssx->binv); for (i = 0; i <= m; i++) mpq_clear(ssx->bbar[i]); xfree(ssx->bbar); for (i = 1; i <= m; i++) mpq_clear(ssx->pi[i]); xfree(ssx->pi); for (j = 1; j <= n; j++) mpq_clear(ssx->cbar[j]); xfree(ssx->cbar); for (i = 1; i <= m; i++) mpq_clear(ssx->rho[i]); xfree(ssx->rho); for (j = 1; j <= n; j++) mpq_clear(ssx->ap[j]); xfree(ssx->ap); for (i = 1; i <= m; i++) mpq_clear(ssx->aq[i]); xfree(ssx->aq); mpq_clear(ssx->delta); xfree(ssx); return; } /* eof */ igraph/src/prpack_preprocessed_scc_graph.cpp0000644000176000001440000001600112325527074021123 0ustar ripleyusers#include "prpack_preprocessed_scc_graph.h" #include #include #include using namespace prpack; using namespace std; void prpack_preprocessed_scc_graph::initialize() { heads_inside = NULL; tails_inside = NULL; vals_inside = NULL; heads_outside = NULL; tails_outside = NULL; vals_outside = NULL; ii = NULL; d = NULL; num_outlinks = NULL; divisions = NULL; encoding = NULL; decoding = NULL; } void prpack_preprocessed_scc_graph::initialize_weighted(const prpack_base_graph* bg) { vals_inside = new double[num_es]; vals_outside = new double[num_es]; d = new double[num_vs]; fill(d, d + num_vs, 1); for (int comp_i = 0; comp_i < num_comps; ++comp_i) { const int start_i = divisions[comp_i]; const int end_i = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs; for (int i = start_i; i < end_i; ++i) { ii[i] = 0; const int decoded = decoding[i]; const int start_j = bg->tails[decoded]; const int end_j = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; tails_inside[i] = num_es_inside; tails_outside[i] = num_es_outside; for (int j = start_j; j < end_j; ++j) { const int h = encoding[bg->heads[j]]; if (h == i) { ii[i] += bg->vals[j]; } else { if (start_i <= h && h < end_i) { heads_inside[num_es_inside] = h; vals_inside[num_es_inside] = bg->vals[j]; ++num_es_inside; } else { heads_outside[num_es_outside] = h; vals_outside[num_es_outside] = bg->vals[j]; ++num_es_outside; } } d[h] -= bg->vals[j]; } } } } void prpack_preprocessed_scc_graph::initialize_unweighted(const prpack_base_graph* bg) { num_outlinks = new double[num_vs]; fill(num_outlinks, num_outlinks + num_vs, 0); for (int comp_i = 0; comp_i < num_comps; ++comp_i) { const int start_i = divisions[comp_i]; const int end_i = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs; for (int i = start_i; i < end_i; ++i) { ii[i] = 0; const int decoded = decoding[i]; const int start_j = bg->tails[decoded]; const int end_j = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; tails_inside[i] = num_es_inside; tails_outside[i] = num_es_outside; for (int j = start_j; j < end_j; ++j) { const int h = encoding[bg->heads[j]]; if (h == i) { ++ii[i]; } else { if (start_i <= h && h < end_i) heads_inside[num_es_inside++] = h; else heads_outside[num_es_outside++] = h; } ++num_outlinks[h]; } } } for (int i = 0; i < num_vs; ++i) { if (num_outlinks[i] == 0) num_outlinks[i] = -1; ii[i] /= num_outlinks[i]; } } prpack_preprocessed_scc_graph::prpack_preprocessed_scc_graph(const prpack_base_graph* bg) { initialize(); // initialize instance variables num_vs = bg->num_vs; num_es = bg->num_es - bg->num_self_es; // initialize Tarjan's algorithm variables num_comps = 0; int mn = 0; // the number of vertices seen so far int sz = 0; // size of st int decoding_i = 0; // size of decoding currently filled in decoding = new int[num_vs]; int* scc = new int[num_vs]; // the strongly connected component this vertex is in int* low = new int[num_vs]; // the lowest index this vertex can reach int* num = new int[num_vs]; // the index of this vertex in the dfs traversal int* st = new int[num_vs]; // a stack for the dfs memset(num, -1, num_vs*sizeof(num[0])); memset(scc, -1, num_vs*sizeof(scc[0])); int* cs1 = new int[num_vs]; // call stack variable for dfs int* cs2 = new int[num_vs]; // call stack variable for dfs // run iterative Tarjan's algorithm for (int root = 0; root < num_vs; ++root) { if (num[root] != -1) continue; int csz = 1; cs1[0] = root; cs2[0] = bg->tails[root]; // dfs while (csz) { const int p = cs1[csz - 1]; // node we're dfs-ing on int& it = cs2[csz - 1]; // iteration of the for loop if (it == bg->tails[p]) { low[p] = num[p] = mn++; st[sz++] = p; } else { low[p] = min(low[p], low[bg->heads[it - 1]]); } bool done = false; int end_it = (p + 1 != num_vs) ? bg->tails[p + 1] : bg->num_es; for (; it < end_it; ++it) { int h = bg->heads[it]; if (scc[h] == -1) { if (num[h] == -1) { // dfs(h, p); cs1[csz] = h; cs2[csz++] = bg->tails[h]; ++it; done = true; break; } low[p] = min(low[p], low[h]); } } if (done) continue; // if p is the first explored vertex of a scc if (low[p] == num[p]) { cs1[num_vs - 1 - num_comps] = decoding_i; while (scc[p] != num_comps) { scc[st[--sz]] = num_comps; decoding[decoding_i++] = st[sz]; } ++num_comps; } --csz; } } // set up other instance variables divisions = new int[num_comps]; divisions[0] = 0; for (int i = 1; i < num_comps; ++i) divisions[i] = cs1[num_vs - 1 - i]; encoding = num; for (int i = 0; i < num_vs; ++i) encoding[decoding[i]] = i; // fill in inside and outside instance variables ii = new double[num_vs]; tails_inside = cs1; heads_inside = new int[num_es]; tails_outside = cs2; heads_outside = new int[num_es]; num_es_inside = num_es_outside = 0; // continue initialization based off of weightedness if (bg->vals != NULL) initialize_weighted(bg); else initialize_unweighted(bg); // free memory // do not free num <==> encoding // do not free cs1 <==> tails_inside // do not free cs2 <==> tails_outside delete[] scc; delete[] low; delete[] st; } prpack_preprocessed_scc_graph::~prpack_preprocessed_scc_graph() { delete[] heads_inside; delete[] tails_inside; delete[] vals_inside; delete[] heads_outside; delete[] tails_outside; delete[] vals_outside; delete[] ii; delete[] d; delete[] num_outlinks; delete[] divisions; delete[] encoding; delete[] decoding; } igraph/src/glpios07.c0000644000176000001440000004523112325527073014166 0ustar ripleyusers/* glpios07.c (mixed cover cut generator) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpios.h" /*---------------------------------------------------------------------- -- COVER INEQUALITIES -- -- Consider the set of feasible solutions to 0-1 knapsack problem: -- -- sum a[j]*x[j] <= b, (1) -- j in J -- -- x[j] is binary, (2) -- -- where, wlog, we assume that a[j] > 0 (since 0-1 variables can be -- complemented) and a[j] <= b (since a[j] > b implies x[j] = 0). -- -- A set C within J is called a cover if -- -- sum a[j] > b. (3) -- j in C -- -- For any cover C the inequality -- -- sum x[j] <= |C| - 1 (4) -- j in C -- -- is called a cover inequality and is valid for (1)-(2). -- -- MIXED COVER INEQUALITIES -- -- Consider the set of feasible solutions to mixed knapsack problem: -- -- sum a[j]*x[j] + y <= b, (5) -- j in J -- -- x[j] is binary, (6) -- -- 0 <= y <= u is continuous, (7) -- -- where again we assume that a[j] > 0. -- -- Let C within J be some set. From (1)-(4) it follows that -- -- sum a[j] > b - y (8) -- j in C -- -- implies -- -- sum x[j] <= |C| - 1. (9) -- j in C -- -- Thus, we need to modify the inequality (9) in such a way that it be -- a constraint only if the condition (8) is satisfied. -- -- Consider the following inequality: -- -- sum x[j] <= |C| - t. (10) -- j in C -- -- If 0 < t <= 1, then (10) is equivalent to (9), because all x[j] are -- binary variables. On the other hand, if t <= 0, (10) being satisfied -- for any values of x[j] is not a constraint. -- -- Let -- -- t' = sum a[j] + y - b. (11) -- j in C -- -- It is understood that the condition t' > 0 is equivalent to (8). -- Besides, from (6)-(7) it follows that t' has an implied upper bound: -- -- t'max = sum a[j] + u - b. (12) -- j in C -- -- This allows to express the parameter t having desired properties: -- -- t = t' / t'max. (13) -- -- In fact, t <= 1 by definition, and t > 0 being equivalent to t' > 0 -- is equivalent to (8). -- -- Thus, the inequality (10), where t is given by formula (13) is valid -- for (5)-(7). -- -- Note that if u = 0, then y = 0, so t = 1, and the conditions (8) and -- (10) is transformed to the conditions (3) and (4). -- -- GENERATING MIXED COVER CUTS -- -- To generate a mixed cover cut in the form (10) we need to find such -- set C which satisfies to the inequality (8) and for which, in turn, -- the inequality (10) is violated in the current point. -- -- Substituting t from (13) to (10) gives: -- -- 1 -- sum x[j] <= |C| - ----- (sum a[j] + y - b), (14) -- j in C t'max j in C -- -- and finally we have the cut inequality in the standard form: -- -- sum x[j] + alfa * y <= beta, (15) -- j in C -- -- where: -- -- alfa = 1 / t'max, (16) -- -- beta = |C| - alfa * (sum a[j] - b). (17) -- j in C */ #if 1 #define MAXTRY 1000 #else #define MAXTRY 10000 #endif static int cover2(int n, double a[], double b, double u, double x[], double y, int cov[], double *_alfa, double *_beta) { /* try to generate mixed cover cut using two-element cover */ int i, j, try = 0, ret = 0; double eps, alfa, beta, temp, rmax = 0.001; eps = 0.001 * (1.0 + fabs(b)); for (i = 0+1; i <= n; i++) for (j = i+1; j <= n; j++) { /* C = {i, j} */ try++; if (try > MAXTRY) goto done; /* check if condition (8) is satisfied */ if (a[i] + a[j] + y > b + eps) { /* compute parameters for inequality (15) */ temp = a[i] + a[j] - b; alfa = 1.0 / (temp + u); beta = 2.0 - alfa * temp; /* compute violation of inequality (15) */ temp = x[i] + x[j] + alfa * y - beta; /* choose C providing maximum violation */ if (rmax < temp) { rmax = temp; cov[1] = i; cov[2] = j; *_alfa = alfa; *_beta = beta; ret = 1; } } } done: return ret; } static int cover3(int n, double a[], double b, double u, double x[], double y, int cov[], double *_alfa, double *_beta) { /* try to generate mixed cover cut using three-element cover */ int i, j, k, try = 0, ret = 0; double eps, alfa, beta, temp, rmax = 0.001; eps = 0.001 * (1.0 + fabs(b)); for (i = 0+1; i <= n; i++) for (j = i+1; j <= n; j++) for (k = j+1; k <= n; k++) { /* C = {i, j, k} */ try++; if (try > MAXTRY) goto done; /* check if condition (8) is satisfied */ if (a[i] + a[j] + a[k] + y > b + eps) { /* compute parameters for inequality (15) */ temp = a[i] + a[j] + a[k] - b; alfa = 1.0 / (temp + u); beta = 3.0 - alfa * temp; /* compute violation of inequality (15) */ temp = x[i] + x[j] + x[k] + alfa * y - beta; /* choose C providing maximum violation */ if (rmax < temp) { rmax = temp; cov[1] = i; cov[2] = j; cov[3] = k; *_alfa = alfa; *_beta = beta; ret = 1; } } } done: return ret; } static int cover4(int n, double a[], double b, double u, double x[], double y, int cov[], double *_alfa, double *_beta) { /* try to generate mixed cover cut using four-element cover */ int i, j, k, l, try = 0, ret = 0; double eps, alfa, beta, temp, rmax = 0.001; eps = 0.001 * (1.0 + fabs(b)); for (i = 0+1; i <= n; i++) for (j = i+1; j <= n; j++) for (k = j+1; k <= n; k++) for (l = k+1; l <= n; l++) { /* C = {i, j, k, l} */ try++; if (try > MAXTRY) goto done; /* check if condition (8) is satisfied */ if (a[i] + a[j] + a[k] + a[l] + y > b + eps) { /* compute parameters for inequality (15) */ temp = a[i] + a[j] + a[k] + a[l] - b; alfa = 1.0 / (temp + u); beta = 4.0 - alfa * temp; /* compute violation of inequality (15) */ temp = x[i] + x[j] + x[k] + x[l] + alfa * y - beta; /* choose C providing maximum violation */ if (rmax < temp) { rmax = temp; cov[1] = i; cov[2] = j; cov[3] = k; cov[4] = l; *_alfa = alfa; *_beta = beta; ret = 1; } } } done: return ret; } static int cover(int n, double a[], double b, double u, double x[], double y, int cov[], double *alfa, double *beta) { /* try to generate mixed cover cut; input (see (5)): n is the number of binary variables; a[1:n] are coefficients at binary variables; b is the right-hand side; u is upper bound of continuous variable; x[1:n] are values of binary variables at current point; y is value of continuous variable at current point; output (see (15), (16), (17)): cov[1:r] are indices of binary variables included in cover C, where r is the set cardinality returned on exit; alfa coefficient at continuous variable; beta is the right-hand side; */ int j; /* perform some sanity checks */ xassert(n >= 2); for (j = 1; j <= n; j++) xassert(a[j] > 0.0); #if 1 /* ??? */ xassert(b > -1e-5); #else xassert(b > 0.0); #endif xassert(u >= 0.0); for (j = 1; j <= n; j++) xassert(0.0 <= x[j] && x[j] <= 1.0); xassert(0.0 <= y && y <= u); /* try to generate mixed cover cut */ if (cover2(n, a, b, u, x, y, cov, alfa, beta)) return 2; if (cover3(n, a, b, u, x, y, cov, alfa, beta)) return 3; if (cover4(n, a, b, u, x, y, cov, alfa, beta)) return 4; return 0; } /*---------------------------------------------------------------------- -- lpx_cover_cut - generate mixed cover cut. -- -- SYNOPSIS -- -- #include "glplpx.h" -- int lpx_cover_cut(LPX *lp, int len, int ind[], double val[], -- double work[]); -- -- DESCRIPTION -- -- The routine lpx_cover_cut generates a mixed cover cut for a given -- row of the MIP problem. -- -- The given row of the MIP problem should be explicitly specified in -- the form: -- -- sum{j in J} a[j]*x[j] <= b. (1) -- -- On entry indices (ordinal numbers) of structural variables, which -- have non-zero constraint coefficients, should be placed in locations -- ind[1], ..., ind[len], and corresponding constraint coefficients -- should be placed in locations val[1], ..., val[len]. The right-hand -- side b should be stored in location val[0]. -- -- The working array work should have at least nb locations, where nb -- is the number of binary variables in (1). -- -- The routine generates a mixed cover cut in the same form as (1) and -- stores the cut coefficients and right-hand side in the same way as -- just described above. -- -- RETURNS -- -- If the cutting plane has been successfully generated, the routine -- returns 1 <= len' <= n, which is the number of non-zero coefficients -- in the inequality constraint. Otherwise, the routine returns zero. */ static int lpx_cover_cut(LPX *lp, int len, int ind[], double val[], double work[]) { int cov[1+4], j, k, nb, newlen, r; double f_min, f_max, alfa, beta, u, *x = work, y; /* substitute and remove fixed variables */ newlen = 0; for (k = 1; k <= len; k++) { j = ind[k]; if (lpx_get_col_type(lp, j) == LPX_FX) val[0] -= val[k] * lpx_get_col_lb(lp, j); else { newlen++; ind[newlen] = ind[k]; val[newlen] = val[k]; } } len = newlen; /* move binary variables to the beginning of the list so that elements 1, 2, ..., nb correspond to binary variables, and elements nb+1, nb+2, ..., len correspond to rest variables */ nb = 0; for (k = 1; k <= len; k++) { j = ind[k]; if (lpx_get_col_kind(lp, j) == LPX_IV && lpx_get_col_type(lp, j) == LPX_DB && lpx_get_col_lb(lp, j) == 0.0 && lpx_get_col_ub(lp, j) == 1.0) { /* binary variable */ int ind_k; double val_k; nb++; ind_k = ind[nb], val_k = val[nb]; ind[nb] = ind[k], val[nb] = val[k]; ind[k] = ind_k, val[k] = val_k; } } /* now the specified row has the form: sum a[j]*x[j] + sum a[j]*y[j] <= b, where x[j] are binary variables, y[j] are rest variables */ /* at least two binary variables are needed */ if (nb < 2) return 0; /* compute implied lower and upper bounds for sum a[j]*y[j] */ f_min = f_max = 0.0; for (k = nb+1; k <= len; k++) { j = ind[k]; /* both bounds must be finite */ if (lpx_get_col_type(lp, j) != LPX_DB) return 0; if (val[k] > 0.0) { f_min += val[k] * lpx_get_col_lb(lp, j); f_max += val[k] * lpx_get_col_ub(lp, j); } else { f_min += val[k] * lpx_get_col_ub(lp, j); f_max += val[k] * lpx_get_col_lb(lp, j); } } /* sum a[j]*x[j] + sum a[j]*y[j] <= b ===> sum a[j]*x[j] + (sum a[j]*y[j] - f_min) <= b - f_min ===> sum a[j]*x[j] + y <= b - f_min, where y = sum a[j]*y[j] - f_min; note that 0 <= y <= u, u = f_max - f_min */ /* determine upper bound of y */ u = f_max - f_min; /* determine value of y at the current point */ y = 0.0; for (k = nb+1; k <= len; k++) { j = ind[k]; y += val[k] * lpx_get_col_prim(lp, j); } y -= f_min; if (y < 0.0) y = 0.0; if (y > u) y = u; /* modify the right-hand side b */ val[0] -= f_min; /* now the transformed row has the form: sum a[j]*x[j] + y <= b, where 0 <= y <= u */ /* determine values of x[j] at the current point */ for (k = 1; k <= nb; k++) { j = ind[k]; x[k] = lpx_get_col_prim(lp, j); if (x[k] < 0.0) x[k] = 0.0; if (x[k] > 1.0) x[k] = 1.0; } /* if a[j] < 0, replace x[j] by its complement 1 - x'[j] */ for (k = 1; k <= nb; k++) { if (val[k] < 0.0) { ind[k] = - ind[k]; val[k] = - val[k]; val[0] += val[k]; x[k] = 1.0 - x[k]; } } /* try to generate a mixed cover cut for the transformed row */ r = cover(nb, val, val[0], u, x, y, cov, &alfa, &beta); if (r == 0) return 0; xassert(2 <= r && r <= 4); /* now the cut is in the form: sum{j in C} x[j] + alfa * y <= beta */ /* store the right-hand side beta */ ind[0] = 0, val[0] = beta; /* restore the original ordinal numbers of x[j] */ for (j = 1; j <= r; j++) cov[j] = ind[cov[j]]; /* store cut coefficients at binary variables complementing back the variables having negative row coefficients */ xassert(r <= nb); for (k = 1; k <= r; k++) { if (cov[k] > 0) { ind[k] = +cov[k]; val[k] = +1.0; } else { ind[k] = -cov[k]; val[k] = -1.0; val[0] -= 1.0; } } /* substitute y = sum a[j]*y[j] - f_min */ for (k = nb+1; k <= len; k++) { r++; ind[r] = ind[k]; val[r] = alfa * val[k]; } val[0] += alfa * f_min; xassert(r <= len); len = r; return len; } /*---------------------------------------------------------------------- -- lpx_eval_row - compute explictily specified row. -- -- SYNOPSIS -- -- #include "glplpx.h" -- double lpx_eval_row(LPX *lp, int len, int ind[], double val[]); -- -- DESCRIPTION -- -- The routine lpx_eval_row computes the primal value of an explicitly -- specified row using current values of structural variables. -- -- The explicitly specified row may be thought as a linear form: -- -- y = a[1]*x[m+1] + a[2]*x[m+2] + ... + a[n]*x[m+n], -- -- where y is an auxiliary variable for this row, a[j] are coefficients -- of the linear form, x[m+j] are structural variables. -- -- On entry column indices and numerical values of non-zero elements of -- the row should be stored in locations ind[1], ..., ind[len] and -- val[1], ..., val[len], where len is the number of non-zero elements. -- The array ind and val are not changed on exit. -- -- RETURNS -- -- The routine returns a computed value of y, the auxiliary variable of -- the specified row. */ static double lpx_eval_row(LPX *lp, int len, int ind[], double val[]) { int n = lpx_get_num_cols(lp); int j, k; double sum = 0.0; if (len < 0) xerror("lpx_eval_row: len = %d; invalid row length\n", len); for (k = 1; k <= len; k++) { j = ind[k]; if (!(1 <= j && j <= n)) xerror("lpx_eval_row: j = %d; column number out of range\n", j); sum += val[k] * lpx_get_col_prim(lp, j); } return sum; } /*********************************************************************** * NAME * * ios_cov_gen - generate mixed cover cuts * * SYNOPSIS * * #include "glpios.h" * void ios_cov_gen(glp_tree *tree); * * DESCRIPTION * * The routine ios_cov_gen generates mixed cover cuts for the current * point and adds them to the cut pool. */ void ios_cov_gen(glp_tree *tree) { glp_prob *prob = tree->mip; int m = lpx_get_num_rows(prob); int n = lpx_get_num_cols(prob); int i, k, type, kase, len, *ind; double r, *val, *work; xassert(lpx_get_status(prob) == LPX_OPT); /* allocate working arrays */ ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); work = xcalloc(1+n, sizeof(double)); /* look through all rows */ for (i = 1; i <= m; i++) for (kase = 1; kase <= 2; kase++) { type = lpx_get_row_type(prob, i); if (kase == 1) { /* consider rows of '<=' type */ if (!(type == LPX_UP || type == LPX_DB)) continue; len = lpx_get_mat_row(prob, i, ind, val); val[0] = lpx_get_row_ub(prob, i); } else { /* consider rows of '>=' type */ if (!(type == LPX_LO || type == LPX_DB)) continue; len = lpx_get_mat_row(prob, i, ind, val); for (k = 1; k <= len; k++) val[k] = - val[k]; val[0] = - lpx_get_row_lb(prob, i); } /* generate mixed cover cut: sum{j in J} a[j] * x[j] <= b */ len = lpx_cover_cut(prob, len, ind, val, work); if (len == 0) continue; /* at the current point the cut inequality is violated, i.e. sum{j in J} a[j] * x[j] - b > 0 */ r = lpx_eval_row(prob, len, ind, val) - val[0]; if (r < 1e-3) continue; /* add the cut to the cut pool */ glp_ios_add_row(tree, NULL, GLP_RF_COV, 0, len, ind, val, GLP_UP, val[0]); } /* free working arrays */ xfree(ind); xfree(val); xfree(work); return; } /* eof */ igraph/src/foreign-dl-parser.y0000644000176000001440000002255312325372071016066 0ustar ripleyusers/* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "config.h" #include "igraph_hacks_internal.h" #include "igraph_math.h" #include "igraph_types_internal.h" #include "foreign-dl-header.h" #include "foreign-dl-parser.h" #include #define yyscan_t void* int igraph_dl_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, const char *s); char *igraph_dl_yyget_text (yyscan_t yyscanner ); int igraph_dl_yyget_leng (yyscan_t yyscanner ); int igraph_i_dl_add_str(char *newstr, int length, igraph_i_dl_parsedata_t *context); int igraph_i_dl_add_edge(long int from, long int to, igraph_i_dl_parsedata_t *context); int igraph_i_dl_add_edge_w(long int from, long int to, igraph_real_t weight, igraph_i_dl_parsedata_t *context); extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); #define scanner context->scanner %} %pure-parser %output="y.tab.c" %name-prefix="igraph_dl_yy" %defines %locations %error-verbose %parse-param { igraph_i_dl_parsedata_t* context } %lex-param { void* scanner } %union { long int integer; igraph_real_t real; }; %type integer elabel; %type weight; %token NUM %token NEWLINE %token DL %token NEQ %token DATA %token LABELS %token LABELSEMBEDDED %token FORMATFULLMATRIX %token FORMATEDGELIST1 %token FORMATNODELIST1 %token DIGIT %token LABEL %token EOFF %% input: DL NEQ integer NEWLINE rest trail eof { context->n=$3; }; trail: | trail newline; eof: | EOFF; rest: formfullmatrix { context->type=IGRAPH_DL_MATRIX; } | edgelist1 { context->type=IGRAPH_DL_EDGELIST1; } | nodelist1 { context->type=IGRAPH_DL_NODELIST1; } ; formfullmatrix: FORMATFULLMATRIX newline fullmatrix {} | fullmatrix {} ; newline: | NEWLINE ; fullmatrix: DATA newline fullmatrixdata { } | LABELS newline labels newline DATA newline fullmatrixdata { } | LABELSEMBEDDED newline DATA newline labeledfullmatrixdata { } ; labels: {} /* nothing, empty matrix */ | labels newline LABEL { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner), context); } ; fullmatrixdata: {} | fullmatrixdata zerooneseq NEWLINE { context->from += 1; context->to = 0; } ; zerooneseq: | zerooneseq zeroone { } ; zeroone: DIGIT { if (igraph_dl_yyget_text(scanner)[0]=='1') { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->from)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->to)); } context->to += 1; } ; labeledfullmatrixdata: reallabeledfullmatrixdata {} ; reallabeledfullmatrixdata: labelseq NEWLINE labeledmatrixlines {} ; labelseq: | labelseq newline label ; label: LABEL { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner), context); }; labeledmatrixlines: labeledmatrixline { context->from += 1; context->to = 0; } | labeledmatrixlines labeledmatrixline { context->from += 1; context->to = 0; }; labeledmatrixline: LABEL zerooneseq NEWLINE { } ; /*-----------------------------------------------------------*/ edgelist1: FORMATEDGELIST1 newline edgelist1rest {} ; edgelist1rest: DATA edgelist1data {} | LABELS newline labels newline DATA newline edgelist1data {} | LABELSEMBEDDED newline DATA newline labelededgelist1data {} | LABELS newline labels newline LABELSEMBEDDED newline DATA newline labelededgelist1data {} | LABELSEMBEDDED newline LABELS newline labels newline DATA newline labelededgelist1data {} ; edgelist1data: {} /* nothing, empty graph */ | edgelist1data edgelist1dataline {} ; edgelist1dataline: integer integer weight NEWLINE { igraph_i_dl_add_edge_w($1-1, $2-1, $3, context); } | integer integer NEWLINE { igraph_i_dl_add_edge($1-1, $2-1, context); } ; integer: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner)); }; labelededgelist1data: {} /* nothing, empty graph */ | labelededgelist1data labelededgelist1dataline {} ; labelededgelist1dataline: elabel elabel weight NEWLINE { igraph_i_dl_add_edge_w($1, $2, $3, context); } | elabel elabel NEWLINE { igraph_i_dl_add_edge($1, $2, context); }; weight: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner)); }; elabel: LABEL { /* Copy label list to trie, if needed */ if (igraph_strvector_size(&context->labels) != 0) { long int i, id, n=igraph_strvector_size(&context->labels); for (i=0; itrie, STR(context->labels, i), &id); } igraph_strvector_clear(&context->labels); } igraph_trie_get2(&context->trie, igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner), &$$); }; /*-----------------------------------------------------------*/ nodelist1: FORMATNODELIST1 newline nodelist1rest {} ; nodelist1rest: DATA nodelist1data {} | LABELS newline labels newline DATA newline nodelist1data {} | LABELSEMBEDDED newline DATA newline labelednodelist1data {} | LABELS newline labels newline LABELSEMBEDDED newline DATA newline labelednodelist1data {} | LABELSEMBEDDED newline LABELS newline labels newline DATA newline labelednodelist1data {} ; nodelist1data: {} /* nothing, empty graph */ | nodelist1data nodelist1dataline {} ; nodelist1dataline: from tolist NEWLINE {} ; from: NUM { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), igraph_dl_yyget_leng(scanner)); } ; tolist: {} | tolist integer { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->from-1)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2-1)); } ; labelednodelist1data: {} /* nothing, empty graph */ | labelednodelist1data labelednodelist1dataline {} ; labelednodelist1dataline: fromelabel labeltolist NEWLINE { } ; fromelabel: elabel { context->from=$1; }; labeltolist: | labeltolist elabel { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->from)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2)); } ; %% int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, const char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, "%s in line %i", s, locp->first_line); return 0; } int igraph_i_dl_add_str(char *newstr, int length, igraph_i_dl_parsedata_t *context) { int tmp=newstr[length]; newstr[length]='\0'; IGRAPH_CHECK(igraph_strvector_add(&context->labels, newstr)); newstr[length]=tmp; return 0; } int igraph_i_dl_add_edge(long int from, long int to, igraph_i_dl_parsedata_t *context) { IGRAPH_CHECK(igraph_vector_push_back(&context->edges, from)); IGRAPH_CHECK(igraph_vector_push_back(&context->edges, to)); return 0; } int igraph_i_dl_add_edge_w(long int from, long int to, igraph_real_t weight, igraph_i_dl_parsedata_t *context) { long int n=igraph_vector_size(&context->weights); long int n2=igraph_vector_size(&context->edges)/2; if (n != n2) { igraph_vector_resize(&context->weights, n2); for (; nweights)[n]=IGRAPH_NAN; } } IGRAPH_CHECK(igraph_i_dl_add_edge(from, to, context)); IGRAPH_CHECK(igraph_vector_push_back(&context->weights, weight)); return 0; } igraph/src/glpmpl04.c0000644000176000001440000013445112325527073014164 0ustar ripleyusers/* glpmpl04.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsometimes-uninitialized" #endif #define _GLPSTD_ERRNO #define _GLPSTD_STDIO #include "glpmpl.h" #define xfault xerror #define dmp_create_poolx(size) dmp_create_pool() /**********************************************************************/ /* * * GENERATING AND POSTSOLVING MODEL * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- alloc_content - allocate content arrays for all model objects. -- -- This routine allocates content arrays for all existing model objects -- and thereby finalizes creating model. -- -- This routine must be called immediately after reading model section, -- i.e. before reading data section or generating model. */ void alloc_content(MPL *mpl) { STATEMENT *stmt; /* walk through all model statements */ for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) { switch (stmt->type) { case A_SET: /* model set */ xassert(stmt->u.set->array == NULL); stmt->u.set->array = create_array(mpl, A_ELEMSET, stmt->u.set->dim); break; case A_PARAMETER: /* model parameter */ xassert(stmt->u.par->array == NULL); switch (stmt->u.par->type) { case A_NUMERIC: case A_INTEGER: case A_BINARY: stmt->u.par->array = create_array(mpl, A_NUMERIC, stmt->u.par->dim); break; case A_SYMBOLIC: stmt->u.par->array = create_array(mpl, A_SYMBOLIC, stmt->u.par->dim); break; default: xassert(stmt != stmt); } break; case A_VARIABLE: /* model variable */ xassert(stmt->u.var->array == NULL); stmt->u.var->array = create_array(mpl, A_ELEMVAR, stmt->u.var->dim); break; case A_CONSTRAINT: /* model constraint/objective */ xassert(stmt->u.con->array == NULL); stmt->u.con->array = create_array(mpl, A_ELEMCON, stmt->u.con->dim); break; #if 1 /* 11/II-2008 */ case A_TABLE: #endif case A_SOLVE: case A_CHECK: case A_DISPLAY: case A_PRINTF: case A_FOR: /* functional statements have no content array */ break; default: xassert(stmt != stmt); } } return; } /*---------------------------------------------------------------------- -- generate_model - generate model. -- -- This routine executes the model statements which precede the solve -- statement. */ void generate_model(MPL *mpl) { STATEMENT *stmt; xassert(!mpl->flag_p); for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) { execute_statement(mpl, stmt); if (mpl->stmt->type == A_SOLVE) break; } mpl->stmt = stmt; return; } /*---------------------------------------------------------------------- -- build_problem - build problem instance. -- -- This routine builds lists of rows and columns for problem instance, -- which corresponds to the generated model. */ void build_problem(MPL *mpl) { STATEMENT *stmt; MEMBER *memb; VARIABLE *v; CONSTRAINT *c; FORMULA *t; int i, j; xassert(mpl->m == 0); xassert(mpl->n == 0); xassert(mpl->row == NULL); xassert(mpl->col == NULL); /* check that all elemental variables has zero column numbers */ for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) { if (stmt->type == A_VARIABLE) { v = stmt->u.var; for (memb = v->array->head; memb != NULL; memb = memb->next) xassert(memb->value.var->j == 0); } } /* assign row numbers to elemental constraints and objectives */ for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) { if (stmt->type == A_CONSTRAINT) { c = stmt->u.con; for (memb = c->array->head; memb != NULL; memb = memb->next) { xassert(memb->value.con->i == 0); memb->value.con->i = ++mpl->m; /* walk through linear form and mark elemental variables, which are referenced at least once */ for (t = memb->value.con->form; t != NULL; t = t->next) { xassert(t->var != NULL); t->var->memb->value.var->j = -1; } } } } /* assign column numbers to marked elemental variables */ for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) { if (stmt->type == A_VARIABLE) { v = stmt->u.var; for (memb = v->array->head; memb != NULL; memb = memb->next) if (memb->value.var->j != 0) memb->value.var->j = ++mpl->n; } } /* build list of rows */ mpl->row = xcalloc(1+mpl->m, sizeof(ELEMCON *)); for (i = 1; i <= mpl->m; i++) mpl->row[i] = NULL; for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) { if (stmt->type == A_CONSTRAINT) { c = stmt->u.con; for (memb = c->array->head; memb != NULL; memb = memb->next) { i = memb->value.con->i; xassert(1 <= i && i <= mpl->m); xassert(mpl->row[i] == NULL); mpl->row[i] = memb->value.con; } } } for (i = 1; i <= mpl->m; i++) xassert(mpl->row[i] != NULL); /* build list of columns */ mpl->col = xcalloc(1+mpl->n, sizeof(ELEMVAR *)); for (j = 1; j <= mpl->n; j++) mpl->col[j] = NULL; for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) { if (stmt->type == A_VARIABLE) { v = stmt->u.var; for (memb = v->array->head; memb != NULL; memb = memb->next) { j = memb->value.var->j; if (j == 0) continue; xassert(1 <= j && j <= mpl->n); xassert(mpl->col[j] == NULL); mpl->col[j] = memb->value.var; } } } for (j = 1; j <= mpl->n; j++) xassert(mpl->col[j] != NULL); return; } /*---------------------------------------------------------------------- -- postsolve_model - postsolve model. -- -- This routine executes the model statements which follow the solve -- statement. */ void postsolve_model(MPL *mpl) { STATEMENT *stmt; xassert(!mpl->flag_p); mpl->flag_p = 1; for (stmt = mpl->stmt; stmt != NULL; stmt = stmt->next) execute_statement(mpl, stmt); mpl->stmt = NULL; return; } /*---------------------------------------------------------------------- -- clean_model - clean model content. -- -- This routine cleans the model content that assumes deleting all stuff -- dynamically allocated on generating/postsolving phase. -- -- Actually cleaning model content is not needed. This function is used -- mainly to be sure that there were no logical errors on using dynamic -- memory pools during the generation phase. -- -- NOTE: This routine must not be called if any errors were detected on -- the generation phase. */ void clean_model(MPL *mpl) { STATEMENT *stmt; for (stmt = mpl->model; stmt != NULL; stmt = stmt->next) clean_statement(mpl, stmt); /* check that all atoms have been returned to their pools */ if (dmp_in_use(mpl->strings).lo != 0) error(mpl, "internal logic error: %d string segment(s) were lo" "st", dmp_in_use(mpl->strings).lo); if (dmp_in_use(mpl->symbols).lo != 0) error(mpl, "internal logic error: %d symbol(s) were lost", dmp_in_use(mpl->symbols).lo); if (dmp_in_use(mpl->tuples).lo != 0) error(mpl, "internal logic error: %d n-tuple component(s) were" " lost", dmp_in_use(mpl->tuples).lo); if (dmp_in_use(mpl->arrays).lo != 0) error(mpl, "internal logic error: %d array(s) were lost", dmp_in_use(mpl->arrays).lo); if (dmp_in_use(mpl->members).lo != 0) error(mpl, "internal logic error: %d array member(s) were lost" , dmp_in_use(mpl->members).lo); if (dmp_in_use(mpl->elemvars).lo != 0) error(mpl, "internal logic error: %d elemental variable(s) wer" "e lost", dmp_in_use(mpl->elemvars).lo); if (dmp_in_use(mpl->formulae).lo != 0) error(mpl, "internal logic error: %d linear term(s) were lost", dmp_in_use(mpl->formulae).lo); if (dmp_in_use(mpl->elemcons).lo != 0) error(mpl, "internal logic error: %d elemental constraint(s) w" "ere lost", dmp_in_use(mpl->elemcons).lo); return; } /**********************************************************************/ /* * * INPUT/OUTPUT * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- open_input - open input text file. -- -- This routine opens the input text file for scanning. */ void open_input(MPL *mpl, char *file) { mpl->line = 0; mpl->c = '\n'; mpl->token = 0; mpl->imlen = 0; mpl->image[0] = '\0'; mpl->value = 0.0; mpl->b_token = T_EOF; mpl->b_imlen = 0; mpl->b_image[0] = '\0'; mpl->b_value = 0.0; mpl->f_dots = 0; mpl->f_scan = 0; mpl->f_token = 0; mpl->f_imlen = 0; mpl->f_image[0] = '\0'; mpl->f_value = 0.0; memset(mpl->context, ' ', CONTEXT_SIZE); mpl->c_ptr = 0; xassert(mpl->in_fp == NULL); mpl->in_fp = xfopen(file, "r"); if (mpl->in_fp == NULL) error(mpl, "unable to open %s - %s", file, xerrmsg()); mpl->in_file = file; /* scan the very first character */ get_char(mpl); /* scan the very first token */ get_token(mpl); return; } /*---------------------------------------------------------------------- -- read_char - read next character from input text file. -- -- This routine returns a next ASCII character read from the input text -- file. If the end of file has been reached, EOF is returned. */ int read_char(MPL *mpl) { int c; xassert(mpl->in_fp != NULL); c = xfgetc(mpl->in_fp); if (c < 0) { if (xferror(mpl->in_fp)) error(mpl, "read error on %s - %s", mpl->in_file, xerrmsg()); c = EOF; } return c; } /*---------------------------------------------------------------------- -- close_input - close input text file. -- -- This routine closes the input text file. */ void close_input(MPL *mpl) { xassert(mpl->in_fp != NULL); xfclose(mpl->in_fp); mpl->in_fp = NULL; mpl->in_file = NULL; return; } /*---------------------------------------------------------------------- -- open_output - open output text file. -- -- This routine opens the output text file for writing data produced by -- display and printf statements. */ void open_output(MPL *mpl, char *file) { xassert(mpl->out_fp == NULL); /* if (file == NULL) */ /* { file = ""; */ /* mpl->out_fp = (void *)stdout; */ /* } */ /* else */ { mpl->out_fp = xfopen(file, "w"); if (mpl->out_fp == NULL) error(mpl, "unable to create %s - %s", file, xerrmsg()); } mpl->out_file = xmalloc(strlen(file)+1); strcpy(mpl->out_file, file); return; } /*---------------------------------------------------------------------- -- write_char - write next character to output text file. -- -- This routine writes an ASCII character to the output text file. */ void write_char(MPL *mpl, int c) { xassert(mpl->out_fp != NULL); /* if (mpl->out_fp == (void *)stdout) */ /* xprintf("%c", c); */ /* else */ xfprintf(mpl->out_fp, "%c", c); return; } /*---------------------------------------------------------------------- -- write_text - format and write text to output text file. -- -- This routine formats a text using the format control string and then -- writes this text to the output text file. */ void write_text(MPL *mpl, char *fmt, ...) { va_list arg; char buf[OUTBUF_SIZE], *c; va_start(arg, fmt); vsprintf(buf, fmt, arg); xassert(strlen(buf) < sizeof(buf)); va_end(arg); for (c = buf; *c != '\0'; c++) write_char(mpl, *c); return; } /*---------------------------------------------------------------------- -- flush_output - finalize writing data to output text file. -- -- This routine finalizes writing data to the output text file. */ void flush_output(MPL *mpl) { xassert(mpl->out_fp != NULL); /* if (mpl->out_fp != (void *)stdout) */ { xfflush(mpl->out_fp); if (xferror(mpl->out_fp)) error(mpl, "write error on %s - %s", mpl->out_file, xerrmsg()); } return; } /**********************************************************************/ /* * * SOLVER INTERFACE * * */ /**********************************************************************/ /*---------------------------------------------------------------------- -- error - print error message and terminate model processing. -- -- This routine formats and prints an error message and then terminates -- model processing. */ void error(MPL *mpl, char *fmt, ...) { va_list arg; char msg[4095+1]; va_start(arg, fmt); vsprintf(msg, fmt, arg); xassert(strlen(msg) < sizeof(msg)); va_end(arg); switch (mpl->phase) { case 1: case 2: /* translation phase */ xprintf("%s:%d: %s\n", mpl->in_file == NULL ? "(unknown)" : mpl->in_file, mpl->line, msg); print_context(mpl); break; case 3: /* generation/postsolve phase */ xprintf("%s:%d: %s\n", mpl->mod_file == NULL ? "(unknown)" : mpl->mod_file, mpl->stmt == NULL ? 0 : mpl->stmt->line, msg); break; default: xassert(mpl != mpl); } mpl->phase = 4; longjmp(mpl->jump, 1); /* no return */ } /*---------------------------------------------------------------------- -- warning - print warning message and continue model processing. -- -- This routine formats and prints a warning message and returns to the -- calling program. */ void warning(MPL *mpl, char *fmt, ...) { va_list arg; char msg[4095+1]; va_start(arg, fmt); vsprintf(msg, fmt, arg); xassert(strlen(msg) < sizeof(msg)); va_end(arg); switch (mpl->phase) { case 1: case 2: /* translation phase */ xprintf("%s:%d: warning: %s\n", mpl->in_file == NULL ? "(unknown)" : mpl->in_file, mpl->line, msg); break; case 3: /* generation/postsolve phase */ xprintf("%s:%d: warning: %s\n", mpl->mod_file == NULL ? "(unknown)" : mpl->mod_file, mpl->stmt == NULL ? 0 : mpl->stmt->line, msg); break; default: xassert(mpl != mpl); } return; } /*---------------------------------------------------------------------- -- mpl_initialize - create and initialize translator database. -- -- *Synopsis* -- -- #include "glpmpl.h" -- MPL *mpl_initialize(void); -- -- *Description* -- -- The routine mpl_initialize creates and initializes the database used -- by the GNU MathProg translator. -- -- *Returns* -- -- The routine returns a pointer to the database created. */ MPL *mpl_initialize(void) { MPL *mpl; mpl = xmalloc(sizeof(MPL)); /* scanning segment */ mpl->line = 0; mpl->c = 0; mpl->token = 0; mpl->imlen = 0; mpl->image = xcalloc(MAX_LENGTH+1, sizeof(char)); mpl->image[0] = '\0'; mpl->value = 0.0; mpl->b_token = 0; mpl->b_imlen = 0; mpl->b_image = xcalloc(MAX_LENGTH+1, sizeof(char)); mpl->b_image[0] = '\0'; mpl->b_value = 0.0; mpl->f_dots = 0; mpl->f_scan = 0; mpl->f_token = 0; mpl->f_imlen = 0; mpl->f_image = xcalloc(MAX_LENGTH+1, sizeof(char)); mpl->f_image[0] = '\0'; mpl->f_value = 0.0; mpl->context = xcalloc(CONTEXT_SIZE, sizeof(char)); memset(mpl->context, ' ', CONTEXT_SIZE); mpl->c_ptr = 0; mpl->flag_d = 0; /* translating segment */ mpl->pool = dmp_create_poolx(0); mpl->tree = avl_create_tree(avl_strcmp, NULL); mpl->model = NULL; mpl->flag_x = 0; mpl->as_within = 0; mpl->as_in = 0; mpl->as_binary = 0; mpl->flag_s = 0; /* common segment */ mpl->strings = dmp_create_poolx(sizeof(STRING)); mpl->symbols = dmp_create_poolx(sizeof(SYMBOL)); mpl->tuples = dmp_create_poolx(sizeof(TUPLE)); mpl->arrays = dmp_create_poolx(sizeof(ARRAY)); mpl->members = dmp_create_poolx(sizeof(MEMBER)); mpl->elemvars = dmp_create_poolx(sizeof(ELEMVAR)); mpl->formulae = dmp_create_poolx(sizeof(FORMULA)); mpl->elemcons = dmp_create_poolx(sizeof(ELEMCON)); mpl->a_list = NULL; mpl->sym_buf = xcalloc(255+1, sizeof(char)); mpl->sym_buf[0] = '\0'; mpl->tup_buf = xcalloc(255+1, sizeof(char)); mpl->tup_buf[0] = '\0'; /* generating/postsolving segment */ mpl->rand = rng_create_rand(); mpl->flag_p = 0; mpl->stmt = NULL; #if 1 /* 11/II-2008 */ mpl->dca = NULL; #endif mpl->m = 0; mpl->n = 0; mpl->row = NULL; mpl->col = NULL; /* input/output segment */ mpl->in_fp = NULL; mpl->in_file = NULL; mpl->out_fp = NULL; mpl->out_file = NULL; mpl->prt_fp = NULL; mpl->prt_file = NULL; /* solver interface segment */ if (setjmp(mpl->jump)) xassert(mpl != mpl); mpl->phase = 0; mpl->mod_file = NULL; mpl->mpl_buf = xcalloc(255+1, sizeof(char)); mpl->mpl_buf[0] = '\0'; return mpl; } /*---------------------------------------------------------------------- -- mpl_read_model - read model section and optional data section. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_read_model(MPL *mpl, char *file, int skip_data); -- -- *Description* -- -- The routine mpl_read_model reads model section and optionally data -- section, which may follow the model section, from the text file, -- whose name is the character string file, performs translating model -- statements and data blocks, and stores all the information in the -- translator database. -- -- The parameter skip_data is a flag. If the input file contains the -- data section and this flag is set, the data section is not read as -- if there were no data section and a warning message is issued. This -- allows reading the data section from another input file. -- -- This routine should be called once after the routine mpl_initialize -- and before other API routines. -- -- *Returns* -- -- The routine mpl_read_model returns one the following codes: -- -- 1 - translation successful. The input text file contains only model -- section. In this case the calling program may call the routine -- mpl_read_data to read data section from another file. -- 2 - translation successful. The input text file contains both model -- and data section. -- 4 - processing failed due to some errors. In this case the calling -- program should call the routine mpl_terminate to terminate model -- processing. */ int mpl_read_model(MPL *mpl, char *file, int skip_data) { if (mpl->phase != 0) xfault("mpl_read_model: invalid call sequence\n"); if (file == NULL) xfault("mpl_read_model: no input filename specified\n"); /* set up error handler */ if (setjmp(mpl->jump)) goto done; /* translate model section */ mpl->phase = 1; xprintf("Reading model section from %s...\n", file); open_input(mpl, file); model_section(mpl); if (mpl->model == NULL) error(mpl, "empty model section not allowed"); /* save name of the input text file containing model section for error diagnostics during the generation phase */ mpl->mod_file = xcalloc(strlen(file)+1, sizeof(char)); strcpy(mpl->mod_file, mpl->in_file); /* allocate content arrays for all model objects */ alloc_content(mpl); /* optional data section may begin with the keyword 'data' */ if (is_keyword(mpl, "data")) { if (skip_data) { warning(mpl, "data section ignored"); goto skip; } mpl->flag_d = 1; get_token(mpl /* data */); if (mpl->token != T_SEMICOLON) error(mpl, "semicolon missing where expected"); get_token(mpl /* ; */); /* translate data section */ mpl->phase = 2; xprintf("Reading data section from %s...\n", file); data_section(mpl); } /* process end statement */ end_statement(mpl); skip: xprintf("%d line%s were read\n", mpl->line, mpl->line == 1 ? "" : "s"); close_input(mpl); done: /* return to the calling program */ return mpl->phase; } /*---------------------------------------------------------------------- -- mpl_read_data - read data section. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_read_data(MPL *mpl, char *file); -- -- *Description* -- -- The routine mpl_read_data reads data section from the text file, -- whose name is the character string file, performs translating data -- blocks, and stores the data read in the translator database. -- -- If this routine is used, it should be called once after the routine -- mpl_read_model and if the latter returned the code 1. -- -- *Returns* -- -- The routine mpl_read_data returns one of the following codes: -- -- 2 - data section has been successfully processed. -- 4 - processing failed due to some errors. In this case the calling -- program should call the routine mpl_terminate to terminate model -- processing. */ int mpl_read_data(MPL *mpl, char *file) #if 0 /* 02/X-2008 */ { if (mpl->phase != 1) #else { if (!(mpl->phase == 1 || mpl->phase == 2)) #endif xfault("mpl_read_data: invalid call sequence\n"); if (file == NULL) xfault("mpl_read_data: no input filename specified\n"); /* set up error handler */ if (setjmp(mpl->jump)) goto done; /* process data section */ mpl->phase = 2; xprintf("Reading data section from %s...\n", file); mpl->flag_d = 1; open_input(mpl, file); /* in this case the keyword 'data' is optional */ if (is_literal(mpl, "data")) { get_token(mpl /* data */); if (mpl->token != T_SEMICOLON) error(mpl, "semicolon missing where expected"); get_token(mpl /* ; */); } data_section(mpl); /* process end statement */ end_statement(mpl); xprintf("%d line%s were read\n", mpl->line, mpl->line == 1 ? "" : "s"); close_input(mpl); done: /* return to the calling program */ return mpl->phase; } /*---------------------------------------------------------------------- -- mpl_generate - generate model. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_generate(MPL *mpl, char *file); -- -- *Description* -- -- The routine mpl_generate generates the model using its description -- stored in the translator database. This phase means generating all -- variables, constraints, and objectives, executing check and display -- statements, which precede the solve statement (if it is presented), -- and building the problem instance. -- -- The character string file specifies the name of output text file, to -- which output produced by display statements should be written. It is -- allowed to specify NULL, in which case the output goes to stdout via -- the routine print. -- -- This routine should be called once after the routine mpl_read_model -- or mpl_read_data and if one of the latters returned the code 2. -- -- *Returns* -- -- The routine mpl_generate returns one of the following codes: -- -- 3 - model has been successfully generated. In this case the calling -- program may call other api routines to obtain components of the -- problem instance from the translator database. -- 4 - processing failed due to some errors. In this case the calling -- program should call the routine mpl_terminate to terminate model -- processing. */ int mpl_generate(MPL *mpl, char *file) { if (!(mpl->phase == 1 || mpl->phase == 2)) xfault("mpl_generate: invalid call sequence\n"); /* set up error handler */ if (setjmp(mpl->jump)) goto done; /* generate model */ mpl->phase = 3; open_output(mpl, file); generate_model(mpl); flush_output(mpl); /* build problem instance */ build_problem(mpl); /* generation phase has been finished */ xprintf("Model has been successfully generated\n"); done: /* return to the calling program */ return mpl->phase; } /*---------------------------------------------------------------------- -- mpl_get_prob_name - obtain problem (model) name. -- -- *Synopsis* -- -- #include "glpmpl.h" -- char *mpl_get_prob_name(MPL *mpl); -- -- *Returns* -- -- The routine mpl_get_prob_name returns a pointer to internal buffer, -- which contains symbolic name of the problem (model). -- -- *Note* -- -- Currently MathProg has no feature to assign a symbolic name to the -- model. Therefore the routine mpl_get_prob_name tries to construct -- such name using the name of input text file containing model section, -- although this is not a good idea (due to portability problems). */ char *mpl_get_prob_name(MPL *mpl) { char *name = mpl->mpl_buf; char *file = mpl->mod_file; int k; if (mpl->phase != 3) xfault("mpl_get_prob_name: invalid call sequence\n"); for (;;) { if (strchr(file, '/') != NULL) file = strchr(file, '/') + 1; else if (strchr(file, '\\') != NULL) file = strchr(file, '\\') + 1; else if (strchr(file, ':') != NULL) file = strchr(file, ':') + 1; else break; } for (k = 0; ; k++) { if (k == 255) break; if (!(isalnum((unsigned char)*file) || *file == '_')) break; name[k] = *file++; } if (k == 0) strcpy(name, "Unknown"); else name[k] = '\0'; xassert(strlen(name) <= 255); return name; } /*---------------------------------------------------------------------- -- mpl_get_num_rows - determine number of rows. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_get_num_rows(MPL *mpl); -- -- *Returns* -- -- The routine mpl_get_num_rows returns total number of rows in the -- problem, where each row is an individual constraint or objective. */ int mpl_get_num_rows(MPL *mpl) { if (mpl->phase != 3) xfault("mpl_get_num_rows: invalid call sequence\n"); return mpl->m; } /*---------------------------------------------------------------------- -- mpl_get_num_cols - determine number of columns. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_get_num_cols(MPL *mpl); -- -- *Returns* -- -- The routine mpl_get_num_cols returns total number of columns in the -- problem, where each column is an individual variable. */ int mpl_get_num_cols(MPL *mpl) { if (mpl->phase != 3) xfault("mpl_get_num_cols: invalid call sequence\n"); return mpl->n; } /*---------------------------------------------------------------------- -- mpl_get_row_name - obtain row name. -- -- *Synopsis* -- -- #include "glpmpl.h" -- char *mpl_get_row_name(MPL *mpl, int i); -- -- *Returns* -- -- The routine mpl_get_row_name returns a pointer to internal buffer, -- which contains symbolic name of i-th row of the problem. */ char *mpl_get_row_name(MPL *mpl, int i) { char *name = mpl->mpl_buf, *t; int len; if (mpl->phase != 3) xfault("mpl_get_row_name: invalid call sequence\n"); if (!(1 <= i && i <= mpl->m)) xfault("mpl_get_row_name: i = %d; row number out of range\n", i); strcpy(name, mpl->row[i]->con->name); len = strlen(name); xassert(len <= 255); t = format_tuple(mpl, '[', mpl->row[i]->memb->tuple); while (*t) { if (len == 255) break; name[len++] = *t++; } name[len] = '\0'; if (len == 255) strcpy(name+252, "..."); xassert(strlen(name) <= 255); return name; } /*---------------------------------------------------------------------- -- mpl_get_row_kind - determine row kind. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_get_row_kind(MPL *mpl, int i); -- -- *Returns* -- -- The routine mpl_get_row_kind returns the kind of i-th row, which can -- be one of the following: -- -- MPL_ST - non-free (constraint) row; -- MPL_MIN - free (objective) row to be minimized; -- MPL_MAX - free (objective) row to be maximized. */ int mpl_get_row_kind(MPL *mpl, int i) { int kind; if (mpl->phase != 3) xfault("mpl_get_row_kind: invalid call sequence\n"); if (!(1 <= i && i <= mpl->m)) xfault("mpl_get_row_kind: i = %d; row number out of range\n", i); switch (mpl->row[i]->con->type) { case A_CONSTRAINT: kind = MPL_ST; break; case A_MINIMIZE: kind = MPL_MIN; break; case A_MAXIMIZE: kind = MPL_MAX; break; default: xassert(mpl != mpl); } return kind; } /*---------------------------------------------------------------------- -- mpl_get_row_bnds - obtain row bounds. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_get_row_bnds(MPL *mpl, int i, double *lb, double *ub); -- -- *Description* -- -- The routine mpl_get_row_bnds stores lower and upper bounds of i-th -- row of the problem to the locations, which the parameters lb and ub -- point to, respectively. Besides the routine returns the type of the -- i-th row. -- -- If some of the parameters lb and ub is NULL, the corresponding bound -- value is not stored. -- -- Types and bounds have the following meaning: -- -- Type Bounds Note -- ----------------------------------------------------------- -- MPL_FR -inf < f(x) < +inf Free linear form -- MPL_LO lb <= f(x) < +inf Inequality f(x) >= lb -- MPL_UP -inf < f(x) <= ub Inequality f(x) <= ub -- MPL_DB lb <= f(x) <= ub Inequality lb <= f(x) <= ub -- MPL_FX f(x) = lb Equality f(x) = lb -- -- where f(x) is the corresponding linear form of the i-th row. -- -- If the row has no lower bound, *lb is set to zero; if the row has -- no upper bound, *ub is set to zero; and if the row is of fixed type, -- both *lb and *ub are set to the same value. -- -- *Returns* -- -- The routine returns the type of the i-th row as it is stated in the -- table above. */ int mpl_get_row_bnds(MPL *mpl, int i, double *_lb, double *_ub) { ELEMCON *con; int type; double lb, ub; if (mpl->phase != 3) xfault("mpl_get_row_bnds: invalid call sequence\n"); if (!(1 <= i && i <= mpl->m)) xfault("mpl_get_row_bnds: i = %d; row number out of range\n", i); con = mpl->row[i]; #if 0 /* 21/VII-2006 */ if (con->con->lbnd == NULL && con->con->ubnd == NULL) type = MPL_FR, lb = ub = 0.0; else if (con->con->ubnd == NULL) type = MPL_LO, lb = con->lbnd, ub = 0.0; else if (con->con->lbnd == NULL) type = MPL_UP, lb = 0.0, ub = con->ubnd; else if (con->con->lbnd != con->con->ubnd) type = MPL_DB, lb = con->lbnd, ub = con->ubnd; else type = MPL_FX, lb = ub = con->lbnd; #else lb = (con->con->lbnd == NULL ? -DBL_MAX : con->lbnd); ub = (con->con->ubnd == NULL ? +DBL_MAX : con->ubnd); if (lb == -DBL_MAX && ub == +DBL_MAX) type = MPL_FR, lb = ub = 0.0; else if (ub == +DBL_MAX) type = MPL_LO, ub = 0.0; else if (lb == -DBL_MAX) type = MPL_UP, lb = 0.0; else if (con->con->lbnd != con->con->ubnd) type = MPL_DB; else type = MPL_FX; #endif if (_lb != NULL) *_lb = lb; if (_ub != NULL) *_ub = ub; return type; } /*---------------------------------------------------------------------- -- mpl_get_mat_row - obtain row of the constraint matrix. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_get_mat_row(MPL *mpl, int i, int ndx[], double val[]); -- -- *Description* -- -- The routine mpl_get_mat_row stores column indices and numeric values -- of constraint coefficients for the i-th row to locations ndx[1], ..., -- ndx[len] and val[1], ..., val[len], respectively, where 0 <= len <= n -- is number of (structural) non-zero constraint coefficients, and n is -- number of columns in the problem. -- -- If the parameter ndx is NULL, column indices are not stored. If the -- parameter val is NULL, numeric values are not stored. -- -- Note that free rows may have constant terms, which are not part of -- the constraint matrix and therefore not reported by this routine. The -- constant term of a particular row can be obtained, if necessary, via -- the routine mpl_get_row_c0. -- -- *Returns* -- -- The routine mpl_get_mat_row returns len, which is length of i-th row -- of the constraint matrix (i.e. number of non-zero coefficients). */ int mpl_get_mat_row(MPL *mpl, int i, int ndx[], double val[]) { FORMULA *term; int len = 0; if (mpl->phase != 3) xfault("mpl_get_mat_row: invalid call sequence\n"); if (!(1 <= i && i <= mpl->m)) xfault("mpl_get_mat_row: i = %d; row number out of range\n", i); for (term = mpl->row[i]->form; term != NULL; term = term->next) { xassert(term->var != NULL); len++; xassert(len <= mpl->n); if (ndx != NULL) ndx[len] = term->var->j; if (val != NULL) val[len] = term->coef; } return len; } /*---------------------------------------------------------------------- -- mpl_get_row_c0 - obtain constant term of free row. -- -- *Synopsis* -- -- #include "glpmpl.h" -- double mpl_get_row_c0(MPL *mpl, int i); -- -- *Returns* -- -- The routine mpl_get_row_c0 returns numeric value of constant term of -- i-th row. -- -- Note that only free rows may have non-zero constant terms. Therefore -- if i-th row is not free, the routine returns zero. */ double mpl_get_row_c0(MPL *mpl, int i) { ELEMCON *con; double c0; if (mpl->phase != 3) xfault("mpl_get_row_c0: invalid call sequence\n"); if (!(1 <= i && i <= mpl->m)) xfault("mpl_get_row_c0: i = %d; row number out of range\n", i); con = mpl->row[i]; if (con->con->lbnd == NULL && con->con->ubnd == NULL) c0 = - con->lbnd; else c0 = 0.0; return c0; } /*---------------------------------------------------------------------- -- mpl_get_col_name - obtain column name. -- -- *Synopsis* -- -- #include "glpmpl.h" -- char *mpl_get_col_name(MPL *mpl, int j); -- -- *Returns* -- -- The routine mpl_get_col_name returns a pointer to internal buffer, -- which contains symbolic name of j-th column of the problem. */ char *mpl_get_col_name(MPL *mpl, int j) { char *name = mpl->mpl_buf, *t; int len; if (mpl->phase != 3) xfault("mpl_get_col_name: invalid call sequence\n"); if (!(1 <= j && j <= mpl->n)) xfault("mpl_get_col_name: j = %d; column number out of range\n" , j); strcpy(name, mpl->col[j]->var->name); len = strlen(name); xassert(len <= 255); t = format_tuple(mpl, '[', mpl->col[j]->memb->tuple); while (*t) { if (len == 255) break; name[len++] = *t++; } name[len] = '\0'; if (len == 255) strcpy(name+252, "..."); xassert(strlen(name) <= 255); return name; } /*---------------------------------------------------------------------- -- mpl_get_col_kind - determine column kind. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_get_col_kind(MPL *mpl, int j); -- -- *Returns* -- -- The routine mpl_get_col_kind returns the kind of j-th column, which -- can be one of the following: -- -- MPL_NUM - continuous variable; -- MPL_INT - integer variable; -- MPL_BIN - binary variable. -- -- Note that column kinds are defined independently on type and bounds -- (reported by the routine mpl_get_col_bnds) of corresponding columns. -- This means, in particular, that bounds of an integer column may be -- fractional, or a binary column may have lower and upper bounds that -- are not 0 and 1 (or it may have no lower/upper bound at all). */ int mpl_get_col_kind(MPL *mpl, int j) { int kind; if (mpl->phase != 3) xfault("mpl_get_col_kind: invalid call sequence\n"); if (!(1 <= j && j <= mpl->n)) xfault("mpl_get_col_kind: j = %d; column number out of range\n" , j); switch (mpl->col[j]->var->type) { case A_NUMERIC: kind = MPL_NUM; break; case A_INTEGER: kind = MPL_INT; break; case A_BINARY: kind = MPL_BIN; break; default: xassert(mpl != mpl); } return kind; } /*---------------------------------------------------------------------- -- mpl_get_col_bnds - obtain column bounds. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_get_col_bnds(MPL *mpl, int j, double *lb, double *ub); -- -- *Description* -- -- The routine mpl_get_col_bnds stores lower and upper bound of j-th -- column of the problem to the locations, which the parameters lb and -- ub point to, respectively. Besides the routine returns the type of -- the j-th column. -- -- If some of the parameters lb and ub is NULL, the corresponding bound -- value is not stored. -- -- Types and bounds have the following meaning: -- -- Type Bounds Note -- ------------------------------------------------------ -- MPL_FR -inf < x < +inf Free (unbounded) variable -- MPL_LO lb <= x < +inf Variable with lower bound -- MPL_UP -inf < x <= ub Variable with upper bound -- MPL_DB lb <= x <= ub Double-bounded variable -- MPL_FX x = lb Fixed variable -- -- where x is individual variable corresponding to the j-th column. -- -- If the column has no lower bound, *lb is set to zero; if the column -- has no upper bound, *ub is set to zero; and if the column is of fixed -- type, both *lb and *ub are set to the same value. -- -- *Returns* -- -- The routine returns the type of the j-th column as it is stated in -- the table above. */ int mpl_get_col_bnds(MPL *mpl, int j, double *_lb, double *_ub) { ELEMVAR *var; int type; double lb, ub; if (mpl->phase != 3) xfault("mpl_get_col_bnds: invalid call sequence\n"); if (!(1 <= j && j <= mpl->n)) xfault("mpl_get_col_bnds: j = %d; column number out of range\n" , j); var = mpl->col[j]; #if 0 /* 21/VII-2006 */ if (var->var->lbnd == NULL && var->var->ubnd == NULL) type = MPL_FR, lb = ub = 0.0; else if (var->var->ubnd == NULL) type = MPL_LO, lb = var->lbnd, ub = 0.0; else if (var->var->lbnd == NULL) type = MPL_UP, lb = 0.0, ub = var->ubnd; else if (var->var->lbnd != var->var->ubnd) type = MPL_DB, lb = var->lbnd, ub = var->ubnd; else type = MPL_FX, lb = ub = var->lbnd; #else lb = (var->var->lbnd == NULL ? -DBL_MAX : var->lbnd); ub = (var->var->ubnd == NULL ? +DBL_MAX : var->ubnd); if (lb == -DBL_MAX && ub == +DBL_MAX) type = MPL_FR, lb = ub = 0.0; else if (ub == +DBL_MAX) type = MPL_LO, ub = 0.0; else if (lb == -DBL_MAX) type = MPL_UP, lb = 0.0; else if (var->var->lbnd != var->var->ubnd) type = MPL_DB; else type = MPL_FX; #endif if (_lb != NULL) *_lb = lb; if (_ub != NULL) *_ub = ub; return type; } /*---------------------------------------------------------------------- -- mpl_has_solve_stmt - check if model has solve statement. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_has_solve_stmt(MPL *mpl); -- -- *Returns* -- -- If the model has the solve statement, the routine returns non-zero, -- otherwise zero is returned. */ int mpl_has_solve_stmt(MPL *mpl) { if (mpl->phase != 3) xfault("mpl_has_solve_stmt: invalid call sequence\n"); return mpl->flag_s; } #if 1 /* 15/V-2010 */ void mpl_put_row_soln(MPL *mpl, int i, int stat, double prim, double dual) { /* store row (constraint/objective) solution components */ xassert(mpl->phase == 3); xassert(1 <= i && i <= mpl->m); mpl->row[i]->stat = stat; mpl->row[i]->prim = prim; mpl->row[i]->dual = dual; return; } #endif #if 1 /* 15/V-2010 */ void mpl_put_col_soln(MPL *mpl, int j, int stat, double prim, double dual) { /* store column (variable) solution components */ xassert(mpl->phase == 3); xassert(1 <= j && j <= mpl->n); mpl->col[j]->stat = stat; mpl->col[j]->prim = prim; mpl->col[j]->dual = dual; return; } #endif #if 0 /* 15/V-2010 */ /*---------------------------------------------------------------------- -- mpl_put_col_value - store column value. -- -- *Synopsis* -- -- #include "glpmpl.h" -- void mpl_put_col_value(MPL *mpl, int j, double val); -- -- *Description* -- -- The routine mpl_put_col_value stores numeric value of j-th column -- into the translator database. It is assumed that the column value is -- provided by the solver. */ void mpl_put_col_value(MPL *mpl, int j, double val) { if (mpl->phase != 3) xfault("mpl_put_col_value: invalid call sequence\n"); if (!(1 <= j && j <= mpl->n)) xfault( "mpl_put_col_value: j = %d; column number out of range\n", j); mpl->col[j]->prim = val; return; } #endif /*---------------------------------------------------------------------- -- mpl_postsolve - postsolve model. -- -- *Synopsis* -- -- #include "glpmpl.h" -- int mpl_postsolve(MPL *mpl); -- -- *Description* -- -- The routine mpl_postsolve performs postsolving of the model using -- its description stored in the translator database. This phase means -- executing statements, which follow the solve statement. -- -- If this routine is used, it should be called once after the routine -- mpl_generate and if the latter returned the code 3. -- -- *Returns* -- -- The routine mpl_postsolve returns one of the following codes: -- -- 3 - model has been successfully postsolved. -- 4 - processing failed due to some errors. In this case the calling -- program should call the routine mpl_terminate to terminate model -- processing. */ int mpl_postsolve(MPL *mpl) { if (!(mpl->phase == 3 && !mpl->flag_p)) xfault("mpl_postsolve: invalid call sequence\n"); /* set up error handler */ if (setjmp(mpl->jump)) goto done; /* perform postsolving */ postsolve_model(mpl); flush_output(mpl); /* postsolving phase has been finished */ xprintf("Model has been successfully processed\n"); done: /* return to the calling program */ return mpl->phase; } /*---------------------------------------------------------------------- -- mpl_terminate - free all resources used by translator. -- -- *Synopsis* -- -- #include "glpmpl.h" -- void mpl_terminate(MPL *mpl); -- -- *Description* -- -- The routine mpl_terminate frees all the resources used by the GNU -- MathProg translator. */ void mpl_terminate(MPL *mpl) { if (setjmp(mpl->jump)) xassert(mpl != mpl); switch (mpl->phase) { case 0: case 1: case 2: case 3: /* there were no errors; clean the model content */ clean_model(mpl); xassert(mpl->a_list == NULL); #if 1 /* 11/II-2008 */ xassert(mpl->dca == NULL); #endif break; case 4: /* model processing has been finished due to error; delete search trees, which may be created for some arrays */ { ARRAY *a; for (a = mpl->a_list; a != NULL; a = a->next) if (a->tree != NULL) avl_delete_tree(a->tree); } #if 1 /* 11/II-2008 */ free_dca(mpl); #endif break; default: xassert(mpl != mpl); } /* delete the translator database */ xfree(mpl->image); xfree(mpl->b_image); xfree(mpl->f_image); xfree(mpl->context); dmp_delete_pool(mpl->pool); avl_delete_tree(mpl->tree); dmp_delete_pool(mpl->strings); dmp_delete_pool(mpl->symbols); dmp_delete_pool(mpl->tuples); dmp_delete_pool(mpl->arrays); dmp_delete_pool(mpl->members); dmp_delete_pool(mpl->elemvars); dmp_delete_pool(mpl->formulae); dmp_delete_pool(mpl->elemcons); xfree(mpl->sym_buf); xfree(mpl->tup_buf); rng_delete_rand(mpl->rand); if (mpl->row != NULL) xfree(mpl->row); if (mpl->col != NULL) xfree(mpl->col); if (mpl->in_fp != NULL) xfclose(mpl->in_fp); if (mpl->out_fp != NULL /* && mpl->out_fp != (void *)stdout */) xfclose(mpl->out_fp); if (mpl->out_file != NULL) xfree(mpl->out_file); if (mpl->prt_fp != NULL) xfclose(mpl->prt_fp); if (mpl->prt_file != NULL) xfree(mpl->prt_file); if (mpl->mod_file != NULL) xfree(mpl->mod_file); xfree(mpl->mpl_buf); xfree(mpl); return; } /* eof */ igraph/src/walktrap_heap.h0000644000176000001440000001037412325527074015352 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* The original version of this file was written by Pascal Pons The original copyright notice follows here. The FSF address was fixed by Tamas Nepusz */ // File: heap.h //----------------------------------------------------------------------------- // Walktrap v0.2 -- Finds community structure of networks using random walks // Copyright (C) 2004-2005 Pascal Pons // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA //----------------------------------------------------------------------------- // Author : Pascal Pons // Email : pons@liafa.jussieu.fr // Web page : http://www.liafa.jussieu.fr/~pons/ // Location : Paris, France // Time : June 2005 //----------------------------------------------------------------------------- // see readme.txt for more details #ifndef HEAP_H #define HEAP_H namespace igraph { namespace walktrap { class Neighbor { public: int community1; // the two adjacent communities int community2; // community1 < community2 float delta_sigma; // the delta sigma between the two communities float weight; // the total weight of the edges between the two communities bool exact; // true if delta_sigma is exact, false if it is only a lower bound Neighbor* next_community1; // pointers of two double Neighbor* previous_community1; // chained lists containing Neighbor* next_community2; // all the neighbors of Neighbor* previous_community2; // each communities. int heap_index; // Neighbor(); }; class Neighbor_heap { private: int size; int max_size; Neighbor** H; // the heap that contains a pointer to each Neighbor object stored void move_up(int index); void move_down(int index); public: void add(Neighbor* N); // add a new distance void update(Neighbor* N); // update a distance void remove(Neighbor* N); // remove a distance Neighbor* get_first(); // get the first item long memory(); bool is_empty(); Neighbor_heap(int max_size); ~Neighbor_heap(); }; class Min_delta_sigma_heap { private: int size; int max_size; int* H; // the heap that contains the number of each community int* I; // the index of each community in the heap (-1 = not stored) void move_up(int index); void move_down(int index); public: int get_max_community(); // return the community with the maximal delta_sigma void remove_community(int community); // remove a community; void update(int community); // update (or insert if necessary) the community long memory(); // the memory used in Bytes. bool is_empty(); float* delta_sigma; // the delta_sigma of the stored communities Min_delta_sigma_heap(int max_size); ~Min_delta_sigma_heap(); }; } } /* end of namespaces */ #endif igraph/src/cs_pinv.c0000644000176000001440000000254012325527073014157 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* pinv = p', or p = pinv' */ CS_INT *cs_pinv (CS_INT const *p, CS_INT n) { CS_INT k, *pinv ; if (!p) return (NULL) ; /* p = NULL denotes identity */ pinv = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ if (!pinv) return (NULL) ; /* out of memory */ for (k = 0 ; k < n ; k++) pinv [p [k]] = k ;/* invert the permutation */ return (pinv) ; /* return result */ } igraph/src/glpapi01.c0000644000176000001440000014745112325527073014146 0ustar ripleyusers/* glpapi01.c (problem creating and modifying routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include "glpios.h" /* CAUTION: DO NOT CHANGE THE LIMITS BELOW */ #define M_MAX 100000000 /* = 100*10^6 */ /* maximal number of rows in the problem object */ #define N_MAX 100000000 /* = 100*10^6 */ /* maximal number of columns in the problem object */ #define NNZ_MAX 500000000 /* = 500*10^6 */ /* maximal number of constraint coefficients in the problem object */ /*********************************************************************** * NAME * * glp_create_prob - create problem object * * SYNOPSIS * * glp_prob *glp_create_prob(void); * * DESCRIPTION * * The routine glp_create_prob creates a new problem object, which is * initially "empty", i.e. has no rows and columns. * * RETURNS * * The routine returns a pointer to the object created, which should be * used in any subsequent operations on this object. */ static void create_prob(glp_prob *lp) { lp->magic = GLP_PROB_MAGIC; lp->pool = dmp_create_pool(); #if 0 /* 17/XI-2009 */ lp->cps = xmalloc(sizeof(struct LPXCPS)); lpx_reset_parms(lp); #else lp->parms = NULL; #endif lp->tree = NULL; #if 0 lp->lwa = 0; lp->cwa = NULL; #endif /* LP/MIP data */ lp->name = NULL; lp->obj = NULL; lp->dir = GLP_MIN; lp->c0 = 0.0; lp->m_max = 100; lp->n_max = 200; lp->m = lp->n = 0; lp->nnz = 0; lp->row = xcalloc(1+lp->m_max, sizeof(GLPROW *)); lp->col = xcalloc(1+lp->n_max, sizeof(GLPCOL *)); lp->r_tree = lp->c_tree = NULL; /* basis factorization */ lp->valid = 0; lp->head = xcalloc(1+lp->m_max, sizeof(int)); lp->bfcp = NULL; lp->bfd = NULL; /* basic solution (LP) */ lp->pbs_stat = lp->dbs_stat = GLP_UNDEF; lp->obj_val = 0.0; lp->it_cnt = 0; lp->some = 0; /* interior-point solution (LP) */ lp->ipt_stat = GLP_UNDEF; lp->ipt_obj = 0.0; /* integer solution (MIP) */ lp->mip_stat = GLP_UNDEF; lp->mip_obj = 0.0; return; } glp_prob *glp_create_prob(void) { glp_prob *lp; lp = xmalloc(sizeof(glp_prob)); create_prob(lp); return lp; } /*********************************************************************** * NAME * * glp_set_prob_name - assign (change) problem name * * SYNOPSIS * * void glp_set_prob_name(glp_prob *lp, const char *name); * * DESCRIPTION * * The routine glp_set_prob_name assigns a given symbolic name (1 up to * 255 characters) to the specified problem object. * * If the parameter name is NULL or empty string, the routine erases an * existing symbolic name of the problem object. */ void glp_set_prob_name(glp_prob *lp, const char *name) { glp_tree *tree = lp->tree; if (tree != NULL && tree->reason != 0) xerror("glp_set_prob_name: operation not allowed\n"); if (lp->name != NULL) { dmp_free_atom(lp->pool, lp->name, strlen(lp->name)+1); lp->name = NULL; } if (!(name == NULL || name[0] == '\0')) { int k; for (k = 0; name[k] != '\0'; k++) { if (k == 256) xerror("glp_set_prob_name: problem name too long\n"); if (iscntrl((unsigned char)name[k])) xerror("glp_set_prob_name: problem name contains invalid" " character(s)\n"); } lp->name = dmp_get_atom(lp->pool, strlen(name)+1); strcpy(lp->name, name); } return; } /*********************************************************************** * NAME * * glp_set_obj_name - assign (change) objective function name * * SYNOPSIS * * void glp_set_obj_name(glp_prob *lp, const char *name); * * DESCRIPTION * * The routine glp_set_obj_name assigns a given symbolic name (1 up to * 255 characters) to the objective function of the specified problem * object. * * If the parameter name is NULL or empty string, the routine erases an * existing name of the objective function. */ void glp_set_obj_name(glp_prob *lp, const char *name) { glp_tree *tree = lp->tree; if (tree != NULL && tree->reason != 0) xerror("glp_set_obj_name: operation not allowed\n"); if (lp->obj != NULL) { dmp_free_atom(lp->pool, lp->obj, strlen(lp->obj)+1); lp->obj = NULL; } if (!(name == NULL || name[0] == '\0')) { int k; for (k = 0; name[k] != '\0'; k++) { if (k == 256) xerror("glp_set_obj_name: objective name too long\n"); if (iscntrl((unsigned char)name[k])) xerror("glp_set_obj_name: objective name contains invali" "d character(s)\n"); } lp->obj = dmp_get_atom(lp->pool, strlen(name)+1); strcpy(lp->obj, name); } return; } /*********************************************************************** * NAME * * glp_set_obj_dir - set (change) optimization direction flag * * SYNOPSIS * * void glp_set_obj_dir(glp_prob *lp, int dir); * * DESCRIPTION * * The routine glp_set_obj_dir sets (changes) optimization direction * flag (i.e. "sense" of the objective function) as specified by the * parameter dir: * * GLP_MIN - minimization; * GLP_MAX - maximization. */ void glp_set_obj_dir(glp_prob *lp, int dir) { glp_tree *tree = lp->tree; if (tree != NULL && tree->reason != 0) xerror("glp_set_obj_dir: operation not allowed\n"); if (!(dir == GLP_MIN || dir == GLP_MAX)) xerror("glp_set_obj_dir: dir = %d; invalid direction flag\n", dir); lp->dir = dir; return; } /*********************************************************************** * NAME * * glp_add_rows - add new rows to problem object * * SYNOPSIS * * int glp_add_rows(glp_prob *lp, int nrs); * * DESCRIPTION * * The routine glp_add_rows adds nrs rows (constraints) to the specified * problem object. New rows are always added to the end of the row list, * so the ordinal numbers of existing rows remain unchanged. * * Being added each new row is initially free (unbounded) and has empty * list of the constraint coefficients. * * RETURNS * * The routine glp_add_rows returns the ordinal number of the first new * row added to the problem object. */ int glp_add_rows(glp_prob *lp, int nrs) { glp_tree *tree = lp->tree; GLPROW *row; int m_new, i; /* determine new number of rows */ if (nrs < 1) xerror("glp_add_rows: nrs = %d; invalid number of rows\n", nrs); if (nrs > M_MAX - lp->m) xerror("glp_add_rows: nrs = %d; too many rows\n", nrs); m_new = lp->m + nrs; /* increase the room, if necessary */ if (lp->m_max < m_new) { GLPROW **save = lp->row; while (lp->m_max < m_new) { lp->m_max += lp->m_max; xassert(lp->m_max > 0); } lp->row = xcalloc(1+lp->m_max, sizeof(GLPROW *)); memcpy(&lp->row[1], &save[1], lp->m * sizeof(GLPROW *)); xfree(save); /* do not forget about the basis header */ xfree(lp->head); lp->head = xcalloc(1+lp->m_max, sizeof(int)); } /* add new rows to the end of the row list */ for (i = lp->m+1; i <= m_new; i++) { /* create row descriptor */ lp->row[i] = row = dmp_get_atom(lp->pool, sizeof(GLPROW)); row->i = i; row->name = NULL; row->node = NULL; #if 1 /* 20/IX-2008 */ row->level = 0; row->origin = 0; row->klass = 0; if (tree != NULL) { switch (tree->reason) { case 0: break; case GLP_IROWGEN: xassert(tree->curr != NULL); row->level = tree->curr->level; row->origin = GLP_RF_LAZY; break; case GLP_ICUTGEN: xassert(tree->curr != NULL); row->level = tree->curr->level; row->origin = GLP_RF_CUT; break; default: xassert(tree != tree); } } #endif row->type = GLP_FR; row->lb = row->ub = 0.0; row->ptr = NULL; row->rii = 1.0; row->stat = GLP_BS; #if 0 row->bind = -1; #else row->bind = 0; #endif row->prim = row->dual = 0.0; row->pval = row->dval = 0.0; row->mipx = 0.0; } /* set new number of rows */ lp->m = m_new; /* invalidate the basis factorization */ lp->valid = 0; #if 1 if (tree != NULL && tree->reason != 0) tree->reopt = 1; #endif /* return the ordinal number of the first row added */ return m_new - nrs + 1; } /*********************************************************************** * NAME * * glp_add_cols - add new columns to problem object * * SYNOPSIS * * int glp_add_cols(glp_prob *lp, int ncs); * * DESCRIPTION * * The routine glp_add_cols adds ncs columns (structural variables) to * the specified problem object. New columns are always added to the end * of the column list, so the ordinal numbers of existing columns remain * unchanged. * * Being added each new column is initially fixed at zero and has empty * list of the constraint coefficients. * * RETURNS * * The routine glp_add_cols returns the ordinal number of the first new * column added to the problem object. */ int glp_add_cols(glp_prob *lp, int ncs) { glp_tree *tree = lp->tree; GLPCOL *col; int n_new, j; if (tree != NULL && tree->reason != 0) xerror("glp_add_cols: operation not allowed\n"); /* determine new number of columns */ if (ncs < 1) xerror("glp_add_cols: ncs = %d; invalid number of columns\n", ncs); if (ncs > N_MAX - lp->n) xerror("glp_add_cols: ncs = %d; too many columns\n", ncs); n_new = lp->n + ncs; /* increase the room, if necessary */ if (lp->n_max < n_new) { GLPCOL **save = lp->col; while (lp->n_max < n_new) { lp->n_max += lp->n_max; xassert(lp->n_max > 0); } lp->col = xcalloc(1+lp->n_max, sizeof(GLPCOL *)); memcpy(&lp->col[1], &save[1], lp->n * sizeof(GLPCOL *)); xfree(save); } /* add new columns to the end of the column list */ for (j = lp->n+1; j <= n_new; j++) { /* create column descriptor */ lp->col[j] = col = dmp_get_atom(lp->pool, sizeof(GLPCOL)); col->j = j; col->name = NULL; col->node = NULL; col->kind = GLP_CV; col->type = GLP_FX; col->lb = col->ub = 0.0; col->coef = 0.0; col->ptr = NULL; col->sjj = 1.0; col->stat = GLP_NS; #if 0 col->bind = -1; #else col->bind = 0; /* the basis may remain valid */ #endif col->prim = col->dual = 0.0; col->pval = col->dval = 0.0; col->mipx = 0.0; } /* set new number of columns */ lp->n = n_new; /* return the ordinal number of the first column added */ return n_new - ncs + 1; } /*********************************************************************** * NAME * * glp_set_row_name - assign (change) row name * * SYNOPSIS * * void glp_set_row_name(glp_prob *lp, int i, const char *name); * * DESCRIPTION * * The routine glp_set_row_name assigns a given symbolic name (1 up to * 255 characters) to i-th row (auxiliary variable) of the specified * problem object. * * If the parameter name is NULL or empty string, the routine erases an * existing name of i-th row. */ void glp_set_row_name(glp_prob *lp, int i, const char *name) { glp_tree *tree = lp->tree; GLPROW *row; if (!(1 <= i && i <= lp->m)) xerror("glp_set_row_name: i = %d; row number out of range\n", i); row = lp->row[i]; if (tree != NULL && tree->reason != 0) { xassert(tree->curr != NULL); xassert(row->level == tree->curr->level); } if (row->name != NULL) { if (row->node != NULL) { xassert(lp->r_tree != NULL); avl_delete_node(lp->r_tree, row->node); row->node = NULL; } dmp_free_atom(lp->pool, row->name, strlen(row->name)+1); row->name = NULL; } if (!(name == NULL || name[0] == '\0')) { int k; for (k = 0; name[k] != '\0'; k++) { if (k == 256) xerror("glp_set_row_name: i = %d; row name too long\n", i); if (iscntrl((unsigned char)name[k])) xerror("glp_set_row_name: i = %d: row name contains inva" "lid character(s)\n", i); } row->name = dmp_get_atom(lp->pool, strlen(name)+1); strcpy(row->name, name); if (lp->r_tree != NULL) { xassert(row->node == NULL); row->node = avl_insert_node(lp->r_tree, row->name); avl_set_node_link(row->node, row); } } return; } /*********************************************************************** * NAME * * glp_set_col_name - assign (change) column name * * SYNOPSIS * * void glp_set_col_name(glp_prob *lp, int j, const char *name); * * DESCRIPTION * * The routine glp_set_col_name assigns a given symbolic name (1 up to * 255 characters) to j-th column (structural variable) of the specified * problem object. * * If the parameter name is NULL or empty string, the routine erases an * existing name of j-th column. */ void glp_set_col_name(glp_prob *lp, int j, const char *name) { glp_tree *tree = lp->tree; GLPCOL *col; if (tree != NULL && tree->reason != 0) xerror("glp_set_col_name: operation not allowed\n"); if (!(1 <= j && j <= lp->n)) xerror("glp_set_col_name: j = %d; column number out of range\n" , j); col = lp->col[j]; if (col->name != NULL) { if (col->node != NULL) { xassert(lp->c_tree != NULL); avl_delete_node(lp->c_tree, col->node); col->node = NULL; } dmp_free_atom(lp->pool, col->name, strlen(col->name)+1); col->name = NULL; } if (!(name == NULL || name[0] == '\0')) { int k; for (k = 0; name[k] != '\0'; k++) { if (k == 256) xerror("glp_set_col_name: j = %d; column name too long\n" , j); if (iscntrl((unsigned char)name[k])) xerror("glp_set_col_name: j = %d: column name contains i" "nvalid character(s)\n", j); } col->name = dmp_get_atom(lp->pool, strlen(name)+1); strcpy(col->name, name); if (lp->c_tree != NULL && col->name != NULL) { xassert(col->node == NULL); col->node = avl_insert_node(lp->c_tree, col->name); avl_set_node_link(col->node, col); } } return; } /*********************************************************************** * NAME * * glp_set_row_bnds - set (change) row bounds * * SYNOPSIS * * void glp_set_row_bnds(glp_prob *lp, int i, int type, double lb, * double ub); * * DESCRIPTION * * The routine glp_set_row_bnds sets (changes) the type and bounds of * i-th row (auxiliary variable) of the specified problem object. * * Parameters type, lb, and ub specify the type, lower bound, and upper * bound, respectively, as follows: * * Type Bounds Comments * ------------------------------------------------------ * GLP_FR -inf < x < +inf Free variable * GLP_LO lb <= x < +inf Variable with lower bound * GLP_UP -inf < x <= ub Variable with upper bound * GLP_DB lb <= x <= ub Double-bounded variable * GLP_FX x = lb Fixed variable * * where x is the auxiliary variable associated with i-th row. * * If the row has no lower bound, the parameter lb is ignored. If the * row has no upper bound, the parameter ub is ignored. If the row is * an equality constraint (i.e. the corresponding auxiliary variable is * of fixed type), only the parameter lb is used while the parameter ub * is ignored. */ void glp_set_row_bnds(glp_prob *lp, int i, int type, double lb, double ub) { GLPROW *row; if (!(1 <= i && i <= lp->m)) xerror("glp_set_row_bnds: i = %d; row number out of range\n", i); row = lp->row[i]; row->type = type; switch (type) { case GLP_FR: row->lb = row->ub = 0.0; if (row->stat != GLP_BS) row->stat = GLP_NF; break; case GLP_LO: row->lb = lb, row->ub = 0.0; if (row->stat != GLP_BS) row->stat = GLP_NL; break; case GLP_UP: row->lb = 0.0, row->ub = ub; if (row->stat != GLP_BS) row->stat = GLP_NU; break; case GLP_DB: row->lb = lb, row->ub = ub; if (!(row->stat == GLP_BS || row->stat == GLP_NL || row->stat == GLP_NU)) row->stat = (fabs(lb) <= fabs(ub) ? GLP_NL : GLP_NU); break; case GLP_FX: row->lb = row->ub = lb; if (row->stat != GLP_BS) row->stat = GLP_NS; break; default: xerror("glp_set_row_bnds: i = %d; type = %d; invalid row ty" "pe\n", i, type); } return; } /*********************************************************************** * NAME * * glp_set_col_bnds - set (change) column bounds * * SYNOPSIS * * void glp_set_col_bnds(glp_prob *lp, int j, int type, double lb, * double ub); * * DESCRIPTION * * The routine glp_set_col_bnds sets (changes) the type and bounds of * j-th column (structural variable) of the specified problem object. * * Parameters type, lb, and ub specify the type, lower bound, and upper * bound, respectively, as follows: * * Type Bounds Comments * ------------------------------------------------------ * GLP_FR -inf < x < +inf Free variable * GLP_LO lb <= x < +inf Variable with lower bound * GLP_UP -inf < x <= ub Variable with upper bound * GLP_DB lb <= x <= ub Double-bounded variable * GLP_FX x = lb Fixed variable * * where x is the structural variable associated with j-th column. * * If the column has no lower bound, the parameter lb is ignored. If the * column has no upper bound, the parameter ub is ignored. If the column * is of fixed type, only the parameter lb is used while the parameter * ub is ignored. */ void glp_set_col_bnds(glp_prob *lp, int j, int type, double lb, double ub) { GLPCOL *col; if (!(1 <= j && j <= lp->n)) xerror("glp_set_col_bnds: j = %d; column number out of range\n" , j); col = lp->col[j]; col->type = type; switch (type) { case GLP_FR: col->lb = col->ub = 0.0; if (col->stat != GLP_BS) col->stat = GLP_NF; break; case GLP_LO: col->lb = lb, col->ub = 0.0; if (col->stat != GLP_BS) col->stat = GLP_NL; break; case GLP_UP: col->lb = 0.0, col->ub = ub; if (col->stat != GLP_BS) col->stat = GLP_NU; break; case GLP_DB: col->lb = lb, col->ub = ub; if (!(col->stat == GLP_BS || col->stat == GLP_NL || col->stat == GLP_NU)) col->stat = (fabs(lb) <= fabs(ub) ? GLP_NL : GLP_NU); break; case GLP_FX: col->lb = col->ub = lb; if (col->stat != GLP_BS) col->stat = GLP_NS; break; default: xerror("glp_set_col_bnds: j = %d; type = %d; invalid column" " type\n", j, type); } return; } /*********************************************************************** * NAME * * glp_set_obj_coef - set (change) obj. coefficient or constant term * * SYNOPSIS * * void glp_set_obj_coef(glp_prob *lp, int j, double coef); * * DESCRIPTION * * The routine glp_set_obj_coef sets (changes) objective coefficient at * j-th column (structural variable) of the specified problem object. * * If the parameter j is 0, the routine sets (changes) the constant term * ("shift") of the objective function. */ void glp_set_obj_coef(glp_prob *lp, int j, double coef) { glp_tree *tree = lp->tree; if (tree != NULL && tree->reason != 0) xerror("glp_set_obj_coef: operation not allowed\n"); if (!(0 <= j && j <= lp->n)) xerror("glp_set_obj_coef: j = %d; column number out of range\n" , j); if (j == 0) lp->c0 = coef; else lp->col[j]->coef = coef; return; } /*********************************************************************** * NAME * * glp_set_mat_row - set (replace) row of the constraint matrix * * SYNOPSIS * * void glp_set_mat_row(glp_prob *lp, int i, int len, const int ind[], * const double val[]); * * DESCRIPTION * * The routine glp_set_mat_row stores (replaces) the contents of i-th * row of the constraint matrix of the specified problem object. * * Column indices and numeric values of new row elements must be placed * in locations ind[1], ..., ind[len] and val[1], ..., val[len], where * 0 <= len <= n is the new length of i-th row, n is the current number * of columns in the problem object. Elements with identical column * indices are not allowed. Zero elements are allowed, but they are not * stored in the constraint matrix. * * If the parameter len is zero, the parameters ind and/or val can be * specified as NULL. */ void glp_set_mat_row(glp_prob *lp, int i, int len, const int ind[], const double val[]) { glp_tree *tree = lp->tree; GLPROW *row; GLPCOL *col; GLPAIJ *aij, *next; int j, k; /* obtain pointer to i-th row */ if (!(1 <= i && i <= lp->m)) xerror("glp_set_mat_row: i = %d; row number out of range\n", i); row = lp->row[i]; if (tree != NULL && tree->reason != 0) { xassert(tree->curr != NULL); xassert(row->level == tree->curr->level); } /* remove all existing elements from i-th row */ while (row->ptr != NULL) { /* take next element in the row */ aij = row->ptr; /* remove the element from the row list */ row->ptr = aij->r_next; /* obtain pointer to corresponding column */ col = aij->col; /* remove the element from the column list */ if (aij->c_prev == NULL) col->ptr = aij->c_next; else aij->c_prev->c_next = aij->c_next; if (aij->c_next == NULL) ; else aij->c_next->c_prev = aij->c_prev; /* return the element to the memory pool */ dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; /* if the corresponding column is basic, invalidate the basis factorization */ if (col->stat == GLP_BS) lp->valid = 0; } /* store new contents of i-th row */ if (!(0 <= len && len <= lp->n)) xerror("glp_set_mat_row: i = %d; len = %d; invalid row length " "\n", i, len); if (len > NNZ_MAX - lp->nnz) xerror("glp_set_mat_row: i = %d; len = %d; too many constraint" " coefficients\n", i, len); for (k = 1; k <= len; k++) { /* take number j of corresponding column */ j = ind[k]; /* obtain pointer to j-th column */ if (!(1 <= j && j <= lp->n)) xerror("glp_set_mat_row: i = %d; ind[%d] = %d; column index" " out of range\n", i, k, j); col = lp->col[j]; /* if there is element with the same column index, it can only be found in the beginning of j-th column list */ if (col->ptr != NULL && col->ptr->row->i == i) xerror("glp_set_mat_row: i = %d; ind[%d] = %d; duplicate co" "lumn indices not allowed\n", i, k, j); /* create new element */ aij = dmp_get_atom(lp->pool, sizeof(GLPAIJ)), lp->nnz++; aij->row = row; aij->col = col; aij->val = val[k]; /* add the new element to the beginning of i-th row and j-th column lists */ aij->r_prev = NULL; aij->r_next = row->ptr; aij->c_prev = NULL; aij->c_next = col->ptr; if (aij->r_next != NULL) aij->r_next->r_prev = aij; if (aij->c_next != NULL) aij->c_next->c_prev = aij; row->ptr = col->ptr = aij; /* if the corresponding column is basic, invalidate the basis factorization */ if (col->stat == GLP_BS && aij->val != 0.0) lp->valid = 0; } /* remove zero elements from i-th row */ for (aij = row->ptr; aij != NULL; aij = next) { next = aij->r_next; if (aij->val == 0.0) { /* remove the element from the row list */ if (aij->r_prev == NULL) row->ptr = next; else aij->r_prev->r_next = next; if (next == NULL) ; else next->r_prev = aij->r_prev; /* remove the element from the column list */ xassert(aij->c_prev == NULL); aij->col->ptr = aij->c_next; if (aij->c_next != NULL) aij->c_next->c_prev = NULL; /* return the element to the memory pool */ dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; } } return; } /*********************************************************************** * NAME * * glp_set_mat_col - set (replace) column of the constraint matrix * * SYNOPSIS * * void glp_set_mat_col(glp_prob *lp, int j, int len, const int ind[], * const double val[]); * * DESCRIPTION * * The routine glp_set_mat_col stores (replaces) the contents of j-th * column of the constraint matrix of the specified problem object. * * Row indices and numeric values of new column elements must be placed * in locations ind[1], ..., ind[len] and val[1], ..., val[len], where * 0 <= len <= m is the new length of j-th column, m is the current * number of rows in the problem object. Elements with identical column * indices are not allowed. Zero elements are allowed, but they are not * stored in the constraint matrix. * * If the parameter len is zero, the parameters ind and/or val can be * specified as NULL. */ void glp_set_mat_col(glp_prob *lp, int j, int len, const int ind[], const double val[]) { glp_tree *tree = lp->tree; GLPROW *row; GLPCOL *col; GLPAIJ *aij, *next; int i, k; if (tree != NULL && tree->reason != 0) xerror("glp_set_mat_col: operation not allowed\n"); /* obtain pointer to j-th column */ if (!(1 <= j && j <= lp->n)) xerror("glp_set_mat_col: j = %d; column number out of range\n", j); col = lp->col[j]; /* remove all existing elements from j-th column */ while (col->ptr != NULL) { /* take next element in the column */ aij = col->ptr; /* remove the element from the column list */ col->ptr = aij->c_next; /* obtain pointer to corresponding row */ row = aij->row; /* remove the element from the row list */ if (aij->r_prev == NULL) row->ptr = aij->r_next; else aij->r_prev->r_next = aij->r_next; if (aij->r_next == NULL) ; else aij->r_next->r_prev = aij->r_prev; /* return the element to the memory pool */ dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; } /* store new contents of j-th column */ if (!(0 <= len && len <= lp->m)) xerror("glp_set_mat_col: j = %d; len = %d; invalid column leng" "th\n", j, len); if (len > NNZ_MAX - lp->nnz) xerror("glp_set_mat_col: j = %d; len = %d; too many constraint" " coefficients\n", j, len); for (k = 1; k <= len; k++) { /* take number i of corresponding row */ i = ind[k]; /* obtain pointer to i-th row */ if (!(1 <= i && i <= lp->m)) xerror("glp_set_mat_col: j = %d; ind[%d] = %d; row index ou" "t of range\n", j, k, i); row = lp->row[i]; /* if there is element with the same row index, it can only be found in the beginning of i-th row list */ if (row->ptr != NULL && row->ptr->col->j == j) xerror("glp_set_mat_col: j = %d; ind[%d] = %d; duplicate ro" "w indices not allowed\n", j, k, i); /* create new element */ aij = dmp_get_atom(lp->pool, sizeof(GLPAIJ)), lp->nnz++; aij->row = row; aij->col = col; aij->val = val[k]; /* add the new element to the beginning of i-th row and j-th column lists */ aij->r_prev = NULL; aij->r_next = row->ptr; aij->c_prev = NULL; aij->c_next = col->ptr; if (aij->r_next != NULL) aij->r_next->r_prev = aij; if (aij->c_next != NULL) aij->c_next->c_prev = aij; row->ptr = col->ptr = aij; } /* remove zero elements from j-th column */ for (aij = col->ptr; aij != NULL; aij = next) { next = aij->c_next; if (aij->val == 0.0) { /* remove the element from the row list */ xassert(aij->r_prev == NULL); aij->row->ptr = aij->r_next; if (aij->r_next != NULL) aij->r_next->r_prev = NULL; /* remove the element from the column list */ if (aij->c_prev == NULL) col->ptr = next; else aij->c_prev->c_next = next; if (next == NULL) ; else next->c_prev = aij->c_prev; /* return the element to the memory pool */ dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; } } /* if j-th column is basic, invalidate the basis factorization */ if (col->stat == GLP_BS) lp->valid = 0; return; } /*********************************************************************** * NAME * * glp_load_matrix - load (replace) the whole constraint matrix * * SYNOPSIS * * void glp_load_matrix(glp_prob *lp, int ne, const int ia[], * const int ja[], const double ar[]); * * DESCRIPTION * * The routine glp_load_matrix loads the constraint matrix passed in * the arrays ia, ja, and ar into the specified problem object. Before * loading the current contents of the constraint matrix is destroyed. * * Constraint coefficients (elements of the constraint matrix) must be * specified as triplets (ia[k], ja[k], ar[k]) for k = 1, ..., ne, * where ia[k] is the row index, ja[k] is the column index, ar[k] is a * numeric value of corresponding constraint coefficient. The parameter * ne specifies the total number of (non-zero) elements in the matrix * to be loaded. Coefficients with identical indices are not allowed. * Zero coefficients are allowed, however, they are not stored in the * constraint matrix. * * If the parameter ne is zero, the parameters ia, ja, and ar can be * specified as NULL. */ void glp_load_matrix(glp_prob *lp, int ne, const int ia[], const int ja[], const double ar[]) { glp_tree *tree = lp->tree; GLPROW *row; GLPCOL *col; GLPAIJ *aij, *next; int i, j, k; if (tree != NULL && tree->reason != 0) xerror("glp_load_matrix: operation not allowed\n"); /* clear the constraint matrix */ for (i = 1; i <= lp->m; i++) { row = lp->row[i]; while (row->ptr != NULL) { aij = row->ptr; row->ptr = aij->r_next; dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; } } xassert(lp->nnz == 0); for (j = 1; j <= lp->n; j++) lp->col[j]->ptr = NULL; /* load the new contents of the constraint matrix and build its row lists */ if (ne < 0) xerror("glp_load_matrix: ne = %d; invalid number of constraint" " coefficients\n", ne); if (ne > NNZ_MAX) xerror("glp_load_matrix: ne = %d; too many constraint coeffici" "ents\n", ne); for (k = 1; k <= ne; k++) { /* take indices of new element */ i = ia[k], j = ja[k]; /* obtain pointer to i-th row */ if (!(1 <= i && i <= lp->m)) xerror("glp_load_matrix: ia[%d] = %d; row index out of rang" "e\n", k, i); row = lp->row[i]; /* obtain pointer to j-th column */ if (!(1 <= j && j <= lp->n)) xerror("glp_load_matrix: ja[%d] = %d; column index out of r" "ange\n", k, j); col = lp->col[j]; /* create new element */ aij = dmp_get_atom(lp->pool, sizeof(GLPAIJ)), lp->nnz++; aij->row = row; aij->col = col; aij->val = ar[k]; /* add the new element to the beginning of i-th row list */ aij->r_prev = NULL; aij->r_next = row->ptr; if (aij->r_next != NULL) aij->r_next->r_prev = aij; row->ptr = aij; } xassert(lp->nnz == ne); /* build column lists of the constraint matrix and check elements with identical indices */ for (i = 1; i <= lp->m; i++) { for (aij = lp->row[i]->ptr; aij != NULL; aij = aij->r_next) { /* obtain pointer to corresponding column */ col = aij->col; /* if there is element with identical indices, it can only be found in the beginning of j-th column list */ if (col->ptr != NULL && col->ptr->row->i == i) { for (k = 1; k <= ne; k++) if (ia[k] == i && ja[k] == col->j) break; xerror("glp_load_mat: ia[%d] = %d; ja[%d] = %d; duplicat" "e indices not allowed\n", k, i, k, col->j); } /* add the element to the beginning of j-th column list */ aij->c_prev = NULL; aij->c_next = col->ptr; if (aij->c_next != NULL) aij->c_next->c_prev = aij; col->ptr = aij; } } /* remove zero elements from the constraint matrix */ for (i = 1; i <= lp->m; i++) { row = lp->row[i]; for (aij = row->ptr; aij != NULL; aij = next) { next = aij->r_next; if (aij->val == 0.0) { /* remove the element from the row list */ if (aij->r_prev == NULL) row->ptr = next; else aij->r_prev->r_next = next; if (next == NULL) ; else next->r_prev = aij->r_prev; /* remove the element from the column list */ if (aij->c_prev == NULL) aij->col->ptr = aij->c_next; else aij->c_prev->c_next = aij->c_next; if (aij->c_next == NULL) ; else aij->c_next->c_prev = aij->c_prev; /* return the element to the memory pool */ dmp_free_atom(lp->pool, aij, sizeof(GLPAIJ)), lp->nnz--; } } } /* invalidate the basis factorization */ lp->valid = 0; return; } /*********************************************************************** * NAME * * glp_check_dup - check for duplicate elements in sparse matrix * * SYNOPSIS * * int glp_check_dup(int m, int n, int ne, const int ia[], * const int ja[]); * * DESCRIPTION * * The routine glp_check_dup checks for duplicate elements (that is, * elements with identical indices) in a sparse matrix specified in the * coordinate format. * * The parameters m and n specifies, respectively, the number of rows * and columns in the matrix, m >= 0, n >= 0. * * The parameter ne specifies the number of (structurally) non-zero * elements in the matrix, ne >= 0. * * Elements of the matrix are specified as doublets (ia[k],ja[k]) for * k = 1,...,ne, where ia[k] is a row index, ja[k] is a column index. * * The routine glp_check_dup can be used prior to a call to the routine * glp_load_matrix to check that the constraint matrix to be loaded has * no duplicate elements. * * RETURNS * * The routine glp_check_dup returns one of the following values: * * 0 - the matrix has no duplicate elements; * * -k - indices ia[k] or/and ja[k] are out of range; * * +k - element (ia[k],ja[k]) is duplicate. */ int glp_check_dup(int m, int n, int ne, const int ia[], const int ja[]) { int i, j, k, *ptr, *next, ret; char *flag; if (m < 0) xerror("glp_check_dup: m = %d; invalid parameter\n"); if (n < 0) xerror("glp_check_dup: n = %d; invalid parameter\n"); if (ne < 0) xerror("glp_check_dup: ne = %d; invalid parameter\n"); if (ne > 0 && ia == NULL) xerror("glp_check_dup: ia = %p; invalid parameter\n", ia); if (ne > 0 && ja == NULL) xerror("glp_check_dup: ja = %p; invalid parameter\n", ja); for (k = 1; k <= ne; k++) { i = ia[k], j = ja[k]; if (!(1 <= i && i <= m && 1 <= j && j <= n)) { ret = -k; goto done; } } if (m == 0 || n == 0) { ret = 0; goto done; } /* allocate working arrays */ ptr = xcalloc(1+m, sizeof(int)); next = xcalloc(1+ne, sizeof(int)); flag = xcalloc(1+n, sizeof(char)); /* build row lists */ for (i = 1; i <= m; i++) ptr[i] = 0; for (k = 1; k <= ne; k++) { i = ia[k]; next[k] = ptr[i]; ptr[i] = k; } /* clear column flags */ for (j = 1; j <= n; j++) flag[j] = 0; /* check for duplicate elements */ for (i = 1; i <= m; i++) { for (k = ptr[i]; k != 0; k = next[k]) { j = ja[k]; if (flag[j]) { /* find first element (i,j) */ for (k = 1; k <= ne; k++) if (ia[k] == i && ja[k] == j) break; xassert(k <= ne); /* find next (duplicate) element (i,j) */ for (k++; k <= ne; k++) if (ia[k] == i && ja[k] == j) break; xassert(k <= ne); ret = +k; goto skip; } flag[j] = 1; } /* clear column flags */ for (k = ptr[i]; k != 0; k = next[k]) flag[ja[k]] = 0; } /* no duplicate element found */ ret = 0; skip: /* free working arrays */ xfree(ptr); xfree(next); xfree(flag); done: return ret; } /*********************************************************************** * NAME * * glp_sort_matrix - sort elements of the constraint matrix * * SYNOPSIS * * void glp_sort_matrix(glp_prob *P); * * DESCRIPTION * * The routine glp_sort_matrix sorts elements of the constraint matrix * rebuilding its row and column linked lists. On exit from the routine * the constraint matrix is not changed, however, elements in the row * linked lists become ordered by ascending column indices, and the * elements in the column linked lists become ordered by ascending row * indices. */ void glp_sort_matrix(glp_prob *P) { GLPAIJ *aij; int i, j; if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_sort_matrix: P = %p; invalid problem object\n", P); /* rebuild row linked lists */ for (i = P->m; i >= 1; i--) P->row[i]->ptr = NULL; for (j = P->n; j >= 1; j--) { for (aij = P->col[j]->ptr; aij != NULL; aij = aij->c_next) { i = aij->row->i; aij->r_prev = NULL; aij->r_next = P->row[i]->ptr; if (aij->r_next != NULL) aij->r_next->r_prev = aij; P->row[i]->ptr = aij; } } /* rebuild column linked lists */ for (j = P->n; j >= 1; j--) P->col[j]->ptr = NULL; for (i = P->m; i >= 1; i--) { for (aij = P->row[i]->ptr; aij != NULL; aij = aij->r_next) { j = aij->col->j; aij->c_prev = NULL; aij->c_next = P->col[j]->ptr; if (aij->c_next != NULL) aij->c_next->c_prev = aij; P->col[j]->ptr = aij; } } return; } /*********************************************************************** * NAME * * glp_del_rows - delete rows from problem object * * SYNOPSIS * * void glp_del_rows(glp_prob *lp, int nrs, const int num[]); * * DESCRIPTION * * The routine glp_del_rows deletes rows from the specified problem * object. Ordinal numbers of rows to be deleted should be placed in * locations num[1], ..., num[nrs], where nrs > 0. * * Note that deleting rows involves changing ordinal numbers of other * rows remaining in the problem object. New ordinal numbers of the * remaining rows are assigned under the assumption that the original * order of rows is not changed. */ void glp_del_rows(glp_prob *lp, int nrs, const int num[]) { glp_tree *tree = lp->tree; GLPROW *row; int i, k, m_new; /* mark rows to be deleted */ if (!(1 <= nrs && nrs <= lp->m)) xerror("glp_del_rows: nrs = %d; invalid number of rows\n", nrs); for (k = 1; k <= nrs; k++) { /* take the number of row to be deleted */ i = num[k]; /* obtain pointer to i-th row */ if (!(1 <= i && i <= lp->m)) xerror("glp_del_rows: num[%d] = %d; row number out of range" "\n", k, i); row = lp->row[i]; if (tree != NULL && tree->reason != 0) { if (!(tree->reason == GLP_IROWGEN || tree->reason == GLP_ICUTGEN)) xerror("glp_del_rows: operation not allowed\n"); xassert(tree->curr != NULL); if (row->level != tree->curr->level) xerror("glp_del_rows: num[%d] = %d; invalid attempt to d" "elete row created not in current subproblem\n", k,i); if (row->stat != GLP_BS) xerror("glp_del_rows: num[%d] = %d; invalid attempt to d" "elete active row (constraint)\n", k, i); tree->reinv = 1; } /* check that the row is not marked yet */ if (row->i == 0) xerror("glp_del_rows: num[%d] = %d; duplicate row numbers n" "ot allowed\n", k, i); /* erase symbolic name assigned to the row */ glp_set_row_name(lp, i, NULL); xassert(row->node == NULL); /* erase corresponding row of the constraint matrix */ glp_set_mat_row(lp, i, 0, NULL, NULL); xassert(row->ptr == NULL); /* mark the row to be deleted */ row->i = 0; } /* delete all marked rows from the row list */ m_new = 0; for (i = 1; i <= lp->m; i++) { /* obtain pointer to i-th row */ row = lp->row[i]; /* check if the row is marked */ if (row->i == 0) { /* it is marked, delete it */ dmp_free_atom(lp->pool, row, sizeof(GLPROW)); } else { /* it is not marked; keep it */ row->i = ++m_new; lp->row[row->i] = row; } } /* set new number of rows */ lp->m = m_new; /* invalidate the basis factorization */ lp->valid = 0; return; } /*********************************************************************** * NAME * * glp_del_cols - delete columns from problem object * * SYNOPSIS * * void glp_del_cols(glp_prob *lp, int ncs, const int num[]); * * DESCRIPTION * * The routine glp_del_cols deletes columns from the specified problem * object. Ordinal numbers of columns to be deleted should be placed in * locations num[1], ..., num[ncs], where ncs > 0. * * Note that deleting columns involves changing ordinal numbers of * other columns remaining in the problem object. New ordinal numbers * of the remaining columns are assigned under the assumption that the * original order of columns is not changed. */ void glp_del_cols(glp_prob *lp, int ncs, const int num[]) { glp_tree *tree = lp->tree; GLPCOL *col; int j, k, n_new; if (tree != NULL && tree->reason != 0) xerror("glp_del_cols: operation not allowed\n"); /* mark columns to be deleted */ if (!(1 <= ncs && ncs <= lp->n)) xerror("glp_del_cols: ncs = %d; invalid number of columns\n", ncs); for (k = 1; k <= ncs; k++) { /* take the number of column to be deleted */ j = num[k]; /* obtain pointer to j-th column */ if (!(1 <= j && j <= lp->n)) xerror("glp_del_cols: num[%d] = %d; column number out of ra" "nge", k, j); col = lp->col[j]; /* check that the column is not marked yet */ if (col->j == 0) xerror("glp_del_cols: num[%d] = %d; duplicate column number" "s not allowed\n", k, j); /* erase symbolic name assigned to the column */ glp_set_col_name(lp, j, NULL); xassert(col->node == NULL); /* erase corresponding column of the constraint matrix */ glp_set_mat_col(lp, j, 0, NULL, NULL); xassert(col->ptr == NULL); /* mark the column to be deleted */ col->j = 0; /* if it is basic, invalidate the basis factorization */ if (col->stat == GLP_BS) lp->valid = 0; } /* delete all marked columns from the column list */ n_new = 0; for (j = 1; j <= lp->n; j++) { /* obtain pointer to j-th column */ col = lp->col[j]; /* check if the column is marked */ if (col->j == 0) { /* it is marked; delete it */ dmp_free_atom(lp->pool, col, sizeof(GLPCOL)); } else { /* it is not marked; keep it */ col->j = ++n_new; lp->col[col->j] = col; } } /* set new number of columns */ lp->n = n_new; /* if the basis header is still valid, adjust it */ if (lp->valid) { int m = lp->m; int *head = lp->head; for (j = 1; j <= n_new; j++) { k = lp->col[j]->bind; if (k != 0) { xassert(1 <= k && k <= m); head[k] = m + j; } } } return; } /*********************************************************************** * NAME * * glp_copy_prob - copy problem object content * * SYNOPSIS * * void glp_copy_prob(glp_prob *dest, glp_prob *prob, int names); * * DESCRIPTION * * The routine glp_copy_prob copies the content of the problem object * prob to the problem object dest. * * The parameter names is a flag. If it is non-zero, the routine also * copies all symbolic names; otherwise, if it is zero, symbolic names * are not copied. */ void glp_copy_prob(glp_prob *dest, glp_prob *prob, int names) { glp_tree *tree = dest->tree; glp_bfcp bfcp; int i, j, len, *ind; double *val; if (tree != NULL && tree->reason != 0) xerror("glp_copy_prob: operation not allowed\n"); if (dest == prob) xerror("glp_copy_prob: copying problem object to itself not al" "lowed\n"); if (!(names == GLP_ON || names == GLP_OFF)) xerror("glp_copy_prob: names = %d; invalid parameter\n", names); glp_erase_prob(dest); if (names && prob->name != NULL) glp_set_prob_name(dest, prob->name); if (names && prob->obj != NULL) glp_set_obj_name(dest, prob->obj); dest->dir = prob->dir; dest->c0 = prob->c0; if (prob->m > 0) glp_add_rows(dest, prob->m); if (prob->n > 0) glp_add_cols(dest, prob->n); glp_get_bfcp(prob, &bfcp); glp_set_bfcp(dest, &bfcp); dest->pbs_stat = prob->pbs_stat; dest->dbs_stat = prob->dbs_stat; dest->obj_val = prob->obj_val; dest->some = prob->some; dest->ipt_stat = prob->ipt_stat; dest->ipt_obj = prob->ipt_obj; dest->mip_stat = prob->mip_stat; dest->mip_obj = prob->mip_obj; for (i = 1; i <= prob->m; i++) { GLPROW *to = dest->row[i]; GLPROW *from = prob->row[i]; if (names && from->name != NULL) glp_set_row_name(dest, i, from->name); to->type = from->type; to->lb = from->lb; to->ub = from->ub; to->rii = from->rii; to->stat = from->stat; to->prim = from->prim; to->dual = from->dual; to->pval = from->pval; to->dval = from->dval; to->mipx = from->mipx; } ind = xcalloc(1+prob->m, sizeof(int)); val = xcalloc(1+prob->m, sizeof(double)); for (j = 1; j <= prob->n; j++) { GLPCOL *to = dest->col[j]; GLPCOL *from = prob->col[j]; if (names && from->name != NULL) glp_set_col_name(dest, j, from->name); to->kind = from->kind; to->type = from->type; to->lb = from->lb; to->ub = from->ub; to->coef = from->coef; len = glp_get_mat_col(prob, j, ind, val); glp_set_mat_col(dest, j, len, ind, val); to->sjj = from->sjj; to->stat = from->stat; to->prim = from->prim; to->dual = from->dual; to->pval = from->pval; to->dval = from->dval; to->mipx = from->mipx; } xfree(ind); xfree(val); return; } /*********************************************************************** * NAME * * glp_erase_prob - erase problem object content * * SYNOPSIS * * void glp_erase_prob(glp_prob *lp); * * DESCRIPTION * * The routine glp_erase_prob erases the content of the specified * problem object. The effect of this operation is the same as if the * problem object would be deleted with the routine glp_delete_prob and * then created anew with the routine glp_create_prob, with exception * that the handle (pointer) to the problem object remains valid. */ static void delete_prob(glp_prob *lp); void glp_erase_prob(glp_prob *lp) { glp_tree *tree = lp->tree; if (tree != NULL && tree->reason != 0) xerror("glp_erase_prob: operation not allowed\n"); delete_prob(lp); create_prob(lp); return; } /*********************************************************************** * NAME * * glp_delete_prob - delete problem object * * SYNOPSIS * * void glp_delete_prob(glp_prob *lp); * * DESCRIPTION * * The routine glp_delete_prob deletes the specified problem object and * frees all the memory allocated to it. */ static void delete_prob(glp_prob *lp) { lp->magic = 0x3F3F3F3F; dmp_delete_pool(lp->pool); #if 0 /* 17/XI-2009 */ xfree(lp->cps); #else if (lp->parms != NULL) xfree(lp->parms); #endif xassert(lp->tree == NULL); #if 0 if (lp->cwa != NULL) xfree(lp->cwa); #endif xfree(lp->row); xfree(lp->col); if (lp->r_tree != NULL) avl_delete_tree(lp->r_tree); if (lp->c_tree != NULL) avl_delete_tree(lp->c_tree); xfree(lp->head); if (lp->bfcp != NULL) xfree(lp->bfcp); if (lp->bfd != NULL) bfd_delete_it(lp->bfd); return; } void glp_delete_prob(glp_prob *lp) { glp_tree *tree = lp->tree; if (tree != NULL && tree->reason != 0) xerror("glp_delete_prob: operation not allowed\n"); delete_prob(lp); xfree(lp); return; } /* eof */ igraph/src/cs_lsolve.c0000644000176000001440000000261012325527073014505 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* solve Lx=b where x and b are dense. x=b on input, solution on output. */ CS_INT cs_lsolve (const cs *L, CS_ENTRY *x) { CS_INT p, j, n, *Lp, *Li ; CS_ENTRY *Lx ; if (!CS_CSC (L) || !x) return (0) ; /* check inputs */ n = L->n ; Lp = L->p ; Li = L->i ; Lx = L->x ; for (j = 0 ; j < n ; j++) { x [j] /= Lx [Lp [j]] ; for (p = Lp [j]+1 ; p < Lp [j+1] ; p++) { x [Li [p]] -= Lx [p] * x [j] ; } } return (1) ; } igraph/src/cs_utsolve.c0000644000176000001440000000264212325527073014707 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* solve U'x=b where x and b are dense. x=b on input, solution on output. */ CS_INT cs_utsolve (const cs *U, CS_ENTRY *x) { CS_INT p, j, n, *Up, *Ui ; CS_ENTRY *Ux ; if (!CS_CSC (U) || !x) return (0) ; /* check inputs */ n = U->n ; Up = U->p ; Ui = U->i ; Ux = U->x ; for (j = 0 ; j < n ; j++) { for (p = Up [j] ; p < Up [j+1]-1 ; p++) { x [j] -= CS_CONJ (Ux [p]) * x [Ui [p]] ; } x [j] /= CS_CONJ (Ux [Up [j+1]-1]) ; } return (1) ; } igraph/src/sparsemat.c0000644000176000001440000022330212325527074014517 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "cs/cs.h" #include "igraph_sparsemat.h" #include "igraph_error.h" #include "igraph_interface.h" #include "igraph_constructors.h" #include "igraph_memory.h" #include "igraph_vector_ptr.h" #include "igraph_attributes.h" #include /** * \section about_sparsemat About sparse matrices * * * The igraph_sparsemat_t data type stores sparse matrices, * i.e. matrices in which the majority of the elements are zero. * * * The data type is essentially a wrapper to some of the * functions in the CXSparse library, by Tim Davis, see * http://www.cise.ufl.edu/research/sparse/CXSparse/ * * * * Matrices can be stored in two formats: triplet and * column-compressed. The triplet format is intended for sparse matrix * initialization, as it is easy to add new (non-zero) elements to * it. Most of the computations are done on sparse matrices in * column-compressed format, after the user has converted the triplet * matrix to column-compressed, via \ref igraph_sparsemat_compress(). * * * * Both formats are dynamic, in the sense that new elements can be * added to them, possibly resulting the allocation of more memory. * * * * Row and column indices follow the C convention and are zero-based. * * * * \example examples/simple/igraph_sparsemat.c * \example examples/simple/igraph_sparsemat2.c * \example examples/simple/igraph_sparsemat3.c * \example examples/simple/igraph_sparsemat4.c * \example examples/simple/igraph_sparsemat5.c * \example examples/simple/igraph_sparsemat6.c * \example examples/simple/igraph_sparsemat7.c * \example examples/simple/igraph_sparsemat8.c * */ /** * \function igraph_sparsemat_init * Initialize a sparse matrix, in triplet format * * This is the most common way to create a sparse matrix, together * with the \ref igraph_sparsemat_entry() function, which can be used to * add the non-zero elements one by one. Once done, the user can call * \ref igraph_sparsemat_compress() to convert the matrix to * column-compressed, to allow computations with it. * * The user must call \ref igraph_sparsemat_destroy() on * the matrix to deallocate the memory, once the matrix is no more * needed. * \param A Pointer to a not yet initialized sparse matrix. * \param rows The number of rows in the matrix. * \param cols The number of columns. * \param nzmax The maximum number of non-zero elements in the * matrix. It is not compulsory to get this right, but it is * useful for the allocation of the proper amount of memory. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax) { if (rows < 0) { IGRAPH_ERROR("Negative number of rows", IGRAPH_EINVAL); } if (cols < 0) { IGRAPH_ERROR("Negative number of columns", IGRAPH_EINVAL); } A->cs=cs_spalloc( rows, cols, nzmax, /*values=*/ 1, /*triplet=*/ 1); if (!A->cs) { IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM); } return 0; } /** * \function igraph_sparsemat_copy * Copy a sparse matrix * * Create a sparse matrix object, by copying another one. The source * matrix can be either in triplet or column-compressed format. * * * Exactly the same amount of memory will be allocated to the * copy matrix, as it is currently for the original one. * \param to Pointer to an uninitialized sparse matrix, the copy will * be created here. * \param from The sparse matrix to copy. * \return Error code. * * Time complexity: O(n+nzmax), the number of columns plus the maximum * number of non-zero elements. */ int igraph_sparsemat_copy(igraph_sparsemat_t *to, const igraph_sparsemat_t *from) { int ne=from->cs->nz == -1 ? from->cs->n+1 : from->cs->nzmax; to->cs = cs_spalloc(from->cs->m, from->cs->n, from->cs->nzmax, /*values=*/ 1, /*triplet=*/ igraph_sparsemat_is_triplet(from)); to->cs->nzmax = from->cs->nzmax; to->cs->m = from->cs->m; to->cs->n = from->cs->n; to->cs->nz = from->cs->nz; memcpy(to->cs->p, from->cs->p, sizeof(int) * (size_t) ne); memcpy(to->cs->i, from->cs->i, sizeof(int) * (size_t) (from->cs->nzmax)); memcpy(to->cs->x, from->cs->x, sizeof(double) * (size_t) (from->cs->nzmax)); return 0; } /** * \function igraph_sparsemat_destroy * Deallocate memory used by a sparse matrix * * One destroyed, the sparse matrix must be initialized again, before * calling any other operation on it. * \param A The sparse matrix to destroy. * * Time complexity: O(1). */ void igraph_sparsemat_destroy(igraph_sparsemat_t *A) { cs_spfree(A->cs); } /** * \function igraph_sparsemat_realloc * Allocate more (or less) memory for a sparse matrix * * Sparse matrices automatically allocate more memory, as needed. To * control memory allocation, the user can call this function, to * allocate memory for a given number of non-zero elements. * \param A The sparse matrix, it can be in triplet or * column-compressed format. * \param nzmax The new maximum number of non-zero elements. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax) { return !cs_sprealloc(A->cs, nzmax); } /** * \function igraph_sparsemat_nrow * Number of rows * * \param A The input matrix, in triplet or column-compressed format. * \return The number of rows in the \p A matrix. * * Time complexity: O(1). */ long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { return A->cs->m; } /** * \function igraph_sparsemat_ncol * Number of columns. * * \param A The input matrix, in triplet or column-compressed format. * \return The number of columns in the \p A matrix. * * Time complexity: O(1). */ long int igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { return A->cs->n; } /** * \function igraph_sparsemat_type * Type of a sparse matrix (triplet or column-compressed) * * Gives whether a sparse matrix is stored in the triplet format or in * column-compressed format. * \param A The input matrix. * \return Either \c IGRAPH_SPARSEMAT_CC or \c * IGRAPH_SPARSEMAT_TRIPLET. * * Time complexity: O(1). */ igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A) { return A->cs->nz < 0 ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET; } /** * \function igraph_sparsemat_is_triplet * Is this sparse matrix in triplet format? * * Decides whether a sparse matrix is in triplet format. * \param A The input matrix. * \return One if the input matrix is in triplet format, zero * otherwise. * * Time complexity: O(1). */ igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A) { return A->cs->nz >= 0; } /** * \function igraph_sparsemat_is_cc * Is this sparse matrix in column-compressed format? * * Decides whether a sparse matrix is in column-compressed format. * \param A The input matrix. * \return One if the input matrix is in column-compressed format, zero * otherwise. * * Time complexity: O(1). */ igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A) { return A->cs->nz < 0; } /** * \function igraph_sparsemat_permute * Permute the rows and columns of a sparse matrix * * \param A The input matrix, it must be in column-compressed format. * \param p Integer vector, giving the permutation of the rows. * \param q Integer vector, the permutation of the columns. * \param res Pointer to an uninitialized sparse matrix, the result is * stored here. * \return Error code. * * Time complexity: O(m+n+nz), the number of rows plus the number of * columns plus the number of non-zero elements in the matrix. */ int igraph_sparsemat_permute(const igraph_sparsemat_t *A, const igraph_vector_int_t *p, const igraph_vector_int_t *q, igraph_sparsemat_t *res) { long int nrow=A->cs->m, ncol=A->cs->n; igraph_vector_int_t pinv; long int i; if (nrow != igraph_vector_int_size(p)) { IGRAPH_ERROR("Invalid row permutation length", IGRAPH_FAILURE); } if (ncol != igraph_vector_int_size(q)) { IGRAPH_ERROR("Invalid column permutation length", IGRAPH_FAILURE); } /* We invert the permutation by hand */ IGRAPH_CHECK(igraph_vector_int_init(&pinv, nrow)); IGRAPH_FINALLY(igraph_vector_int_destroy, &pinv); for (i=0; ics = cs_permute(A->cs, VECTOR(pinv), VECTOR(*q), /*values=*/ 1))) { IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE); } igraph_vector_int_destroy(&pinv); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A, const igraph_vector_int_t *p, igraph_sparsemat_t *res, igraph_real_t *constres) { igraph_sparsemat_t II, II2; long int nrow=A->cs->m; long int idx_rows=igraph_vector_int_size(p); long int k; /* Create index matrix */ IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow, (int) idx_rows)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2); for (k=0; kcs->p[1] != 0) { *constres = res->cs->x[0]; } else { *constres = 0.0; } } return 0; } int igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, const igraph_vector_int_t *q, igraph_sparsemat_t *res, igraph_real_t *constres) { igraph_sparsemat_t JJ, JJ2; long int ncol=A->cs->n; long int idx_cols=igraph_vector_int_size(q); long int k; /* Create index matrix */ IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, (int) ncol, (int) idx_cols, (int) idx_cols)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2); for (k=0; kcs->p [1] != 0) { *constres = res->cs->x [0]; } else { *constres = 0.0; } } return 0; } /** * \function igraph_sparsemat_index * Index a sparse matrix, extract a submatrix, or a single element * * This function serves two purposes. First, it can extract * submatrices from a sparse matrix. Second, as a special case, it can * extract a single element from a sparse matrix. * \param A The input matrix, it must be in column-compressed format. * \param p An integer vector, or a null pointer. The selected row * index or indices. A null pointer selects all rows. * \param q An integer vector, or a null pointer. The selected column * index or indices. A null pointer selects all columns. * \param res Pointer to an uninitialized sparse matrix, or a null * pointer. If not a null pointer, then the selected submatrix is * stored here. * \param constres Pointer to a real variable or a null pointer. If * not a null pointer, then the first non-zero element in the * selected submatrix is stored here, if there is one. Otherwise * zero is stored here. This behavior is handy if one * wants to select a single entry from the matrix. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_index(const igraph_sparsemat_t *A, const igraph_vector_int_t *p, const igraph_vector_int_t *q, igraph_sparsemat_t *res, igraph_real_t *constres) { igraph_sparsemat_t II, JJ, II2, JJ2, tmp; long int nrow=A->cs->m; long int ncol=A->cs->n; long int idx_rows= p ? igraph_vector_int_size(p) : -1; long int idx_cols= q ? igraph_vector_int_size(q) : -1; long int k; igraph_sparsemat_t *myres=res, mres; if (!p && !q) { IGRAPH_ERROR("No index vectors", IGRAPH_EINVAL); } if (!res && (idx_rows != 1 || idx_cols != 1)) { IGRAPH_ERROR("Sparse matrix indexing: must give `res' if not a " "single element is selected", IGRAPH_EINVAL); } if (!q) { return igraph_i_sparsemat_index_rows(A, p, res, constres); } if (!p) { return igraph_i_sparsemat_index_cols(A, q, res, constres); } if (!res) { myres=&mres; } /* Create first index matrix */ IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow, (int) idx_rows)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2); for (k=0; kcs->p [1] != 0) { *constres = myres->cs->x [0]; } else { *constres = 0.0; } } if (!res) { igraph_sparsemat_destroy(myres); } return 0; } /** * \function igraph_sparsemat_entry * Add an element to a sparse matrix * * This function can be used to add the entries to a sparse matrix, * after initializing it with \ref igraph_sparsemat_init(). * \param A The input matrix, it must be in triplet format. * \param row The row index of the entry to add. * \param col The column index of the entry to add. * \param elem The value of the entry. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, igraph_real_t elem) { if (!cs_entry(A->cs, row, col, elem)) { IGRAPH_ERROR("Cannot add entry to sparse matrix", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_compress * Compress a sparse matrix, i.e. convert it to column-compress format * * Almost all sparse matrix operations require that the matrix is in * column-compressed format. * \param A The input matrix, it must be in triplet format. * \param res Pointer to an uninitialized sparse matrix object, the * compressed version of \p A is stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_compress(const igraph_sparsemat_t *A, igraph_sparsemat_t *res) { if (! (res->cs=cs_compress(A->cs)) ) { IGRAPH_ERROR("Cannot compress sparse matrix", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_transpose * Transpose a sparse matrix * * \param A The input matrix, column-compressed or triple format. * \param res Pointer to an uninitialized sparse matrix, the result is * stored here. * \param values If this is non-zero, the matrix transpose is * calculated the normal way. If it is zero, then only the pattern * of the input matrix is stored in the result, the values are not. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_transpose(const igraph_sparsemat_t *A, igraph_sparsemat_t *res, int values) { if (A->cs->nz < 0) { /* column-compressed */ if (! (res->cs=cs_transpose(A->cs, values)) ) { IGRAPH_ERROR("Cannot transpose sparse matrix", IGRAPH_FAILURE); } } else { /* triplets */ int *tmp; IGRAPH_CHECK(igraph_sparsemat_copy(res, A)); tmp = res->cs->p; res->cs->p = res->cs->i; res->cs->i = tmp; } return 0; } igraph_bool_t igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A) { igraph_sparsemat_t t, tt; igraph_bool_t res; int nz; IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t, /*values=*/ 1)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &t); IGRAPH_CHECK(igraph_sparsemat_dupl(&t)); IGRAPH_CHECK(igraph_sparsemat_transpose(&t, &tt, /*values=*/ 1)); igraph_sparsemat_destroy(&t); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_sparsemat_destroy, &tt); IGRAPH_CHECK(igraph_sparsemat_transpose(&tt, &t, /*values=*/ 1)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &t); nz=t.cs->p[t.cs->n]; res = memcmp(t.cs->i, tt.cs->i, sizeof(int) * (size_t) nz) == 0; res = res && memcmp(t.cs->p, tt.cs->p, sizeof(int) * (size_t)(t.cs->n+1)) == 0; res = res && memcmp(t.cs->x, tt.cs->x, sizeof(igraph_real_t) * (size_t)nz)==0; igraph_sparsemat_destroy(&t); igraph_sparsemat_destroy(&tt); IGRAPH_FINALLY_CLEAN(2); return res; } igraph_bool_t igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A) { igraph_sparsemat_t tmp; igraph_bool_t res; IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); res=igraph_i_sparsemat_is_symmetric_cc(&tmp); igraph_sparsemat_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return res; } igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A) { if (A->cs->m != A->cs->n) { return 0; } if (A->cs->nz < 0) { return igraph_i_sparsemat_is_symmetric_cc(A); } else { return igraph_i_sparsemat_is_symmetric_triplet(A); } } /** * \function igraph_sparsemat_dupl * Remove duplicate elements from a sparse matrix * * It is possible that a column-compressed sparse matrix stores a * single matrix entry in multiple pieces. The entry is then the sum * of all its pieces. (Some functions create matrices like this.) This * function eliminates the multiple pieces. * \param A The input matrix, in column-compressed format. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_dupl(igraph_sparsemat_t *A) { if (!cs_dupl(A->cs)) { IGRAPH_ERROR("Cannot remove duplicates from sparse matrix", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_fkeep * Filter the elements of a sparse matrix * * This function can be used to filter the (non-zero) elements of a * sparse matrix. For all entries, it calls the supplied function and * depending on the return values either keeps, or deleted the element * from the matrix. * \param A The input matrix, in column-compressed format. * \param fkeep The filter function. It must take four arguments: the * first is an \c int, the row index of the entry, the second is * another \c int, the column index. The third is \c igraph_real_t, * the value of the entry. The fourth element is a \c void pointer, * the \p other argument is passed here. The function must return * an \c int. If this is zero, then the entry is deleted, otherwise * it is kept. * \param other A \c void pointer that is passed to the filtering * function. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_fkeep(igraph_sparsemat_t *A, int (*fkeep)(int, int, igraph_real_t, void*), void *other) { if (!cs_fkeep(A->cs, fkeep, other)) { IGRAPH_ERROR("Cannot filter sparse matrix", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_dropzeros * Drop the zero elements from a sparse matrix * * As a result of matrix operations, some of the entries in a sparse * matrix might be zero. This function removes these entries. * \param A The input matrix, it must be in column-compressed format. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { if (!cs_dropzeros(A->cs)) { IGRAPH_ERROR("Cannot drop zeros from sparse matrix", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_droptol * Drop the almost zero elements of a sparse matrix * * This function is similar to \ref igraph_sparsemat_dropzeros(), but it * also drops entries that are closer to zero than the given tolerance * threshold. * \param A The input matrix, it must be in column-compressed format. * \param tol Real number, giving the tolerance threshold. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { if (!cs_droptol(A->cs, tol)) { IGRAPH_ERROR("Cannot drop (almost) zeros from sparse matrix", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_multiply * Matrix multiplication * * Multiplies two sparse matrices. * \param A The first input matrix (left hand side), in * column-compressed format. * \param B The second input matrix (right hand side), in * column-compressed format. * \param res Pointer to an uninitialized sparse matrix, the result is * stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_sparsemat_t *res) { if (! (res->cs=cs_multiply(A->cs, B->cs))) { IGRAPH_ERROR("Cannot multiply matrices", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_add * Sum of two sparse matrices * * \param A The first input matrix, in column-compressed format. * \param B The second input matrix, in column-compressed format. * \param alpha Real scalar, \p A is multiplied by \p alpha before the * addition. * \param beta Real scalar, \p B is multiplied by \p beta before the * addition. * \param res Pointer to an uninitialized sparse matrix, the result * is stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_add(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_real_t alpha, igraph_real_t beta, igraph_sparsemat_t *res) { if (! (res->cs=cs_add(A->cs, B->cs, alpha, beta))) { IGRAPH_ERROR("Cannot add matrices", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_gaxpy * Matrix-vector product, added to another vector. * * \param A The input matrix, in column-compressed format. * \param x The input vector, its size must match the number of * columns in \p A. * \param res This vector is added to the matrix-vector product * and it is overwritten by the result. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, const igraph_vector_t *x, igraph_vector_t *res) { if (A->cs->n != igraph_vector_size(x) || A->cs->m != igraph_vector_size(res)) { IGRAPH_ERROR("Invalid matrix/vector size for multiplication", IGRAPH_EINVAL); } if (! (cs_gaxpy(A->cs, VECTOR(*x), VECTOR(*res)))) { IGRAPH_ERROR("Cannot perform sparse matrix vector multiplication", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_lsolve * Solve a lower-triangular linear system * * Solve the Lx=b linear equation system, where the L coefficient * matrix is square and lower-triangular, with a zero-free diagonal. * \param L The input matrix, in column-compressed format. * \param b The right hand side of the linear system. * \param res An initialized vector, the result is stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, const igraph_vector_t *b, igraph_vector_t *res) { if (L->cs->m != L->cs->n) { IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_NONSQUARE); } if (res != b) { IGRAPH_CHECK(igraph_vector_update(res, b)); } if (! cs_lsolve(L->cs, VECTOR(*res))) { IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_ltsolve * Solve an upper-triangular linear system * * Solve the L'x=b linear equation system, where the L * matrix is square and lower-triangular, with a zero-free diagonal. * \param L The input matrix, in column-compressed format. * \param b The right hand side of the linear system. * \param res An initialized vector, the result is stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, const igraph_vector_t *b, igraph_vector_t *res) { if (L->cs->m != L->cs->n) { IGRAPH_ERROR("Cannot perform transposed lower triangular solve", IGRAPH_NONSQUARE); } if (res != b) { IGRAPH_CHECK(igraph_vector_update(res,b)); } if (!cs_ltsolve(L->cs, VECTOR(*res))) { IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_usolve * Solve an upper-triangular linear system * * Solves the Ux=b upper triangular system. * \param U The input matrix, in column-compressed format. * \param b The right hand side of the linear system. * \param res An initialized vector, the result is stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_usolve(const igraph_sparsemat_t *U, const igraph_vector_t *b, igraph_vector_t *res) { if (U->cs->m != U->cs->n) { IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_NONSQUARE); } if (res != b) { IGRAPH_CHECK(igraph_vector_update(res, b)); } if (! cs_usolve(U->cs, VECTOR(*res))) { IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_utsolve * Solve a lower-triangular linear system * * This is the same as \ref igraph_sparsemat_usolve(), but U'x=b is * solved, where the apostrophe denotes the transpose. * \param U The input matrix, in column-compressed format. * \param b The right hand side of the linear system. * \param res An initialized vector, the result is stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, const igraph_vector_t *b, igraph_vector_t *res) { if (U->cs->m != U->cs->n) { IGRAPH_ERROR("Cannot perform transposed upper triangular solve", IGRAPH_NONSQUARE); } if (res != b) { IGRAPH_CHECK(igraph_vector_update(res,b)); } if (!cs_utsolve(U->cs, VECTOR(*res))) { IGRAPH_ERROR("Cannot perform transposed upper triangular solve", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_cholsol * Solve a symmetric linear system via Cholesky decomposition * * Solve Ax=b, where A is a symmetric positive definite matrix. * \param A The input matrix, in column-compressed format. * \param v The right hand side. * \param res An initialized vector, the result is stored here. * \param order An integer giving the ordering method to use for the * factorization. Zero is the natural ordering; if it is one, then * the fill-reducing minimum-degree ordering of A+A' is used. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, int order) { if (A->cs->m != A->cs->n) { IGRAPH_ERROR("Cannot perform sparse symmetric solve", IGRAPH_NONSQUARE); } if (res != b) { IGRAPH_CHECK(igraph_vector_update(res,b)); } if (! cs_cholsol(order, A->cs, VECTOR(*res))) { IGRAPH_ERROR("Cannot perform sparse symmetric solve", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_lusol * Solve a linear system via LU decomposition * * Solve Ax=b, via LU factorization of A. * \param A The input matrix, in column-compressed format. * \param b The right hand side of the equation. * \param res An initialized vector, the result is stored here. * \param order The ordering method to use, zero means the natural * ordering, one means the fill-reducing minimum-degree ordering of * A+A', two means the ordering of A'*A, after removing the dense * rows from A. Three means the ordering of A'*A. * \param tol Real number, the tolerance limit to use for the numeric * LU factorization. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, int order, igraph_real_t tol) { if (A->cs->m != A->cs->n) { IGRAPH_ERROR("Cannot perform LU solve", IGRAPH_NONSQUARE); } if (res != b) { IGRAPH_CHECK(igraph_vector_update(res,b)); } if (! cs_lusol(order, A->cs, VECTOR(*res), tol)) { IGRAPH_ERROR("Cannot perform LU solve", IGRAPH_FAILURE); } return 0; } int igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { igraph_vector_t edges; long int no_of_nodes=A->cs->m; long int no_of_edges=A->cs->p[A->cs->n]; int *p=A->cs->p; int *i=A->cs->i; long int from=0; long int to=0; long int e=0; if (no_of_nodes != A->cs->n) { IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); } IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2); while (*p < no_of_edges) { while (to < *(p+1)) { if (directed || from >= *i) { VECTOR(edges)[e++] = from; VECTOR(edges)[e++] = (*i); } to++; i++; } from++; p++; } igraph_vector_resize(&edges, e); IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { igraph_vector_t edges; long int no_of_nodes=A->cs->m; long int no_of_edges=A->cs->nz; int *i=A->cs->p; int *j=A->cs->i; long int e; if (no_of_nodes != A->cs->n) { IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); } IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges*2); for (e=0; e<2*no_of_edges; i++, j++) { if (directed || *i >= *j) { VECTOR(edges)[e++] = (*i); VECTOR(edges)[e++] = (*j); } } igraph_vector_resize(&edges, e); IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_sparsemat * Create an igraph graph from a sparse matrix * * One edge is created for each non-zero entry in the matrix. If you * have a symmetric matrix, and want to create an undirected graph, * then delete the entries in the upper diagonal first, or call \ref * igraph_simplify() on the result graph to eliminate the multiple * edges. * \param graph Pointer to an uninitialized igraph_t object, the * graphs is stored here. * \param A The input matrix, in triplet or column-compressed format. * \param directed Boolean scalar, whether to create a directed * graph. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { if (A->cs->nz < 0) { return(igraph_i_sparsemat_cc(graph, A, directed)); } else { return(igraph_i_sparsemat_triplet(graph, A, directed)); } } int igraph_i_weighted_sparsemat_cc(const igraph_sparsemat_t *A, igraph_bool_t directed, const char *attr, igraph_bool_t loops, igraph_vector_t *edges, igraph_vector_t *weights) { long int no_of_edges=A->cs->p[A->cs->n]; int *p=A->cs->p; int *i=A->cs->i; igraph_real_t *x=A->cs->x; long int from=0; long int to=0; long int e=0, w=0; IGRAPH_UNUSED(attr); igraph_vector_resize(edges, no_of_edges*2); igraph_vector_resize(weights, no_of_edges); while (*p < no_of_edges) { while (to < *(p+1)) { if ( (loops || from != *i) && (directed || from >= *i) && *x != 0) { VECTOR(*edges)[e++] = (*i); VECTOR(*edges)[e++] = from; VECTOR(*weights)[w++] = (*x); } to++; i++; x++; } from++; p++; } igraph_vector_resize(edges, e); igraph_vector_resize(weights, w); return 0; } int igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat_t *A, igraph_bool_t directed, const char *attr, igraph_bool_t loops, igraph_vector_t *edges, igraph_vector_t *weights) { IGRAPH_UNUSED(A); IGRAPH_UNUSED(directed); IGRAPH_UNUSED(attr); IGRAPH_UNUSED(loops); IGRAPH_UNUSED(edges); IGRAPH_UNUSED(weights); /* TODO */ IGRAPH_ERROR("Triplet matrices are not implemented", IGRAPH_UNIMPLEMENTED); return 0; } int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed, const char *attr, igraph_bool_t loops) { igraph_vector_t edges, weights; int pot_edges= A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; const char* default_attr = "weight"; igraph_vector_ptr_t attr_vec; igraph_attribute_record_t attr_rec; long int no_of_nodes=A->cs->m; if (no_of_nodes != A->cs->n) { IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); } IGRAPH_VECTOR_INIT_FINALLY(&edges, pot_edges*2); IGRAPH_VECTOR_INIT_FINALLY(&weights, pot_edges); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1); if (A->cs->nz < 0) { IGRAPH_CHECK(igraph_i_weighted_sparsemat_cc(A, directed, attr, loops, &edges, &weights)); } else { IGRAPH_CHECK(igraph_i_weighted_sparsemat_triplet(A, directed, attr, loops, &edges, &weights)); } /* Prepare attribute record */ attr_rec.name = attr ? attr : default_attr; attr_rec.type = IGRAPH_ATTRIBUTE_NUMERIC; attr_rec.value = &weights; VECTOR(attr_vec)[0] = &attr_rec; /* Create graph */ IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, directed)); IGRAPH_FINALLY(igraph_destroy, graph); if (igraph_vector_size(&edges)>0) { IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); } IGRAPH_FINALLY_CLEAN(1); /* Cleanup */ igraph_vector_destroy(&edges); igraph_vector_destroy(&weights); igraph_vector_ptr_destroy(&attr_vec); IGRAPH_FINALLY_CLEAN(3); return 0; } /** * \function igraph_get_sparsemat * Convert an igraph graph to a sparse matrix * * If the graph is undirected, then a symmetric matrix is created. * \param graph The input graph. * \param res Pointer to an uninitialized sparse matrix. The result * will be stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_bool_t directed=igraph_is_directed(graph); long int nzmax= directed ? no_of_edges : no_of_edges*2; long int i; IGRAPH_CHECK(igraph_sparsemat_init(res, (igraph_integer_t) no_of_nodes, (igraph_integer_t) no_of_nodes, (igraph_integer_t) nzmax)); for (i=0; ics->nz < 0) { /* CC */ int j, p; for (j=0; jcs->n; j++) { CHECK(fprintf(outstream, "col %i: locations %i to %i\n", j, A->cs->p[j], A->cs->p[j+1]-1)); for (p=A->cs->p[j]; p < A->cs->p[j+1]; p++) { CHECK(fprintf(outstream, "%i : %g\n", A->cs->i[p], A->cs->x[p])); } } } else { /* Triplet */ int p; for (p=0; pcs->nz; p++) { CHECK(fprintf(outstream, "%i %i : %g\n", A->cs->i[p], A->cs->p[p], A->cs->x[p])); } } return 0; } #undef CHECK int igraph_i_sparsemat_eye_triplet(igraph_sparsemat_t *A, int n, int nzmax, igraph_real_t value) { long int i; IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax)); for (i=0; ics = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0)) ) { IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE); } for (i=0; ics->p [i] = (int) i; A->cs->i [i] = (int) i; A->cs->x [i] = value; } A->cs->p [n] = n; return 0; } /** * \function igraph_sparsemat_eye * Create a sparse identity matrix * * \param A An uninitialized sparse matrix, the result is stored * here. * \param n The number of rows and number of columns in the matrix. * \param nzmax The maximum number of non-zero elements, this * essentially gives the amount of memory that will be allocated for * matrix elements. * \param value The value to store in the diagonal. * \param compress Whether to create a column-compressed matrix. If * false, then a triplet matrix is created. * \return Error code. * * Time complexity: O(n). */ int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax, igraph_real_t value, igraph_bool_t compress) { if (compress) { return(igraph_i_sparsemat_eye_cc(A, n, value)); } else { return(igraph_i_sparsemat_eye_triplet(A, n, nzmax, value)); } } int igraph_i_sparsemat_diag_triplet(igraph_sparsemat_t *A, int nzmax, const igraph_vector_t *values) { int i, n=(int) igraph_vector_size(values); IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax)); for (i=0; ics = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0)) ) { IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE); } for (i=0; ics->p [i] = i; A->cs->i [i] = i; A->cs->x [i] = VECTOR(*values)[i]; } A->cs->p [n] = n; return 0; } /** * \function igraph_sparsemat_diag * Create a sparse diagonal matrix * * \param A An uninitialized sparse matrix, the result is stored * here. * \param nzmax The maximum number of non-zero elements, this * essentially gives the amount of memory that will be allocated for * matrix elements. * \param values The values to store in the diagonal, the size of the * matrix defined by the length of this vector. * \param compress Whether to create a column-compressed matrix. If * false, then a triplet matrix is created. * \return Error code. * * Time complexity: O(n), the length of the diagonal vector. */ int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax, const igraph_vector_t *values, igraph_bool_t compress) { if (compress) { return(igraph_i_sparsemat_diag_cc(A, values)); } else { return(igraph_i_sparsemat_diag_triplet(A, nzmax, values)); } } int igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_sparsemat_t *A=extra; igraph_vector_t vto, vfrom; igraph_vector_view(&vto, to, n); igraph_vector_view(&vfrom, from, n); igraph_vector_null(&vto); IGRAPH_CHECK(igraph_sparsemat_gaxpy(A, &vfrom, &vto)); return 0; } typedef struct igraph_i_sparsemat_arpack_rssolve_data_t { igraph_sparsemat_symbolic_t *dis; igraph_sparsemat_numeric_t *din; igraph_real_t tol; igraph_sparsemat_solve_t method; } igraph_i_sparsemat_arpack_rssolve_data_t; int igraph_i_sparsemat_arpack_solve(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_sparsemat_arpack_rssolve_data_t *data=extra; igraph_vector_t vfrom, vto; igraph_vector_view(&vfrom, from, n); igraph_vector_view(&vto, to, n); if (data->method == IGRAPH_SPARSEMAT_SOLVE_LU) { IGRAPH_CHECK(igraph_sparsemat_luresol(data->dis, data->din, &vfrom, &vto)); } else if (data->method == IGRAPH_SPARSEMAT_SOLVE_QR) { IGRAPH_CHECK(igraph_sparsemat_qrresol(data->dis, data->din, &vfrom, &vto)); } return 0; } /** * \function igraph_sparsemat_arpack_rssolve * Eigenvalues and eigenvectors of a symmetric sparse matrix via ARPACK * * \param The input matrix, must be column-compressed. * \param options It is passed to \ref igraph_arpack_rssolve(). See * \ref igraph_arpack_options_t for the details. If \c mode is 1, * then ARPACK uses regular mode, if \c mode is 3, then shift and * invert mode is used and the \c sigma structure member defines * the shift. * \param storage Storage for ARPACK. See \ref * igraph_arpack_rssolve() and \ref igraph_arpack_storage_t for * details. * \param values An initialized vector or a null pointer, the * eigenvalues are stored here. * \param vectors An initialised matrix, or a null pointer, the * eigenvectors are stored here, in the columns. * \param solvemethod The method to solve the linear system, if \c * mode is 3, i.e. the shift and invert mode is used. * Possible values: * \clist * \cli IGRAPH_SPARSEMAT_SOLVE_LU * The linear system is solved using LU decomposition. * \cli IGRAPH_SPARSEMAT_SOLVE_QR * The linear system is solved using QR decomposition. * \endclist * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_vector_t *values, igraph_matrix_t *vectors, igraph_sparsemat_solve_t solvemethod) { int n=(int) igraph_sparsemat_nrow(A); if (n != igraph_sparsemat_ncol(A)) { IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); } options->n=n; if (options->mode==1) { IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_multiply, (void*) A, options, storage, values, vectors)); } else if (options->mode==3) { igraph_real_t sigma=options->sigma; igraph_sparsemat_t OP, eye; igraph_sparsemat_symbolic_t symb; igraph_sparsemat_numeric_t num; igraph_i_sparsemat_arpack_rssolve_data_t data; /*-----------------------------------*/ /* We need to factor the (A-sigma*I) */ /*-----------------------------------*/ /* Create (A-sigma*I) */ IGRAPH_CHECK(igraph_sparsemat_eye(&eye, /*n=*/ n, /*nzmax=*/ n, /*value=*/ -sigma, /*compress=*/ 1)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &eye); IGRAPH_CHECK(igraph_sparsemat_add(/*A=*/ A, /*B=*/ &eye, /*alpha=*/ 1.0, /*beta=*/ 1.0, /*res=*/ &OP)); igraph_sparsemat_destroy(&eye); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_sparsemat_destroy, &OP); if (solvemethod==IGRAPH_SPARSEMAT_SOLVE_LU) { /* Symbolic analysis */ IGRAPH_CHECK(igraph_sparsemat_symblu(/*order=*/ 0, &OP, &symb)); IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb); /* Numeric LU factorization */ IGRAPH_CHECK(igraph_sparsemat_lu(&OP, &symb, &num, /*tol=*/ 0)); IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num); } else if (solvemethod==IGRAPH_SPARSEMAT_SOLVE_QR) { /* Symbolic analysis */ IGRAPH_CHECK(igraph_sparsemat_symbqr(/*order=*/ 0, &OP, &symb)); IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb); /* Numeric QR factorization */ IGRAPH_CHECK(igraph_sparsemat_qr(&OP, &symb, &num)); IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num); } data.dis=&symb; data.din=# data.tol=options->tol; data.method=solvemethod; IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_solve, (void*) &data, options, storage, values, vectors)); igraph_sparsemat_numeric_destroy(&num); igraph_sparsemat_symbolic_destroy(&symb); igraph_sparsemat_destroy(&OP); IGRAPH_FINALLY_CLEAN(3); } return 0; } /** * \function igraph_sparsemat_arpack_rnsolve * Eigenvalues and eigenvectors of a nonsymmetric sparse matrix via ARPACK * * Eigenvalues and/or eigenvectors of a nonsymmetric sparse matrix. * \param A The input matrix, in column-compressed mode. * \param options ARPACK options, it is passed to \ref * igraph_arpack_rnsolve(). See also \ref igraph_arpack_options_t * for details. * \param storage Storage for ARPACK, this is passed to \ref * igraph_arpack_rnsolve(). See \ref igraph_arpack_storage_t for * details. * \param values An initialized matrix, or a null pointer. If not a * null pointer, then the eigenvalues are stored here, the first * column is the real part, the second column is the imaginary * part. * \param vectors An initialized matrix, or a null pointer. If not a * null pointer, then the eigenvectors are stored here, please see * \ref igraph_arpack_rnsolve() for the format. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, igraph_arpack_options_t *options, igraph_arpack_storage_t *storage, igraph_matrix_t *values, igraph_matrix_t *vectors) { int n=(int) igraph_sparsemat_nrow(A); if (n != igraph_sparsemat_ncol(A)) { IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); } options->n=n; return igraph_arpack_rnsolve(igraph_i_sparsemat_arpack_multiply, (void*) A, options, storage, values, vectors); } /** * \function igraph_sparsemat_symbqr * Symbolic QR decomposition * * QR decomposition of sparse matrices involves two steps, the first * is calling this function, and then \ref * igraph_sparsemat_qr(). * \param order The ordering to use: 0 means natural ordering, 1 means * minimum degree ordering of A+A', 2 is minimum degree ordering of * A'A after removing the dense rows from A, and 3 is the minimum * degree ordering of A'A. * \param A The input matrix, in column-compressed format. * \param dis The result of the symbolic analysis is stored here. Once * not needed anymore, it must be destroyed by calling \ref * igraph_sparsemat_symbolic_destroy(). * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis) { dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 1); if (!dis->symbolic) { IGRAPH_ERROR("Cannot do symbolic QR decomposition", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_symblu * Symbolic LU decomposition * * LU decomposition of sparse matrices involves two steps, the first * is calling this function, and then \ref igraph_sparsemat_lu(). * \param order The ordering to use: 0 means natural ordering, 1 means * minimum degree ordering of A+A', 2 is minimum degree ordering of * A'A after removing the dense rows from A, and 3 is the minimum * degree ordering of A'A. * \param A The input matrix, in column-compressed format. * \param dis The result of the symbolic analysis is stored here. Once * not needed anymore, it must be destroyed by calling \ref * igraph_sparsemat_symbolic_destroy(). * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis) { dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 0); if (!dis->symbolic) { IGRAPH_ERROR("Cannot do symbolic LU decomposition", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_lu * LU decomposition of a sparse matrix * * Performs numeric sparse LU decomposition of a matrix. * \param A The input matrix, in column-compressed format. * \param dis The symbolic analysis for LU decomposition, coming from * a call to the \ref igraph_sparsemat_symblu() function. * \param din The numeric decomposition, the result is stored here. It * can be used to solve linear systems with changing right hand * side vectors, by calling \ref igraph_sparsemat_luresol(). Once * not needed any more, it must be destroyed by calling \ref * igraph_sparsemat_symbolic_destroy() on it. * \param tol The tolerance for the numeric LU decomposition. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_lu(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din, double tol) { din->numeric=cs_lu(A->cs, dis->symbolic, tol); if (!din->numeric) { IGRAPH_ERROR("Cannot do LU decomposition", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_qr * QR decomposition of a sparse matrix * * Numeric QR decomposition of a sparse matrix. * \param A The input matrix, in column-compressed format. * \param dis The result of the symbolic QR analysis, from the * function \ref igraph_sparsemat_symbqr(). * \param din The result of the decomposition is stored here, it can * be used to solve many linear systems with the same coefficient * matrix and changing right hand sides, using the \ref * igraph_sparsemat_qrresol() function. Once not needed any more, * one should call \ref igraph_sparsemat_numeric_destroy() on it to * free the allocated memory. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_qr(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din) { din->numeric=cs_qr(A->cs, dis->symbolic); if (!din->numeric) { IGRAPH_ERROR("Cannot do QR decomposition", IGRAPH_FAILURE); } return 0; } /** * \function igraph_sparsemat_luresol * Solve linear system using a precomputed LU decomposition * * Uses the LU decomposition of a matrix to solve linear systems. * \param dis The symbolic analysis of the coefficient matrix, the * result of \ref igraph_sparsemat_symblu(). * \param din The LU decomposition, the result of a call to \ref * igraph_sparsemat_lu(). * \param b A vector that defines the right hand side of the linear * equation system. * \param res An initialized vector, the solution of the linear system * is stored here. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, const igraph_sparsemat_numeric_t *din, const igraph_vector_t *b, igraph_vector_t *res) { int n=din->numeric->L->n; igraph_real_t *workspace; if (res != b) { IGRAPH_CHECK(igraph_vector_update(res, b)); } workspace=igraph_Calloc(n, igraph_real_t); if (!workspace) { IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, workspace); if (!cs_ipvec(din->numeric->pinv, VECTOR(*res), workspace, n)) { IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); } if (!cs_lsolve(din->numeric->L, workspace)) { IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); } if (!cs_usolve(din->numeric->U, workspace)) { IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); } if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) { IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); } igraph_Free(workspace); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_sparsemat_qrresol * Solve a linear system using a precomputed QR decomposition * * Solves a linear system using a QR decomposition of its coefficient * matrix. * \param dis Symbolic analysis of the coefficient matrix, the result * of \ref igraph_sparsemat_symbqr(). * \param din The QR decomposition of the coefficient matrix, the * result of \ref igraph_sparsemat_qr(). * \param b Vector, giving the right hand side of the linear equation * system. * \param res An initialized vector, the solution is stored here. It * is resized as needed. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, const igraph_sparsemat_numeric_t *din, const igraph_vector_t *b, igraph_vector_t *res) { int n=din->numeric->L->n; igraph_real_t *workspace; int k; if (res != b) { IGRAPH_CHECK(igraph_vector_update(res, b)); } workspace=igraph_Calloc(dis->symbolic ? dis->symbolic->m2 : 1, igraph_real_t); if (!workspace) { IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); } IGRAPH_FINALLY(igraph_free, workspace); if (!cs_ipvec(dis->symbolic->pinv, VECTOR(*res), workspace, n)) { IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); } for (k=0; knumeric->L, k, din->numeric->B[k], workspace)) { IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); } } if (!cs_usolve(din->numeric->U, workspace)) { IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); } if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) { IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); } igraph_Free(workspace); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_sparsemat_symbolic_destroy * Deallocate memory for a symbolic decomposition * * Frees the memory allocated by \ref igraph_sparsemat_symbqr() or * \ref igraph_sparsemat_symblu(). * \param dis The symbolic analysis. * * Time complexity: O(1). */ void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis) { cs_sfree(dis->symbolic); dis->symbolic=0; } /** * \function igraph_sparsemat_numeric_destroy * Deallocate memory for a numeric decomposition * * Frees the memoty allocated by \ref igraph_sparsemat_qr() or \ref * igraph_sparsemat_lu(). * \param din The LU or QR decomposition. * * Time complexity: O(1). */ void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din) { cs_nfree(din->numeric); din->numeric=0; } /** * \function igraph_matrix_as_sparsemat * Convert a dense matrix to a sparse matrix * * \param res An uninitialized sparse matrix, the result is stored * here. * \param mat The dense input matrix. * \param tol Real scalar, the tolerance. Values closer than \p tol to * zero are considered as zero, and will not be included in the * sparse matrix. * \return Error code. * * Time complexity: O(mn), the number of elements in the dense * matrix. */ int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, const igraph_matrix_t *mat, igraph_real_t tol) { int nrow=(int) igraph_matrix_nrow(mat); int ncol=(int) igraph_matrix_ncol(mat); int i, j, nzmax=0; for (i=0; i tol) { nzmax++; } } } IGRAPH_CHECK(igraph_sparsemat_init(res, nrow, ncol, nzmax)); for (i=0; i tol) { IGRAPH_CHECK(igraph_sparsemat_entry(res, i, j, MATRIX(*mat, i, j))); } } } return 0; } int igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, const igraph_sparsemat_t *spmat) { int nrow=(int) igraph_sparsemat_nrow(spmat); int ncol=(int) igraph_sparsemat_ncol(spmat); int *p=spmat->cs->p; int *i=spmat->cs->i; igraph_real_t *x=spmat->cs->x; int nzmax=spmat->cs->nzmax; int from=0, to=0; IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol)); igraph_matrix_null(res); while (*p < nzmax) { while (to < *(p+1)) { MATRIX(*res, *i, from) += *x; to++; i++; x++; } from++; p++; } return 0; } int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, const igraph_sparsemat_t *spmat) { int nrow=(int) igraph_sparsemat_nrow(spmat); int ncol=(int) igraph_sparsemat_ncol(spmat); int *i=spmat->cs->p; int *j=spmat->cs->i; igraph_real_t *x=spmat->cs->x; int nz=spmat->cs->nz; int e; IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol)); igraph_matrix_null(res); for (e=0; ecs->nz < 0) { return(igraph_i_sparsemat_as_matrix_cc(res, spmat)); } else { return(igraph_i_sparsemat_as_matrix_triplet(res, spmat)); } } /** * \function igraph_sparsemat_max * Maximum of a sparse matrix * * \param A The input matrix, column-compressed. * \return The maximum in the input matrix, or \c IGRAPH_NEGINFINITY * if the matrix has zero elements. * * Time complexity: TODO. */ igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A) { int i, n; igraph_real_t *ptr; igraph_real_t res; IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr=A->cs->x; n = A->cs->nz==-1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n==0) { return IGRAPH_NEGINFINITY; } res = *ptr; for (i=1; i res) { res=*ptr; } } return res; } /* TODO: CC matrix don't actually need _dupl, because the elements are right beside each other. Same for max and minmax. */ /** * \function igraph_sparsemat_min * Minimum of a sparse matrix * * \param A The input matrix, column-compressed. * \return The minimum in the input matrix, or \c IGRAPH_POSINFINITY * if the matrix has zero elements. * * Time complexity: TODO. */ igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) { int i, n; igraph_real_t *ptr; igraph_real_t res; IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr=A->cs->x; n = A->cs->nz==-1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n==0) { return IGRAPH_POSINFINITY; } res = *ptr; for (i=1; ics->x; n = A->cs->nz==-1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n==0) { *min=IGRAPH_POSINFINITY; *max=IGRAPH_NEGINFINITY; return 0; } *min = *max = *ptr; for (i=1; i *max) { *max=*ptr; } else if (*ptr < *min) { *min=*ptr; } } return 0; } /** * \function igraph_sparsemat_count_nonzero * Count nonzero elements of a sparse matrix * * \param A The input matrix, column-compressed. * \return Error code. * * Time complexity: TODO. */ long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { int i, n; int res=0; igraph_real_t *ptr; IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr=A->cs->x; n = A->cs->nz==-1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n==0) { return 0; } for (i=0; ics->x; n = A->cs->nz==-1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n==0) { return 0; } for (i=0; i tol) { res++; } } return res; } int igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A, igraph_vector_t *res) { int i; int *pi=A->cs->i; double *px=A->cs->x; IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); igraph_vector_null(res); for (i=0; ics->nz; i++, pi++, px++) { VECTOR(*res)[ *pi ] += *px; } return 0; } int igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, igraph_vector_t *res) { int ne=A->cs->p[A->cs->n]; double *px=A->cs->x; int *pi=A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); igraph_vector_null(res); for (; pi < A->cs->i+ne; pi++, px++) { VECTOR(*res)[ *pi ] += *px; } return 0; } /** * \function igraph_sparsemat_rowsums * Row-wise sums. * * \param A The input matrix, in triplet or column-compressed format. * \param res An initialized vector, the result is stored here. It * will be resized as needed. * \return Error code. * * Time complexity: O(nz), the number of non-zero elements. */ int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, igraph_vector_t *res) { if (igraph_sparsemat_is_triplet(A)) { return igraph_i_sparsemat_rowsums_triplet(A, res); } else { return igraph_i_sparsemat_rowsums_cc(A, res); } } int igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A, igraph_vector_t *res) { int i; int *pp=A->cs->p; double *px=A->cs->x; IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); igraph_vector_null(res); for (i=0; ics->nz; i++, pp++, px++) { VECTOR(*res)[ *pp ] += *px; } return 0; } int igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, igraph_vector_t *res) { int n=A->cs->n; double *px=A->cs->x; int *pp=A->cs->p; int *pi=A->cs->i; double *pr; IGRAPH_CHECK(igraph_vector_resize(res, n)); igraph_vector_null(res); pr=VECTOR(*res); for (; pp < A->cs->p + n; pp++, pr++) { for (; pi < A->cs->i + *(pp+1); pi++, px++) { *pr += *px; } } return 0; } /** * \function igraph_sparsemat_colsums * Column-wise sums * * \param A The input matrix, in triplet or column-compressed format. * \param res An initialized vector, the result is stored here. It * will be resized as needed. * \return Error code. * * Time complexity: O(nz) for triplet matrices, O(nz+n) for * column-compressed ones, nz is the number of non-zero elements, n is * the number of columns. */ int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, igraph_vector_t *res) { if (igraph_sparsemat_is_triplet(A)) { return igraph_i_sparsemat_colsums_triplet(A, res); } else { return igraph_i_sparsemat_colsums_cc(A, res); } } /** * \function igraph_sparsemat_scale * Scale a sparse matrix * * Multiplies all elements of a sparse matrix, by the given scalar. * \param A The input matrix. * \param by The scaling factor. * \return Error code. * * Time complexity: O(nz), the number of non-zero elements in the * matrix. */ int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { double *px = A->cs->x; int n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; double *stop=px+n; for (; px < stop; px++) { *px *= by; } return 0; } /** * \function igraph_sparsemat_add_rows * Add rows to a sparse matrix * * The current matrix elements are retained and all elements in the * new rows are zero. * \param A The input matrix, in triplet or column-compressed format. * \param n The number of rows to add. * \return Error code. * * Time complexity: O(1). */ int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n) { A->cs->m += n; return 0; } /** * \function igraph_sparsemat_add_cols * Add columns to a sparse matrix * * The current matrix elements are retained, and all elements in the * new columns are zero. * \param A The input matrix, in triplet or column-compressed format. * \param n The number of columns to add. * \return Error code. * * Time complexity: TODO. */ int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n) { if (igraph_sparsemat_is_triplet(A)) { A->cs->n += n; } else { int *newp=realloc(A->cs->p, sizeof(int) * (size_t) (A->cs->n + n + 1)); int i; if (!newp) { IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM); } if (newp != A->cs->p) { A->cs->p=newp; } for (i=A->cs->n+1; ics->n + n + 1; i++) { A->cs->p[i]=A->cs->p[i-1]; } A->cs->n += n; } return 0; } /** * \function igraph_sparsemat_resize * Resize a sparse matrix * * This function resizes a sparse matrix. The resized sparse matrix * will be empty. * * \param A The initialized sparse matrix to resize. * \param nrow The new number of rows. * \param ncol The new number of columns. * \param nzmax The new maximum number of elements. * \return Error code. * * Time complexity: O(nzmax), the maximum number of non-zero elements. */ int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, long int ncol, int nzmax) { if (A->cs->nz < 0) { igraph_sparsemat_t tmp; IGRAPH_CHECK(igraph_sparsemat_init(&tmp, (int) nrow, (int) ncol, nzmax)); igraph_sparsemat_destroy(A); *A = tmp; } else { IGRAPH_CHECK(igraph_sparsemat_realloc(A, nzmax)); A->cs->m = (int) nrow; A->cs->n = (int) ncol; A->cs->nz = 0; } return 0; } int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A) { if (A->cs->nz < 0) { return A->cs->p[A->cs->n]; } else { return A->cs->nz; } } int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x) { int nz=A->cs->nz; if (nz < 0) { nz=A->cs->p[A->cs->n]; IGRAPH_CHECK(igraph_vector_int_resize(i, nz)); IGRAPH_CHECK(igraph_vector_int_resize(j, A->cs->n+1)); IGRAPH_CHECK(igraph_vector_resize(x, nz)); memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(int)); memcpy(VECTOR(*j), A->cs->p, (size_t) (A->cs->n+1) * sizeof(int)); memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(igraph_real_t)); } else { IGRAPH_CHECK(igraph_vector_int_resize(i, nz)); IGRAPH_CHECK(igraph_vector_int_resize(j, nz)); IGRAPH_CHECK(igraph_vector_resize(x, nz)); memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(int)); memcpy(VECTOR(*j), A->cs->p, (size_t) nz * sizeof(int)); memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(igraph_real_t)); } return 0; } int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, const igraph_vector_t *fact) { int *i=A->cs->i; igraph_real_t *x=A->cs->x; int no_of_edges=A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; int e; for (e=0; ecs->i; igraph_real_t *x=A->cs->x; int no_of_edges=A->cs->p[A->cs->n]; int e; int c=0; /* actual column */ for (e=0; ecs->n && A->cs->p[c+1] == e) { c++; } f=VECTOR(*fact)[c]; (*x) *= f; } return 0; } int igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A, const igraph_vector_t *fact) { int *j=A->cs->p; igraph_real_t *x=A->cs->x; int no_of_edges=A->cs->nz; int e; for (e=0; ecs->nz < 0) { return igraph_i_sparsemat_scale_cols_cc(A, fact); } else { return igraph_i_sparsemat_scale_cols_triplet(A, fact); } } int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, const igraph_matrix_t *B, igraph_matrix_t *res) { int m=(int) igraph_sparsemat_nrow(A); int n=(int) igraph_sparsemat_ncol(A); int p=(int) igraph_matrix_ncol(B); int i; if (igraph_matrix_nrow(B) != n) { IGRAPH_ERROR("Invalid dimensions in sparse-dense matrix product", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(res, m, p)); igraph_matrix_null(res); for (i=0; ics, &MATRIX(*B, 0, i), &MATRIX(*res, 0, i)))) { IGRAPH_ERROR("Cannot perform sparse-dense matrix multiplication", IGRAPH_FAILURE); } } return 0; } int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, const igraph_sparsemat_t *B, igraph_matrix_t *res) { int m=(int) igraph_matrix_nrow(A); int n=(int) igraph_matrix_ncol(A); int p=(int) igraph_sparsemat_ncol(B); int r, c; int *Bp=B->cs->p; if (igraph_sparsemat_nrow(B) != n) { IGRAPH_ERROR("Invalid dimensions in dense-sparse matrix product", IGRAPH_EINVAL); } if (!igraph_sparsemat_is_cc(B)) { IGRAPH_ERROR("Dense-sparse product is only implemented for " "column-compressed sparse matrices", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(res, m, p)); igraph_matrix_null(res); for (c=0; ccs->i[idx]) * B->cs->x[idx]; idx++; } } Bp++; } return 0; } int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, int *p, int *i, double *x, int nz) { A->cs = cs_calloc(1, sizeof(cs_di)); A->cs->nzmax = nzmax; A->cs->m = m; A->cs->n = n; A->cs->p = p; A->cs->i = i; A->cs->x = x; A->cs->nz = nz; return 0; } int igraph_sparsemat_sort(const igraph_sparsemat_t *A, igraph_sparsemat_t *sorted) { igraph_sparsemat_t tmp; IGRAPH_CHECK(igraph_sparsemat_transpose(A, &tmp, /*values=*/ 1)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); IGRAPH_CHECK(igraph_sparsemat_transpose(&tmp, sorted, /*values=*/ 1)); igraph_sparsemat_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x) { if (A->cs->nz < 0) { igraph_sparsemat_t tmp; IGRAPH_CHECK(igraph_sparsemat_sort(A, &tmp)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); IGRAPH_CHECK(igraph_sparsemat_getelements(&tmp, i, j, x)); igraph_sparsemat_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); } else { IGRAPH_CHECK(igraph_sparsemat_getelements(A, i, j, x)); } return 0; } int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) { return A->cs->nzmax; } int igraph_sparsemat_neg(igraph_sparsemat_t *A) { int i, nz=A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; igraph_real_t *px=A->cs->x; for (i=0; imat=sparsemat; igraph_sparsemat_iterator_reset(it); return 0; } int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) { it->pos=0; if (!igraph_sparsemat_is_triplet(it->mat)) { it->col=0; while (it->col < it->mat->cs->n && it->mat->cs->p[it->col+1] == it->pos) { it->col ++; } } return 0; } igraph_bool_t igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it) { int nz=it->mat->cs->nz == -1 ? it->mat->cs->p[it->mat->cs->n] : it->mat->cs->nz; return it->pos >= nz; } int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { return it->mat->cs->i[it->pos]; } int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it) { if (igraph_sparsemat_is_triplet(it->mat)) { return it->mat->cs->p[it->pos]; } else { return it->col; } } igraph_real_t igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it) { return it->mat->cs->x[it->pos]; } int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) { it->pos += 1; while (it->col < it->mat->cs->n && it->mat->cs->p[it->col+1] == it->pos) { it->col++; } return it->pos; } int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { return it->pos; } igraph/src/zeta.c0000644000176000001440000001101012325527074013452 0ustar ripleyusers/* specfunc/zeta.c * * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004 Gerard Jungman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Author: G. Jungman */ /* This file was taken from the GNU Scientific Library. Some modifications * were done in order to make it independent from the rest of GSL */ /* #include #include #include #include #include #include #include #include #include "error.h" #include "chebyshev.h" #include "cheb_eval.c" */ #include #include #include "error.h" /*-*-*-*-*-*-*-*-*-*- From gsl_machine.h -*-*-*-*-*-*-*-*-*-*-*-*-*/ #define GSL_LOG_DBL_MIN (-7.0839641853226408e+02) #define GSL_LOG_DBL_MAX 7.0978271289338397e+02 #define GSL_DBL_EPSILON 2.2204460492503131e-16 /*-*-*-*-*-*-*-*-*-* From gsl_sf_result.h *-*-*-*-*-*-*-*-*-*-*-*/ struct gsl_sf_result_struct { double val; double err; }; typedef struct gsl_sf_result_struct gsl_sf_result; /*-*-*-*-*-*-*-*-*-*-*-* Private Section *-*-*-*-*-*-*-*-*-*-*-*/ /* coefficients for Maclaurin summation in hzeta() * B_{2j}/(2j)! */ static double hzeta_c[15] = { 1.00000000000000000000000000000, 0.083333333333333333333333333333, -0.00138888888888888888888888888889, 0.000033068783068783068783068783069, -8.2671957671957671957671957672e-07, 2.0876756987868098979210090321e-08, -5.2841901386874931848476822022e-10, 1.3382536530684678832826980975e-11, -3.3896802963225828668301953912e-13, 8.5860620562778445641359054504e-15, -2.1748686985580618730415164239e-16, 5.5090028283602295152026526089e-18, -1.3954464685812523340707686264e-19, 3.5347070396294674716932299778e-21, -8.9535174270375468504026113181e-23 }; /*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/ static int gsl_sf_hzeta_e(const double s, const double q, gsl_sf_result * result) { /* CHECK_POINTER(result) */ if(s <= 1.0 || q <= 0.0) { PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL); } else { const double max_bits = 54.0; const double ln_term0 = -s * log(q); if(ln_term0 < GSL_LOG_DBL_MIN + 1.0) { PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); } else if(ln_term0 > GSL_LOG_DBL_MAX - 1.0) { PLFIT_ERROR("overflow", PLFIT_OVERFLOW); } else if((s > max_bits && q < 1.0) || (s > 0.5*max_bits && q < 0.25)) { result->val = pow(q, -s); result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val); return PLFIT_SUCCESS; } else if(s > 0.5*max_bits && q < 1.0) { const double p1 = pow(q, -s); const double p2 = pow(q/(1.0+q), s); const double p3 = pow(q/(2.0+q), s); result->val = p1 * (1.0 + p2 + p3); result->err = GSL_DBL_EPSILON * (0.5*s + 2.0) * fabs(result->val); return PLFIT_SUCCESS; } else { /* Euler-Maclaurin summation formula * [Moshier, p. 400, with several typo corrections] */ const int jmax = 12; const int kmax = 10; int j, k; const double pmax = pow(kmax + q, -s); double scp = s; double pcp = pmax / (kmax + q); double ans = pmax*((kmax+q)/(s-1.0) + 0.5); for(k=0; kval = ans; result->err = 2.0 * (jmax + 1.0) * GSL_DBL_EPSILON * fabs(ans); return PLFIT_SUCCESS; } } } /*-*-*-*-*-*-*-*-*-* Functions w/ Natural Prototypes *-*-*-*-*-*-*-*-*-*-*/ double gsl_sf_hzeta(const double s, const double a) { gsl_sf_result result; gsl_sf_hzeta_e(s, a, &result); return result.val; } igraph/src/dseigt.f0000644000176000001440000001227112325527073014002 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdseigt c c\Description: c Compute the eigenvalues of the current symmetric tridiagonal matrix c and the corresponding error bounds given the current residual norm. c c\Usage: c call igraphdseigt c ( RNORM, N, H, LDH, EIG, BOUNDS, WORKL, IERR ) c c\Arguments c RNORM Double precision scalar. (INPUT) c RNORM contains the residual norm corresponding to the current c symmetric tridiagonal matrix H. c c N Integer. (INPUT) c Size of the symmetric tridiagonal matrix H. c c H Double precision N by 2 array. (INPUT) c H contains the symmetric tridiagonal matrix with the c subdiagonal in the first column starting at H(2,1) and the c main diagonal in igraphsecond column. c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c EIG Double precision array of length N. (OUTPUT) c On output, EIG contains the N eigenvalues of H possibly c unsorted. The BOUNDS arrays are returned in the c same sorted order as EIG. c c BOUNDS Double precision array of length N. (OUTPUT) c On output, BOUNDS contains the error estimates corresponding c to the eigenvalues EIG. This is equal to RNORM times the c last components of the eigenvectors corresponding to the c eigenvalues in EIG. c c WORKL Double precision work array of length 3*N. (WORKSPACE) c Private (replicated) array on each PE or array allocated on c the front end. c c IERR Integer. (OUTPUT) c Error exit flag from igraphdstqrb. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c igraphdstqrb ARPACK routine that computes the eigenvalues and the c last components of the eigenvectors of a symmetric c and tridiagonal matrix. c igraphsecond ARPACK utility routine for timing. c igraphdvout ARPACK utility routine that prints vectors. c dcopy Level 1 BLAS that copies one vector to another. c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.4' c c\SCCS Information: @(#) c FILE: seigt.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 c c\Remarks c None c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdseigt & ( rnorm, n, h, ldh, eig, bounds, workl, ierr ) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c integer ierr, ldh, n Double precision & rnorm c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & eig(n), bounds(n), h(ldh,2), workl(3*n) c c %------------% c | Parameters | c %------------% c Double precision & zero parameter (zero = 0.0D+0) c c %---------------% c | Local Scalars | c %---------------% c integer i, k, msglvl c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, igraphdstqrb, igraphdvout, igraphsecond c c %-----------------------% c | Executable Statements | c %-----------------------% c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = mseigt c if (msglvl .gt. 0) then call igraphdvout (logfil, n, h(1,2), ndigit, & '_seigt: main diagonal of matrix H') if (n .gt. 1) then call igraphdvout (logfil, n-1, h(2,1), ndigit, & '_seigt: sub diagonal of matrix H') end if end if c call dcopy (n, h(1,2), 1, eig, 1) call dcopy (n-1, h(2,1), 1, workl, 1) call igraphdstqrb (n, eig, workl, bounds, workl(n+1), ierr) if (ierr .ne. 0) go to 9000 if (msglvl .gt. 1) then call igraphdvout (logfil, n, bounds, ndigit, & '_seigt: last row of the eigenvector matrix for H') end if c c %-----------------------------------------------% c | Finally determine the error bounds associated | c | with the n Ritz values of H. | c %-----------------------------------------------% c do 30 k = 1, n bounds(k) = rnorm*abs(bounds(k)) 30 continue c call igraphsecond (t1) tseigt = tseigt + (t1 - t0) c 9000 continue return c c %---------------% c | End of igraphdseigt | c %---------------% c end igraph/src/dneigh.f0000644000176000001440000002444612325527073013770 0ustar ripleyusersc----------------------------------------------------------------------- c\BeginDoc c c\Name: igraphdneigh c c\Description: c Compute the eigenvalues of the current upper Hessenberg matrix c and the corresponding Ritz estimates given the current residual norm. c c\Usage: c call igraphdneigh c ( RNORM, N, H, LDH, RITZR, RITZI, BOUNDS, Q, LDQ, WORKL, IERR ) c c\Arguments c RNORM Double precision scalar. (INPUT) c Residual norm corresponding to the current upper Hessenberg c matrix H. c c N Integer. (INPUT) c Size of the matrix H. c c H Double precision N by N array. (INPUT) c H contains the current upper Hessenberg matrix. c c LDH Integer. (INPUT) c Leading dimension of H exactly as declared in the calling c program. c c RITZR, Double precision arrays of length N. (OUTPUT) c RITZI On output, RITZR(1:N) (resp. RITZI(1:N)) contains the real c (respectively imaginary) parts of the eigenvalues of H. c c BOUNDS Double precision array of length N. (OUTPUT) c On output, BOUNDS contains the Ritz estimates associated with c the eigenvalues RITZR and RITZI. This is equal to RNORM c times the last components of the eigenvectors corresponding c to the eigenvalues in RITZR and RITZI. c c Q Double precision N by N array. (WORKSPACE) c Workspace needed to store the eigenvectors of H. c c LDQ Integer. (INPUT) c Leading dimension of Q exactly as declared in the calling c program. c c WORKL Double precision work array of length N**2 + 3*N. (WORKSPACE) c Private (replicated) array on each PE or array allocated on c the front end. This is needed to keep the full Schur form c of H and also in the calculation of the eigenvectors of H. c c IERR Integer. (OUTPUT) c Error exit flag from igraphdlaqrb or dtrevc. c c\EndDoc c c----------------------------------------------------------------------- c c\BeginLib c c\Local variables: c xxxxxx real c c\Routines called: c igraphdlaqrb ARPACK routine to compute the real Schur form of an c upper Hessenberg matrix and last row of the Schur vectors. c igraphsecond ARPACK utility routine for timing. c igraphdmout ARPACK utility routine that prints matrices c igraphdvout ARPACK utility routine that prints vectors. c dlacpy LAPACK matrix copy routine. c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. c dtrevc LAPACK routine to compute the eigenvectors of a matrix c in upper quasi-triangular form c dgemv Level 2 BLAS routine for matrix vector multiplication. c dcopy Level 1 BLAS that copies one vector to another . c dnrm2 Level 1 BLAS that computes the norm of a vector. c dscal Level 1 BLAS that scales a vector. c c c\Author c Danny Sorensen Phuong Vu c Richard Lehoucq CRPC / Rice University c Dept. of Computational & Houston, Texas c Applied Mathematics c Rice University c Houston, Texas c c\Revision history: c xx/xx/92: Version ' 2.1' c c\SCCS Information: @(#) c FILE: neigh.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 c c\Remarks c None c c\EndLib c c----------------------------------------------------------------------- c subroutine igraphdneigh (rnorm, n, h, ldh, ritzr, ritzi, bounds, & q, ldq, workl, ierr) c c %----------------------------------------------------% c | Include files for debugging and timing information | c %----------------------------------------------------% c include 'debug.h' include 'stat.h' c c %------------------% c | Scalar Arguments | c %------------------% c integer ierr, n, ldh, ldq Double precision & rnorm c c %-----------------% c | Array Arguments | c %-----------------% c Double precision & bounds(n), h(ldh,n), q(ldq,n), ritzi(n), ritzr(n), & workl(n*(n+3)) c c %------------% c | Parameters | c %------------% c Double precision & one, zero parameter (one = 1.0D+0, zero = 0.0D+0) c c %------------------------% c | Local Scalars & Arrays | c %------------------------% c logical select(1) integer i, iconj, msglvl Double precision & temp, vl(1) c c %----------------------% c | External Subroutines | c %----------------------% c external dcopy, dlacpy, igraphdlaqrb, dtrevc, igraphdvout, & igraphsecond c c %--------------------% c | External Functions | c %--------------------% c Double precision & dlapy2, dnrm2 external dlapy2, dnrm2 c c %---------------------% c | Intrinsic Functions | c %---------------------% c intrinsic abs c c %-----------------------% c | Executable Statements | c %-----------------------% c c c %-------------------------------% c | Initialize timing statistics | c | & message level for debugging | c %-------------------------------% c call igraphsecond (t0) msglvl = mneigh c if (msglvl .gt. 2) then call igraphdmout (logfil, n, n, h, ldh, ndigit, & '_neigh: Entering upper Hessenberg matrix H ') end if c c %-----------------------------------------------------------% c | 1. Compute the eigenvalues, the last components of the | c | corresponding Schur vectors and the full Schur form T | c | of the current upper Hessenberg matrix H. | c | igraphdlaqrb returns the full Schur form of H in WORKL(1:N**2) | c | and the last components of the Schur vectors in BOUNDS. | c %-----------------------------------------------------------% c call dlacpy ('All', n, n, h, ldh, workl, n) call igraphdlaqrb (.true., n, 1, n, workl, n, ritzr, ritzi, & bounds, ierr) if (ierr .ne. 0) go to 9000 c if (msglvl .gt. 1) then call igraphdvout (logfil, n, bounds, ndigit, & '_neigh: last row of the Schur matrix for H') end if c c %-----------------------------------------------------------% c | 2. Compute the eigenvectors of the full Schur form T and | c | apply the last components of the Schur vectors to get | c | the last components of the corresponding eigenvectors. | c | Remember that if the i-th and (i+1)-st eigenvalues are | c | complex conjugate pairs, then the real & imaginary part | c | of the eigenvector components are split across adjacent | c | columns of Q. | c %-----------------------------------------------------------% c call dtrevc ('R', 'A', select, n, workl, n, vl, n, q, ldq, & n, n, workl(n*n+1), ierr) c if (ierr .ne. 0) go to 9000 c c %------------------------------------------------% c | Scale the returning eigenvectors so that their | c | euclidean norms are all one. LAPACK subroutine | c | dtrevc returns each eigenvector normalized so | c | that the element of largest magnitude has | c | magnitude 1; here the magnitude of a complex | c | number (x,y) is taken to be |x| + |y|. | c %------------------------------------------------% c iconj = 0 do 10 i=1, n if ( abs( ritzi(i) ) .le. zero ) then c c %----------------------% c | Real eigenvalue case | c %----------------------% c temp = dnrm2( n, q(1,i), 1 ) call dscal ( n, one / temp, q(1,i), 1 ) else c c %-------------------------------------------% c | Complex conjugate pair case. Note that | c | since the real and imaginary part of | c | the eigenvector are stored in consecutive | c | columns, we further normalize by the | c | square root of two. | c %-------------------------------------------% c if (iconj .eq. 0) then temp = dlapy2( dnrm2( n, q(1,i), 1 ), & dnrm2( n, q(1,i+1), 1 ) ) call dscal ( n, one / temp, q(1,i), 1 ) call dscal ( n, one / temp, q(1,i+1), 1 ) iconj = 1 else iconj = 0 end if end if 10 continue c call dgemv ('T', n, n, one, q, ldq, bounds, 1, zero, workl, 1) c if (msglvl .gt. 1) then call igraphdvout (logfil, n, workl, ndigit, & '_neigh: Last row of the eigenvector matrix for H') end if c c %----------------------------% c | Compute the Ritz estimates | c %----------------------------% c iconj = 0 do 20 i = 1, n if ( abs( ritzi(i) ) .le. zero ) then c c %----------------------% c | Real eigenvalue case | c %----------------------% c bounds(i) = rnorm * abs( workl(i) ) else c c %-------------------------------------------% c | Complex conjugate pair case. Note that | c | since the real and imaginary part of | c | the eigenvector are stored in consecutive | c | columns, we need to take the magnitude | c | of the last components of the two vectors | c %-------------------------------------------% c if (iconj .eq. 0) then bounds(i) = rnorm * dlapy2( workl(i), workl(i+1) ) bounds(i+1) = bounds(i) iconj = 1 else iconj = 0 end if end if 20 continue c if (msglvl .gt. 2) then call igraphdvout (logfil, n, ritzr, ndigit, & '_neigh: Real part of the eigenvalues of H') call igraphdvout (logfil, n, ritzi, ndigit, & '_neigh: Imaginary part of the eigenvalues of H') call igraphdvout (logfil, n, bounds, ndigit, & '_neigh: Ritz estimates for the eigenvalues of H') end if c call igraphsecond (t1) tneigh = tneigh + (t1 - t0) c 9000 continue return c c %---------------% c | End of igraphdneigh | c %---------------% c end igraph/src/cs_dmperm.c0000644000176000001440000001622412325527073014473 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* breadth-first search for coarse decomposition (C0,C1,R1 or R0,R3,C3) */ static CS_INT cs_bfs (const cs *A, CS_INT n, CS_INT *wi, CS_INT *wj, CS_INT *queue, const CS_INT *imatch, const CS_INT *jmatch, CS_INT mark) { CS_INT *Ap, *Ai, head = 0, tail = 0, j, i, p, j2 ; cs *C ; for (j = 0 ; j < n ; j++) /* place all unmatched nodes in queue */ { if (imatch [j] >= 0) continue ; /* skip j if matched */ wj [j] = 0 ; /* j in set C0 (R0 if transpose) */ queue [tail++] = j ; /* place unmatched col j in queue */ } if (tail == 0) return (1) ; /* quick return if no unmatched nodes */ C = (mark == 1) ? ((cs *) A) : cs_transpose (A, 0) ; if (!C) return (0) ; /* bfs of C=A' to find R3,C3 from R0 */ Ap = C->p ; Ai = C->i ; while (head < tail) /* while queue is not empty */ { j = queue [head++] ; /* get the head of the queue */ for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; if (wi [i] >= 0) continue ; /* skip if i is marked */ wi [i] = mark ; /* i in set R1 (C3 if transpose) */ j2 = jmatch [i] ; /* traverse alternating path to j2 */ if (wj [j2] >= 0) continue ;/* skip j2 if it is marked */ wj [j2] = mark ; /* j2 in set C1 (R3 if transpose) */ queue [tail++] = j2 ; /* add j2 to queue */ } } if (mark != 1) cs_spfree (C) ; /* free A' if it was created */ return (1) ; } /* collect matched rows and columns into p and q */ static void cs_matched (CS_INT n, const CS_INT *wj, const CS_INT *imatch, CS_INT *p, CS_INT *q, CS_INT *cc, CS_INT *rr, CS_INT set, CS_INT mark) { CS_INT kc = cc [set], j ; CS_INT kr = rr [set-1] ; for (j = 0 ; j < n ; j++) { if (wj [j] != mark) continue ; /* skip if j is not in C set */ p [kr++] = imatch [j] ; q [kc++] = j ; } cc [set+1] = kc ; rr [set] = kr ; } /* collect unmatched rows into the permutation vector p */ static void cs_unmatched (CS_INT m, const CS_INT *wi, CS_INT *p, CS_INT *rr, CS_INT set) { CS_INT i, kr = rr [set] ; for (i = 0 ; i < m ; i++) if (wi [i] == 0) p [kr++] = i ; rr [set+1] = kr ; } /* return 1 if row i is in R2 */ static CS_INT cs_rprune (CS_INT i, CS_INT j, CS_ENTRY aij, void *other) { CS_INT *rr = (CS_INT *) other ; return (i >= rr [1] && i < rr [2]) ; } /* Given A, compute coarse and then fine dmperm */ csd *cs_dmperm (const cs *A, CS_INT seed) { CS_INT m, n, i, j, k, cnz, nc, *jmatch, *imatch, *wi, *wj, *pinv, *Cp, *Ci, *ps, *rs, nb1, nb2, *p, *q, *cc, *rr, *r, *s, ok ; cs *C ; csd *D, *scc ; /* --- Maximum matching ------------------------------------------------- */ if (!CS_CSC (A)) return (NULL) ; /* check inputs */ m = A->m ; n = A->n ; D = cs_dalloc (m, n) ; /* allocate result */ if (!D) return (NULL) ; p = D->p ; q = D->q ; r = D->r ; s = D->s ; cc = D->cc ; rr = D->rr ; jmatch = cs_maxtrans (A, seed) ; /* max transversal */ imatch = jmatch + m ; /* imatch = inverse of jmatch */ if (!jmatch) return (cs_ddone (D, NULL, jmatch, 0)) ; /* --- Coarse decomposition --------------------------------------------- */ wi = r ; wj = s ; /* use r and s as workspace */ for (j = 0 ; j < n ; j++) wj [j] = -1 ; /* unmark all cols for bfs */ for (i = 0 ; i < m ; i++) wi [i] = -1 ; /* unmark all rows for bfs */ cs_bfs (A, n, wi, wj, q, imatch, jmatch, 1) ; /* find C1, R1 from C0*/ ok = cs_bfs (A, m, wj, wi, p, jmatch, imatch, 3) ; /* find R3, C3 from R0*/ if (!ok) return (cs_ddone (D, NULL, jmatch, 0)) ; cs_unmatched (n, wj, q, cc, 0) ; /* unmatched set C0 */ cs_matched (n, wj, imatch, p, q, cc, rr, 1, 1) ; /* set R1 and C1 */ cs_matched (n, wj, imatch, p, q, cc, rr, 2, -1) ; /* set R2 and C2 */ cs_matched (n, wj, imatch, p, q, cc, rr, 3, 3) ; /* set R3 and C3 */ cs_unmatched (m, wi, p, rr, 3) ; /* unmatched set R0 */ cs_free (jmatch) ; /* --- Fine decomposition ----------------------------------------------- */ pinv = cs_pinv (p, m) ; /* pinv=p' */ if (!pinv) return (cs_ddone (D, NULL, NULL, 0)) ; C = cs_permute (A, pinv, q, 0) ;/* C=A(p,q) (it will hold A(R2,C2)) */ cs_free (pinv) ; if (!C) return (cs_ddone (D, NULL, NULL, 0)) ; Cp = C->p ; nc = cc [3] - cc [2] ; /* delete cols C0, C1, and C3 from C */ if (cc [2] > 0) for (j = cc [2] ; j <= cc [3] ; j++) Cp [j-cc[2]] = Cp [j] ; C->n = nc ; if (rr [2] - rr [1] < m) /* delete rows R0, R1, and R3 from C */ { cs_fkeep (C, cs_rprune, rr) ; cnz = Cp [nc] ; Ci = C->i ; if (rr [1] > 0) for (k = 0 ; k < cnz ; k++) Ci [k] -= rr [1] ; } C->m = nc ; scc = cs_scc (C) ; /* find strongly connected components of C*/ if (!scc) return (cs_ddone (D, C, NULL, 0)) ; /* --- Combine coarse and fine decompositions --------------------------- */ ps = scc->p ; /* C(ps,ps) is the permuted matrix */ rs = scc->r ; /* kth block is rs[k]..rs[k+1]-1 */ nb1 = scc->nb ; /* # of blocks of A(R2,C2) */ for (k = 0 ; k < nc ; k++) wj [k] = q [ps [k] + cc [2]] ; for (k = 0 ; k < nc ; k++) q [k + cc [2]] = wj [k] ; for (k = 0 ; k < nc ; k++) wi [k] = p [ps [k] + rr [1]] ; for (k = 0 ; k < nc ; k++) p [k + rr [1]] = wi [k] ; nb2 = 0 ; /* create the fine block partitions */ r [0] = s [0] = 0 ; if (cc [2] > 0) nb2++ ; /* leading coarse block A (R1, [C0 C1]) */ for (k = 0 ; k < nb1 ; k++) /* coarse block A (R2,C2) */ { r [nb2] = rs [k] + rr [1] ; /* A (R2,C2) splits into nb1 fine blocks */ s [nb2] = rs [k] + cc [2] ; nb2++ ; } if (rr [2] < m) { r [nb2] = rr [2] ; /* trailing coarse block A ([R3 R0], C3) */ s [nb2] = cc [3] ; nb2++ ; } r [nb2] = m ; s [nb2] = n ; D->nb = nb2 ; cs_dfree (scc) ; return (cs_ddone (D, C, NULL, 1)) ; } igraph/src/glprng02.c0000644000176000001440000000440712325527073014155 0ustar ripleyusers/* glprng02.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpenv.h" #include "glprng.h" #define xfault xerror /*********************************************************************** * NAME * * rng_unif_01 - obtain pseudo-random number in the range [0, 1] * * SYNOPSIS * * #include "glprng.h" * double rng_unif_01(RNG *rand); * * RETURNS * * The routine rng_unif_01 returns a next pseudo-random number which is * uniformly distributed in the range [0, 1]. */ double rng_unif_01(RNG *rand) { double x; x = (double)rng_next_rand(rand) / 2147483647.0; xassert(0.0 <= x && x <= 1.0); return x; } /*********************************************************************** * NAME * * rng_uniform - obtain pseudo-random number in the range [a, b] * * SYNOPSIS * * #include "glprng.h" * double rng_uniform(RNG *rand, double a, double b); * * RETURNS * * The routine rng_uniform returns a next pseudo-random number which is * uniformly distributed in the range [a, b]. */ double rng_uniform(RNG *rand, double a, double b) { double x; if (a >= b) xfault("rng_uniform: a = %g, b = %g; invalid range\n", a, b); x = rng_unif_01(rand); x = a * (1.0 - x) + b * x; xassert(a <= x && x <= b); return x; } /* eof */ igraph/src/glplib03.c0000644000176000001440000004347512325527073014146 0ustar ripleyusers/* glplib03.c (miscellaneous library routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif #include "glpenv.h" #include "glplib.h" /*********************************************************************** * NAME * * str2int - convert character string to value of int type * * SYNOPSIS * * #include "glplib.h" * int str2int(const char *str, int *val); * * DESCRIPTION * * The routine str2int converts the character string str to a value of * integer type and stores the value into location, which the parameter * val points to (in the case of error content of this location is not * changed). * * RETURNS * * The routine returns one of the following error codes: * * 0 - no error; * 1 - value out of range; * 2 - character string is syntactically incorrect. */ int str2int(const char *str, int *_val) { int d, k, s, val = 0; /* scan optional sign */ if (str[0] == '+') s = +1, k = 1; else if (str[0] == '-') s = -1, k = 1; else s = +1, k = 0; /* check for the first digit */ if (!isdigit((unsigned char)str[k])) return 2; /* scan digits */ while (isdigit((unsigned char)str[k])) { d = str[k++] - '0'; if (s > 0) { if (val > INT_MAX / 10) return 1; val *= 10; if (val > INT_MAX - d) return 1; val += d; } else { if (val < INT_MIN / 10) return 1; val *= 10; if (val < INT_MIN + d) return 1; val -= d; } } /* check for terminator */ if (str[k] != '\0') return 2; /* conversion has been done */ *_val = val; return 0; } /*********************************************************************** * NAME * * str2num - convert character string to value of double type * * SYNOPSIS * * #include "glplib.h" * int str2num(const char *str, double *val); * * DESCRIPTION * * The routine str2num converts the character string str to a value of * double type and stores the value into location, which the parameter * val points to (in the case of error content of this location is not * changed). * * RETURNS * * The routine returns one of the following error codes: * * 0 - no error; * 1 - value out of range; * 2 - character string is syntactically incorrect. */ int str2num(const char *str, double *_val) { int k; double val; /* scan optional sign */ k = (str[0] == '+' || str[0] == '-' ? 1 : 0); /* check for decimal point */ if (str[k] == '.') { k++; /* a digit should follow it */ if (!isdigit((unsigned char)str[k])) return 2; k++; goto frac; } /* integer part should start with a digit */ if (!isdigit((unsigned char)str[k])) return 2; /* scan integer part */ while (isdigit((unsigned char)str[k])) k++; /* check for decimal point */ if (str[k] == '.') k++; frac: /* scan optional fraction part */ while (isdigit((unsigned char)str[k])) k++; /* check for decimal exponent */ if (str[k] == 'E' || str[k] == 'e') { k++; /* scan optional sign */ if (str[k] == '+' || str[k] == '-') k++; /* a digit should follow E, E+ or E- */ if (!isdigit((unsigned char)str[k])) return 2; } /* scan optional exponent part */ while (isdigit((unsigned char)str[k])) k++; /* check for terminator */ if (str[k] != '\0') return 2; /* perform conversion */ { char *endptr; val = strtod(str, &endptr); if (*endptr != '\0') return 2; } /* check for overflow */ if (!(-DBL_MAX <= val && val <= +DBL_MAX)) return 1; /* check for underflow */ if (-DBL_MIN < val && val < +DBL_MIN) val = 0.0; /* conversion has been done */ *_val = val; return 0; } /*********************************************************************** * NAME * * strspx - remove all spaces from character string * * SYNOPSIS * * #include "glplib.h" * char *strspx(char *str); * * DESCRIPTION * * The routine strspx removes all spaces from the character string str. * * RETURNS * * The routine returns a pointer to the character string. * * EXAMPLES * * strspx(" Errare humanum est ") => "Errarehumanumest" * * strspx(" ") => "" */ char *strspx(char *str) { char *s, *t; for (s = t = str; *s; s++) if (*s != ' ') *t++ = *s; *t = '\0'; return str; } /*********************************************************************** * NAME * * strtrim - remove trailing spaces from character string * * SYNOPSIS * * #include "glplib.h" * char *strtrim(char *str); * * DESCRIPTION * * The routine strtrim removes trailing spaces from the character * string str. * * RETURNS * * The routine returns a pointer to the character string. * * EXAMPLES * * strtrim("Errare humanum est ") => "Errare humanum est" * * strtrim(" ") => "" */ char *strtrim(char *str) { char *t; for (t = strrchr(str, '\0') - 1; t >= str; t--) { if (*t != ' ') break; *t = '\0'; } return str; } /*********************************************************************** * NAME * * strrev - reverse character string * * SYNOPSIS * * #include "glplib.h" * char *strrev(char *s); * * DESCRIPTION * * The routine strrev changes characters in a character string s to the * reverse order, except the terminating null character. * * RETURNS * * The routine returns the pointer s. * * EXAMPLES * * strrev("") => "" * * strrev("Today is Monday") => "yadnoM si yadoT" */ char *strrev(char *s) { int i, j; char t; for (i = 0, j = strlen(s)-1; i < j; i++, j--) t = s[i], s[i] = s[j], s[j] = t; return s; } /*********************************************************************** * NAME * * gcd - find greatest common divisor of two integers * * SYNOPSIS * * #include "glplib.h" * int gcd(int x, int y); * * RETURNS * * The routine gcd returns gcd(x, y), the greatest common divisor of * the two positive integers given. * * ALGORITHM * * The routine gcd is based on Euclid's algorithm. * * REFERENCES * * Don Knuth, The Art of Computer Programming, Vol.2: Seminumerical * Algorithms, 3rd Edition, Addison-Wesley, 1997. Section 4.5.2: The * Greatest Common Divisor, pp. 333-56. */ int gcd(int x, int y) { int r; xassert(x > 0 && y > 0); while (y > 0) r = x % y, x = y, y = r; return x; } /*********************************************************************** * NAME * * gcdn - find greatest common divisor of n integers * * SYNOPSIS * * #include "glplib.h" * int gcdn(int n, int x[]); * * RETURNS * * The routine gcdn returns gcd(x[1], x[2], ..., x[n]), the greatest * common divisor of n positive integers given, n > 0. * * BACKGROUND * * The routine gcdn is based on the following identity: * * gcd(x, y, z) = gcd(gcd(x, y), z). * * REFERENCES * * Don Knuth, The Art of Computer Programming, Vol.2: Seminumerical * Algorithms, 3rd Edition, Addison-Wesley, 1997. Section 4.5.2: The * Greatest Common Divisor, pp. 333-56. */ int gcdn(int n, int x[]) { int d, j; xassert(n > 0); for (j = 1; j <= n; j++) { xassert(x[j] > 0); if (j == 1) d = x[1]; else d = gcd(d, x[j]); if (d == 1) break; } return d; } /*********************************************************************** * NAME * * lcm - find least common multiple of two integers * * SYNOPSIS * * #include "glplib.h" * int lcm(int x, int y); * * RETURNS * * The routine lcm returns lcm(x, y), the least common multiple of the * two positive integers given. In case of integer overflow the routine * returns zero. * * BACKGROUND * * The routine lcm is based on the following identity: * * lcm(x, y) = (x * y) / gcd(x, y) = x * [y / gcd(x, y)], * * where gcd(x, y) is the greatest common divisor of x and y. */ int lcm(int x, int y) { xassert(x > 0); xassert(y > 0); y /= gcd(x, y); if (x > INT_MAX / y) return 0; return x * y; } /*********************************************************************** * NAME * * lcmn - find least common multiple of n integers * * SYNOPSIS * * #include "glplib.h" * int lcmn(int n, int x[]); * * RETURNS * * The routine lcmn returns lcm(x[1], x[2], ..., x[n]), the least * common multiple of n positive integers given, n > 0. In case of * integer overflow the routine returns zero. * * BACKGROUND * * The routine lcmn is based on the following identity: * * lcmn(x, y, z) = lcm(lcm(x, y), z), * * where lcm(x, y) is the least common multiple of x and y. */ int lcmn(int n, int x[]) { int m, j; xassert(n > 0); for (j = 1; j <= n; j++) { xassert(x[j] > 0); if (j == 1) m = x[1]; else m = lcm(m, x[j]); if (m == 0) break; } return m; } /*********************************************************************** * NAME * * round2n - round floating-point number to nearest power of two * * SYNOPSIS * * #include "glplib.h" * double round2n(double x); * * RETURNS * * Given a positive floating-point value x the routine round2n returns * 2^n such that |x - 2^n| is minimal. * * EXAMPLES * * round2n(10.1) = 2^3 = 8 * round2n(15.3) = 2^4 = 16 * round2n(0.01) = 2^(-7) = 0.0078125 * * BACKGROUND * * Let x = f * 2^e, where 0.5 <= f < 1 is a normalized fractional part, * e is an integer exponent. Then, obviously, 0.5 * 2^e <= x < 2^e, so * if x - 0.5 * 2^e <= 2^e - x, we choose 0.5 * 2^e = 2^(e-1), and 2^e * otherwise. The latter condition can be written as 2 * x <= 1.5 * 2^e * or 2 * f * 2^e <= 1.5 * 2^e or, finally, f <= 0.75. */ double round2n(double x) { int e; double f; xassert(x > 0.0); f = frexp(x, &e); return ldexp(1.0, f <= 0.75 ? e-1 : e); } /*********************************************************************** * NAME * * fp2rat - convert floating-point number to rational number * * SYNOPSIS * * #include "glplib.h" * int fp2rat(double x, double eps, double *p, double *q); * * DESCRIPTION * * Given a floating-point number 0 <= x < 1 the routine fp2rat finds * its "best" rational approximation p / q, where p >= 0 and q > 0 are * integer numbers, such that |x - p / q| <= eps. * * RETURNS * * The routine fp2rat returns the number of iterations used to achieve * the specified precision eps. * * EXAMPLES * * For x = sqrt(2) - 1 = 0.414213562373095 and eps = 1e-6 the routine * gives p = 408 and q = 985, where 408 / 985 = 0.414213197969543. * * BACKGROUND * * It is well known that every positive real number x can be expressed * as the following continued fraction: * * x = b[0] + a[1] * ------------------------ * b[1] + a[2] * ----------------- * b[2] + a[3] * ---------- * b[3] + ... * * where: * * a[k] = 1, k = 0, 1, 2, ... * * b[k] = floor(x[k]), k = 0, 1, 2, ... * * x[0] = x, * * x[k] = 1 / frac(x[k-1]), k = 1, 2, 3, ... * * To find the "best" rational approximation of x the routine computes * partial fractions f[k] by dropping after k terms as follows: * * f[k] = A[k] / B[k], * * where: * * A[-1] = 1, A[0] = b[0], B[-1] = 0, B[0] = 1, * * A[k] = b[k] * A[k-1] + a[k] * A[k-2], * * B[k] = b[k] * B[k-1] + a[k] * B[k-2]. * * Once the condition * * |x - f[k]| <= eps * * has been satisfied, the routine reports p = A[k] and q = B[k] as the * final answer. * * In the table below here is some statistics obtained for one million * random numbers uniformly distributed in the range [0, 1). * * eps max p mean p max q mean q max k mean k * ------------------------------------------------------------- * 1e-1 8 1.6 9 3.2 3 1.4 * 1e-2 98 6.2 99 12.4 5 2.4 * 1e-3 997 20.7 998 41.5 8 3.4 * 1e-4 9959 66.6 9960 133.5 10 4.4 * 1e-5 97403 211.7 97404 424.2 13 5.3 * 1e-6 479669 669.9 479670 1342.9 15 6.3 * 1e-7 1579030 2127.3 3962146 4257.8 16 7.3 * 1e-8 26188823 6749.4 26188824 13503.4 19 8.2 * * REFERENCES * * W. B. Jones and W. J. Thron, "Continued Fractions: Analytic Theory * and Applications," Encyclopedia on Mathematics and Its Applications, * Addison-Wesley, 1980. */ int fp2rat(double x, double eps, double *p, double *q) { int k; double xk, Akm1, Ak, Bkm1, Bk, ak, bk, fk, temp; if (!(0.0 <= x && x < 1.0)) xerror("fp2rat: x = %g; number out of range\n", x); for (k = 0; ; k++) { xassert(k <= 100); if (k == 0) { /* x[0] = x */ xk = x; /* A[-1] = 1 */ Akm1 = 1.0; /* A[0] = b[0] = floor(x[0]) = 0 */ Ak = 0.0; /* B[-1] = 0 */ Bkm1 = 0.0; /* B[0] = 1 */ Bk = 1.0; } else { /* x[k] = 1 / frac(x[k-1]) */ temp = xk - floor(xk); xassert(temp != 0.0); xk = 1.0 / temp; /* a[k] = 1 */ ak = 1.0; /* b[k] = floor(x[k]) */ bk = floor(xk); /* A[k] = b[k] * A[k-1] + a[k] * A[k-2] */ temp = bk * Ak + ak * Akm1; Akm1 = Ak, Ak = temp; /* B[k] = b[k] * B[k-1] + a[k] * B[k-2] */ temp = bk * Bk + ak * Bkm1; Bkm1 = Bk, Bk = temp; } /* f[k] = A[k] / B[k] */ fk = Ak / Bk; #if 0 print("%.*g / %.*g = %.*g", DBL_DIG, Ak, DBL_DIG, Bk, DBL_DIG, fk); #endif if (fabs(x - fk) <= eps) break; } *p = Ak; *q = Bk; return k; } /*********************************************************************** * NAME * * jday - convert calendar date to Julian day number * * SYNOPSIS * * #include "glplib.h" * int jday(int d, int m, int y); * * DESCRIPTION * * The routine jday converts a calendar date, Gregorian calendar, to * corresponding Julian day number j. * * From the given day d, month m, and year y, the Julian day number j * is computed without using tables. * * The routine is valid for 1 <= y <= 4000. * * RETURNS * * The routine jday returns the Julian day number, or negative value if * the specified date is incorrect. * * REFERENCES * * R. G. Tantzen, Algorithm 199: conversions between calendar date and * Julian day number, Communications of the ACM, vol. 6, no. 8, p. 444, * Aug. 1963. */ int jday(int d, int m, int y) { int c, ya, j, dd; if (!(1 <= d && d <= 31 && 1 <= m && m <= 12 && 1 <= y && y <= 4000)) { j = -1; goto done; } if (m >= 3) m -= 3; else m += 9, y--; c = y / 100; ya = y - 100 * c; j = (146097 * c) / 4 + (1461 * ya) / 4 + (153 * m + 2) / 5 + d + 1721119; jdate(j, &dd, NULL, NULL); if (d != dd) j = -1; done: return j; } /*********************************************************************** * NAME * * jdate - convert Julian day number to calendar date * * SYNOPSIS * * #include "glplib.h" * void jdate(int j, int *d, int *m, int *y); * * DESCRIPTION * * The routine jdate converts a Julian day number j to corresponding * calendar date, Gregorian calendar. * * The day d, month m, and year y are computed without using tables and * stored in corresponding locations. * * The routine is valid for 1721426 <= j <= 3182395. * * RETURNS * * If the conversion is successful, the routine returns zero, otherwise * non-zero. * * REFERENCES * * R. G. Tantzen, Algorithm 199: conversions between calendar date and * Julian day number, Communications of the ACM, vol. 6, no. 8, p. 444, * Aug. 1963. */ int jdate(int j, int *_d, int *_m, int *_y) { int d, m, y, ret = 0; if (!(1721426 <= j && j <= 3182395)) { ret = 1; goto done; } j -= 1721119; y = (4 * j - 1) / 146097; j = (4 * j - 1) % 146097; d = j / 4; j = (4 * d + 3) / 1461; d = (4 * d + 3) % 1461; d = (d + 4) / 4; m = (5 * d - 3) / 153; d = (5 * d - 3) % 153; d = (d + 5) / 5; y = 100 * y + j; if (m <= 9) m += 3; else m -= 9, y++; if (_d != NULL) *_d = d; if (_m != NULL) *_m = m; if (_y != NULL) *_y = y; done: return ret; } #if 0 int main(void) { int jbeg, jend, j, d, m, y; jbeg = jday(1, 1, 1); jend = jday(31, 12, 4000); for (j = jbeg; j <= jend; j++) { xassert(jdate(j, &d, &m, &y) == 0); xassert(jday(d, m, y) == j); } xprintf("Routines jday and jdate work correctly.\n"); return 0; } #endif /* eof */ igraph/src/igraph_operators.h0000644000176000001440000000470412325527073016077 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_OPERATORS_H #define IGRAPH_OPERATORS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_constants.h" #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Graph operators */ /* -------------------------------------------------- */ int igraph_disjoint_union(igraph_t *res, const igraph_t *left, const igraph_t *right); int igraph_disjoint_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs); int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, igraph_vector_ptr_t *edgemaps); int igraph_intersection(igraph_t *res, const igraph_t *left, const igraph_t *right, igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); int igraph_intersection_many(igraph_t *res, const igraph_vector_ptr_t *graphs, igraph_vector_ptr_t *edgemaps); int igraph_difference(igraph_t *res, const igraph_t *orig, const igraph_t *sub); int igraph_complementer(igraph_t *res, const igraph_t *graph, igraph_bool_t loops); int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); __END_DECLS #endif igraph/src/cs_dropzeros.c0000644000176000001440000000214312325527073015231 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" static CS_INT cs_nonzero (CS_INT i, CS_INT j, CS_ENTRY aij, void *other) { return (aij != 0) ; } CS_INT cs_dropzeros (cs *A) { return (cs_fkeep (A, &cs_nonzero, NULL)) ; /* keep all nonzero entries */ } igraph/src/plfit.c0000644000176000001440000005416012325527074013642 0ustar ripleyusers/* plfit.c * * Copyright (C) 2010-2011 Tamas Nepusz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "error.h" #include "gss.h" #include "lbfgs.h" #include "platform.h" #include "plfit.h" #include "kolmogorov.h" #include "zeta.h" /* #define PLFIT_DEBUG */ #define DATA_POINTS_CHECK \ if (n <= 0) { \ PLFIT_ERROR("no data points", PLFIT_EINVAL); \ } #define XMIN_CHECK_ZERO \ if (xmin <= 0) { \ PLFIT_ERROR("xmin must be greater than zero", PLFIT_EINVAL); \ } #define XMIN_CHECK_ONE \ if (xmin < 1) { \ PLFIT_ERROR("xmin must be at least 1", PLFIT_EINVAL); \ } static int double_comparator(const void *a, const void *b) { const double *da = (const double*)a; const double *db = (const double*)b; return (*da > *db) - (*da < *db); } /** * Given a sorted array of doubles, return another array that contains pointers * into the array for the start of each block of identical elements. * * \param begin pointer to the beginning of the array * \param end pointer to the first element after the end of the array * \param result_length if not \c NULL, the number of unique elements in the * given array is returned here */ static double** unique_element_pointers(double* begin, double* end, size_t* result_length) { double* ptr = begin; double** result; double prev_x; size_t num_elts = 15; size_t used_elts = 0; /* Special case: empty array */ if (begin == end) { result = calloc(1, sizeof(double*)); if (result != 0) { result[0] = 0; } return result; } /* Allocate initial result array, including the guard element */ result = calloc(num_elts+1, sizeof(double*)); if (result == 0) return 0; prev_x = *begin; result[used_elts++] = begin; /* Process the input array */ for (ptr = begin+1; ptr < end; ptr++) { if (*ptr == prev_x) continue; /* New block found */ if (used_elts >= num_elts) { /* Array full; allocate a new chunk */ num_elts = num_elts*2 + 1; result = realloc(result, sizeof(double*) * (num_elts+1)); if (result == 0) return 0; } /* Store the new element */ result[used_elts++] = ptr; prev_x = *ptr; } /* Calculate the result length */ if (result_length != 0) { *result_length = used_elts; } /* Add the guard entry to the end of the result */ result[used_elts++] = 0; return result; } static void plfit_i_perform_finite_size_correction(plfit_result_t* result, size_t n) { result->alpha = result->alpha * (n-1) / n + 1.0 / n; } /********** Continuous power law distribution fitting **********/ void plfit_i_logsum_less_than_continuous(double* begin, double* end, double xmin, double* result, size_t* m) { double logsum = 0.0; size_t count = 0; for (; begin != end; begin++) { if (*begin >= xmin) { count++; logsum += log(*begin / xmin); } } *m = count; *result = logsum; } double plfit_i_logsum_continuous(double* begin, double* end, double xmin) { double logsum = 0.0; for (; begin != end; begin++) logsum += log(*begin / xmin); return logsum; } int plfit_i_estimate_alpha_continuous(double* xs, size_t n, double xmin, double* alpha) { double result; size_t m; XMIN_CHECK_ZERO; plfit_i_logsum_less_than_continuous(xs, xs+n, xmin, &result, &m); if (m == 0) { PLFIT_ERROR("no data point was larger than xmin", PLFIT_EINVAL); } *alpha = 1 + m / result; return PLFIT_SUCCESS; } int plfit_i_estimate_alpha_continuous_sorted(double* xs, size_t n, double xmin, double* alpha) { double* end = xs+n; XMIN_CHECK_ZERO; for (; xs != end && *xs < xmin; xs++); if (xs == end) { PLFIT_ERROR("no data point was larger than xmin", PLFIT_EINVAL); } *alpha = 1 + (end-xs) / plfit_i_logsum_continuous(xs, end, xmin); return PLFIT_SUCCESS; } static int plfit_i_ks_test_continuous(double* xs, double* xs_end, const double alpha, const double xmin, double* D) { /* Assumption: xs is sorted and cut off at xmin so the first element is * always larger than or equal to xmin. */ double result = 0, n; int m = 0; n = xs_end - xs; while (xs < xs_end) { double d = fabs(1-pow(xmin / *xs, alpha-1) - m / n); if (d > result) result = d; xs++; m++; } *D = result; return PLFIT_SUCCESS; } int plfit_log_likelihood_continuous(double* xs, size_t n, double alpha, double xmin, double* L) { double logsum, c; size_t m; if (alpha <= 1) { PLFIT_ERROR("alpha must be greater than one", PLFIT_EINVAL); } XMIN_CHECK_ZERO; c = (alpha - 1) / xmin; plfit_i_logsum_less_than_continuous(xs, xs+n, xmin, &logsum, &m); *L = -alpha * logsum + log(c) * m; return PLFIT_SUCCESS; } int plfit_estimate_alpha_continuous(double* xs, size_t n, double xmin, const plfit_continuous_options_t* options, plfit_result_t *result) { double *xs_copy; if (!options) options = &plfit_continuous_default_options; /* Make a copy of xs and sort it */ xs_copy = (double*)malloc(sizeof(double) * n); memcpy(xs_copy, xs, sizeof(double) * n); qsort(xs_copy, n, sizeof(double), double_comparator); PLFIT_CHECK(plfit_estimate_alpha_continuous_sorted(xs_copy, n, xmin, options, result)); free(xs_copy); return PLFIT_SUCCESS; } int plfit_estimate_alpha_continuous_sorted(double* xs, size_t n, double xmin, const plfit_continuous_options_t* options, plfit_result_t *result) { double* end; if (!options) options = &plfit_continuous_default_options; end = xs + n; while (xs < end && *xs < xmin) xs++; n = (size_t) (end - xs); PLFIT_CHECK(plfit_i_estimate_alpha_continuous_sorted(xs, n, xmin, &result->alpha)); PLFIT_CHECK(plfit_i_ks_test_continuous(xs, end, result->alpha, xmin, &result->D)); if (options->finite_size_correction) plfit_i_perform_finite_size_correction(result, n); result->xmin = xmin; result->p = plfit_ks_test_one_sample_p(result->D, n); plfit_log_likelihood_continuous(xs, n, result->alpha, result->xmin, &result->L); return PLFIT_SUCCESS; } typedef struct { double *begin; /**< Pointer to the beginning of the array holding the data */ double *end; /**< Pointer to after the end of the array holding the data */ double **uniques; /**< Pointers to unique elements of the input array */ plfit_result_t last; /**< Result of the last evaluation */ } plfit_continuous_xmin_opt_data_t; double plfit_i_continuous_xmin_opt_evaluate(void* instance, double x) { plfit_continuous_xmin_opt_data_t* data = (plfit_continuous_xmin_opt_data_t*)instance; double* begin = data->uniques[(int)x]; data->last.xmin = *begin; #ifdef PLFIT_DEBUG printf("Trying with xmin = %.4f\n", *begin); #endif plfit_i_estimate_alpha_continuous_sorted(begin, (size_t) (data->end-begin), *begin, &data->last.alpha); plfit_i_ks_test_continuous(begin, data->end, data->last.alpha, *begin, &data->last.D); return data->last.D; } int plfit_i_continuous_xmin_opt_progress(void* instance, double x, double fx, double min, double fmin, double left, double right, int k) { #ifdef PLFIT_DEBUG printf("Iteration #%d: [%.4f; %.4f), x=%.4f, fx=%.4f, min=%.4f, fmin=%.4f\n", k, left, right, x, fx, min, fmin); #endif /* Continue only if `left' and `right' point to different integers */ return (int)left == (int)right; } int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* options, plfit_result_t* result) { gss_parameter_t gss_param; plfit_continuous_xmin_opt_data_t opt_data; plfit_result_t best_result; int success; size_t i, best_n, num_uniques; double x, *px; DATA_POINTS_CHECK; if (!options) options = &plfit_continuous_default_options; /* Make a copy of xs and sort it */ opt_data.begin = (double*)malloc(sizeof(double) * n); memcpy(opt_data.begin, xs, sizeof(double) * n); qsort(opt_data.begin, n, sizeof(double), double_comparator); opt_data.end = opt_data.begin + n; /* Create an array containing pointers to the unique elements of the input. From * each block of unique elements, we add the pointer to the first one. */ opt_data.uniques = unique_element_pointers(opt_data.begin, opt_data.end, &num_uniques); if (opt_data.uniques == 0) return PLFIT_ENOMEM; /* We will now determine the best xmin that yields the lowest D-score. * First we try a golden section search if needed. If that fails, we try * a linear search. */ if (options->xmin_method == PLFIT_GSS_OR_LINEAR && num_uniques > 5) { gss_parameter_init(&gss_param); success = (gss(0, num_uniques-5, &x, 0, plfit_i_continuous_xmin_opt_evaluate, plfit_i_continuous_xmin_opt_progress, &opt_data, &gss_param) == 0); best_result = opt_data.last; /* plfit_i_continuous_xmin_opt_evaluate will set opt_data.last to * indicate the location of the optimum and the value of D */ } else { success = 0; } if (success) { /* calculate best_n because we'll need it later. Luckily x indicates * the index in opt_data.uniques that we have to look up in order to * find the first element in the array that is included */ px = opt_data.uniques[(int)x]; best_n = (size_t) (opt_data.end-px+1); } else { /* GSS failed or skipped; try linear search */ /* Prepare some variables */ best_n = 0; best_result.D = DBL_MAX; best_result.xmin = 0; best_result.alpha = 0; for (i = 0; i < num_uniques-1; i++) { plfit_i_continuous_xmin_opt_evaluate(&opt_data, i); if (opt_data.last.D < best_result.D) { best_result = opt_data.last; best_n = (size_t) (opt_data.end - opt_data.uniques[i] + 1); } } } /* Get rid of the uniques array, we don't need it any more */ free(opt_data.uniques); /* Sort out the result */ *result = best_result; if (options->finite_size_correction) plfit_i_perform_finite_size_correction(result, best_n); result->p = plfit_ks_test_one_sample_p(result->D, best_n); plfit_log_likelihood_continuous(opt_data.begin + n - best_n, best_n, result->alpha, result->xmin, &result->L); /* Get rid of the copied data as well */ free(opt_data.begin); return PLFIT_SUCCESS; } /********** Discrete power law distribution fitting **********/ typedef struct { size_t m; double logsum; double xmin; } plfit_i_estimate_alpha_discrete_data_t; double plfit_i_logsum_discrete(double* begin, double* end, double xmin) { double logsum = 0.0; for (; begin != end; begin++) logsum += log(*begin); return logsum; } void plfit_i_logsum_less_than_discrete(double* begin, double* end, double xmin, double* logsum, size_t* m) { double result = 0.0; size_t count = 0; for (; begin != end; begin++) { if (*begin < xmin) continue; result += log(*begin); count++; } *logsum = result; *m = count; } lbfgsfloatval_t plfit_i_estimate_alpha_discrete_lbfgs_evaluate( void* instance, const lbfgsfloatval_t* x, lbfgsfloatval_t* g, const int n, const lbfgsfloatval_t step) { plfit_i_estimate_alpha_discrete_data_t* data; lbfgsfloatval_t result; double dx = step; double huge = 1e10; /* pseudo-infinity; apparently DBL_MAX does not work */ data = (plfit_i_estimate_alpha_discrete_data_t*)instance; #ifdef PLFIT_DEBUG printf("- Evaluating at %.4f (step = %.4f, xmin = %.4f)\n", *x, step, data->xmin); #endif if (isnan(*x)) { g[0] = huge; return huge; } /* Find the delta X value to estimate the gradient */ if (dx > 0.001 || dx == 0) dx = 0.001; else if (dx < -0.001) dx = -0.001; /* Is x[0] in its valid range? */ if (x[0] <= 1.0) { /* The Hurwitz zeta function is infinite in this case */ g[0] = (dx > 0) ? -huge : huge; return huge; } if (x[0] + dx <= 1.0) g[0] = huge; else g[0] = data->logsum + data->m * (log(gsl_sf_hzeta(x[0] + dx, data->xmin)) - log(gsl_sf_hzeta(x[0], data->xmin))) / dx; result = x[0] * data->logsum + data->m * log(gsl_sf_hzeta(x[0], data->xmin)); #ifdef PLFIT_DEBUG printf(" - Gradient: %.4f\n", g[0]); printf(" - Result: %.4f\n", result); #endif return result; } int plfit_i_estimate_alpha_discrete_lbfgs_progress(void* instance, const lbfgsfloatval_t* x, const lbfgsfloatval_t* g, const lbfgsfloatval_t fx, const lbfgsfloatval_t xnorm, const lbfgsfloatval_t gnorm, const lbfgsfloatval_t step, int n, int k, int ls) { return 0; } int plfit_i_estimate_alpha_discrete_linear_scan(double* xs, size_t n, double xmin, double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) { double curr_alpha, best_alpha, L, L_max; double logsum; size_t m; XMIN_CHECK_ONE; if (options->alpha.min <= 1.0) { PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL); } if (options->alpha.max < options->alpha.min) { PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL); } if (options->alpha.step <= 0) { PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL); } if (sorted) { logsum = plfit_i_logsum_discrete(xs, xs+n, xmin); m = n; } else { plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &logsum, &m); } best_alpha = options->alpha.min; L_max = -DBL_MAX; for (curr_alpha = options->alpha.min; curr_alpha <= options->alpha.max; curr_alpha += options->alpha.step) { L = -curr_alpha * logsum - m * log(gsl_sf_hzeta(curr_alpha, xmin)); if (L > L_max) { L_max = L; best_alpha = curr_alpha; } } *alpha = best_alpha; return PLFIT_SUCCESS; } int plfit_i_estimate_alpha_discrete_lbfgs(double* xs, size_t n, double xmin, double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) { lbfgs_parameter_t param; lbfgsfloatval_t* variables; plfit_i_estimate_alpha_discrete_data_t data; int ret; XMIN_CHECK_ONE; /* Initialize algorithm parameters */ lbfgs_parameter_init(¶m); param.max_iterations = 0; /* proceed until infinity */ /* Set up context for optimization */ data.xmin = xmin; if (sorted) { data.logsum = plfit_i_logsum_discrete(xs, xs+n, xmin); data.m = n; } else { plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &data.logsum, &data.m); } /* Allocate space for the single alpha variable */ variables = lbfgs_malloc(1); variables[0] = 3.0; /* initial guess */ /* Optimization */ ret = lbfgs(1, variables, /* ptr_fx = */ 0, plfit_i_estimate_alpha_discrete_lbfgs_evaluate, plfit_i_estimate_alpha_discrete_lbfgs_progress, &data, ¶m); if (ret < 0 && ret != LBFGSERR_ROUNDING_ERROR && ret != LBFGSERR_MAXIMUMLINESEARCH && ret != LBFGSERR_CANCELED) { char buf[4096]; snprintf(buf, 4096, "L-BFGS optimization signaled an error (error code = %d)", ret); lbfgs_free(variables); PLFIT_ERROR(buf, PLFIT_FAILURE); } *alpha = variables[0]; /* Deallocate the variable array */ lbfgs_free(variables); return PLFIT_SUCCESS; } int plfit_i_estimate_alpha_discrete_fast(double* xs, size_t n, double xmin, double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) { plfit_continuous_options_t cont_options; if (!options) options = &plfit_discrete_default_options; plfit_continuous_options_init(&cont_options); cont_options.finite_size_correction = options->finite_size_correction; XMIN_CHECK_ONE; if (sorted) { return plfit_i_estimate_alpha_continuous_sorted(xs, n, xmin-0.5, alpha); } else { return plfit_i_estimate_alpha_continuous(xs, n, xmin-0.5, alpha); } } int plfit_i_estimate_alpha_discrete(double* xs, size_t n, double xmin, double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) { switch (options->alpha_method) { case PLFIT_LBFGS: PLFIT_CHECK(plfit_i_estimate_alpha_discrete_lbfgs(xs, n, xmin, alpha, options, sorted)); break; case PLFIT_LINEAR_SCAN: PLFIT_CHECK(plfit_i_estimate_alpha_discrete_linear_scan(xs, n, xmin, alpha, options, sorted)); break; case PLFIT_PRETEND_CONTINUOUS: PLFIT_CHECK(plfit_i_estimate_alpha_discrete_fast(xs, n, xmin, alpha, options, sorted)); break; default: PLFIT_ERROR("unknown optimization method specified", PLFIT_EINVAL); } return PLFIT_SUCCESS; } static int plfit_i_ks_test_discrete(double* xs, double* xs_end, const double alpha, const double xmin, double* D) { /* Assumption: xs is sorted and cut off at xmin so the first element is * always larger than or equal to xmin. */ double result = 0, n, hzeta, x; int m = 0; n = xs_end - xs; hzeta = gsl_sf_hzeta(alpha, xmin); while (xs < xs_end) { double d; x = *xs; d = fabs(1-(gsl_sf_hzeta(alpha, x) / hzeta) - m / n); if (d > result) result = d; do { xs++; m++; } while (xs < xs_end && *xs == x); } *D = result; return PLFIT_SUCCESS; } int plfit_log_likelihood_discrete(double* xs, size_t n, double alpha, double xmin, double* L) { double result; size_t m; if (alpha <= 1) { PLFIT_ERROR("alpha must be greater than one", PLFIT_EINVAL); } XMIN_CHECK_ONE; plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &result, &m); result = - alpha * result - m * log(gsl_sf_hzeta(alpha, xmin)); *L = result; return PLFIT_SUCCESS; } int plfit_estimate_alpha_discrete(double* xs, size_t n, double xmin, const plfit_discrete_options_t* options, plfit_result_t *result) { double *xs_copy, *end; if (!options) options = &plfit_discrete_default_options; /* Check the validity of the input parameters */ DATA_POINTS_CHECK; if (options->alpha_method == PLFIT_LINEAR_SCAN) { if (options->alpha.min <= 1.0) { PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL); } if (options->alpha.max < options->alpha.min) { PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL); } if (options->alpha.step <= 0) { PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL); } } /* Make a copy of xs and sort it */ xs_copy = (double*)malloc(sizeof(double) * n); memcpy(xs_copy, xs, sizeof(double) * n); qsort(xs_copy, n, sizeof(double), double_comparator); xs = xs_copy; end = xs_copy + n; while (xs < end && *xs < xmin) xs++; n = (size_t) (end - xs); PLFIT_CHECK(plfit_i_estimate_alpha_discrete(xs, n, xmin, &result->alpha, options, /* sorted = */ 1)); PLFIT_CHECK(plfit_i_ks_test_discrete(xs, end, result->alpha, xmin, &result->D)); result->xmin = xmin; if (options->finite_size_correction) plfit_i_perform_finite_size_correction(result, n); result->p = plfit_ks_test_one_sample_p(result->D, n); plfit_log_likelihood_discrete(xs, n, result->alpha, result->xmin, &result->L); free(xs_copy); return PLFIT_SUCCESS; } int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options, plfit_result_t* result) { double curr_D, curr_alpha; plfit_result_t best_result; double *xs_copy, *px, *end, *end_xmin, prev_x; size_t best_n; size_t m; if (!options) options = &plfit_discrete_default_options; /* Check the validity of the input parameters */ DATA_POINTS_CHECK; if (options->alpha_method == PLFIT_LINEAR_SCAN) { if (options->alpha.min <= 1.0) { PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL); } if (options->alpha.max < options->alpha.min) { PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL); } if (options->alpha.step <= 0) { PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL); } } /* Make a copy of xs and sort it */ xs_copy = (double*)malloc(sizeof(double) * n); memcpy(xs_copy, xs, sizeof(double) * n); qsort(xs_copy, n, sizeof(double), double_comparator); best_result.D = DBL_MAX; best_result.xmin = 1; best_result.alpha = 1; best_n = 0; /* Make sure there are at least three distinct values if possible */ px = xs_copy; end = px + n; end_xmin = end - 1; m = 0; prev_x = *end_xmin; while (*end_xmin == prev_x && end_xmin > px) end_xmin--; prev_x = *end_xmin; while (*end_xmin == prev_x && end_xmin > px) end_xmin--; prev_x = 0; while (px < end_xmin) { while (px < end_xmin && *px == prev_x) { px++; m++; } plfit_i_estimate_alpha_discrete(px, n - m, *px, &curr_alpha, options, /* sorted = */ 1); plfit_i_ks_test_discrete(px, end, curr_alpha, *px, &curr_D); if (curr_D < best_result.D) { best_result.alpha = curr_alpha; best_result.xmin = *px; best_result.D = curr_D; best_n = n - m; } prev_x = *px; px++; m++; } *result = best_result; if (options->finite_size_correction) plfit_i_perform_finite_size_correction(result, best_n); result->p = plfit_ks_test_one_sample_p(result->D, best_n); plfit_log_likelihood_discrete(xs_copy+(n-best_n), best_n, result->alpha, result->xmin, &result->L); free(xs_copy); return PLFIT_SUCCESS; } igraph/src/igraph_heap_pmt.h0000644000176000001440000000357012325527073015656 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ typedef struct TYPE(igraph_heap) { BASE* stor_begin; BASE* stor_end; BASE* end; int destroy; } TYPE(igraph_heap); int FUNCTION(igraph_heap,init)(TYPE(igraph_heap)* h, long int size); int FUNCTION(igraph_heap,init_array)(TYPE(igraph_heap) *t, BASE* data, long int len); void FUNCTION(igraph_heap,destroy)(TYPE(igraph_heap)* h); igraph_bool_t FUNCTION(igraph_heap,empty)(TYPE(igraph_heap)* h); int FUNCTION(igraph_heap,push)(TYPE(igraph_heap)* h, BASE elem); BASE FUNCTION(igraph_heap,top)(TYPE(igraph_heap)* h); BASE FUNCTION(igraph_heap,delete_top)(TYPE(igraph_heap)* h); long int FUNCTION(igraph_heap,size)(TYPE(igraph_heap)* h); int FUNCTION(igraph_heap,reserve)(TYPE(igraph_heap)* h, long int size); void FUNCTION(igraph_heap,i_build)(BASE* arr, long int size, long int head); void FUNCTION(igraph_heap,i_shift_up)(BASE* arr, long int size, long int elem); void FUNCTION(igraph_heap,i_sink)(BASE* arr, long int size, long int head); void FUNCTION(igraph_heap,i_switch)(BASE* arr, long int e1, long int e2); igraph/src/igraph_f2c.h0000644000176000001440000001112012325527073014521 0ustar ripleyusers/* f2c.h -- Standard Fortran to C header file */ /** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ #ifndef F2C_INCLUDE #define F2C_INCLUDE typedef long int integer; typedef unsigned long int uinteger; typedef char *address; typedef short int shortint; typedef float real; typedef double doublereal; typedef struct { real r, i; } complex; typedef struct { doublereal r, i; } doublecomplex; typedef long int logical; typedef short int shortlogical; typedef char logical1; typedef char integer1; #ifdef INTEGER_STAR_8 /* Adjust for integer*8. */ typedef long long longint; /* system-dependent */ typedef unsigned long long ulongint; /* system-dependent */ #define qbit_clear(a,b) ((a) & ~((ulongint)1 << (b))) #define qbit_set(a,b) ((a) | ((ulongint)1 << (b))) #endif #define TRUE_ (1) #define FALSE_ (0) /* Extern is for use with -E */ #ifndef Extern #define Extern extern #endif /* I/O stuff */ #ifdef f2c_i2 /* for -i2 */ typedef short flag; typedef short ftnlen; typedef short ftnint; #else typedef long int flag; typedef long int ftnlen; typedef long int ftnint; #endif /*external read, write*/ typedef struct { flag cierr; ftnint ciunit; flag ciend; char *cifmt; ftnint cirec; } cilist; /*internal read, write*/ typedef struct { flag icierr; char *iciunit; flag iciend; char *icifmt; ftnint icirlen; ftnint icirnum; } icilist; /*open*/ typedef struct { flag oerr; ftnint ounit; char *ofnm; ftnlen ofnmlen; char *osta; char *oacc; char *ofm; ftnint orl; char *oblnk; } olist; /*close*/ typedef struct { flag cerr; ftnint cunit; char *csta; } cllist; /*rewind, backspace, endfile*/ typedef struct { flag aerr; ftnint aunit; } alist; /* inquire */ typedef struct { flag inerr; ftnint inunit; char *infile; ftnlen infilen; ftnint *inex; /*parameters in standard's order*/ ftnint *inopen; ftnint *innum; ftnint *innamed; char *inname; ftnlen innamlen; char *inacc; ftnlen inacclen; char *inseq; ftnlen inseqlen; char *indir; ftnlen indirlen; char *infmt; ftnlen infmtlen; char *inform; ftnint informlen; char *inunf; ftnlen inunflen; ftnint *inrecl; ftnint *innrec; char *inblank; ftnlen inblanklen; } inlist; #define VOID void union Multitype { /* for multiple entry points */ integer1 g; shortint h; integer i; /* longint j; */ real r; doublereal d; complex c; doublecomplex z; }; typedef union Multitype Multitype; /*typedef long int Long;*/ /* No longer used; formerly in Namelist */ struct Vardesc { /* for Namelist */ char *name; char *addr; ftnlen *dims; int type; }; typedef struct Vardesc Vardesc; struct Namelist { char *name; Vardesc **vars; int nvars; }; typedef struct Namelist Namelist; #define abs(x) ((x) >= 0 ? (x) : -(x)) #define dabs(x) (doublereal)abs(x) #define min(a,b) ((a) <= (b) ? (a) : (b)) #define max(a,b) ((a) >= (b) ? (a) : (b)) #define dmin(a,b) (doublereal)min(a,b) #define dmax(a,b) (doublereal)max(a,b) #define bit_test(a,b) ((a) >> (b) & 1) #define bit_clear(a,b) ((a) & ~((uinteger)1 << (b))) #define bit_set(a,b) ((a) | ((uinteger)1 << (b))) /* procedure parameter types for -A and -C++ */ #define F2C_proc_par_types 1 #ifdef __cplusplus typedef int /* Unknown procedure type */ (*U_fp)(...); typedef shortint (*J_fp)(...); typedef integer (*I_fp)(...); typedef real (*R_fp)(...); typedef doublereal (*D_fp)(...), (*E_fp)(...); typedef /* Complex */ VOID (*C_fp)(...); typedef /* Double Complex */ VOID (*Z_fp)(...); typedef logical (*L_fp)(...); typedef shortlogical (*K_fp)(...); typedef /* Character */ VOID (*H_fp)(...); typedef /* Subroutine */ int (*S_fp)(...); #else typedef int /* Unknown procedure type */ (*U_fp)(); typedef shortint (*J_fp)(); typedef integer (*I_fp)(); typedef real (*R_fp)(); typedef doublereal (*D_fp)(), (*E_fp)(); typedef /* Complex */ VOID (*C_fp)(); typedef /* Double Complex */ VOID (*Z_fp)(); typedef logical (*L_fp)(); typedef shortlogical (*K_fp)(); typedef /* Character */ VOID (*H_fp)(); typedef /* Subroutine */ int (*S_fp)(); #endif /* E_fp is for real functions when -R is not specified */ typedef VOID C_f; /* complex function */ typedef VOID H_f; /* character function */ typedef VOID Z_f; /* double complex function */ typedef doublereal E_f; /* real function with -R not specified */ /* undef any lower-case symbols that your C compiler predefines, e.g.: */ #ifndef Skip_f2c_Undefs #undef cray #undef gcos #undef mc68010 #undef mc68020 #undef mips #undef pdp11 #undef sgi #undef sparc #undef sun #undef sun2 #undef sun3 #undef sun4 #undef u370 #undef u3b #undef u3b2 #undef u3b5 #undef unix #undef vax #endif #endif igraph/src/foreign-lgl-parser.c0000644000176000001440000013405012325527073016217 0ustar ripleyusers/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Using locations. */ #define YYLSP_NEEDED 1 /* Substitute the variable and function names. */ #define yyparse igraph_lgl_yyparse #define yylex igraph_lgl_yylex #define yyerror igraph_lgl_yyerror #define yylval igraph_lgl_yylval #define yychar igraph_lgl_yychar #define yydebug igraph_lgl_yydebug #define yynerrs igraph_lgl_yynerrs #define yylloc igraph_lgl_yylloc /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { ALNUM = 258, NEWLINE = 259, HASH = 260 }; #endif /* Tokens. */ #define ALNUM 258 #define NEWLINE 259 #define HASH 260 /* Copy the first part of user declarations. */ #line 23 "igraph/src/foreign-lgl-parser.y" /* IGraph library. Copyright (C) 2006-2012 Gabor Csardi 334 Harvard st, Cambridge, MA, 02138 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __clang__ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wsign-conversion" #endif #include #include #include "igraph_hacks_internal.h" #include "igraph_types.h" #include "igraph_types_internal.h" #include "igraph_math.h" #include "igraph_memory.h" #include "igraph_error.h" #include "config.h" #include "foreign-lgl-header.h" #include "foreign-lgl-parser.h" #define yyscan_t void* int igraph_lgl_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, char *s); char *igraph_lgl_yyget_text (yyscan_t yyscanner ); int igraph_lgl_yyget_leng (yyscan_t yyscanner ); igraph_real_t igraph_lgl_get_number(const char *str, long int len); #define scanner context->scanner /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE #line 86 "igraph/src/foreign-lgl-parser.y" { long int edgenum; double weightnum; } /* Line 193 of yacc.c. */ #line 173 "y.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; # define yyltype YYLTYPE /* obsolescent; will be withdrawn */ # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ /* Line 216 of yacc.c. */ #line 198 "y.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int i) #else static int YYID (i) int i; #endif { return i; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss; YYSTYPE yyvs; YYLTYPE yyls; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 10 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 6 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 8 /* YYNRULES -- Number of rules. */ #define YYNRULES 12 /* YYNRULES -- Number of states. */ #define YYNSTATES 17 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 260 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint8 yyprhs[] = { 0, 0, 3, 4, 7, 10, 13, 17, 18, 21, 24, 28, 30 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 7, 0, -1, -1, 7, 4, -1, 7, 8, -1, 9, 10, -1, 5, 12, 4, -1, -1, 10, 11, -1, 12, 4, -1, 12, 13, 4, -1, 3, -1, 3, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { 0, 100, 100, 101, 102, 105, 107, 109, 109, 111, 116, 125, 130 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "ALNUM", "NEWLINE", "HASH", "$accept", "input", "vertex", "vertexdef", "edges", "edge", "edgeid", "weight", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 6, 7, 7, 7, 8, 9, 10, 10, 11, 11, 12, 13 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 0, 2, 2, 2, 3, 0, 2, 2, 3, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 2, 0, 1, 3, 0, 4, 7, 11, 0, 5, 6, 8, 0, 12, 9, 0, 10 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 1, 5, 6, 9, 11, 8, 15 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -3 static const yytype_int8 yypact[] = { -3, 0, -3, -3, 3, -3, -3, -3, -1, 3, -3, -3, -2, -3, -3, 4, -3 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -3, -3, -3, -3, -3, -3, 1, -3 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint8 yytable[] = { 2, 13, 14, 10, 3, 4, 7, 0, 16, 0, 12 }; static const yytype_int8 yycheck[] = { 0, 3, 4, 4, 4, 5, 3, -1, 4, -1, 9 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 7, 0, 4, 5, 8, 9, 3, 12, 10, 4, 11, 12, 3, 4, 13, 4 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) #else # define YYLEX yylex (&yylval, &yylloc, scanner) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location, context); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_lgl_parsedata_t* context; #endif { if (!yyvaluep) return; YYUSE (yylocationp); YYUSE (context); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; YYLTYPE const * const yylocationp; igraph_i_lgl_parsedata_t* context; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); YY_LOCATION_PRINT (yyoutput, *yylocationp); YYFPRINTF (yyoutput, ": "); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void yy_stack_print (bottom, top) yytype_int16 *bottom; yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); for (; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_lgl_parsedata_t* context) #else static void yy_reduce_print (yyvsp, yylsp, yyrule, context) YYSTYPE *yyvsp; YYLTYPE *yylsp; int yyrule; igraph_i_lgl_parsedata_t* context; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , context); fprintf (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, yylsp, Rule, context); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_lgl_parsedata_t* context) #else static void yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) const char *yymsg; int yytype; YYSTYPE *yyvaluep; YYLTYPE *yylocationp; igraph_i_lgl_parsedata_t* context; #endif { YYUSE (yyvaluep); YYUSE (yylocationp); YYUSE (context); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (igraph_i_lgl_parsedata_t* context); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (igraph_i_lgl_parsedata_t* context) #else int yyparse (context) igraph_i_lgl_parsedata_t* context; #endif #endif { /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /* Location data for the look-ahead symbol. */ YYLTYPE yylloc; int yystate; int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss = yyssa; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[2]; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; yylsp = yyls; #if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Initialize the default location before parsing starts. */ yylloc.first_line = yylloc.last_line = 1; yylloc.first_column = yylloc.last_column = 0; #endif goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyls = yyls1; yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); YYSTACK_RELOCATE (yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a look-ahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); YY_REDUCE_PRINT (yyn); switch (yyn) { case 6: #line 107 "igraph/src/foreign-lgl-parser.y" { context->actvertex=(yyvsp[(2) - (3)].edgenum); ;} break; case 9: #line 111 "igraph/src/foreign-lgl-parser.y" { igraph_vector_push_back(context->vector, context->actvertex); igraph_vector_push_back(context->vector, (yyvsp[(1) - (2)].edgenum)); igraph_vector_push_back(context->weights, 0); ;} break; case 10: #line 116 "igraph/src/foreign-lgl-parser.y" { igraph_vector_push_back(context->vector, context->actvertex); igraph_vector_push_back(context->vector, (yyvsp[(1) - (3)].edgenum)); igraph_vector_push_back(context->weights, (yyvsp[(2) - (3)].weightnum)); context->has_weights = 1; ;} break; case 11: #line 125 "igraph/src/foreign-lgl-parser.y" { igraph_trie_get2(context->trie, igraph_lgl_yyget_text(scanner), igraph_lgl_yyget_leng(scanner), &(yyval.edgenum)); ;} break; case 12: #line 130 "igraph/src/foreign-lgl-parser.y" { (yyval.weightnum)=igraph_lgl_get_number(igraph_lgl_yyget_text(scanner), igraph_lgl_yyget_leng(scanner)); ;} break; /* Line 1267 of yacc.c. */ #line 1458 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (&yylloc, context, YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (&yylloc, context, yymsg); } else { yyerror (&yylloc, context, YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } yyerror_range[0] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, context); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; yyerror_range[0] = yylsp[1-yylen]; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[0] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp, context); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; yyerror_range[1] = yylloc; /* Using YYLLOC is tempting, but would change the location of the look-ahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, context, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, context); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp, context); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } #line 133 "igraph/src/foreign-lgl-parser.y" int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, char *s) { snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char), "Parse error in LGL file, line %i (%s)", locp->first_line, s); return 0; } igraph_real_t igraph_lgl_get_number(const char *str, long int length) { igraph_real_t num; char *tmp=igraph_Calloc(length+1, char); strncpy(tmp, str, length); tmp[length]='\0'; sscanf(tmp, "%lf", &num); igraph_Free(tmp); return num; } igraph/src/cs_scc.c0000644000176000001440000000534212325527073013756 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* find the strongly connected components of a square matrix */ csd *cs_scc (cs *A) /* matrix A temporarily modified, then restored */ { CS_INT n, i, k, b, nb = 0, top, *xi, *pstack, *p, *r, *Ap, *ATp, *rcopy, *Blk ; cs *AT ; csd *D ; if (!CS_CSC (A)) return (NULL) ; /* check inputs */ n = A->n ; Ap = A->p ; D = cs_dalloc (n, 0) ; /* allocate result */ AT = cs_transpose (A, 0) ; /* AT = A' */ xi = cs_malloc (2*n+1, sizeof (CS_INT)) ; /* get workspace */ if (!D || !AT || !xi) return (cs_ddone (D, AT, xi, 0)) ; Blk = xi ; rcopy = pstack = xi + n ; p = D->p ; r = D->r ; ATp = AT->p ; top = n ; for (i = 0 ; i < n ; i++) /* first dfs(A) to find finish times (xi) */ { if (!CS_MARKED (Ap, i)) top = cs_dfs (i, A, top, xi, pstack, NULL) ; } for (i = 0 ; i < n ; i++) CS_MARK (Ap, i) ; /* restore A; unmark all nodes*/ top = n ; nb = n ; for (k = 0 ; k < n ; k++) /* dfs(A') to find strongly connnected comp */ { i = xi [k] ; /* get i in reverse order of finish times */ if (CS_MARKED (ATp, i)) continue ; /* skip node i if already ordered */ r [nb--] = top ; /* node i is the start of a component in p */ top = cs_dfs (i, AT, top, p, pstack, NULL) ; } r [nb] = 0 ; /* first block starts at zero; shift r up */ for (k = nb ; k <= n ; k++) r [k-nb] = r [k] ; D->nb = nb = n-nb ; /* nb = # of strongly connected components */ for (b = 0 ; b < nb ; b++) /* sort each block in natural order */ { for (k = r [b] ; k < r [b+1] ; k++) Blk [p [k]] = b ; } for (b = 0 ; b <= nb ; b++) rcopy [b] = r [b] ; for (i = 0 ; i < n ; i++) p [rcopy [Blk [i]]++] = i ; return (cs_ddone (D, AT, xi, 1)) ; } igraph/src/fortran_intrinsics.c0000644000176000001440000000242712325527073016442 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2011-12 Gabor Csardi 334 Harvard street, Cambridge MA, 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include double digitsdbl_(double x) { return (double) DBL_MANT_DIG; } double epsilondbl_(double x) { return DBL_EPSILON; } double hugedbl_(double x) { return DBL_MAX; } double tinydbl_(double x) { return DBL_MIN; } int maxexponentdbl_(double x) { return DBL_MAX_EXP; } int minexponentdbl_(double x) { return DBL_MIN_EXP; } double radixdbl_(double x) { return (double) FLT_RADIX; } igraph/src/bigint.h0000644000176000001440000000665112325527072014005 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_BIGINT_H #define IGRAPH_BIGINT_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_vector.h" #include "bignum.h" #include /* Arbitrary precision integer */ #define BASE_LIMB #include "igraph_pmt.h" #include "igraph_vector_type.h" #include "igraph_vector_pmt.h" #include "igraph_pmt_off.h" #undef BASE_LIMB typedef struct igraph_biguint_t { igraph_vector_limb_t v; } igraph_biguint_t; #define IGRAPH_BIGUINT_DEFAULT_SIZE 5 int igraph_biguint_init(igraph_biguint_t *b); void igraph_biguint_destroy(igraph_biguint_t *b); int igraph_biguint_copy(igraph_biguint_t *to, igraph_biguint_t *from); int igraph_biguint_extend(igraph_biguint_t *b, limb_t l); int igraph_biguint_size(igraph_biguint_t *b); int igraph_biguint_resize(igraph_biguint_t *b, int newlength); int igraph_biguint_reserve(igraph_biguint_t *b, int length); int igraph_biguint_zero(igraph_biguint_t *b); int igraph_biguint_set_limb(igraph_biguint_t *b, int value); igraph_real_t igraph_biguint_get(igraph_biguint_t *b); int igraph_biguint_compare_limb(igraph_biguint_t *b, limb_t l); int igraph_biguint_compare(igraph_biguint_t *left, igraph_biguint_t *right); igraph_bool_t igraph_biguint_equal(igraph_biguint_t *left, igraph_biguint_t *right); igraph_bool_t igraph_biguint_bigger(igraph_biguint_t *left, igraph_biguint_t *right); igraph_bool_t igraph_biguint_biggerorequal(igraph_biguint_t *left, igraph_biguint_t *right); int igraph_biguint_inc(igraph_biguint_t *res, igraph_biguint_t *b); int igraph_biguint_dec(igraph_biguint_t *res, igraph_biguint_t *b); int igraph_biguint_add_limb(igraph_biguint_t *res, igraph_biguint_t *b, limb_t l); int igraph_biguint_sub_limb(igraph_biguint_t *res, igraph_biguint_t *b, limb_t l); int igraph_biguint_mul_limb(igraph_biguint_t *res, igraph_biguint_t *b, limb_t l); int igraph_biguint_add(igraph_biguint_t *res, igraph_biguint_t *left, igraph_biguint_t *right); int igraph_biguint_sub(igraph_biguint_t *res, igraph_biguint_t *left, igraph_biguint_t *right); int igraph_biguint_mul(igraph_biguint_t *res, igraph_biguint_t *left, igraph_biguint_t *right); int igraph_biguint_div(igraph_biguint_t *q, igraph_biguint_t *r, igraph_biguint_t *u, igraph_biguint_t *v); int igraph_biguint_print(igraph_biguint_t *b); int igraph_biguint_fprint(igraph_biguint_t *b, FILE *file); __END_DECLS #endif igraph/src/cs_gaxpy.c0000644000176000001440000000246412325527073014340 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* y = A*x+y */ CS_INT cs_gaxpy (const cs *A, const CS_ENTRY *x, CS_ENTRY *y) { CS_INT p, j, n, *Ap, *Ai ; CS_ENTRY *Ax ; if (!CS_CSC (A) || !x || !y) return (0) ; /* check inputs */ n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; for (j = 0 ; j < n ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { y [Ai [p]] += Ax [p] * x [j] ; } } return (1) ; } igraph/src/amd_post_tree.c0000644000176000001440000001070512325527072015344 0ustar ripleyusers/* ========================================================================= */ /* === AMD_post_tree ======================================================= */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* AMD, Copyright (c) Timothy A. Davis, */ /* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ /* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ /* web: http://www.cise.ufl.edu/research/sparse/amd */ /* ------------------------------------------------------------------------- */ /* Post-ordering of a supernodal elimination tree. */ #include "amd_internal.h" GLOBAL Int AMD_post_tree ( Int root, /* root of the tree */ Int k, /* start numbering at k */ Int Child [ ], /* input argument of size nn, undefined on * output. Child [i] is the head of a link * list of all nodes that are children of node * i in the tree. */ const Int Sibling [ ], /* input argument of size nn, not modified. * If f is a node in the link list of the * children of node i, then Sibling [f] is the * next child of node i. */ Int Order [ ], /* output order, of size nn. Order [i] = k * if node i is the kth node of the reordered * tree. */ Int Stack [ ] /* workspace of size nn */ #ifndef NDEBUG , Int nn /* nodes are in the range 0..nn-1. */ #endif ) { Int f, head, h, i ; #if 0 /* --------------------------------------------------------------------- */ /* recursive version (Stack [ ] is not used): */ /* --------------------------------------------------------------------- */ /* this is simple, but can caouse stack overflow if nn is large */ i = root ; for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) { k = AMD_post_tree (f, k, Child, Sibling, Order, Stack, nn) ; } Order [i] = k++ ; return (k) ; #endif /* --------------------------------------------------------------------- */ /* non-recursive version, using an explicit stack */ /* --------------------------------------------------------------------- */ /* push root on the stack */ head = 0 ; Stack [0] = root ; while (head >= 0) { /* get head of stack */ ASSERT (head < nn) ; i = Stack [head] ; AMD_DEBUG1 (("head of stack "ID" \n", i)) ; ASSERT (i >= 0 && i < nn) ; if (Child [i] != EMPTY) { /* the children of i are not yet ordered */ /* push each child onto the stack in reverse order */ /* so that small ones at the head of the list get popped first */ /* and the biggest one at the end of the list gets popped last */ for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) { head++ ; ASSERT (head < nn) ; ASSERT (f >= 0 && f < nn) ; } h = head ; ASSERT (head < nn) ; for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) { ASSERT (h > 0) ; Stack [h--] = f ; AMD_DEBUG1 (("push "ID" on stack\n", f)) ; ASSERT (f >= 0 && f < nn) ; } ASSERT (Stack [h] == i) ; /* delete child list so that i gets ordered next time we see it */ Child [i] = EMPTY ; } else { /* the children of i (if there were any) are already ordered */ /* remove i from the stack and order it. Front i is kth front */ head-- ; AMD_DEBUG1 (("pop "ID" order "ID"\n", i, k)) ; Order [i] = k++ ; ASSERT (k <= nn) ; } #ifndef NDEBUG AMD_DEBUG1 (("\nStack:")) ; for (h = head ; h >= 0 ; h--) { Int j = Stack [h] ; AMD_DEBUG1 ((" "ID, j)) ; ASSERT (j >= 0 && j < nn) ; } AMD_DEBUG1 (("\n\n")) ; ASSERT (head < nn) ; #endif } return (k) ; } igraph/src/version.c0000644000176000001440000000414712325527074014211 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2008-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_version.h" #include static const char *igraph_version_string=IGRAPH_VERSION; /** * \function igraph_version * Return the version of the igraph C library * * \param version_string Pointer to a string pointer. If not null, it * is set to the igraph version string, e.g. "0.6" or "0.5.3". This * string should not be modified or deallocated. * \param major If not a null pointer, then it is set to the major * igraph version. E.g. for version "0.5.3" this is 0. * \param minor If not a null pointer, then it is set to the minor * igraph version. E.g. for version "0.5.3" this is 5. * \param subminor If not a null pointer, then it is set to the * subminor igraph version. E.g. for version "0.5.3" this is 3. * \return Error code. * * Time complexity: O(1). * * \example examples/simple/igraph_version.c */ int igraph_version(const char **version_string, int *major, int *minor, int *subminor) { int i1, i2, i3; int *p1= major ? major : &i1, *p2= minor ? minor : &i2, *p3= subminor ? subminor : &i3; if (version_string) { *version_string = igraph_version_string; } *p1 = *p2 = *p3 = 0; sscanf(IGRAPH_VERSION, "%i.%i.%i", p1, p2, p3); return 0; } igraph/src/igraph_hrg.cc0000644000176000001440000007452112325527073015003 0ustar ripleyusers/* -*- mode: C++ -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_interface.h" #include "igraph_community.h" #include "igraph_memory.h" #include "igraph_constructors.h" #include "igraph_attributes.h" #include "igraph_foreign.h" #include "igraph_hrg.h" #include "igraph_random.h" #include "hrg_dendro.h" #include "hrg_graph.h" #include "hrg_graph_simp.h" using namespace fitHRG; /** * \section hrg_intro Introduction * * A hierarchical random graph is an ensemble of undirected * graphs with \c n vertices. It is defined via a binary tree with \c * n leaf and \c n-1 internal vertices, where the * internal vertices are labeled with probabilities. * The probability that two vertices are connected in the random graph * is given by the probability label at their closest common * ancestor. * * * Please read the following two articles for more about * hierarchical random graphs: A. Clauset, C. Moore, and M.E.J. Newman. * Hierarchical structure and the prediction of missing links in networks. * Nature 453, 98 - 101 (2008); and A. Clauset, C. Moore, and M.E.J. Newman. * Structural Inference of Hierarchies in Networks. In E. M. Airoldi * et al. (Eds.): ICML 2006 Ws, Lecture Notes in Computer Science * 4503, 1-13. Springer-Verlag, Berlin Heidelberg (2007). * * * * igraph contains functions for fitting HRG models to a given network * (\ref igraph_hrg_fit), for generating networks from a given HRG * ensemble (\ref igraph_hrg_game, \ref igraph_hrg_sample), converting * an igraph graph to a HRG and back (\ref igraph_hrg_create, \ref * igraph_hrg_dendrogram), for calculating a consensus tree from a * set of sampled HRGs (\ref igraph_hrg_consensus) and for predicting * missing edges in a network based on its HRG models (\ref * igraph_hrg_predict). * * * The igraph HRG implementation is heavily based on the code * published by Aaron Clauset, at his website, * http://tuvalu.santafe.edu/~aaronc/hierarchy/ * */ namespace fitHRG { struct pblock { double L; int i; int j; }; } int markovChainMonteCarlo(dendro *d, unsigned int period, igraph_hrg_t *hrg) { igraph_real_t bestL=d->getLikelihood(); double dL; bool flag_taken; // Because moves in the dendrogram space are chosen (Monte // Carlo) so that we sample dendrograms with probability // proportional to their likelihood, a likelihood-proportional // sampling of the dendrogram models would be equivalent to a // uniform sampling of the walk itself. We would still have to // decide how often to sample the walk (at most once every n // steps is recommended) but for simplicity, the code here // simply runs the MCMC itself. To actually compute something // over the set of sampled dendrogram models (in a Bayesian // model averaging sense), you'll need to code that yourself. // do 'period' MCMC moves before doing anything else for (unsigned int i=0; imonteCarloMove(dL, flag_taken, 1.0)); // get likelihood of this D given G igraph_real_t cl= d->getLikelihood(); if (cl > bestL) { // store the current best likelihood bestL = cl; // record the HRG structure d->recordDendrogramStructure(hrg); } } // corrects floating-point errors O(n) d->refreshLikelihood(); return 0; } int markovChainMonteCarlo2(dendro *d, int num_samples) { bool flag_taken; double dL, ptest = 1.0/(50.0*(double)(d->g->numNodes())); int sample_num=0, t=1, thresh = 200 * d->g->numNodes(); // Since we're sampling uniformly at random over the equilibrium // walk, we just need to do a bunch of MCMC moves and let the // sampling happen on its own. while (sample_num < num_samples) { // Make a single MCMC move d->monteCarloMove(dL, flag_taken, 1.0); // We sample the dendrogram space once every n MCMC moves (on // average). Depending on the flags on the command line, we sample // different aspects of the dendrograph structure. if (t > thresh && RNG_UNIF01() < ptest) { sample_num++; d->sampleSplitLikelihoods(sample_num); } t++; // correct floating-point errors O(n) d->refreshLikelihood(); // TODO: less frequently } return 0; } int MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { // We want to run the MCMC until we've found equilibrium; we // use the heuristic of the average log-likelihood (which is // exactly the entropy) over X steps being very close to the // average log-likelihood (entropy) over the X steps that // preceded those. In other words, we look for an apparent // local convergence of the entropy measure of the MCMC. bool flag_taken; igraph_real_t dL, Likeli; igraph_real_t oldMeanL; igraph_real_t newMeanL=-1e-49; while (1) { oldMeanL = newMeanL; newMeanL = 0.0; for (int i=0; i<65536; i++) { IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0)); Likeli = d->getLikelihood(); newMeanL += Likeli; } // corrects floating-point errors O(n) d->refreshLikelihood(); if (fabs(newMeanL-oldMeanL)/65536.0 < 1.0) { break; } } // Record the result if (hrg) { d->recordDendrogramStructure(hrg); } return 0; } int igraph_i_hrg_getgraph(const igraph_t *igraph, dendro *d) { int no_of_nodes = igraph_vcount(igraph); int no_of_edges = igraph_ecount(igraph); int i; // Create graph d->g=new graph(no_of_nodes); // Add edges for (i=0; ig->doesLinkExist(from, to)) { d->g->addLink(from, to); } if (!d->g->doesLinkExist(to, from)) { d->g->addLink(to, from); } } d->buildDendrogram(); return 0; } int igraph_i_hrg_getsimplegraph(const igraph_t *igraph, dendro *d, simpleGraph **sg, int num_bins) { int no_of_nodes = igraph_vcount(igraph); int no_of_edges = igraph_ecount(igraph); int i; // Create graphs d->g = new graph(no_of_nodes, true); d->g->setAdjacencyHistograms(num_bins); (*sg) = new simpleGraph(no_of_nodes); for (i=0; ig->doesLinkExist(from, to)) { d->g->addLink(from, to); } if (!d->g->doesLinkExist(to, from)) { d->g->addLink(to, from); } if (!(*sg)->doesLinkExist(from, to)) { (*sg)->addLink(from, to); } if (!(*sg)->doesLinkExist(to, from)) { (*sg)->addLink(to, from); } } d->buildDendrogram(); return 0; } /** * \function igraph_hrg_init * Allocate memory for a HRG. * * This function must be called before passing an \ref igraph_hrg_t to * an igraph function. * \param hrg Pointer to the HRG data structure to initialize. * \param n The number of vertices in the graph that is modeled by * this HRG. It can be zero, if this is not yet known. * \return Error code. * * Time complexity: O(n), the number of vertices in the graph. */ int igraph_hrg_init(igraph_hrg_t *hrg, int n) { IGRAPH_VECTOR_INIT_FINALLY(&hrg->left, n-1); IGRAPH_VECTOR_INIT_FINALLY(&hrg->right, n-1); IGRAPH_VECTOR_INIT_FINALLY(&hrg->prob, n-1); IGRAPH_VECTOR_INIT_FINALLY(&hrg->edges, n-1); IGRAPH_VECTOR_INIT_FINALLY(&hrg->vertices, n-1); IGRAPH_FINALLY_CLEAN(5); return 0; } /** * \function igraph_hrg_destroy * Deallocate memory for an HRG. * * The HRG data structure can be reinitialized again with an \ref * igraph_hrg_destroy call. * \param hrg Pointer to the HRG data structure to deallocate. * * Time complexity: operating system dependent. */ void igraph_hrg_destroy(igraph_hrg_t *hrg) { igraph_vector_destroy(&hrg->left); igraph_vector_destroy(&hrg->right); igraph_vector_destroy(&hrg->prob); igraph_vector_destroy(&hrg->edges); igraph_vector_destroy(&hrg->vertices); } /** * \function igraph_hrg_size * Returns the size of the HRG, the number of leaf nodes. * * \param hrg Pointer to the HRG. * \return The number of leaf nodes in the HRG. * * Time complexity: O(1). */ int igraph_hrg_size(const igraph_hrg_t *hrg) { return igraph_vector_size(&hrg->left)+1; } /** * \function igraph_hrg_resize * Resize a HRG. * * \param hrg Pointer to an initialized (see \ref igraph_hrg_init) * HRG. * \param newsize The new size, i.e. the number of leaf nodes. * \return Error code. * * Time complexity: O(n), n is the new size. */ int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize) { int origsize=igraph_hrg_size(hrg); int ret=0; igraph_error_handler_t *oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); ret = igraph_vector_resize(&hrg->left, newsize-1); ret |= igraph_vector_resize(&hrg->right, newsize-1); ret |= igraph_vector_resize(&hrg->prob, newsize-1); ret |= igraph_vector_resize(&hrg->edges, newsize-1); ret |= igraph_vector_resize(&hrg->vertices, newsize-1); igraph_set_error_handler(oldhandler); if (ret) { igraph_vector_resize(&hrg->left, origsize); igraph_vector_resize(&hrg->right, origsize); igraph_vector_resize(&hrg->prob, origsize); igraph_vector_resize(&hrg->edges, origsize); igraph_vector_resize(&hrg->vertices, origsize); IGRAPH_ERROR("Cannot resize HRG", ret); } return 0; } /** * \function igraph_hrg_fit * Fit a hierarchical random graph model to a network * * \param graph The igraph graph to fit the model to. Edge directions * are ignored in directed graphs. * \param hrg Pointer to an initialized HRG, the result of the fitting * is stored here. It can also be used to pass a HRG to the * function, that can be used as the starting point of the Markov * Chain Monte Carlo fitting, if the \c start argument is true. * \param start Logical, whether to start the fitting from the given * HRG. * \param steps Integer, the number of MCMC steps to take in the * fitting procedure. If this is zero, then the fitting stop is a * convergence criteria is fulfilled. * \return Error code. * * Time complexity: TODO. */ int igraph_hrg_fit(const igraph_t *graph, igraph_hrg_t *hrg, igraph_bool_t start, int steps) { int no_of_nodes=igraph_vcount(graph); dendro *d; RNG_BEGIN(); d = new dendro; // Convert the igraph graph IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); // If we want to start from HRG if (start) { if (igraph_hrg_size(hrg) != no_of_nodes) { IGRAPH_ERROR("Invalid HRG to start from", IGRAPH_EINVAL); } d->importDendrogramStructure(hrg); } else { IGRAPH_CHECK(igraph_hrg_resize(hrg, no_of_nodes)); } // Run fixed number of steps, or until convergence if (steps > 0) { IGRAPH_CHECK(markovChainMonteCarlo(d, steps, hrg)); } else { IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); } delete d; RNG_END(); return 0; } /** * \function igraph_hrg_sample * Sample from a hierarchical random graph model * * Sample from a hierarchical random graph ensemble. The ensemble can * be given as a graph (\c input_graph), or as a HRG object (\c hrg). * If a graph is given, then first an MCMC optimization is performed * to find the optimal fitting model; then the MCMC is used to sample * the graph(s). * \param input_graph An igraph graph, or a null pointer. If not a * null pointer, then a HRG is first fitted to the graph, possibly * starting from the given HRG, if the \c start argument is true. If * is is a null pointer, then the given HRG is used as a starting * point, to find the optimum of the Markov chain, before the * sampling. * \param sample Pointer to an uninitialized graph, or a null * pointer. If only one sample is requested, and it is not a null * pointer, then the sample is stored here. * \param samples An initialized vector of pointers. If more than one * samples are requested, then they are stored here. Note that to * free this data structure, you need to call \ref igraph_destroy on * each graph first, then \c free() on all pointers, and finally * \ref igraph_vector_ptr_destroy. * \param no_samples The number of samples to generate. * \param hrg A HRG. It is modified during the sampling. * \param start Logical, whether to start the MCMC from the given * HRG. * \return Error code. * * Time complexity: TODO. */ int igraph_hrg_sample(const igraph_t *input_graph, igraph_t *sample, igraph_vector_ptr_t *samples, int no_samples, igraph_hrg_t *hrg, igraph_bool_t start) { int i; dendro *d; if (no_samples < 0) { IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL); } if (!sample && !samples) { IGRAPH_ERROR("Give at least one of `sample' and `samples'", IGRAPH_EINVAL); } if (no_samples != 1 && sample) { IGRAPH_ERROR("Number of samples should be one if `sample' is given", IGRAPH_EINVAL); } if (no_samples > 1 && !samples) { IGRAPH_ERROR("`samples' must be non-null if number of samples " "is larger than 1", IGRAPH_EINVAL); } if (!start && !input_graph) { IGRAPH_ERROR("Input graph must be given if initial HRG is not used", IGRAPH_EINVAL); } if (!start) { IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(input_graph))); } if (input_graph && igraph_hrg_size(hrg) != igraph_vcount(input_graph)) { IGRAPH_ERROR("Invalid HRG size, should match number of nodes", IGRAPH_EINVAL); } RNG_BEGIN(); d = new dendro; // Need to find equilibrium first? if (start) { d->importDendrogramStructure(hrg); } else { IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); } // TODO: free on error if (sample) { // A single graph d->makeRandomGraph(); d->recordGraphStructure(sample); if (samples) { igraph_t *G=igraph_Calloc(1, igraph_t); if (!G) { IGRAPH_ERROR("Cannot sample HRG graphs", IGRAPH_ENOMEM); } d->recordGraphStructure(G); IGRAPH_CHECK(igraph_vector_ptr_resize(samples, 1)); VECTOR(*samples)[0]=G; } } else { // Sample many IGRAPH_CHECK(igraph_vector_ptr_resize(samples, no_samples)); for (i=0; imakeRandomGraph(); d->recordGraphStructure(G); VECTOR(*samples)[i]=G; } } delete d; RNG_END(); return 0; } /** * \function igraph_hrg_game * Generate a hierarchical random graph * * This function is a simple shortcut to \ref igraph_hrg_sample. * It creates a single graph, from the given HRG. * \param graph Pointer to an uninitialized graph, the new graph is * created here. * \param hrg The hierarchical random graph model to sample from. It * is modified during the MCMC process. * \return Error code. * * Time complexity: TODO. */ int igraph_hrg_game(igraph_t *graph, const igraph_hrg_t *hrg) { return igraph_hrg_sample(/* input_graph= */ 0, /* sample= */ graph, /* samples= */ 0, /* no_samples=*/ 1, /* hrg= */ (igraph_hrg_t*) hrg, /* start= */ 1); } /** * \function igraph_hrg_dendrogram * Create a dendrogram from a hierarchical random graph. * * Creates the igraph graph equivalent of an \ref igraph_hrg_t data * structure. * \param graph Pointer to an uninitialized graph, the result is * stored here. * \param hrg The hierarchical random graph to convert. * \return Error code. * * Time complexity: O(n), the number of vertices in the graph. */ int igraph_hrg_dendrogram(igraph_t *graph, const igraph_hrg_t *hrg) { int orig_nodes=igraph_hrg_size(hrg); int no_of_nodes=orig_nodes * 2 - 1; int no_of_edges=no_of_nodes-1; igraph_vector_t edges; int i, idx=0; igraph_vector_ptr_t vattrs; igraph_vector_t prob; igraph_attribute_record_t rec = { "probability", IGRAPH_ATTRIBUTE_NUMERIC, &prob }; // Probability labels, for leaf nodes they are IGRAPH_NAN IGRAPH_VECTOR_INIT_FINALLY(&prob, no_of_nodes); for (i=0; iprob)[i]; } IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattrs); VECTOR(vattrs)[0] = &rec; for (i=0; ileft)[i]; int right=VECTOR(hrg->right)[i]; VECTOR(edges)[idx++] = orig_nodes+i; VECTOR(edges)[idx++] = left < 0 ? orig_nodes-left-1 : left; VECTOR(edges)[idx++] = orig_nodes+i; VECTOR(edges)[idx++] = right < 0 ? orig_nodes-right-1 : right; } IGRAPH_CHECK(igraph_empty(graph, 0, IGRAPH_DIRECTED)); IGRAPH_FINALLY(igraph_destroy, graph); IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, &vattrs)); IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0)); igraph_vector_ptr_destroy(&vattrs); igraph_vector_destroy(&edges); igraph_vector_destroy(&prob); IGRAPH_FINALLY_CLEAN(4); // + 1 for graph return 0; } /** * \function igraph_hrg_consensus * Calculate a consensus tree for a HRG. * * The calculation can be started from the given HRG (\c hrg), or (if * \c start is false), a HRG is first fitted to the given graph. * * \param graph The input graph. * \param parents An initialized vector, the results are stored * here. For each vertex, the id of its parent vertex is stored, or * -1, if the vertex is the root vertex in the tree. The first n * vertex ids (from 0) refer to the original vertices of the graph, * the other ids refer to vertex groups. * \param weights Numeric vector, counts the number of times a given * tree split occured in the generated network samples, for each * internal vertices. The order is the same as in \c parents. * \param hrg A hierarchical random graph. It is used as a starting * point for the sampling, if the \c start argument is true. It is * modified along the MCMC. * \param start Logical, whether to use the supplied HRG (in \c hrg) * as a starting point for the MCMC. * \param num_samples The number of samples to generate for creating * the consensus tree. * \return Error code. * * Time complexity: TODO. */ int igraph_hrg_consensus(const igraph_t *graph, igraph_vector_t *parents, igraph_vector_t *weights, igraph_hrg_t *hrg, igraph_bool_t start, int num_samples) { dendro *d; if (start && !hrg) { IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); } RNG_BEGIN(); d = new dendro; IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); if (start) { d->importDendrogramStructure(hrg); } else { if (hrg) { igraph_hrg_resize(hrg, igraph_vcount(graph)); } IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); } IGRAPH_CHECK(markovChainMonteCarlo2(d, num_samples)); d->recordConsensusTree(parents, weights); delete d; RNG_END(); return 0; } int MCMCEquilibrium_Sample(dendro *d, int num_samples) { // Because moves in the dendrogram space are chosen (Monte // Carlo) so that we sample dendrograms with probability // proportional to their likelihood, a likelihood-proportional // sampling of the dendrogram models would be equivalent to a // uniform sampling of the walk itself. We would still have to // decide how often to sample the walk (at most once every n steps // is recommended) but for simplicity, the code here simply runs the // MCMC itself. To actually compute something over the set of // sampled dendrogram models (in a Bayesian model averaging sense), // you'll need to code that yourself. double dL; bool flag_taken; int sample_num=0; int t=1, thresh=100 * d->g->numNodes(); double ptest=1.0/10.0/d->g->numNodes(); while (sample_num < num_samples) { d->monteCarloMove(dL, flag_taken, 1.0); if (t > thresh && RNG_UNIF01() < ptest) { sample_num++; d->sampleAdjacencyLikelihoods(); } d->refreshLikelihood(); // TODO: less frequently t++; } return 0; } int QsortPartition (pblock* array, int left, int right, int index) { pblock p_value, temp; p_value.L = array[index].L; p_value.i = array[index].i; p_value.j = array[index].j; // swap(array[p_value], array[right]) temp.L = array[right].L; temp.i = array[right].i; temp.j = array[right].j; array[right].L = array[index].L; array[right].i = array[index].i; array[right].j = array[index].j; array[index].L = temp.L; array[index].i = temp.i; array[index].j = temp.j; int stored = left; for (int i=left; i left) { int pivot = left; int part = QsortPartition(array, left, right, pivot); QsortMain(array, left, part-1); QsortMain(array, part+1, right ); } return; } int rankCandidatesByProbability(simpleGraph *sg, dendro *d, pblock *br_list, int mk) { int mkk=0; int n=sg->getNumNodes(); for (int i=0; igetAdjacency(i, j) < 0.5) { double temp=d->g->getAdjacencyAverage(i, j); br_list[mkk].L = temp * (1.0 + RNG_UNIF01()/1000.0); br_list[mkk].i = i; br_list[mkk].j = j; mkk++; } } } // Sort the candidates by their average probability QsortMain(br_list, 0, mk-1); return 0; } int recordPredictions(pblock *br_list, igraph_vector_t *edges, igraph_vector_t *prob, int mk) { IGRAPH_CHECK(igraph_vector_resize(edges, mk*2)); IGRAPH_CHECK(igraph_vector_resize(prob, mk)); for (int i=mk-1, idx=0, idx2=0; i>=0; i--) { VECTOR(*edges)[idx++] = br_list[i].i; VECTOR(*edges)[idx++] = br_list[i].j; VECTOR(*prob)[idx2++] = br_list[i].L; } return 0; } /** * \function igraph_hrg_predict * Predict missing edges in a graph, based on HRG models * * Samples HRG models for a network, and estimated the probability * that an edge was falsely observed as non-existent in the network. * \param graph The input graph. * \param edges The list of missing edges is stored here, the first * two elements are the first edge, the next two the second edge, * etc. * \param prob Vector of probabilies for the existence of missing * edges, in the order corresponding to \c edges. * \param hrg A HRG, it is used as a starting point if \c start is * true. It is also modified during the MCMC sampling. * \param start Logical, whether to start the MCMC from the given HRG. * \param num_samples The number of samples to generate. * \param num_bins Controls the resolution of the edge * probabilities. Higher numbers result higher resolution. * \return Error code. * * Time complexity: TODO. */ int igraph_hrg_predict(const igraph_t *graph, igraph_vector_t *edges, igraph_vector_t *prob, igraph_hrg_t *hrg, igraph_bool_t start, int num_samples, int num_bins) { dendro *d; pblock *br_list; int mk; simpleGraph *sg; if (start && !hrg) { IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); } RNG_BEGIN(); d = new dendro; IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, d, &sg, num_bins)); mk = sg->getNumNodes() * (sg->getNumNodes()-1) / 2 - sg->getNumLinks()/2; br_list = new pblock[mk]; for (int i=0; iimportDendrogramStructure(hrg); } else { if (hrg) { igraph_hrg_resize(hrg, igraph_vcount(graph)); } IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); } IGRAPH_CHECK(MCMCEquilibrium_Sample(d, num_samples)); IGRAPH_CHECK(rankCandidatesByProbability(sg, d, br_list, mk)); IGRAPH_CHECK(recordPredictions(br_list, edges, prob, mk)); delete d; delete sg; delete [] br_list; RNG_END(); return 0; } /** * \function igraph_hrg_create * Create a HRG from an igraph graph. * * \param hrg Pointer to an initialized \ref igraph_hrg_t. The result * is stored here. * \param graph The igraph graph to convert. It must be a directed * binary tree, with n-1 internal and n leaf vertices. The root * vertex must have in-degree zero. * \param prob The vector of probabilities, this is used to label the * internal nodes of the hierarchical random graph. The values * corresponding to the leaves are ignored. * \return Error code. * * Time complexity: O(n), the number of vertices in the tree. */ int igraph_hrg_create(igraph_hrg_t *hrg, const igraph_t *graph, const igraph_vector_t *prob) { int no_of_nodes=igraph_vcount(graph); int no_of_internal=(no_of_nodes-1)/2; igraph_vector_t deg, idx; int root=0; int d0=0, d1=0, d2=0; int ii=0, il=0; igraph_vector_t neis; igraph_vector_t path; // -------------------------------------------------------- // CHECKS // -------------------------------------------------------- // At least three vertices are required if (no_of_nodes < 3) { IGRAPH_ERROR("HRG tree must have at least three vertices", IGRAPH_EINVAL); } // Prob vector was given if (!prob) { IGRAPH_ERROR("Probability vector must be given for HRG", IGRAPH_EINVAL); } // Length of prob vector if (igraph_vector_size(prob) != no_of_nodes) { IGRAPH_ERROR("HRG probability vector of wrong size", IGRAPH_EINVAL); } // Must be a directed graph if (!igraph_is_directed(graph)) { IGRAPH_ERROR("HRG graph must be directed", IGRAPH_EINVAL); } // Number of nodes must be odd if (no_of_nodes % 2 == 0) { IGRAPH_ERROR("Complete HRG graph must have odd number of vertices", IGRAPH_EINVAL); } IGRAPH_VECTOR_INIT_FINALLY(°, 0); // Every vertex, except for the root must have in-degree one. IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); for (int i=0; i= 0) { continue; } IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); VECTOR(hrg->left )[-ri-1] = VECTOR(idx)[ (int) VECTOR(neis)[0] ]; VECTOR(hrg->right)[-ri-1] = VECTOR(idx)[ (int) VECTOR(neis)[1] ]; VECTOR(hrg->prob )[-ri-1] = VECTOR(*prob)[i]; } // Calculate the number of vertices and edges in each subtree igraph_vector_null(&hrg->edges); igraph_vector_null(&hrg->vertices); IGRAPH_VECTOR_INIT_FINALLY(&path, 0); IGRAPH_CHECK(igraph_vector_push_back(&path, VECTOR(idx)[root])); while (!igraph_vector_empty(&path)) { int ri=igraph_vector_tail(&path); int lc=VECTOR(hrg->left)[-ri-1]; int rc=VECTOR(hrg->right)[-ri-1]; if (lc < 0 && VECTOR(hrg->vertices)[-lc-1]==0) { // Go left IGRAPH_CHECK(igraph_vector_push_back(&path, lc)); } else if (rc < 0 && VECTOR(hrg->vertices)[-rc-1]==0) { // Go right IGRAPH_CHECK(igraph_vector_push_back(&path, rc)); } else { // Subtrees are done, update node and go up VECTOR(hrg->vertices)[-ri-1] += lc < 0 ? VECTOR(hrg->vertices)[-lc-1] : 1; VECTOR(hrg->vertices)[-ri-1] += rc < 0 ? VECTOR(hrg->vertices)[-rc-1] : 1; VECTOR(hrg->edges)[-ri-1] += lc < 0 ? VECTOR(hrg->edges)[-lc-1]+1 : 1; VECTOR(hrg->edges)[-ri-1] += rc < 0 ? VECTOR(hrg->edges)[-rc-1]+1 : 1; igraph_vector_pop_back(&path); } } igraph_vector_destroy(&path); igraph_vector_destroy(&neis); igraph_vector_destroy(&idx); igraph_vector_destroy(°); IGRAPH_FINALLY_CLEAN(4); return 0; } igraph/src/igraph_cohesive_blocks.h0000644000176000001440000000231712325527073017221 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_COHESIVE_BLOCKS_H #define IGRAPH_COHESIVE_BLOCKS_H #include "igraph_datatype.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" int igraph_cohesive_blocks(const igraph_t *graph, igraph_vector_ptr_t *blocks, igraph_vector_t *cohesion, igraph_vector_t *parent, igraph_t *block_tree); #endif igraph/src/glpapi09.c0000644000176000001440000005754412325527073014161 0ustar ripleyusers/* glpapi09.c (mixed integer programming routines) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpios.h" #include "glpnpp.h" /*********************************************************************** * NAME * * glp_set_col_kind - set (change) column kind * * SYNOPSIS * * void glp_set_col_kind(glp_prob *mip, int j, int kind); * * DESCRIPTION * * The routine glp_set_col_kind sets (changes) the kind of j-th column * (structural variable) as specified by the parameter kind: * * GLP_CV - continuous variable; * GLP_IV - integer variable; * GLP_BV - binary variable. */ void glp_set_col_kind(glp_prob *mip, int j, int kind) { GLPCOL *col; if (!(1 <= j && j <= mip->n)) xerror("glp_set_col_kind: j = %d; column number out of range\n" , j); col = mip->col[j]; switch (kind) { case GLP_CV: col->kind = GLP_CV; break; case GLP_IV: col->kind = GLP_IV; break; case GLP_BV: col->kind = GLP_IV; if (!(col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0)) glp_set_col_bnds(mip, j, GLP_DB, 0.0, 1.0); break; default: xerror("glp_set_col_kind: j = %d; kind = %d; invalid column" " kind\n", j, kind); } return; } /*********************************************************************** * NAME * * glp_get_col_kind - retrieve column kind * * SYNOPSIS * * int glp_get_col_kind(glp_prob *mip, int j); * * RETURNS * * The routine glp_get_col_kind returns the kind of j-th column, i.e. * the kind of corresponding structural variable, as follows: * * GLP_CV - continuous variable; * GLP_IV - integer variable; * GLP_BV - binary variable */ int glp_get_col_kind(glp_prob *mip, int j) { GLPCOL *col; int kind; if (!(1 <= j && j <= mip->n)) xerror("glp_get_col_kind: j = %d; column number out of range\n" , j); col = mip->col[j]; kind = col->kind; switch (kind) { case GLP_CV: break; case GLP_IV: if (col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0) kind = GLP_BV; break; default: xassert(kind != kind); } return kind; } /*********************************************************************** * NAME * * glp_get_num_int - retrieve number of integer columns * * SYNOPSIS * * int glp_get_num_int(glp_prob *mip); * * RETURNS * * The routine glp_get_num_int returns the current number of columns, * which are marked as integer. */ int glp_get_num_int(glp_prob *mip) { GLPCOL *col; int j, count = 0; for (j = 1; j <= mip->n; j++) { col = mip->col[j]; if (col->kind == GLP_IV) count++; } return count; } /*********************************************************************** * NAME * * glp_get_num_bin - retrieve number of binary columns * * SYNOPSIS * * int glp_get_num_bin(glp_prob *mip); * * RETURNS * * The routine glp_get_num_bin returns the current number of columns, * which are marked as binary. */ int glp_get_num_bin(glp_prob *mip) { GLPCOL *col; int j, count = 0; for (j = 1; j <= mip->n; j++) { col = mip->col[j]; if (col->kind == GLP_IV && col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0) count++; } return count; } /*********************************************************************** * NAME * * glp_intopt - solve MIP problem with the branch-and-bound method * * SYNOPSIS * * int glp_intopt(glp_prob *P, const glp_iocp *parm); * * DESCRIPTION * * The routine glp_intopt is a driver to the MIP solver based on the * branch-and-bound method. * * On entry the problem object should contain optimal solution to LP * relaxation (which can be obtained with the routine glp_simplex). * * The MIP solver has a set of control parameters. Values of the control * parameters can be passed in a structure glp_iocp, which the parameter * parm points to. * * The parameter parm can be specified as NULL, in which case the MIP * solver uses default settings. * * RETURNS * * 0 The MIP problem instance has been successfully solved. This code * does not necessarily mean that the solver has found optimal * solution. It only means that the solution process was successful. * * GLP_EBOUND * Unable to start the search, because some double-bounded variables * have incorrect bounds or some integer variables have non-integer * (fractional) bounds. * * GLP_EROOT * Unable to start the search, because optimal basis for initial LP * relaxation is not provided. * * GLP_EFAIL * The search was prematurely terminated due to the solver failure. * * GLP_EMIPGAP * The search was prematurely terminated, because the relative mip * gap tolerance has been reached. * * GLP_ETMLIM * The search was prematurely terminated, because the time limit has * been exceeded. * * GLP_ENOPFS * The MIP problem instance has no primal feasible solution (only if * the MIP presolver is used). * * GLP_ENODFS * LP relaxation of the MIP problem instance has no dual feasible * solution (only if the MIP presolver is used). * * GLP_ESTOP * The search was prematurely terminated by application. */ static int solve_mip(glp_prob *P, const glp_iocp *parm) { /* solve MIP directly without using the preprocessor */ glp_tree *T; int ret; /* optimal basis to LP relaxation must be provided */ if (glp_get_status(P) != GLP_OPT) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: optimal basis to initial LP relaxation" " not provided\n"); ret = GLP_EROOT; goto done; } /* it seems all is ok */ if (parm->msg_lev >= GLP_MSG_ALL) xprintf("Integer optimization begins...\n"); /* create the branch-and-bound tree */ T = ios_create_tree(P, parm); /* solve the problem instance */ ret = ios_driver(T); /* delete the branch-and-bound tree */ ios_delete_tree(T); /* analyze exit code reported by the mip driver */ if (ret == 0) { if (P->mip_stat == GLP_FEAS) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("INTEGER OPTIMAL SOLUTION FOUND\n"); P->mip_stat = GLP_OPT; } else { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO INTEGER FEASIBLE SOLUTION\n"); P->mip_stat = GLP_NOFEAS; } } else if (ret == GLP_EMIPGAP) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("RELATIVE MIP GAP TOLERANCE REACHED; SEARCH TERMINA" "TED\n"); } else if (ret == GLP_ETMLIM) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n"); } else if (ret == GLP_EFAIL) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: cannot solve current LP relaxation\n"); } else if (ret == GLP_ESTOP) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("SEARCH TERMINATED BY APPLICATION\n"); } else xassert(ret != ret); done: return ret; } static int preprocess_and_solve_mip(glp_prob *P, const glp_iocp *parm) { /* solve MIP using the preprocessor */ ENV *env = get_env_ptr(); int term_out = env->term_out; NPP *npp; glp_prob *mip = NULL; glp_bfcp bfcp; glp_smcp smcp; int ret; if (parm->msg_lev >= GLP_MSG_ALL) xprintf("Preprocessing...\n"); /* create preprocessor workspace */ npp = npp_create_wksp(); /* load original problem into the preprocessor workspace */ npp_load_prob(npp, P, GLP_OFF, GLP_MIP, GLP_OFF); /* process MIP prior to applying the branch-and-bound method */ if (!term_out || parm->msg_lev < GLP_MSG_ALL) env->term_out = GLP_OFF; else env->term_out = GLP_ON; ret = npp_integer(npp, parm); env->term_out = term_out; if (ret == 0) ; else if (ret == GLP_ENOPFS) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION\n"); } else if (ret == GLP_ENODFS) { if (parm->msg_lev >= GLP_MSG_ALL) xprintf("LP RELAXATION HAS NO DUAL FEASIBLE SOLUTION\n"); } else xassert(ret != ret); if (ret != 0) goto done; /* build transformed MIP */ mip = glp_create_prob(); npp_build_prob(npp, mip); /* if the transformed MIP is empty, it has empty solution, which is optimal */ if (mip->m == 0 && mip->n == 0) { mip->mip_stat = GLP_OPT; mip->mip_obj = mip->c0; if (parm->msg_lev >= GLP_MSG_ALL) { xprintf("Objective value = %17.9e\n", mip->mip_obj); xprintf("INTEGER OPTIMAL SOLUTION FOUND BY MIP PREPROCESSOR" "\n"); } goto post; } /* display some statistics */ if (parm->msg_lev >= GLP_MSG_ALL) { int ni = glp_get_num_int(mip); int nb = glp_get_num_bin(mip); char s[50]; xprintf("%d row%s, %d column%s, %d non-zero%s\n", mip->m, mip->m == 1 ? "" : "s", mip->n, mip->n == 1 ? "" : "s", mip->nnz, mip->nnz == 1 ? "" : "s"); if (nb == 0) strcpy(s, "none of"); else if (ni == 1 && nb == 1) strcpy(s, ""); else if (nb == 1) strcpy(s, "one of"); else if (nb == ni) strcpy(s, "all of"); else sprintf(s, "%d of", nb); xprintf("%d integer variable%s, %s which %s binary\n", ni, ni == 1 ? "" : "s", s, nb == 1 ? "is" : "are"); } /* inherit basis factorization control parameters */ glp_get_bfcp(P, &bfcp); glp_set_bfcp(mip, &bfcp); /* scale the transformed problem */ if (!term_out || parm->msg_lev < GLP_MSG_ALL) env->term_out = GLP_OFF; else env->term_out = GLP_ON; glp_scale_prob(mip, GLP_SF_GM | GLP_SF_EQ | GLP_SF_2N | GLP_SF_SKIP); env->term_out = term_out; /* build advanced initial basis */ if (!term_out || parm->msg_lev < GLP_MSG_ALL) env->term_out = GLP_OFF; else env->term_out = GLP_ON; glp_adv_basis(mip, 0); env->term_out = term_out; /* solve initial LP relaxation */ if (parm->msg_lev >= GLP_MSG_ALL) xprintf("Solving LP relaxation...\n"); glp_init_smcp(&smcp); smcp.msg_lev = parm->msg_lev; mip->it_cnt = P->it_cnt; ret = glp_simplex(mip, &smcp); P->it_cnt = mip->it_cnt; if (ret != 0) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: cannot solve LP relaxation\n"); ret = GLP_EFAIL; goto done; } /* check status of the basic solution */ ret = glp_get_status(mip); if (ret == GLP_OPT) ret = 0; else if (ret == GLP_NOFEAS) ret = GLP_ENOPFS; else if (ret == GLP_UNBND) ret = GLP_ENODFS; else xassert(ret != ret); if (ret != 0) goto done; /* solve the transformed MIP */ mip->it_cnt = P->it_cnt; ret = solve_mip(mip, parm); P->it_cnt = mip->it_cnt; /* only integer feasible solution can be postprocessed */ if (!(mip->mip_stat == GLP_OPT || mip->mip_stat == GLP_FEAS)) { P->mip_stat = mip->mip_stat; goto done; } /* postprocess solution from the transformed MIP */ post: npp_postprocess(npp, mip); /* the transformed MIP is no longer needed */ glp_delete_prob(mip), mip = NULL; /* store solution to the original problem */ npp_unload_sol(npp, P); done: /* delete the transformed MIP, if it exists */ if (mip != NULL) glp_delete_prob(mip); /* delete preprocessor workspace */ npp_delete_wksp(npp); return ret; } #ifndef HAVE_ALIEN_SOLVER /* 28/V-2010 */ int _glp_intopt1(glp_prob *P, const glp_iocp *parm) { xassert(P == P); xassert(parm == parm); xprintf("glp_intopt: no alien solver is available\n"); return GLP_EFAIL; } #endif int glp_intopt(glp_prob *P, const glp_iocp *parm) { /* solve MIP problem with the branch-and-bound method */ glp_iocp _parm; int i, j, ret; /* check problem object */ if (P == NULL || P->magic != GLP_PROB_MAGIC) xerror("glp_intopt: P = %p; invalid problem object\n", P); if (P->tree != NULL) xerror("glp_intopt: operation not allowed\n"); /* check control parameters */ if (parm == NULL) parm = &_parm, glp_init_iocp((glp_iocp *)parm); if (!(parm->msg_lev == GLP_MSG_OFF || parm->msg_lev == GLP_MSG_ERR || parm->msg_lev == GLP_MSG_ON || parm->msg_lev == GLP_MSG_ALL || parm->msg_lev == GLP_MSG_DBG)) xerror("glp_intopt: msg_lev = %d; invalid parameter\n", parm->msg_lev); if (!(parm->br_tech == GLP_BR_FFV || parm->br_tech == GLP_BR_LFV || parm->br_tech == GLP_BR_MFV || parm->br_tech == GLP_BR_DTH || parm->br_tech == GLP_BR_PCH)) xerror("glp_intopt: br_tech = %d; invalid parameter\n", parm->br_tech); if (!(parm->bt_tech == GLP_BT_DFS || parm->bt_tech == GLP_BT_BFS || parm->bt_tech == GLP_BT_BLB || parm->bt_tech == GLP_BT_BPH)) xerror("glp_intopt: bt_tech = %d; invalid parameter\n", parm->bt_tech); if (!(0.0 < parm->tol_int && parm->tol_int < 1.0)) xerror("glp_intopt: tol_int = %g; invalid parameter\n", parm->tol_int); if (!(0.0 < parm->tol_obj && parm->tol_obj < 1.0)) xerror("glp_intopt: tol_obj = %g; invalid parameter\n", parm->tol_obj); if (parm->tm_lim < 0) xerror("glp_intopt: tm_lim = %d; invalid parameter\n", parm->tm_lim); if (parm->out_frq < 0) xerror("glp_intopt: out_frq = %d; invalid parameter\n", parm->out_frq); if (parm->out_dly < 0) xerror("glp_intopt: out_dly = %d; invalid parameter\n", parm->out_dly); if (!(0 <= parm->cb_size && parm->cb_size <= 256)) xerror("glp_intopt: cb_size = %d; invalid parameter\n", parm->cb_size); if (!(parm->pp_tech == GLP_PP_NONE || parm->pp_tech == GLP_PP_ROOT || parm->pp_tech == GLP_PP_ALL)) xerror("glp_intopt: pp_tech = %d; invalid parameter\n", parm->pp_tech); if (parm->mip_gap < 0.0) xerror("glp_intopt: mip_gap = %g; invalid parameter\n", parm->mip_gap); if (!(parm->mir_cuts == GLP_ON || parm->mir_cuts == GLP_OFF)) xerror("glp_intopt: mir_cuts = %d; invalid parameter\n", parm->mir_cuts); if (!(parm->gmi_cuts == GLP_ON || parm->gmi_cuts == GLP_OFF)) xerror("glp_intopt: gmi_cuts = %d; invalid parameter\n", parm->gmi_cuts); if (!(parm->cov_cuts == GLP_ON || parm->cov_cuts == GLP_OFF)) xerror("glp_intopt: cov_cuts = %d; invalid parameter\n", parm->cov_cuts); if (!(parm->clq_cuts == GLP_ON || parm->clq_cuts == GLP_OFF)) xerror("glp_intopt: clq_cuts = %d; invalid parameter\n", parm->clq_cuts); if (!(parm->presolve == GLP_ON || parm->presolve == GLP_OFF)) xerror("glp_intopt: presolve = %d; invalid parameter\n", parm->presolve); if (!(parm->binarize == GLP_ON || parm->binarize == GLP_OFF)) xerror("glp_intopt: binarize = %d; invalid parameter\n", parm->binarize); if (!(parm->fp_heur == GLP_ON || parm->fp_heur == GLP_OFF)) xerror("glp_intopt: fp_heur = %d; invalid parameter\n", parm->fp_heur); #if 1 /* 28/V-2010 */ if (!(parm->alien == GLP_ON || parm->alien == GLP_OFF)) xerror("glp_intopt: alien = %d; invalid parameter\n", parm->alien); #endif /* integer solution is currently undefined */ P->mip_stat = GLP_UNDEF; P->mip_obj = 0.0; /* check bounds of double-bounded variables */ for (i = 1; i <= P->m; i++) { GLPROW *row = P->row[i]; if (row->type == GLP_DB && row->lb >= row->ub) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: row %d: lb = %g, ub = %g; incorrect" " bounds\n", i, row->lb, row->ub); ret = GLP_EBOUND; goto done; } } for (j = 1; j <= P->n; j++) { GLPCOL *col = P->col[j]; if (col->type == GLP_DB && col->lb >= col->ub) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: column %d: lb = %g, ub = %g; incorr" "ect bounds\n", j, col->lb, col->ub); ret = GLP_EBOUND; goto done; } } /* bounds of all integer variables must be integral */ for (j = 1; j <= P->n; j++) { GLPCOL *col = P->col[j]; if (col->kind != GLP_IV) continue; if (col->type == GLP_LO || col->type == GLP_DB) { if (col->lb != floor(col->lb)) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: integer column %d has non-intege" "r lower bound %g\n", j, col->lb); ret = GLP_EBOUND; goto done; } } if (col->type == GLP_UP || col->type == GLP_DB) { if (col->ub != floor(col->ub)) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: integer column %d has non-intege" "r upper bound %g\n", j, col->ub); ret = GLP_EBOUND; goto done; } } if (col->type == GLP_FX) { if (col->lb != floor(col->lb)) { if (parm->msg_lev >= GLP_MSG_ERR) xprintf("glp_intopt: integer column %d has non-intege" "r fixed value %g\n", j, col->lb); ret = GLP_EBOUND; goto done; } } } /* solve MIP problem */ if (parm->msg_lev >= GLP_MSG_ALL) { int ni = glp_get_num_int(P); int nb = glp_get_num_bin(P); char s[50]; xprintf("GLPK Integer Optimizer, v%s\n", glp_version()); xprintf("%d row%s, %d column%s, %d non-zero%s\n", P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", P->nnz, P->nnz == 1 ? "" : "s"); if (nb == 0) strcpy(s, "none of"); else if (ni == 1 && nb == 1) strcpy(s, ""); else if (nb == 1) strcpy(s, "one of"); else if (nb == ni) strcpy(s, "all of"); else sprintf(s, "%d of", nb); xprintf("%d integer variable%s, %s which %s binary\n", ni, ni == 1 ? "" : "s", s, nb == 1 ? "is" : "are"); } #if 1 /* 28/V-2010 */ if (parm->alien) { /* use alien integer optimizer */ ret = _glp_intopt1(P, parm); goto done; } #endif if (!parm->presolve) ret = solve_mip(P, parm); else ret = preprocess_and_solve_mip(P, parm); done: /* return to the application program */ return ret; } /*********************************************************************** * NAME * * glp_init_iocp - initialize integer optimizer control parameters * * SYNOPSIS * * void glp_init_iocp(glp_iocp *parm); * * DESCRIPTION * * The routine glp_init_iocp initializes control parameters, which are * used by the integer optimizer, with default values. * * Default values of the control parameters are stored in a glp_iocp * structure, which the parameter parm points to. */ void glp_init_iocp(glp_iocp *parm) { parm->msg_lev = GLP_MSG_ALL; parm->br_tech = GLP_BR_DTH; parm->bt_tech = GLP_BT_BLB; parm->tol_int = 1e-5; parm->tol_obj = 1e-7; parm->tm_lim = INT_MAX; parm->out_frq = 5000; parm->out_dly = 10000; parm->cb_func = NULL; parm->cb_info = NULL; parm->cb_size = 0; parm->pp_tech = GLP_PP_ALL; parm->mip_gap = 0.0; parm->mir_cuts = GLP_OFF; parm->gmi_cuts = GLP_OFF; parm->cov_cuts = GLP_OFF; parm->clq_cuts = GLP_OFF; parm->presolve = GLP_OFF; parm->binarize = GLP_OFF; parm->fp_heur = GLP_OFF; #if 1 /* 28/V-2010 */ parm->alien = GLP_OFF; #endif return; } /*********************************************************************** * NAME * * glp_mip_status - retrieve status of MIP solution * * SYNOPSIS * * int glp_mip_status(glp_prob *mip); * * RETURNS * * The routine lpx_mip_status reports the status of MIP solution found * by the branch-and-bound solver as follows: * * GLP_UNDEF - MIP solution is undefined; * GLP_OPT - MIP solution is integer optimal; * GLP_FEAS - MIP solution is integer feasible but its optimality * (or non-optimality) has not been proven, perhaps due to * premature termination of the search; * GLP_NOFEAS - problem has no integer feasible solution (proven by the * solver). */ int glp_mip_status(glp_prob *mip) { int mip_stat = mip->mip_stat; return mip_stat; } /*********************************************************************** * NAME * * glp_mip_obj_val - retrieve objective value (MIP solution) * * SYNOPSIS * * double glp_mip_obj_val(glp_prob *mip); * * RETURNS * * The routine glp_mip_obj_val returns value of the objective function * for MIP solution. */ double glp_mip_obj_val(glp_prob *mip) { /*struct LPXCPS *cps = mip->cps;*/ double z; z = mip->mip_obj; /*if (cps->round && fabs(z) < 1e-9) z = 0.0;*/ return z; } /*********************************************************************** * NAME * * glp_mip_row_val - retrieve row value (MIP solution) * * SYNOPSIS * * double glp_mip_row_val(glp_prob *mip, int i); * * RETURNS * * The routine glp_mip_row_val returns value of the auxiliary variable * associated with i-th row. */ double glp_mip_row_val(glp_prob *mip, int i) { /*struct LPXCPS *cps = mip->cps;*/ double mipx; if (!(1 <= i && i <= mip->m)) xerror("glp_mip_row_val: i = %d; row number out of range\n", i) ; mipx = mip->row[i]->mipx; /*if (cps->round && fabs(mipx) < 1e-9) mipx = 0.0;*/ return mipx; } /*********************************************************************** * NAME * * glp_mip_col_val - retrieve column value (MIP solution) * * SYNOPSIS * * double glp_mip_col_val(glp_prob *mip, int j); * * RETURNS * * The routine glp_mip_col_val returns value of the structural variable * associated with j-th column. */ double glp_mip_col_val(glp_prob *mip, int j) { /*struct LPXCPS *cps = mip->cps;*/ double mipx; if (!(1 <= j && j <= mip->n)) xerror("glp_mip_col_val: j = %d; column number out of range\n", j); mipx = mip->col[j]->mipx; /*if (cps->round && fabs(mipx) < 1e-9) mipx = 0.0;*/ return mipx; } /* eof */ igraph/src/glpnpp02.c0000644000176000001440000012623412325527073014167 0ustar ripleyusers/* glpnpp02.c */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, * Moscow Aviation Institute, Moscow, Russia. All rights reserved. * E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK 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 General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "glpnpp.h" /*********************************************************************** * NAME * * npp_free_row - process free (unbounded) row * * SYNOPSIS * * #include "glpnpp.h" * void npp_free_row(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_free_row processes row p, which is free (i.e. has * no finite bounds): * * -inf < sum a[p,j] x[j] < +inf. (1) * j * * PROBLEM TRANSFORMATION * * Constraint (1) cannot be active, so it is redundant and can be * removed from the original problem. * * Removing row p leads to removing a column of multiplier pi[p] for * this row in the dual system. Since row p has no bounds, pi[p] = 0, * so removing the column does not affect the dual solution. * * RECOVERING BASIC SOLUTION * * In solution to the original problem row p is inactive constraint, * so it is assigned status GLP_BS, and multiplier pi[p] is assigned * zero value. * * RECOVERING INTERIOR-POINT SOLUTION * * In solution to the original problem row p is inactive constraint, * so its multiplier pi[p] is assigned zero value. * * RECOVERING MIP SOLUTION * * None needed. */ struct free_row { /* free (unbounded) row */ int p; /* row reference number */ }; static int rcv_free_row(NPP *npp, void *info); void npp_free_row(NPP *npp, NPPROW *p) { /* process free (unbounded) row */ struct free_row *info; /* the row must be free */ xassert(p->lb == -DBL_MAX && p->ub == +DBL_MAX); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_free_row, sizeof(struct free_row)); info->p = p->i; /* remove the row from the problem */ npp_del_row(npp, p); return; } static int rcv_free_row(NPP *npp, void *_info) { /* recover free (unbounded) row */ struct free_row *info = _info; if (npp->sol == GLP_SOL) npp->r_stat[info->p] = GLP_BS; if (npp->sol != GLP_MIP) npp->r_pi[info->p] = 0.0; return 0; } /*********************************************************************** * NAME * * npp_geq_row - process row of 'not less than' type * * SYNOPSIS * * #include "glpnpp.h" * void npp_geq_row(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_geq_row processes row p, which is 'not less than' * inequality constraint: * * L[p] <= sum a[p,j] x[j] (<= U[p]), (1) * j * * where L[p] < U[p], and upper bound may not exist (U[p] = +oo). * * PROBLEM TRANSFORMATION * * Constraint (1) can be replaced by equality constraint: * * sum a[p,j] x[j] - s = L[p], (2) * j * * where * * 0 <= s (<= U[p] - L[p]) (3) * * is a non-negative surplus variable. * * Since in the primal system there appears column s having the only * non-zero coefficient in row p, in the dual system there appears a * new row: * * (-1) pi[p] + lambda = 0, (4) * * where (-1) is coefficient of column s in row p, pi[p] is multiplier * of row p, lambda is multiplier of column q, 0 is coefficient of * column s in the objective row. * * RECOVERING BASIC SOLUTION * * Status of row p in solution to the original problem is determined * by its status and status of column q in solution to the transformed * problem as follows: * * +--------------------------------------+------------------+ * | Transformed problem | Original problem | * +-----------------+--------------------+------------------+ * | Status of row p | Status of column s | Status of row p | * +-----------------+--------------------+------------------+ * | GLP_BS | GLP_BS | N/A | * | GLP_BS | GLP_NL | GLP_BS | * | GLP_BS | GLP_NU | GLP_BS | * | GLP_NS | GLP_BS | GLP_BS | * | GLP_NS | GLP_NL | GLP_NL | * | GLP_NS | GLP_NU | GLP_NU | * +-----------------+--------------------+------------------+ * * Value of row multiplier pi[p] in solution to the original problem * is the same as in solution to the transformed problem. * * 1. In solution to the transformed problem row p and column q cannot * be basic at the same time; otherwise the basis matrix would have * two linear dependent columns: unity column of auxiliary variable * of row p and unity column of variable s. * * 2. Though in the transformed problem row p is equality constraint, * it may be basic due to primal degenerate solution. * * RECOVERING INTERIOR-POINT SOLUTION * * Value of row multiplier pi[p] in solution to the original problem * is the same as in solution to the transformed problem. * * RECOVERING MIP SOLUTION * * None needed. */ struct ineq_row { /* inequality constraint row */ int p; /* row reference number */ int s; /* column reference number for slack/surplus variable */ }; static int rcv_geq_row(NPP *npp, void *info); void npp_geq_row(NPP *npp, NPPROW *p) { /* process row of 'not less than' type */ struct ineq_row *info; NPPCOL *s; /* the row must have lower bound */ xassert(p->lb != -DBL_MAX); xassert(p->lb < p->ub); /* create column for surplus variable */ s = npp_add_col(npp); s->lb = 0.0; s->ub = (p->ub == +DBL_MAX ? +DBL_MAX : p->ub - p->lb); /* and add it to the transformed problem */ npp_add_aij(npp, p, s, -1.0); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_geq_row, sizeof(struct ineq_row)); info->p = p->i; info->s = s->j; /* replace the row by equality constraint */ p->ub = p->lb; return; } static int rcv_geq_row(NPP *npp, void *_info) { /* recover row of 'not less than' type */ struct ineq_row *info = _info; if (npp->sol == GLP_SOL) { if (npp->r_stat[info->p] == GLP_BS) { if (npp->c_stat[info->s] == GLP_BS) { npp_error(); return 1; } else if (npp->c_stat[info->s] == GLP_NL || npp->c_stat[info->s] == GLP_NU) npp->r_stat[info->p] = GLP_BS; else { npp_error(); return 1; } } else if (npp->r_stat[info->p] == GLP_NS) { if (npp->c_stat[info->s] == GLP_BS) npp->r_stat[info->p] = GLP_BS; else if (npp->c_stat[info->s] == GLP_NL) npp->r_stat[info->p] = GLP_NL; else if (npp->c_stat[info->s] == GLP_NU) npp->r_stat[info->p] = GLP_NU; else { npp_error(); return 1; } } else { npp_error(); return 1; } } return 0; } /*********************************************************************** * NAME * * npp_leq_row - process row of 'not greater than' type * * SYNOPSIS * * #include "glpnpp.h" * void npp_leq_row(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_leq_row processes row p, which is 'not greater than' * inequality constraint: * * (L[p] <=) sum a[p,j] x[j] <= U[p], (1) * j * * where L[p] < U[p], and lower bound may not exist (L[p] = +oo). * * PROBLEM TRANSFORMATION * * Constraint (1) can be replaced by equality constraint: * * sum a[p,j] x[j] + s = L[p], (2) * j * * where * * 0 <= s (<= U[p] - L[p]) (3) * * is a non-negative slack variable. * * Since in the primal system there appears column s having the only * non-zero coefficient in row p, in the dual system there appears a * new row: * * (+1) pi[p] + lambda = 0, (4) * * where (+1) is coefficient of column s in row p, pi[p] is multiplier * of row p, lambda is multiplier of column q, 0 is coefficient of * column s in the objective row. * * RECOVERING BASIC SOLUTION * * Status of row p in solution to the original problem is determined * by its status and status of column q in solution to the transformed * problem as follows: * * +--------------------------------------+------------------+ * | Transformed problem | Original problem | * +-----------------+--------------------+------------------+ * | Status of row p | Status of column s | Status of row p | * +-----------------+--------------------+------------------+ * | GLP_BS | GLP_BS | N/A | * | GLP_BS | GLP_NL | GLP_BS | * | GLP_BS | GLP_NU | GLP_BS | * | GLP_NS | GLP_BS | GLP_BS | * | GLP_NS | GLP_NL | GLP_NU | * | GLP_NS | GLP_NU | GLP_NL | * +-----------------+--------------------+------------------+ * * Value of row multiplier pi[p] in solution to the original problem * is the same as in solution to the transformed problem. * * 1. In solution to the transformed problem row p and column q cannot * be basic at the same time; otherwise the basis matrix would have * two linear dependent columns: unity column of auxiliary variable * of row p and unity column of variable s. * * 2. Though in the transformed problem row p is equality constraint, * it may be basic due to primal degeneracy. * * RECOVERING INTERIOR-POINT SOLUTION * * Value of row multiplier pi[p] in solution to the original problem * is the same as in solution to the transformed problem. * * RECOVERING MIP SOLUTION * * None needed. */ static int rcv_leq_row(NPP *npp, void *info); void npp_leq_row(NPP *npp, NPPROW *p) { /* process row of 'not greater than' type */ struct ineq_row *info; NPPCOL *s; /* the row must have upper bound */ xassert(p->ub != +DBL_MAX); xassert(p->lb < p->ub); /* create column for slack variable */ s = npp_add_col(npp); s->lb = 0.0; s->ub = (p->lb == -DBL_MAX ? +DBL_MAX : p->ub - p->lb); /* and add it to the transformed problem */ npp_add_aij(npp, p, s, +1.0); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_leq_row, sizeof(struct ineq_row)); info->p = p->i; info->s = s->j; /* replace the row by equality constraint */ p->lb = p->ub; return; } static int rcv_leq_row(NPP *npp, void *_info) { /* recover row of 'not greater than' type */ struct ineq_row *info = _info; if (npp->sol == GLP_SOL) { if (npp->r_stat[info->p] == GLP_BS) { if (npp->c_stat[info->s] == GLP_BS) { npp_error(); return 1; } else if (npp->c_stat[info->s] == GLP_NL || npp->c_stat[info->s] == GLP_NU) npp->r_stat[info->p] = GLP_BS; else { npp_error(); return 1; } } else if (npp->r_stat[info->p] == GLP_NS) { if (npp->c_stat[info->s] == GLP_BS) npp->r_stat[info->p] = GLP_BS; else if (npp->c_stat[info->s] == GLP_NL) npp->r_stat[info->p] = GLP_NU; else if (npp->c_stat[info->s] == GLP_NU) npp->r_stat[info->p] = GLP_NL; else { npp_error(); return 1; } } else { npp_error(); return 1; } } return 0; } /*********************************************************************** * NAME * * npp_free_col - process free (unbounded) column * * SYNOPSIS * * #include "glpnpp.h" * void npp_free_col(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_free_col processes column q, which is free (i.e. has * no finite bounds): * * -oo < x[q] < +oo. (1) * * PROBLEM TRANSFORMATION * * Free (unbounded) variable can be replaced by the difference of two * non-negative variables: * * x[q] = s' - s'', s', s'' >= 0. (2) * * Assuming that in the transformed problem x[q] becomes s', * transformation (2) causes new column s'' to appear, which differs * from column s' only in the sign of coefficients in constraint and * objective rows. Thus, if in the dual system the following row * corresponds to column s': * * sum a[i,q] pi[i] + lambda' = c[q], (3) * i * * the row which corresponds to column s'' is the following: * * sum (-a[i,q]) pi[i] + lambda'' = -c[q]. (4) * i * * Then from (3) and (4) it follows that: * * lambda' + lambda'' = 0 => lambda' = lmabda'' = 0, (5) * * where lambda' and lambda'' are multipliers for columns s' and s'', * resp. * * RECOVERING BASIC SOLUTION * * With respect to (5) status of column q in solution to the original * problem is determined by statuses of columns s' and s'' in solution * to the transformed problem as follows: * * +--------------------------------------+------------------+ * | Transformed problem | Original problem | * +------------------+-------------------+------------------+ * | Status of col s' | Status of col s'' | Status of col q | * +------------------+-------------------+------------------+ * | GLP_BS | GLP_BS | N/A | * | GLP_BS | GLP_NL | GLP_BS | * | GLP_NL | GLP_BS | GLP_BS | * | GLP_NL | GLP_NL | GLP_NF | * +------------------+-------------------+------------------+ * * Value of column q is computed with formula (2). * * 1. In solution to the transformed problem columns s' and s'' cannot * be basic at the same time, because they differ only in the sign, * hence, are linear dependent. * * 2. Though column q is free, it can be non-basic due to dual * degeneracy. * * 3. If column q is integral, columns s' and s'' are also integral. * * RECOVERING INTERIOR-POINT SOLUTION * * Value of column q is computed with formula (2). * * RECOVERING MIP SOLUTION * * Value of column q is computed with formula (2). */ struct free_col { /* free (unbounded) column */ int q; /* column reference number for variables x[q] and s' */ int s; /* column reference number for variable s'' */ }; static int rcv_free_col(NPP *npp, void *info); void npp_free_col(NPP *npp, NPPCOL *q) { /* process free (unbounded) column */ struct free_col *info; NPPCOL *s; NPPAIJ *aij; /* the column must be free */ xassert(q->lb == -DBL_MAX && q->ub == +DBL_MAX); /* variable x[q] becomes s' */ q->lb = 0.0, q->ub = +DBL_MAX; /* create variable s'' */ s = npp_add_col(npp); s->is_int = q->is_int; s->lb = 0.0, s->ub = +DBL_MAX; /* duplicate objective coefficient */ s->coef = -q->coef; /* duplicate column of the constraint matrix */ for (aij = q->ptr; aij != NULL; aij = aij->c_next) npp_add_aij(npp, aij->row, s, -aij->val); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_free_col, sizeof(struct free_col)); info->q = q->j; info->s = s->j; return; } static int rcv_free_col(NPP *npp, void *_info) { /* recover free (unbounded) column */ struct free_col *info = _info; if (npp->sol == GLP_SOL) { if (npp->c_stat[info->q] == GLP_BS) { if (npp->c_stat[info->s] == GLP_BS) { npp_error(); return 1; } else if (npp->c_stat[info->s] == GLP_NL) npp->c_stat[info->q] = GLP_BS; else { npp_error(); return -1; } } else if (npp->c_stat[info->q] == GLP_NL) { if (npp->c_stat[info->s] == GLP_BS) npp->c_stat[info->q] = GLP_BS; else if (npp->c_stat[info->s] == GLP_NL) npp->c_stat[info->q] = GLP_NF; else { npp_error(); return -1; } } else { npp_error(); return -1; } } /* compute value of x[q] with formula (2) */ npp->c_value[info->q] -= npp->c_value[info->s]; return 0; } /*********************************************************************** * NAME * * npp_lbnd_col - process column with (non-zero) lower bound * * SYNOPSIS * * #include "glpnpp.h" * void npp_lbnd_col(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_lbnd_col processes column q, which has (non-zero) * lower bound: * * l[q] <= x[q] (<= u[q]), (1) * * where l[q] < u[q], and upper bound may not exist (u[q] = +oo). * * PROBLEM TRANSFORMATION * * Column q can be replaced as follows: * * x[q] = l[q] + s, (2) * * where * * 0 <= s (<= u[q] - l[q]) (3) * * is a non-negative variable. * * Substituting x[q] from (2) into the objective row, we have: * * z = sum c[j] x[j] + c0 = * j * * = sum c[j] x[j] + c[q] x[q] + c0 = * j!=q * * = sum c[j] x[j] + c[q] (l[q] + s) + c0 = * j!=q * * = sum c[j] x[j] + c[q] s + c~0, * * where * * c~0 = c0 + c[q] l[q] (4) * * is the constant term of the objective in the transformed problem. * Similarly, substituting x[q] into constraint row i, we have: * * L[i] <= sum a[i,j] x[j] <= U[i] ==> * j * * L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> * j!=q * * L[i] <= sum a[i,j] x[j] + a[i,q] (l[q] + s) <= U[i] ==> * j!=q * * L~[i] <= sum a[i,j] x[j] + a[i,q] s <= U~[i], * j!=q * * where * * L~[i] = L[i] - a[i,q] l[q], U~[i] = U[i] - a[i,q] l[q] (5) * * are lower and upper bounds of row i in the transformed problem, * resp. * * Transformation (2) does not affect the dual system. * * RECOVERING BASIC SOLUTION * * Status of column q in solution to the original problem is the same * as in solution to the transformed problem (GLP_BS, GLP_NL or GLP_NU). * Value of column q is computed with formula (2). * * RECOVERING INTERIOR-POINT SOLUTION * * Value of column q is computed with formula (2). * * RECOVERING MIP SOLUTION * * Value of column q is computed with formula (2). */ struct bnd_col { /* bounded column */ int q; /* column reference number for variables x[q] and s */ double bnd; /* lower/upper bound l[q] or u[q] */ }; static int rcv_lbnd_col(NPP *npp, void *info); void npp_lbnd_col(NPP *npp, NPPCOL *q) { /* process column with (non-zero) lower bound */ struct bnd_col *info; NPPROW *i; NPPAIJ *aij; /* the column must have non-zero lower bound */ xassert(q->lb != 0.0); xassert(q->lb != -DBL_MAX); xassert(q->lb < q->ub); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_lbnd_col, sizeof(struct bnd_col)); info->q = q->j; info->bnd = q->lb; /* substitute x[q] into objective row */ npp->c0 += q->coef * q->lb; /* substitute x[q] into constraint rows */ for (aij = q->ptr; aij != NULL; aij = aij->c_next) { i = aij->row; if (i->lb == i->ub) i->ub = (i->lb -= aij->val * q->lb); else { if (i->lb != -DBL_MAX) i->lb -= aij->val * q->lb; if (i->ub != +DBL_MAX) i->ub -= aij->val * q->lb; } } /* column x[q] becomes column s */ if (q->ub != +DBL_MAX) q->ub -= q->lb; q->lb = 0.0; return; } static int rcv_lbnd_col(NPP *npp, void *_info) { /* recover column with (non-zero) lower bound */ struct bnd_col *info = _info; if (npp->sol == GLP_SOL) { if (npp->c_stat[info->q] == GLP_BS || npp->c_stat[info->q] == GLP_NL || npp->c_stat[info->q] == GLP_NU) npp->c_stat[info->q] = npp->c_stat[info->q]; else { npp_error(); return 1; } } /* compute value of x[q] with formula (2) */ npp->c_value[info->q] = info->bnd + npp->c_value[info->q]; return 0; } /*********************************************************************** * NAME * * npp_ubnd_col - process column with upper bound * * SYNOPSIS * * #include "glpnpp.h" * void npp_ubnd_col(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_ubnd_col processes column q, which has upper bound: * * (l[q] <=) x[q] <= u[q], (1) * * where l[q] < u[q], and lower bound may not exist (l[q] = -oo). * * PROBLEM TRANSFORMATION * * Column q can be replaced as follows: * * x[q] = u[q] - s, (2) * * where * * 0 <= s (<= u[q] - l[q]) (3) * * is a non-negative variable. * * Substituting x[q] from (2) into the objective row, we have: * * z = sum c[j] x[j] + c0 = * j * * = sum c[j] x[j] + c[q] x[q] + c0 = * j!=q * * = sum c[j] x[j] + c[q] (u[q] - s) + c0 = * j!=q * * = sum c[j] x[j] - c[q] s + c~0, * * where * * c~0 = c0 + c[q] u[q] (4) * * is the constant term of the objective in the transformed problem. * Similarly, substituting x[q] into constraint row i, we have: * * L[i] <= sum a[i,j] x[j] <= U[i] ==> * j * * L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> * j!=q * * L[i] <= sum a[i,j] x[j] + a[i,q] (u[q] - s) <= U[i] ==> * j!=q * * L~[i] <= sum a[i,j] x[j] - a[i,q] s <= U~[i], * j!=q * * where * * L~[i] = L[i] - a[i,q] u[q], U~[i] = U[i] - a[i,q] u[q] (5) * * are lower and upper bounds of row i in the transformed problem, * resp. * * Note that in the transformed problem coefficients c[q] and a[i,q] * change their sign. Thus, the row of the dual system corresponding to * column q: * * sum a[i,q] pi[i] + lambda[q] = c[q] (6) * i * * in the transformed problem becomes the following: * * sum (-a[i,q]) pi[i] + lambda[s] = -c[q]. (7) * i * * Therefore: * * lambda[q] = - lambda[s], (8) * * where lambda[q] is multiplier for column q, lambda[s] is multiplier * for column s. * * RECOVERING BASIC SOLUTION * * With respect to (8) status of column q in solution to the original * problem is determined by status of column s in solution to the * transformed problem as follows: * * +-----------------------+--------------------+ * | Status of column s | Status of column q | * | (transformed problem) | (original problem) | * +-----------------------+--------------------+ * | GLP_BS | GLP_BS | * | GLP_NL | GLP_NU | * | GLP_NU | GLP_NL | * +-----------------------+--------------------+ * * Value of column q is computed with formula (2). * * RECOVERING INTERIOR-POINT SOLUTION * * Value of column q is computed with formula (2). * * RECOVERING MIP SOLUTION * * Value of column q is computed with formula (2). */ static int rcv_ubnd_col(NPP *npp, void *info); void npp_ubnd_col(NPP *npp, NPPCOL *q) { /* process column with upper bound */ struct bnd_col *info; NPPROW *i; NPPAIJ *aij; /* the column must have upper bound */ xassert(q->ub != +DBL_MAX); xassert(q->lb < q->ub); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_ubnd_col, sizeof(struct bnd_col)); info->q = q->j; info->bnd = q->ub; /* substitute x[q] into objective row */ npp->c0 += q->coef * q->ub; q->coef = -q->coef; /* substitute x[q] into constraint rows */ for (aij = q->ptr; aij != NULL; aij = aij->c_next) { i = aij->row; if (i->lb == i->ub) i->ub = (i->lb -= aij->val * q->ub); else { if (i->lb != -DBL_MAX) i->lb -= aij->val * q->ub; if (i->ub != +DBL_MAX) i->ub -= aij->val * q->ub; } aij->val = -aij->val; } /* column x[q] becomes column s */ if (q->lb != -DBL_MAX) q->ub -= q->lb; else q->ub = +DBL_MAX; q->lb = 0.0; return; } static int rcv_ubnd_col(NPP *npp, void *_info) { /* recover column with upper bound */ struct bnd_col *info = _info; if (npp->sol == GLP_BS) { if (npp->c_stat[info->q] == GLP_BS) npp->c_stat[info->q] = GLP_BS; else if (npp->c_stat[info->q] == GLP_NL) npp->c_stat[info->q] = GLP_NU; else if (npp->c_stat[info->q] == GLP_NU) npp->c_stat[info->q] = GLP_NL; else { npp_error(); return 1; } } /* compute value of x[q] with formula (2) */ npp->c_value[info->q] = info->bnd - npp->c_value[info->q]; return 0; } /*********************************************************************** * NAME * * npp_dbnd_col - process non-negative column with upper bound * * SYNOPSIS * * #include "glpnpp.h" * void npp_dbnd_col(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_dbnd_col processes column q, which is non-negative * and has upper bound: * * 0 <= x[q] <= u[q], (1) * * where u[q] > 0. * * PROBLEM TRANSFORMATION * * Upper bound of column q can be replaced by the following equality * constraint: * * x[q] + s = u[q], (2) * * where s >= 0 is a non-negative complement variable. * * Since in the primal system along with new row (2) there appears a * new column s having the only non-zero coefficient in this row, in * the dual system there appears a new row: * * (+1)pi + lambda[s] = 0, (3) * * where (+1) is coefficient at column s in row (2), pi is multiplier * for row (2), lambda[s] is multiplier for column s, 0 is coefficient * at column s in the objective row. * * RECOVERING BASIC SOLUTION * * Status of column q in solution to the original problem is determined * by its status and status of column s in solution to the transformed * problem as follows: * * +-----------------------------------+------------------+ * | Transformed problem | Original problem | * +-----------------+-----------------+------------------+ * | Status of col q | Status of col s | Status of col q | * +-----------------+-----------------+------------------+ * | GLP_BS | GLP_BS | GLP_BS | * | GLP_BS | GLP_NL | GLP_NU | * | GLP_NL | GLP_BS | GLP_NL | * | GLP_NL | GLP_NL | GLP_NL (*) | * +-----------------+-----------------+------------------+ * * Value of column q in solution to the original problem is the same as * in solution to the transformed problem. * * 1. Formally, in solution to the transformed problem columns q and s * cannot be non-basic at the same time, since the constraint (2) * would be violated. However, if u[q] is close to zero, violation * may be less than a working precision even if both columns q and s * are non-basic. In this degenerate case row (2) can be only basic, * i.e. non-active constraint (otherwise corresponding row of the * basis matrix would be zero). This allows to pivot out auxiliary * variable and pivot in column s, in which case the row becomes * active while column s becomes basic. * * 2. If column q is integral, column s is also integral. * * RECOVERING INTERIOR-POINT SOLUTION * * Value of column q in solution to the original problem is the same as * in solution to the transformed problem. * * RECOVERING MIP SOLUTION * * Value of column q in solution to the original problem is the same as * in solution to the transformed problem. */ struct dbnd_col { /* double-bounded column */ int q; /* column reference number for variable x[q] */ int s; /* column reference number for complement variable s */ }; static int rcv_dbnd_col(NPP *npp, void *info); void npp_dbnd_col(NPP *npp, NPPCOL *q) { /* process non-negative column with upper bound */ struct dbnd_col *info; NPPROW *p; NPPCOL *s; /* the column must be non-negative with upper bound */ xassert(q->lb == 0.0); xassert(q->ub > 0.0); xassert(q->ub != +DBL_MAX); /* create variable s */ s = npp_add_col(npp); s->is_int = q->is_int; s->lb = 0.0, s->ub = +DBL_MAX; /* create equality constraint (2) */ p = npp_add_row(npp); p->lb = p->ub = q->ub; npp_add_aij(npp, p, q, +1.0); npp_add_aij(npp, p, s, +1.0); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_dbnd_col, sizeof(struct dbnd_col)); info->q = q->j; info->s = s->j; /* remove upper bound of x[q] */ q->ub = +DBL_MAX; return; } static int rcv_dbnd_col(NPP *npp, void *_info) { /* recover non-negative column with upper bound */ struct dbnd_col *info = _info; if (npp->sol == GLP_BS) { if (npp->c_stat[info->q] == GLP_BS) { if (npp->c_stat[info->s] == GLP_BS) npp->c_stat[info->q] = GLP_BS; else if (npp->c_stat[info->s] == GLP_NL) npp->c_stat[info->q] = GLP_NU; else { npp_error(); return 1; } } else if (npp->c_stat[info->q] == GLP_NL) { if (npp->c_stat[info->s] == GLP_BS || npp->c_stat[info->s] == GLP_NL) npp->c_stat[info->q] = GLP_NL; else { npp_error(); return 1; } } else { npp_error(); return 1; } } return 0; } /*********************************************************************** * NAME * * npp_fixed_col - process fixed column * * SYNOPSIS * * #include "glpnpp.h" * void npp_fixed_col(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_fixed_col processes column q, which is fixed: * * x[q] = s[q], (1) * * where s[q] is a fixed column value. * * PROBLEM TRANSFORMATION * * The value of a fixed column can be substituted into the objective * and constraint rows that allows removing the column from the problem. * * Substituting x[q] = s[q] into the objective row, we have: * * z = sum c[j] x[j] + c0 = * j * * = sum c[j] x[j] + c[q] x[q] + c0 = * j!=q * * = sum c[j] x[j] + c[q] s[q] + c0 = * j!=q * * = sum c[j] x[j] + c~0, * j!=q * * where * * c~0 = c0 + c[q] s[q] (2) * * is the constant term of the objective in the transformed problem. * Similarly, substituting x[q] = s[q] into constraint row i, we have: * * L[i] <= sum a[i,j] x[j] <= U[i] ==> * j * * L[i] <= sum a[i,j] x[j] + a[i,q] x[q] <= U[i] ==> * j!=q * * L[i] <= sum a[i,j] x[j] + a[i,q] s[q] <= U[i] ==> * j!=q * * L~[i] <= sum a[i,j] x[j] + a[i,q] s <= U~[i], * j!=q * * where * * L~[i] = L[i] - a[i,q] s[q], U~[i] = U[i] - a[i,q] s[q] (3) * * are lower and upper bounds of row i in the transformed problem, * resp. * * RECOVERING BASIC SOLUTION * * Column q is assigned status GLP_NS and its value is assigned s[q]. * * RECOVERING INTERIOR-POINT SOLUTION * * Value of column q is assigned s[q]. * * RECOVERING MIP SOLUTION * * Value of column q is assigned s[q]. */ struct fixed_col { /* fixed column */ int q; /* column reference number for variable x[q] */ double s; /* value, at which x[q] is fixed */ }; static int rcv_fixed_col(NPP *npp, void *info); void npp_fixed_col(NPP *npp, NPPCOL *q) { /* process fixed column */ struct fixed_col *info; NPPROW *i; NPPAIJ *aij; /* the column must be fixed */ xassert(q->lb == q->ub); /* create transformation stack entry */ info = npp_push_tse(npp, rcv_fixed_col, sizeof(struct fixed_col)); info->q = q->j; info->s = q->lb; /* substitute x[q] = s[q] into objective row */ npp->c0 += q->coef * q->lb; /* substitute x[q] = s[q] into constraint rows */ for (aij = q->ptr; aij != NULL; aij = aij->c_next) { i = aij->row; if (i->lb == i->ub) i->ub = (i->lb -= aij->val * q->lb); else { if (i->lb != -DBL_MAX) i->lb -= aij->val * q->lb; if (i->ub != +DBL_MAX) i->ub -= aij->val * q->lb; } } /* remove the column from the problem */ npp_del_col(npp, q); return; } static int rcv_fixed_col(NPP *npp, void *_info) { /* recover fixed column */ struct fixed_col *info = _info; if (npp->sol == GLP_SOL) npp->c_stat[info->q] = GLP_NS; npp->c_value[info->q] = info->s; return 0; } /*********************************************************************** * NAME * * npp_make_equality - process row with almost identical bounds * * SYNOPSIS * * #include "glpnpp.h" * int npp_make_equality(NPP *npp, NPPROW *p); * * DESCRIPTION * * The routine npp_make_equality processes row p: * * L[p] <= sum a[p,j] x[j] <= U[p], (1) * j * * where -oo < L[p] < U[p] < +oo, i.e. which is double-sided inequality * constraint. * * RETURNS * * 0 - row bounds have not been changed; * * 1 - row has been replaced by equality constraint. * * PROBLEM TRANSFORMATION * * If bounds of row (1) are very close to each other: * * U[p] - L[p] <= eps, (2) * * where eps is an absolute tolerance for row value, the row can be * replaced by the following almost equivalent equiality constraint: * * sum a[p,j] x[j] = b, (3) * j * * where b = (L[p] + U[p]) / 2. If the right-hand side in (3) happens * to be very close to its nearest integer: * * |b - floor(b + 0.5)| <= eps, (4) * * it is reasonable to use this nearest integer as the right-hand side. * * RECOVERING BASIC SOLUTION * * Status of row p in solution to the original problem is determined * by its status and the sign of its multiplier pi[p] in solution to * the transformed problem as follows: * * +-----------------------+---------+--------------------+ * | Status of row p | Sign of | Status of row p | * | (transformed problem) | pi[p] | (original problem) | * +-----------------------+---------+--------------------+ * | GLP_BS | + / - | GLP_BS | * | GLP_NS | + | GLP_NL | * | GLP_NS | - | GLP_NU | * +-----------------------+---------+--------------------+ * * Value of row multiplier pi[p] in solution to the original problem is * the same as in solution to the transformed problem. * * RECOVERING INTERIOR POINT SOLUTION * * Value of row multiplier pi[p] in solution to the original problem is * the same as in solution to the transformed problem. * * RECOVERING MIP SOLUTION * * None needed. */ struct make_equality { /* row with almost identical bounds */ int p; /* row reference number */ }; static int rcv_make_equality(NPP *npp, void *info); int npp_make_equality(NPP *npp, NPPROW *p) { /* process row with almost identical bounds */ struct make_equality *info; double b, eps, nint; /* the row must be double-sided inequality */ xassert(p->lb != -DBL_MAX); xassert(p->ub != +DBL_MAX); xassert(p->lb < p->ub); /* check row bounds */ eps = 1e-9 + 1e-12 * fabs(p->lb); if (p->ub - p->lb > eps) return 0; /* row bounds are very close to each other */ /* create transformation stack entry */ info = npp_push_tse(npp, rcv_make_equality, sizeof(struct make_equality)); info->p = p->i; /* compute right-hand side */ b = 0.5 * (p->ub + p->lb); nint = floor(b + 0.5); if (fabs(b - nint) <= eps) b = nint; /* replace row p by almost equivalent equality constraint */ p->lb = p->ub = b; return 1; } int rcv_make_equality(NPP *npp, void *_info) { /* recover row with almost identical bounds */ struct make_equality *info = _info; if (npp->sol == GLP_SOL) { if (npp->r_stat[info->p] == GLP_BS) npp->r_stat[info->p] = GLP_BS; else if (npp->r_stat[info->p] == GLP_NS) { if (npp->r_pi[info->p] >= 0.0) npp->r_stat[info->p] = GLP_NL; else npp->r_stat[info->p] = GLP_NU; } else { npp_error(); return 1; } } return 0; } /*********************************************************************** * NAME * * npp_make_fixed - process column with almost identical bounds * * SYNOPSIS * * #include "glpnpp.h" * int npp_make_fixed(NPP *npp, NPPCOL *q); * * DESCRIPTION * * The routine npp_make_fixed processes column q: * * l[q] <= x[q] <= u[q], (1) * * where -oo < l[q] < u[q] < +oo, i.e. which has both lower and upper * bounds. * * RETURNS * * 0 - column bounds have not been changed; * * 1 - column has been fixed. * * PROBLEM TRANSFORMATION * * If bounds of column (1) are very close to each other: * * u[q] - l[q] <= eps, (2) * * where eps is an absolute tolerance for column value, the column can * be fixed: * * x[q] = s[q], (3) * * where s[q] = (l[q] + u[q]) / 2. And if the fixed column value s[q] * happens to be very close to its nearest integer: * * |s[q] - floor(s[q] + 0.5)| <= eps, (4) * * it is reasonable to use this nearest integer as the fixed value. * * RECOVERING BASIC SOLUTION * * In the dual system of the original (as well as transformed) problem * column q corresponds to the following row: * * sum a[i,q] pi[i] + lambda[q] = c[q]. (5) * i * * Since multipliers pi[i] are known for all rows from solution to the * transformed problem, formula (5) allows computing value of multiplier * (reduced cost) for column q: * * lambda[q] = c[q] - sum a[i,q] pi[i]. (6) * i * * Status of column q in solution to the original problem is determined * by its status and the sign of its multiplier lambda[q] in solution to * the transformed problem as follows: * * +-----------------------+-----------+--------------------+ * | Status of column q | Sign of | Status of column q | * | (transformed problem) | lambda[q] | (original problem) | * +-----------------------+-----------+--------------------+ * | GLP_BS | + / - | GLP_BS | * | GLP_NS | + | GLP_NL | * | GLP_NS | - | GLP_NU | * +-----------------------+-----------+--------------------+ * * Value of column q in solution to the original problem is the same as * in solution to the transformed problem. * * RECOVERING INTERIOR POINT SOLUTION * * Value of column q in solution to the original problem is the same as * in solution to the transformed problem. * * RECOVERING MIP SOLUTION * * None needed. */ struct make_fixed { /* column with almost identical bounds */ int q; /* column reference number */ double c; /* objective coefficient at x[q] */ NPPLFE *ptr; /* list of non-zero coefficients a[i,q] */ }; static int rcv_make_fixed(NPP *npp, void *info); int npp_make_fixed(NPP *npp, NPPCOL *q) { /* process column with almost identical bounds */ struct make_fixed *info; NPPAIJ *aij; NPPLFE *lfe; double s, eps, nint; /* the column must be double-bounded */ xassert(q->lb != -DBL_MAX); xassert(q->ub != +DBL_MAX); xassert(q->lb < q->ub); /* check column bounds */ eps = 1e-9 + 1e-12 * fabs(q->lb); if (q->ub - q->lb > eps) return 0; /* column bounds are very close to each other */ /* create transformation stack entry */ info = npp_push_tse(npp, rcv_make_fixed, sizeof(struct make_fixed)); info->q = q->j; info->c = q->coef; info->ptr = NULL; /* save column coefficients a[i,q] (needed for basic solution only) */ if (npp->sol == GLP_SOL) { for (aij = q->ptr; aij != NULL; aij = aij->c_next) { lfe = dmp_get_atom(npp->stack, sizeof(NPPLFE)); lfe->ref = aij->row->i; lfe->val = aij->val; lfe->next = info->ptr; info->ptr = lfe; } } /* compute column fixed value */ s = 0.5 * (q->ub + q->lb); nint = floor(s + 0.5); if (fabs(s - nint) <= eps) s = nint; /* make column q fixed */ q->lb = q->ub = s; return 1; } static int rcv_make_fixed(NPP *npp, void *_info) { /* recover column with almost identical bounds */ struct make_fixed *info = _info; NPPLFE *lfe; double lambda; if (npp->sol == GLP_SOL) { if (npp->c_stat[info->q] == GLP_BS) npp->c_stat[info->q] = GLP_BS; else if (npp->c_stat[info->q] == GLP_NS) { /* compute multiplier for column q with formula (6) */ lambda = info->c; for (lfe = info->ptr; lfe != NULL; lfe = lfe->next) lambda -= lfe->val * npp->r_pi[lfe->ref]; /* assign status to non-basic column */ if (lambda >= 0.0) npp->c_stat[info->q] = GLP_NL; else npp->c_stat[info->q] = GLP_NU; } else { npp_error(); return 1; } } return 0; } /* eof */ igraph/src/dvout.f0000644000176000001440000001031212325527073013656 0ustar ripleyusers*----------------------------------------------------------------------- * Routine: DVOUT * * Purpose: Real vector output routine. * * Usage: CALL DVOUT (LOUT, N, SX, IDIGIT, IFMT) * * Arguments * N - Length of array SX. (Input) * SX - Real array to be printed. (Input) * IFMT - Format to be used in printing array SX. (Input) * IDIGIT - Print up to IABS(IDIGIT) decimal digits per number. (In) * If IDIGIT .LT. 0, printing is done with 72 columns. * If IDIGIT .GT. 0, printing is done with 132 columns. * *----------------------------------------------------------------------- * SUBROUTINE IGRAPHDVOUT( LOUT, N, SX, IDIGIT, IFMT ) * ... * ... SPECIFICATIONS FOR ARGUMENTS * ... * ... SPECIFICATIONS FOR LOCAL VARIABLES * .. Scalar Arguments .. CHARACTER*( * ) IFMT INTEGER IDIGIT, LOUT, N * .. * .. Array Arguments .. DOUBLE PRECISION SX( * ) * .. * .. Local Scalars .. CHARACTER*80 LINE INTEGER I, K1, K2, LLL, NDIGIT * .. * .. Intrinsic Functions .. INTRINSIC LEN, MIN, MIN0 * .. * .. Executable Statements .. * ... * ... FIRST EXECUTABLE STATEMENT * * c$$$ LLL = MIN( LEN( IFMT ), 80 ) c$$$ DO 10 I = 1, LLL c$$$ LINE( I: I ) = '-' c$$$ 10 CONTINUE c$$$* c$$$ DO 20 I = LLL + 1, 80 c$$$ LINE( I: I ) = ' ' c$$$ 20 CONTINUE c$$$* c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) c$$$* c$$$ IF( N.LE.0 ) c$$$ $ RETURN c$$$ NDIGIT = IDIGIT c$$$ IF( IDIGIT.EQ.0 ) c$$$ $ NDIGIT = 4 c$$$* c$$$*======================================================================= c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT c$$$*======================================================================= c$$$* c$$$ IF( IDIGIT.LT.0 ) THEN c$$$ NDIGIT = -IDIGIT c$$$ IF( NDIGIT.LE.4 ) THEN c$$$ DO 30 K1 = 1, N, 5 c$$$ K2 = MIN0( N, K1+4 ) c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 30 CONTINUE c$$$ ELSE IF( NDIGIT.LE.6 ) THEN c$$$ DO 40 K1 = 1, N, 4 c$$$ K2 = MIN0( N, K1+3 ) c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 40 CONTINUE c$$$ ELSE IF( NDIGIT.LE.10 ) THEN c$$$ DO 50 K1 = 1, N, 3 c$$$ K2 = MIN0( N, K1+2 ) c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 50 CONTINUE c$$$ ELSE c$$$ DO 60 K1 = 1, N, 2 c$$$ K2 = MIN0( N, K1+1 ) c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 60 CONTINUE c$$$ END IF c$$$* c$$$*======================================================================= c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT c$$$*======================================================================= c$$$* c$$$ ELSE c$$$ IF( NDIGIT.LE.4 ) THEN c$$$ DO 70 K1 = 1, N, 10 c$$$ K2 = MIN0( N, K1+9 ) c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 70 CONTINUE c$$$ ELSE IF( NDIGIT.LE.6 ) THEN c$$$ DO 80 K1 = 1, N, 8 c$$$ K2 = MIN0( N, K1+7 ) c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 80 CONTINUE c$$$ ELSE IF( NDIGIT.LE.10 ) THEN c$$$ DO 90 K1 = 1, N, 6 c$$$ K2 = MIN0( N, K1+5 ) c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 90 CONTINUE c$$$ ELSE c$$$ DO 100 K1 = 1, N, 5 c$$$ K2 = MIN0( N, K1+4 ) c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) c$$$ 100 CONTINUE c$$$ END IF c$$$ END IF c$$$ WRITE( LOUT, FMT = 9994 ) c$$$ RETURN c$$$ 9998 FORMAT( 1X, I4, ' - ', I4, ':', 1P, 10D12.3 ) c$$$ 9997 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 8D14.5 ) c$$$ 9996 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 6D18.9 ) c$$$ 9995 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 5D24.13 ) c$$$ 9994 FORMAT( 1X, ' ' ) END igraph/src/igraph_types_internal.h0000644000176000001440000003534412325527073017125 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_TYPES_INTERNAL_H #define IGRAPH_TYPES_INTERNAL_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_stack.h" #include "igraph_strvector.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Indexed heap */ /* -------------------------------------------------- */ /** * Indexed heap data type. * \ingroup internal */ typedef struct s_indheap { igraph_real_t* stor_begin; igraph_real_t* stor_end; igraph_real_t* end; int destroy; long int* index_begin; } igraph_indheap_t; #define IGRAPH_INDHEAP_NULL { 0,0,0,0,0 } int igraph_indheap_init (igraph_indheap_t* h, long int size); int igraph_indheap_init_array (igraph_indheap_t *t, igraph_real_t* data, long int len); void igraph_indheap_destroy (igraph_indheap_t* h); int igraph_indheap_clear(igraph_indheap_t *h); igraph_bool_t igraph_indheap_empty (igraph_indheap_t* h); int igraph_indheap_push (igraph_indheap_t* h, igraph_real_t elem); int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_real_t elem); int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem); igraph_real_t igraph_indheap_max (igraph_indheap_t* h); igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h); long int igraph_indheap_size (igraph_indheap_t* h); int igraph_indheap_reserve (igraph_indheap_t* h, long int size); long int igraph_indheap_max_index(igraph_indheap_t *h); void igraph_indheap_i_build(igraph_indheap_t* h, long int head); void igraph_indheap_i_shift_up(igraph_indheap_t* h, long int elem); void igraph_indheap_i_sink(igraph_indheap_t* h, long int head); void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2); /* -------------------------------------------------- */ /* Doubly indexed heap */ /* -------------------------------------------------- */ /* This is a heap containing double elements and two indices, its intended usage is the storage of weighted edges. */ /** * Doubly indexed heap data type. * \ingroup internal */ typedef struct s_indheap_d { igraph_real_t* stor_begin; igraph_real_t* stor_end; igraph_real_t* end; int destroy; long int* index_begin; long int* index2_begin; } igraph_d_indheap_t; #define IGRAPH_D_INDHEAP_NULL { 0,0,0,0,0,0 } int igraph_d_indheap_init (igraph_d_indheap_t* h, long int size); void igraph_d_indheap_destroy (igraph_d_indheap_t* h); igraph_bool_t igraph_d_indheap_empty (igraph_d_indheap_t* h); int igraph_d_indheap_push (igraph_d_indheap_t* h, igraph_real_t elem, long int idx, long int idx2); igraph_real_t igraph_d_indheap_max (igraph_d_indheap_t* h); igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t* h); long int igraph_d_indheap_size (igraph_d_indheap_t* h); int igraph_d_indheap_reserve (igraph_d_indheap_t* h, long int size); void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int *idx2); void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head); void igraph_d_indheap_i_shift_up(igraph_d_indheap_t* h, long int elem); void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head); void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2); /* -------------------------------------------------- */ /* Two-way indexed heap */ /* -------------------------------------------------- */ /* This is a smart indexed heap. In addition to the "normal" indexed heap it allows to access every element through its index in O(1) time. In other words, for this heap the _modify operation is O(1), the normal heap does this in O(n) time.... */ typedef struct igraph_2wheap_t { long int size; igraph_vector_t data; igraph_vector_long_t index; igraph_vector_long_t index2; } igraph_2wheap_t; int igraph_2wheap_init(igraph_2wheap_t *h, long int size); void igraph_2wheap_destroy(igraph_2wheap_t *h); int igraph_2wheap_clear(igraph_2wheap_t *h); int igraph_2wheap_push_with_index(igraph_2wheap_t *h, long int idx, igraph_real_t elem); igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h); long int igraph_2wheap_size(const igraph_2wheap_t *h); long int igraph_2wheap_max_size(const igraph_2wheap_t *h); igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h); long int igraph_2wheap_max_index(const igraph_2wheap_t *h); igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h); igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx); igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx); igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, long int idx); igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h); igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx); int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem); int igraph_2wheap_check(igraph_2wheap_t *h); /** * Trie data type * \ingroup internal */ typedef struct s_igraph_trie_node { igraph_strvector_t strs; igraph_vector_ptr_t children; igraph_vector_t values; } igraph_trie_node_t; typedef struct s_igraph_trie { igraph_strvector_t strs; igraph_vector_ptr_t children; igraph_vector_t values; long int maxvalue; igraph_bool_t storekeys; igraph_strvector_t keys; } igraph_trie_t; #define IGRAPH_TRIE_NULL { IGRAPH_STRVECTOR_NULL, IGRAPH_VECTOR_PTR_NULL, \ IGRAPH_VECTOR_NULL, 0, 0, IGRAPH_STRVECTOR_NULL } #define IGRAPH_TRIE_INIT_FINALLY(tr, sk) \ do { IGRAPH_CHECK(igraph_trie_init(tr, sk)); \ IGRAPH_FINALLY(igraph_trie_destroy, tr); } while (0) int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys); void igraph_trie_destroy(igraph_trie_t *t); int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id); int igraph_trie_check(igraph_trie_t *t, const char *key, long int *id); int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length, long int *id); void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str); int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv); long int igraph_trie_size(igraph_trie_t *t); /** * 2d grid containing points */ typedef struct igraph_2dgrid_t { igraph_matrix_t *coords; igraph_real_t minx, maxx, deltax; igraph_real_t miny, maxy, deltay; long int stepsx, stepsy; igraph_matrix_t startidx; igraph_vector_t next; igraph_vector_t prev; igraph_real_t massx, massy; /* The sum of the coordinates */ long int vertices; /* Number of active vertices */ } igraph_2dgrid_t; int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, igraph_real_t minx, igraph_real_t maxx, igraph_real_t deltax, igraph_real_t miny, igraph_real_t maxy, igraph_real_t deltay); void igraph_2dgrid_destroy(igraph_2dgrid_t *grid); void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem, igraph_real_t xc, igraph_real_t yc); void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem); void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem, igraph_real_t xc, igraph_real_t yc); void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid, igraph_real_t *massx, igraph_real_t *massy); igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, long int elem); igraph_real_t igraph_2dgrid_dist(const igraph_2dgrid_t *grid, long int e1, long int e2); int igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_t *eids, igraph_integer_t vid, igraph_real_t r); typedef struct igraph_2dgrid_iterator_t { long int vid, x, y; long int nei; long int nx[4], ny[4], ncells; } igraph_2dgrid_iterator_t; void igraph_2dgrid_reset(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it); igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it); igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it); /* Another type of grid, each cell is owned by exactly one graph */ typedef struct igraph_i_layout_mergegrid_t { long int *data; long int stepsx, stepsy; igraph_real_t minx, maxx, deltax; igraph_real_t miny, maxy, deltay; } igraph_i_layout_mergegrid_t; int igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, igraph_real_t minx, igraph_real_t maxx, long int stepsx, igraph_real_t miny, igraph_real_t maxy, long int stepsy); void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid); int igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, igraph_real_t x, igraph_real_t y, igraph_real_t r, long int id); long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, igraph_real_t x, igraph_real_t y); long int igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *g, igraph_real_t x, igraph_real_t y, igraph_real_t r); /* string -> string hash table */ typedef struct igraph_hashtable_t { igraph_trie_t keys; igraph_strvector_t elements; igraph_strvector_t defaults; } igraph_hashtable_t; int igraph_hashtable_init(igraph_hashtable_t *ht); void igraph_hashtable_destroy(igraph_hashtable_t *ht); int igraph_hashtable_addset(igraph_hashtable_t *ht, const char *key, const char *def, const char *elem); int igraph_hashtable_addset2(igraph_hashtable_t *ht, const char *key, const char *def, const char *elem, int elemlen); int igraph_hashtable_get(igraph_hashtable_t *ht, const char *key, char **elem); int igraph_hashtable_getkeys(igraph_hashtable_t *ht, const igraph_strvector_t **sv); int igraph_hashtable_reset(igraph_hashtable_t *ht); /* Buckets, needed for the maximum flow algorithm */ typedef struct igraph_buckets_t { igraph_vector_long_t bptr; igraph_vector_long_t buckets; igraph_integer_t max, no; } igraph_buckets_t; int igraph_buckets_init(igraph_buckets_t *b, long int bsize, long int size); void igraph_buckets_destroy(igraph_buckets_t *b); void igraph_buckets_clear(igraph_buckets_t *b); long int igraph_buckets_popmax(igraph_buckets_t *b); long int igraph_buckets_pop(igraph_buckets_t *b, long int bucket); igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b); igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b, long int bucket); void igraph_buckets_add(igraph_buckets_t *b, long int bucket, long int elem); typedef struct igraph_dbuckets_t { igraph_vector_long_t bptr; igraph_vector_long_t next, prev; igraph_integer_t max, no; } igraph_dbuckets_t; int igraph_dbuckets_init(igraph_dbuckets_t *b, long int bsize, long int size); void igraph_dbuckets_destroy(igraph_dbuckets_t *b); void igraph_dbuckets_clear(igraph_dbuckets_t *b); long int igraph_dbuckets_popmax(igraph_dbuckets_t *b); long int igraph_dbuckets_pop(igraph_dbuckets_t *b, long int bucket); igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b); igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b, long int bucket); void igraph_dbuckets_add(igraph_dbuckets_t *b, long int bucket, long int elem); void igraph_dbuckets_delete(igraph_dbuckets_t *b, long int bucket, long int elem); /* Special maximum heap, needed for the minimum cut algorithm */ typedef struct igraph_i_cutheap_t { igraph_vector_t heap; igraph_vector_t index; igraph_vector_t hptr; long int dnodes; } igraph_i_cutheap_t; int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes); void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch); igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch); igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch); igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch); igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch); igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch); int igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add); int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex); /* -------------------------------------------------- */ /* Flexible set */ /* -------------------------------------------------- */ /** * Set containing integer numbers regardless of the order * \ingroup types */ typedef struct s_set { igraph_integer_t* stor_begin; igraph_integer_t* stor_end; igraph_integer_t* end; } igraph_set_t; #define IGRAPH_SET_NULL { 0,0,0 } #define IGRAPH_SET_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_set_init(v, size)); \ IGRAPH_FINALLY(igraph_set_destroy, v); } while (0) int igraph_set_init (igraph_set_t* set, long int size); void igraph_set_destroy (igraph_set_t* set); igraph_bool_t igraph_set_inited (igraph_set_t* set); int igraph_set_reserve (igraph_set_t* set, long int size); igraph_bool_t igraph_set_empty (const igraph_set_t* set); void igraph_set_clear (igraph_set_t* set); long int igraph_set_size (const igraph_set_t* set); int igraph_set_add (igraph_set_t* v, igraph_integer_t e); igraph_bool_t igraph_set_contains (igraph_set_t* set, igraph_integer_t e); igraph_bool_t igraph_set_iterate (igraph_set_t* set, long int* state, igraph_integer_t* element); /* -------------------------------------------------- */ /* Vectorlist, fixed length */ /* -------------------------------------------------- */ typedef struct igraph_fixed_vectorlist_t { igraph_vector_t *vecs; igraph_vector_ptr_t v; long int length; } igraph_fixed_vectorlist_t; void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l); int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, const igraph_vector_t *from, long int size); __END_DECLS #endif igraph/src/igraph_constants.h0000644000176000001440000001244712325527073016100 0ustar ripleyusers/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2009-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IGRAPH_CONSTANTS_H #define IGRAPH_CONSTANTS_H #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif #include "igraph_types.h" #include "igraph_datatype.h" __BEGIN_DECLS /* -------------------------------------------------- */ /* Constants */ /* -------------------------------------------------- */ typedef enum { IGRAPH_UNDIRECTED=0, IGRAPH_DIRECTED=1 } igraph_i_directed_t; typedef enum { IGRAPH_NO_LOOPS=0, IGRAPH_LOOPS=1 } igraph_i_loops_t; typedef enum { IGRAPH_NO_MULTIPLE=0, IGRAPH_MULTIPLE=1 } igraph_i_multiple_t; typedef enum { IGRAPH_ASCENDING=0, IGRAPH_DESCENDING=1 } igraph_order_t; typedef enum { IGRAPH_MINIMUM=0, IGRAPH_MAXIMUM=1 } igraph_optimal_t; typedef enum { IGRAPH_OUT=1, IGRAPH_IN=2, IGRAPH_ALL=3, IGRAPH_TOTAL=3 } igraph_neimode_t; typedef enum { IGRAPH_WEAK=1, IGRAPH_STRONG=2 } igraph_connectedness_t; typedef enum { IGRAPH_RECIPROCITY_DEFAULT=0, IGRAPH_RECIPROCITY_RATIO=1 } igraph_reciprocity_t; typedef enum { IGRAPH_ADJ_DIRECTED=0, IGRAPH_ADJ_UNDIRECTED=1, IGRAPH_ADJ_MAX=1, IGRAPH_ADJ_UPPER, IGRAPH_ADJ_LOWER, IGRAPH_ADJ_MIN, IGRAPH_ADJ_PLUS } igraph_adjacency_t; typedef enum { IGRAPH_STAR_OUT=0, IGRAPH_STAR_IN, IGRAPH_STAR_UNDIRECTED, IGRAPH_STAR_MUTUAL } igraph_star_mode_t; typedef enum { IGRAPH_TREE_OUT=0, IGRAPH_TREE_IN, IGRAPH_TREE_UNDIRECTED } igraph_tree_mode_t; typedef enum { IGRAPH_ERDOS_RENYI_GNP=0, IGRAPH_ERDOS_RENYI_GNM } igraph_erdos_renyi_t; typedef enum { IGRAPH_GET_ADJACENCY_UPPER=0, IGRAPH_GET_ADJACENCY_LOWER, IGRAPH_GET_ADJACENCY_BOTH } igraph_get_adjacency_t; typedef enum { IGRAPH_DEGSEQ_SIMPLE=0, IGRAPH_DEGSEQ_VL, IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE } igraph_degseq_t; typedef enum { IGRAPH_FILEFORMAT_EDGELIST=0, IGRAPH_FILEFORMAT_NCOL, IGRAPH_FILEFORMAT_PAJEK, IGRAPH_FILEFORMAT_LGL, IGRAPH_FILEFORMAT_GRAPHML } igraph_fileformat_type_t; typedef enum { IGRAPH_REWIRING_SIMPLE=0, IGRAPH_REWIRING_SIMPLE_LOOPS } igraph_rewiring_t; typedef enum { IGRAPH_EDGEORDER_ID=0, IGRAPH_EDGEORDER_FROM, IGRAPH_EDGEORDER_TO } igraph_edgeorder_type_t; typedef enum { IGRAPH_TO_DIRECTED_ARBITRARY=0, IGRAPH_TO_DIRECTED_MUTUAL } igraph_to_directed_t; typedef enum { IGRAPH_TO_UNDIRECTED_EACH=0, IGRAPH_TO_UNDIRECTED_COLLAPSE, IGRAPH_TO_UNDIRECTED_MUTUAL} igraph_to_undirected_t; typedef enum { IGRAPH_VCONN_NEI_ERROR=0, IGRAPH_VCONN_NEI_NUMBER_OF_NODES, IGRAPH_VCONN_NEI_IGNORE, IGRAPH_VCONN_NEI_NEGATIVE } igraph_vconn_nei_t; typedef enum { IGRAPH_SPINCOMM_UPDATE_SIMPLE=0, IGRAPH_SPINCOMM_UPDATE_CONFIG } igraph_spincomm_update_t; typedef enum { IGRAPH_DONT_SIMPLIFY=0, IGRAPH_SIMPLIFY } igraph_lazy_adlist_simplify_t; typedef enum { IGRAPH_TRANSITIVITY_NAN=0, IGRAPH_TRANSITIVITY_ZERO } igraph_transitivity_mode_t; typedef enum { IGRAPH_SPINCOMM_IMP_ORIG=0, IGRAPH_SPINCOMM_IMP_NEG } igraph_spinglass_implementation_t; typedef enum { IGRAPH_COMMCMP_VI = 0, IGRAPH_COMMCMP_NMI, IGRAPH_COMMCMP_SPLIT_JOIN, IGRAPH_COMMCMP_RAND, IGRAPH_COMMCMP_ADJUSTED_RAND } igraph_community_comparison_t; typedef enum { IGRAPH_ADD_WEIGHTS_NO = 0, IGRAPH_ADD_WEIGHTS_YES, IGRAPH_ADD_WEIGHTS_IF_PRESENT } igraph_add_weights_t; typedef enum { IGRAPH_BARABASI_BAG = 0, IGRAPH_BARABASI_PSUMTREE, IGRAPH_BARABASI_PSUMTREE_MULTIPLE} igraph_barabasi_algorithm_t; typedef enum { IGRAPH_FAS_EXACT_IP = 0, IGRAPH_FAS_APPROX_EADES } igraph_fas_algorithm_t; typedef enum { IGRAPH_SUBGRAPH_AUTO = 0, IGRAPH_SUBGRAPH_COPY_AND_DELETE, IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH } igraph_subgraph_implementation_t; typedef enum { IGRAPH_IMITATE_AUGMENTED = 0, IGRAPH_IMITATE_BLIND, IGRAPH_IMITATE_CONTRACTED } igraph_imitate_algorithm_t; typedef igraph_real_t igraph_scalar_function_t(const igraph_vector_t *var, const igraph_vector_t *par, void* extra); typedef void igraph_vector_function_t(const igraph_vector_t *var, const igraph_vector_t *par, igraph_vector_t* res, void* extra); __END_DECLS #endif igraph/src/cs_sqr.c0000644000176000001440000001131012325527073014003 0ustar ripleyusers/* * CXSPARSE: a Concise Sparse Matrix package - Extended. * Copyright (c) 2006-2009, Timothy A. Davis. * http://www.cise.ufl.edu/research/sparse/CXSparse * * CXSparse is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * CXSparse 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this Module; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cs.h" /* compute nnz(V) = S->lnz, S->pinv, S->leftmost, S->m2 from A and S->parent */ static CS_INT cs_vcount (const cs *A, css *S) { CS_INT i, k, p, pa, n = A->n, m = A->m, *Ap = A->p, *Ai = A->i, *next, *head, *tail, *nque, *pinv, *leftmost, *w, *parent = S->parent ; S->pinv = pinv = cs_malloc (m+n, sizeof (CS_INT)) ; /* allocate pinv, */ S->leftmost = leftmost = cs_malloc (m, sizeof (CS_INT)) ; /* and leftmost */ w = cs_malloc (m+3*n, sizeof (CS_INT)) ; /* get workspace */ if (!pinv || !w || !leftmost) { cs_free (w) ; /* pinv and leftmost freed later */ return (0) ; /* out of memory */ } next = w ; head = w + m ; tail = w + m + n ; nque = w + m + 2*n ; for (k = 0 ; k < n ; k++) head [k] = -1 ; /* queue k is empty */ for (k = 0 ; k < n ; k++) tail [k] = -1 ; for (k = 0 ; k < n ; k++) nque [k] = 0 ; for (i = 0 ; i < m ; i++) leftmost [i] = -1 ; for (k = n-1 ; k >= 0 ; k--) { for (p = Ap [k] ; p < Ap [k+1] ; p++) { leftmost [Ai [p]] = k ; /* leftmost[i] = min(find(A(i,:)))*/ } } for (i = m-1 ; i >= 0 ; i--) /* scan rows in reverse order */ { pinv [i] = -1 ; /* row i is not yet ordered */ k = leftmost [i] ; if (k == -1) continue ; /* row i is empty */ if (nque [k]++ == 0) tail [k] = i ; /* first row in queue k */ next [i] = head [k] ; /* put i at head of queue k */ head [k] = i ; } S->lnz = 0 ; S->m2 = m ; for (k = 0 ; k < n ; k++) /* find row permutation and nnz(V)*/ { i = head [k] ; /* remove row i from queue k */ S->lnz++ ; /* count V(k,k) as nonzero */ if (i < 0) i = S->m2++ ; /* add a fictitious row */ pinv [i] = k ; /* associate row i with V(:,k) */ if (--nque [k] <= 0) continue ; /* skip if V(k+1:m,k) is empty */ S->lnz += nque [k] ; /* nque [k] is nnz (V(k+1:m,k)) */ if ((pa = parent [k]) != -1) /* move all rows to parent of k */ { if (nque [pa] == 0) tail [pa] = tail [k] ; next [tail [k]] = head [pa] ; head [pa] = next [i] ; nque [pa] += nque [k] ; } } for (i = 0 ; i < m ; i++) if (pinv [i] < 0) pinv [i] = k++ ; cs_free (w) ; return (1) ; } /* symbolic ordering and analysis for QR or LU */ css *cs_sqr (CS_INT order, const cs *A, CS_INT qr) { CS_INT n, k, ok = 1, *post ; css *S ; if (!CS_CSC (A)) return (NULL) ; /* check inputs */ n = A->n ; S = cs_calloc (1, sizeof (css)) ; /* allocate result S */ if (!S) return (NULL) ; /* out of memory */ S->q = cs_amd (order, A) ; /* fill-reducing ordering */ if (order && !S->q) return (cs_sfree (S)) ; if (qr) /* QR symbolic analysis */ { cs *C = order ? cs_permute (A, NULL, S->q, 0) : ((cs *) A) ; S->parent = cs_etree (C, 1) ; /* etree of C'*C, where C=A(:,q) */ post = cs_post (S->parent, n) ; S->cp = cs_counts (C, S->parent, post, 1) ; /* col counts chol(C'*C) */ cs_free (post) ; ok = C && S->parent && S->cp && cs_vcount (C, S) ; if (ok) for (S->unz = 0, k = 0 ; k < n ; k++) S->unz += S->cp [k] ; ok = ok && S->lnz >= 0 && S->unz >= 0 ; /* CS_INT overflow guard */ if (order) cs_spfree (C) ; } else { S->unz = 4*(A->p [n]) + n ; /* for LU factorization only, */ S->lnz = S->unz ; /* guess nnz(L) and nnz(U) */ } return (ok ? S : cs_sfree (S)) ; /* return result S */ } igraph/NAMESPACE0000644000176000001440000003026512325372070013002 0ustar ripleyusers importFrom(stats, as.dendrogram) importFrom(stats, as.hclust) importFrom(stats, median) importFrom(stats, quantile) import(methods) # The igraph interface export(add.vertices, delete.edges, delete.vertices, ecount, neighbors, incident, get.edges, get.edge.ids) export("[.igraph", "[[.igraph", "[<-.igraph") S3method("[", "igraph") S3method("[[", "igraph") S3method("[<-", "igraph") export(edge, edges, vertex, vertices, path, "+.igraph", "-.igraph") S3method("+", "igraph") S3method("-", "igraph") # Attributes export(get.graph.attribute, set.graph.attribute, graph.attributes, get.vertex.attribute, set.vertex.attribute, vertex.attributes, get.edge.attribute, set.edge.attribute, edge.attributes, list.graph.attributes, list.vertex.attributes, list.edge.attributes, remove.graph.attribute, remove.vertex.attribute, remove.edge.attribute, "graph.attributes<-", "vertex.attributes<-", "edge.attributes<-") # iterators export(V, E, "$.igraph.vs", "$.igraph.es", "[.igraph.vs", "[[.igraph.vs", "[.igraph.es", "[[.igraph.es", "[<-.igraph.vs", "[<-.igraph.es", "[[<-.igraph.vs", "[[<-.igraph.es", "$<-.igraph.vs", "$<-.igraph.es", "V<-", "E<-", print.igraph.vs, print.igraph.es, "%--%", "%->%", "%<-%") S3method("$", igraph) S3method("$<-", igraph) S3method("$", igraph.vs) S3method("[", igraph.vs) S3method("[[", igraph.vs) S3method("[<-", igraph.vs) S3method("[[<-", igraph.vs) S3method("$<-", igraph.vs) S3method("$", igraph.es) S3method("[", igraph.es) S3method("[[", igraph.es) S3method("[<-", igraph.es) S3method("[[<-", igraph.es) S3method("$<-", igraph.es) S3method(print, igraph.vs) S3method(print, igraph.es) # basic functions, printing export(is.igraph, is.named, is.weighted, is.bipartite, are.connected, print.igraph, str.igraph, summary.igraph, is.directed, get.edge) S3method("str", "igraph") # structure generators export(graph, graph.adjacency, graph.star, graph.tree, graph.lattice, graph.ring, graph.full, graph.atlas, graph.data.frame, graph.edgelist, graph.extended.chordal.ring, line.graph, graph.de.bruijn, graph.kautz, graph.formula, graph.famous) # games export(erdos.renyi.game, random.graph.game, degree.sequence.game, aging.prefatt.game, aging.barabasi.game, aging.ba.game, growing.random.game, barabasi.game, ba.game, callaway.traits.game, establishment.game, grg.game, preference.game, asymmetric.preference.game, connect.neighborhood, rewire.edges, watts.strogatz.game, lastcit.game, cited.type.game, citing.cited.type.game, bipartite.random.game) # community structure export(edge.betweenness.community, spinglass.community, walktrap.community, edge.betweenness.community.merges, fastgreedy.community, community.to.membership) export(membership, modularity, sizes, algorithm, is.hierarchical, merges, cutat, communities, crossing, plot.communities, compare, compare.communities, showtrace, code.length, asPhylo, dendPlot, create.communities) S3method("print", communities) S3method("modularity", communities) S3method("length", communities) S3method("as.dendrogram", communities) S3method("as.hclust", communities) S3method("asPhylo", communities) S3method("modularity", igraph) S3method("dendPlot", communities) S3method("compare", communities) S3method("compare", numeric) S3method("compare", default) # conversion export(get.adjacency, get.edgelist, as.directed, as.undirected, get.adjlist, get.adjedgelist, igraph.from.graphNEL, igraph.to.graphNEL, get.data.frame) # fitting, other export(power.law.fit, running.mean, igraph.sample, srand) # foreign formats export(read.graph, write.graph, graph.graphdb) # layouts export(layout.auto, layout.random, layout.circle, layout.spring, layout.kamada.kawai, layout.lgl, layout.fruchterman.reingold.grid, layout.sphere, layout.merge, layout.reingold.tilford, layout.norm, piecewise.layout) export(igraph.drl.default, igraph.drl.coarsen, igraph.drl.coarsest, igraph.drl.refine, igraph.drl.final) # structural properties export(subgraph, degree, degree.distribution, diameter, subcomponent, betweenness, bibcoupling, cocitation, shortest.paths, minimum.spanning.tree, get.shortest.paths, average.path.length, transitivity, get.all.shortest.paths, get.diameter, farthest.nodes, constraint, page.rank, reciprocity, rewire, graph.density, neighborhood.size, neighborhood, graph.neighborhood, graph.coreness, topological.sort, girth, is.loop, is.multiple, count.multiple) # plotting export(plot.igraph, tkplot, tkplot.close, tkplot.off, tkplot.fit.to.screen, tkplot.reshape, tkplot.export.postscript, tkplot.getcoords, tkplot.center, tkplot.rotate, tkplot.canvas, tkplot.setcoords, rglplot.igraph, rglplot, autocurve.edges, vertex.shapes, add.vertex.shape, igraph.shape.noclip, igraph.shape.noplot) S3method(rglplot, igraph) # components export(cluster.distribution, is.connected, decompose.graph, no.clusters) # centrality export(evcent, bonpow, alpha.centrality, subgraph.centrality) export(igraph.arpack.default, igraph.eigen.default) # dynamics measurement export(revolver.d, revolver.error.d, revolver.ad, revolver.error.ad, revolver.ade, revolver.error.ade, revolver.e, revolver.error.e, revolver.de, revolver.error.de, revolver.l, revolver.error.l, revolver.dl, revolver.error.dl, revolver.el, revolver.error.el, revolver.r, revolver.error.r, revolver.ar, revolver.error.ar, revolver.di, revolver.error.di, revolver.adi, revolver.error.adi, revolver.il, revolver.error.il, revolver.ir, revolver.error.ir, revolver.air, revolver.error.air) export (revolver.d.d, revolver.p.p) export (evolver.d) # isomorphism, motifs export(graph.isoclass, graph.isomorphic, graph.motifs, graph.motifs.est, graph.isocreate, graph.motifs.no, graph.isomorphic.vf2, graph.subisomorphic.vf2, graph.count.isomorphisms.vf2, graph.count.subisomorphisms.vf2, graph.get.isomorphisms.vf2, graph.get.subisomorphisms.vf2) # operators export(graph.disjoint.union, "%du%", graph.intersection, "%s%", graph.union, "%u%", graph.difference, "%m%", graph.complementer, graph.compose, "%c%") # parameters, config export(igraph.par, igraph.options, getIgraphOpt, igraphtest, igraph.version) # console export(igraph.console, .igraph.progress, .igraph.status) # flows, cuts, etc. export(graph.mincut, vertex.connectivity, edge.connectivity, edge.disjoint.paths, vertex.disjoint.paths, graph.adhesion, graph.cohesion) # cliques export(cliques, largest.cliques, maximal.cliques, clique.number, independent.vertex.sets, largest.independent.vertex.sets, maximal.independent.vertex.sets, independence.number) # cohesive blocking, this is what remains from the old implementation S3method(layout.svd, igraph) export(layout.svd) # cohesive blocking, new style export(blocks, blockGraphs, cohesion, hierarchy, parent, plotHierarchy, exportPajek, maxcohesion) S3method(print, cohesiveBlocks) S3method(summary, cohesiveBlocks) S3method(plot, cohesiveBlocks) S3method(length, cohesiveBlocks) # arpack export(arpack) # tkigraph, demo export(tkigraph,igraphdemo) # HRG S3method(print, igraphHRG) S3method(print, igraphHRGConsensus) S3method("as.dendrogram", igraphHRG) S3method("as.hclust", igraphHRG) S3method("asPhylo", igraphHRG) S3method("dendPlot", igraphHRG) # SCG export(scg) S3method(scg, "igraph") S3method(scg, "matrix") S3method(scg, "Matrix") # nexus export(nexus.get, nexus.list, nexus.info, nexus.search) S3method(print, nexusDatasetInfo) S3method(print, nexusDatasetInfoList) S3method(summary, nexusDatasetInfoList) S3method("[", nexusDatasetInfoList) # Sparse data frame S3method(as.data.frame, igraphSDF) S3method("[", igraphSDF) S3method("[<-", igraphSDF) # convert to igraph export(as.igraph) S3method(as.igraph, "igraphHRG") # Graphlets export(graphlets, graphlets.project) # SIR export(plot.sir, time_bins) S3method(plot, sir) S3method(median, sir) S3method(quantile, sir) S3method(time_bins, sir) ######################## REST IS GENERATED BY stimulus export(graph.empty) export(add.edges) export(vcount) export(graph.full.citation) export(graph.lcf) export(graph.adjlist) export(graph.full.bipartite) export(forest.fire.game) export(interconnected.islands.game) export(static.fitness.game) export(static.power.law.game) export(k.regular.game) export(sbm.game) export(closeness) export(closeness.estimate) export(betweenness.estimate) export(edge.betweenness) export(edge.betweenness.estimate) export(page.rank.old) export(page.rank) export(induced.subgraph) export(subgraph.edges) export(path.length.hist) export(simplify) export(is.dag) export(is.simple) export(has.multiple) export(evcent) export(hub.score) export(authority.score) export(arpack.unpack.complex) export(unfold.tree) export(is.mutual) export(maximum.cardinality.search) export(is.chordal) export(graph.knn) export(graph.strength) export(centralize.scores) export(centralization.degree) export(centralization.degree.tmax) export(centralization.betweenness) export(centralization.betweenness.tmax) export(centralization.closeness) export(centralization.closeness.tmax) export(centralization.evcent) export(centralization.evcent.tmax) export(assortativity.nominal) export(assortativity) export(assortativity.degree) export(contract.vertices) export(eccentricity) export(radius) export(graph.diversity) export(is.degree.sequence) export(is.graphical.degree.sequence) export(graph.bfs) export(graph.dfs) export(bipartite.projection.size) export(bipartite.projection) export(graph.bipartite) export(graph.incidence) export(get.incidence) export(bipartite.mapping) export(graph.laplacian) export(clusters) export(articulation.points) export(biconnected.components) export(maximal.cliques.count) export(layout.star) export(layout.grid) export(layout.grid.3d) export(layout.fruchterman.reingold) export(layout.graphopt) export(layout.drl) export(layout.drl) export(layout.sugiyama) export(layout.mds) export(layout.bipartite) export(similarity.jaccard) export(similarity.dice) export(similarity.invlogweighted) export(community.le.to.membership) export(mod.matrix) export(leading.eigenvector.community) export(label.propagation.community) export(multilevel.community) export(optimal.community) export(hrg.fit) export(hrg.game) export(hrg.dendrogram) export(hrg.consensus) export(hrg.predict) export(hrg.create) export(infomap.community) export(graphlets) export(graphlets.candidate.basis) export(graphlets.project) export(as.undirected) export(get.stochastic) export(dyad.census) export(triad.census) export(adjacent.triangles) export(graph.maxflow) export(dominator.tree) export(stCuts) export(stMincuts) export(is.separator) export(is.minimal.separator) export(minimal.st.separators) export(minimum.size.separators) export(cohesive.blocks) export(graph.isoclass) export(graph.isomorphic) export(graph.isoclass.subgraph) export(graph.isocreate) export(graph.isomorphic.vf2) export(graph.count.isomorphisms.vf2) export(graph.get.isomorphisms.vf2) export(graph.subisomorphic.vf2) export(graph.count.subisomorphisms.vf2) export(graph.get.subisomorphisms.vf2) export(graph.isomorphic.34) export(canonical.permutation) export(permute.vertices) export(graph.isomorphic.bliss) export(graph.automorphisms) export(graph.subisomorphic.lad) export(scgGrouping) export(scgSemiProjectors) export(scgNormEps) export(is.matching) export(is.maximal.matching) export(maximum.bipartite.matching) export(graph.eigen) export(sir) export(convex.hull) export(revolver.ml.d) export(revolver.probs.d) export(revolver.ml.de) export(revolver.probs.de) export(revolver.ml.ade) export(revolver.probs.ade) export(revolver.ml.f) export(revolver.ml.df) export(revolver.ml.l) export(revolver.ml.ad) export(revolver.probs.ad) export(revolver.ml.D.alpha) export(revolver.ml.D.alpha.a) export(revolver.ml.DE.alpha.a) export(revolver.ml.AD.alpha.a.beta) export(revolver.ml.AD.dpareto) export(revolver.ml.AD.dpareto.eval) export(revolver.ml.ADE.alpha.a.beta) export(revolver.ml.ADE.dpareto) export(revolver.ml.ADE.dpareto.eval) export(revolver.ml.ADE.dpareto.evalf) export(revolver.probs.ADE.dpareto) igraph/demo/0000755000176000001440000000000012251656216012506 5ustar ripleyusersigraph/demo/hrg.R0000644000176000001440000000361012251656216013411 0ustar ripleyusers pause <- function() { cat("Press ENTER/RETURN/NEWLINE to continue.") readLines(n=1) invisible() } ### Download the Zachary Karate Club network from Nexus karate <- nexus.get("karate") karate pause() ### Optimalize modularity optcom <- optimal.community(karate) V(karate)$comm <- membership(optcom) plot(optcom, karate) pause() ### Fit a HRG model to the network hrg <- hrg.fit(karate) hrg pause() ### The fitted model, more details print(hrg, level=5) pause() ### Plot the full hierarchy, as an igraph graph ihrg <- as.igraph(hrg) ihrg$layout <- layout.reingold.tilford plot(ihrg, vertex.size=10, edge.arrow.size=0.2) pause() ### Customize the plot a bit, show probabilities and communities vn <- sub("Actor ", "", V(ihrg)$name) colbar <- rainbow(length(optcom)) vc <- ifelse(is.na(V(ihrg)$prob), colbar[V(karate)$comm], "darkblue") V(ihrg)$label <- ifelse(is.na(V(ihrg)$prob), vn, round(V(ihrg)$prob, 2)) par(mar=c(0,0,3,0)) plot(ihrg, vertex.size=10, edge.arrow.size=0.2, vertex.shape="none", vertex.label.color=vc, main="Hierarchical network model of the Karate Club") pause() ### Plot it as a dendrogram, looks better if the 'ape' package is installed dendPlot(hrg) pause() ### Make a very hierarchical graph g1 <- graph.full(5) g2 <- graph.ring(5) g <- g1 + g2 g <- g + edge(1, vcount(g1)+1) plot(g) pause() ### Fit HRG ghrg <- hrg.fit(g) dendPlot(ghrg) pause() ### Create a consensus dendrogram from multiple samples, takes longer... hcons <- hrg.consensus(g) hcons$consensus pause() ### Predict missing edges pred <- hrg.predict(g) pred pause() ### Add some the top 5 predicted edges to the graph, colored red E(g)$color <- "grey" lay <- layout.auto(g) g2 <- add.edges(g, t(pred$edges[1:5,]), color="red") plot(g2, layout=lay) pause() ### Add four more predicted edges, colored orange g3 <- add.edges(g2, t(pred$edges[6:9,]), color="orange") plot(g3, layout=lay) igraph/demo/centrality.R0000644000176000001440000001107212240234657015007 0ustar ripleyusers pause <- function() { cat("Press ENTER/RETURN/NEWLINE to continue.") readLines(n=1) invisible() } ### Traditional approaches: degree, closeness, betweenness g <- graph.formula(Andre----Beverly:Diane:Fernando:Carol, Beverly--Andre:Diane:Garth:Ed, Carol----Andre:Diane:Fernando, Diane----Andre:Carol:Fernando:Garth:Ed:Beverly, Ed-------Beverly:Diane:Garth, Fernando-Carol:Andre:Diane:Garth:Heather, Garth----Ed:Beverly:Diane:Fernando:Heather, Heather--Fernando:Garth:Ike, Ike------Heather:Jane, Jane-----Ike ) pause() ### Hand-drawn coordinates coords <- c(5,5,119,256,119,256,120,340,478, 622,116,330,231,116,5,330,451,231,231,231) coords <- matrix(coords, nc=2) pause() ### Labels the same as names V(g)$label <- V(g)$name g$layout <- coords # $ pause() ### Take a look at it plotG <- function(g) { plot(g, asp=FALSE, vertex.label.color="blue", vertex.label.cex=1.5, vertex.label.font=2, vertex.size=25, vertex.color="white", vertex.frame.color="white", edge.color="black") } plotG(g) pause() ### Add degree centrality to labels V(g)$label <- paste(sep="\n", V(g)$name, degree(g)) pause() ### And plot again plotG(g) pause() ### Betweenness V(g)$label <- paste(sep="\n", V(g)$name, round(betweenness(g), 2)) plotG(g) pause() ### Closeness V(g)$label <- paste(sep="\n", V(g)$name, round(closeness(g), 2)) plotG(g) pause() ### Eigenvector centrality V(g)$label <- paste(sep="\n", V(g)$name, round(evcent(g)$vector, 2)) plotG(g) pause() ### PageRank V(g)$label <- paste(sep="\n", V(g)$name, round(page.rank(g)$vector, 2)) plotG(g) pause() ### Correlation between centrality measures karate <- graph.famous("Zachary") cent <- list(`Degree`=degree(g), `Closeness`=closeness(g), `Betweenness`=betweenness(g), `Eigenvector`=evcent(g)$vector, `PageRank`=page.rank(g)$vector) pause() ### Pairs plot pairs(cent, lower.panel=function(x,y) { usr <- par("usr") text(mean(usr[1:2]), mean(usr[3:4]), round(cor(x,y), 3), cex=2, col="blue") } ) pause() ## ### A real network, US supreme court citations ## ## You will need internet connection for this to work ## vertices <- read.csv("http://jhfowler.ucsd.edu/data/judicial.csv") ## edges <- read.table("http://jhfowler.ucsd.edu/data/allcites.txt") ## jg <- graph.data.frame(edges, vertices=vertices, dir=TRUE) ## pause() ## ### Basic data ## summary(jg) ## pause() ## ### Is it a simple graph? ## is.simple(jg) ## pause() ## ### Is it connected? ## is.connected(jg) ## pause() ## ### How many components? ## no.clusters(jg) ## pause() ## ### How big are these? ## table(clusters(jg)$csize) ## pause() ## ### In-degree distribution ## plot(degree.distribution(jg, mode="in"), log="xy") ## pause() ## ### Out-degree distribution ## plot(degree.distribution(jg, mode="out"), log="xy") ## pause() ## ### Largest in- and out-degree, total degree ## c(max(degree(jg, mode="in")), ## max(degree(jg, mode="out")), ## max(degree(jg, mode="all"))) ## pause() ## ### Density ## graph.density(jg) ## pause() ## ### Transitivity ## transitivity(jg) ## pause() ## ### Transitivity of a random graph of the same size ## g <- erdos.renyi.game(vcount(jg), ecount(jg), type="gnm") ## transitivity(g) ## pause() ## ### Transitivity of a random graph with the same degree distribution ## g <- degree.sequence.game(degree(jg, mode="out"), degree(jg, mode="in"), ## method="simple") ## transitivity(g) ## pause() ## ### Authority and Hub scores ## AS <- authority.score(jg)$vector ## HS <- hub.score(jg)$vector ## pause() ## ### Time evolution of authority scores ## AS <- authority.score(jg)$vector ## center <- which.max(AS) ## startyear <- V(jg)[center]$year ## pause() ## ### Function to go back in time ## auth.year <- function(y) { ## print(y) ## keep <- which(V(jg)$year <= y) ## g2 <- subgraph(jg, keep) ## as <- abs(authority.score(g2, scale=FALSE)$vector) ## w <- match(V(jg)[center]$usid, V(g2)$usid) ## as[w] ## } ## pause() ## ### Go back in time for the top authority, do a plot ## AS2 <- sapply(startyear:2005, auth.year) ## plot(startyear:2005, AS2, type="b", xlab="year", ylab="authority score") ## pause() ## ### Check another case ## center <- "22US1" ## startyear <- V(jg)[center]$year ## pause() ## ### Calculate past authority scores & plot them ## AS3 <- sapply(startyear:2005, auth.year) ## plot(startyear:2005, AS3, type="b", xlab="year", ylab="authority score") igraph/demo/cohesive.R0000644000176000001440000000200312240234657014430 0ustar ripleyusers pause <- function() { cat("Press ENTER/RETURN/NEWLINE to continue.") readLines(n=1) invisible() } ### The Zachary Karate club network karate <- graph.famous("Zachary") summary(karate) pause() ### Create a layout that is used from now on karate$layout <- layout.auto(karate) plot(karate) pause() ### Run cohesive blocking on it cbKarate <- cohesive.blocks(karate) cbKarate pause() ### Plot the results and all the groups plot(cbKarate, karate) pause() ### This is a bit messy, plot them step-by-step ### See the hierarchy tree first hierarchy(cbKarate) plotHierarchy(cbKarate) ## Plot the first level, blocks 1 & 2 plot(cbKarate, karate, mark.groups=blocks(cbKarate)[1:2+1], col="cyan") pause() ### The second group is simple, plot its more cohesive subgroup plot(cbKarate, karate, mark.groups=blocks(cbKarate)[c(2,5)+1], col="cyan") pause() ### The first group has more subgroups, plot them sub1 <- blocks(cbKarate)[parent(cbKarate)==1] sub1 plot(cbKarate, karate, mark.groups=sub1) pause() igraph/demo/00Index0000644000176000001440000000037512251656216013645 0ustar ripleyuserscrashR A crash-course into R centrality Classic and other vertex centrality indices community Community structure detection smallworld Small-world networks cohesive Cohesive blocking, the Moody & White method hrg Hierarchical random graphs igraph/demo/community.R0000644000176000001440000001251712240234657014662 0ustar ripleyusers pause <- function() { cat("Press ENTER/RETURN/NEWLINE to continue.") readLines(n=1) invisible() } ### A modular graph has dense subgraphs mod <- graph.full(10) %du% graph.full(10) %du% graph.full(10) perfect <- c(rep(1,10), rep(2,10), rep(3,10)) perfect pause() ### Plot it with community (=component) colors plot(mod, vertex.color=perfect, layout=layout.fruchterman.reingold) pause() ### Modularity of the perfect division modularity(mod, perfect) pause() ### Modularity of the trivial partition, quite bad modularity(mod, rep(1, 30)) pause() ### Modularity of a good partition with two communities modularity(mod, c(rep(1, 10), rep(2,20))) pause() ### A real little network, Zachary's karate club data karate <- graph.famous("Zachary") karate$layout <- layout.kamada.kawai(karate, niter=1000) pause() ### Greedy algorithm fc <- fastgreedy.community(karate) memb <- membership(fc) plot(karate, vertex.color=memb) pause() ### Greedy algorithm, easier plotting plot(fc, karate) pause() ### Spinglass algorithm, create a hierarchical network pref.mat <- matrix(0, 16, 16) pref.mat[1:4,1:4] <- pref.mat[5:8,5:8] <- pref.mat[9:12,9:12] <- pref.mat[13:16,13:16] <- 7.5/127 pref.mat[ pref.mat==0 ] <- 5/(3*128) diag(pref.mat) <- diag(pref.mat) + 10/31 pause() ### Create the network with the given vertex preferences G <- preference.game(128*4, types=16, pref.matrix=pref.mat) pause() ### Run spinglass community detection with two gamma parameters sc1 <- spinglass.community(G, spins=4, gamma=1.0) sc2.2 <- spinglass.community(G, spins=16, gamma=2.2) pause() ### Plot the adjacency matrix, use the Matrix package if available if (require(Matrix)) { myimage <- function(...) image(Matrix(...)) } else { myimage <- image } A <- get.adjacency(G) myimage(A) pause() ### Ordering according to (big) communities ord1 <- order(membership(sc1)) myimage(A[ord1,ord1]) pause() ### Ordering according to (small) communities ord2.2 <- order(membership(sc2.2)) myimage(A[ord2.2,ord2.2]) pause() ### Consensus ordering ord <- order(membership(sc1), membership(sc2.2)) myimage(A[ord,ord]) pause() ### Comparision of algorithms communities <- list() pause() ### edge.betweenness.community ebc <- edge.betweenness.community(karate) communities$`Edge betweenness` <- ebc pause() ### fastgreedy.community fc <- fastgreedy.community(karate) communities$`Fast greedy` <- fc pause() ### leading.eigenvector.community lec <- leading.eigenvector.community(karate) communities$`Leading eigenvector` <- lec pause() ### spinglass.community sc <- spinglass.community(karate, spins=10) communities$`Spinglass` <- sc pause() ### walktrap.community wt <- walktrap.community(karate) communities$`Walktrap` <- wt pause() ### label.propagation.community labprop <- label.propagation.community(karate) communities$`Label propagation` <- labprop pause() ### Plot everything layout(rbind(1:3, 4:6)) coords <- layout.kamada.kawai(karate) lapply(seq_along(communities), function(x) { m <- modularity(communities[[x]]) par(mar=c(1,1,3,1)) plot(communities[[x]], karate, layout=coords, main=paste(names(communities)[x], "\n", "Modularity:", round(m, 3))) }) pause() ### Function to calculate clique communities clique.community <- function(graph, k) { clq <- cliques(graph, min=k, max=k) edges <- c() for (i in seq(along=clq)) { for (j in seq(along=clq)) { if ( length(unique(c(clq[[i]], clq[[j]]))) == k+1 ) { edges <- c(edges, c(i,j)) } } } clq.graph <- simplify(graph(edges)) V(clq.graph)$name <- seq(length=vcount(clq.graph)) comps <- decompose.graph(clq.graph) lapply(comps, function(x) { unique(unlist(clq[ V(x)$name ])) }) } pause() ### Apply it to a graph, this is the example graph from ## the original publication g <- graph.formula(A-B:F:C:E:D, B-A:D:C:E:F:G, C-A:B:F:E:D, D-A:B:C:F:E, E-D:A:C:B:F:V:W:U, F-H:B:A:C:D:E, G-B:J:K:L:H, H-F:G:I:J:K:L, I-J:L:H, J-I:G:H:L, K-G:H:L:M, L-H:G:I:J:K:M, M-K:L:Q:R:S:P:O:N, N-M:Q:R:P:S:O, O-N:M:P, P-Q:M:N:O:S, Q-M:N:P:V:U:W:R, R-M:N:V:W:Q, S-N:P:M:U:W:T, T-S:V:W:U, U-E:V:Q:S:W:T, V-E:U:W:T:R:Q, W-U:E:V:Q:R:S:T) pause() ### Hand-made layout to make it look like the original in the paper lay <- c(387.0763, 306.6947, 354.0305, 421.0153, 483.5344, 512.1145, 148.6107, 392.4351, 524.6183, 541.5878, 240.6031, 20, 65.54962, 228.0992, 61.9771, 152.1832, 334.3817, 371.8931, 421.9084, 265.6107, 106.6336, 57.51145, 605, 20, 124.8780, 273.6585, 160.2439, 241.9512, 132.1951, 123.6585, 343.1707, 465.1220, 317.561, 216.3415, 226.0976, 343.1707, 306.5854, 123.6585, 360.2439, 444.3902, 532.1951, 720, 571.2195, 639.5122, 505.3659, 644.3902) lay <- matrix(lay, nc=2) lay[,2] <- max(lay[,2])-lay[,2] pause() ### Take a look at it layout(1) plot(g, layout=lay, vertex.label=V(g)$name) pause() ### Calculate communities res <- clique.community(g, k=4) pause() ### Paint them to different colors colbar <- rainbow( length(res)+1 ) for (i in seq(along=res)) { V(g)[ res[[i]] ]$color <- colbar[i+1] } pause() ### Paint the vertices in multiple communities to red V(g)[ unlist(res)[ duplicated(unlist(res)) ] ]$color <- "red" pause() ### Plot with the new colors plot(g, layout=lay, vertex.label=V(g)$name) igraph/demo/smallworld.R0000644000176000001440000000745012240234657015016 0ustar ripleyusers pause <- function() { cat("Press ENTER/RETURN/NEWLINE to continue.") readLines(n=1) invisible() } pause() ### Create a star-like graph t1 <- graph.formula(A-B:C:D:E) t1 pause() ### Define its plotting properties t1$layout <- layout.circle V(t1)$color <- "white" V(t1)[name=="A"]$color <- "orange" V(t1)$size <- 40 V(t1)$label.cex <- 3 V(t1)$label <- V(t1)$name E(t1)$color <- "black" E(t1)$width <- 3 pause() ### Plot 't1' and A's transitivity tr <- transitivity(t1, type="local")[1] plot(t1, main=paste("Transitivity of 'A':", tr)) pause() ### Add an edge and recalculate transitivity t2 <- add.edges(t1, V(t1)[name %in% c("C","D")], color="red", width=3) tr <- transitivity(t2, type="local")[1] plot(t2, main=paste("Transitivity of 'A':", round(tr,4))) pause() ### Add two more edges newe <- match(c("B", "C", "B", "E"), V(t2)$name)-1 t3 <- add.edges(t2, newe, color="red", width=3) tr <- transitivity(t3, type="local")[1] plot(t3, main=paste("Transitivity of 'A':", round(tr,4))) pause() ### A one dimensional, circular lattice ring <- graph.ring(50) ring$layout <- layout.circle V(ring)$size <- 3 plot(ring, vertex.label=NA, main="Ring graph") pause() ### Watts-Strogatz model ws1 <- watts.strogatz.game(1, 50, 3, p=0) ws1$layout <- layout.circle V(ws1)$size <- 3 E(ws1)$curved <- 1 plot(ws1, vertex.label=NA, main="regular graph") pause() ### Zoom in to this part axis(1) axis(2) abline(h=c(0.8, 1.1)) abline(v=c(-0.2,0.2)) pause() ### Zoom in to this part plot(ws1, vertex.label=NA, xlim=c(-0.2, 0.2), ylim=c(0.8,1.1)) pause() ### Transitivity of the ring graph transitivity(ws1) pause() ### Path lengths, regular graph average.path.length(ws1) pause() ### Function to test regular graph with given size try.ring.pl <- function(n) { g <- watts.strogatz.game(1, n, 3, p=0) average.path.length(g) } try.ring.pl(10) try.ring.pl(100) pause() ### Test a number of regular graphs ring.size <- seq(100, 1000, by=100) ring.pl <- sapply(ring.size, try.ring.pl) plot(ring.size, ring.pl, type="b") pause() ### Path lengths, random graph rg <- erdos.renyi.game(50, 50*3, type="gnm") rg$layout <- layout.circle V(rg)$size <- 3 plot(rg, vertex.label=NA, main="Random graph") average.path.length(rg) pause() ### Path length of random graphs try.random.pl <- function(n) { g <- erdos.renyi.game(n, n*3, type="gnm") average.path.length(g) } try.random.pl(100) pause() ### Plot network size vs. average path length random.pl <- sapply(ring.size, try.random.pl) plot(ring.size, random.pl, type="b") pause() ### Plot again, logarithmic 'x' axis plot(ring.size, random.pl, type="b", log="x") pause() ### Transitivity, random graph, by definition ecount(rg) / (vcount(rg)*(vcount(rg)-1)/2) transitivity(rg, type="localaverage") pause() ### Rewiring ws2 <- watts.strogatz.game(1, 50, 3, p=0.1) ws2$layout <- layout.circle V(ws2)$size <- 3 plot(ws2, vertex.label=NA) average.path.length(ws2) pause() ### Path lengths in randomized lattices try.rr.pl <- function(n, p) { g <- watts.strogatz.game(1, n, 3, p=p) average.path.length(g) } rr.pl.0.1 <- sapply(ring.size, try.rr.pl, p=0.1) plot(ring.size, rr.pl.0.1, type="b") pause() ### Logarithmic 'x' axis plot(ring.size, rr.pl.0.1, type="b", log="x") pause() ### Create the graph in the Watts-Strogatz paper ws.paper <- function(p, n=1000) { g <- watts.strogatz.game(1, n, 10, p=p) tr <- transitivity(g, type="localaverage") pl <- average.path.length(g) c(tr, pl) } pause() ### Do the simulation for a number of 'p' values rewire.prob <- ((1:10)^4)/(10^4) ws.result <- sapply(rewire.prob, ws.paper) dim(ws.result) pause() ### Plot it plot(rewire.prob, ws.result[1,]/ws.result[1,1], log="x", pch=22, xlab="p", ylab="") points(rewire.prob, ws.result[2,]/ws.result[2,1], pch=20) legend("bottomleft", c(expression(C(p)/C(0)), expression(L(p)/L(0))), pch=c(22, 20)) igraph/demo/crashR.R0000644000176000001440000001126512240234657014057 0ustar ripleyusers pause <- function() { cat("Press ENTER/RETURN/NEWLINE to continue.") readLines(n=1) invisible() } ### R objects, (real) numbers a <- 3 a b <- 4 b a+b pause() ### Case sensitive A <- 16 a A pause() ### Vector objects a <- c(1,2,3,4,5,6,7,8,9,10) a b <- 1:100 b a[1] b[1:5] a[1] <- 10 a a[1:4] <- 2 a pause() ### Vector arithmetic a * 2 + 1 pause() ### Functions ls() length(a) mean(a) sd(a) sd c pause() ### Getting help # ?sd # ??"standard deviation" # RSiteSearch("network betweenness") pause() ### Classes class(2) class(1:10) class(sd) pause() ### Character vectors char.vec <- c("this", "is", "a", "vector", "of", "characters") char_vec <- char.vec char.vec[1] pause() ### Index vectors age <- c(45, 36, 65, 21, 52, 19) age[1] age[1:5] age[c(2,5,6)] b[ seq(1,100,by=2) ] pause() ### Named vectors names(age) <- c("Alice", "Bob", "Cecil", "David", "Eve", "Fiona") age age["Bob"] age[c("Eve", "David", "David")] pause() ### Indexing with logical vectors age[c(FALSE, TRUE, FALSE, TRUE, FALSE, TRUE)] names(age)[ age>40 ] age > 40 pause() ### Matrices M <- matrix(1:20, 10, 2) M M2 <- matrix(1:20, 10, 2, byrow=TRUE) ## Named argument! M2 M[1,1] M[1,] M[,1] M[1:5,2] pause() ### Generic functions sd(a) sd(M) class(a) class(M) pause() ### Lists l <- list(1:10, "Hello!", diag(5)) l l[[1]] l[2:3] l l2 <- list(A=1:10, H="Hello!", M=diag(5)) l2 l2$A l2$M pause() ### Factors countries <- c("SUI", "USA", "GBR", "GER", "SUI", "SUI", "GBR", "GER", "FRA", "GER") countries fcountries <- factor(countries) fcountries levels(fcountries) pause() ### Data frames survey <- data.frame(row.names=c("Alice", "Bob", "Cecil", "David", "Eve"), Sex=c("F","M","F","F","F"), Age=c(45,36,65,21,52), Country=c("SUI", "USA", "SUI", "GBR", "USA"), Married=c(TRUE, FALSE, FALSE, TRUE, TRUE), Salary=c(70, 65, 200, 45, 100)) survey survey$Sex plot(survey$Age, survey$Salary) AS.model <- lm(Salary ~ Age, data=survey) AS.model summary(AS.model) abline(AS.model) tapply(survey$Salary, survey$Country, mean) pause() ### Packages # install.packages("igraph") # library(help="igraph") library(igraph) sessionInfo() pause() ### Graphs ## Create a small graph, A->B, A->C, B->C, C->E, D ## A=1, B=2, C=3, D=4, E=5 g <- graph( c(1,2, 1,3, 2,3, 3,5), n=5 ) pause() ### Print a graph to the screen g pause() ### Create an undirected graph as well ## A--B, A--C, B--C, C--E, D g2 <- graph( c(1,2, 1,3, 2,3, 3,5), n=5, dir=FALSE ) g2 pause() ### Is this object an igraph graph? is.igraph(g) is.igraph(1:10) pause() ### Summary, number of vertices, edges summary(g) vcount(g) ecount(g) pause() ### Is the graph directed? is.directed(g) is.directed(g2) pause() ### Convert from directed to undirected as.undirected(g) pause() ### And back as.directed(as.undirected(g)) pause() ### Multiple edges g <- graph( c(1,2,1,2, 1,3, 2,3, 4,5), n=5 ) g is.simple(g) is.multiple(g) pause() ### Remove multiple edges g <- simplify(g) is.simple(g) pause() ### Loop edges g <- graph( c(1,1,1,2, 1,3, 2,3, 4,5), n=5 ) g is.simple(g) is.loop(g) pause() ### Remove loop edges g <- simplify(g) is.simple(g) pause() ### Naming vertices g <- graph.ring(10) V(g)$name <- letters[1:10] V(g)$name g print(g, v=T) pause() ### Create undirected example graph g2 <- graph.formula(Alice-Bob:Cecil:Daniel, Cecil:Daniel-Eugene:Gordon ) print(g2, v=T) pause() ### Remove Alice g3 <- delete.vertices(g2, match("Alice", V(g2)$name)) pause() ### Add three new vertices g4 <- add.vertices(g3, 3) print(g4, v=T) igraph.options(print.vertex.attributes=TRUE, plot.layout=layout.fruchterman.reingold) g4 plot(g4) pause() ### Add three new vertices, with names this time g4 <- add.vertices(g3, 3, attr=list(name=c("Helen", "Ike", "Jane"))) g4 pause() ### Add some edges as well g4 <- add.edges(g4, match(c("Helen", "Jane", "Ike", "Jane"), V(g4)$name )) g4 pause() ### Edge sequences, first create a directed example graph g2 <- graph.formula(Alice -+ Bob:Cecil:Daniel, Cecil:Daniel +-+ Eugene:Gordon ) print(g2, v=T) plot(g2, layout=layout.kamada.kawai, vertex.label=V(g2)$name) pause() ### Sequence of all edges E(g2) pause() ### Edge from a vertex to another E(g2, P=c(1,2)) pause() ### Delete this edge g3 <- delete.edges(g2, E(g2, P=c(1,2))) g3 pause() ### Get the id of the edge as.vector(E(g2, P=c(1,2))) pause() ### All adjacent edges of a vertex E(g2)[ adj(3) ] pause() ### Or multiple vertices E(g2)[ adj(c(3,1)) ] pause() ### Outgoing edges E(g2)[ from(3) ] pause() ### Incoming edges E(g2)[ to(3) ] pause() ### Edges along a path E(g2, path=c(1,4,5)) igraph/AUTHORS0000644000176000001440000001115712263024035012626 0ustar ripleyusers igraph authors, in alphabetical order: -------------------------------------- Patrick R. Amestoy AMD library Adelchi Azzalini igraph.options based on the sm package Tamas Badics GLPK Gregory Benison Minimum cut calculation Adrian Bowman igraph.options based on the sm package Keith Briggs Parts from the Very Nauty Graph Library Geometric random graphs Girth Various patches and bug fixes Jeroen Bruggeman spinglass community detection Burt's constraints Juergen Buchmueller Big number math implementation Carter T. Butts Some layout algorithms from the SNA R package bonpow function in the SNA R package Some R manual pages, from the SNA R package Aaron Clauset Hierarchical random graphs J.T. Conklin logbl function Topher Cooper GSL random number generators (not used in R) Gabor Csardi Most of igraph Trevor Croft simpleraytracer Peter DalGaard zeroin root finder Timothy A Davis CXSPARSE: a Concise Sparse Matrix package - Extended AMD library Sparse matrix column ordering Laurent Deniau Bits of the error handling system Ulrich Drepper logbl function Iain S. Duff AMD library GLPK S.I. Feldman f2c David Firth Display data frame in Tk, from relimp package P. Foggia VF2 graph isomorphism algorithm John Fox R: suppressing X11 warnings Alan George GLPK John Gilbert Sparse matrix column ordering D.Goldfarb GLPK Brian Gough GSL random number generators (not used in R) Tom Gregorovic Multilevel community detection M.Grigoriadis GLPK Oscar Gustafsson GLPK Paul Hsieh pstdint.h Ross Ihaka Some random number generators (not used in R) Tommi Junttila BLISS graph isomorphism library Petteri Kaski BLISS graph isomorphism library Oleg Keselyov zeroin root finder Darwin Klingman GLPK Donald E. Knuth GLPK Stefan I. Larimore Sparse matrix column ordering Yusin Lee GLPK Richard Lehoucq ARPACK Rene Locher R arrow drawing function, from IDPmisc package J.C. Nash BFGS optimizer Joseph W-H Liu GLPK Makoto Matsumoto GSL random number generators (not used in R) Vincent Matossian Graph laplacian igraph_neighborhood_graphs Line graphs Peter McMahan Cohesive blocking Andrew Makhorin GLPK David Morton de Lachapelle Spectral coarse graining Laurence Muller Fixes for compilation on MS Visual Studio Fionn Murtagh Order a hierarchical clustering Emmanuel Navarro infomap community detection Various fixes and patches Tamas Nepusz Most of igraph Esmond Ng Sparse matrix column ordering Kevin O'Neill Maximal independent vertex sets Takuji Nishimura GSL random number generators (not used in R) Jim Orlin GLPK Patric Ostergard GLPK Elliot Paquette psumtree data type Pascal Pons walktrap community detection Joerg Reichardt spinglass community detection Marc Rieffel GSL random number generators (not used in R) B.D. Ripley igraph.options based on the sm package BFGS optimizer Various bug fixes Martin Rosvall infomap community detection Andreas Ruckstuhl R arrow drawing function, from IDPmisc package Heinrich Schuchardt GLPK J.K. Reid GLPK C. Sansone VF2 graph isomorphism algorithm Michael Schmuhl The graphopt layout generator Christine Solnon LAD graph isomorphism library Danny Sorensen ARPACK James Theiler GSL random number generators (not used in R) Samuel Thiriot Interconnected islands graph generator Vincent A. Traag spinglass community detection Magnus Torfason R operators that work by name Minh Van Nguyen Microscopic update rules Various test cases Many documentation and other fixes M. Vento VF2 graph isomorphism algorithm Fabien Viger gengraph graph generator Phuong Vu ARPACK P.J. Weinberger f2c Garrett A. Wollman qsort B.N. Wylie DrL layout generator Chao Yang ARPACK Institutional copyright owners: ------------------------------- Free Software Foundation, Inc Code generated by bison Sandia Corporation DrL layout generator The R Development Core Team Some random number generators (not used in R) R: as.dendrogram from stats package The Regents of the University of California qsort Xerox PARC Sparse matrix column ordering Other contributors ------------------ Neal Becker Patches to compile with gcc 4.4 Richard Bowman R patches Alex Chen Patch to compile on Intel compilers Daniel Cordeiro Patches Tom Gregorovic Bug fixes Mayank Lahiri Forest fire game fix John Lapeyre Patches Christopher Lu Various fixes and patches André Panisson R patches Bob Pap Bug fixes Keith Ponting R package bug fixes Martin J Reed Bug fixes Elena Tea Russo Bug fixes KennyTM Bug fixes Jordi Torrents Patches Matthew Walker Various patches Kai Willadsen Arrow size support in Python igraph/NEWS0000644000176000001440000012217612325341250012261 0ustar ripleyusers============ igraph 0.7.1 ============ April 21, 2014 Release Notes ------------- Some bug fixes, to make sure that the code included in 'Statistical Analysis of Network Data with R' works. See https://github.com/kolaczyk/sand Detailed changes: ----------------- - Graph drawing: fix labels of curved edges, issue #181. - Graph drawing: allow fixing edge labels at given positions, issue #181. - Drop the 'type' vertex attribute after bipartite projection, the projections are not bipartite any more, issue #255. - Print logical attributes in header properly (i.e. encoded by `l`, not `x`, which is for complex attributes. Issue #578. - Add a constructor for `communities` objects, see `create.communities()`. Issue #547. - Better error handling in the GraphML parser. - GraphML reader is a bit more lenient now; makes it possible to read GraphML files saved from yWorks apps. - Fixed a bug in `constaint()`, issue #580. - Bipartite projection now detects invalid edges instead of giving a cryptic error, issue #543. - Fixed the `simplify` argument of `graph.formula()`, which was broken, issue #586. - The function `crossing()` adds better names to the result, fixes issue #587. - The `sir()` function gives an error if the input graph is not simple, fixes issue #582. - Calling igraph functions from igraph callbacks is not allowed now, fixes issue #571. ============ igraph 0.7.0 ============ February 4, 2014 Release Notes ------------- There are a bunch of new features in the library itself, and other important changes in the life of the project. Thanks everyone for sending code and reporting bugs! ### igraph @ github igraph's development has moved from Launchpad to github. This has actually happened several month ago, but never announced officially. The place for reporting bugs is at https://github.com/igraph/igraph/issues. ### New homepage igraph's homepage is now hosted at http://igraph.org, and it is brand new. We wanted to make it easier to use and modern. ### Better nightly downloads You can download nightly builds from igraph at http://igraph.org/nightly. Source and binary R packages (for windows and OSX), are all built. New features and bug fixes ----------------------------- - Added a demo for hierarchical random graphs, invoke it via `demo(hrg)`. - Make attribute prefixes optional when writing a GraphML file. - Added function `mod.matrix()`. - Support edge weights in leading eigenvector community detection. - Added the LAD library for checking (sub)graph isomorphism, version 1. - Logical attributes. - Added `layout.bipartite()` function, a simple two-column layout for bipartite graphs. - Support incidence matrices in bipartite Pajek files. - Pajek files in matrix format are now directed by default, unless they are bipartite. - Support weighted (and signed) networks in Pajek when file is in matrix format. - Fixed a bug in `barabasi.game()`, algorithm psumtree-multiple just froze. - Function `layout.mds()` by default returns a layout matrix now. - Added support for Boolean attributes in the GraphML and GML readers and writer. - Change MDS layout coordinates, first dim is according to first eigenvalue, etc. - `plot.communities()` (`plot.igraph()`, really) draws a border around the marked groups by default. - printing graphs now converts the `name` graph attribute to character - Convenience functions to query and set all attributes at once: `vertex.attriubutes()`, `graph.attributes()` and `edge.attributes()`. - Function `graph.disjoint.union()` handles attributes now. - Rewrite `graph.union()` to handle attributes properly. - `rewire()`: now supports the generation and destruction of loops. - Erdos-Renyi type bipartite random graphs: `bipartite.random.game()`. - Support the new options (predecessors and inbound_edges) of `get_shortest_paths()`, reorganized the output of `get.shortest.paths()` completely. - Added `graphlets()` and related functions. - Fix modularity values of multilevel community if there are no merges at all. - Fixed bug when deleting edges with FALSE in the matrix notation. - Fix `bonpow()` and `alpha.centrality()` and make sure that the sparse solver is called. - `tkplot()` news: enable setting coordinates from the command line via `tkplot.setcoords()` and access to the canvas via `tkplot.canvas()`. - Fixed a potential crash in `igraph_edge_connectivity()`, because of an un-initialized variable in the C code. - Avoiding overflow in `closeness()` and related functions. - Check for NAs after converting 'type' to logical in `bipartite.projection()`. - `graphNEL` conversion functions only load the 'graph' package if it was not loaded before and they load it at the end of the search path, to minimize conflicts. - Fixed a bug when creating graphs from adjacency matrices, we now convert them to double, in case they are integers. - Fixed an invalid memory read (and a potential crash) in the infomap community detection. - Fixed a memory leak in the functions with attribute combinations. - Removed some memory leaks from the SCG functions. - Fixed some memory leaks in the ray tracer. - Fixed memory leak in `graph.bfs()` and `graph.dfs()`. - Fix a bug in triad census that set the first element of the result to NaN. - Fixed a crash in `is.chordal()`. - Fixed a bug in weighted mudularity calculation, sum of the weights was truncated to an integer. - Fixed a bug in weighted multilevel communtiies, the maximum weight was rounded to an integer. - Fixed a bug in `centralization.closeness.tmax()`. - Reimplement push-relabel maximum flow with gap heuristics. - Maximum flow functions now return some statistics about the push relabel algorithm steps. - Function `arpack()` now gives error message if unknown options are given. - Fixed missing whitespace in Pajek writer when the ID attribute was numeric. - Fixed a bug that caused the GML reader to crash when the ID attribute was non-numeric. - Fixed issue #500, potential segfault if the two graphs in BLISS differ in the number of vertices or edges. - Added `igraphtest()` function. - Fix dyad census instability, sometimes incorrect results were reported. - Dyad census detects integer overflow now and gives a warning. - Function `add.edges()` does not allow now zeros in the vertex set. - Added a function to count the number of adjacent triangles: `adjacenct.triangles()`. - Added `graph.eigen()` function, eigenproblems on adjacency matrices. - Added some workarounds for functions that create a lot of graphs, `decompose.graph()` and `graph.neighborhood()` use it. Fixes issue #508. - Added weights support for `optimal.community()`, closes #511. - Faster maximal clique finding. - Added a function to count maximum cliques. - Set operations: union, intersection, disjoint, union, difference, compose now work based on vertex names (if they are present) and keep attributes, closes #20. - Removed functions `graph.intersection.by.name()`, `graph.union.by.name()`, `graph.difference.by.name()`. - The `+` operator on graphs now calls `graph.union()` if both argument graphs are named, and calls `graph.disjoint.union()` otherwise. - Added function `igraph.version()`. - Generate graphs from a stochastic block model: `sbm.game()`. - Do not suggest the stats, XML, jpeg and png packages any more. - Fixed a `set.vertex/edge.attribute` bug that changed both graph objects, after copying (#533) - Fixed a bug in `barabasi.game` that caused crashes. - We use PRPACK to calculate PageRank scores see https://github.com/dgleich/prpack - Added`'which` argument to `bipartite.projection` (#307). - Add `normalized` argument to closeness functions, fixes issue #3. - R: better handling of complex attributes, `[[` on vertex/edge sets, fixes #231. - Implement the `start` argument in `hrg.fit` (#225). - Set root vertex in Reingold-Tilford layout, solves #473. - Fix betweenness normalization for directed graphs. - Fixed a bug in `graph.density` that resulted in incorrect values for undirected graphs with loops - Fixed a bug when many graphs were created in one C call (e.g. by `graph.decompose`), causing #550. - Fixed sparse `graph.adjacency` bugs for graphs with one edge, and graphs with zero edges. - Fixed a bug that made Bellman-Ford shortest paths calculations fail. - Fixed a `graph.adjacency` bug for undirected, weighted graphs and sparse matrices. - `main`, `sub`, `xlab` and `ylab` are proper graphics parameters now (#555). - `graph.data.frame` coerces arguments to data frame (#557). - Fixed a minimum cut bug for weighted undirected graphs (#564). - Functions for simulating epidemics (SIR model) on networks, see the `sir` function. - Fixed argument ordering in `graph.mincut` and related functions. - Avoid copying attributes in query functions and print (#573), these functions are much faster now for graphs with many vertices/edges and attributes. - Speed up writing GML and GraphML files, if some attributes are integer. It was really-really slow. - Fix multiple root vertices in `graph.bfs` (#575). ============ igraph 0.6.6 ============ Released Oct 28, 2013 Some bugs fixed: - Fixed a potential crash in the infomap.community() function. - Various fixed for the operators that work on vertex names (#136). - Fixed an example in the arpack() manual page. - arpack() now gives error message if unknown options are supplied (#492). - Better arpack() error messages. - Fixed missing whitespace in Pajek writer when ID attribute was numeric. - Fixed dyad census instability, sometimes incorrect results were reported (#496). - Fixed a bug that caused the GML reader to crash when the ID attribute was non-numeric - Fixed a potential segfault if the two graphs in BLISS differ in the number of vertices or edges (#500). - Added the igraphtest() function to run tests from R (#485). - Dyad census detects integer overflow now and gives a warning (#497). - R: add.edges() does not allow now zeros in the vertex set (#503). - Add C++ namespace to the files that didn't have one. Fixes some incompatibility with other packages (e.g. rgl) and mysterious crashes (#523). - Fixed a bug that caused a side effect in set.vertex.attributes(), set.edge.attributes() and set.graph.attributes() (#533). - Fixed a bug in degree.distribution() and cluster.distribution() (#257). ============== igraph 0.6.5-2 ============== Released May 16, 2013 Worked two CRAN check problems, and a gfortran bug (string bound checking does not work if code is called from C and without string length arguments at the "right" place). Otherwise identical to 0.6.5-1. ============== igraph 0.6.5-1 ============== Released February 27, 2013 Fixing an annoying bug, that broke two other packages on CRAN: - Setting graph attributes failed sometimes, if the attributes were lists or other complex objects. ============ igraph 0.6.5 ============ Released February 24, 2013 This is a minor release, to fix some very annoying bugs in 0.6.4: - igraph should now work well with older R versions. - Eliminate gap between vertex and edge when plotting an edge without an arrow. Fixes #1118448. - Fixed an out-of-bounds array indexing error in the DrL layout, that potentially caused crashes. - Fixed a crash in weighted betweenness calculation. - Plotting: fixed a bug that caused misplaced arrows at rectangle vertex shapes. ============ igraph 0.6.4 ============ Released February 2, 2013 The version number is not a mistake, we jump to 0.6.4 from 0.6, for technical reasons. This version was actually never really released, but some R packages of this version were uplodaded to CRAN, so we include this version in this NEW file. ========================== New features and bug fixes ========================== - Added a vertex shape API for defining new vertex shapes, and also a couple of new vertex shapes. - Added the get.data.frame() function, opposite of graph.data.frame(). - Added bipartite support to the Pajek reader and writer, closes bug #1042298. - degree.sequence.game() has a new method now: "simple_no_multiple". - Added the is.degree.sequence() and is.graphical.degree.sequence() functions. - rewire() has a new method: "loops", that can create loop edges. - Walktrap community detection now handles isolates. - layout.mds() returns a layout matrix now. - layout.mds() uses LAPACK instead of ARPACK. - Handle the '~' character in write.graph and read.graph. Bug #1066986. - Added k.regular.game(). - Use vertex names to plot if no labels are specified in the function call or as vetex attributes. Fixes issue #1085431. - power.law.fit() can now use a C implementation. - Fixed a bug in barabasi.game() when out.seq was an empty vector. - Fixed a bug that made functions with a progress bar fail if called from another package. - Fixed a bug when creating graphs from a weighted integer adjacency matrix via graph.adjacency(). Bug #1019624. - Fixed overflow issues in centralization calculations. - Fixed a minimal.st.separators() bug, some vertex sets were incorrectly reported as separators. Bug #1033045. - Fixed a bug that mishandled vertex colors in VF2 isomorphism functions. Bug #1032819. - Pajek exporter now always quotes strings, thanks to Elena Tea Russo. - Fixed a bug with handling small edge weights in shortest paths calculation in shortest.paths() (Dijkstra's algorithm.) Thanks to Martin J Reed. - Weighted transitivity uses V(graph) as 'vids' if it is NULL. - Fixed a bug when 'pie' vertices were drawn together with other vertex shapes. - Speed up printing graphs. - Speed up attribute queries and other basic operations, by avoiding copying of the graph. Bug #1043616. - Fixed a bug in the NCV setting for ARPACK functions. It cannot be bigger than the matrix size. - layout.merge()'s DLA mode has better defaults now. - Fixed a bug in layout.mds() that resulted vertices on top of each other. - Fixed a bug in layout.spring(), it was not working properly. - Fixed layout.svd(), which was completely defunct. - Fixed a bug in layout.graphopt() that caused warnings and on some platforms crashes. - Fixed community.to.membership(). Bug #1022850. - Fixed a graph.incidence() crash if it was called with a non-matrix argument. - Fixed a get.shortest.paths bug, when output was set to "both". - Motif finding functions return NA for isomorphism classes that are not motifs (i.e. not connected). Fixes bug #1050859. - Fixed get.adjacency() when attr is given, and the attribute has some complex type. Bug #1025799. - Fixed attribute name in graph.adjacency() for dense matrices. Bug #1066952. - Fixed erratic behavior of alpha.centrality(). - Fixed igraph indexing, when attr is given. Bug #1073705. - Fixed a bug when calculating the largest cliques of a directed graph. Bug #1073800. - Fixed a bug in the maximal clique search, closes #1074402. - Warn for negative weights when calculating PageRank. - Fixed dense, unweighted graph.adjacency when diag=FALSE. Closes issue #1077425. - Fixed a bug in eccentricity() and radius(), the results were often simply wrong. - Fixed a bug in get.all.shortest.paths() when some edges had zero weight. - graph.data.frame() is more careful when vertex names are numbers, to avoid their scientific notation. Fixes issue #1082221. - Better check for NAs in vertex names. Fixes issue #1087215 - Fixed a potential crash in the DrL layout generator. - Fixed a bug in the Reingold-Tilford layout when the graph is directed and mode != ALL. ========== igraph 0.6 ========== Released June 11, 2012 See also the release notes at http://igraph.sf.net/relnotes-0.6.html ===================== R: Major new features ===================== - Vertices and edges are numbered from 1 instead of 0. Note that this makes most of the old R igraph code incompatible with igraph 0.6. If you want to use your old code, please use the igraph0 package. See more at http://igraph.sf.net/relnotes-0.6.html. - The '[' and '[[' operators can now be used on igraph graphs, for '[' the graph behaves as an adjacency matrix, for '[[' is is treated as an adjacency list. It is also much simpler to manipulate the graph structure, i.e. add/remove edges and vertices, with some new operators. See more at ?graph.structure. - In all functions that take a vector or list of vertices or edges, vertex/edge names can be given instead of the numeric ids. - New package 'igraphdata', contains a number of data sets that can be used directly in igraph. - Igraph now supports loading graphs from the Nexus online data repository, see nexus.get(), nexus.info(), nexus.list() and nexus.search(). - All the community structure finding algorithm return a 'communities' object now, which has a bunch of useful operations, see ?communities for details. - Vertex and edge attributes are handled much better now. They are kept whenever possible, and can be combined via a flexible API. See ?attribute.combination. - R now prints igraph graphs to the screen in a more structured and informative way. The output of summary() was also updated accordingly. ===================== R: Other new features ===================== - It is possible to mark vertex groups on plots, via shading. Communities and cohesive blocks are plotted using this by default. - Some igraph demos are now available, see a list via 'demo(package="igraph")'. - igraph now tries to select the optimal layout algorithm, when plotting a graph. - Added a simple console, using Tcl/Tk. It contains a text area for status messages and also a status bar. See igraph.console(). - Reimplemented igraph options support, see igraph.options() and getIgraphOpt(). - Igraph functions can now print status messages. =========================== R: New or updated functions =========================== Community detection ------------------- - The multi-level modularity optimization community structure detection algorithm by Blondel et al. was added, see multilevel.community(). - Distance between two community structures: compare.communities(). - Community structure via exact modularity optimization, optimal.community(). - Hierarchical random graphs and community finding, porting the code from Aaron Clauset. See hrg.game(), hrg.fit(), etc. - Added the InfoMAP community finding method, thanks to Emmanuel Navarro for the code. See infomap.community(). Shortest paths -------------- - Eccentricity (eccentricity()), and radius (radius()) calculations. - Shortest path calculations with get.shortest.paths() can now return the edges along the shortest paths. - get.all.shortest.paths() now supports edge weights. Centrality ---------- - Centralization scores for degree, closeness, betweenness and eigenvector centrality. See centralization.scores(). - Personalized Page-Rank scores, see page.rank(). - Subgraph centrality, subgraph.centrality(). - Authority (authority.score()) and hub (hub.score()) scores support edge weights now. - Support edge weights in betweenness and closeness calculations. - bonpow(), Bonacich's power centrality and alpha.centrality(), Alpha centrality calculations now use sparse matrices by default. - Eigenvector centrality calculation, evcent() now works for directed graphs. - Betweenness calculation can now use arbitrarily large integers, this is required for some lattice-like graphs to avoid overflow. Input/output and file formats ----------------------------- - Support the DL file format in graph.read(). See http://www.analytictech.com/networks/dataentry.htm. - Support writing the LEDA file format in write.graph(). Plotting and layouts -------------------- - Star layout: layout.star(). - Layout based on multidimensional scaling, layout.mds(). - New layouts layout.grid() and layout.grid.3d(). - Sugiyama layout algorithm for layered directed acyclic graphs, layout.sugiyama(). Graph generators ---------------- - New graph generators: static.fitness.game(), static.power.law.game(). - barabasi.game() was rewritten and it supports three algorithms now, the default algorithm does not generate multiple or loop edges. The graph generation process can now start from a supplied graph. - The Watts-Strogatz graph generator, igraph_watts_strogatz() can now create graphs without loop edges. Others ------ - Added the Spectral Coarse Graining algorithm, see scg(). - The cohesive.blocks() function was rewritten in C, it is much faster now. It has a nicer API, too. See demo("cohesive"). - Added generic breadth-first and depth-first search implementations with many callbacks, graph.bfs() and graph_dfs(). - Support vertex and edge coloring in the VF2 (sub)graph isomorphism functions (graph.isomorphic.vf2(), graph.count.isomorphisms.vf2(), graph.get.isomorphisms.vf2(), graph.subisomorphic.vf2(), graph.count.subisomorphisms.vf2(), graph.get.subisomorphisms.vf2()). - Assortativity coefficient, assortativity(), assortativity.nominal() and assortativity.degree(). - Vertex operators that work by vertex names: graph.intersection.by.name(), graph.union.by.name(), graph.difference.by.name(). Thanks to Magnus Torfason for contributing his code! - Function to calculate a non-induced subraph: subgraph.edges(). - More comprehensive maximum flow and minimum cut calculation, see functions graph.maxflow(), graph.mincut(), stCuts(), stMincuts(). - Check whether a directed graph is a DAG, is.dag(). - has.multiple() to decide whether a graph has multiple edges. - Added a function to calculate a diversity score for the vertices, graph.diversity(). - Graph Laplacian calculation (graph.laplacian()) supports edge weights now. - Biconnected component calculation, biconnected.components() now returns the components themselves. - bipartite.projection() calculates multiplicity of edges. - Maximum cardinality search: maximum.cardinality.search() and chordality test: is.chordal() - Convex hull computation, convex.hull(). - Contract vertices, contract.vertices(). ============ igraph 0.5.3 ============ Released November 22, 2009 Bugs corrected in the R interface --------------------------------- - Some small changes to make 'R CMD check' clean - Fixed a bug in graph.incidence, the 'directed' and 'mode' arguments were not handled correctly - Betweenness and edge betweenness functions work for graphs with many shortest paths now (up to the limit of long long int) - When compiling the package, the configure script fails if there is no C compiler available - igraph.from.graphNEL creates the right number of loop edges now - Fixed a bug in bipartite.projection() that caused occasional crashes on some systems ============ igraph 0.5.2 ============ Released April 10, 2009 See also the release notes at http://igraph.sf.net/relnotes-0.5.2.html New in the R interface ---------------------- - Added progress bar support to beweenness() and betweenness.estimate(), layout.drl() - Speeded up betweenness estimation - Speeded up are.connected() - Johnson's shortest paths algorithm added - shortest.paths() has now an 'algorithm' argument to choose from the various implementations manually - Always quote symbolic vertex names when printing graphs or edges - Average nearest neighbor degree calculation, graph.knn() - Weighted degree (also called strength) calculation, graph.strength() - Some new functions to support bipartite graphs: graph.bipartite(), is.bipartite(), get.indicence(), graph.incidence(), bipartite.projection(), bipartite.projection.size() - Support for plotting curved edges with plot.igraph() and tkplot() - Added support for weighted graphs in alpha.centrality() - Added the label propagation community detection algorithm by Raghavan et al., label.propagation.community() - cohesive.blocks() now has a 'cutsetHeuristic' argument to choose between two cutset algorithms - Added a function to "unfold" a tree, unfold.tree() - New tkplot() arguments to change the drawing area - Added a minimal GUI, invoke it with tkigraph() - The DrL layout generator, layout.drl() has a three dimensional mode now. Bugs corrected in the R interface --------------------------------- - Fixed a bug in VF2 graph isomorphism functions - Fixed a bug when a sparse adjacency matrix was requested in get.adjacency() and the graph was named - VL graph generator in degree.sequence.game() checks now that the sum of the degrees is even - Many fixes for supporting various compilers, e.g. GCC 4.4 and Sun's C compiler - Fixed memory leaks in graph.automorphisms(), Bellman-Ford shortest.paths(), independent.vertex.sets() - Fix a bug when a graph was imported from LGL and exported to NCOL format (#289596) - cohesive.blocks() creates its temporary file in the session temporary directory - write.graph() and read.graph() now give error messages when unknown arguments are given - The GraphML reader checks the name of the attributes to avoid adding a duplicate 'id' attribute - It is possible to change the 'ncv' ARPACK parameter for leading.eigenvector.community() - Fixed a bug in path.length.hist(), 'unconnected' was wrong for unconnected and undirected graphs - Better handling of attribute assingment via iterators, this is now also clarified in the manual - Better error messages for unknown vertex shapes - Make R package unload cleanly if unloadNamespace() is used - Fixed a bug in plotting square shaped vertices (#325244) - Fixed a bug in graph.adjacency() when the matrix is a sparse matrix of class "dgTMatrix" ============ igraph 0.5.1 ============ Released July 14, 2008 See also the release notes at http://igraph.sf.net/relnotes-0.5.1.html New in the R interface ---------------------- - A new layout generator called DrL. - Uniform sampling of random connected undirected graphs with a given degree sequence. - Edge labels are plotted at 1/3 of the edge, this is better if the graph has mutual edges. - Initial and experimental vertex shape support in 'plot'. - New function, 'graph.adjlist' creates igraph graphs from adjacency lists. - Conversion to/from graphNEL graphs, from the 'graph' R package. - Fastgreedy community detection can utilize edge weights now, this was missing from the R interface. - The 'arrow.width' graphical parameter was added. - graph.data.frame has a new argument 'vertices'. - graph.adjacency and get.adjacency support sparse matrices, the 'Matrix' package is required to use this functionality. - graph.adjacency adds column/row names as 'name' attribute. - Weighted shortest paths using Dijkstra's or the Belmann-Ford algorithm. - Shortest path functions return 'Inf' for unreachable vertices. - New function 'is.mutual' to find mutual edges in a directed graph. - Added inverse log-weighted similarity measure (a.k.a. Adamic/Adar similarity). - preference.game and asymmetric.preference.game were rewritten, they are O(|V|+|E|) now, instead of O(|V|^2). - Edge weight support in function 'get.shortest.paths', it uses Dijkstra's algorithm. Bugs corrected in the R interface --------------------------------- - A bug was corrected in write.pajek.bgraph. - Several bugs were corrected in graph.adjacency. - Pajek reader bug corrected, used to segfault if '*Vertices' was missing. - Directedness is handled correctly when writing GML files. (But note that 'correct' conflicts the standard here.) - Corrected a bug when calculating weighted, directed PageRank on an undirected graph. (Which does not make sense anyway.) - Several bugs were fixed in the Reingold-Tilford layout to avoid edge crossings. - A bug was fixed in the GraphML reader, when the value of a graph attribute was not specified. - Fixed a bug in the graph isomorphism routine for small (3-4 vertices) graphs. - Corrected the random sampling implementation (igraph_random_sample), now it always generates unique numbers. This affects the Gnm Erdos-Renyi generator, it always generates simple graphs now. - The basic igraph constructor (igraph_empty_attrs, all functions are expected to call this internally) now checks whether the number of vertices is finite. - The LGL, NCOL and Pajek graph readers handle errors properly now. - The non-symmetric ARPACK solver returns results in a consistent form now. - The fast greedy community detection routine now checks that the graph is simple. - The LGL and NCOL parsers were corrected to work with all kinds of end-of-line encodings. - Hub & authority score calculations initialize ARPACK parameters now. - Fixed a bug in the Walktrap community detection routine, when applied to unconnected graphs. - Several small memory leaks were removed, and a big one from the Spinglass community structure detection function ========= igraph 0.5 ========= Released February 14, 2008 See also the release notes at http://igraph.sf.net/relnotes-0.5.html New in the R interface ---------------------- - The 'rescale', 'asp' and 'frame' graphical parameters were added - Create graphs from a formula notation (graph.formula) - Handle graph attributes properly - Calculate the actual minimum cut for undirected graphs - Adjacency lists, get.adjlist and get.adjedgelist added - Eigenvector centrality computation is much faster now - Proper R warnings, instead of writing the warning to the terminal - R checks graphical parameters now, the unknown ones are not just ignored, but an error message is given - plot.igraph has an 'add' argument now to compose plots with multiple graphs - plot.igraph supports the 'main' and 'sub' arguments - layout.norm is public now, it can normalize a layout - It is possible to supply startup positions to layout generators - Always free memory when CTRL+C/ESC is pressed, in all operating systems - plot.igraph can plot square vertices now, see the 'shape' parameter - graph.adjacency rewritten when creating weighted graphs - We use match.arg whenever possible. This means that character scalar options can be abbreviated and they are always case insensitive - VF2 graph isomorphism routines can check subgraph isomorphism now, and they are able to return matching(s) - The BLISS graph isomorphism algorithm is included in igraph now. See canonical.permutation, graph.isomorphic.bliss - We use ARPACK for eigenvalue/eigenvector calculation. This means that the following functions were rewritten: page.rank, leading.eigenvector.community.*, evcent. New functions based on ARPACK: hub.score, authority.score, arpack. - Edge weights for Fruchterman-Reingold layout (layout.fruchterman.reingold). - Line graph calculation (line.graph) - Kautz and de Bruijn graph generators (graph.kautz, graph.de.bruijn) - Support for writing graphs in DOT format - Jaccard and Dice similarity coefficients added (similarity.jaccard, similarity.dice) - Counting the multiplicity of edges (count.multiple) - The graphopt layout algorithm was added, layout.graphopt - Generation of "famous" graphs (graph.famous). - Create graphs from LCF notation (graph.cf). - Dyad census and triad cencus functions (dyad.census, triad.census) - Cheking for simple graphs (is.simple) - Create full citation networks (graph.full.citation) - Create a histogram of path lengths (path.length.hist) - Forest fire model added (forest.fire.game) - DIMACS reader can handle different file types now - Biconnected components and articulation points (biconnected.components, articulation.points) - Kleinberg's hub and authority scores (hub.score, authority.score) - as.undirected handles attributes now - Geometric random graph generator (grg.game) can return the coordinates of the vertices - Function added to convert leading eigenvector community structure result to a membership vector (community.le.to.membership) - Weighted fast greedy community detection - Weighted page rank calculation - Functions for estimating closeness, betweenness, edge betweenness by introducing a cutoff for path lengths (closeness.estimate, betweenness.estimate, edge.betweenness.estimate) - Weighted modularity calculation - Function for permuting vertices (permute.vertices) - Betweenness and closeness calculations are speeded up - read.graph can handle all possible line terminators now (\r, \n, \r\n, \n\r) - Error handling was rewritten for walktrap community detection, the calculation can be interrupted now - The maxflow/mincut functions allow to supply NULL pointer for edge capacities, implying unit capacities for all edges Bugs corrected in the R interface --------------------------------- - Fixed a bug in cohesive.blocks, cohesive blocks were sometimes not calculated correctly ========= igraph 0.4.5 ========= Released January 1, 2008 New: - Cohesive block finding in the R interface, thanks to Peter McMahan for contributing his code. See James Moody and Douglas R. White, 2003, in Structural Cohesion and Embeddedness: A Hierarchical Conception of Social Groups American Sociological Review 68(1):1-25 - Biconnected components and articulation points. - R interface: better printing of attributes. - R interface: graph attributes can be used via '$'. Bug fixed: - Erdos-Renyi random graph generators rewritten. ========= igraph 0.4.4 ========= Released October 3, 2007 This release should work seemlessly with the new R 2.6.0 version. Some other bugs were also fixed: - A bug was fixed in the Erdos-Renyi graph generator, which sometimes added an extra vertex. ========= igraph 0.4.3 ========= Released August 13, 2007 The next one in the sequence of bugfix releases. Thanks to many people sending bug reports. Here are the changes: - Some memory leaks removed when using attributes from R or Python. - GraphML parser: entities and character data in multiple chunks are now handled correctly. - A bug corrected in edge betweenness community structure detection, it failed if called many times from the same program/session. - Edge betweeness community structure: handle unconnected graphs properly. - Fixed bug related to fast greedy community detection in unconnected graphs. - Use a different kind of parser (Push) for reading GraphML files. This is almost invisible for users but fixed a nondeterministic bug when reading in GraphML files. - R interface: plot now handles properly if called with a vector as the edge.width argument for directed graphs. - R interface: bug (typo) corrected for walktrap.community and weighted graphs. ========= igraph 0.4.2 ========= Released June 7, 2007 This is another bugfix release, as there was a serious bug in the R package of the previous version: it could not read and write graphs to files in any format under MS Windows. Some other bits added: - circular Reingold-Tilford layout generator for trees - corrected a bug, Pajek files are written properly under MS Windows now. - arrow.size graphical edge parameter added in the R interface. ========= igraph 0.4.1 ========= Released May 23, 2007 This is a minor release, it corrects a number of bugs, mostly in the R package. ========= igraph 0.4 ========= Released May 21, 2007 The major new additions in this release is a bunch of community detection algorithms and support for the GML file format. Here is the complete list of changes: New in the R interface ---------------------- - as the internal representation changed, graphs stored with 'save' with an older igraph version cannot be read back with the new version reliably. - neighbors returns ordered lists - is.loop and is.multiple were added - topological sorting - VF2 isomorphism algorithm - support for reading graphs from the Graph Database for isomorphism - graph.mincut can calculate the actual minimum cut - girth calculation added, thanks to Keith Briggs - support for reading and writing GML files - Walktrap community detection algorithm added, thanks to Matthieu Latapy and Pascal Pons - edge betweenness based community detection algorithm added - fast greedy algorithm for community detection by Clauset et al. added thanks to Aaron Clauset for sharing his code - leading eigenvector community detection algorithm by Mark Newman added - functions for creating dendrograms from the output of the community detection algorithms added - community.membership supporting function added, creates a membership vector from a community structure merge tree - modularity calculation added - graphics parameter handling is completely rewritten, uniform handling of colors and fonts, make sure you read ?igraph.plotting - new plotting parameter for edges: arrow.mode - a bug corrected when playing a nonlinear barabasi.game - better looking plotting in 3d using rglplot: edges are 3d too - rglplot layout is allowed to be two dimensional now - rglplot suspends updates while drawing, this makes it faster - loop edges are correctly plotted by all three plotting functions - better printing of attributes when printing graphs - summary of a graph prints attribute names - is.igraph rewritten to make it possible to inherit from the 'igraph' class - somewhat better looking progress meter for functions which support it Others ------ - many functions benefit from the new internal representation and are faster now: transitivity, reciprocity, graph operator functions like intersection and union, etc. Bugs corrected -------------- - corrected a bug when reading Pajek files: directed graphs were read as undirected ========= igraph 0.3.2 ========= Released Dec 19, 2006 This is a new major release, it contains many new things: Changes in the R interface -------------------------- - bonpow function ported from SNA to calculate Bonacich power centrality - get.adjacency supports attributes now, this means that it sets the colnames and rownames attributes and can return attribute values in the matrix instead of 0/1 - grg.game, geometric random graphs - graph.density, graph density calculation - edge and vertex attributes can be added easily now when added new edges with add.edges or new vertices with add.vertices - graph.data.frame creates graph from data frames, this can be used to create graphs with edge attributes easily - plot.igraph and tkplot can plot self-loop edges now - graph.edgelist to create a graph from an edge list, can also handle edge lists with symbolic names - get.edgelist has now a 'names' argument and can return symbolic vertex names instead of vertex ids, by default id uses the 'name' vertex attribute is returned - printing graphs on screen also prints symbolic symbolic names (the 'name' attribute if present) - maximum flow and minimum cut functions: graph.maxflow, graph.mincut - vertex and edge connectivity: edge.connectivity, vertex.connectivity - edge and vertex disjoint paths: edge.disjoint.paths, vertex.disjoint.paths - White's cohesion and adhesion measure: graph.adhesion, graph.cohesion - dimacs file format added - as.directed handles attributes now - constraint corrected, it handles weighted graphs as well now - weighted attribute to graph.adjacency - spinglass-based community structure detection, the Joerg Reichardt -- Stefan Bornholdt algorithm added: spinglass.community - graph.extended.chordal.ring, extended chordal ring generation - no.clusters calculates the number of clusters without calculating the clusters themselves - minimum spanning tree functions updated to keep attributes - transitivity can calculate local transitivity as well - neighborhood related functions added: neighborhood, neighborhood.size, graph.neighborhood - new graph generators based on vertex types: preference.game and asymmetric.preference.game Bugs corrected -------------- - attribute handling bug when deleting edges corrected - GraphML escaping and NaN handling corrected - bug corrected to make it possible compile the R package without the libxml2 library - a bug in Erdos-Renyi graph generation corrected: it had problems with generating large directed graphs - bug in constraint calculation corrected, it works well now - fixed memory leaks in the GraphML reader - error handling bug corrected in the GraphML reader - bug corrected in R version of graph.laplacian when normalized Laplacian is requested - memory leak corrected in get.all.shortest.paths in the R package ========= igraph 0.2.1 ========= Released Aug 23, 2006 This is a bug-fix release. Bugs fixed: - reciprocity corrected to avoid segfaults - some docs updates - various R package updates to make it conform to the CRAN rules ========= igraph 0.2 ========= Released Aug 18, 2006 Release time at last! There are many new things in igraph 0.2, the most important ones: - reading writing Pajek and GraphML formats with attributes (not all Pajek and GraphML files are supported, see documentation for details) - the RANDEDU fast motif search algorithm is implemented - many new graph generators, both games and regular graphs - many new structural properties: transitivity, reciprocity, etc. - graph operators: union, intersection, difference, structural holes, etc. - conversion between directed and undirected graphs - new layout algorithms for trees and large graphs, 3D layouts and many more. New things specifically in the R package: - support for CTRL+C - new functions: Graph Laplacian, Burt's constraint, etc. - vertex/edge sequences totally rewritten, smart indexing (see manual) - new R manual and tutorial: `Network Analysis with igraph', still under development but useful - very basic 3D plotting using OpenGL Although this release was somewhat tested on Linux, MS Windows, Mac OSX, Solaris 8 and FreeBSD, no heavy testing was done, so it might contain bugs, and we kindly ask you to send bug reports to make igraph better. ========= igraph 0.1 ========= Released Jan 30, 2006 After about a year of development this is the first "official" release of the igraph library. This release should be considered as beta software, but it should be useful in general. Please send your questions and comments. igraph/R/0000755000176000001440000000000012325372070011756 5ustar ripleyusersigraph/R/decomposition.R0000644000176000001440000000330712240234657014764 0ustar ripleyusers # IGraph R package # Copyright (C) 2008-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Graph decomposition ################################################################### is.chordal <- function(graph, alpha = NULL, alpham1 = NULL, fillin = FALSE, newgraph = FALSE) { if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.null(alpha)) alpha <- as.numeric(alpha)-1 if (!is.null(alpham1)) alpham1 <- as.numeric(alpham1)-1 fillin <- as.logical(fillin) newgraph <- as.logical(newgraph) on.exit(.Call("R_igraph_finalizer", PACKAGE = "igraph")) res <- .Call("R_igraph_is_chordal", graph, alpha, alpham1, fillin, newgraph, PACKAGE = "igraph") if (fillin) { res$fillin <- res$fillin + 1 } res } igraph/R/auto.R0000644000176000001440000017366212325372070013070 0ustar ripleyusersgraph.empty <- function(n=0, directed=TRUE) { # Argument checks n <- as.integer(n) directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_empty", n, directed, PACKAGE="igraph") res } vcount <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_vcount", graph, PACKAGE="igraph") res } graph.full.citation <- function(n, directed=TRUE) { # Argument checks n <- as.integer(n) directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_full_citation", n, directed, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'Full citation graph') res } graph.lcf <- function(n, shifts, repeats=1) { # Argument checks n <- as.integer(n) shifts <- as.numeric(shifts) repeats <- as.integer(repeats) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_lcf_vector", n, shifts, repeats, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'LCF graph') res } graph.adjlist <- function(adjlist, mode=c("out", "in", "all", "total"), duplicate=TRUE) { # Argument checks adjlist <- lapply(adjlist, function(x) as.integer(x)-1L) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) duplicate <- as.logical(duplicate) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_adjlist", adjlist, mode, duplicate, PACKAGE="igraph") res } forest.fire.game <- function(nodes, fw.prob, bw.factor=1, ambs=1, directed=TRUE) { # Argument checks nodes <- as.integer(nodes) fw.prob <- as.numeric(fw.prob) bw.factor <- as.numeric(bw.factor) ambs <- as.integer(ambs) directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_forest_fire_game", nodes, fw.prob, bw.factor, ambs, directed, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'Forest fire model') res <- set.graph.attribute(res, 'fw.prob', fw.prob) res <- set.graph.attribute(res, 'bw.factor', bw.factor) res <- set.graph.attribute(res, 'ambs', ambs) res } interconnected.islands.game <- function(islands.n, islands.size, islands.pin, n.inter) { # Argument checks islands.n <- as.integer(islands.n) islands.size <- as.integer(islands.size) islands.pin <- as.numeric(islands.pin) n.inter <- as.integer(n.inter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_simple_interconnected_islands_game", islands.n, islands.size, islands.pin, n.inter, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'Interconnected islands model') res <- set.graph.attribute(res, 'islands.n', islands.n) res <- set.graph.attribute(res, 'islands.size', islands.size) res <- set.graph.attribute(res, 'islands.pin', islands.pin) res <- set.graph.attribute(res, 'n.inter', n.inter) res } static.fitness.game <- function(no.of.edges, fitness.out, fitness.in=NULL, loops=FALSE, multiple=FALSE) { # Argument checks no.of.edges <- as.integer(no.of.edges) fitness.out <- as.numeric(fitness.out) if (!is.null(fitness.in)) fitness.in <- as.numeric(fitness.in) loops <- as.logical(loops) multiple <- as.logical(multiple) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_static_fitness_game", no.of.edges, fitness.out, fitness.in, loops, multiple, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'Static fitness model') res <- set.graph.attribute(res, 'loops', loops) res <- set.graph.attribute(res, 'multiple', multiple) res } static.power.law.game <- function(no.of.nodes, no.of.edges, exponent.out, exponent.in=-1, loops=FALSE, multiple=FALSE, finite.size.correction=TRUE) { # Argument checks no.of.nodes <- as.integer(no.of.nodes) no.of.edges <- as.integer(no.of.edges) exponent.out <- as.numeric(exponent.out) exponent.in <- as.numeric(exponent.in) loops <- as.logical(loops) multiple <- as.logical(multiple) finite.size.correction <- as.logical(finite.size.correction) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_static_power_law_game", no.of.nodes, no.of.edges, exponent.out, exponent.in, loops, multiple, finite.size.correction, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'Static power law model') res <- set.graph.attribute(res, 'exponent.out', exponent.out) res <- set.graph.attribute(res, 'exponent.in', exponent.in) res <- set.graph.attribute(res, 'loops', loops) res <- set.graph.attribute(res, 'multiple', multiple) res <- set.graph.attribute(res, 'finite.size.correction', finite.size.correction) res } k.regular.game <- function(no.of.nodes, k, directed=FALSE, multiple=FALSE) { # Argument checks no.of.nodes <- as.integer(no.of.nodes) k <- as.integer(k) directed <- as.logical(directed) multiple <- as.logical(multiple) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_k_regular_game", no.of.nodes, k, directed, multiple, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'k-regular graph') res <- set.graph.attribute(res, 'k', k) res } sbm.game <- function(n, pref.matrix, block.sizes, directed=FALSE, loops=FALSE) { # Argument checks n <- as.integer(n) pref.matrix <- as.matrix(structure(as.double(pref.matrix), dim=dim(pref.matrix))) block.sizes <- as.integer(block.sizes) directed <- as.logical(directed) loops <- as.logical(loops) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_sbm_game", n, pref.matrix, block.sizes, directed, loops, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'Stochastic block-model') res <- set.graph.attribute(res, 'loops', loops) res } closeness.estimate <- function(graph, vids=V(graph), mode=c("out", "in", "all", "total"), cutoff, weights=NULL, normalized=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) cutoff <- as.numeric(cutoff) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } normalized <- as.logical(normalized) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_closeness_estimate", graph, vids-1, mode, cutoff, weights, normalized, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", vids) } res } betweenness.estimate <- function(graph, vids=V(graph), directed=TRUE, cutoff, weights=NULL, nobigint=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) directed <- as.logical(directed) cutoff <- as.numeric(cutoff) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } nobigint <- as.logical(nobigint) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_betweenness_estimate", graph, vids-1, directed, cutoff, weights, nobigint, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", vids) } res } page.rank.old <- function(graph, vids=V(graph), directed=TRUE, niter=1000, eps=0.001, damping=0.85, old=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) directed <- as.logical(directed) niter <- as.integer(niter) eps <- as.numeric(eps) damping <- as.numeric(damping) old <- as.logical(old) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_pagerank_old", graph, vids-1, directed, niter, eps, damping, old, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", vids) } res } page.rank <- function(graph, algo=c("prpack", "arpack", "power"), vids=V(graph), directed=TRUE, damping=0.85, personalized=NULL, weights=NULL, options=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } algo <- switch(igraph.match.arg(algo), "power"=0L, "arpack"=1L, "prpack"=2L) vids <- as.igraph.vs(graph, vids) directed <- as.logical(directed) damping <- as.numeric(damping) if (!is.null(personalized)) personalized <- as.numeric(personalized) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } if (is.null(options)) { if (algo == 0L) { options <- list(niter=1000, eps=0.001) } else if (algo == 1L) { options <- igraph.arpack.default } else { options <- NULL } } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_personalized_pagerank", graph, algo, vids-1, directed, damping, personalized, weights, options, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res$vector) <- get.vertex.attribute(graph, "name", vids) } res } induced.subgraph <- function(graph, vids, impl=c("auto", "copy_and_delete", "create_from_scratch")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) impl <- switch(igraph.match.arg(impl), "auto"=0, "copy_and_delete"=1, "create_from_scratch"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_induced_subgraph", graph, vids-1, impl, PACKAGE="igraph") res } subgraph.edges <- function(graph, eids, delete.vertices=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } eids <- as.igraph.es(graph, eids) delete.vertices <- as.logical(delete.vertices) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_subgraph_edges", graph, eids-1, delete.vertices, PACKAGE="igraph") res } path.length.hist <- function(graph, directed=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_path_length_hist", graph, directed, PACKAGE="igraph") res } simplify <- function(graph, remove.multiple=TRUE, remove.loops=TRUE, edge.attr.comb=getIgraphOpt("edge.attr.comb")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } remove.multiple <- as.logical(remove.multiple) remove.loops <- as.logical(remove.loops) edge.attr.comb <- igraph.i.attribute.combination(edge.attr.comb) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_simplify", graph, remove.multiple, remove.loops, edge.attr.comb, PACKAGE="igraph") res } is.dag <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_dag", graph, PACKAGE="igraph") res } is.simple <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_simple", graph, PACKAGE="igraph") res } has.multiple <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_has_multiple", graph, PACKAGE="igraph") res } evcent <- function(graph, directed=FALSE, scale=TRUE, weights=NULL, options=igraph.arpack.default) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } directed <- as.logical(directed) scale <- as.logical(scale) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } options.tmp <- igraph.arpack.default; options.tmp[ names(options) ] <- options ; options <- options.tmp on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_eigenvector_centrality", graph, directed, scale, weights, options, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res$vector) <- get.vertex.attribute(graph, "name", ) } res } hub.score <- function(graph, scale=TRUE, weights=NULL, options=igraph.arpack.default) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } scale <- as.logical(scale) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } options.tmp <- igraph.arpack.default; options.tmp[ names(options) ] <- options ; options <- options.tmp on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hub_score", graph, scale, weights, options, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res$vector) <- get.vertex.attribute(graph, "name", ) } res } authority.score <- function(graph, scale=TRUE, weights=NULL, options=igraph.arpack.default) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } scale <- as.logical(scale) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } options.tmp <- igraph.arpack.default; options.tmp[ names(options) ] <- options ; options <- options.tmp on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_authority_score", graph, scale, weights, options, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res$vector) <- get.vertex.attribute(graph, "name", ) } res } arpack.unpack.complex <- function(vectors, values, nev) { # Argument checks vectors <- as.matrix(structure(as.double(vectors), dim=dim(vectors))) values <- as.matrix(structure(as.double(values), dim=dim(values))) nev <- as.integer(nev) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_arpack_unpack_complex", vectors, values, nev, PACKAGE="igraph") res } is.mutual <- function(graph, es=E(graph)) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } es <- as.igraph.es(graph, es) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_mutual", graph, es-1, PACKAGE="igraph") res } maximum.cardinality.search <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_maximum_cardinality_search", graph, PACKAGE="igraph") res } graph.knn <- function(graph, vids=V(graph), weights=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_avg_nearest_neighbor_degree", graph, vids-1, weights, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res$knn) <- get.vertex.attribute(graph, "name", vids) } res } graph.strength <- function(graph, vids=V(graph), mode=c("all", "out", "in", "total"), loops=TRUE, weights=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) loops <- as.logical(loops) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_strength", graph, vids-1, mode, loops, weights, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", vids) } res } centralize.scores <- function(scores, theoretical.max=0, normalized=TRUE) { # Argument checks scores <- as.numeric(scores) theoretical.max <- as.numeric(theoretical.max) normalized <- as.logical(normalized) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization", scores, theoretical.max, normalized, PACKAGE="igraph") res } centralization.degree <- function(graph, mode=c("all", "out", "in", "total"), loops=TRUE, normalized=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) loops <- as.logical(loops) normalized <- as.logical(normalized) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_degree", graph, mode, loops, normalized, PACKAGE="igraph") res } centralization.degree.tmax <- function(graph=NULL, nodes=0, mode=c("all", "out", "in", "total"), loops=FALSE) { # Argument checks if (!is.null(graph) && !is.igraph(graph)) { stop("Not a graph object") } nodes <- as.integer(nodes) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) loops <- as.logical(loops) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_degree_tmax", graph, nodes, mode, loops, PACKAGE="igraph") res } centralization.betweenness <- function(graph, directed=TRUE, nobigint=TRUE, normalized=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } directed <- as.logical(directed) nobigint <- as.logical(nobigint) normalized <- as.logical(normalized) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_betweenness", graph, directed, nobigint, normalized, PACKAGE="igraph") res } centralization.betweenness.tmax <- function(graph=NULL, nodes=0, directed=TRUE) { # Argument checks if (!is.null(graph) && !is.igraph(graph)) { stop("Not a graph object") } nodes <- as.integer(nodes) directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_betweenness_tmax", graph, nodes, directed, PACKAGE="igraph") res } centralization.closeness <- function(graph, mode=c("out", "in", "all", "total"), normalized=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) normalized <- as.logical(normalized) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_closeness", graph, mode, normalized, PACKAGE="igraph") res } centralization.closeness.tmax <- function(graph=NULL, nodes=0, mode=c("out", "in", "all", "total")) { # Argument checks if (!is.null(graph) && !is.igraph(graph)) { stop("Not a graph object") } nodes <- as.integer(nodes) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_closeness_tmax", graph, nodes, mode, PACKAGE="igraph") res } centralization.evcent <- function(graph, directed=FALSE, scale=TRUE, options=igraph.arpack.default, normalized=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } directed <- as.logical(directed) scale <- as.logical(scale) options.tmp <- igraph.arpack.default; options.tmp[ names(options) ] <- options ; options <- options.tmp normalized <- as.logical(normalized) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_eigenvector_centrality", graph, directed, scale, options, normalized, PACKAGE="igraph") res } centralization.evcent.tmax <- function(graph=NULL, nodes=0, directed=FALSE, scale=TRUE) { # Argument checks if (!is.null(graph) && !is.igraph(graph)) { stop("Not a graph object") } nodes <- as.integer(nodes) directed <- as.logical(directed) scale <- as.logical(scale) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_centralization_eigenvector_centrality_tmax", graph, nodes, directed, scale, PACKAGE="igraph") res } assortativity.nominal <- function(graph, types, directed=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } types <- as.numeric(types)-1 directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_assortativity_nominal", graph, types, directed, PACKAGE="igraph") res } assortativity <- function(graph, types1, types2=NULL, directed=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } types1 <- as.numeric(types1) if (!is.null(types2)) types2 <- as.numeric(types2) directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_assortativity", graph, types1, types2, directed, PACKAGE="igraph") res } assortativity.degree <- function(graph, directed=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_assortativity_degree", graph, directed, PACKAGE="igraph") res } contract.vertices <- function(graph, mapping, vertex.attr.comb=getIgraphOpt("vertex.attr.comb")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } mapping <- as.numeric(mapping)-1 vertex.attr.comb <- igraph.i.attribute.combination(vertex.attr.comb) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_contract_vertices", graph, mapping, vertex.attr.comb, PACKAGE="igraph") res } eccentricity <- function(graph, vids=V(graph), mode=c("all", "out", "in", "total")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_eccentricity", graph, vids-1, mode, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", vids) } res } radius <- function(graph, mode=c("all", "out", "in", "total")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_radius", graph, mode, PACKAGE="igraph") res } graph.diversity <- function(graph, weights=NULL, vids=V(graph)) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } vids <- as.igraph.vs(graph, vids) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_diversity", graph, weights, vids-1, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", vids) } res } is.degree.sequence <- function(out.deg, in.deg=NULL) { # Argument checks out.deg <- as.numeric(out.deg) if (!is.null(in.deg)) in.deg <- as.numeric(in.deg) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_degree_sequence", out.deg, in.deg, PACKAGE="igraph") res } is.graphical.degree.sequence <- function(out.deg, in.deg=NULL) { # Argument checks out.deg <- as.numeric(out.deg) if (!is.null(in.deg)) in.deg <- as.numeric(in.deg) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_graphical_degree_sequence", out.deg, in.deg, PACKAGE="igraph") res } bipartite.projection.size <- function(graph, types=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(types) && "type" %in% list.vertex.attributes(graph)) { types <- V(graph)$type } if (!is.null(types)) { if (!is.logical(types)) { warning("vertex types converted to logical") } types <- as.logical(types) if (any(is.na(types))) { stop("`NA' is not allowed in vertex types") } } else { stop("Not a bipartite graph, supply `types' argument") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_bipartite_projection_size", graph, types, PACKAGE="igraph") res } bipartite.mapping <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_bipartite", graph, PACKAGE="igraph") res } articulation.points <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_articulation_points", graph, PACKAGE="igraph") res } biconnected.components <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_biconnected_components", graph, PACKAGE="igraph") res } layout.star <- function(graph, center=V(graph)[1], order=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } center <- as.igraph.vs(graph, center) if (!is.null(order)) order <- as.numeric(order)-1 on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_layout_star", graph, center-1, order, PACKAGE="igraph") res } layout.grid <- function(graph, width=0) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } width <- as.integer(width) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_layout_grid", graph, width, PACKAGE="igraph") res } layout.grid.3d <- function(graph, width=0, height=0) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } width <- as.integer(width) height <- as.integer(height) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_layout_grid_3d", graph, width, height, PACKAGE="igraph") res } layout.bipartite <- function(graph, types=NULL, hgap=1, vgap=1, maxiter=100) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(types) && "type" %in% list.vertex.attributes(graph)) { types <- V(graph)$type } if (!is.null(types)) { if (!is.logical(types)) { warning("vertex types converted to logical") } types <- as.logical(types) if (any(is.na(types))) { stop("`NA' is not allowed in vertex types") } } else { stop("Not a bipartite graph, supply `types' argument") } hgap <- as.numeric(hgap) vgap <- as.numeric(vgap) maxiter <- as.integer(maxiter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_layout_bipartite", graph, types, hgap, vgap, maxiter, PACKAGE="igraph") res } similarity.jaccard <- function(graph, vids=V(graph), mode=c("all", "out", "in", "total"), loops=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) loops <- as.logical(loops) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_similarity_jaccard", graph, vids-1, mode, loops, PACKAGE="igraph") res } similarity.dice <- function(graph, vids=V(graph), mode=c("all", "out", "in", "total"), loops=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) loops <- as.logical(loops) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_similarity_dice", graph, vids-1, mode, loops, PACKAGE="igraph") res } similarity.invlogweighted <- function(graph, vids=V(graph), mode=c("all", "out", "in", "total")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_similarity_inverse_log_weighted", graph, vids-1, mode, PACKAGE="igraph") res } community.le.to.membership <- function(merges, steps, membership) { # Argument checks merges <- as.matrix(structure(as.double(merges), dim=dim(merges))) steps <- as.integer(steps) membership <- as.numeric(membership) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_le_community_to_membership", merges, steps, membership, PACKAGE="igraph") res } mod.matrix <- function(graph, membership, weights=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } membership <- as.numeric(membership)-1 if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_modularity_matrix", graph, membership, weights, PACKAGE="igraph") res } reindex.membership <- function(membership) { # Argument checks membership <- as.numeric(membership) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_reindex_membership", membership, PACKAGE="igraph") res } hrg.game <- function(hrg) { # Argument checks if (is.null(hrg)) { hrg <- list(left=c(), right=c(), prob=c(), edges=c(), vertices=c()) } hrg <- lapply(hrg[c("left","right","prob","edges","vertices")], as.numeric) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hrg_game", hrg, PACKAGE="igraph") res <- set.graph.attribute(res, 'name', 'Hierarchical random graph model') res } hrg.dendrogram <- function(hrg) { # Argument checks if (is.null(hrg)) { hrg <- list(left=c(), right=c(), prob=c(), edges=c(), vertices=c()) } hrg <- lapply(hrg[c("left","right","prob","edges","vertices")], as.numeric) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hrg_dendrogram", hrg, PACKAGE="igraph") res } hrg.consensus <- function(graph, hrg=NULL, start=FALSE, num.samples=10000) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(hrg)) { hrg <- list(left=c(), right=c(), prob=c(), edges=c(), vertices=c()) } hrg <- lapply(hrg[c("left","right","prob","edges","vertices")], as.numeric) start <- as.logical(start) num.samples <- as.integer(num.samples) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hrg_consensus", graph, hrg, start, num.samples, PACKAGE="igraph") res } hrg.create <- function(graph, prob) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } prob <- as.numeric(prob) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hrg_create", graph, prob, PACKAGE="igraph") class(res) <- "igraphHRG" res } graphlets <- function(graph, weights=NULL, niter=1000) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } niter <- as.integer(niter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_graphlets", graph, weights, niter, PACKAGE="igraph") res } as.undirected <- function(graph, mode=c("collapse", "each", "mutual"), edge.attr.comb=getIgraphOpt("edge.attr.comb")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } mode <- switch(igraph.match.arg(mode), "collapse"=1, "each"=0, "mutual"=2) edge.attr.comb <- igraph.i.attribute.combination(edge.attr.comb) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_to_undirected", graph, mode, edge.attr.comb, PACKAGE="igraph") res } dyad.census <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_dyad_census", graph, PACKAGE="igraph") res } triad.census <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_triad_census", graph, PACKAGE="igraph") res } adjacent.triangles <- function(graph, vids=V(graph)) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_adjacent_triangles", graph, vids-1, PACKAGE="igraph") res } graph.maxflow <- function(graph, source, target, capacity=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } source <- as.igraph.vs(graph, source) target <- as.igraph.vs(graph, target) if (is.null(capacity) && "capacity" %in% list.edge.attributes(graph)) { capacity <- E(graph)$capacity } if (!is.null(capacity) && any(!is.na(capacity))) { capacity <- as.numeric(capacity) } else { capacity <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_maxflow", graph, source-1, target-1, capacity, PACKAGE="igraph") res } dominator.tree <- function(graph, root, mode=c("out", "in")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } root <- as.igraph.vs(graph, root) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_dominator_tree", graph, root-1, mode, PACKAGE="igraph") res } stCuts <- function(graph, source, target) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } source <- as.igraph.vs(graph, source) target <- as.igraph.vs(graph, target) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_all_st_cuts", graph, source-1, target-1, PACKAGE="igraph") res } stMincuts <- function(graph, source, target, capacity=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } source <- as.igraph.vs(graph, source) target <- as.igraph.vs(graph, target) if (is.null(capacity) && "weight" %in% list.edge.attributes(graph)) { capacity <- E(graph)$weight } if (!is.null(capacity) && any(!is.na(capacity))) { capacity <- as.numeric(capacity) } else { capacity <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_all_st_mincuts", graph, source-1, target-1, capacity, PACKAGE="igraph") res } is.separator <- function(graph, candidate) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } candidate <- as.igraph.vs(graph, candidate) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_separator", graph, candidate-1, PACKAGE="igraph") res } is.minimal.separator <- function(graph, candidate) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } candidate <- as.igraph.vs(graph, candidate) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_minimal_separator", graph, candidate-1, PACKAGE="igraph") res } minimal.st.separators <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_all_minimal_st_separators", graph, PACKAGE="igraph") res } minimum.size.separators <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_minimum_size_separators", graph, PACKAGE="igraph") res } graph.isoclass <- function(graph) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_isoclass", graph, PACKAGE="igraph") res } graph.isomorphic <- function(graph1, graph2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_isomorphic", graph1, graph2, PACKAGE="igraph") res } graph.isocreate <- function(size, number, directed=TRUE) { # Argument checks size <- as.integer(size) number <- as.integer(number) directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_isoclass_create", size, number, directed, PACKAGE="igraph") res } graph.isomorphic.vf2 <- function(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } if (missing(vertex.color1)) { if ("color" %in% list.vertex.attributes(graph1)) { vertex.color1 <- V(graph1)$color } else { vertex.color1 <- NULL } } if (!is.null(vertex.color1)) { vertex.color1 <- as.integer(vertex.color1)-1L } if (missing(vertex.color2)) { if ("color" %in% list.vertex.attributes(graph2)) { vertex.color2 <- V(graph2)$color } else { vertex.color2 <- NULL } } if (!is.null(vertex.color2)) { vertex.color2 <- as.integer(vertex.color2)-1L } if (missing(edge.color1)) { if ("color" %in% list.edge.attributes(graph1)) { edge.color1 <- E(graph1)$color } else { edge.color1 <- NULL } } if (!is.null(edge.color1)) { edge.color1 <- as.integer(edge.color1)-1L } if (missing(edge.color2)) { if ("color" %in% list.edge.attributes(graph2)) { edge.color2 <- E(graph2)$color } else { edge.color2 <- NULL } } if (!is.null(edge.color2)) { edge.color2 <- as.integer(edge.color2)-1L } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_isomorphic_vf2", graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2, PACKAGE="igraph") res } graph.count.isomorphisms.vf2 <- function(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } if (missing(vertex.color1)) { if ("color" %in% list.vertex.attributes(graph1)) { vertex.color1 <- V(graph1)$color } else { vertex.color1 <- NULL } } if (!is.null(vertex.color1)) { vertex.color1 <- as.integer(vertex.color1)-1L } if (missing(vertex.color2)) { if ("color" %in% list.vertex.attributes(graph2)) { vertex.color2 <- V(graph2)$color } else { vertex.color2 <- NULL } } if (!is.null(vertex.color2)) { vertex.color2 <- as.integer(vertex.color2)-1L } if (missing(edge.color1)) { if ("color" %in% list.edge.attributes(graph1)) { edge.color1 <- E(graph1)$color } else { edge.color1 <- NULL } } if (!is.null(edge.color1)) { edge.color1 <- as.integer(edge.color1)-1L } if (missing(edge.color2)) { if ("color" %in% list.edge.attributes(graph2)) { edge.color2 <- E(graph2)$color } else { edge.color2 <- NULL } } if (!is.null(edge.color2)) { edge.color2 <- as.integer(edge.color2)-1L } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_count_isomorphisms_vf2", graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2, PACKAGE="igraph") res } graph.subisomorphic.vf2 <- function(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } if (missing(vertex.color1)) { if ("color" %in% list.vertex.attributes(graph1)) { vertex.color1 <- V(graph1)$color } else { vertex.color1 <- NULL } } if (!is.null(vertex.color1)) { vertex.color1 <- as.integer(vertex.color1)-1L } if (missing(vertex.color2)) { if ("color" %in% list.vertex.attributes(graph2)) { vertex.color2 <- V(graph2)$color } else { vertex.color2 <- NULL } } if (!is.null(vertex.color2)) { vertex.color2 <- as.integer(vertex.color2)-1L } if (missing(edge.color1)) { if ("color" %in% list.edge.attributes(graph1)) { edge.color1 <- E(graph1)$color } else { edge.color1 <- NULL } } if (!is.null(edge.color1)) { edge.color1 <- as.integer(edge.color1)-1L } if (missing(edge.color2)) { if ("color" %in% list.edge.attributes(graph2)) { edge.color2 <- E(graph2)$color } else { edge.color2 <- NULL } } if (!is.null(edge.color2)) { edge.color2 <- as.integer(edge.color2)-1L } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_subisomorphic_vf2", graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2, PACKAGE="igraph") res } graph.count.subisomorphisms.vf2 <- function(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } if (missing(vertex.color1)) { if ("color" %in% list.vertex.attributes(graph1)) { vertex.color1 <- V(graph1)$color } else { vertex.color1 <- NULL } } if (!is.null(vertex.color1)) { vertex.color1 <- as.integer(vertex.color1)-1L } if (missing(vertex.color2)) { if ("color" %in% list.vertex.attributes(graph2)) { vertex.color2 <- V(graph2)$color } else { vertex.color2 <- NULL } } if (!is.null(vertex.color2)) { vertex.color2 <- as.integer(vertex.color2)-1L } if (missing(edge.color1)) { if ("color" %in% list.edge.attributes(graph1)) { edge.color1 <- E(graph1)$color } else { edge.color1 <- NULL } } if (!is.null(edge.color1)) { edge.color1 <- as.integer(edge.color1)-1L } if (missing(edge.color2)) { if ("color" %in% list.edge.attributes(graph2)) { edge.color2 <- E(graph2)$color } else { edge.color2 <- NULL } } if (!is.null(edge.color2)) { edge.color2 <- as.integer(edge.color2)-1L } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_count_subisomorphisms_vf2", graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2, PACKAGE="igraph") res } graph.isomorphic.34 <- function(graph1, graph2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_isomorphic_34", graph1, graph2, PACKAGE="igraph") res } canonical.permutation <- function(graph, sh="fm") { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } sh <- switch(igraph.match.arg(sh), "f"=0, "fl"=1, "fs"=2, "fm"=3, "flm"=4, "fsm"=5) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_canonical_permutation", graph, sh, PACKAGE="igraph") res } permute.vertices <- function(graph, permutation) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } permutation <- as.numeric(permutation)-1 on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_permute_vertices", graph, permutation, PACKAGE="igraph") res } graph.isomorphic.bliss <- function(graph1, graph2, sh1="fm", sh2="fm") { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } sh1 <- switch(igraph.match.arg(sh1), "f"=0, "fl"=1, "fs"=2, "fm"=3, "flm"=4, "fsm"=5) sh2 <- switch(igraph.match.arg(sh2), "f"=0, "fl"=1, "fs"=2, "fm"=3, "flm"=4, "fsm"=5) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_isomorphic_bliss", graph1, graph2, sh1, sh2, PACKAGE="igraph") res } graph.automorphisms <- function(graph, sh="fm") { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } sh <- switch(igraph.match.arg(sh), "f"=0, "fl"=1, "fs"=2, "fm"=3, "flm"=4, "fsm"=5) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_automorphisms", graph, sh, PACKAGE="igraph") res } scgNormEps <- function(V, groups, mtype=c("symmetric", "laplacian", "stochastic"), p=NULL, norm=c("row", "col")) { # Argument checks V <- as.matrix(structure(as.double(V), dim=dim(V))) groups <- as.numeric(groups)-1 mtype <- switch(igraph.match.arg(mtype), "symmetric"=1, "laplacian"=2, "stochastic"=3) if (!is.null(p)) p <- as.numeric(p) norm <- switch(igraph.match.arg(norm), "row"=1, "col"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_scg_norm_eps", V, groups, mtype, p, norm, PACKAGE="igraph") res } graph.eigen <- function(graph, algorithm=c("arpack", "auto", "lapack", "comp_auto", "comp_lapack", "comp_arpack"), which=list(), options=igraph.arpack.default) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } algorithm <- switch(igraph.match.arg(algorithm), "auto"=0, "lapack"=1, "arpack"=2, "comp_auto"=3, "comp_lapack"=4, "comp_arpack"=5) which.tmp <- igraph.eigen.default; which.tmp[ names(which) ] <- which ; which <- which.tmp options.tmp <- igraph.arpack.default; options.tmp[ names(options) ] <- options ; options <- options.tmp on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_eigen_adjacency", graph, algorithm, which, options, PACKAGE="igraph") res } power.law.fit.new <- function(data, xmin=-1, force.continuous=FALSE) { # Argument checks data <- as.numeric(data) xmin <- as.numeric(xmin) force.continuous <- as.logical(force.continuous) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_power_law_fit", data, xmin, force.continuous, PACKAGE="igraph") res } sir <- function(graph, beta, gamma, no.sim=100) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } beta <- as.numeric(beta) gamma <- as.numeric(gamma) no.sim <- as.integer(no.sim) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_sir", graph, beta, gamma, no.sim, PACKAGE="igraph") class(res) <- "sir" res } convex.hull <- function(data) { # Argument checks data <- as.matrix(structure(as.double(data), dim=dim(data))) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_convex_hull", data, PACKAGE="igraph") res } revolver.ml.d <- function(graph, niter, delta=1e-10, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } niter <- as.integer(niter) delta <- as.numeric(delta) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_d", graph, niter, delta, filter, PACKAGE="igraph") res } revolver.probs.d <- function(graph, kernel, ntk=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } kernel <- as.numeric(kernel) ntk <- as.logical(ntk) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_probs_d", graph, kernel, ntk, PACKAGE="igraph") res } revolver.ml.de <- function(graph, niter, cats, delta=1e-10, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } niter <- as.integer(niter) cats <- as.numeric(cats) delta <- as.numeric(delta) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_de", graph, niter, cats, delta, filter, PACKAGE="igraph") res } revolver.probs.de <- function(graph, kernel, cats) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } kernel <- as.matrix(structure(as.double(kernel), dim=dim(kernel))) cats <- as.numeric(cats) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_probs_de", graph, kernel, cats, PACKAGE="igraph") res } revolver.ml.ade <- function(graph, niter, cats, agebins=300, delta=1e-10, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } niter <- as.integer(niter) cats <- as.numeric(cats) agebins <- as.integer(agebins) delta <- as.numeric(delta) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_ade", graph, niter, cats, agebins, delta, filter, PACKAGE="igraph") res } revolver.probs.ade <- function(graph, kernel, cats) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } kernel <- structure(as.double(kernel), dim=dim(kernel)) cats <- as.numeric(cats) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_probs_ade", graph, kernel, cats, PACKAGE="igraph") res } revolver.ml.f <- function(graph, niter, delta=1e-10) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } niter <- as.integer(niter) delta <- as.numeric(delta) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_f", graph, niter, delta, PACKAGE="igraph") res } revolver.ml.df <- function(graph, niter, delta=1e-10) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } niter <- as.integer(niter) delta <- as.numeric(delta) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_df", graph, niter, delta, PACKAGE="igraph") res } revolver.ml.l <- function(graph, niter, agebins=300, delta=1e-10) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } niter <- as.integer(niter) agebins <- as.integer(agebins) delta <- as.numeric(delta) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_l", graph, niter, agebins, delta, PACKAGE="igraph") res } revolver.ml.ad <- function(graph, niter, agebins=300, delta=1e-10, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } niter <- as.integer(niter) agebins <- as.integer(agebins) delta <- as.numeric(delta) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_ad", graph, niter, agebins, delta, filter, PACKAGE="igraph") res } revolver.probs.ad <- function(graph, kernel, ntk=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } kernel <- as.matrix(structure(as.double(kernel), dim=dim(kernel))) ntk <- as.logical(ntk) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_probs_ad", graph, kernel, ntk, PACKAGE="igraph") res } revolver.ml.D.alpha <- function(graph, alpha, abstol=1e-8, reltol=1e-8, maxit=1000, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } alpha <- as.numeric(alpha) abstol <- as.numeric(abstol) reltol <- as.numeric(reltol) maxit <- as.integer(maxit) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_D_alpha", graph, alpha, abstol, reltol, maxit, filter, PACKAGE="igraph") res } revolver.ml.D.alpha.a <- function(graph, alpha, a, abstol=1e-8, reltol=1e-8, maxit=1000, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } alpha <- as.numeric(alpha) a <- as.numeric(a) abstol <- as.numeric(abstol) reltol <- as.numeric(reltol) maxit <- as.integer(maxit) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_D_alpha_a", graph, alpha, a, abstol, reltol, maxit, filter, PACKAGE="igraph") res } revolver.ml.DE.alpha.a <- function(graph, cats, alpha, a, coeffs, abstol=1e-8, reltol=1e-8, maxit=1000, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } cats <- as.numeric(cats) alpha <- as.numeric(alpha) a <- as.numeric(a) coeffs <- as.numeric(coeffs) abstol <- as.numeric(abstol) reltol <- as.numeric(reltol) maxit <- as.integer(maxit) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_DE_alpha_a", graph, cats, alpha, a, coeffs, abstol, reltol, maxit, filter, PACKAGE="igraph") res } revolver.ml.AD.alpha.a.beta <- function(graph, alpha, a, beta, abstol=1e-8, reltol=1e-8, maxit=1000, agebins=300, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } alpha <- as.numeric(alpha) a <- as.numeric(a) beta <- as.numeric(beta) abstol <- as.numeric(abstol) reltol <- as.numeric(reltol) maxit <- as.integer(maxit) agebins <- as.integer(agebins) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_AD_alpha_a_beta", graph, alpha, a, beta, abstol, reltol, maxit, agebins, filter, PACKAGE="igraph") res } revolver.ml.AD.dpareto <- function(graph, alpha, a, paralpha, parbeta, parscale, abstol=1e-8, reltol=1e-8, maxit=1000, agebins=300, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } alpha <- as.numeric(alpha) a <- as.numeric(a) paralpha <- as.numeric(paralpha) parbeta <- as.numeric(parbeta) parscale <- as.numeric(parscale) abstol <- as.numeric(abstol) reltol <- as.numeric(reltol) maxit <- as.integer(maxit) agebins <- as.integer(agebins) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_AD_dpareto", graph, alpha, a, paralpha, parbeta, parscale, abstol, reltol, maxit, agebins, filter, PACKAGE="igraph") res } revolver.ml.AD.dpareto.eval <- function(graph, alpha, a, paralpha, parbeta, parscale, agebins=300, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } alpha <- as.numeric(alpha) a <- as.numeric(a) paralpha <- as.numeric(paralpha) parbeta <- as.numeric(parbeta) parscale <- as.numeric(parscale) agebins <- as.integer(agebins) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_AD_dpareto_eval", graph, alpha, a, paralpha, parbeta, parscale, agebins, filter, PACKAGE="igraph") res } revolver.ml.ADE.alpha.a.beta <- function(graph, cats, alpha, a, beta, coeffs, abstol=1e-8, reltol=1e-8, maxit=1000, agebins=300, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } cats <- as.numeric(cats) alpha <- as.numeric(alpha) a <- as.numeric(a) beta <- as.numeric(beta) coeffs <- as.numeric(coeffs) abstol <- as.numeric(abstol) reltol <- as.numeric(reltol) maxit <- as.integer(maxit) agebins <- as.integer(agebins) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_ADE_alpha_a_beta", graph, cats, alpha, a, beta, coeffs, abstol, reltol, maxit, agebins, filter, PACKAGE="igraph") res } revolver.ml.ADE.dpareto <- function(graph, cats, alpha, a, paralpha, parbeta, parscale, coeffs, abstol=1e-8, reltol=1e-8, maxit=1000, agebins=300, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } cats <- as.numeric(cats) alpha <- as.numeric(alpha) a <- as.numeric(a) paralpha <- as.numeric(paralpha) parbeta <- as.numeric(parbeta) parscale <- as.numeric(parscale) coeffs <- as.numeric(coeffs) abstol <- as.numeric(abstol) reltol <- as.numeric(reltol) maxit <- as.integer(maxit) agebins <- as.integer(agebins) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_ADE_dpareto", graph, cats, alpha, a, paralpha, parbeta, parscale, coeffs, abstol, reltol, maxit, agebins, filter, PACKAGE="igraph") res } revolver.ml.ADE.dpareto.eval <- function(graph, cats, alpha, a, paralpha, parbeta, parscale, coeffs, agebins=300, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } cats <- as.numeric(cats) alpha <- as.numeric(alpha) a <- as.numeric(a) paralpha <- as.numeric(paralpha) parbeta <- as.numeric(parbeta) parscale <- as.numeric(parscale) coeffs <- as.numeric(coeffs) agebins <- as.integer(agebins) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_ADE_dpareto_eval", graph, cats, alpha, a, paralpha, parbeta, parscale, coeffs, agebins, filter, PACKAGE="igraph") res } revolver.ml.ADE.dpareto.evalf <- function(graph, cats, par, agebins, filter=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } cats <- as.numeric(cats) par <- as.matrix(structure(as.double(par), dim=dim(par))) agebins <- as.integer(agebins) if (!is.null(filter)) filter <- as.numeric(filter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_ml_ADE_dpareto_evalf", graph, cats, par, agebins, filter, PACKAGE="igraph") res } revolver.probs.ADE.dpareto <- function(graph, par, cats, gcats, agebins) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } par <- as.matrix(structure(as.double(par), dim=dim(par))) cats <- as.numeric(cats) gcats <- as.numeric(gcats) agebins <- as.integer(agebins) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_revolver_probs_ADE_dpareto", graph, par, cats, gcats, agebins, PACKAGE="igraph") res } igraph/R/games.R0000644000176000001440000004151712263023733013205 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ba.game <- function(n, power=1, m=NULL, out.dist=NULL, out.seq=NULL, out.pref=FALSE, zero.appeal=1, directed=TRUE, algorithm=c("psumtree", "psumtree-multiple", "bag"), start.graph=NULL) { if (!is.null(start.graph) && !is.igraph(start.graph)) { stop("`start.graph' not an `igraph' object") } # Checks if (! is.null(out.seq) && (!is.null(m) || !is.null(out.dist))) { warning("if `out.seq' is given `m' and `out.dist' should be NULL") m <- out.dist <- NULL } if (is.null(out.seq) && !is.null(out.dist) && !is.null(m)) { warning("if `out.dist' is given `m' will be ignored") m <- NULL } if (!is.null(m) && m==0) { warning("`m' is zero, graph will be empty") } if (power < 0) { warning("`power' is negative") } if (is.null(m) && is.null(out.dist) && is.null(out.seq)) { m <- 1 } n <- as.numeric(n) power <- as.numeric(power) if (!is.null(m)) { m <- as.numeric(m) } if (!is.null(out.dist)) { out.dist <- as.numeric(out.dist) } if (!is.null(out.seq)) { out.seq <- as.numeric(out.seq) } out.pref <- as.logical(out.pref) if (!is.null(out.dist)) { nn <- if (is.null(start.graph)) n else n-vcount(start.graph) out.seq <- as.numeric(sample(0:(length(out.dist)-1), nn, replace=TRUE, prob=out.dist)) } if (is.null(out.seq)) { out.seq <- numeric() } algorithm <- igraph.match.arg(algorithm) algorithm1 <- switch(algorithm, "psumtree"=1, "psumtree-multiple"=2, "bag"=0) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_barabasi_game", n, power, m, out.seq, out.pref, zero.appeal, directed, algorithm1, start.graph, PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Barabasi graph" res$power <- power res$m <- m res$zero.appeal <- zero.appeal res$algorithm <- algorithm } res } barabasi.game <- ba.game erdos.renyi.game <- function(n, p.or.m, type=c("gnp", "gnm"), directed=FALSE, loops=FALSE, ...) { type <- igraph.match.arg(type) type1 <- switch(type, "gnp"=0, "gnm"=1) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_erdos_renyi_game", as.numeric(n), as.numeric(type1), as.numeric(p.or.m), as.logical(directed), as.logical(loops), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- sprintf("Erdos renyi (%s) graph", type) res$type <- type res$loops <- loops if (type=="gnp") { res$p <- p.or.m } if (type=="gnm") { res$m <- p.or.m } } res } random.graph.game <- erdos.renyi.game degree.sequence.game <- function(out.deg, in.deg=NULL, method=c("simple", "vl", "simple.no.multiple"), ...) { method <- igraph.match.arg(method) method1 <- switch(method, "simple"=0, "vl"=1, "simple.no.multiple"=2) if (!is.null(in.deg)) { in.deg <- as.numeric(in.deg) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_degree_sequence_game", as.numeric(out.deg), in.deg, as.numeric(method1), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Degree sequence random graph" res$method <- method } res } growing.random.game <- function(n, m=1, directed=TRUE, citation=FALSE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_growing_random_game", as.numeric(n), as.numeric(m), as.logical(directed), as.logical(citation), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Growing random graph" res$m <- m res$citation <- citation } res } aging.prefatt.game <- function(n, pa.exp, aging.exp, m=NULL, aging.bin=300, out.dist=NULL, out.seq=NULL, out.pref=FALSE, directed=TRUE, zero.deg.appeal=1, zero.age.appeal=0, deg.coef=1, age.coef=1, time.window=NULL) { # Checks if (! is.null(out.seq) && (!is.null(m) || !is.null(out.dist))) { warning("if `out.seq' is given `m' and `out.dist' should be NULL") m <- out.dist <- NULL } if (is.null(out.seq) && !is.null(out.dist) && !is.null(m)) { warning("if `out.dist' is given `m' will be ignored") m <- NULL } if (!is.null(out.seq) && length(out.seq) != n) { stop("`out.seq' should be of length `n'") } if (!is.null(out.seq) && min(out.seq)<0) { stop("negative elements in `out.seq'"); } if (!is.null(m) && m<0) { stop("`m' is negative") } if (!is.null(time.window) && time.window <= 0) { stop("time window size should be positive") } if (!is.null(m) && m==0) { warning("`m' is zero, graph will be empty") } if (pa.exp < 0) { warning("preferential attachment is negative") } if (aging.exp > 0) { warning("aging exponent is positive") } if (zero.deg.appeal <=0 ) { warning("initial attractiveness is not positive") } if (is.null(m) && is.null(out.dist) && is.null(out.seq)) { m <- 1 } n <- as.numeric(n) if (!is.null(m)) { m <- as.numeric(m) } if (!is.null(out.dist)) { out.dist <- as.numeric(out.dist) } if (!is.null(out.seq)) { out.seq <- as.numeric(out.seq) } out.pref <- as.logical(out.pref) if (!is.null(out.dist)) { out.seq <- as.numeric(sample(0:(length(out.dist)-1), n, replace=TRUE, prob=out.dist)) } if (is.null(out.seq)) { out.seq <- numeric() } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- if (is.null(time.window)) { .Call("R_igraph_barabasi_aging_game", as.numeric(n), as.numeric(pa.exp), as.numeric(aging.exp), as.numeric(aging.bin), m, out.seq, out.pref, as.numeric(zero.deg.appeal), as.numeric(zero.age.appeal), as.numeric(deg.coef), as.numeric(age.coef), directed, PACKAGE="igraph") } else { .Call("R_igraph_recent_degree_aging_game", as.numeric(n), as.numeric(pa.exp), as.numeric(aging.exp), as.numeric(aging.bin), m, out.seq, out.pref, as.numeric(zero.deg.appeal), directed, time.window, PACKAGE="igraph") } if (getIgraphOpt("add.params")) { res$name <- "Aging Barabasi graph" res$pa.exp <- pa.exp res$aging.exp <- aging.exp res$m <- m res$aging.bin <- aging.bin res$out.pref <- out.pref res$zero.deg.appeal <- zero.deg.appeal res$zero.age.appeal <- zero.age.appeal res$deg.coef <- deg.coef res$age.coef <- age.coef res$time.window <- if (is.null(time.window)) Inf else time.window } res } aging.barabasi.game <- aging.ba.game <- aging.prefatt.game callaway.traits.game <- function(nodes, types, edge.per.step=1, type.dist=rep(1, types), pref.matrix=matrix(1, types, types), directed=FALSE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_callaway_traits_game", as.double(nodes), as.double(types), as.double(edge.per.step), as.double(type.dist), matrix(as.double(pref.matrix), types, types), as.logical(directed), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Trait-based Callaway graph" res$types <- types res$edge.per.step <- edge.per.step res$type.dist <- type.dist res$pref.matrix <- pref.matrix } res } establishment.game <- function(nodes, types, k=1, type.dist=rep(1, types), pref.matrix=matrix(1, types, types), directed=FALSE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_establishment_game", as.double(nodes), as.double(types), as.double(k), as.double(type.dist), matrix(as.double(pref.matrix), types, types), as.logical(directed), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Trait-based growing graph" res$types <- types res$k <- k res$type.dist <- type.dist res$pref.matrix <- pref.matrix } res } grg.game <- function(nodes, radius, torus=FALSE, coords=FALSE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_grg_game", as.double(nodes), as.double(radius), as.logical(torus), as.logical(coords), PACKAGE="igraph") if (coords) { V(res[[1]])$x <- res[[2]] V(res[[1]])$y <- res[[3]] } if (getIgraphOpt("add.params")) { res[[1]]$name <- "Geometric random graph" res[[1]]$radius <- radius res[[1]]$torus <- torus } res[[1]] } preference.game <- function(nodes, types, type.dist=rep(1, types), fixed.sizes=FALSE, pref.matrix=matrix(1, types, types), directed=FALSE, loops=FALSE) { if (nrow(pref.matrix) != types || ncol(pref.matrix) != types) { stop("Invalid size for preference matrix") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_preference_game", as.double(nodes), as.double(types), as.double(type.dist), as.logical(fixed.sizes), matrix(as.double(pref.matrix), types, types), as.logical(directed), as.logical(loops), PACKAGE="igraph") V(res[[1]])$type <- res[[2]]+1 if (getIgraphOpt("add.params")) { res[[1]]$name <- "Preference random graph" res[[1]]$types <- types res[[1]]$type.dist <- type.dist res[[1]]$fixed.sizes <- fixed.sizes res[[1]]$pref.matrix <- pref.matrix res[[1]]$loops <- loops } res[[1]] } asymmetric.preference.game <- function(nodes, types, type.dist.matrix=matrix(1, types,types), pref.matrix=matrix(1, types, types), loops=FALSE) { if (nrow(pref.matrix) != types || ncol(pref.matrix) != types) { stop("Invalid size for preference matrix") } if (nrow(type.dist.matrix) != types || ncol(type.dist.matrix) != types) { stop("Invalid size for type distribution matrix") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_asymmetric_preference_game", as.double(nodes), as.double(types), matrix(as.double(type.dist.matrix), types, types), matrix(as.double(pref.matrix), types, types), as.logical(loops), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Asymmetric preference random graph" res$types <- types res$type.dist.matrix <- type.dist.matrix res$pref.matrix <- pref.matrix res$loops <- loops } } connect.neighborhood <- function(graph, order, mode=c("all", "out", "in", "total")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_connect_neighborhood", graph, as.numeric(order), as.numeric(mode), PACKAGE="igraph") } rewire.edges <- function(graph, prob, loops=FALSE, multiple=FALSE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_rewire_edges", graph, as.numeric(prob), as.logical(loops), as.logical(multiple), PACKAGE="igraph") } watts.strogatz.game <- function(dim, size, nei, p, loops=FALSE, multiple=FALSE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_watts_strogatz_game", as.numeric(dim), as.numeric(size), as.numeric(nei), as.numeric(p), as.logical(loops), as.logical(multiple), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Watts-Strogatz random graph" res$dim <- dim res$size <- size res$nei <- nei res$p <- p res$loops <- loops res$multiple <- multiple } res } lastcit.game <- function(n, edges=1, agebins=n/7100, pref=(1:(agebins+1))^-3, directed=TRUE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_lastcit_game", as.numeric(n), as.numeric(edges), as.numeric(agebins), as.numeric(pref), as.logical(directed), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Random citation graph based on last citation" res$edges <- edges res$agebins <- agebins } res } cited.type.game <- function(n, edges=1, types=rep(0, n), pref=rep(1, length(types)), directed=TRUE, attr=TRUE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_cited_type_game", as.numeric(n), as.numeric(edges), as.numeric(types), as.numeric(pref), as.logical(directed), PACKAGE="igraph") if (attr) { V(res)$type <- types } if (getIgraphOpt("add.params")) { res$name <- "Random citation graph (cited type)" res$edges <- edges } res } citing.cited.type.game <- function(n, edges=1, types=rep(0, n), pref=matrix(1, nrow=length(types), ncol=length(types)), directed=TRUE, attr=TRUE) { pref <- structure(as.numeric(pref), dim=dim(pref)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_citing_cited_type_game", as.numeric(n), as.numeric(types), pref, as.numeric(edges), as.logical(directed), PACKAGE="igraph") if (attr) { V(res)$type <- types } if (getIgraphOpt("add.params")) { res$name <- "Random citation graph (citing & cited type)" res$edges <- edges } res } simple.interconnected.islands.game <- function(islands.n, islands.size, islands.pin, n.inter) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call( "R_igraph_simple_interconnected_islands_game", as.numeric(islands.n), as.numeric(islands.size), as.numeric(islands.pin), as.numeric(n.inter), PACKAGE="igraph") } bipartite.random.game <- function(n1, n2, type=c("gnp", "gnm"), p, m, directed=FALSE, mode=c("out", "in", "all")) { n1 <- as.integer(n1) n2 <- as.integer(n2) type <- igraph.match.arg(type) if (!missing(p)) { p <- as.numeric(p) } if (!missing(m)) { m <- as.integer(m) } directed <- as.logical(directed) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3) if (type=="gnp" && missing(p)) { stop("Connection probability `p' is not given for Gnp graph") } if (type=="gnp" && !missing(m)) { warning("Number of edges `m' is ignored for Gnp graph") } if (type=="gnm" && missing(m)) { stop("Number of edges `m' is not given for Gnm graph") } if (type=="gnm" && !missing(p)) { warning("Connection probability `p' is ignored for Gnp graph") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) if (type=="gnp") { res <- .Call("R_igraph_bipartite_game_gnp", n1, n2, p, directed, mode, PACKAGE="igraph") res <- set.vertex.attribute(res$graph, "type", value=res$types) res$name <- "Bipartite Gnp random graph" res$p <- p } else if (type=="gnm") { res <- .Call("R_igraph_bipartite_game_gnm", n1, n2, m, directed, mode, PACKAGE="igraph") res <- set.vertex.attribute(res$graph, "type", value=res$types) res$name <- "Bipartite Gnm random graph" res$m <- m } res } igraph/R/structure.generators.R0000644000176000001440000006164312325263533016326 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### graph <- function( edges, n=max(edges), directed=TRUE ) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_create", as.numeric(edges)-1, as.numeric(n), as.logical(directed), PACKAGE="igraph") } graph.formula <- function(..., simplify=TRUE) { mf <- as.list(match.call())[-1] ## In case 'simplify' is given if ('simplify' %in% names(mf)) { w <- which(names(mf)=='simplify') if (length(w) > 1) { stop("'simplify' specified multiple times") } mf <- mf[-w] } ## Operators first f <- function(x) { if (is.call(x)) { return (list(as.character(x[[1]]), lapply(x[-1], f))) } else { return (NULL) } } ops <- unlist(lapply(mf, f)) if (all(ops %in% c("-", ":"))) { directed <- FALSE } else if (all(ops %in% c("-", "+", ":"))) { directed <- TRUE } else { stop("Invalid operator in formula") } f <- function(x) { if (is.call(x)) { if (length(x)==3) { return( list(f(x[[2]]), op=as.character(x[[1]]), f(x[[3]])) ) } else { return( list(op=as.character(x[[1]]), f(x[[2]])) ) } } else { return( c(sym=as.character(x)) ) } } ret <- lapply(mf, function(x) unlist(f(x))) v <- unique(unlist(lapply(ret, function(x) { x[ names(x)=="sym" ] }))) ## Merge symbols for ":" ret <- lapply(ret, function(x) { res <- list() for (i in seq(along=x)) { if (x[i]==":" && names(x)[i]=="op") { ## SKIP } else if (i>1 && x[i-1]==":" && names(x)[i-1]=="op") { res[[length(res)]] <- c(res[[length(res)]], unname(x[i])) } else { res <- c(res, x[i]) } } res }) ## Ok, create the edges edges <- numeric() for (i in seq(along=ret)) { prev.sym <- character() lhead <- rhead <- character() for (j in seq(along=ret[[i]])) { act <- ret[[i]][[j]] if (names(ret[[i]])[j]=="op") { if (length(lhead)==0) { lhead <- rhead <- act } else { rhead <- act } } else if (names(ret[[i]])[j]=="sym") { for (ps in prev.sym) { for (ps2 in act) { if (lhead=="+") { edges <- c(edges, unname(c(ps2, ps))) } if (!directed || rhead=="+") { edges <- c(edges, unname(c(ps, ps2))) } } } lhead <- rhead <- character() prev.sym <- act } } } ids <- seq(along=v) names(ids) <- v res <- graph( unname(ids[edges]), n=length(v), directed=directed) if (simplify) res <- simplify(res) res <- set.vertex.attribute(res, "name", value=v) res } graph.adjacency.dense <- function(adjmatrix, mode=c("directed", "undirected", "max", "min", "upper", "lower", "plus"), weighted=NULL, diag=TRUE) { mode <- igraph.match.arg(mode) mode <- switch(mode, "directed"=0, "undirected"=1, "max"=1, "upper"=2, "lower"=3, "min"=4, "plus"=5) mode(adjmatrix) <- "double" if (!is.null(weighted)) { if (is.logical(weighted) && weighted) { weighted <- "weight" } if (!is.character(weighted)) { stop("invalid value supplied for `weighted' argument, please see docs.") } if (nrow(adjmatrix) != ncol(adjmatrix)) { stop("not a square matrix") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_weighted_adjacency", adjmatrix, as.numeric(mode), weighted, diag, PACKAGE="igraph") } else { adjmatrix <- as.matrix(adjmatrix) attrs <- attributes(adjmatrix) adjmatrix <- as.numeric(adjmatrix) attributes(adjmatrix) <- attrs if (!diag) { diag(adjmatrix) <- 0 } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_graph_adjacency", adjmatrix, as.numeric(mode), PACKAGE="igraph") } res } graph.adjacency.sparse <- function(adjmatrix, mode=c("directed", "undirected", "max", "min", "upper", "lower", "plus"), weighted=NULL, diag=TRUE) { mode <- igraph.match.arg(mode) if (!is.null(weighted)) { if (is.logical(weighted) && weighted) { weighted <- "weight" } if (!is.character(weighted)) { stop("invalid value supplied for `weighted' argument, please see docs.") } } mysummary <- Matrix::summary if (nrow(adjmatrix) != ncol(adjmatrix)) { stop("not a square matrix") } vc <- nrow(adjmatrix) ## to remove non-redundancies that can persist in a dgtMatrix if(inherits(adjmatrix, "dgTMatrix")) { adjmatrix = as(adjmatrix, "CsparseMatrix") } if (is.null(weighted) && mode=="undirected") { mode <- "max" } if (mode == "directed") { ## DIRECTED el <- mysummary(adjmatrix) if (!diag) { el <- el[ el[,1] != el[,2], ] } } else if (mode == "undirected") { ## UNDIRECTED, must be symmetric if weighted if (!is.null(weighted) && !Matrix::isSymmetric(adjmatrix)) { stop("Please supply a symmetric matrix if you want to create a weighted graph with mode=UNDIRECTED.") } if (diag) { adjmatrix <- Matrix::tril(adjmatrix) } else { adjmatrix <- Matrix::tril(adjmatrix, -1) } el <- mysummary(adjmatrix) } else if (mode=="max") { ## MAXIMUM el <- mysummary(adjmatrix) rm(adjmatrix) if (!diag) { el <- el[ el[,1] != el[,2], ] } el <- el[ el[,3] != 0, ] w <- el[,3] el <- el[,1:2] el <- cbind( pmin(el[,1],el[,2]), pmax(el[,1], el[,2]) ) o <- order(el[,1], el[,2]) el <- el[o,,drop=FALSE] w <- w[o] if (nrow(el) > 1) { dd <- el[2:nrow(el),1] == el[1:(nrow(el)-1),1] & el[2:nrow(el),2] == el[1:(nrow(el)-1),2] dd <- which(dd) if (length(dd)>0) { mw <- pmax(w[dd], w[dd+1]) w[dd] <- mw w[dd+1] <- mw el <- el[-dd,,drop=FALSE] w <- w[-dd] } } el <- cbind(el, w) } else if (mode=="upper") { ## UPPER if (diag) { adjmatrix <- Matrix::triu(adjmatrix) } else { adjmatrix <- Matrix::triu(adjmatrix, 1) } el <- mysummary(adjmatrix) rm(adjmatrix) if (!diag) { el <- el[ el[,1] != el[,2], ] } } else if (mode=="lower") { ## LOWER if (diag) { adjmatrix <- Matrix::tril(adjmatrix) } else { adjmatrix <- Matrix::tril(adjmatrix, -1) } el <- mysummary(adjmatrix) rm(adjmatrix) if (!diag) { el <- el[ el[,1] != el[,2], ] } } else if (mode=="min") { ## MINIMUM adjmatrix <- sign(adjmatrix) * sign(Matrix::t(adjmatrix)) * adjmatrix el <- mysummary(adjmatrix) if (!diag) { el <- el[ el[,1] != el[,2], ] } el <- el[ el[,3] != 0, ] w <- el[,3] el <- el[,1:2] el <- cbind( pmin(el[,1],el[,2]), pmax(el[,1], el[,2]) ) o <- order(el[,1], el[,2]) el <- el[o,] w <- w[o] if (nrow(el) > 1) { dd <- el[2:nrow(el),1] == el[1:(nrow(el)-1),1] & el[2:nrow(el),2] == el[1:(nrow(el)-1),2] dd <- which(dd) if (length(dd)>0) { mw <- pmin(w[dd], w[dd+1]) w[dd] <- mw w[dd+1] <- mw el <- el[-dd,] w <- w[-dd] } } el <- cbind(el, w) } else if (mode=="plus") { ## PLUS adjmatrix <- adjmatrix + Matrix::t(adjmatrix) if (diag) { adjmatrix <- Matrix::tril(adjmatrix) } else { adjmatrix <- Matrix::tril(adjmatrix, -1) } el <- mysummary(adjmatrix) if (diag) { loop <- el[,1] == el[,2] el[loop,3] <- el[loop,3] / 2 } el <- el[ el[,3] != 0, ] rm(adjmatrix) } if (!is.null(weighted)) { res <- graph.empty(n=vc, directed=(mode=="directed")) weight <- list(el[,3]) names(weight) <- weighted res <- add.edges(res, edges=t(as.matrix(el[,1:2])), attr=weight) } else { edges <- unlist(apply(el, 1, function(x) rep(unname(x[1:2]), x[3]))) res <- graph(n=vc, edges, directed=(mode=="directed")) } res } graph.adjacency <- function(adjmatrix, mode=c("directed", "undirected", "max", "min", "upper", "lower", "plus"), weighted=NULL, diag=TRUE, add.colnames=NULL, add.rownames=NA) { if (inherits(adjmatrix, "Matrix")) { res <- graph.adjacency.sparse(adjmatrix, mode=mode, weighted=weighted, diag=diag) } else { res <- graph.adjacency.dense(adjmatrix, mode=mode, weighted=weighted, diag=diag) } ## Add columns and row names as attributes if (is.null(add.colnames)) { if (!is.null(colnames(adjmatrix))) { add.colnames <- "name" } else { add.colnames <- NA } } else if (!is.na(add.colnames)) { if (is.null(colnames(adjmatrix))) { warning("No column names to add") add.colnames <- NA } } if (is.null(add.rownames)) { if (!is.null(rownames(adjmatrix))) { add.rownames <- "name" } else { add.colnames <- NA } } else if (!is.na(add.rownames)) { if (is.null(rownames(adjmatrix))) { warning("No row names to add") add.rownames <- NA } } if (!is.na(add.rownames) && !is.na(add.colnames) && add.rownames == add.colnames ) { warning("Same attribute for columns and rows, row names are ignored") add.rownames <- NA } if (!is.na(add.colnames)) { res <- set.vertex.attribute(res, add.colnames, value=colnames(adjmatrix)) } if (!is.na(add.rownames)) { res <- set.vertex.attribute(res, add.rownames, value=rownames(adjmatrix)) } res } graph.star <- function(n, mode=c("in", "out", "mutual", "undirected"), center=1 ) { mode <- igraph.match.arg(mode) mode1 <- switch(mode, "out"=0, "in"=1, "undirected"=2, "mutual"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_star", as.numeric(n), as.numeric(mode1), as.numeric(center)-1, PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- switch(mode, "in"="In-star", "out"="Out-star", "Star") res$mode <- mode res$center <- center } res } graph.full <- function(n, directed=FALSE, loops=FALSE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_full", as.numeric(n), as.logical(directed), as.logical(loops), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Full graph" res$loops <- loops } res } ################################################################### # Lattices, every kind ################################################################### graph.lattice <- function(dimvector=NULL,length=NULL, dim=NULL, nei=1, directed=FALSE, mutual=FALSE, circular=FALSE, ...) { ## # Check ## if (is.null(dimvector) && (is.null(length) || is.null(dim))) { ## stop("Either `length' and `dim' or 'dimvector' must be set. See docs.") ## } ## if (!is.null(length) && length < 1) { ## stop("Invalid `length' argument, should be at least one") ## } ## if (!is.null(length) && dim < 1) { ## stop("Invalid `dim' argument, should be at least one") ## } ## if (!is.null(length) && any(dimvector < 1)) { ## stop("Invalid `dimvector', has negative or smaller than one elements") ## } ## if (mutual && !directed) { ## warning("`mutual' specified for undirected graph, proceeding with multiplex edges...") ## } ## if (nei < 1) { ## stop("`nei' should be at least one") ## } ## if (!is.null(length)) { ## length <- as.numeric(length) ## dim <- as.numeric(dim) ## dimvector <- rep(length, times=dim) ## } else { ## dimvector <- as.numeric(dimvector) ## } ## nei <- as.numeric(nei) ## n <- prod(dimvector) ## res <- graph.empty(n=n, directed=directed, ...) ## res <- add.edges(res, .Call("REST_create_lattice", dimvector, n, ## circular, mutual, PACKAGE="igraph")) ## # Connect also to local neighborhood ## if (nei >= 2) { ## neighbors <- lapply(1:length(res), function(a) get.neighborhood(res, a)) ## res <- add.edges(res, .Call("REST_connect_neighborhood", neighbors, nei, ## mutual, PACKAGE="igraph")) ## } ## res if (is.null(dimvector)) { dimvector <- rep(length, dim) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_lattice", as.numeric(dimvector), as.numeric(nei), as.logical(directed), as.logical(mutual), as.logical(circular), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Lattice graph" res$dimvector <- dimvector res$nei <- nei res$mutual <- mutual res$circular <- circular } res } graph.ring <- function(n, directed=FALSE, mutual=FALSE, circular=TRUE) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_ring", as.numeric(n), as.logical(directed), as.logical(mutual), as.logical(circular), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Ring graph" res$mutual <- mutual res$circular <- circular } res } ################################################################### # Trees, regular ################################################################### graph.tree <- function(n, children=2, mode=c("out", "in", "undirected")) { mode <- igraph.match.arg(mode) mode1 <- switch(mode, "out"=0, "in"=1, "undirected"=2); on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_tree", as.numeric(n), as.numeric(children), as.numeric(mode1), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Tree" res$children <- children res$mode <- mode } res } ################################################################### # The graph atlas ################################################################### graph.atlas <- function(n) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_atlas", as.numeric(n), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- sprintf("Graph from the Atlas #%i", n) res$n <- n } res } ################################################################### # Create a graph from a data frame ################################################################### graph.data.frame <- function(d, directed=TRUE, vertices=NULL) { d <- as.data.frame(d) if (!is.null(vertices)) { vertices <- as.data.frame(vertices) } if (ncol(d) < 2) { stop("the data frame should contain at least two columns") } ## Handle if some elements are 'NA' if (any(is.na(d[,1:2]))) { warning("In `d' `NA' elements were replaced with string \"NA\"") d[,1:2][ is.na(d[,1:2]) ] <- 'NA' } if (!is.null(vertices) && any(is.na(vertices[,1]))) { warning("In `vertices[,1]' `NA' elements were replaced with string \"NA\"") vertices[,1][is.na(vertices[,1])] <- 'NA' } names <- unique( c(as.character(d[,1]), as.character(d[,2])) ) if (!is.null(vertices)) { names2 <- names vertices <- as.data.frame(vertices) if (ncol(vertices) < 1) { stop("Vertex data frame contains no rows") } names <- as.character(vertices[,1]) if (any(duplicated(names))) { stop("Duplicate vertex names") } if (any(! names2 %in% names)) { stop("Some vertex names in edge list are not listed in vertex data frame") } } # create graph g <- graph.empty(n=0, directed=directed) # vertex attributes attrs <- list(name=names) if (!is.null(vertices)) { if (ncol(vertices) > 1) { for (i in 2:ncol(vertices)) { newval <- vertices[,i] if (class(newval) == "factor") { newval <- as.character(newval) } attrs[[ names(vertices)[i] ]] <- newval } } } # add vertices g <- add.vertices(g, length(names), attr=attrs) # create edge list from <- as.character(d[,1]) to <- as.character(d[,2]) edges <- rbind(match(from, names), match(to,names)) # edge attributes attrs <- list() if (ncol(d) > 2) { for (i in 3:ncol(d)) { newval <- d[,i] if (class(newval) == "factor") { newval <- as.character(newval) } attrs[[ names(d)[i] ]] <- newval } } # add the edges g <- add.edges(g, edges, attr=attrs) g } graph.edgelist <- function(el, directed=TRUE) { if (!is.matrix(el) || ncol(el) != 2) { stop("graph.edgelist expects a matrix with two columns") } if (nrow(el) == 0) { res <- graph.empty(directed=directed) } else { if (is.character(el)) { ## symbolic edge list names <- unique(as.character(t(el))) ids <- seq(names) names(ids) <- names res <- graph( unname(ids[t(el)]), directed=directed) rm(ids) V(res)$name <- names } else { ## normal edge list res <- graph( t(el), directed=directed ) } } res } graph.extended.chordal.ring <- function(n, w) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_extended_chordal_ring", as.numeric(n), as.matrix(w), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Extended chordal ring" res$w <- w } res } line.graph <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_line_graph", graph, PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- "Line graph" } res } graph.de.bruijn <- function(m, n) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_de_bruijn", as.numeric(m), as.numeric(n), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- sprintf("De-Bruijn graph %i-%i", m, n) res$m <- m res$n <- n } res } graph.kautz <- function(m, n) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_kautz", as.numeric(m), as.numeric(n), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- sprintf("Kautz graph %i-%i", m, n) res$m <- m res$n <- n } res } graph.famous <- function(name) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_famous", as.character(name), PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$name <- name } res } graph.full.bipartite <- function(n1, n2, directed=FALSE, mode=c("all", "out", "in")) { n1 <- as.integer(n1) n2 <- as.integer(n2) directed <- as.logical(directed) mode1 <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_full_bipartite", n1, n2, as.logical(directed), mode1, PACKAGE="igraph") if (getIgraphOpt("add.params")) { res$graph$name <- "Full bipartite graph" res$n1 <- n1 res$n2 <- n2 res$mode <- mode } set.vertex.attribute(res$graph, "type", value=res$types) } graph.bipartite <- function(types, edges, directed=FALSE) { types <- as.logical(types) edges <- as.numeric(edges)-1 directed <- as.logical(directed) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_create_bipartite", types, edges, directed, PACKAGE="igraph") set.vertex.attribute(res, "type", value=types) } graph.incidence.sparse <- function(incidence, directed, mode, multiple, weighted) { n1 <- nrow(incidence) n2 <- ncol(incidence) el <- Matrix::summary(incidence) ## el <- summary(incidence) el[,2] <- el[,2] + n1 if (!is.null(weighted)) { if (is.logical(weighted) && weighted) { weighted <- "weight" } if (!is.character(weighted)) { stop("invalid value supplied for `weighted' argument, please see docs.") } if (!directed || mode==1) { ## nothing do to } else if (mode==2) { el[,1:2] <- el[,c(2,1)] } else if (mode==3) { el <- rbind(el, el[,c(2,1,3)]) } res <- graph.empty(n=n1+n2, directed=directed) weight <- list(el[,3]) names(weight) <- weighted res <- add.edges(res, edges=t(as.matrix(el[,1:2])), attr=weight) } else { if (multiple) { el[,3] <- ceiling(el[,3]) el[,3][ el[,3] < 0 ] <- 0 } else { el[,3] <- el[,3] != 0 } if (!directed || mode==1) { ## nothing do to } else if (mode==2) { el[,1:2] <- el[,c(2,1)] } else if (mode==3) { el <- rbind(el, el[,c(2,1,3)]) } edges <- unlist(apply(el, 1, function(x) rep(unname(x[1:2]), x[3]))) res <- graph(n=n1+n2, edges, directed=directed) } set.vertex.attribute(res, "type", value=c(rep(FALSE, n1), rep(TRUE, n2))) } graph.incidence.dense <- function(incidence, directed, mode, multiple, weighted) { if (!is.null(weighted)) { if (is.logical(weighted) && weighted) { weighted <- "weight" } if (!is.character(weighted)) { stop("invalid value supplied for `weighted' argument, please see docs.") } n1 <- nrow(incidence) n2 <- ncol(incidence) no.edges <- sum(incidence != 0) if (directed && mode==3) { no.edges <- no.edges * 2 } edges <- numeric(2*no.edges) weight <- numeric(no.edges) ptr <- 1 for (i in seq_len(nrow(incidence))) { for (j in seq_len(ncol(incidence))) { if (incidence[i,j] != 0) { if (!directed || mode==1) { edges[2*ptr-1] <- i edges[2*ptr] <- n1+j weight[ptr] <- incidence[i,j] ptr <- ptr + 1 } else if (mode==2) { edges[2*ptr-1] <- n1+j edges[2*ptr] <- i weight[ptr] <- incidence[i,j] ptr <- ptr + 1 } else if (mode==3) { edges[2*ptr-1] <- i edges[2*ptr] <- n1+j weight[ptr] <- incidence[i,j] ptr <- ptr + 1 edges[2*ptr-1] <- n1+j edges[2*ptr] <- i } } } } res <- graph.empty(n=n1+n2, directed=directed) weight <- list(weight) names(weight) <- weighted res <- add.edges(res, edges, attr=weight) res <- set.vertex.attribute(res, "type", value=c(rep(FALSE, n1), rep(TRUE, n2))) } else { mode(incidence) <- "double" on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) ## Function call res <- .Call("R_igraph_incidence", incidence, directed, mode, multiple, PACKAGE="igraph") res <- set.vertex.attribute(res$graph, "type", value=res$types) } res } graph.incidence <- function(incidence, directed=FALSE, mode=c("all", "out", "in", "total"), multiple=FALSE, weighted=NULL, add.names=NULL) { # Argument checks directed <- as.logical(directed) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) multiple <- as.logical(multiple) if (inherits(incidence, "Matrix")) { res <- graph.incidence.sparse(incidence, directed=directed, mode=mode, multiple=multiple, weighted=weighted) } else { incidence <- as.matrix(incidence) res <- graph.incidence.dense(incidence, directed=directed, mode=mode, multiple=multiple, weighted=weighted) } ## Add names if (is.null(add.names)) { if (!is.null(rownames(incidence)) && !is.null(colnames(incidence))) { add.names <- "name" } else { add.names <- NA } } else if (!is.na(add.names)) { if (is.null(rownames(incidence)) || is.null(colnames(incidence))) { warning("Cannot add row- and column names, at least one of them is missing") add.names <- NA } } if (!is.na(add.names)) { res <- set.vertex.attribute(res, add.names, value=c(rownames(incidence), colnames(incidence))) } res } igraph/R/attributes.R0000644000176000001440000002150412272005146014267 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ## ## The brand new attribute interface: ## ## g(graph)$name # get a graph attribute ## g(graph)$name <- "Ring" # set a graph attribute ## ## v(graph)$color <- "red" # set vertex attribute ## v(graph)$color[1:5] <- "blue" ## v(graph)$color[c(5,6,7)] # get vertex attribute ## ## e(graph)$weight <- 1 # set edge attribute ## e(graph)$weight[1:10] # get edge attribute ## get.graph.attribute <- function(graph, name) { if (!is.igraph(graph)) { stop("Not a graph object") } .Call("R_igraph_mybracket2", graph, 9L, 2L, PACKAGE="igraph")[[as.character(name)]] } set.graph.attribute <- function(graph, name, value) { if (!is.igraph(graph)) { stop("Not a graph object") } .Call("R_igraph_mybracket3_set", graph, 9L, 2L, name, value, PACKAGE="igraph") } graph.attributes <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } .Call("R_igraph_mybracket2_copy", graph, 9L, 2L, PACKAGE="igraph") } "graph.attributes<-" <- function(graph, value) { if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.list(value) || (length(value) > 0 && is.null(names(value))) || any(names(value) == "") || any(duplicated(names(value)))) { stop("Value must be a named list with unique names") } .Call("R_igraph_mybracket2_set", graph, 9L, 2L, value, PACKAGE="igraph") } get.vertex.attribute <- function(graph, name, index=V(graph)) { if (!is.igraph(graph)) { stop("Not a graph object") } index <- as.igraph.vs(graph, index) myattr <- .Call("R_igraph_mybracket2", graph, 9L, 3L, PACKAGE="igraph")[[as.character(name)]] myattr[index] } set.vertex.attribute <- function(graph, name, index=V(graph), value) { if (!is.igraph(graph)) { stop("Not a graph object") } single <- "single" %in% names(attributes(index)) && attr(index, "single") if (!missing(index)) { index <- as.igraph.vs(graph, index) } name <- as.character(name) vc <- vcount(graph) vattrs <- .Call("R_igraph_mybracket2", graph, 9L, 3L, PACKAGE="igraph") if (single) { vattrs[[name]][[index]] <- value } else { vattrs[[name]][index] <- value } length(vattrs[[name]]) <- vc .Call("R_igraph_mybracket2_set", graph, 9L, 3L, vattrs, PACKAGE="igraph") } vertex.attributes <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } .Call("R_igraph_mybracket2_copy", graph, 9L, 3L, PACKAGE="igraph") } "vertex.attributes<-" <- function(graph, value) { if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.list(value) || (length(value) > 0 && is.null(names(value))) || any(names(value) == "") || any(duplicated(names(value)))) { stop("Value must be a named list with unique names") } if ( any(sapply(value, length) != vcount(graph)) ) { stop("Invalid attribute value length, must match number of vertices") } .Call("R_igraph_mybracket2_set", graph, 9L, 3L, value, PACKAGE="igraph") } get.edge.attribute <- function(graph, name, index=E(graph)) { if (!is.igraph(graph)) { stop("Not a graph object") } name <- as.character(name) index <- as.igraph.es(graph, index) myattr <- .Call("R_igraph_mybracket2", graph, 9L, 4L, PACKAGE="igraph")[[name]] myattr[index] } set.edge.attribute <- function(graph, name, index=E(graph), value) { if (!is.igraph(graph)) { stop("Not a graph object") } single <- "single" %in% names(attributes(index)) && attr(index, "single") name <- as.character(name) index <- as.igraph.es(graph, index) ec <- ecount(graph) eattrs <- .Call("R_igraph_mybracket2", graph, 9L, 4L, PACKAGE="igraph") if (single) { eattrs[[name]][[index]] <- value } else { eattrs[[name]][index] <- value } length(eattrs[[name]]) <- ec .Call("R_igraph_mybracket2_set", graph, 9L, 4L, eattrs, PACKAGE="igraph") } edge.attributes <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } .Call("R_igraph_mybracket2_copy", graph, 9L, 4L, PACKAGE="igraph") } "edge.attributes<-" <- function(graph, value) { if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.list(value) || (length(value) > 0 && is.null(names(value))) || any(names(value) == "") || any(duplicated(names(value)))) { stop("Value must be a named list with unique names") } if ( any(sapply(value, length) != ecount(graph)) ) { stop("Invalid attribute value length, must match number of edges") } .Call("R_igraph_mybracket2_set", graph, 9L, 4L, value, PACKAGE="igraph") } list.graph.attributes <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } res <- .Call("R_igraph_mybracket2_names", graph, 9L, 2L, PACKAGE="igraph") if (is.null(res)) { res <- character() } res } list.vertex.attributes <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } res <- .Call("R_igraph_mybracket2_names", graph, 9L, 3L, PACKAGE="igraph") if (is.null(res)) { res <- character() } res } list.edge.attributes <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } res <- .Call("R_igraph_mybracket2_names", graph, 9L, 4L, PACKAGE="igraph") if (is.null(res)) { res <- character() } res } remove.graph.attribute <- function(graph, name) { if (!is.igraph(graph)) { stop("Not a graph object") } name <- as.character(name) if (!name %in% list.graph.attributes(graph)) { stop("No such graph attribute: ", name) } gattr <- .Call("R_igraph_mybracket2", graph, 9L, 2L, PACKAGE="igraph") gattr[[name]] <- NULL .Call("R_igraph_mybracket2_set", graph, 9L, 2L, gattr, PACKAGE="igraph") } remove.vertex.attribute <- function(graph, name) { if (!is.igraph(graph)) { stop("Not a graph object") } name <- as.character(name) if (!name %in% list.vertex.attributes(graph)) { stop("No such vertex attribute: ", name) } vattr <- .Call("R_igraph_mybracket2", graph, 9L, 3L, PACKAGE="igraph") vattr[[name]] <- NULL .Call("R_igraph_mybracket2_set", graph, 9L, 3L, vattr, PACKAGE="igraph") } remove.edge.attribute <- function(graph, name) { if (!is.igraph(graph)) { stop("Not a graph object") } name <- as.character(name) if (!name %in% list.edge.attributes(graph)) { stop("No such edge attribute: ", name) } eattr <- .Call("R_igraph_mybracket2", graph, 9L, 4L, PACKAGE="igraph") eattr[[name]] <- NULL .Call("R_igraph_mybracket2_set", graph, 9L, 4L, eattr, PACKAGE="igraph") } ############# is.named <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } "name" %in% list.vertex.attributes(graph) } is.weighted <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } "weight" %in% list.edge.attributes(graph) } is.bipartite <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } "type" %in% list.vertex.attributes(graph) } ############# igraph.i.attribute.combination <- function(comb) { if (is.function(comb)) { comb <- list(comb) } comb <- as.list(comb) if (any(!sapply(comb, function(x) is.function(x) || (is.character(x) && length(x)==1)))) { stop("Attribute combination element must be a function or character scalar") } if (is.null(names(comb))) { names(comb) <- rep("", length(comb)) } if (any(duplicated(names(comb)))) { warning("Some attributes are duplicated") } comb <- lapply(comb, function(x) { if (!is.character(x)) { x } else { known <- data.frame(n=c("ignore", "sum", "prod", "min", "max", "random", "first", "last", "mean", "median", "concat"), i=c(0,3,4,5,6,7,8,9,10,11,12), stringsAsFactors=FALSE) x <- pmatch(tolower(x), known[,1]) if (is.na(x)) { stop("Unknown/unambigous attribute combination specification") } known[,2][x] } }) comb } igraph/R/operators.R0000644000176000001440000003555612251656216014142 0ustar ripleyusers # IGraph R package # Copyright (C) 2006-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### rename.attr.if.needed <- function(type, graphs, newsize=NULL, maps=NULL, maps2=NULL, ignore=character()) { listfun <- switch(type, "g"=list.graph.attributes, "v"=list.vertex.attributes, "e"=list.edge.attributes, stop("Internal igraph error")) getfun <- switch(type, "g"=get.graph.attribute, "v"=get.vertex.attribute, "e"=get.edge.attribute, stop("Internal igraph error")) alist <- lapply(graphs, listfun) an <- unique(unlist(alist)) an <- setdiff(an, ignore) getval <- function(which, name) { newval <- getfun(graphs[[which]], name) if (!is.null(maps)) { tmpval <- newval[ maps[[which]] >= 0 ] mm <- maps[[which]][ maps[[which]] >= 0 ] + 1 newval <- rep(NA, newsize) newval[mm] <- tmpval } if (!is.null(maps2)) { newval <- newval[ maps2[[which]] + 1 ] } if (!is.null(newsize)) { length(newval) <- newsize } newval } attr <- list() for (name in an) { w <- which(sapply(alist, function(x) name %in% x)) if (length(w)==1) { attr[[name]] <- getval(w, name) } else { for (w2 in w) { nname <- paste(name, sep="_", w2) newval <- getval(w2, name) attr[[nname]] <-newval } } } attr } graph.disjoint.union <- function(...) { graphs <- unlist(recursive=FALSE, lapply(list(...), function(l) { if (is.igraph(l)) list(l) else l } )) if (!all(sapply(graphs, is.igraph))) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_disjoint_union", graphs, PACKAGE="igraph") ## Graph attributes graph.attributes(res) <- rename.attr.if.needed("g", graphs) ## Vertex attributes attr <- list() vc <- sapply(graphs, vcount) cumvc <- c(0, cumsum(vc)) for (i in seq_along(graphs)) { va <- vertex.attributes(graphs[[i]]) exattr <- intersect(names(va), names(attr)) # existing and present noattr <- setdiff(names(attr), names(va)) # existint and missing newattr <- setdiff(names(va), names(attr)) # new for (a in seq_along(exattr)) { attr[[ exattr[a] ]] <- c(attr[[ exattr[a] ]], va[[ exattr[a] ]]) } for (a in seq_along(noattr)) { attr[[ noattr[a] ]] <- c(attr[[ noattr[a] ]], rep(NA, vc[i])) } for (a in seq_along(newattr)) { attr[[ newattr[a] ]] <- c(rep(NA, cumvc[i]), va[[ newattr[a] ]]) } } vertex.attributes(res) <- attr if ("name" %in% names(attr) && any(duplicated(attr$name))) { warning("Duplicate vertex names in disjoint union") } ## Edge attributes attr <- list() ec <- sapply(graphs, ecount) cumec <- c(0, cumsum(ec)) for (i in seq_along(graphs)) { ea <- edge.attributes(graphs[[i]]) exattr <- intersect(names(ea), names(attr)) # existing and present noattr <- setdiff(names(attr), names(ea)) # existint and missing newattr <- setdiff(names(ea), names(attr)) # new for (a in seq_along(exattr)) { attr[[ exattr[a] ]] <- c(attr[[ exattr[a] ]], ea[[ exattr[a] ]]) } for (a in seq_along(noattr)) { attr[[ noattr[a] ]] <- c(attr[[ noattr[a] ]], rep(NA, ec[i])) } for (a in seq_along(newattr)) { attr[[ newattr[a] ]] <- c(rep(NA, cumec[i]), ea[[ newattr[a] ]]) } } edge.attributes(res) <- attr res } "%du%" <- function(x,y) { graph.disjoint.union(x,y) } .igraph.graph.union.or.intersection <- function(call, ..., byname, keep.all.vertices) { graphs <- unlist(recursive=FALSE, lapply(list(...), function(l) { if (is.igraph(l)) list(l) else l } )) if (!all(sapply(graphs, is.igraph))) { stop("Not a graph object") } if (byname != "auto" && !is.logical(byname)) { stop("`bynam' must be \"auto\", or logical") } nonamed <- sum(sapply(graphs, is.named)) if (byname == "auto") { byname <- all(sapply(graphs, is.named)) if (nonamed != 0 && nonamed != length(graphs)) { warning("Some, but not all graphs are named, not using vertex names") } } else if (byname && nonamed != length(graphs)) { stop("Some graphs are not named") } edgemaps <- length(unlist(lapply(graphs, list.edge.attributes))) != 0 if (byname) { allnames <- lapply(graphs, get.vertex.attribute, "name") if (keep.all.vertices) { uninames <- unique(unlist(allnames)) newgraphs <- lapply(graphs, function(g) { g <- g + setdiff(uninames, V(g)$name) permute.vertices(g, match(V(g)$name, uninames)) }) } else { uninames <- Reduce(intersect, allnames) newgraphs <- lapply(graphs, function(g) { g <- g - setdiff(V(g)$name, uninames) permute.vertices(g, match(V(g)$name, uninames)) }) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call(call, newgraphs, edgemaps, PACKAGE="igraph") maps <- res$edgemaps res <- res$graph ## We might need to rename all attributes graph.attributes(res) <- rename.attr.if.needed("g", newgraphs) vertex.attributes(res) <- rename.attr.if.needed("v", newgraphs, vcount(res), ignore="name") V(res)$name <- uninames ## Edges are a bit more difficult, we need a mapping if (edgemaps) { edge.attributes(res) <- rename.attr.if.needed("e", newgraphs, ecount(res), maps=maps) } } else { if (!keep.all.vertices) { minsize <- min(sapply(graphs, vcount)) graphs <- lapply(graphs, function(g) { vc <- vcount(g) if (vc > minsize) { g <- g - (minsize+1):vc } g }) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call(call, graphs, edgemaps, PACKAGE="igraph") maps <- res$edgemaps res <- res$graph ## We might need to rename all attributes graph.attributes(res) <- rename.attr.if.needed("g", graphs) vertex.attributes(res) <- rename.attr.if.needed("v", graphs, vcount(res)) ## Edges are a bit more difficult, we need a mapping if (edgemaps) { edge.attributes(res) <- rename.attr.if.needed("e", graphs, ecount(res), maps=maps) } } res } graph.union <- function(..., byname="auto") { .igraph.graph.union.or.intersection("R_igraph_union", ..., byname=byname, keep.all.vertices=TRUE) } "%u%" <- function(x,y) { graph.union(x,y) } graph.intersection <- function(..., byname="auto", keep.all.vertices=TRUE) { .igraph.graph.union.or.intersection("R_igraph_intersection", ..., byname=byname, keep.all.vertices=keep.all.vertices) } "%s%" <- function(x,y) { graph.intersection(x,y) } graph.difference <- function(big, small, byname="auto") { if (!is.igraph(big) || !is.igraph(small)) { stop("argument is not a graph") } if (byname != "auto" && !is.logical(byname)) { stop("`bynam' must be \"auto\", or logical") } nonamed <- is.named(big) + is.named(small) if (byname == "auto") { byname <- nonamed == 2 if (nonamed == 1) { warning("One, but not both graphs are named, not using vertex names") } } else if (byname && nonamed != 2) { stop("Some graphs are not named") } if (byname) { bnames <- V(big)$name snames <- V(small)$name if (any(! snames %in% bnames)) { small <- small - setdiff(snames, bnames) snames <- V(small)$name } perm <- match(bnames, snames) if (any(is.na(perm))) { perm[is.na(perm)] <- seq(from=vcount(small)+1, to=vcount(big)) } big <- permute.vertices(big, perm) on.exit(.Call("R_igraph_finalizer", PACKAGE="igraph")) res <- .Call("R_igraph_difference", big, small, PACKAGE="igraph") permute.vertices(res, match(V(res)$name, bnames)) } else { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_difference", big, small, PACKAGE="igraph") } } "%m%" <- function(x,y) { graph.difference(x,y) } graph.complementer <- function(graph, loops=FALSE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_complementer", graph, as.logical(loops), PACKAGE="igraph") } graph.compose <- function(g1, g2, byname="auto") { if (!is.igraph(g1) || !is.igraph(g2)) { stop("Not a graph object") } if (byname != "auto" && !is.logical(byname)) { stop("`byname' must be \"auto\", or logical") } nonamed <- is.named(g1) + is.named(g2) if (byname == "auto") { byname <- nonamed == 2 if (nonamed == 1) { warning("One, but not both graphs are named, not using vertex names") } } else if (byname && nonamed != 2) { stop("Some graphs are not named") } if (byname) { uninames <- unique(c(V(g1)$name, V(g2)$name)) if (vcount(g1) < length(uninames)) { g1 <- g1 + setdiff(uninames, V(g1)$name) } if (vcount(g2) < length(uninames)) { g2 <- g2 + setdiff(uninames, V(g2)$name) } if (any(uninames != V(g1)$name)) { g1 <- permute.vertices(g1, match(V(g1)$name, uninames)) } if (any(uninames != V(g2)$name)) { g2 <- permute.vertices(g2, match(V(g2)$name, uninames)) } } edgemaps <- (length(list.edge.attributes(g1)) != 0 || length(list.edge.attributes(g2)) != 0) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_compose", g1, g2, edgemaps, PACKAGE="igraph") maps <- list(res$edge_map1, res$edge_map2) res <- res$graph ## We might need to rename all attributes graphs <- list(g1, g2) graph.attributes(res) <- rename.attr.if.needed("g", graphs) if (byname) { vertex.attributes(res) <- rename.attr.if.needed("v", graphs, vcount(res), ignore="name") V(res)$name <- uninames } else { vertex.attributes(res) <- rename.attr.if.needed("v", graphs, vcount(res)) } if (edgemaps) { edge.attributes(res) <- rename.attr.if.needed("e", graphs, ecount(res), maps2=maps) } res } "%c%" <- function(x,y) { graph.compose(x,y) } edge <- function(...) { structure(list(...), class="igraph.edge") } edges <- edge vertex <- function(...) { structure(list(...), class="igraph.vertex") } vertices <- vertex path <- function(...) { structure(list(...), class="igraph.path") } `+.igraph` <- function(e1, e2) { if (!is.igraph(e1) && is.igraph(e2)) { tmp <- e1 e1 <- e2 e2 <- tmp } if (is.igraph(e2) && is.named(e1) && is.named(e2)) { ## Union of graphs res <- graph.union(e1, e2) } else if (is.igraph(e2)) { ## Disjoint union of graphs res <- graph.disjoint.union(e1,e2) } else if ("igraph.edge" %in% class(e2)) { ## Adding edges, possibly with attributes ## Non-named arguments define the edges if (is.null(names(e2))) { toadd <- unlist(e2, recursive=FALSE) attr <- list() } else { toadd <- unlist(e2[names(e2)==""]) attr <- e2[names(e2)!=""] } res <- add.edges(e1, as.igraph.vs(e1, toadd), attr=attr) } else if ("igraph.vertex" %in% class(e2)) { ## Adding vertices, possibly with attributes ## If there is a single unnamed argument, that contains the vertex names wn <- which(names(e2)=="") if (length(wn)==1) { names(e2)[wn] <- "name" } else if (is.null(names(e2))) { ## No names at all, everything is a vertex name e2 <- list(name=unlist(e2, recursive=FALSE)) } else if (length(wn)==0) { ## If there are no non-named arguments, we are fine } else { ## Otherwise, all unnamed arguments are collected and used as ## vertex names nn <- unlist(e2[wn], recursive=FALSE) e2 <- c(list(name=nn), e2[names(e2)!=""]) } la <- unique(sapply(e2, length)) res <- add.vertices(e1, la, attr=e2) } else if ("igraph.path" %in% class(e2)) { ## Adding edges along a path, possibly with attributes ## Non-named arguments define the edges if (is.null(names(e2))) { toadd <- unlist(e2, recursive=FALSE) attr <- list() } else { toadd <- unlist(e2[names(e2)==""]) attr <- e2[names(e2)!=""] } toadd <- as.igraph.vs(e1, toadd) lt <- length(toadd) if (lt >= 2) { toadd <- c(toadd[1], rep(toadd[2:(lt-1)], each=2), toadd[lt]) res <- add.edges(e1, toadd, attr=attr) } else { res <- e1 } } else if (is.numeric(e2) && length(e2)==1) { ## Adding some isolate vertices res <- add.vertices(e1, e2) } else if (is.character(e2)) { ## Adding named vertices res <- add.vertices(e1, length(e2), name=e2) } else { stop("Cannot add unknown type to igraph graph") } res } `-.igraph` <- function(e1, e2) { if (missing(e2)) { stop("Non-numeric argument to negation operator") } if (is.igraph(e2)) { res <- graph.difference(e1, e2) } else if ("igraph.vertex" %in% class(e2)) { res <- delete.vertices(e1, unlist(e2, recursive=FALSE)) } else if ("igraph.edge" %in% class(e2)) { res <- delete.edges(e1, unlist(e2, recursive=FALSE)) } else if ("igraph.path" %in% class(e2)) { todel <- unlist(e2, recursive=FALSE) lt <- length(todel) if (lt >= 2) { todel <- paste(todel[-lt], todel[-1], sep="|") res <- delete.edges(e1, todel) } else { res <- e1 } } else if ("igraph.vs" %in% class(e2)) { res <- delete.vertices(e1, e2) } else if ("igraph.es" %in% class(e2)) { res <- delete.edges(e1, e2) } else if (is.numeric(e2) || is.character(e2)) { res <- delete.vertices(e1, e2) } else { stop("Cannot substract unknown type from igraph graph") } res } igraph/R/structural.properties.R0000644000176000001440000010212512325365704016513 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Structural properties ################################################################### diameter <- function(graph, directed=TRUE, unconnected=TRUE, weights=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_diameter", graph, as.logical(directed), as.logical(unconnected), weights, PACKAGE="igraph") } get.diameter <- function(graph, directed=TRUE, unconnected=TRUE, weights=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_get_diameter", graph, as.logical(directed), as.logical(unconnected), weights, PACKAGE="igraph") res + 1 } farthest.nodes <- function(graph, directed=TRUE, unconnected=TRUE, weights=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_farthest_points", graph, as.logical(directed), as.logical(unconnected), weights, PACKAGE="igraph") res[1:2] <- res[1:2] + 1 res } average.path.length <- function(graph, directed=TRUE, unconnected=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_average_path_length", graph, as.logical(directed), as.logical(unconnected), PACKAGE="igraph") } degree <- function(graph, v=V(graph), mode=c("all", "out", "in", "total"), loops=TRUE, normalized=FALSE){ if (!is.igraph(graph)) { stop("Not a graph object") } v <- as.igraph.vs(graph, v) mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_degree", graph, v-1, as.numeric(mode), as.logical(loops), PACKAGE="igraph") if (normalized) { res <- res / (vcount(graph)-1) } if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- V(graph)$name[v] } res } degree.distribution <- function(graph, cumulative=FALSE, ...) { if (!is.igraph(graph)) { stop("Not a graph object") } cs <- degree(graph, ...) hi <- hist(cs, -1:max(cs), plot=FALSE)$density if (!cumulative) { res <- hi } else { res <- rev(cumsum(rev(hi))) } res } shortest.paths <- function(graph, v=V(graph), to=V(graph), mode=c("all", "out", "in"), weights=NULL, algorithm=c("automatic", "unweighted", "dijkstra", "bellman-ford", "johnson")) { if (!is.igraph(graph)) { stop("Not a graph object") } v <- as.igraph.vs(graph, v) to <- as.igraph.vs(graph, to) mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) algorithm <- igraph.match.arg(algorithm) algorithm <- switch(algorithm, "automatic"=0, "unweighted"=1, "dijkstra"=2, "bellman-ford"=3, "johnson"=4) if (is.null(weights)) { if ("weight" %in% list.edge.attributes(graph)) { weights <- as.numeric(E(graph)$weight) } } else { if (length(weights)==1 && is.na(weights)) { weights <- NULL } else { weights <- as.numeric(weights) } } if (! is.null(weights) && algorithm==1) { weights <- NULL warning("Unweighted algorithm chosen, weights ignored") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_shortest_paths", graph, v-1, to-1, as.numeric(mode), weights, as.numeric(algorithm), PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { rownames(res) <- V(graph)$name[v] colnames(res) <- V(graph)$name[to] } res } get.shortest.paths <- function(graph, from, to=V(graph), mode=c("out", "all", "in"), weights=NULL, output=c("vpath", "epath", "both"), predecessors=FALSE, inbound.edges=FALSE) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) output <- igraph.match.arg(output) output <- switch(output, "vpath"=0, "epath"=1, "both"=2) if (is.null(weights)) { if ("weight" %in% list.edge.attributes(graph)) { weights <- as.numeric(E(graph)$weight) } } else { if (length(weights)==1 && is.na(weights)) { weights <- NULL } else { weights <- as.numeric(weights) } } to <- as.igraph.vs(graph, to)-1 on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_get_shortest_paths", graph, as.igraph.vs(graph, from)-1, to, as.numeric(mode), as.numeric(length(to)), weights, as.numeric(output), as.logical(predecessors), as.logical(inbound.edges), PACKAGE="igraph") if (!is.null(res$vpath)) { res$vpath <- lapply(res$vpath, function(x) x+1) } if (!is.null(res$epath)) { res$epath <- lapply(res$epath, function(x) x+1) } if (!is.null(res$predecessors)) { res$predecessors <- res$predecessors + 1 } if (!is.null(res$inbound_edges)) { res$inbound_edges <- res$inbound_edges + 1 } res } get.all.shortest.paths <- function(graph, from, to=V(graph), mode=c("out", "all", "in"), weights=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) if (is.null(weights)) { if ("weight" %in% list.edge.attributes(graph)) { weights <- as.numeric(E(graph)$weight) } } else { if (length(weights)==1 && is.na(weights)) { weights <- NULL } else { weights <- as.numeric(weights) } } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) if (is.null(weights)) { res <- .Call("R_igraph_get_all_shortest_paths", graph, as.igraph.vs(graph, from)-1, as.igraph.vs(graph, to)-1, as.numeric(mode), PACKAGE="igraph") } else { res <- .Call("R_igraph_get_all_shortest_paths_dijkstra", graph, as.igraph.vs(graph, from)-1, as.igraph.vs(graph, to)-1, weights, as.numeric(mode), PACKAGE="igraph") } res } subcomponent <- function(graph, v, mode=c("all", "out", "in")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_subcomponent", graph, as.igraph.vs(graph, v)-1, as.numeric(mode), PACKAGE="igraph") res+1 } subgraph <- function(graph, v) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_subgraph", graph, as.igraph.vs(graph, v)-1, PACKAGE="igraph") } betweenness <- function(graph, v=V(graph), directed=TRUE, weights=NULL, nobigint=TRUE, normalized=FALSE) { if (!is.igraph(graph)) { stop("Not a graph object") } v <- as.igraph.vs(graph, v) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_betweenness", graph, v-1, as.logical(directed), weights, as.logical(nobigint), PACKAGE="igraph") if (normalized) { vc <- vcount(graph) if (is.directed(graph) && directed) { res <- res / ( vc*vc-3*vc+2) } else { res <- 2*res / ( vc*vc-3*vc+2) } } if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- V(graph)$name[v] } res } transitivity <- function(graph, type=c("undirected", "global", "globalundirected", "localundirected", "local", "average", "localaverage", "localaverageundirected", "barrat", "weighted"), vids=NULL, weights=NULL, isolates=c("NaN", "zero")) { if (!is.igraph(graph)) { stop("Not a graph object") } type <- igraph.match.arg(type) type <- switch(type, "undirected"=0, "global"=0, "globalundirected"=0, "localundirected"=1, "local"=1, "average"=2, "localaverage"=2, "localaverageundirected"=2, "barrat"=3, "weighted"=3) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } isolates <- igraph.match.arg(isolates) isolates <- as.double(switch(isolates, "nan"=0, "zero"=1)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) if (type==0) { .Call("R_igraph_transitivity_undirected", graph, isolates, PACKAGE="igraph") } else if (type==1) { if (is.null(vids)) { .Call("R_igraph_transitivity_local_undirected_all", graph, isolates, PACKAGE="igraph") } else { vids <- as.igraph.vs(graph, vids)-1 .Call("R_igraph_transitivity_local_undirected", graph, vids, isolates, PACKAGE="igraph") } } else if (type==2) { .Call("R_igraph_transitivity_avglocal_undirected", graph, isolates, PACKAGE="igraph") } else if (type==3) { if (is.null(vids)) { vids <- V(graph) } vids <- as.igraph.vs(graph, vids)-1 if (is.null(weights)) { .Call("R_igraph_transitivity_local_undirected", graph, vids, isolates, PACKAGE="igraph") } else { .Call("R_igraph_transitivity_barrat", graph, vids, weights, isolates, PACKAGE="igraph") } } } ## Generated by stimulus now ## graph.laplacian <- function(graph, normalized=FALSE) { ## if (!is.igraph(graph)) { ## stop("Not a graph object") ## } ## on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) ## .Call("R_igraph_laplacian", graph, as.logical(normalized), ## PACKAGE="igraph") ## } ## OLD implementation ## graph.laplacian <- function(graph, normalized=FALSE) { ## if (!is.igraph(graph)) { ## stop("Not a graph object") ## } ## if (is.directed(graph)) { ## warning("Laplacian of a directed graph???") ## } ## M <- get.adjacency(graph) ## if (!normalized) { ## M <- structure(ifelse(M>0, -1, 0), dim=dim(M)) ## diag(M) <- degree(graph) ## } else { ## deg <- degree(graph) ## deg <- outer(deg, deg, "*") ## M <- structure(ifelse(M>0, -1/deg, 0)) ## diag(M) <- 1 ## } ## M ## } ## Structural holes a'la Burt, code contributed by ## Jeroen Bruggeman ## constraint.orig <- function(graph, nodes=V(graph), attr=NULL) { ## if (!is.igraph(graph)) { ## stop("Not a graph object") ## } ## idx <- degree(graph) != 0 ## A <- get.adjacency(graph, attr=attr) ## A <- A[idx, idx] ## n <- sum(idx) ## one <- c(rep(1,n)) ## CZ <- A + t(A) ## cs <- CZ %*% one # degree of vertices ## ics <- 1/cs ## CS <- ics %*% t(one) # 1/degree of vertices ## P <- CZ * CS #intermediate result: proportionate tie strengths ## PSQ <- P%*%P #sum paths of length two ## P.bi <- as.numeric(P>0) #exclude paths to non-contacts (& reflexive): ## PC <- (P + (PSQ*P.bi))^2 #dyadic constraint ## ci <- PC %*% one #overall constraint ## dim(ci) <- NULL ## ci2 <- numeric(vcount(graph)) ## ci2[idx] <- ci ## ci2[!idx] <- NaN ## ci2[nodes+1] ## } ## Newest implementation, hopefully correct, there is a C implementation ## now so we don't need this ## constraint.old <- function(graph, nodes=V(graph)) { ## if (!is.igraph(graph)) { ## stop("Not a graph object") ## } ## nodes <- as.numeric(nodes) ## res <- numeric(length(nodes)) ## deg <- degree(graph, mode="all", loops=FALSE) ## not <- function(i, v) v[ v!=i ] ## for (a in seq(along=nodes)) { ## i <- nodes[a] ## first <- not(i, neighbors(graph, i, mode="all")) ## first <- unique(first) ## for (b in seq(along=first)) { ## j <- first[b] ## ## cj is the contribution of j ## cj <- are.connected(graph, i, j) / deg[i+1] ## cj <- cj + are.connected(graph, j, i) / deg[i+1] ## second <- not(i, not(j, neighbors(graph, j, mode="all"))) ## for (c in seq(along=second)) { ## q <- second[c] ## cj <- cj + are.connected(graph, i, q) / deg[q+1] / deg[i+1] ## cj <- cj + are.connected(graph, q, i) / deg[q+1] / deg[i+1] ## } ## ## Ok, we have the total contribution of j ## res[a] <- res[a] + cj*cj ## } ## } ## if (!is.directed(graph)) { ## res <- res/4 ## } ## res ## } constraint <- function(graph, nodes=V(graph), weights=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } nodes <- as.igraph.vs(graph, nodes) if (is.null(weights)) { if ("weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_constraint", graph, nodes-1, as.numeric(weights), PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- V(graph)$name[nodes] } res } reciprocity <- function(graph, ignore.loops=TRUE, mode=c("default", "ratio")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- switch(igraph.match.arg(mode), 'default'=0, 'ratio'=1) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_reciprocity", graph, as.logical(ignore.loops), as.numeric(mode), PACKAGE="igraph") } rewire <- function(graph, mode=c("simple", "loops"), niter=100) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "simple"=0, "loops"=1) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_rewire", graph, as.numeric(niter), as.numeric(mode), PACKAGE="igraph") } bonpow.dense <- function(graph, nodes=V(graph), loops=FALSE, exponent=1, rescale=FALSE, tol=1e-7){ if (!is.igraph(graph)) { stop("Not a graph object") } d <- get.adjacency(graph) if (!loops) { diag(d) <- 0 } n <- vcount(graph) id <- matrix(0,nrow=n,ncol=n) diag(id) <- 1 # ev <- apply(solve(id-exponent*d,tol=tol)%*%d,1,sum) ev <- solve(id-exponent*d, tol=tol) %*% apply(d,1,sum) if(rescale) { ev <- ev/sum(ev) } else { ev <- ev*sqrt(n/sum((ev)^2)) } ev[as.numeric(nodes)] } bonpow.sparse <- function(graph, nodes=V(graph), loops=FALSE, exponent=1, rescale=FALSE, tol=1e-07) { ## remove loops if requested if (!loops) { graph <- simplify(graph, remove.multiple=FALSE, remove.loops=TRUE) } vg <- vcount(graph) ## sparse adjacency matrix d <- get.adjacency(graph, sparse=TRUE) ## sparse identity matrix id <- Matrix::Diagonal(vg) ## solve it ev <- Matrix::solve(id - exponent * d, degree(graph, mode="out"), tol=tol) if (rescale) { ev <- ev/sum(ev) } else { ev <- ev * sqrt(vcount(graph)/sum((ev)^2)) } ev[as.numeric(nodes)] } bonpow <- function(graph, nodes=V(graph), loops=FALSE, exponent=1, rescale=FALSE, tol=1e-7, sparse=TRUE){ nodes <- as.igraph.vs(graph, nodes) if (sparse) { res <- bonpow.sparse(graph, nodes, loops, exponent, rescale, tol) } else { res <- bonpow.dense(graph, nodes, loops, exponent, rescale, tol) } if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", nodes) } res } alpha.centrality.dense <- function(graph, nodes=V(graph), alpha=1, loops=FALSE, exo=1, weights=NULL, tol=1e-7) { if (!is.igraph(graph)) { stop("Not a graph object") } exo <- rep(exo, length=vcount(graph)) exo <- matrix(exo, ncol=1) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { ## weights == NULL and there is a "weight" edge attribute attr <- "weight" } else if (is.null(weights)) { ## weights == NULL, but there is no "weight" edge attribute attr <- NULL } else if (is.character(weights) && length(weights)==1) { ## name of an edge attribute, nothing to do attr <- "weight" } else if (any(!is.na(weights))) { ## weights != NULL and weights != rep(NA, x) graph <- set.edge.attribute(graph, "weight", value=as.numeric(weights)) attr <- "weight" } else { ## weights != NULL, but weights == rep(NA, x) attr <- NULL } d <- t(get.adjacency(graph, attr=attr, sparse=FALSE)) if (!loops) { diag(d) <- 0 } n <- vcount(graph) id <- matrix(0, nrow=n, ncol=n) diag(id) <- 1 ev <- solve(id-alpha*d, tol=tol) %*% exo ev[as.numeric(nodes)] } alpha.centrality.sparse <- function(graph, nodes=V(graph), alpha=1, loops=FALSE, exo=1, weights=NULL, tol=1e-7) { if (!is.igraph(graph)) { stop("Not a graph object") } vc <- vcount(graph) if (!loops) { graph <- simplify(graph, remove.multiple=FALSE, remove.loops=TRUE) } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { ## weights == NULL and there is a "weight" edge attribute weights <- E(graph)$weight } else if (is.null(weights)) { ## weights == NULL, but there is no "weight" edge attribute weights <- rep(1, ecount(graph)) } else if (is.character(weights) && length(weights)==1) { weights <- get.edge.attribute(graph, weights) } else if (any(!is.na(weights))) { weights <- as.numeric(weights) } else { ## weights != NULL, but weights == rep(NA, x) weights <- rep(1, ecount(graph)) } el <- get.edgelist(graph, names=FALSE) M <- Matrix::sparseMatrix(dims=c(vc, vc), i=el[,2], j=el[,1], x=weights) M <- as(M, "dgCMatrix") ## Create an identity matrix M2 <- Matrix::sparseMatrix(dims=c(vc, vc), i=1:vc, j=1:vc, x=rep(1, vc)) M2 <- as(M2, "dgCMatrix") ## exo exo <- cbind(rep(exo, length=vc)) ## Solve the equation M3 <- M2-alpha*M r <- Matrix::solve(M3, tol=tol, exo) r[ as.numeric(nodes)] } alpha.centrality <- function(graph, nodes=V(graph), alpha=1, loops=FALSE, exo=1, weights=NULL, tol=1e-7, sparse=TRUE) { nodes <- as.igraph.vs(graph, nodes) if (sparse) { res <- alpha.centrality.sparse(graph, nodes, alpha, loops, exo, weights, tol) } else { res <- alpha.centrality.dense(graph, nodes, alpha, loops, exo, weights, tol) } if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name", nodes) } res } graph.density <- function(graph, loops=FALSE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_density", graph, as.logical(loops), PACKAGE="igraph") } neighborhood.size <- function(graph, order, nodes=V(graph), mode=c("all", "out", "in")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_neighborhood_size", graph, as.igraph.vs(graph, nodes)-1, as.numeric(order), as.numeric(mode), PACKAGE="igraph") } neighborhood <- function(graph, order, nodes=V(graph), mode=c("all", "out", "in")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_neighborhood", graph, as.igraph.vs(graph, nodes)-1, as.numeric(order), as.numeric(mode), PACKAGE="igraph") res <- lapply(res, function(x) x+1) res } graph.neighborhood <- function(graph, order, nodes=V(graph), mode=c("all", "out", "in")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_neighborhood_graphs", graph, as.igraph.vs(graph, nodes)-1, as.numeric(order), as.numeric(mode), PACKAGE="igraph") res } graph.coreness <- function(graph, mode=c("all", "out", "in")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_coreness", graph, as.numeric(mode), PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name") } res } topological.sort <- function(graph, mode=c("out", "all", "in")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_topological_sorting", graph, as.numeric(mode), PACKAGE="igraph") res+1 } girth <- function(graph, circle=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_girth", graph, as.logical(circle), PACKAGE="igraph") } is.loop <- function(graph, eids=E(graph)) { if (!is.igraph(graph)) { stop("Not a graph object"); } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_is_loop", graph, as.igraph.es(graph, eids)-1, PACKAGE="igraph") } is.multiple <- function(graph, eids=E(graph)) { if (!is.igraph(graph)) { stop("Not a graph object"); } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_is_multiple", graph, as.igraph.es(graph, eids)-1, PACKAGE="igraph") } count.multiple <- function(graph, eids=E(graph)) { if (!is.igraph(graph)) { stop("Not a graph object"); } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_count_multiple", graph, as.igraph.es(graph, eids)-1, PACKAGE="igraph") } graph.bfs <- function(graph, root, neimode=c("out", "in", "all", "total"), unreachable=TRUE, restricted=NULL, order=TRUE, rank=FALSE, father=FALSE, pred=FALSE, succ=FALSE, dist=FALSE, callback=NULL, extra=NULL, rho=parent.frame()) { if (!is.igraph(graph)) { stop("Not a graph object"); } if (length(root)==1) { root <- as.igraph.vs(graph, root)-1 roots <- NULL } else { roots <- as.igraph.vs(graph, root)-1 root <- 0 # ignored anyway } neimode <- switch(igraph.match.arg(neimode), "out"=1, "in"=2, "all"=3, "total"=3) unreachable <- as.logical(unreachable) if (!is.null(restricted)) { restricted <- as.igraph.vs(graph, restricted) } if (!is.null(callback)) { callback <- as.function(callback) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_bfs", graph, root, roots, neimode, unreachable, restricted, as.logical(order), as.logical(rank), as.logical(father), as.logical(pred), as.logical(succ), as.logical(dist), callback, extra, rho, PACKAGE="igraph") if (order) res$order <- res$order+1 if (rank) res$rank <- res$rank+1 if (father) res$father <- res$father+1 if (pred) res$pred <- res$pred+1 if (succ) res$succ <- res$succ+1 res } graph.dfs <- function(graph, root, neimode=c("out", "in", "all", "total"), unreachable=TRUE, order=TRUE, order.out=FALSE, father=FALSE, dist=FALSE, in.callback=NULL, out.callback=NULL, extra=NULL, rho=parent.frame()) { if (!is.igraph(graph)) { stop("Not a graph object"); } root <- as.igraph.vs(graph, root)-1 neimode <- switch(igraph.match.arg(neimode), "out"=1, "in"=2, "all"=3, "total"=3) unreachable <- as.logical(unreachable) if (!is.null(in.callback)) { in.callback <- as.function(in.callback) } if (!is.null(out.callback)) { out.callback <- as.function(out.callback) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_dfs", graph, root, neimode, unreachable, as.logical(order), as.logical(order.out), as.logical(father), as.logical(dist), in.callback, out.callback, extra, rho, PACKAGE="igraph") if (order) res$order <- res$order+1 if (order.out) res$order.out <- res$order.out+1 if (father) res$father <- res$father+1 res } edge.betweenness <- function(graph, e=E(graph), directed=TRUE, weights=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } e <- as.igraph.es(graph, e) directed <- as.logical(directed) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_edge_betweenness", graph, directed, weights, PACKAGE="igraph") res[as.numeric(e)] } edge.betweenness.estimate <- function(graph, e=E(graph), directed=TRUE, cutoff, weights=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } e <- as.igraph.es(graph, e) directed <- as.logical(directed) cutoff <- as.numeric(cutoff) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_edge_betweenness_estimate", graph, directed, cutoff, weights, PACKAGE="igraph") res[as.numeric(e)] } clusters <- function(graph, mode=c("weak", "strong")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } mode <- switch(igraph.match.arg(mode), "weak"=1, "strong"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_clusters", graph, mode, PACKAGE="igraph") res$membership <- res$membership + 1 res } unfold.tree <- function(graph, mode=c("all", "out", "in", "total"), roots) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) roots <- as.igraph.vs(graph, roots)-1 on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_unfold_tree", graph, mode, roots, PACKAGE="igraph") res } closeness <- function(graph, vids=V(graph), mode=c("out", "in", "all", "total"), weights=NULL, normalized=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids) mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, "total"=3) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } normalized <- as.logical(normalized) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_closeness", graph, vids-1, mode, weights, normalized, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- V(graph)$name[vids] } res } graph.laplacian <- function(graph, normalized=FALSE, weights=NULL, sparse=getIgraphOpt("sparsematrices")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } normalized <- as.logical(normalized) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } sparse <- as.logical(sparse) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_laplacian", graph, normalized, weights, sparse, PACKAGE="igraph") if (sparse) { res <- igraph.i.spMatrix(res) } if (getIgraphOpt("add.vertex.names") && is.named(graph)) { rownames(res) <- colnames(res) <- V(graph)$name } res } is.matching <- function(graph, matching, types=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(types) && "type" %in% list.vertex.attributes(graph)) { types <- V(graph)$type } if (!is.null(types)) { types <- as.logical(types) } matching <- as.igraph.vs(graph, matching, na.ok=TRUE)-1 matching[ is.na(matching) ] <- -1 on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_matching", graph, types, matching, PACKAGE="igraph") res } is.maximal.matching <- function(graph, matching, types=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(types) && "type" %in% list.vertex.attributes(graph)) { types <- V(graph)$type } if (!is.null(types)) { types <- as.logical(types) } matching <- as.igraph.vs(graph, matching, na.ok=TRUE)-1 matching[ is.na(matching) ] <- -1 on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_is_maximal_matching", graph, types, matching, PACKAGE="igraph") res } maximum.bipartite.matching <- function(graph, types=NULL, weights=NULL, eps=.Machine$double.eps) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(types) && "type" %in% list.vertex.attributes(graph)) { types <- V(graph)$type } if (!is.null(types)) { types <- as.logical(types) } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } eps <- as.numeric(eps) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_maximum_bipartite_matching", graph, types, weights, eps, PACKAGE="igraph") res$matching[ res$matching==0 ] <- NA if (getIgraphOpt("add.vertex.names") && is.named(graph)) { res$matching <- V(graph)$name[res$matching] names(res$matching) <- V(graph)$name } res } igraph/R/scg.R0000644000176000001440000002346312251656216012672 0ustar ripleyusers # IGraph R package # Copyright (C) 2010-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### get.stochastic <- function(graph, column.wise=FALSE, sparse=getIgraphOpt("sparsematrices")) { if (!is.igraph(graph)) { stop("Not a graph object") } column.wise <- as.logical(column.wise) if (length(column.wise) != 1) { stop("`column.wise' must be a logical scalar") } sparse <- as.logical(sparse) if (length(sparse) != 1) { stop("`sparse' must be a logical scalar") } on.exit(.Call("R_igraph_finalizer", PACKAGE="igraph")) if (sparse) { res <- .Call("R_igraph_get_stochastic_sparsemat", graph, column.wise, PACKAGE="igraph") res <- igraph.i.spMatrix(res) } else { res <- .Call("R_igraph_get_stochastic", graph, column.wise, PACKAGE="igraph") } if (getIgraphOpt("add.vertex.names") && is.named(graph)) { rownames(res) <- colnames(res) <- V(graph)$name } res } scgGrouping <- function(V, nt, mtype=c("symmetric", "laplacian", "stochastic"), algo=c("optimum", "interv_km", "interv", "exact_scg"), p=NULL, maxiter=100) { V <- as.matrix(structure(as.double(V), dim=dim(V))) groups <- as.numeric(nt) mtype <- switch(igraph.match.arg(mtype), "symmetric"=1, "laplacian"=2, "stochastic"=3) algo <- switch(igraph.match.arg(algo), "optimum"=1, "interv_km"=2, "interv"=3, "exact_scg"=4) if (!is.null(p)) p <- as.numeric(p) maxiter <- as.integer(maxiter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_scg_grouping", V, as.integer(nt[1]), if (length(nt)==1) NULL else nt, mtype, algo, p, maxiter, PACKAGE="igraph") res } scgSemiProjectors <- function(groups, mtype=c("symmetric", "laplacian", "stochastic"), p=NULL, norm=c("row", "col"), sparse=getIgraphOpt("sparsematrices")) { # Argument checks groups <- as.numeric(groups)-1 mtype <- switch(igraph.match.arg(mtype), "symmetric"=1, "laplacian"=2, "stochastic"=3) if (!is.null(p)) p <- as.numeric(p) norm <- switch(igraph.match.arg(norm), "row"=1, "col"=2) sparse <- as.logical(sparse) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_scg_semiprojectors", groups, mtype, p, norm, sparse, PACKAGE="igraph") if (sparse) { res$L <- igraph.i.spMatrix(res$L) res$R <- igraph.i.spMatrix(res$R) } res } scg <- function(X, ev, nt, groups=NULL, mtype=c("symmetric", "laplacian", "stochastic"), algo=c("optimum", "interv_km", "interv", "exact_scg"), norm=c("row", "col"), direction=c("default", "left", "right"), evec=NULL, p=NULL, use.arpack=FALSE, maxiter=300, sparse=getIgraphOpt("sparsematrices"), output=c("default", "matrix", "graph"), semproj=FALSE, epairs=FALSE, stat.prob=FALSE) UseMethod("scg") scg.igraph <- function(X, ev, nt, groups=NULL, mtype=c("symmetric", "laplacian", "stochastic"), algo=c("optimum", "interv_km", "interv", "exact_scg"), norm=c("row", "col"), direction=c("default", "left", "right"), evec=NULL, p=NULL, use.arpack=FALSE, maxiter=300, sparse=getIgraphOpt("sparsematrices"), output=c("default", "matrix", "graph"), semproj=FALSE, epairs=FALSE, stat.prob=FALSE) { myscg(graph=X, matrix=NULL, sparsemat=NULL, ev=ev, nt=nt, groups=groups, mtype=mtype, algo=algo, norm=norm, direction=direction, evec=evec, p=p, use.arpack=use.arpack, maxiter=maxiter, sparse=sparse, output=output, semproj=semproj, epairs=epairs, stat.prob=stat.prob) } scg.matrix <- function(X, ev, nt, groups=NULL, mtype=c("symmetric", "laplacian", "stochastic"), algo=c("optimum", "interv_km", "interv", "exact_scg"), norm=c("row", "col"), direction=c("default", "left", "right"), evec=NULL, p=NULL, use.arpack=FALSE, maxiter=300, sparse=getIgraphOpt("sparsematrices"), output=c("default", "matrix", "graph"), semproj=FALSE, epairs=FALSE, stat.prob=FALSE) { myscg(graph=NULL, matrix=X, sparsemat=NULL, ev=ev, nt=nt, groups=groups, mtype=mtype, algo=algo, norm=norm, direction=direction, evec=evec, p=p, use.arpack=use.arpack, maxiter=maxiter, sparse=sparse, output=output, semproj=semproj, epairs=epairs, stat.prob=stat.prob) } scg.Matrix <- function(X, ev, nt, groups=NULL, mtype=c("symmetric", "laplacian", "stochastic"), algo=c("optimum", "interv_km", "interv", "exact_scg"), norm=c("row", "col"), direction=c("default", "left", "right"), evec=NULL, p=NULL, use.arpack=FALSE, maxiter=300, sparse=getIgraphOpt("sparsematrices"), output=c("default", "matrix", "graph"), semproj=FALSE, epairs=FALSE, stat.prob=FALSE) { myscg(graph=NULL, matrix=NULL, sparsemat=X, ev=ev, nt=nt, groups=groups, mtype=mtype, algo=algo, norm=norm, direction=direction, evec=evec, p=p, use.arpack=use.arpack, maxiter=maxiter, sparse=sparse, output=output, semproj=semproj, epairs=epairs, stat.prob=stat.prob) } myscg <- function(graph, matrix, sparsemat, ev, nt, groups=NULL, mtype=c("symmetric", "laplacian", "stochastic"), algo=c("optimum", "interv_km", "interv", "exact_scg"), norm=c("row", "col"), direction=c("default", "left", "right"), evec=NULL, p=NULL, use.arpack=FALSE, maxiter=300, sparse=getIgraphOpt("sparsematrices"), output=c("default", "matrix", "graph"), semproj=FALSE, epairs=FALSE, stat.prob=FALSE) { ## Argument checks if (!is.null(graph)) { stopifnot(is.igraph(graph)) } if (!is.null(matrix)) { stopifnot(is.matrix(matrix)) } if (!is.null(sparsemat)) { stopifnot(inherits(sparsemat, "Matrix")) } if (!is.null(sparsemat)) { sparsemat <- as(sparsemat, "dgCMatrix") } ev <- as.numeric(as.integer(ev)) nt <- as.numeric(as.integer(nt)) if (!is.null(groups)) groups <- as.numeric(groups) mtype <- igraph.match.arg(mtype) algo <- switch(igraph.match.arg(algo), "optimum"=1, "interv_km"=2, "interv"=3, "exact_scg"=4) if (!is.null(groups)) { storage.mode(groups) <- "double" } use.arpack <- as.logical(use.arpack) maxiter <- as.integer(maxiter) sparse <- as.logical(sparse) output <- switch(igraph.match.arg(output), "default"=1, "matrix"=2, "graph"=3) semproj <- as.logical(semproj) epairs <- as.logical(epairs) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) if (mtype=="symmetric") { if (!is.null(evec)) { storage.mode(evec) <- "double" } res <- .Call("R_igraph_scg_adjacency", graph, matrix, sparsemat, ev, nt, algo, evec, groups, use.arpack, maxiter, sparse, output, semproj, epairs, PACKAGE="igraph") } else if (mtype=="laplacian") { norm <- switch(igraph.match.arg(norm), "row"=1, "col"=2) if (!is.null(evec)) { storage.mode(evec) <- "complex" } direction <- switch(igraph.match.arg(direction), "default"=1, "left"=2, "right"=3) res <- .Call("R_igraph_scg_laplacian", graph, matrix, sparsemat, ev, nt, algo, norm, direction, evec, groups, use.arpack, maxiter, sparse, output, semproj, epairs, PACKAGE="igraph") } else if (mtype=="stochastic") { norm <- switch(igraph.match.arg(norm), "row"=1, "col"=2) if (!is.null(evec)) { storage.mode(evec) <- "complex" } if (!is.null(p)) { storage.mode(p) <- "double" } stat.prob <- as.logical(stat.prob) res <- .Call("R_igraph_scg_stochastic", graph, matrix, sparsemat, ev, nt, algo, norm, evec, groups, p, use.arpack, maxiter, sparse, output, semproj, epairs, stat.prob, PACKAGE="igraph") } if (!is.null(res$Xt) && class(res$Xt) == "igraph.tmp.sparse") { res$Xt <- igraph.i.spMatrix(res$Xt) } if (!is.null(res$L) && class(res$L) == "igraph.tmp.sparse") { res$L <- igraph.i.spMatrix(res$L) } if (!is.null(res$R) && class(res$R) == "igraph.tmp.sparse") { res$R <- igraph.i.spMatrix(res$R) } res } igraph/R/minimum.spanning.tree.R0000644000176000001440000000364612240234657016343 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### minimum.spanning.tree <- function(graph, weights=NULL, algorithm=NULL, ...) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(algorithm)) { if (!is.null(weights) || "weight" %in% list.edge.attributes(graph)) { algorithm <- "prim" } else { algorithm <- "unweighted" } } if (algorithm=="unweighted") { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_minimum_spanning_tree_unweighted", graph, PACKAGE="igraph") } else if (algorithm=="prim") { if (is.null(weights) && ! "weight" %in% list.edge.attributes(graph)) { stop("edges weights must be supplied for Prim's algorithm") } else if (is.null(weights)) { weights <- E(graph)$weight } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_minimum_spanning_tree_prim", graph, as.numeric(weights), PACKAGE="igraph") } else { stop("Invalid algorithm") } } igraph/R/structure.info.R0000644000176000001440000000233112240234657015076 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### are.connected <- function(graph, v1, v2) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_are_connected", graph, as.igraph.vs(graph, v1)-1, as.igraph.vs(graph, v2)-1, PACKAGE="igraph") } igraph/R/epi.R0000644000176000001440000001212712273332127012662 0ustar ripleyusers # IGraph R package # Copyright (C) 2014 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### time_bins <- function(x, middle=TRUE) UseMethod("time_bins") time_bins.sir <- function(x, middle=TRUE) { sir <- x if (!inherits(sir, "sir")) { stop("This is not an SIR model output") } big.time <- unlist(sapply(sir, "[[", "times")) medlen <- median(sapply(lapply(sir, "[[", "times"), length)) ## Adhoc use of Freedman-Diaconis binwidth; rescale time accordingly. w <- 2 * IQR(big.time) / (medlen^(1/3)) minbt <- min(big.time) ; maxbt <- max(big.time) res <- seq(minbt, maxbt, length.out=ceiling((maxbt - minbt)/w)) if (middle) { res <- (res[-1] + res[-length(res)]) / 2 } res } median.sir <- function(x, na.rm=FALSE) { sir <- x if (!inherits(sir, "sir")) { stop("This is not an SIR model output") } times <- unlist(sapply(sir, "[[", "times")) big.N.NS <- unlist(sapply(sir, "[[", "NS")) big.N.NI <- unlist(sapply(sir, "[[", "NI")) big.N.NR <- unlist(sapply(sir, "[[", "NR")) time.bin <- cut(times, time_bins(sir, middle=FALSE), include.lowest=TRUE) NS <- tapply(big.N.NS, time.bin, median, na.rm=na.rm) NI <- tapply(big.N.NI, time.bin, median, na.rm=na.rm) NR <- tapply(big.N.NR, time.bin, median, na.rm=na.rm) list(NS=NS, NI=NI, NR=NR) } quantile.sir <- function(x, comp=c("NI", "NS", "NR"), prob, ...) { sir <- x if (!inherits(sir, "sir")) { stop("This is not an SIR model output") } comp <- toupper(igraph.match.arg(comp)) times <- unlist(sapply(sir, "[[", "times")) big.N <- unlist(sapply(sir, function(x) { x[[comp]] })) time.bin <- cut(times, time_bins(sir, middle=FALSE), include.lowest=TRUE) res <- lapply(prob, function(pp) { tapply(big.N, time.bin, function(x) { quantile(x, prob=pp) }) }) if (length(res) == 1) { res <- res[[1]] } res } # R function to plot compartment total curves from simul.net.epi . # Inputs: sim.res := list of simulated network SIR processes # comp := compartment (i.e., "NS", "NI", or "NR") # q := vector of lower and upper quantiles, resp # cols := char vector of colors for lines, median, and quantiles, resp. # Outputs: None. Just produces the plot of all compartment curves, # with median and quantiles. plot.sir <- function(x, comp=c("NI", "NS", "NR"), median=TRUE, quantiles=c(0.1, 0.9), color=NULL, median_color=NULL, quantile_color=NULL, lwd.median=2, lwd.quantile=2, lty.quantile=3, xlim=NULL, ylim=NULL, xlab="Time", ylab=NULL, ...) { sir <- x if (!inherits(sir, "sir")) { stop("This is not an SIR model output") } comp <- toupper(igraph.match.arg(comp)) if (!all(quantiles >= 0 & quantiles <= 1)) { stop("Quantiles should be in [0,1]") } if (is.null(color)) { color <- c(NI="skyblue", NS="pink", NR="palegoldenrod")[comp] } if (is.null(median_color)) { median_color <- c(NI="blue", NS="red", NR="gold")[comp] } if (is.null(quantile_color)) { quantile_color <- c(NI="blue", NS="red", NR="gold")[comp] } quantile_color <- rep(quantile_color, length.out=length(quantiles)) ns <- length(sir) if (is.null(xlim)) { xlim <- c(0, max(sapply(sir, function(x) max(x$times)))) } if (is.null(ylim)) { ylim <- c(0, max(sapply(sir, function(x) max(x[[comp]])))) } ## Generate the plot, first with individual curves, and then ## adding median and quantile curves. if (is.null(ylab)) { if (comp == "NI") { ylab <- expression(N[I](t)) } if (comp == "NR") { ylab <- expression(N[R](t)) } if (comp == "NS") { ylab <- expression(N[S](t)) } } # Plot the stochastic curves individually. plot(0, 0, type="n", xlim=xlim, ylim=ylim, xlab=xlab, ylab=ylab, ...) lapply(seq_along(sir), function(i) { lines(sir[[i]]$time, sir[[i]][[comp]], col=color[1]) }) # Plot the median and quantiles. if (median || length(quantiles) > 0) { time.bin <- time_bins(sir, middle=TRUE) } if (median) { lines(time.bin, median(sir)[[comp]], type="l", lwd=lwd.median, col=median_color) } for (i in seq_along(quantiles)) { my.ql <- quantile(sir, comp, quantiles[i]) lines(time.bin, my.ql, type="l", lty=lty.quantile, lwd=lwd.quantile, col=quantile_color[i]) } invisible() } igraph/R/hrg.R0000644000176000001440000003767012263024035012672 0ustar ripleyusers # IGraph R package # Copyright (C) 2011-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### hrg.fit <- function(graph, hrg=NULL, start=FALSE, steps=0) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(hrg)) { hrg <- list(left=c(), right=c(), prob=c(), edges=c(), vertices=c()) } hrg <- lapply(hrg[c("left","right","prob","edges","vertices")], as.numeric) start <- as.logical(start) steps <- as.integer(steps) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hrg_fit", graph, hrg, start, steps, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { res$names <- V(graph)$name } class(res) <- "igraphHRG" res } hrg.consensus <- function(graph, hrg=NULL, start=FALSE, num.samples=10000) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(hrg)) { hrg <- list(left=c(), right=c(), prob=c(), edges=c(), vertices=c()) } hrg <- lapply(hrg[c("left","right","prob","edges","vertices")], as.numeric) start <- as.logical(start) num.samples <- as.integer(num.samples) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hrg_consensus", graph, hrg, start, num.samples, PACKAGE="igraph") res$parents <- res$parents + 1 res <- list(consensus=list(parents=res$parents, weights=res$weights), hrg=res$hrg) class(res$consensus) <- "igraphHRGConsensus" class(res$hrg) <- "igraphHRG" if (getIgraphOpt("add.vertex.names") && is.named(graph)) { res$hrg$names <- V(graph)$name res$consensus$names <- V(graph)$name } res } hrg.predict <- function(graph, hrg=NULL, start=FALSE, num.samples=10000, num.bins=25) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(hrg)) { hrg <- list(left=c(), right=c(), prob=c(), edges=c(), vertices=c()) } hrg <- lapply(hrg[c("left","right","prob","edges","vertices")], as.numeric) start <- as.logical(start) num.samples <- as.integer(num.samples) num.bins <- as.integer(num.bins) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_hrg_predict", graph, hrg, start, num.samples, num.bins, PACKAGE="igraph") res$edges <- matrix(res$edges, ncol=2, byrow=TRUE) class(res$hrg) <- "igraphHRG" res } as.igraph <- function(x, ...) UseMethod("as.igraph") as.igraph.igraphHRG <- function(x, ...) { ovc <- length(x$left)+1L ivc <- ovc-1L ll <- ifelse(x$left < 0, -x$left + ovc, x$left + 1) rr <- ifelse(x$right < 0, -x$right + ovc, x$right + 1) edges <- c(rbind(seq_len(ivc)+ovc, ll), rbind(seq_len(ivc)+ovc, rr)) res <- graph(edges) V(res)$name <- c(if (!is.null(x$names)) x$names else as.character(1:ovc), paste0("g", 1:ivc)) V(res)$prob <- c(rep(NA, ovc), x$prob) res$name <- "Fitted HRG" res } buildMerges <- function(object) { ## Build a merge matrix. This is done by a post-order ## traversal of the tree. S <- numeric() vcount <- length(object$left)+1 nMerge <- vcount-1 merges <- matrix(0, nrow=vcount-1, ncol=3) mptr <- 1 S[length(S)+1] <- -1 prev <- NULL while (length(S) != 0) { curr <- S[length(S)] ## coming from parent? going left if possible. if (is.null(prev) || (prev < 0 && object$left[-prev] == curr) || (prev < 0 && object$right[-prev] == curr)) { if (curr < 0) { S <- c(S, object$left[-curr]) } ## coming from left child? going right } else if (curr < 0 && object$left[-curr] == prev) { S <- c(S, object$right[-curr]) ## coming from right child? going up } else { if (curr < 0) { merges[mptr,] <- c(object$left[-curr], object$right[-curr], curr) mptr <- mptr + 1 } S <- S[-length(S)] } prev <- curr } merges } as.dendrogram.igraphHRG <- function(object, hang=0.01, ...) { nMerge <- length(object$left) merges <- buildMerges(object) .memberDend <- function(x) { r <- attr(x,"x.member") if(is.null(r)) { r <- attr(x,"members") if(is.null(r)) r <- 1:1 } r } oHgt <- 1:nrow(merges) hMax <- oHgt[length(oHgt)] mynames <- if (is.null(object$names)) 1:(nMerge+1) else object$names z <- list() for (k in 1:nMerge) { x <- merges[k,1:2] if (any(neg <- x >= 0)) { h0 <- if (hang < 0) 0 else max(0, oHgt[k] - hang * hMax) } if (all(neg)) { # two leaves zk <- as.list(x+1) attr(zk, "members") <- 2L attr(zk, "midpoint") <- 1/2 # mean( c(0,1) ) objlabels <- mynames[x+1] attr(zk[[1]], "label") <- objlabels[1] attr(zk[[2]], "label") <- objlabels[2] attr(zk[[1]], "members") <- attr(zk[[2]], "members") <- 1L attr(zk[[1]], "height") <- attr(zk[[2]], "height") <- h0 attr(zk[[1]], "leaf") <- attr(zk[[2]], "leaf") <- TRUE } else if (any(neg)) { # one leaf, one node X <- paste0("g", -x) isL <- x[1] >= 0 zk <- if (isL) list(x[1]+1, z[[X[2]]]) else list(z[[X[1]]], x[2]+1) attr(zk, "members") <- attr(z[[X[1+isL]]], "members") + 1L attr(zk, "midpoint") <- (.memberDend(zk[[1]]) + attr(z[[X[1+isL]]], "midpoint"))/2 attr(zk[[2 - isL]], "members") <- 1L attr(zk[[2 - isL]], "height") <- h0 attr(zk[[2 - isL]], "label") <- mynames[x[2 - isL]+1] attr(zk[[2 - isL]], "leaf") <- TRUE } else { #two nodes X <- paste0("g", -x) zk <- list(z[[X[1]]], z[[X[2]]]) attr(zk, "members") <- attr(z[[X[1]]], "members") + attr(z[[X[2]]], "members") attr(zk, "midpoint") <- (attr(z[[X[1]]], "members") + attr(z[[X[1]]], "midpoint") + attr(z[[X[2]]], "midpoint"))/2 } attr(zk, "height") <- oHgt[k] z[[k <- paste0("g", -merges[k,3])]] <- zk } z <- z[[k]] class(z) <- "dendrogram" z } as.hclust.igraphHRG <- function(x, ...) { merge3 <- buildMerges(x) ## We need to rewrite the merge matrix, because hclust assumes ## that group ids are assigned in the order of the merges map <- order(-merge3[,3]) merge <- merge3[,1:2] gs <- which(merge < 0) merge[ gs] <- map[ -merge[gs] ] merge[-gs] <- -merge[-gs]-1 ## To get the ordering, we need to recode the merge matrix again, ## without using group ids. Here the right node is merged _into_ ## the left node. map2 <- numeric(nrow(merge)) mergeInto <- merge for (i in 1:nrow(merge)) { mr <- mergeInto[i,] mr[mr > 0] <- -map2[mr[mr>0]] mergeInto[i,] <- -mr map2[i] <- -mr[1] } n <- nrow(merge)+1 hcass <- .C("igraphhcass2", n=as.integer(n), ia=as.integer(mergeInto[,1]), ib=as.integer(mergeInto[,2]), order=integer(n), iia=integer(n), iib=integer(n), PACKAGE="igraph") mynames <- if (is.null(x$names)) 1:n else x$names res <- list(merge=merge, height=1:nrow(merge), order=hcass$order, labels=mynames, method=NA_character_, dist.method=NA_character_) class(res) <- "hclust" res } asPhylo.igraphHRG <- function(x, ...) { require(ape, quietly=TRUE) ovc <- length(x$left)+1L ivc <- ovc-1L ll <- ifelse(x$left < 0, -x$left + ovc, x$left + 1) rr <- ifelse(x$right < 0, -x$right + ovc, x$right + 1) edge <- matrix(rbind(seq_len(ivc)+ovc, ll, seq_len(ivc)+ovc, rr), ncol=2, byrow=TRUE) edge.length <- rep(0.5, nrow(edge)) labels <- if (is.null(x$names)) 1:ovc else x$names obj <- list(edge=edge, edge.length=edge.length/2, tip.label=labels, Nnode=ivc) class(obj) <- "phylo" reorder(obj) } dendPlot.igraphHRG <- function(x, mode=getIgraphOpt("dend.plot.type"), ...) { if (mode=="auto") { value <- tryCatch(suppressWarnings(library("ape", character.only=TRUE, logical.return=TRUE, warn.conflicts=FALSE, quietly=TRUE, pos="package:base")), error=function(e) e) mode <- if (value) "phylo" else "hclust" } if (mode=="hclust") { hrgPlotHclust(x, ...) } else if (mode=="dendrogram") { hrgPlotDendrogram(x, ...) } else if (mode=="phylo") { hrgPlotPhylo(x, ...) } } hrgPlotHclust <- function(x, rect=0, colbar=rainbow(rect), hang=.01, ann=FALSE, main="", sub="", xlab="", ylab="", ...) { hc <- as.hclust(x) ret <- plot(hc, hang=hang, ann=ann, main=main, sub=sub, xlab=xlab, ylab=ylab, ...) if (rect > 0) { rect.hclust(hc, k=rect, border=colbar) } invisible(ret) } hrgPlotDendrogram <- function(x, ...) { plot(as.dendrogram(x), ...) } hrgPlotPhylo <- function(x, colbar=rainbow(11, start=.7, end=.1), edge.color=NULL, use.edge.length=FALSE, ...) { vc <- length(x$left)+1 phy <- asPhylo(x) br <- seq(0,1,length=length(colbar)) ; br[1] <- -1 cc <- as.integer(cut(x$prob[phy$edge[,1] - vc], breaks=br)) if (is.null(edge.color)) { edge.color <- colbar[cc] } plot(phy, edge.color=edge.color, use.edge.length=use.edge.length, ...) } print.igraphHRG <- function(x, type=c("auto", "tree", "plain"), level=3, ...) { type <- igraph.match.arg(type) if (type=="auto") { type <- if (length(x$left <= 100)) "tree" else "plain" } if (type=="tree") { return(print1.igraphHRG(x, level=level, ...)) } else { return(print2.igraphHRG(x, ...)) } } print1.igraphHRG <- function(x, level=3, ...) { cat(sep="", "Hierarchical random graph, at level ", level, ":\n") ## Depth of printed top of the dendrogram .depth <- function(b, l) { l[2] <- max(l[2], nchar(format(x$prob[b], digits=2))) if (l[1]==level) { return(l) } if (x$left[b] < 0 && x$right[b] < 0) { l1 <- .depth(-x$left[b], c(l[1]+1, l[2])) l2 <- .depth(-x$right[b], c(l[1]+1, l[2])) return(pmax(l1,l2)) } if (x$left[b] < 0) { return(.depth(-x$left[b], c(l[1]+1, l[2]))) } if (x$right[b] < 0) { return(.depth(-x$right[b], c(l[1]+1, l[2]))) } return(l) } cs <- .depth(1, c(1, 0)) pw <- cs[2] cs <- cs[1] * 3 vw <- nchar(as.character(length(x$left)+1)) sp <- paste(collapse="", rep(" ", cs+pw+2+2)) nn <- if (is.null(x$names)) seq_len(length(x$left)+1) else x$names ## Function to collect all individual vertex children .children <- function(b) { res <- c() if (x$left[b] < 0) { res <- c(res, .children(-x$left[b])) } else { res <- c(x$left[b]+1, res) } if (x$right[b] < 0) { res <- c(res, .children(-x$right[b])) } else { res <- c(x$right[b]+1, res) } return(res) } ## Recursive printing .plot <- function(b, l, ind = "") { if (b != 1) { he <- format(paste(sep="", ind, "'- g", b), width=cs) ind <- paste(" ", ind) } else { he <- format(paste(sep="", ind, "g", b), width=cs) } ## whether to go left and/or right gol <- x$left[b] < 0 && l < level gor <- x$right[b] < 0 && l < level ## the children to print ch1 <- character() if (!gol && x$left[b] < 0) { ch1 <- c(ch1, paste(sep="", "g", -x$left[b])) } if (!gor && x$right[b] < 0) { ch1 <- c(ch1, paste(sep="", "g", -x$right[b])) } ch2 <- numeric() if (!gol) { if (x$left[b] < 0) { ch2 <- c(ch2, .children(-x$left[b])) } if (x$left[b] >= 0) { ch2 <- c(ch2, x$left[b] + 1) } } if (!gor) { if (x$right[b] < 0) { ch2 <- c(ch2, .children(-x$right[b])) } if (x$right[b] >= 0) { ch2 <- c(ch2, x$right[b] + 1) } } ## print this line ch2 <- as.character(nn[ch2]) lf <- gsub(" ", "x", format(ch2, width=vw), fixed=TRUE) lf <- paste(collapse=" ", lf) lf <- strwrap(lf, width=getOption("width") - cs - pw - 3 - 2) lf <- gsub("x", " ", lf, fixed=TRUE) if (length(lf) > 1) { lf <- c(lf[1], paste(sp, lf[-1])) lf <- paste(collapse="\n", lf) } op <- paste(sep="", format(he, width=cs), " p=", format(x$prob[b], digits=2, width=pw, justify="left"), " ", paste(collapse=" ", lf)) cat(op, fill=TRUE) ## recursive call if (x$left[b] < 0 && l < level) .plot(-x$left[b], l+1, ind) if (x$right[b] < 0 && l < level) .plot(-x$right[b], l+1, ind) } ## Do it if (length(x$left) > 0) .plot(b=1, l=1) invisible(x) } print2.igraphHRG <- function(x, ...) { cat("Hierarchical random graph:\n") bw <- ceiling(log10(length(x$left)+1))+1 p <- format(x$prob, digits=1) pw <- 4 + max(nchar(p)) nn <- if (is.null(x$names)) seq_len(length(x$left)+1) else x$names op <- sapply(seq_along(x$left), function(i) { lc <- if (x$left[i] < 0) { paste(sep="", "g", -x$left[i]) } else { nn[x$left[i]+1] } rc <- if (x$right[i] < 0) { paste(sep="", "g", -x$right[i]) } else { nn[x$right[i]+1] } paste(sep="", format(paste(sep="", "g", i), width=bw), format(paste(sep="", " p=", p[i]), width=pw), "-> ", lc, " ", rc) }) op <- format(op, justify="left") cat(op, sep=" ", fill=TRUE) invisible(x) } # TODO: print as a tree print.igraphHRGConsensus <- function(x, ...) { cat("HRG consensus tree:\n") n <- length(x$parents) - length(x$weights) mn <- if (is.null(x$names)) seq_len(n) else x$names id <- c(mn, paste(sep="", "g", seq_along(x$weights))) ch <- tapply(id, x$parents, c)[-1] # first is zero bw <- nchar(as.character(length(x$weights))) vw <- max(nchar(id)) op <- sapply(seq_along(x$weights), function(i) { mych <- format(ch[[i]], width=vw) if (length(ch[[i]])*(vw+1) + bw + 4 > getOption("width")) { mych <- gsub(" ", "x", mych, fixed=TRUE) mych <- paste(collapse=" ", mych) pref <- paste(collapse="", rep(" ", bw+5)) mych <- strwrap(mych, width=getOption("width") - bw - 4, initial="", prefix=pref) mych <- gsub("x", " ", mych, fixed=TRUE) mych <- paste(collapse="\n", mych) } else { mych <- paste(collapse=" ", mych) } paste(sep="", "g", format(i, width=bw), " -> ", mych) }) if (max(nchar(op)) < (getOption("width")-4)/2) { op <- format(op, justify="left") cat(op, sep=" ", fill=TRUE) } else { cat(op, sep="\n") } invisible(x) } " ## How to print HRGs? B-1 p=0 '- B-3 p=1 6 '- B-7 p=1 2 '- B-5 p=1 1 5 '- B-6 p=1 7 '- B-2 p=1 4 '- B-4 p=1 3 8 ## The same at levels 1, 2 and 3: B-1 p=0 B-3 B-6 6 2 1 5 7 4 3 8 B-1 p=0 '+ B-3 p=1 B-7 6 2 1 5 '+ B-6 p=1 B-2 7 4 3 8 B-1 p=0 '- B-3 p=1 6 '+ B-7 p=1 B-5 2 1 5 '- B-6 p=1 7 '+ B-2 p=1 B-4 4 3 8 ## This can be tedious if the graph is big, as we always have n-1 ## internal nodes, we can restrict ourselves to (say) level 3 by default. ## Another possibility is to order the lines according to the group ids. B-1 p=0 B-3 B-6 B-2 p=1 B-4 4 B-3 p=1 B-7 6 B-4 p=1 3 8 B-5 p=1 1 5 B-6 p=1 B-2 7 B-7 p=1 B-5 2 " igraph/R/auto.R.in0000644000176000001440000000000012240234657013450 0ustar ripleyusersigraph/R/plot.R0000644000176000001440000007140712325356537013102 0ustar ripleyusers # IGraph R package # Copyright (C) 2003-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### plot.igraph <- function(x, # SPECIFIC: ##################################### axes=FALSE, add=FALSE, xlim=c(-1,1), ylim=c(-1,1), mark.groups=list(), mark.shape=1/2, mark.col=rainbow(length(mark.groups), alpha=0.3), mark.border=rainbow(length(mark.groups), alpha=1), mark.expand=15, ...) { graph <- x if (!is.igraph(graph)) { stop("Not a graph object") } ################################################################ ## Visual parameters params <- i.parse.plot.params(graph, list(...)) vertex.size <- 1/200 * params("vertex", "size") label.family <- params("vertex", "label.family") label.font <- params("vertex", "label.font") label.cex <- params("vertex", "label.cex") label.degree <- params("vertex", "label.degree") label.color <- params("vertex", "label.color") label.dist <- params("vertex", "label.dist") labels <- params("vertex", "label") shape <- igraph.check.shapes(params("vertex", "shape")) edge.color <- params("edge", "color") edge.width <- params("edge", "width") edge.lty <- params("edge", "lty") arrow.mode <- params("edge", "arrow.mode") edge.labels <- params("edge", "label") loop.angle <- params("edge", "loop.angle") edge.label.font <- params("edge", "label.font") edge.label.family <- params("edge", "label.family") edge.label.cex <- params("edge", "label.cex") edge.label.color <- params("edge", "label.color") elab.x <- params("edge", "label.x") elab.y <- params("edge", "label.y") arrow.size <- params("edge", "arrow.size")[1] arrow.width <- params("edge", "arrow.width")[1] curved <- params("edge", "curved") if (is.function(curved)) { curved <- curved(graph) } layout <- params("plot", "layout") margin <- params("plot", "margin") margin <- rep(margin, length=4) rescale <- params("plot", "rescale") asp <- params("plot", "asp") frame <- params("plot", "frame") main <- params("plot", "main") sub <- params("plot", "sub") xlab <- params("plot", "xlab") ylab <- params("plot", "ylab") # the new style parameters can't do this yet arrow.mode <- i.get.arrow.mode(graph, arrow.mode) ################################################################ ## create the plot maxv <- max(vertex.size) if (rescale) { # norm layout to (-1, 1) layout <- layout.norm(layout, -1, 1, -1, 1) xlim <- c(xlim[1]-margin[2]-maxv, xlim[2]+margin[4]+maxv) ylim <- c(ylim[1]-margin[1]-maxv, ylim[2]+margin[3]+maxv) } if (!add) { plot(0, 0, type="n", xlab=xlab, ylab=ylab, xlim=xlim, ylim=ylim, axes=axes, frame=frame, asp=asp, main=main, sub=sub) } ################################################################ ## Mark vertex groups if (!is.list(mark.groups) && is.numeric(mark.groups)) { mark.groups <- list(mark.groups) } mark.shape <- rep(mark.shape, length=length(mark.groups)) mark.border <- rep(mark.border, length=length(mark.groups)) mark.col <- rep(mark.col, length=length(mark.groups)) mark.expand <- rep(mark.expand, length=length(mark.groups)) for (g in seq_along(mark.groups)) { v <- mark.groups[[g]] if (length(vertex.size)==1) { vs <- vertex.size } else { vs <- rep(vertex.size, length=vcount(graph))[v] } igraph.polygon(layout[v,,drop=FALSE], vertex.size=vs, expand.by=mark.expand[g]/200, shape=mark.shape[g], col=mark.col[g], border=mark.border[g]) } ################################################################ ## calculate position of arrow-heads el <- get.edgelist(graph, names=FALSE) loops.e <- which(el[,1] == el[,2]) nonloops.e <- which(el[,1] != el[,2]) loops.v <- el[,1] [loops.e] loop.labels <- edge.labels[loops.e] loop.labx <- if (is.null(elab.x)) { rep(NA, length(loops.e)) } else { elab.x[loops.e] } loop.laby <- if (is.null(elab.y)) { rep(NA, length(loops.e)) } else { elab.y[loops.e] } edge.labels <- edge.labels[nonloops.e] elab.x <- if (is.null(elab.x)) NULL else elab.x[nonloops.e] elab.y <- if (is.null(elab.y)) NULL else elab.y[nonloops.e] el <- el[nonloops.e,,drop=FALSE] edge.coords <- matrix(0, nrow=nrow(el), ncol=4) edge.coords[,1] <- layout[,1][ el[,1] ] edge.coords[,2] <- layout[,2][ el[,1] ] edge.coords[,3] <- layout[,1][ el[,2] ] edge.coords[,4] <- layout[,2][ el[,2] ] if ( length(unique(shape)) == 1) { ## same vertex shape for all vertices ec <- .igraph.shapes[[ shape[1] ]]$clip(edge.coords, el, params=params, end="both") } else { ## different vertex shapes, do it by "endpoint" shape <- rep(shape, length=vcount(graph)) ec <- edge.coords ec[,1:2] <- t(sapply(seq(length=nrow(el)), function(x) { .igraph.shapes[[ shape[el[x,1]] ]]$clip(edge.coords[x,,drop=FALSE], el[x,,drop=FALSE], params=params, end="from") })) ec[,3:4] <- t(sapply(seq(length=nrow(el)), function(x) { .igraph.shapes[[ shape[el[x,2]] ]]$clip(edge.coords[x,,drop=FALSE], el[x,,drop=FALSE], params=params, end="to") })) } x0 <- ec[,1] ; y0 <- ec[,2] ; x1 <- ec[,3] ; y1 <- ec[,4] ################################################################ ## add the loop edges if (length(loops.e) > 0) { ec <- edge.color if (length(ec)>1) { ec <- ec[loops.e] } point.on.cubic.bezier <- function(cp, t) { c <- 3 * (cp[2,] - cp[1,]) b <- 3 * (cp[3,] - cp[2,]) - c a <- cp[4,] - cp[1,] - c - b t2 <- t*t; t3 <- t*t*t a*t3 + b*t2 + c*t + cp[1,] } compute.bezier <- function(cp, points) { dt <- seq(0, 1, by=1/(points-1)) sapply(dt, function(t) point.on.cubic.bezier(cp, t)) } plot.bezier <- function(cp, points, color, width, arr, lty, arrow.size, arr.w) { p <- compute.bezier( cp, points ) polygon(p[1,], p[2,], border=color, lwd=width, lty=lty) if (arr==1 || arr==3) { igraph.Arrows(p[1,ncol(p)-1], p[2,ncol(p)-1], p[1,ncol(p)], p[2,ncol(p)], sh.col=color, h.col=color, size=arrow.size, sh.lwd=width, h.lwd=width, open=FALSE, code=2, width=arr.w) } if (arr==2 || arr==3) { igraph.Arrows(p[1,2], p[2,2], p[1,1], p[2,1], sh.col=color, h.col=color, size=arrow.size, sh.lwd=width, h.lwd=width, open=FALSE, code=2, width=arr.w) } } loop <- function(x0, y0, cx=x0, cy=y0, color, angle=0, label=NA, width=1, arr=2, lty=1, arrow.size=arrow.size, arr.w=arr.w, lab.x, lab.y) { rad <- angle center <- c(cx,cy) cp <- matrix( c(x0,y0, x0+.4,y0+.2, x0+.4,y0-.2, x0,y0), ncol=2, byrow=TRUE) phi <- atan2(cp[,2]-center[2], cp[,1]-center[1]) r <- sqrt((cp[,1]-center[1])**2 + (cp[,2]-center[2])**2) phi <- phi + rad cp[,1] <- cx+r*cos(phi) cp[,2] <- cy+r*sin(phi) plot.bezier(cp, 50, color, width, arr=arr, lty=lty, arrow.size=arrow.size, arr.w=arr.w) if (is.language(label) || !is.na(label)) { lx <- x0+.3 ly <- y0 phi <- atan2(ly-center[2], lx-center[1]) r <- sqrt((lx-center[1])**2 + (ly-center[2])**2) phi <- phi + rad lx <- cx+r*cos(phi) ly <- cy+r*sin(phi) if (!is.na(lab.x)) { lx <- lab.x } if (!is.na(lab.y)) { ly <- lab.y } text(lx, ly, label, col=edge.label.color, font=edge.label.font, family=edge.label.family, cex=edge.label.cex) } } ec <- edge.color if (length(ec)>1) { ec <- ec[loops.e] } vs <- vertex.size if (length(vertex.size)>1) { vs <- vs[loops.v] } ew <- edge.width if (length(edge.width)>1) { ew <- ew[loops.e] } la <- loop.angle if (length(loop.angle)>1) { la <- la[loops.e] } lty <- edge.lty if (length(edge.lty)>1) { lty <- lty[loops.e] } arr <- arrow.mode if (length(arrow.mode)>1) { arr <- arrow.mode[loops.e] } asize <- arrow.size if (length(arrow.size)>1) { asize <- arrow.size[loops.e] } xx0 <- layout[loops.v,1] + cos(la) * vs yy0 <- layout[loops.v,2] - sin(la) * vs mapply(loop, xx0, yy0, color=ec, angle=-la, label=loop.labels, lty=lty, width=ew, arr=arr, arrow.size=asize, arr.w=arrow.width, lab.x=loop.labx, lab.y=loop.laby) } ################################################################ ## non-loop edges if (length(x0) != 0) { if (length(edge.color)>1) { edge.color <- edge.color[nonloops.e] } if (length(edge.width)>1) { edge.width <- edge.width[nonloops.e] } if (length(edge.lty)>1) { edge.lty <- edge.lty[nonloops.e] } if (length(arrow.mode)>1) { arrow.mode <- arrow.mode[nonloops.e] } if (length(arrow.size)>1) { arrow.size <- arrow.size[nonloops.e] } if (length(curved)>1) { curved <- curved[nonloops.e] } if (length(unique(arrow.mode))==1) { lc <-igraph.Arrows(x0, y0, x1, y1, h.col=edge.color, sh.col=edge.color, sh.lwd=edge.width, h.lwd=1, open=FALSE, code=arrow.mode[1], sh.lty=edge.lty, h.lty=1, size=arrow.size, width=arrow.width, curved=curved) lc.x <- lc$lab.x lc.y <- lc$lab.y } else { ## different kinds of arrows drawn separately as 'arrows' cannot ## handle a vector as the 'code' argument curved <- rep(curved, length=ecount(graph))[nonloops.e] lc.x <- lc.y <- numeric(length(curved)) for (code in 0:3) { valid <- arrow.mode==code if (!any(valid)) { next } ec <- edge.color ; if (length(ec)>1) { ec <- ec[valid] } ew <- edge.width ; if (length(ew)>1) { ew <- ew[valid] } el <- edge.lty ; if (length(el)>1) { el <- el[valid] } lc <- igraph.Arrows(x0[valid], y0[valid], x1[valid], y1[valid], code=code, sh.col=ec, h.col=ec, sh.lwd=ew, h.lwd=1, h.lty=1, sh.lty=el, open=FALSE, size=arrow.size, width=arrow.width, curved=curved[valid]) lc.x[valid] <- lc$lab.x lc.y[valid] <- lc$lab.y } } if (!is.null(elab.x)) { lc.x <- ifelse(is.na(elab.x), lc.x, elab.x) } if (!is.null(elab.y)) { lc.y <- ifelse(is.na(elab.y), lc.y, elab.y) } text(lc.x, lc.y, labels=edge.labels, col=edge.label.color, family=edge.label.family, font=edge.label.font, cex=edge.label.cex) } rm(x0, y0, x1, y1) ################################################################ # add the vertices if (length(unique(shape)) == 1) { .igraph.shapes[[ shape[1] ]]$plot(layout, params=params) } else { sapply(seq(length=vcount(graph)), function(x) { .igraph.shapes[[ shape[x] ]]$plot(layout[x,,drop=FALSE], v=x, params=params) }) } ################################################################ # add the labels par(xpd=TRUE) x <- layout[,1]+label.dist*cos(-label.degree)* (vertex.size+6*8*log10(nchar(labels)+1))/200 y <- layout[,2]+label.dist*sin(-label.degree)* (vertex.size+6*8*log10(nchar(labels)+1))/200 if (length(label.family)==1) { text(x, y, labels=labels, col=label.color, family=label.family, font=label.font, cex=label.cex) } else { if1 <- function(vect, idx) if (length(vect)==1) vect else vect[idx] sapply(seq_len(vcount(graph)), function(v) { text(x[v], y[v], labels=if1(labels, v), col=if1(label.color, v), family=if1(label.family, v), font=if1(label.font, v), cex=if1(label.cex, v)) }) } rm(x, y) invisible(NULL) } rglplot <- function(x, ...) UseMethod("rglplot", x) rglplot.igraph <- function(x, ...) { require(rgl) graph <- x if (!is.igraph(graph)) { stop("Not a graph object") } create.edge <- function(v1, v2, r1, r2, ec, ew, am, as) { ## these could also be parameters: aw <- 0.005*3*as # arrow width al <- 0.005*4*as # arrow length dist <- sqrt(sum((v2-v1)^2)) # distance of the centers if (am==0) { edge <- qmesh3d(c(-ew/2,-ew/2,dist,1, ew/2,-ew/2,dist,1, ew/2,ew/2,dist,1, -ew/2,ew/2,dist,1, -ew/2,-ew/2,0,1, ew/2,-ew/2,0,1, ew/2,ew/2,0,1, -ew/2,ew/2,0,1), c(1,2,3,4, 5,6,7,8, 1,2,6,5, 2,3,7,6, 3,4,8,7, 4,1,5,8)) } else if (am==1) { edge <- qmesh3d(c(-ew/2,-ew/2,dist,1, ew/2,-ew/2,dist,1, ew/2,ew/2,dist,1, -ew/2,ew/2,dist,1, -ew/2,-ew/2,al+r1,1, ew/2,-ew/2,al+r1,1, ew/2,ew/2,al+r1,1, -ew/2,ew/2,al+r1,1, -aw/2,-aw/2,al+r1,1, aw/2,-aw/2,al+r1,1, aw/2,aw/2,al+r1,1, -aw/2,aw/2,al+r1,1, 0,0,r1,1), c(1,2,3,4, 5,6,7,8, 1,2,6,5, 2,3,7,6, 3,4,8,7, 4,1,5,8, 9,10,11,12, 9,12,13,13, 9,10,13,13, 10,11,13,13, 11,12,13,13)) } else if (am==2) { box <- dist-r2-al edge <- qmesh3d(c(-ew/2,-ew/2,box,1, ew/2,-ew/2,box,1, ew/2,ew/2,box,1, -ew/2,ew/2,box,1, -ew/2,-ew/2,0,1, ew/2,-ew/2,0,1, ew/2,ew/2,0,1, -ew/2,ew/2,0,1, -aw/2,-aw/2,box,1, aw/2,-aw/2,box,1, aw/2,aw/2,box,1, -aw/2,aw/2,box,1, 0,0,box+al,1), c(1,2,3,4, 5,6,7,8, 1,2,6,5, 2,3,7,6, 3,4,8,7, 4,1,5,8, 9,10,11,12, 9,12,13,13, 9,10,13,13, 10,11,13,13, 11,12,13,13)) } else { edge <- qmesh3d(c(-ew/2,-ew/2,dist-al-r2,1, ew/2,-ew/2,dist-al-r2,1, ew/2,ew/2,dist-al-r2,1, -ew/2,ew/2,dist-al-r2,1, -ew/2,-ew/2,r1+al,1, ew/2,-ew/2,r1+al,1, ew/2,ew/2,r1+al,1, -ew/2,ew/2,r1+al,1, -aw/2,-aw/2,dist-al-r2,1, aw/2,-aw/2,dist-al-r2,1, aw/2,aw/2,dist-al-r2,1, -aw/2,aw/2,dist-al-r2,1, -aw/2,-aw/2,r1+al,1, aw/2,-aw/2,r1+al,1, aw/2,aw/2,r1+al,1, -aw/2,aw/2,r1+al,1, 0,0,dist-r2,1, 0,0,r1,1), c(1,2,3,4, 5,6,7,8, 1,2,6,5, 2,3,7,6, 3,4,8,7, 4,1,5,8, 9,10,11,12, 9,12,17,17, 9,10,17,17, 10,11,17,17, 11,12,17,17, 13,14,15,16, 13,16,18,18, 13,14,18,18, 14,15,18,18, 15,16,18,18)) } ## rotate and shift it to its position phi<- -atan2(v2[2]-v1[2],v1[1]-v2[1])-pi/2 psi<- acos((v2[3]-v1[3])/dist) rot1 <- rbind(c(1,0,0),c(0,cos(psi),sin(psi)), c(0,-sin(psi),cos(psi))) rot2 <- rbind(c(cos(phi),sin(phi),0),c(-sin(phi),cos(phi),0), c(0,0,1)) rot <- rot1 %*% rot2 edge <- transform3d(edge, rotationMatrix(matrix=rot)) edge <- transform3d(edge, translationMatrix(v1[1], v1[2], v1[3])) ## we are ready shade3d(edge, col=ec) } create.loop <- function(v, r, ec, ew, am, la, la2, as) { aw <- 0.005*3*as al <- 0.005*4*as wi <- aw*2 # size of the loop wi2 <- wi+aw-ew # size including the arrow heads hi <- al*2+ew*2 gap <- wi-2*ew if (am==0) { edge <- qmesh3d(c(-wi/2,-ew/2,0,1, -gap/2,-ew/2,0,1, -gap/2,ew/2,0,1, -wi/2,ew/2,0,1, -wi/2,-ew/2,hi-ew+r,1, -gap/2,-ew/2,hi-ew+r,1, -gap/2,ew/2,hi-ew+r,1, -wi/2,ew/2,hi-ew+r,1, wi/2,-ew/2,0,1, gap/2,-ew/2,0,1, gap/2,ew/2,0,1, wi/2,ew/2,0,1, wi/2,-ew/2,hi-ew+r,1, gap/2,-ew/2,hi-ew+r,1, gap/2,ew/2,hi-ew+r,1, wi/2,ew/2,hi-ew+r,1, -wi/2,-ew/2,hi+r,1, -wi/2,ew/2,hi+r,1, wi/2,-ew/2,hi+r,1, wi/2,ew/2,hi+r,1 ), c(1,2,3,4, 5,6,7,8, 1,2,6,5, 2,3,7,6, 3,4,8,7, 1,4,18,17, 9,10,11,12, 13,14,15,16, 9,10,14,13, 10,11,15,14, 11,12,16,15, 9,12,20,19, 5,13,19,17, 17,18,20,19, 8,16,20,18, 6,7,15,14 )) } else if (am==1 || am==2) { edge <- qmesh3d(c(-wi/2,-ew/2,r+al,1, -gap/2,-ew/2,r+al,1, -gap/2,ew/2,r+al,1, -wi/2,ew/2,r+al,1, -wi/2,-ew/2,hi-ew+r,1, -gap/2,-ew/2,hi-ew+r,1, -gap/2,ew/2,hi-ew+r,1, -wi/2,ew/2,hi-ew+r,1, wi/2,-ew/2,0,1, gap/2,-ew/2,0,1, gap/2,ew/2,0,1, wi/2,ew/2,0,1, wi/2,-ew/2,hi-ew+r,1, gap/2,-ew/2,hi-ew+r,1, gap/2,ew/2,hi-ew+r,1, wi/2,ew/2,hi-ew+r,1, -wi/2,-ew/2,hi+r,1, -wi/2,ew/2,hi+r,1, wi/2,-ew/2,hi+r,1, wi/2,ew/2,hi+r,1, # the arrow -wi2/2,-aw/2,r+al,1, -wi2/2+aw,-aw/2,r+al,1, -wi2/2+aw,aw/2,r+al,1, -wi2/2,aw/2,r+al,1, -wi2/2+aw/2,0,r,1 ), c(1,2,3,4, 5,6,7,8, 1,2,6,5, 2,3,7,6, 3,4,8,7, 1,4,18,17, 9,10,11,12, 13,14,15,16, 9,10,14,13, 10,11,15,14, 11,12,16,15, 9,12,20,19, 5,13,19,17, 17,18,20,19, 8,16,20,18, 6,7,15,14, # the arrow 21,22,23,24, 21,22,25,25, 22,23,25,25, 23,24,25,25, 21,24,25,25 )) } else if (am==3) { edge <- qmesh3d(c(-wi/2,-ew/2,r+al,1, -gap/2,-ew/2,r+al,1, -gap/2,ew/2,r+al,1, -wi/2,ew/2,r+al,1, -wi/2,-ew/2,hi-ew+r,1, -gap/2,-ew/2,hi-ew+r,1, -gap/2,ew/2,hi-ew+r,1, -wi/2,ew/2,hi-ew+r,1, wi/2,-ew/2,r+al,1, gap/2,-ew/2,r+al,1, gap/2,ew/2,r+al,1, wi/2,ew/2,r+al,1, wi/2,-ew/2,hi-ew+r,1, gap/2,-ew/2,hi-ew+r,1, gap/2,ew/2,hi-ew+r,1, wi/2,ew/2,hi-ew+r,1, -wi/2,-ew/2,hi+r,1, -wi/2,ew/2,hi+r,1, wi/2,-ew/2,hi+r,1, wi/2,ew/2,hi+r,1, # the arrows -wi2/2,-aw/2,r+al,1, -wi2/2+aw,-aw/2,r+al,1, -wi2/2+aw,aw/2,r+al,1, -wi2/2,aw/2,r+al,1, -wi2/2+aw/2,0,r,1, wi2/2,-aw/2,r+al,1, wi2/2-aw,-aw/2,r+al,1, wi2/2-aw,aw/2,r+al,1, wi2/2,aw/2,r+al,1, wi2/2-aw/2,0,r,1 ), c(1,2,3,4, 5,6,7,8, 1,2,6,5, 2,3,7,6, 3,4,8,7, 1,4,18,17, 9,10,11,12, 13,14,15,16, 9,10,14,13, 10,11,15,14, 11,12,16,15, 9,12,20,19, 5,13,19,17, 17,18,20,19, 8,16,20,18, 6,7,15,14, # the arrows 21,22,23,24, 21,22,25,25, 22,23,25,25, 23,24,25,25, 21,24,25,25, 26,27,28,29, 26,27,30,30, 27,28,30,30, 28,29,30,30, 26,29,30,30 )) } # rotate and shift to its position rot1 <- rbind(c(1,0,0),c(0,cos(la2),sin(la2)), c(0,-sin(la2),cos(la2))) rot2 <- rbind(c(cos(la),sin(la),0),c(-sin(la),cos(la),0), c(0,0,1)) rot <- rot1 %*% rot2 edge <- transform3d(edge, rotationMatrix(matrix=rot)) edge <- transform3d(edge, translationMatrix(v[1], v[2], v[3])) ## we are ready shade3d(edge, col=ec) } # Visual parameters params <- i.parse.plot.params(graph, list(...)) labels <- params("vertex", "label") label.color <- params("vertex", "label.color") label.font <- params("vertex", "label.font") label.degree <- params("vertex", "label.degree") label.dist <- params("vertex", "label.dist") vertex.color <- params("vertex", "color") vertex.size <- (1/200) * params("vertex", "size") loop.angle <- params("edge", "loop.angle") loop.angle2 <- params("edge", "loop.angle2") edge.color <- params("edge", "color") edge.width <- (1/200) * params("edge", "width") edge.labels <- params("edge","label") arrow.mode <- params("edge","arrow.mode") arrow.size <- params("edge","arrow.size") layout <- params("plot", "layout") rescale <- params("plot", "rescale") # the new style parameters can't do this yet arrow.mode <- i.get.arrow.mode(graph, arrow.mode) # norm layout to (-1, 1) if (ncol(layout)==2) { layout <- cbind(layout, 0) } if (rescale) { layout <- layout.norm(layout, -1, 1, -1, 1, -1, 1) } # add the edges, the loops are handled separately el <- get.edgelist(graph, names=FALSE) # It is faster this way par3d(skipRedraw=TRUE) # edges first for (i in seq(length=nrow(el))) { from <- el[i,1] to <- el[i,2] v1 <- layout[from,] v2 <- layout[to,] am <- arrow.mode; if (length(am)>1) { am <- am[i] } ew <- edge.width; if (length(ew)>1) { ew <- ew[i] } ec <- edge.color; if (length(ec)>1) { ec <- ec[i] } r1 <- vertex.size; if (length(r1)>1) { r1 <- r1[from] } r2 <- vertex.size; if (length(r2)>1) { r2 <- r2[to] } if (from!=to) { create.edge(v1,v2,r1,r2,ec,ew,am,arrow.size) } else { la <- loop.angle; if (length(la)>1) { la <- la[i] } la2 <- loop.angle2; if (length(la2)>1) { la2 <- la2[i] } create.loop(v1,r1,ec,ew,am,la,la2,arrow.size) } } # add the vertices if (length(vertex.size)==1) { vertex.size <- rep(vertex.size, nrow(layout)) } rgl.spheres(layout[,1], layout[,2], layout[,3], radius=vertex.size, col=vertex.color) # add the labels, 'l1' is a stupid workaround of a mysterious rgl bug labels[is.na(labels)] <- "" x <- layout[,1]+label.dist*cos(-label.degree)* (vertex.size+6*10*log10(nchar(labels)+1))/200 y <- layout[,2]+label.dist*sin(-label.degree)* (vertex.size+6*10*log10(nchar(labels)+1))/200 z <- layout[,3] l1 <- labels[1] labels[1] <- "" rgl.texts(x,y,z, labels, col=label.color, adj=0) rgl.texts(c(0,x[1]), c(0,y[1]), c(0,z[1]), c("",l1), col=c(label.color[1],label.color[1]), adj=0) edge.labels[is.na(edge.labels)] <- "" if (any(edge.labels != "")) { x0 <- layout[,1][el[,1]] x1 <- layout[,1][el[,2]] y0 <- layout[,2][el[,1]] y1 <- layout[,2][el[,2]] z0 <- layout[,3][el[,1]] z1 <- layout[,4][el[,2]] rgl.texts((x0+x1)/2, (y0+y1)/2, (z0+z1)/2, edge.labels, col=label.color) } # draw everything par3d(skipRedraw=FALSE) invisible(NULL) } # This is taken from the IDPmisc package, # slightly modified: code argument added igraph.Arrows <- function (x1, y1, x2, y2, code=2, size= 1, width= 1.2/4/cin, open=TRUE, sh.adj=0.1, sh.lwd=1, sh.col=if(is.R()) par("fg") else 1, sh.lty=1, h.col=sh.col, h.col.bo=sh.col, h.lwd=sh.lwd, h.lty=sh.lty, curved=FALSE) ## Author: Andreas Ruckstuhl, refined by Rene Locher ## Version: 2005-10-17 { cin <- size * par("cin")[2] width <- width * (1.2/4/cin) uin <- if (is.R()) 1/xyinch() else par("uin") x <- sqrt(seq(0, cin^2, length = floor(35 * cin) + 2)) delta <- sqrt(h.lwd)*par("cin")[2]*0.005 ## has been 0.05 x.arr <- c(-rev(x), -x) wx2 <- width * x^2 y.arr <- c(-rev(wx2 + delta), wx2 + delta) deg.arr <- c(atan2(y.arr, x.arr), NA) r.arr <- c(sqrt(x.arr^2 + y.arr^2), NA) ## backup bx1 <- x1 ; bx2 <- x2 ; by1 <- y1 ; by2 <- y2 ## shaft lx <- length(x1) r.seg <- rep(cin*sh.adj, lx) theta1 <- atan2((y1 - y2) * uin[2], (x1 - x2) * uin[1]) th.seg1 <- theta1 + rep(atan2(0, -cin), lx) theta2 <- atan2((y2 - y1) * uin[2], (x2 - x1) * uin[1]) th.seg2 <- theta2 + rep(atan2(0, -cin), lx) x1d <- y1d <- x2d <- y2d <- 0 if (code %in% c(1,3)) { x2d <- r.seg*cos(th.seg2)/uin[1] y2d <- r.seg*sin(th.seg2)/uin[2] } if (code %in% c(2,3)) { x1d <- r.seg*cos(th.seg1)/uin[1] y1d <- r.seg*sin(th.seg1)/uin[2] } if (is.logical(curved) && all(!curved)) { segments(x1+x1d, y1+y1d, x2+x2d, y2+y2d, lwd=sh.lwd, col=sh.col, lty=sh.lty) phi <- atan2(y1-y2, x1-x2) r <- sqrt( (x1-x2)^2 + (y1-y2)^2 ) lc.x <- x2 + 2/3*r*cos(phi) lc.y <- y2 + 2/3*r*sin(phi) } else { if (is.numeric(curved)) { lambda <- curved } else { lambda <- as.logical(curved) * 0.5 } c.x1 <- x1+x1d c.y1 <- y1+y1d c.x2 <- x2+x2d c.y2 <- y2+y2d midx <- (x1+x2)/2 midy <- (y1+y2)/2 spx <- midx - lambda * 1/2 * (c.y2-c.y1) spy <- midy + lambda * 1/2 * (c.x2-c.x1) sh.col <- rep(sh.col, length=length(c.x1)) sh.lty <- rep(sh.lty, length=length(c.x1)) sh.lwd <- rep(sh.lwd, length=length(c.x1)) lc.x <- lc.y <- numeric(length(c.x1)) for (i in seq_len(length(c.x1))) { spl <- xspline(x=c(c.x1[i],spx[i],c.x2[i]), y=c(c.y1[i],spy[i],c.y2[i]), shape=1, draw=FALSE) lines(spl, lwd=sh.lwd[i], col=sh.col[i], lty=sh.lty[i]) if (code %in% c(2,3)) { x1[i] <- spl$x[3*length(spl$x)/4] y1[i] <- spl$y[3*length(spl$y)/4] } if (code %in% c(1,3)) { x2[i] <- spl$x[length(spl$x)/4] y2[i] <- spl$y[length(spl$y)/4] } lc.x[i] <- spl$x[2/3 * length(spl$x)] lc.y[i] <- spl$y[2/3 * length(spl$y)] } } ## forward arrowhead if (code %in% c(2,3)) { theta <- atan2((by2 - y1) * uin[2], (bx2 - x1) * uin[1]) Rep <- rep(length(deg.arr), lx) p.x2 <- rep(bx2, Rep) p.y2 <- rep(by2, Rep) ttheta <- rep(theta, Rep) + rep(deg.arr, lx) r.arr <- rep(r.arr, lx) if(open) lines((p.x2 + r.arr * cos(ttheta)/uin[1]), (p.y2 + r.arr*sin(ttheta)/uin[2]), lwd=h.lwd, col = h.col.bo, lty=h.lty) else polygon(p.x2 + r.arr * cos(ttheta)/uin[1], p.y2 + r.arr*sin(ttheta)/uin[2], col = h.col, lwd=h.lwd, border=h.col.bo, lty=h.lty) } ## backward arrow head if (code %in% c(1,3)) { x1 <- bx1; y1 <- by1 tmp <- x1 ; x1 <- x2 ; x2 <- tmp tmp <- y1 ; y1 <- y2 ; y2 <- tmp theta <- atan2((y2 - y1) * uin[2], (x2 - x1) * uin[1]) lx <- length(x1) Rep <- rep(length(deg.arr), lx) p.x2 <- rep(x2, Rep) p.y2 <- rep(y2, Rep) ttheta <- rep(theta, Rep) + rep(deg.arr, lx) r.arr <- rep(r.arr, lx) if(open) lines((p.x2 + r.arr * cos(ttheta)/uin[1]), (p.y2 + r.arr*sin(ttheta)/uin[2]), lwd=h.lwd, col = h.col.bo, lty=h.lty) else polygon(p.x2 + r.arr * cos(ttheta)/uin[1], p.y2 + r.arr*sin(ttheta)/uin[2], col = h.col, lwd=h.lwd, border=h.col.bo, lty=h.lty) } list(lab.x=lc.x, lab.y=lc.y) } # Arrows igraph.polygon <- function(points, vertex.size=15/200, expand.by=15/200, shape=1/2, col="#ff000033", border=NA) { by <- expand.by pp <- rbind(points, cbind(points[,1]-vertex.size-by, points[,2]), cbind(points[,1]+vertex.size+by, points[,2]), cbind(points[,1], points[,2]-vertex.size-by), cbind(points[,1], points[,2]+vertex.size+by)) cl <- convex.hull(pp) xspline(cl$rescoords, shape=shape, open=FALSE, col=col, border=border) } igraph/R/indexing.R0000644000176000001440000001770012263024035013707 0ustar ripleyusers ## IGraph library. ## Copyright (C) 2010-2012 Gabor Csardi ## 334 Harvard street, Cambridge, MA 02139 USA ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 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 General Public License for more details. ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ## 02110-1301 USA # Indexing of igraph graphs. # # Goals: # 1. flexible graph manipulation # 2. to be as close to the usual matrix and adjacency list semantics, # as possible # 3. simple # 4. fast # 5. orthogonal # # Rules: # - [ is about the existence of the edges. # - [ can be used for weights as well, if the graph is weighted. # - [[ is about adjacent vertices, and essentially works as an # adjacency list. # # Use cases: # - G[1,2] is there an edge from vertex 1 to vertex 2? # - G[1,1:3] are there edges from vertex 1 to vertices 1:3? # - G[1:2,1:3] are there adges from vertices 1:2 to vertices 1:3? # this returns a (possibly sparse) matrix. # - G[degree(G)==0,1:4] # logical vectors work # - G[1,-1] negative indices work # # - G[[1,]] adjacent vertices of 1 # - G[[,1]] adjacent predessors of 1 # - G[[degree(G),]] # logical vectors work # - G[[-1,]] negative indices work # # - G[1,2,attr="value"] # query an edge attribute # - G[1:3,2,eid=TRUE] # create an edge sequence `[.igraph` <- function(x, i, j, ..., from, to, sparse=getIgraphOpt("sparsematrices"), edges=FALSE, drop=TRUE, attr=if (is.weighted(x)) "weight" else NULL) { ## TODO: make it faster, don't need the whole matrix usually ################################################################ ## Argument checks if ((!missing(from) || !missing(to)) && (!missing(i) || !missing(j))) { stop("Cannot give 'from'/'to' together with regular indices") } if ((!missing(from) && missing(to)) || ( missing(from) && !missing(to))) { stop("Cannot give 'from'/'to' without the other") } if (!missing(from)) { if ((!is.numeric(from) && !is.character(from)) || any(is.na(from))) { stop("'from' must be a numeric or character vector without NAs") } if ((!is.numeric(to) && !is.character(to)) || any(is.na(to))) { stop("'to' must be a numeric or character vector without NAs") } if (length(from) != length(to)) { stop("'from' and 'to' must have the same length") } } ################################################################## if (!missing(from)) { res <- get.edge.ids(x, rbind(from, to), error=FALSE) if (edges) { ## nop } else if (!is.null(attr)) { if (any(res!=0)) { res[res!=0] <- get.edge.attribute(x, attr, res[res!=0]) } } else { res <- as.logical(res)+0 } res } else if (missing(i) && missing(j)) { get.adjacency(x, sparse=sparse, attr=attr, edges=edges) } else if (missing(j)) { get.adjacency(x, sparse=sparse, attr=attr, edges=edges)[i,,drop=drop] } else if (missing(i)) { get.adjacency(x, sparse=sparse, attr=attr, edges=edges)[,j,drop=drop] } else { get.adjacency(x, sparse=sparse, attr=attr, edges=edges)[i,j,drop=drop] } } `[[.igraph` <- function(x, i, j, ..., directed=TRUE, edges=FALSE, exact=TRUE) { ## TODO: make it faster, don't need the whole list usually getfun <- if (edges) get.adjedgelist else get.adjlist if (missing(i) && missing(j)) { mode <- if (directed) "out" else "all" getfun(x, mode=mode) } else if (missing(j)) { mode <- if (directed) "out" else "all" getfun(x, mode=mode)[i] } else if (missing(i)) { mode <- if (directed) "in" else "all" getfun(x, mode=mode)[j] } else { mode <- if (directed) "out" else "all" i <- as.igraph.vs(x, i) j <- as.igraph.vs(x, j) if (!edges) { lapply(getfun(x, mode=mode)[i], intersect, j) } else { ee <- get.adjedgelist(x, mode=mode)[i] lapply(seq_along(i), function(yy) { from <- i[yy] el <- get.edges(x, ee[[yy]]) other <- ifelse(el[,1]==from, el[,2], el[,1]) ee[[yy]][other %in% j] }) } } } `[<-.igraph` <- function(x, i, j, ..., from, to, attr=if (is.weighted(x)) "weight" else NULL, value) { ## TODO: rewrite this in C to make it faster ################################################################ ## Argument checks if ((!missing(from) || !missing(to)) && (!missing(i) || !missing(j))) { stop("Cannot give 'from'/'to' together with regular indices") } if ((!missing(from) && missing(to)) || ( missing(from) && !missing(to))) { stop("Cannot give 'from'/'to' without the other") } if (is.null(attr) && (!is.null(value) && !is.numeric(value) && !is.logical(value))) { stop("New value should be NULL, numeric or logical") } if (is.null(attr) && !is.null(value) && length(value) != 1) { stop("Logical or numeric value must be of length 1") } if (!missing(from)) { if ((!is.numeric(from) && !is.character(from)) || any(is.na(from))) { stop("'from' must be a numeric or character vector without NAs") } if ((!is.numeric(to) && !is.character(to)) || any(is.na(to))) { stop("'to' must be a numeric or character vector without NAs") } if (length(from) != length(to)) { stop("'from' and 'to' must have the same length") } } ################################################################## if (!missing(from)) { if (is.null(value) || (is.logical(value) && !value) || (is.null(attr) && is.numeric(value) && value==0)) { ## Delete edges todel <- x[from=from, to=to, ..., edges=TRUE] x <- delete.edges(x, todel) } else { ## Addition or update of an attribute (or both) ids <- x[from=from, to=to, ..., edges=TRUE] if (any(ids==0)) { x <- add.edges(x, rbind(from[ids==0], to[ids==0])) } if (!is.null(attr)) { ids <- x[from=from, to=to, ..., edges=TRUE] x <- set.edge.attribute(x, attr, ids, value=value) } } } else if (is.null(value) || (is.logical(value) && !value) || (is.null(attr) && is.numeric(value) && value==0)) { ## Delete edges if (missing(i) && missing(j)) { todel <- unlist(x[[ , , ..., edges=TRUE]]) } else if (missing(j)) { todel <- unlist(x[[i, , ..., edges=TRUE]]) } else if (missing(i)) { todel <- unlist(x[[ , j, ..., edges=TRUE]]) } else { todel <- unlist(x[[i, j, ..., edges=TRUE]]) } x <- delete.edges(x, todel) } else { ## Addition or update of an attribute (or both) i <- if (missing(i)) as.numeric(V(x)) else as.igraph.vs(x, i) j <- if (missing(j)) as.numeric(V(x)) else as.igraph.vs(x, j) if (length(i) != 0 && length(j) != 0) { ## Existing edges, and their endpoints exe <- x[[i, j, ..., edges=TRUE]] exv <- x[[i, j, ...]] toadd <- unlist(lapply(seq_along(exv), function(idx) { to <- setdiff(j, exv[[idx]]) if (length(to!=0)) { rbind(i[idx], setdiff(j, exv[[idx]])) } else { numeric() } })) ## Do the changes if (is.null(attr)) { x <- add.edges(x, toadd) } else { x <- add.edges(x, toadd, attr=structure(list(value), names=attr)) toupdate <- unlist(x[[i, j, ..., edges=TRUE]]) x <- set.edge.attribute(x, attr, toupdate, value) } } } x } igraph/R/topology.R0000644000176000001440000001456112251656216013771 0ustar ripleyusers # IGraph R package # Copyright (C) 2006-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### graph.get.isomorphisms.vf2 <- function(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } if (missing(vertex.color1)) { if ("color" %in% list.vertex.attributes(graph1)) { vertex.color1 <- V(graph1)$color } else { vertex.color1 <- NULL } } if (!is.null(vertex.color1)) { vertex.color1 <- as.integer(vertex.color1)-1L } if (missing(vertex.color2)) { if ("color" %in% list.vertex.attributes(graph2)) { vertex.color2 <- V(graph2)$color } else { vertex.color2 <- NULL } } if (!is.null(vertex.color2)) { vertex.color2 <- as.integer(vertex.color2)-1L } if (missing(edge.color1)) { if ("color" %in% list.edge.attributes(graph1)) { edge.color1 <- E(graph1)$color } else { edge.color1 <- NULL } } if (!is.null(edge.color1)) { edge.color1 <- as.integer(edge.color1)-1L } if (missing(edge.color2)) { if ("color" %in% list.edge.attributes(graph2)) { edge.color2 <- E(graph2)$color } else { edge.color2 <- NULL } } if (!is.null(edge.color2)) { edge.color2 <- as.integer(edge.color2)-1L } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_get_isomorphisms_vf2", graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2, PACKAGE="igraph") res <- lapply(res, "+", 1) if (getIgraphOpt("add.vertex.names") && is.named(graph2)) { for (i in seq_along(res)) { names(res[[i]]) <- V(graph2)$name[ res[[i]] ] } } res } graph.get.subisomorphisms.vf2 <- function(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) { # Argument checks if (!is.igraph(graph1)) { stop("Not a graph object") } if (!is.igraph(graph2)) { stop("Not a graph object") } if (missing(vertex.color1)) { if ("color" %in% list.vertex.attributes(graph1)) { vertex.color1 <- V(graph1)$color } else { vertex.color1 <- NULL } } if (!is.null(vertex.color1)) { vertex.color1 <- as.integer(vertex.color1)-1L } if (missing(vertex.color2)) { if ("color" %in% list.vertex.attributes(graph2)) { vertex.color2 <- V(graph2)$color } else { vertex.color2 <- NULL } } if (!is.null(vertex.color2)) { vertex.color2 <- as.integer(vertex.color2)-1L } if (missing(edge.color1)) { if ("color" %in% list.edge.attributes(graph1)) { edge.color1 <- E(graph1)$color } else { edge.color1 <- NULL } } if (!is.null(edge.color1)) { edge.color1 <- as.integer(edge.color1)-1L } if (missing(edge.color2)) { if ("color" %in% list.edge.attributes(graph2)) { edge.color2 <- E(graph2)$color } else { edge.color2 <- NULL } } if (!is.null(edge.color2)) { edge.color2 <- as.integer(edge.color2)-1L } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_get_subisomorphisms_vf2", graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2, PACKAGE="igraph") res <- lapply(res, "+", 1) if (getIgraphOpt("add.vertex.names") && is.named(graph2)) { for (i in seq_along(res)) { names(res[[i]]) <- V(graph2)$name[ res[[i]] ] } } res } graph.isoclass.subgraph <- function(graph, vids) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } vids <- as.igraph.vs(graph, vids)-1 on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_isoclass_subgraph", graph, vids, PACKAGE="igraph") res } graph.subisomorphic.lad <- function(pattern, target, domains=NULL, induced=FALSE, map=TRUE, all.maps=FALSE, time.limit=Inf) { # Argument checks if (!is.igraph(pattern)) { stop("Not a graph object") } if (!is.igraph(target)) { stop("Not a graph object") } induced <- as.logical(induced) if (time.limit==Inf) { time.limit <- 0L } else { time.limit <- as.integer(time.limit) } map <- as.logical(map) all.maps <- as.logical(all.maps) if (!is.null(domains)) { if (!is.list(domains)) { stop("`domains' must be a list of vertex vectors from `target'") } if (length(domains) != vcount(pattern)) { stop("`domains' length and `pattern' number of vertices must match") } domains <- lapply(domains, function(x) as.igraph.vs(target, x)-1) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_subisomorphic_lad", pattern, target, domains, induced, time.limit, map, all.maps, PACKAGE="igraph") if (map) { res$map <- res$map + 1 if (getIgraphOpt("add.vertex.names") && is.named(target)) { names(res$map) <- V(target)$name[res$map] } } if (all.maps) { res$maps <- lapply(res$maps, function(x) x + 1) if (getIgraphOpt("add.vertex.names") && is.named(target)) { for (i in seq_along(res$maps)) { names(res$maps[[i]]) <- V(target)$name[ res$maps[[i]] ] } } } res } # Rest generated by stimulus igraph/R/foreign.R0000644000176000001440000003531712263023733013543 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Reading foreign file formats ################################################################### read.graph.toraw <- function(filename) { if (is.character(filename)) { filename <- file(filename, open="rb") } if (!isOpen(filename)) { open(filename, open="rb") } tmpbufsize <- 20000 buffer <- tmpbuffer <- readBin(filename, what=raw(0), n=tmpbufsize) while (length(tmpbuffer) == tmpbufsize) { tmpbuffer <- readBin(filename, what=raw(0), n=tmpbufsize) buffer <- c(buffer, tmpbuffer) } close(filename) rm(tmpbuffer) buffer } write.graph.fromraw <- function(buffer, file) { closeit <- FALSE if (is.character(file)) { file <- file(file, open="w+b") closeit <- TRUE } if (!isOpen(file)) { file <- open(file) closeit <- TRUE } writeBin(buffer, file) if (closeit) { close(file) } invisible(NULL) } read.graph <- function(file, format=c("edgelist", "pajek", "ncol", "lgl", "graphml", "dimacs", "graphdb", "gml", "dl"), ...) { if (!is.character(file) || length(grep("://", file, fixed=TRUE)) > 0 || length(grep("~", file, fixed=TRUE)) > 0) { buffer <- read.graph.toraw(file) file <- tempfile() write.graph.fromraw(buffer, file) } format <- igraph.match.arg(format) res <- switch(format, "pajek"=read.graph.pajek(file, ...), "ncol"=read.graph.ncol(file, ...), "edgelist"=read.graph.edgelist(file, ...), "lgl"=read.graph.lgl(file, ...), "graphml"=read.graph.graphml(file, ...), "dimacs"=read.graph.dimacs(file, ...), "graphdb"=read.graph.graphdb(file, ...), "gml"=read.graph.gml(file, ...), "dl"=read.graph.dl(file, ...), stop(paste("Unknown file format:",format)) ) res } write.graph <- function(graph, file, format=c("edgelist", "pajek", "ncol", "lgl", "graphml", "dimacs", "gml", "dot", "leda"), ...) { if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.character(file) || length(grep("://", file, fixed=TRUE)) > 0 || length(grep("~", file, fixed=TRUE)) > 0) { tmpfile <- TRUE origfile <- file file <- tempfile() } else { tmpfile <- FALSE } format <- igraph.match.arg(format) res <- switch(format, "pajek"=write.graph.pajek(graph, file, ...), "edgelist"=write.graph.edgelist(graph, file, ...), "ncol"=write.graph.ncol(graph, file, ...), "lgl"=write.graph.lgl(graph, file, ...), "graphml"=write.graph.graphml(graph, file, ...), "dimacs"=write.graph.dimacs(graph, file, ...), "gml"=write.graph.gml(graph, file, ...), "dot"=write.graph.dot(graph, file, ...), "leda"=write.graph.leda(graph, file, ...), stop(paste("Unknown file format:",format)) ) if (tmpfile) { buffer <- read.graph.toraw(file) write.graph.fromraw(buffer, origfile) } invisible(res) } ################################################################ # Plain edge list format, not sorted ################################################################ read.graph.edgelist <- function(file, n=0, directed=TRUE, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (edgelist format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_read_graph_edgelist", file, as.numeric(n), as.logical(directed), PACKAGE="igraph") } write.graph.edgelist <- function(graph, file, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (edgelist format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_edgelist", graph, file, PACKAGE="igraph") } ################################################################ # NCOL and LGL formats, quite simple ################################################################ read.graph.ncol <- function(file, predef=character(0), names=TRUE, weights=c("auto", "yes", "no"), directed=FALSE, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (NCOL format)") } weights <- switch(igraph.match.arg(weights), "no"=0, "yes"=1, "auto"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_read_graph_ncol", file, as.character(predef), as.logical(names), as.numeric(weights), as.logical(directed), PACKAGE="igraph") } write.graph.ncol <- function(graph, file, names="name", weights="weight", ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (NCOL format)") } names <- as.character(names) weights <- as.character(weights) if (length(names)==0 || ! names %in% list.vertex.attributes(graph)) { names <- NULL } if (length(weights)==0 || ! weights %in% list.edge.attributes(graph)) { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_ncol", graph, file, names, weights, PACKAGE="igraph") } read.graph.lgl <- function(file, names=TRUE, weights=c("auto", "yes", "no"), directed=FALSE, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (LGL format)") } weights <- switch(igraph.match.arg(weights), "no"=0, "yes"=1, "auto"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_read_graph_lgl", file, as.logical(names), as.numeric(weights), as.logical(directed), PACKAGE="igraph") } write.graph.lgl <- function(graph, file, names="name", weights="weight", isolates=FALSE, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (LGL format)") } names <- as.character(names) weights <- as.character(weights) if (length(names)==0 || ! names %in% list.vertex.attributes(graph)) { names <- NULL } if (length(weights)==0 || ! weights %in% list.edge.attributes(graph)) { weights <- NULL } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_lgl", graph, file, names, weights, as.logical(isolates), PACKAGE="igraph") } read.graph.pajek <- function(file, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (Pajek format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_read_graph_pajek", file, PACKAGE="igraph") if ("type" %in% list.vertex.attributes(res)) { type <- as.logical(V(res)$type) res <- remove.vertex.attribute(res, "type") res <- set.vertex.attribute(res, "type", value=type) } res } write.graph.pajek <- function(graph, file, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (Pajek format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_pajek", graph, file, PACKAGE="igraph") } read.graph.dimacs <- function(file, directed=TRUE, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (DIMACS format)") } res <- .Call("R_igraph_read_graph_dimacs", file, as.logical(directed), PACKAGE="igraph") if (res[[1]][1] == "max") { graph <- res[[2]] graph <- set.graph.attribute(graph, "problem", res[[1]]) graph <- set.graph.attribute(graph, "source", res[[3]]) graph <- set.graph.attribute(graph, "target", res[[4]]) E(graph)$capacity <- res[[5]] graph } else if (res[[1]][1] == "edge") { graph <- res[[2]] graph <- set.graph.attribute(graph, "problem", res[[1]]) V(graph)$label <- res[[3]] graph } } write.graph.dimacs <- function(graph, file, source=NULL, target=NULL, capacity=NULL, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (DIMACS format)") } if (is.null(source)) { source <- get.graph.attribute(graph, "source") } if (is.null(target)) { target <- get.graph.attribute(graph, "target") } if (is.null(capacity)) { capacity <- E(graph)$capacity } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_dimacs", graph, file, as.numeric(source), as.numeric(target), as.numeric(capacity), PACKAGE="igraph") } ################################################################ # GraphML ################################################################ read.graph.graphml <- function(file, index=0, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (GraphML format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_read_graph_graphml", file, as.numeric(index), PACKAGE="igraph") } write.graph.graphml <- function(graph, file, prefixAttr=TRUE, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (GraphML format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_graphml", graph, file, as.logical(prefixAttr), PACKAGE="igraph") } ################################################################ # GML ################################################################ read.graph.gml <- function(file, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (GML format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_read_graph_gml", file, PACKAGE="igraph") } write.graph.gml <- function(graph, file, id=NULL, creator=NULL, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (GML format)") } if (!is.null(id)) { id <- as.numeric(id) } if (!is.null(creator)) { creator <- as.character(creator) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_gml", graph, file, id, creator, PACKAGE="igraph") } ################################################################ # UCINET DL ################################################################ read.graph.dl <- function(file, directed=TRUE, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (DL format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_read_graph_dl", file, as.logical(directed), PACKAGE="igraph") } ################################################################ # Dot ################################################################ write.graph.dot <- function(graph, file, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (DOT format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_dot", graph, file, PACKAGE="igraph") } ################################################################ # Download a file from the graph database for # isomorphic problems ################################################################ graph.graphdb <- function(url=NULL, prefix="iso", type="r001", nodes=NULL, pair="A", which=0, base="http://cneurocvs.rmki.kfki.hu/graphdb/gzip", compressed=TRUE, directed=TRUE) { if (is.null(nodes) && is.null(url)) { stop("The `nodes' or the `url' argument must be non-null") } if (is.null(url)) { prefixes <- c("iso", "si6", "mcs10", "mcs30", "mcs50", "mcs70", "mcs90") types <- c("r001", "r005", "r01", "r02", "m2D", "m2Dr2", "m2Dr4", "m2Dr6", "m3D", "m3Dr2", "m3Dr4", "m3Dr6", "m4D", "m4Dr2", "m4Dr4", "m4Dr6", "b03", "b03m", "b06", "b06m", "b09", "b09m") sizecode <- if (nodes<=100) "s" else if (nodes<2000) "m" else "l" # "l" ???? typegroups <- c("rand", "rand", "rand", "rand", "m2D", "m2D", "m2D", "m2D", "m2D", "m3D", "m3D", "m3D", "m4D", "m4D", "m4D", "m4D", "bvg", "bvg", "bvg", "bvg", "bvg", "bvg") typegroup <- typegroups[which(types==type)] if (!prefix %in% prefixes) { stop("Invalid prefix!") } if (!type %in% types) { stop("Invalid graph type!") } suff <- if (compressed) ".gz" else "" filename <- paste(sep="", base, "/", prefix, "/", typegroup, "/", type, "/", prefix, "_", type, "_", sizecode, nodes, ".", pair, formatC(which, width=2, flag="0"), suff) } else { filename <- url } ## ok, we have the filename f <- try(gzcon(file(filename, open="rb"))) if (inherits(f, "try-error")) { stop(paste("Cannot open URL:", filename)); } buffer <- read.graph.toraw(f) f <- tempfile() write.graph.fromraw(buffer, f) .Call("R_igraph_read_graph_graphdb", f, as.logical(directed), PACKAGE="igraph") } read.graph.graphdb <- function(file, directed=TRUE, ...) { if (length(list(...))>0) { stop("Unknown arguments to read.graph (GraphDB format)") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_read_graph_graphdb", file, as.logical(directed), PACKAGE="igraph") } write.graph.leda <- function(graph, file, vertex.attr=NULL, edge.attr=NULL, ...) { if (length(list(...))>0) { stop("Unknown arguments to write.graph (LEDA format)") } if (!is.null(vertex.attr)) { vertex.attr <- as.character(vertex.attr) } if (!is.null(edge.attr)) { edge.attr <- as.character(edge.attr) } on.exit(.Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_write_graph_leda", graph, file, vertex.attr, edge.attr, PACKAGE="igraph") } igraph/R/layout.R0000644000176000001440000007422512325365704013436 0ustar ripleyusers # IGraph R package # Copyright (C) 2003-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Layouts ################################################################### layout.random <- function(graph, params, dim=2) { if (!is.igraph(graph)) { stop("Not a graph object") } if (dim==2) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_layout_random", graph, PACKAGE="igraph") } else if (dim==3) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_layout_random_3d", graph, PACKAGE="igraph") } else { stop("Invalid `dim' value"); } } layout.circle <- function(graph, params) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_layout_circle", graph, PACKAGE="igraph") } layout.sphere <- function(graph, params) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_layout_sphere", graph, PACKAGE="igraph") } layout.fruchterman.reingold <- function(graph, ..., dim=2, params=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } if (length(params)==0) { params <- list(...) } if (dim==2) { fn <- "R_igraph_layout_fruchterman_reingold" } else if (dim==3 ){ fn <- "R_igraph_layout_fruchterman_reingold_3d" } else { stop("Invalid `dim' argument"); } vc <- vcount(graph) if (is.null(params$niter)) { params$niter <- 500 } if (is.null(params$maxdelta)) { params$maxdelta <- vc } if (is.null(params$area)) { params$area <- vc^2 } if (is.null(params$coolexp)) { params$coolexp <- 1.5 } if (is.null(params$repulserad)){ params$repulserad <- params$area * vc } if (is.null(params$weights)) { params$weights <- NULL } else { params$weights <- as.numeric(params$weights) } if (!is.null(params$start)) { params$start <- structure(as.numeric(params$start), dim=dim(params$start)) } if (!is.null(params$minx)) { params$minx <- as.double(params$minx) } if (!is.null(params$maxx)) { params$maxx <- as.double(params$maxx) } if (!is.null(params$miny)) { params$miny <- as.double(params$miny) } if (!is.null(params$maxy)) { params$maxy <- as.double(params$maxy) } if (!is.null(params$minz)) { params$minz <- as.double(params$minz) } if (!is.null(params$maxz)) { params$maxz <- as.double(params$maxz) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call(fn, graph, as.double(params$niter), as.double(params$maxdelta), as.double(params$area), as.double(params$coolexp), as.double(params$repulserad), params$weights, params$start, params$minx, params$maxx, params$miny, params$maxy, params$minz, params$maxz, PACKAGE="igraph") } layout.fruchterman.reingold.grid <- function(graph, ..., params=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } if (length(params)==0) { params <- list(...) } vc <- vcount(graph) if (is.null(params$niter)) { params$niter <- 500 } if (is.null(params$maxdelta)) { params$maxdelta <- vc } if (is.null(params$area)) { params$area <- vc^2 } if (is.null(params$coolexp)) { params$coolexp <- 1.5 } if (is.null(params$repulserad)){ params$repulserad <- params$area * vc } if (is.null(params$cellsize)) { params$cellsize <- (sqrt(sqrt(params$area))) } if (is.null(params$weights)) { params$weights <- NULL } else { params$weights <- as.numeric(params$weights) } if (!is.null(params$start)) { params$start <- structure(as.numeric(params$start), dim=dim(params$start)) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_layout_fruchterman_reingold_grid", graph, as.double(params$niter), as.double(params$maxdelta), as.double(params$area), as.double(params$coolexp), as.double(params$repulserad), as.double(params$cellsize), params$start, params$weights, PACKAGE="igraph") } # FROM SNA 0.5 layout.kamada.kawai<-function(graph, ..., dim=2, params=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } if (length(params)==0) { params <- list(...) } if (dim==2) { fn <- "R_igraph_layout_kamada_kawai" } else if (dim==3) { fn <- "R_igraph_layout_kamada_kawai_3d" } else { stop("Invalid `dim' parameter") } vc <- vcount(graph) if (is.null(params$niter)) { params$niter <- 1000 } if (is.null(params$sigma)) { params$sigma <- vc/4 } if (is.null(params$initemp)) { params$initemp <- 10 } if (is.null(params$coolexp)) { params$coolexp <- 0.99 } if (is.null(params$kkconst)) { params$kkconst <- vc^2 } if (is.null(params$fixz)) { params$fixz <- FALSE} if (!is.null(params$start)) { params$start <- structure(as.numeric(params$start), dim=dim(params$start)) } if (!is.null(params$minx)) { params$minx <- as.double(params$minx) } if (!is.null(params$maxx)) { params$maxx <- as.double(params$maxx) } if (!is.null(params$miny)) { params$miny <- as.double(params$miny) } if (!is.null(params$maxy)) { params$maxy <- as.double(params$maxy) } if (!is.null(params$minz)) { params$minz <- as.double(params$minz) } if (!is.null(params$maxz)) { params$maxz <- as.double(params$maxz) } if (params$fixz && dim==2) { warning("`fixz' works for 3D only, ignored.") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call(fn, graph, as.double(params$niter), as.double(params$initemp), as.double(params$coolexp), as.double(params$kkconst), as.double(params$sigma), params$start, as.logical(params$fixz), params$minx, params$maxx, params$miny, params$maxy, params$minz, params$maxz, PACKAGE="igraph") } layout.graphopt <- function(graph, ..., params=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } if (length(params)==0) { params <- list(...) } vc <- vcount(graph) if (is.null(params$niter)) { params$niter <- 500 } if (is.null(params$charge)) { params$charge <- 0.001 } if (is.null(params$mass)) { params$mass <- 30 } if (is.null(params$spring.length)) { params$spring.length <- 0 } if (is.null(params$spring.constant)) { params$spring.constant <- 1 } if (is.null(params$max.sa.movement)) { params$max.sa.movement <- 5 } if (!is.null(params$start)) { params$start <- structure(as.numeric(params$start), dim=dim(params$start)) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_layout_graphopt", graph, as.double(params$niter), as.double(params$charge), as.double(params$mass), as.double(params$spring.length), as.double(params$spring.constant), params$max.sa.movement, params$start, PACKAGE="igraph") } layout.lgl <- function(graph, ..., params=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } if (length(params)==0) { params <- list(...) } vc <- vcount(graph) if (is.null(params$maxiter)) { params$maxiter <- 150 } if (is.null(params$maxdelta)) { params$maxdelta <- vc } if (is.null(params$area)) { params$area <- vc^2 } if (is.null(params$coolexp)) { params$coolexp <- 1.5 } if (is.null(params$repulserad)){ params$repulserad <- params$area * vc } if (is.null(params$cellsize)) { params$cellsize <- (sqrt(sqrt(params$area))) } if (is.null(params$root)) { params$root <- -1 } else { params$root <- as.igraph.vs(graph, params$root)-1 } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_layout_lgl", graph, as.double(params$maxiter), as.double(params$maxdelta), as.double(params$area), as.double(params$coolexp), as.double(params$repulserad), as.double(params$cellsize), params$root, PACKAGE="igraph") } layout.reingold.tilford <- function(graph, ..., params=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } if (length(params)==0) { params <- list(...) } if (is.null(params$root)) { params$root <- 1 } if (is.null(params$circular)) { params$circular <- FALSE } if (is.null(params$rootlevel)) { params$rootlevel <- numeric() } if (is.null(params$mode)) { params$mode <- "out" } if (is.null(params$flip.y)) { params$flip.y <- TRUE } params$mode <- tolower(params$mode) params$mode <- switch(params$mode, "out"=1, "in"=2, "all"=3, "total"=3) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_layout_reingold_tilford", graph, as.igraph.vs(graph, params$root)-1, as.double(params$mode), as.double(params$rootlevel), as.logical(params$circular), PACKAGE="igraph") if (params$flip.y) { res[,2] <- max(res[,2])-res[,2] } res } layout.merge <- function(graphs, layouts, method="dla") { if (!all(sapply(graphs, is.igraph))) { stop("Not a graph object") } if (method == "dla") { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_layout_merge_dla", graphs, layouts, PACKAGE="igraph") } else { stop("Invalid `method'.") } res } # FROM SNA 0.5 symmetrize.mat <- function(mats,rule=c("weak", "strong", "lower", "upper")){ rule <- igraph.match.arg(rule) #Build the input data structures if(length(dim(mats))>2){ m<-dim(mats)[1] n<-dim(mats)[2] o<-dim(mats)[3] d<-mats }else{ m<-1 n<-dim(mats)[1] o<-dim(mats)[2] d<-array(dim=c(1,n,o)) d[1,,]<-mats } #Apply the symmetry rule for(i in 1:m){ if(rule=="upper"){ temp<-d[i,,] for(j in 1:n) temp[j:n,j]<-temp[j,j:n] d[i,,]<-temp }else if(rule=="lower"){ temp<-d[i,,] for(j in 1:n) temp[j,j:n]<-temp[j:n,j] d[i,,]<-temp }else if(rule=="weak"){ d[i,,]<-matrix(as.numeric(d[i,,]|t(d[i,,])),nrow=n,ncol=o) }else if(rule=="strong"){ d[i,,]<-matrix(as.numeric(d[i,,]&t(d[i,,])),nrow=n,ncol=o) } } #Return the symmetrized matrix if(m==1) out<-d[1,,] else out<-d out } # FROM SNA 0.5 layout.spring<-function(graph, ..., params=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } if (length(params)==0) { params <- list(...) } if (is.null(params$mass)) { params$mass <- 0.1 } if (is.null(params$equil)) { params$equil <- 1 } if (is.null(params$k)) { params$k <- 0.001 } if (is.null(params$repeqdis)) { params$repeqdis <- 0.1 } if (is.null(params$kfr)) { params$kfr <- 0.01 } if (is.null(params$repulse)) { params$repulse <- FALSE } #Create initial condidions vc <- vcount(graph) f.x <- rep(0,vc) #Set initial x/y forces to zero f.y <- rep(0,vc) v.x <- rep(0,vc) #Set initial x/y velocities to zero v.y <- rep(0,vc) tempa <- sample((0:(vc-1))/vc) #Set initial positions randomly on the circle x <- vc/(2*pi)*sin(2*pi*tempa) y <- vc/(2*pi)*cos(2*pi*tempa) ds <- symmetrize.mat(get.adjacency(graph, sparse=FALSE))#Symmetrize/dichotomize the graph kfr <- params$kfr #Set initial friction level niter <- 1 #Set the iteration counter #Simulate, with increasing friction, until motion stops repeat{ niter <- niter+1 #Update the iteration counter dis <- as.matrix(dist(cbind(x,y))) #Get inter-point distances #Get angles relative to the positive x direction theta <- acos(t(outer(x,x,"-"))/dis)*sign(t(outer(y,y,"-"))) #Compute spring forces; note that we assume a base spring coefficient #of params$k units ("pseudo-Newtons/quasi-meter"?), with an equilibrium #extension of params$equil units for all springs f.x <- apply(ds*cos(theta)*params$k*(dis-params$equil),1,sum,na.rm=TRUE) f.y <- apply(ds*sin(theta)*params$k*(dis-params$equil),1,sum,na.rm=TRUE) #If node repulsion is active, add a force component for this #as well. We employ an inverse cube law which is equal in power #to the attractive spring force at distance params$repeqdis if(params$repulse){ f.x <- f.x-apply(cos(theta)*params$k/(dis/params$repeqdis)^3,1, sum,na.rm=TRUE) f.y <- f.y-apply(sin(theta)*params$k/(dis/params$repeqdis)^3,1, sum,na.rm=TRUE) } #Adjust the velocities (assume a mass of params$mass units); note that the #motion is roughly modeled on the sliding of flat objects across #a uniform surface (e.g., spring-connected cylinders across a table). #We assume that the coefficients of static and kinetic friction are #the same, which should only trouble you if you are under the #delusion that this is a simulation rather than a graph drawing #exercise (in which case you should be upset that I'm not using #Runge-Kutta or the like!). v.x <- v.x+f.x/params$mass #Add accumulated spring/repulsion forces v.y <- v.y+f.y/params$mass spd <- sqrt(v.x^2+v.y^2) #Determine frictional forces fmag <- pmin(spd,kfr) #We can't let friction _create_ motion! theta <- acos(v.x/spd)*sign(v.y) #Calculate direction of motion f.x <- fmag*cos(theta) #Decompose frictional forces f.y <- fmag*sin(theta) f.x[is.nan(f.x)] <- 0 #Correct for any 0/0 problems f.y[is.nan(f.y)] <- 0 v.x <- v.x-f.x #Apply frictional forces (opposing motion - v.y <- v.y-f.y #note that mass falls out of equation) #Adjust the positions (yep, it's primitive linear updating time!) x <- x+v.x y <- y+v.y #Check for cessation of motion, and increase friction mdist <- mean(dis) if(all(v.x=3){ thisl <- svd(d[ind, ind], 2)[[2]] thisl[, 1] <- thisl[, 1]/dist(range(thisl[, 1])) thisl[, 2] <- thisl[, 2]/dist(range(thisl[, 2])) llist[[i]] <- thisl }else if(length(which(ind))==2){ llist[[i]] <- d[ind, ind] } else { llist[[i]] <- matrix(c(0, 0), nrow=1) } llen[i] <- length(which(ind)) glist[[i]] <- induced.subgraph(graph, V(graph)[ind]) } ## merge them all: lmerged <- layout.merge(glist, llist) ## now reorder these rows to reflect original graph: l <- matrix(rep(NA, 2*vcount(graph)), ncol=2) l[order(clust$membership), ] <- lmerged return(l) } piecewise.layout <- function(graph, layout=layout.kamada.kawai, ...) { if (!is.igraph(graph)) { stop("Not a graph object") } V(graph)$id <- seq(vcount(graph)) gl <- decompose.graph(graph) ll <- lapply(gl, layout, ...) l <- layout.merge(gl, ll) l[ unlist(sapply(gl, get.vertex.attribute, "id")), ] <- l[] l } layout.drl <- function(graph, use.seed = FALSE, seed=matrix(runif(vcount(graph)*2), ncol=2), options=igraph.drl.default, weights=E(graph)$weight, fixed=NULL, dim=2) { if (!is.igraph(graph)) { stop("Not a graph object") } if (dim != 2 && dim != 3) { stop("`dim' must be 2 or 3") } use.seed <- as.logical(use.seed) seed <- as.matrix(seed) options.tmp <- igraph.drl.default options.tmp[names(options)] <- options options <- options.tmp if (!is.null(weights)) { weights <- as.numeric(weights) } if (!is.null(fixed)) { fixed <- as.logical(fixed) } on.exit(.Call("R_igraph_finalizer", PACKAGE = "igraph")) if (dim==2) { res <- .Call("R_igraph_layout_drl", graph, seed, use.seed, options, weights, fixed, PACKAGE = "igraph") } else { res <- .Call("R_igraph_layout_drl_3d", graph, seed, use.seed, options, weights, fixed, PACKAGE = "igraph") } res } igraph.drl.default <- list(edge.cut=32/40, init.iterations=0, init.temperature=2000, init.attraction=10, init.damping.mult=1.0, liquid.iterations=200, liquid.temperature=2000, liquid.attraction=10, liquid.damping.mult=1.0, expansion.iterations=200, expansion.temperature=2000, expansion.attraction=2, expansion.damping.mult=1.0, cooldown.iterations=200, cooldown.temperature=2000, cooldown.attraction=1, cooldown.damping.mult=.1, crunch.iterations=50, crunch.temperature=250, crunch.attraction=1, crunch.damping.mult=0.25, simmer.iterations=100, simmer.temperature=250, simmer.attraction=.5, simmer.damping.mult=0) igraph.drl.coarsen <- list(edge.cut=32/40, init.iterations=0, init.temperature=2000, init.attraction=10, init.damping.mult=1.0, liquid.iterations=200, liquid.temperature=2000, liquid.attraction=2, liquid.damping.mult=1.0, expansion.iterations=200, expansion.temperature=2000, expansion.attraction=10, expansion.damping.mult=1.0, cooldown.iterations=200, cooldown.temperature=2000, cooldown.attraction=1, cooldown.damping.mult=.1, crunch.iterations=50, crunch.temperature=250, crunch.attraction=1, crunch.damping.mult=0.25, simmer.iterations=100, simmer.temperature=250, simmer.attraction=.5, simmer.damping.mult=0) igraph.drl.coarsest <- list(edge.cut=32/40, init.iterations=0, init.temperature=2000, init.attraction=10, init.damping.mult=1.0, liquid.iterations=200, liquid.temperature=2000, liquid.attraction=2, liquid.damping.mult=1.0, expansion.iterations=200, expansion.temperature=2000, expansion.attraction=10, expansion.damping.mult=1.0, cooldown.iterations=200, cooldown.temperature=2000, cooldown.attraction=1, cooldown.damping.mult=.1, crunch.iterations=200, crunch.temperature=250, crunch.attraction=1, crunch.damping.mult=0.25, simmer.iterations=100, simmer.temperature=250, simmer.attraction=.5, simmer.damping.mult=0) igraph.drl.refine <- list(edge.cut=32/40, init.iterations=0, init.temperature=50, init.attraction=.5, init.damping.mult=1.0, liquid.iterations=0, liquid.temperature=2000, liquid.attraction=2, liquid.damping.mult=1.0, expansion.iterations=50, expansion.temperature=500, expansion.attraction=.1, expansion.damping.mult=.25, cooldown.iterations=50, cooldown.temperature=250, cooldown.attraction=1, cooldown.damping.mult=.1, crunch.iterations=50, crunch.temperature=250, crunch.attraction=1, crunch.damping.mult=0.25, simmer.iterations=0, simmer.temperature=250, simmer.attraction=.5, simmer.damping.mult=0) igraph.drl.final <- list(edge.cut=32/40, init.iterations=0, init.temperature=50, init.attraction=.5, init.damping.mult=0, liquid.iterations=0, liquid.temperature=2000, liquid.attraction=2, liquid.damping.mult=1.0, expansion.iterations=50, expansion.temperature=2000, expansion.attraction=2, expansion.damping.mult=1.0, cooldown.iterations=50, cooldown.temperature=200, cooldown.attraction=1, cooldown.damping.mult=.1, crunch.iterations=50, crunch.temperature=250, crunch.attraction=1, crunch.damping.mult=0.25, simmer.iterations=25, simmer.temperature=250, simmer.attraction=.5, simmer.damping.mult=0) layout.auto <- function(graph, dim=2, ...) { ## 1. If there is a 'layout' graph attribute, we just use that. ## 2. Otherwise, if there are vertex attributes called 'x' and 'y', ## we use those (and the 'z' vertex attribute as well, if present). ## 3. Otherwise, if the graph is connected and small (<100) we use ## the Kamada-Kawai layout. ## 4. Otherwise if the graph is medium size (<1000) we use the ## Fruchterman-Reingold layout. ## 5. Otherwise we use the DrL layout generator. if ("layout" %in% list.graph.attributes(graph)) { lay <- get.graph.attribute(graph, "layout") if (is.function(lay)) { lay(graph, ...) } else { lay } } else if ( all(c("x", "y") %in% list.vertex.attributes(graph)) ) { if ("z" %in% list.vertex.attributes(graph)) { cbind(V(graph)$x, V(graph)$y, V(graph)$z) } else { cbind(V(graph)$x, V(graph)$y) } } else if (is.connected(graph) && vcount(graph) < 100) { layout.kamada.kawai(graph, dim=dim, ...) } else if (vcount(graph) < 1000) { layout.fruchterman.reingold(graph, dim=dim, ...) } else { layout.drl(graph, dim=dim, ...) } } layout.sugiyama <- function(graph, layers=NULL, hgap=1, vgap=1, maxiter=100, weights=NULL, attributes=c("default", "all", "none")) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.null(layers)) layers <- as.numeric(layers)-1 hgap <- as.numeric(hgap) vgap <- as.numeric(vgap) maxiter <- as.integer(maxiter) if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } attributes <- igraph.match.arg(attributes) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_layout_sugiyama", graph, layers, hgap, vgap, maxiter, weights, PACKAGE="igraph") # Flip the y coordinates, more natural this way res$res[,2] <- max(res$res[,2]) - res$res[,2] + 1 # Separate real and dummy vertices vc <- vcount(graph) res$layout <- res$res[seq_len(vc),] if (nrow(res$res)==vc) { res$layout.dummy <- matrix(nrow=0, ncol=2) } else { res$layout.dummy <- res$res[(vc+1):nrow(res$res),] } # Add some attributes to the extended graph E(res$extd_graph)$orig <- res$extd_to_orig_eids res$extd_to_orig_eids <- NULL res$extd_graph <- set.vertex.attribute(res$extd_graph, "dummy", value=c(rep(FALSE, vc), rep(TRUE, nrow(res$res)-vc))) res$extd_graph$layout <- rbind(res$layout, res$layout.dummy) if (attributes=="default" || attributes=="all") { if ("size" %in% list.vertex.attributes(graph)) { V(res$extd_graph)$size <- 0 V(res$extd_graph)$size[ !V(res$extd_graph)$dummy ] <- V(graph)$size } if ("size2" %in% list.vertex.attributes(graph)) { V(res$extd_graph)$size2 <- 0 V(res$extd_graph)$size2[ !V(res$extd_graph)$dummy ] <- V(graph)$size2 } if ("shape" %in% list.vertex.attributes(graph)) { V(res$extd_graph)$shape <- "none" V(res$extd_graph)$shape[ !V(res$extd_graph)$dummy ] <- V(graph)$shape } if ("label" %in% list.vertex.attributes(graph)) { V(res$extd_graph)$label <- "" V(res$extd_graph)$label[ !V(res$extd_graph)$dummy ] <- V(graph)$label } if ("color" %in% list.vertex.attributes(graph)) { V(res$extd_graph)$color <- head(V(graph)$color, 1) V(res$extd_graph)$color[ !V(res$extd_graph)$dummy ] <- V(graph)$color } eetar <- get.edgelist(res$extd_graph, names=FALSE)[,2] E(res$extd_graph)$arrow.mode <- 0 if ("arrow.mode" %in% list.edge.attributes(graph)) { E(res$extd_graph)$arrow.mode[ eetar <= vc ] <- E(graph)$arrow.mode } else { E(res$extd_graph)$arrow.mode[ eetar <= vc ] <- is.directed(graph) * 2 } if ("arrow.size" %in% list.edge.attributes(graph)) { E(res$extd_graph)$arrow.size <- 0 E(res$extd_graph)$arrow.size[ eetar <= vc ] <- E(graph)$arrow.size } } if (attributes=="all") { gatt <- setdiff(list.graph.attributes(graph), "layout") vatt <- setdiff(list.vertex.attributes(graph), c("size", "size2", "shape", "label", "color")) eatt <- setdiff(list.edge.attributes(graph), c("arrow.mode", "arrow.size")) for (ga in gatt) { res$extd_graph <- set.graph.attribute(res$extd_graph, ga, get.graph.attribute(graph, ga)) } for (va in vatt) { notdummy <- which(!V(res$extd_graph)$dummy) res$extd_graph <- set.vertex.attribute(res$extd_graph, va, notdummy, get.vertex.attribute(graph, va)) } for (ea in eatt) { eanew <- get.edge.attribute(graph, ea)[E(res$extd_graph)$orig] res$extd_graph <- set.edge.attribute(res$extd_graph, ea, value=eanew) } } res$res <- NULL res } layout.mds <- function(graph, dist=NULL, dim=2, options=igraph.arpack.default) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.null(dist)) dist <- structure(as.double(dist), dim=dim(dist)) dim <- as.integer(dim) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_layout_mds", graph, dist, dim, PACKAGE="igraph") res } igraph/R/cliques.R0000644000176000001440000001147112251656216013557 0ustar ripleyusers # IGraph R package # Copyright (C) 2006-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### cliques <- function(graph, min=NULL, max=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(min)) { min <- 0 } if (is.null(max)) { max <- 0 } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_cliques", graph, as.numeric(min), as.numeric(max), PACKAGE="igraph") lapply(res, function(x) x+1) } largest.cliques <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_largest_cliques", graph, PACKAGE="igraph") lapply(res, function(x) x+1) } maximal.cliques <- function(graph, min=NULL, max=NULL, subset=NULL, file=NULL) { if (!is.igraph(graph)) { stop("Not a graph object"); } if (is.null(min)) { min <- 0 } if (is.null(max)) { max <- 0 } if (!is.null(subset)) { subset <- as.integer(as.igraph.vs(graph, subset)-1) } if (!is.null(file)) { if (!is.character(file) || length(grep("://", file, fixed=TRUE)) > 0 || length(grep("~", file, fixed=TRUE)) > 0) { tmpfile <- TRUE origfile <- file file <- tempfile() } else { tmpfile <- FALSE } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_maximal_cliques_file", graph, subset, file, as.numeric(min), as.numeric(max), PACKAGE="igraph") if (tmpfile) { buffer <- read.graph.toraw(file) write.graph.fromraw(buffer, origfile) } invisible(NULL) } else { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_maximal_cliques", graph, subset, as.numeric(min), as.numeric(max), PACKAGE="igraph") lapply(res, function(x) x+1) } } maximal.cliques.count <- function(graph, min=NULL, max=NULL, subset=NULL) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(min)) { min <- 0 } if (is.null(max)) { max <- 0 } min <- as.integer(min) max <- as.integer(max) if (!is.null(subset)) { subset <- as.integer(as.igraph.vs(graph, subset)-1) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_maximal_cliques_count", graph, subset, min, max, PACKAGE="igraph") res } clique.number <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object"); } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_clique_number", graph, PACKAGE="igraph") } independent.vertex.sets <- function(graph, min=NULL, max=NULL) { if (!is.igraph(graph)) { stop("Not a graph object"); } if (is.null(min)) { min <- 0 } if (is.null(max)) { max <- 0 } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_independent_vertex_sets", graph, as.numeric(min), as.numeric(max), PACKAGE="igraph") lapply(res, function(x) x+1) } largest.independent.vertex.sets <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object"); } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_largest_independent_vertex_sets", graph, PACKAGE="igraph") lapply(res, function(x) x+1) } maximal.independent.vertex.sets <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object"); } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_maximal_independent_vertex_sets", graph, PACKAGE="igraph") lapply(res, function(x) x+1) } independence.number <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object"); } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_independence_number", graph, PACKAGE="igraph") } igraph/R/par.R0000644000176000001440000000711612252354767012704 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### .igraph.pars <- list("print.vertex.attributes"=FALSE, "print.edge.attributes"=FALSE, "print.graph.attributes"=FALSE, "verbose"=FALSE, "vertex.attr.comb"=list(name="concat", "ignore"), "edge.attr.comb"=list(weight="sum", name="concat", "ignore"), "sparsematrices"=TRUE, "nexus.url"="http://nexus.igraph.org", "add.params"=TRUE, "add.vertex.names"=TRUE, "dend.plot.type"="auto", "print.full"=FALSE, "annotate.plot"=FALSE ) igraph.pars.set.verbose <- function(verbose) { if (is.logical(verbose)) { .Call("R_igraph_set_verbose", verbose, PACKAGE="igraph") } else if (is.character(verbose)) { if (!verbose %in% c("tk", "tkconsole")) { stop("Unknown 'verbose' value") } if (verbose %in% c("tk", "tkconsole")) { if (!capabilities()[["X11"]]) { stop("X11 not available") } if (!require("tcltk")) { stop("tcltk package not available") } } .Call("R_igraph_set_verbose", verbose, PACKAGE="igraph") } else { stop("'verbose' should be a logical or character scalar") } verbose } igraph.pars.callbacks <- list("verbose"=igraph.pars.set.verbose) ## This is based on 'sm.options' in the 'sm' package igraph.options <- function(...) { if (nargs() == 0) return(.igraph.pars) current <- .igraph.pars temp <- list(...) if (length(temp) == 1 && is.null(names(temp))) { arg <- temp[[1]] switch(mode(arg), list = temp <- arg, character = return(.igraph.pars[arg]), stop("invalid argument: ", sQuote(arg))) } if (length(temp) == 0) return(current) n <- names(temp) if (is.null(n)) stop("options must be given by name") env <- asNamespace("igraph") cb <- intersect(names(igraph.pars.callbacks), n) for (cn in cb) { temp[[cn]] <- igraph.pars.callbacks[[cn]](temp[[cn]]) } current <- .igraph.pars # callback might have updated it current[n] <- temp assign(".igraph.pars", current, envir = env) invisible(current) } getIgraphOpt <- function(x, default=NULL) { if (missing(default)) return(igraph.options(x)[[1L]]) if (x %in% names(igraph.options())) igraph.options(x)[[1L]] else default } ## This is deprecated from 0.6 igraph.par <- function(parid, parvalue=NULL) { .Deprecated("igraph.options", package="igraph") if (is.null(parvalue)) { res <- .igraph.pars[[parid]] res } else { .igraph.pars[[parid]] <- parvalue invisible(parvalue) } } igraph/R/plot.shapes.R0000644000176000001440000005721712263024035014351 0ustar ripleyusers # IGraph R package # Copyright (C) 2003-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### ## API design ## ## A vertex shape is defined by two functions: the clipping function and ## the plotting function. ## ## The clipping function is called to determine where to put the ## arrowhead of a potential (incoming) incident edge. Its signature is ## function(coords, el, params, end=c("both", "from", "to")) ## where the arguments are: ## coords A matrix with one row for each edge, and four columns. ## It contains the coordinates of the end points of all ## edges. The first two columns are the coordinates of the ## first end points (sources, if the graph is directed), ## the last two columns are for the other end points ## (targets if the graph is directed). ## el The edge list itself, with vertex ids. ## params A function object to query plotting parameters. ## end Which end points to calculate. "both" means both, ## "from" means the first end point, "to" the second. ## The clipping function must return the new version of "coords", ## modified according to the vertex sizes/shapes, with proper positions ## for the potential arrow heads. The positions are for the tips of the ## arrows. ## ## The plotting function plots the vertex. Its signature is ## function(coords, v=NULL, params) ## where the arguments are ## coords Two column matrix, the coordinates for the vertices to draw. ## v The vertex ids of the vertices to draw. If NULL, then all ## vertices are drawn. ## params A function object to query plotting parameters. ## ## vertex.shapes() - lists all vertex shapes ## vertex.shapes(shape) - returns the clipping and plotting functions ## for a given vertex shape ## add.vertex.shape() - adds a new vertex shape, the clipping and ## plotting functions must be given, and ## optionally the newly introduced plotting ## parameters. This function can also be used ## to overwrite a given vertex shape. ## ## Examples: ## add.vertex.shapes("image", clip=image.clip, plot=image.plot, ## parameters=list(filename=NA)) ## ## add.vertex.shapes("triangle", clip=vertex.shapes("circle")$clip, ## plot=triangle.plot) ## ## add.vertex.shapes("polygon", clip=vertex.shapes("circle")$clip, ## plot=polygon.plot) ## ################################################################### vertex.shapes <- function(shape=NULL) { if (is.null(shape)) { ls(.igraph.shapes) } else { ## checkScalarString(shape) .igraph.shapes[[shape]] } } igraph.shape.noclip <- function(coords, el, params, end=c("both", "from", "to")) { end <- igraph.match.arg(end) if (end=="both") { coords } else if (end=="from") { coords[,1:2,drop=FALSE] } else { coords[,3:4,drop=FALSE] } } igraph.shape.noplot <- function(coords, v=NULL, params) { invisible(NULL) } add.vertex.shape <- function(shape, clip=igraph.shape.noclip, plot=igraph.shape.noplot, parameters=list()) { ## TODO ## checkScalarString(shape) ## checkFunction(clip) ## checkFunction(plot) ## checkList(parameters, named=TRUE) assign(shape, value=list(clip=clip, plot=plot), envir=.igraph.shapes) do.call(igraph.options, parameters) invisible(TRUE) } ## These are the predefined shapes .igraph.shape.circle.clip <- function(coords, el, params, end=c("both", "from", "to")) { end <- match.arg(end) if (length(coords)==0) { return (coords) } vertex.size <- 1/200 * params("vertex", "size") if (end=="from") { phi <- atan2(coords[,4] - coords[,2], coords[,3] - coords[,1]) vsize.from <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } res <- cbind(coords[,1] + vsize.from*cos(phi), coords[,2] + vsize.from*sin(phi) ) } else if (end=="to") { phi <- atan2(coords[,4] - coords[,2], coords[,3] - coords[,1]) r <- sqrt( (coords[,3] - coords[,1])^2 + (coords[,4] - coords[,2])^2 ) vsize.to <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } res <- cbind(coords[,1] + (r-vsize.to)*cos(phi), coords[,2] + (r-vsize.to)*sin(phi) ) } else if (end=="both") { phi <- atan2(coords[,4] - coords[,2], coords[,3] - coords[,1]) r <- sqrt( (coords[,3] - coords[,1])^2 + (coords[,4] - coords[,2])^2 ) vsize.from <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } vsize.to <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } res <- cbind(coords[,1] + vsize.from*cos(phi), coords[,2] + vsize.from*sin(phi), coords[,1] + (r-vsize.to)*cos(phi), coords[,2] + (r-vsize.to)*sin(phi) ) } res } .igraph.shape.circle.plot <- function(coords, v=NULL, params) { vertex.color <- params("vertex", "color") if (length(vertex.color) != 1 && !is.null(v)) { vertex.color <- vertex.color[v] } vertex.frame.color <- params("vertex", "frame.color") if (length(vertex.frame.color) != 1 && !is.null(v)) { vertex.frame.color <- vertex.frame.color[v] } vertex.size <- 1/200 * params("vertex", "size") if (length(vertex.size) != 1 && !is.null(v)) { vertex.size <- vertex.size[v] } vertex.size <- rep(vertex.size, length=nrow(coords)) symbols(x=coords[,1], y=coords[,2], bg=vertex.color, fg=vertex.frame.color, circles=vertex.size, add=TRUE, inches=FALSE) } .igraph.shape.square.clip <- function(coords, el, params, end=c("both", "from", "to")) { end <- match.arg(end) if (length(coords)==0) { return (coords) } vertex.size <- 1/200 * params("vertex", "size") square.shift <- function(x0, y0, x1, y1, vsize) { m <- (y0-y1)/(x0-x1) l <- cbind(x1-vsize/m , y1-vsize, x1-vsize , y1-vsize*m, x1+vsize/m, y1+vsize, x1+vsize , y1+vsize*m ) v <- cbind(x1-vsize <= l[,1] & l[,1] <= x1+vsize & y1-vsize <= l[,2] & l[,2] <= y1+vsize, x1-vsize <= l[,3] & l[,3] <= x1+vsize & y1-vsize <= l[,4] & l[,4] <= y1+vsize, x1-vsize <= l[,5] & l[,5] <= x1+vsize & y1-vsize <= l[,6] & l[,6] <= y1+vsize, x1-vsize <= l[,7] & l[,7] <= x1+vsize & y1-vsize <= l[,8] & l[,8] <= y1+vsize) d <- cbind((l[,1]-x0)^2 + (l[,2]-y0)^2, (l[,3]-x0)^2 + (l[,4]-y0)^2, (l[,5]-x0)^2 + (l[,6]-y0)^2, (l[,7]-x0)^2 + (l[,8]-y0)^2) t(sapply(seq(length=nrow(l)), function(x) { d[x,][!v[x,]] <- Inf m <- which.min(d[x,]) l[x, c(m*2-1, m*2)] })) } if (end %in% c("from", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } res <- res1 <- square.shift(coords[,3], coords[,4], coords[,1], coords[,2], vsize) } if (end %in% c("to", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } res <- res2 <- square.shift(coords[,1], coords[,2], coords[,3], coords[,4], vsize) } if (end=="both") { res <- cbind(res1, res2) } res } .igraph.shape.square.plot <- function(coords, v=NULL, params) { vertex.color <- params("vertex", "color") if (length(vertex.color) != 1 && !is.null(v)) { vertex.color <- vertex.color[v] } vertex.frame.color <- params("vertex", "frame.color") if (length(vertex.frame.color) != 1 && !is.null(v)) { vertex.frame.color <- vertex.frame.color[v] } vertex.size <- 1/200 * params("vertex", "size") if (length(vertex.size) != 1 && !is.null(v)) { vertex.size <- vertex.size[v] } vertex.size <- rep(vertex.size, length=nrow(coords)) symbols(x=coords[,1], y=coords[,2], bg=vertex.color, fg=vertex.frame.color, squares=2*vertex.size, add=TRUE, inches=FALSE) } .igraph.shape.csquare.clip <- function(coords, el, params, end=c("both", "from", "to")) { end <- match.arg(end) if (length(coords)==0) { return (coords) } vertex.size <- 1/200 * params("vertex", "size") square.shift <- function(x0, y0, x1, y1, vsize) { l <- cbind(x1, y1-vsize, x1-vsize, y1, x1, y1+vsize, x1+vsize, y1) d <- cbind((l[,1]-x0)^2 + (l[,2]-y0)^2, (l[,3]-x0)^2 + (l[,4]-y0)^2, (l[,5]-x0)^2 + (l[,6]-y0)^2, (l[,7]-x0)^2 + (l[,8]-y0)^2) t(sapply(seq(length=nrow(l)), function(x) { m <- which.min(d[x,]) l[x, c(m*2-1, m*2)] })) } if (end %in% c("from", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } res <- res1 <- square.shift(coords[,3], coords[,4], coords[,1], coords[,2], vsize) } if (end %in% c("to", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } res <- res2 <- square.shift(coords[,1], coords[,2], coords[,3], coords[,4], vsize) } if (end=="both") { res <- cbind(res1, res2) } res } .igraph.shape.csquare.plot <- .igraph.shape.square.plot .igraph.shape.rectangle.clip <- function(coords, el, params, end=c("both", "from", "to")) { end <- match.arg(end) if (length(coords)==0) { return (coords) } vertex.size <- 1/200 * params("vertex", "size") vertex.size2 <- 1/200 * params("vertex", "size2") rec.shift <- function(x0, y0, x1, y1, vsize, vsize2) { m <- (y0-y1)/(x0-x1) l <- cbind(x1-vsize/m, y1-vsize2, x1-vsize, y1-vsize*m, x1+vsize2/m, y1+vsize2, x1+vsize, y1+vsize*m ) v <- cbind(x1-vsize <= l[,1] & l[,1] <= x1+vsize & y1-vsize2 <= l[,2] & l[,2] <= y1+vsize2, x1-vsize <= l[,3] & l[,3] <= x1+vsize & y1-vsize2 <= l[,4] & l[,4] <= y1+vsize2, x1-vsize <= l[,5] & l[,5] <= x1+vsize & y1-vsize2 <= l[,6] & l[,6] <= y1+vsize2, x1-vsize <= l[,7] & l[,7] <= x1+vsize & y1-vsize2 <= l[,8] & l[,8] <= y1+vsize2) d <- cbind((l[,1]-x0)^2 + (l[,2]-y0)^2, (l[,3]-x0)^2 + (l[,4]-y0)^2, (l[,5]-x0)^2 + (l[,6]-y0)^2, (l[,7]-x0)^2 + (l[,8]-y0)^2) t(sapply(seq(length=nrow(l)), function(x) { d[x,][!v[x,]] <- Inf m <- which.min(d[x,]) l[x, c(m*2-1, m*2)] })) } if (end %in% c("from", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } vsize2 <- if (length(vertex.size2)==1) { vertex.size2 } else { vertex.size2[ el[,1] ] } res <- res1 <- rec.shift(coords[,3], coords[,4], coords[,1], coords[,2], vsize, vsize2) } if (end %in% c("to", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } vsize2 <- if (length(vertex.size2)==1) { vertex.size2 } else { vertex.size2[ el[,2] ] } res <- res2 <- rec.shift(coords[,1], coords[,2], coords[,3], coords[,4], vsize, vsize2) } if (end=="both") { res <- cbind(res1, res2) } res } .igraph.shape.rectangle.plot <- function(coords, v=NULL, params) { vertex.color <- params("vertex", "color") if (length(vertex.color) != 1 && !is.null(v)) { vertex.color <- vertex.color[v] } vertex.frame.color <- params("vertex", "frame.color") if (length(vertex.frame.color) != 1 && !is.null(v)) { vertex.frame.color <- vertex.frame.color[v] } vertex.size <- 1/200 * params("vertex", "size") if (length(vertex.size) != 1 && !is.null(v)) { vertex.size <- vertex.size[v] } vertex.size <- rep(vertex.size, length=nrow(coords)) vertex.size2 <- 1/200 * params("vertex", "size2") if (length(vertex.size2) != 1 && !is.null(v)) { vertex.size2 <- vertex.size2[v] } vertex.size <- cbind(vertex.size, vertex.size2) symbols(x=coords[,1], y=coords[,2], bg=vertex.color, fg=vertex.frame.color, rectangles=2*vertex.size, add=TRUE, inches=FALSE) } .igraph.shape.crectangle.clip <- function(coords, el, params, end=c("both", "from", "to")) { end <- match.arg(end) if (length(coords)==0) { return (coords) } vertex.size <- 1/200 * params("vertex", "size") vertex.size2 <- 1/200 * params("vertex", "size2") rec.shift <- function(x0, y0, x1, y1, vsize, vsize2) { l <- cbind(x1, y1-vsize2, x1-vsize, y1, x1, y1+vsize2, x1+vsize, y1) d <- cbind((l[,1]-x0)^2 + (l[,2]-y0)^2, (l[,3]-x0)^2 + (l[,4]-y0)^2, (l[,5]-x0)^2 + (l[,6]-y0)^2, (l[,7]-x0)^2 + (l[,8]-y0)^2) t(sapply(seq(length=nrow(l)), function(x) { m <- which.min(d[x,]) l[x, c(m*2-1, m*2)] })) } if (end %in% c("from", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } vsize2 <- if (length(vertex.size2)==1) { vertex.size2 } else { vertex.size2[ el[,1] ] } res <- res1 <- rec.shift(coords[,3], coords[,4], coords[,1], coords[,2], vsize, vsize2) } if (end %in% c("to", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } vsize2 <- if (length(vertex.size2)==1) { vertex.size2 } else { vertex.size2[ el[,2] ] } res <- res2 <- rec.shift(coords[,1], coords[,2], coords[,3], coords[,4], vsize, vsize2) } if (end=="both") { res <- cbind(res1, res2) } res } .igraph.shape.crectangle.plot <- .igraph.shape.rectangle.plot .igraph.shape.vrectangle.clip <- function(coords, el, params, end=c("both", "from", "to")) { end <- match.arg(end) if (length(coords)==0) { return (coords) } vertex.size <- 1/200 * params("vertex", "size") vertex.size2 <- 1/200 * params("vertex", "size2") rec.shift <- function(x0, y0, x1, y1, vsize, vsize2) { l <- cbind(x1-vsize, y1, x1+vsize, y1) d <- cbind((l[,1]-x0)^2 + (l[,2]-y0)^2, (l[,3]-x0)^2 + (l[,4]-y0)^2) t(sapply(seq(length=nrow(l)), function(x) { m <- which.min(d[x,]) l[x, c(m*2-1, m*2)] })) } if (end %in% c("from", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } vsize2 <- if (length(vertex.size2)==1) { vertex.size2 } else { vertex.size2[ el[,1] ] } res <- res1 <- rec.shift(coords[,3], coords[,4], coords[,1], coords[,2], vsize, vsize2) } if (end %in% c("to", "both")) { vsize <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } vsize2 <- if (length(vertex.size2)==1) { vertex.size2 } else { vertex.size2[ el[,2] ] } res <- res2 <- rec.shift(coords[,1], coords[,2], coords[,3], coords[,4], vsize, vsize2) } if (end=="both") { res <- cbind(res1, res2) } res } .igraph.shape.vrectangle.plot <- .igraph.shape.rectangle.plot .igraph.shape.none.clip <- .igraph.shape.circle.clip .igraph.shape.none.plot <- function(coords, v=NULL, params) { ## does not plot anything at all invisible(NULL) } mypie <- function(x, y, values, radius, edges=200, col=NULL, angle=45, density=NULL, border=NULL, lty=NULL, init.angle=90, ...) { values <- c(0, cumsum(values)/sum(values)) dx <- diff(values) nx <- length(dx) twopi <- 2 * pi if (is.null(col)) col <- if (is.null(density)) c("white", "lightblue", "mistyrose", "lightcyan", "lavender", "cornsilk") else par("fg") col <- rep(col, length.out = nx) border <- rep(border, length.out = nx) lty <- rep(lty, length.out = nx) angle <- rep(angle, length.out = nx) density <- rep(density, length.out = nx) t2xy <- function(t) { t2p <- twopi * t + init.angle * pi/180 list(x = radius * cos(t2p), y = radius * sin(t2p)) } for (i in 1:nx) { n <- max(2, floor(edges * dx[i])) P <- t2xy(seq.int(values[i], values[i + 1], length.out = n)) polygon(x+c(P$x, 0), y+c(P$y, 0), density = density[i], angle = angle[i], border = border[i], col = col[i], lty = lty[i], ...) } } .igraph.shape.pie.clip <- function(coords, el, params, end=c("both", "from", "to")) { end <- match.arg(end) if (length(coords)==0) { return (coords) } vertex.size <- 1/200 * params("vertex", "size") if (end=="from") { phi <- atan2(coords[,4] - coords[,2], coords[,3] - coords[,1]) vsize.from <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } res <- cbind(coords[,1] + vsize.from*cos(phi), coords[,2] + vsize.from*sin(phi) ) } else if (end=="to") { phi <- atan2(coords[,4] - coords[,2], coords[,3] - coords[,1]) r <- sqrt( (coords[,3] - coords[,1])^2 + (coords[,4] - coords[,2])^2 ) vsize.to <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } res <- cbind(coords[,1] + (r-vsize.to)*cos(phi), coords[,2] + (r-vsize.to)*sin(phi) ) } else if (end=="both") { phi <- atan2(coords[,4] - coords[,2], coords[,3] - coords[,1]) r <- sqrt( (coords[,3] - coords[,1])^2 + (coords[,4] - coords[,2])^2 ) vsize.from <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,1] ] } vsize.to <- if (length(vertex.size)==1) { vertex.size } else { vertex.size[ el[,2] ] } res <- cbind(coords[,1] + vsize.from*cos(phi), coords[,2] + vsize.from*sin(phi), coords[,1] + (r-vsize.to)*cos(phi), coords[,2] + (r-vsize.to)*sin(phi) ) } res } .igraph.shape.pie.plot <- function(coords, v=NULL, params) { getparam <- function(pname) { p <- params("vertex", pname) if (length(p) != 1 && !is.null(v)) { p <- p[v] } p } vertex.color <- getparam("color") vertex.frame.color <- getparam("frame.color") vertex.size <- rep(1/200 * getparam("size"), length=nrow(coords)) vertex.pie <- getparam("pie") vertex.pie.color <- getparam("pie.color") vertex.pie.angle <- getparam("pie.angle") vertex.pie.density <- getparam("pie.density") vertex.pie.lty <- getparam("pie.lty") for (i in seq_len(nrow(coords))) { pie <- if(length(vertex.pie)==1) { vertex.pie[[1]] } else { vertex.pie[[i]] } col <- if (length(vertex.pie.color)==1) { vertex.pie.color[[1]] } else { vertex.pie.color[[i]] } mypie(x=coords[i,1], y=coords[i,2], pie, radius=vertex.size[i], edges=200, col=col, angle=na.omit(vertex.pie.angle[c(i,1)])[1], density=na.omit(vertex.pie.density[c(i,1)])[1], border=na.omit(vertex.frame.color[c(i,1)])[1], lty=na.omit(vertex.pie.lty[c(i,1)])[1]) } } .igraph.shape.sphere.clip <- .igraph.shape.circle.clip .igraph.shape.sphere.plot <- function(coords, v=NULL, params) { getparam <- function(pname) { p <- params("vertex", pname) if (length(p) != 1 && !is.null(v)) { p <- p[v] } p } vertex.color <- rep(getparam("color"), length=nrow(coords)) vertex.size <- rep(1/200 * getparam("size"), length=nrow(coords)) ## Need to create a separate image for every different vertex color allcols <- unique(vertex.color) images <- lapply(allcols, function(col) { img <- .Call("R_igraph_getsphere", pos=c(0.0,0.0,10.0), radius=7.0, color=col2rgb(col)/255, bgcolor=c(0,0,0), lightpos=list(c(-2,2,2)), lightcolor=list(c(1,1,1)), width=100L, height=100L, PACKAGE="igraph") as.raster(img) }) whichImage <- match(vertex.color, allcols) for (i in seq_len(nrow(coords))) { vsp2 <- vertex.size[i] rasterImage(images[[ whichImage[i] ]], coords[i,1]-vsp2, coords[i,2]-vsp2, coords[i,1]+vsp2, coords[i,2]+vsp2) } } .igraph.shape.raster.clip <- .igraph.shape.rectangle.clip .igraph.shape.raster.plot <- function(coords, v=NULL, params) { getparam <- function(pname) { p <- params("vertex", pname) if (is.list(p) && length(p) != 1 && !is.null(v)) { p <- p[v] } p } size <- rep(1/200 * getparam("size"), length=nrow(coords)) size2 <- rep(1/200 * getparam("size2"), length=nrow(coords)) raster <- getparam("raster") for (i in seq_len(nrow(coords))) { ras <- if (!is.list(raster) || length(raster)==1) raster else raster[[i]] rasterImage(ras, coords[i,1]-size[i], coords[i,2]-size2[i], coords[i,1]+size[i], coords[i,2]+size2[i]) } } .igraph.shapes <- new.env() .igraph.shapes[["circle"]] <- list(clip=.igraph.shape.circle.clip, plot=.igraph.shape.circle.plot) .igraph.shapes[["square"]] <- list(clip=.igraph.shape.square.clip, plot=.igraph.shape.square.plot) .igraph.shapes[["csquare"]] <- list(clip=.igraph.shape.csquare.clip, plot=.igraph.shape.csquare.plot) .igraph.shapes[["rectangle"]] <- list(clip=.igraph.shape.rectangle.clip, plot=.igraph.shape.rectangle.plot) .igraph.shapes[["crectangle"]] <- list(clip=.igraph.shape.crectangle.clip, plot=.igraph.shape.crectangle.plot) .igraph.shapes[["vrectangle"]] <- list(clip=.igraph.shape.vrectangle.clip, plot=.igraph.shape.vrectangle.plot) .igraph.shapes[["none"]] <- list(clip=.igraph.shape.none.clip, plot=.igraph.shape.none.plot) .igraph.shapes[["pie"]] <- list(clip=.igraph.shape.pie.clip, plot=.igraph.shape.pie.plot) .igraph.shapes[["sphere"]] <- list(clip=.igraph.shape.sphere.clip, plot=.igraph.shape.sphere.plot) .igraph.shapes[["raster"]] <- list(clip=.igraph.shape.raster.clip, plot=.igraph.shape.raster.plot) igraph/R/cohesive.blocks.R0000644000176000001440000002415612240234657015176 0ustar ripleyusers # IGraph R package # Copyright (C) 2010-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### cohesive.blocks <- function(graph, labels=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_cohesive_blocks", graph, PACKAGE="igraph") class(res) <- "cohesiveBlocks" if (labels && "name" %in% list.vertex.attributes(graph)) { res$labels <- V(graph)$name } res$vcount <- vcount(graph) res } length.cohesiveBlocks <- function(x) { length(x$blocks) } blocks <- function(blocks) { blocks$blocks } blockGraphs <- function(blocks, graph) { lapply(blocks(blocks), induced.subgraph, graph=graph) } cohesion <- function(blocks) { blocks$cohesion } hierarchy <- function(blocks) { blocks$blockTree } parent <- function(blocks) { blocks$parent } print.cohesiveBlocks <- function(x, ...) { cat("Cohesive block structure:\n") myb <- blocks(x) ch <- cohesion(x) pp <- parent(x) si <- sapply(myb, length) cs <- 3 + 2 + nchar(length(x)) + max(shortest.paths(hierarchy(x), mode="out", v=1)) * 3 .plot <- function(b, ind="") { if (b!=1) { he <- format(paste(sep="", ind, "'- B-", b), width=cs) ind <- paste(" ", ind) } else { he <- format(paste(sep="", "B-", b), width=cs) } cat(sep="", he, "c ", format(ch[b], width=nchar(max(ch)), justify="right"), ", n ", format(si[b], width=nchar(x$vcount), justify="right")) if (x$vcount <= options("width")$width-40 && b != 1) { o <- rep(".", x$vcount) o[ myb[[b]] ] <- "o" oo <- character() for (i in 1:floor(x$vcount/10)) { oo <- c(oo, o[((i-1)*10+1):(i*10)], " ") } if (x$vcount %% 10) { oo <- c(oo, o[(i*10+1):length(o)]) } cat(" ", paste(oo, collapse=""), "\n") } else { cat("\n") } wc <- which(pp==b) sapply(wc, .plot, ind=ind) } if (length(x) >0) .plot(1) else cat("No cohesive blocks found.") invisible(x) } summary.cohesiveBlocks <- function(object, ...) { cat("Structurally cohesive block structure, with", length(blocks(object)), "blocks.\n") invisible(object) } plot.cohesiveBlocks <- function(x, y, colbar=rainbow(max(cohesion(x))+1), col=colbar[maxcohesion(x)+1], mark.groups=blocks(x)[-1], ...) { plot(y, mark.groups=mark.groups, vertex.color=col, ...) } plotHierarchy <- function(blocks, layout=layout.reingold.tilford(hierarchy(blocks), root=1), ...) { plot(hierarchy(blocks), layout=layout, ...) } exportPajek.cohesiveblocks.pf <- function(blocks, graph, file) { closeit <- FALSE if (is.character(file)) { file <- file(file, open = "w+b") closeit <- TRUE } if (!isOpen(file)) { file <- open(file) closeit <- TRUE } ## The original graph cat(file=file, sep="", "*Network cohesive_blocks_input.net\r\n") write.graph(graph, file=file, format="pajek") ## The hierarchy graph cat(file=file, sep="", "\r\n*Network hierarchy.net\r\n") write.graph(hierarchy(blocks), file=file, format="pajek") ## The blocks myb <- blocks(blocks) for (b in seq_along(myb)) { thisb <- rep(0, vcount(graph)) thisb[ myb[[b]] ] <- 1 cat(file=file, sep="", "\r\n*Partition block_", b, ".clu\r\n", "*Vertices ", vcount(graph), "\r\n ") cat(thisb, sep="\r\n ", file=file) } if (closeit) { close(file) } invisible(NULL) } exportPajek.cohesiveblocks.nopf <- function(blocks, graph, file) { ## The original graph write.graph(graph, file=paste(sep="", file, ".net"), format="pajek") ## The hierarchy graph write.graph(hierarchy(blocks), file=paste(sep="", file, "_hierarchy.net"), format="pajek") ## The blocks myb <- blocks(blocks) for (b in seq_along(myb)) { thisb <- rep(0, vcount(graph)) thisb[ myb[[b]] ] <- 1 cat(file=paste(sep="", file, "_block_", b, ".clu"), sep="\r\n", paste("*Vertices", vcount(graph)), thisb) } invisible(NULL) } exportPajek <- function(blocks, graph, file, project.file=TRUE) { if (!project.file && !is.character(file)) { stop(paste("`file' must be a filename (without extension) when writing", "to separate files")) } if (project.file) { return(exportPajek.cohesiveblocks.pf(blocks, graph, file)) } else { return(exportPajek.cohesiveblocks.nopf(blocks, graph, file)) } } maxcohesion <- function(blocks) { res <- numeric(blocks$vcount) myb <- blocks(blocks) coh <- cohesion(blocks) oo <- order(coh) myb <- myb[oo] coh <- coh[oo] for (b in seq_along(myb)) { res[ myb[[b]] ] <- coh[b] } res } ######################################################### ## Various designs to print the cohesive blocks ## Cohesive block structure: ## B-1 c. 1, n. 34 ## '- B-2 c. 2, n. 28 1,2,3,4,8,9,10,13,14,15,16,18,19,20,21,22, ## | 23,24,25,26,27,28,29,30,31,32,33,34 ## '- B-4 c. 4, n. 5 1,2,3,4,8 ## '- B-5 c. 3, n. 7 1,2,3,9,31,33,34 ## '- B-7 c. 4, n. 5 1,2,3,4,14 ## '- B-8 c. 3, n. 10 3,24,25,26,28,29,30,32,33,34 ## '- B-3 c. 2, n. 6 1,5,6,7,11,17 ## '- B-6 c. 3, n. 5 1,5,6,7,11 ## Cohesive block structure: ## B-1 c. 1, n. 23 ## '- B-2 c. 2, n. 14 1,2,3,4,5,6,7,8,12,19,20,21,22,23 ## '- B-4 c. 5, n. 7 1,2,3,4,5,6,7 ## '- B-3 c. 2, n. 10 7,9,10,11,13,14,15,16,17,18 ## '- B-5 c. 3, n. 4 7,9,10,11 ## ######################################################### ## Cohesive block structure: ## B-1 c 1, n 34 ## '- B-2 c 2, n 28 oooo...ooo ..oooo.ooo oooooooooo oooo ## '- B-4 c 4, n 5 oooo...o.. .......... .......... .... ## '- B-5 c 3, n 7 ooo.....o. .......... .......... o.oo ## '- B-7 c 4, n 5 oooo...... ...o...... .......... .... ## '- B-8 c 3, n 10 ..o....... .......... ...ooo.ooo .ooo ## '- B-3 c 2, n 6 o...ooo... o.....o... .......... .... ## '- B-6 c 3, n 5 o...ooo... o......... .......... .... ## Cohesive block structure: ## B-1 c 1, n 23 oooooooooo oooooooooo ooo ## '- B-2 c 2, n 14 oooooooo.. .o......oo ooo ## '- B-4 c 5, n 7 ooooooo... .......... ... ## '- B-3 c 2, n 10 ......o.oo o.oooooo.. ... ## '- B-5 c 3, n 4 ......o.oo o......... ... ## ######################################################### ## Cohesive block structure: ## B-1 c. 1, n. 34 ## '- B-2 c. 2, n. 28 1, 2, 3, 4, 8, 9,10,13,14,15,16,18,19,20,21, ## | 22,23,24,25,26,27,28,29,30,31,32,33,34 ## '- B-4 c. 4, n. 5 1, 2, 3, 4, 8 ## '- B-5 c. 3, n. 7 1, 2, 3, 9,31,33,34 ## '- B-7 c. 4, n. 5 1, 2, 3, 4,14 ## '- B-8 c. 3, n. 10 3,24,25,26,28,29,30,32,33,34 ## '- B-3 c. 2, n. 6 1, 5, 6, 7,11,17 ## '- B-6 c. 3, n. 5 1, 5, 6, 7,11 ## Cohesive block structure: ## B-1 c. 1, n. 23 ## '- B-2 c. 2, n. 14 1, 2, 3, 4, 5, 6, 7, 8,12,19,20,21,22,23 ## '- B-4 c. 5, n. 7 1, 2, 3, 4, 5, 6, 7 ## '- B-3 c. 2, n. 10 7, 9,10,11,13,14,15,16,17,18 ## '- B-5 c. 3, n. 4 7, 9,10,11 ## ######################################################### ## Cohesive block structure: ## B-1 c. 1, n. 34 ## '- B-2 c. 2, n. 28 1-4, 8-10, 13-16, 18-34 ## '- B-4 c. 4, n. 5 1-4, 8 ## '- B-5 c. 3, n. 7 1-3, 9, 31, 33-34 ## '- B-7 c. 4, n. 5 1-4, 14 ## '- B-8 c. 3, n. 10 3, 24-26, 28-30, 32-34 ## '- B-3 c. 2, n. 6 1, 5-7, 11, 17 ## '- B-6 c. 3, n. 5 1, 5-7, 11 ## Cohesive block structure: ## B-1 c. 1, n. 23 ## '- B-2 c. 2, n. 14 1-8, 12, 19-23 ## '- B-4 c. 5, n. 7 1-7 ## '- B-3 c. 2, n. 10 7, 9-11, 13-18 ## '- B-5 c. 3, n. 4 7, 9-11 ## ########################################################## ## Cohesive block structure: ## B-1 c. 1, n. 34 ## |- B-2 c. 2, n. 28 [ 1] oooo...ooo ..oooo.ooo ## | | [21] oooooooooo oooo ## | |- B-4 c. 4, n. 5 [ 1] oooo...o.. .......... ## | | [21] .......... .... ## | |- B-5 c. 3, n. 7 [ 1] ooo.....o. .......... ## | | [21] .......... o.oo ## | |- B-7 c. 4, n. 5 [ 1] oooo...... ...o...... ## | | [21] .......... .... ## | |- B-8 c. 3, n. 10 [ 1] ..o....... .......... ## | [21] ...ooo.ooo .ooo ## '- B-3 c. 2, n. 6 [ 1] o...ooo... o.....o... ## | [21] .......... .... ## '- B-6 c. 3, n. 5 [ 1] o...ooo... o......... ## [21] .......... .... ## Cohesive block structure: ## B-1 c. 1, n. 23 [ 1] oooooooooo oooooooooo ## | [21] ooo ## |- B-2 c. 2, n. 14 [ 1] oooooooo.. .o......oo ## | | [21] ooo ## | '- B-4 c. 5, n. 7 [ 1] ooooooo... .......... ## | [21] ... ## '- B-3 c. 2, n. 10 [ 1] ......o.oo o.oooooo.. ## | [21] ... ## '- B-5 c. 3, n. 4 [ 1] ......o.oo o......... ## [21] ... igraph/R/components.R0000644000176000001440000000530012251656216014271 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Connected components, subgraphs, kinda ################################################################### no.clusters <- function(graph, mode=c("weak", "strong")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "weak"=1, "strong"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_no_clusters", graph, as.numeric(mode), PACKAGE="igraph") } cluster.distribution <- function(graph, cumulative=FALSE, mul.size=FALSE, ...) { if (!is.igraph(graph)) { stop("Not a graph object") } cs <- clusters(graph, ...)$csize; hi <- hist(cs, -1:max(cs), plot=FALSE)$density if (mul.size) { hi <- hi*1:max(cs) hi <- hi/sum(hi) } if (!cumulative) { res <- hi } else { res <- rev(cumsum(rev(hi))); } res } is.connected <- function(graph, mode=c("weak", "strong")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "weak"=1, "strong"=2) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_is_connected", graph, as.numeric(mode), PACKAGE="igraph") } decompose.graph <- function(graph, mode=c("weak", "strong"), max.comps=NA, min.vertices=0) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "weak"=1, "strong"=2) if (is.na(max.comps)) { max.comps=-1 } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_decompose", graph, as.numeric(mode), as.numeric(max.comps), as.numeric(min.vertices), PACKAGE="igraph" ) } igraph/R/centrality.R0000644000176000001440000000636212251656216014273 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### igraph.arpack.default <- list(bmat="I", n=0, which="XX", nev=1, tol=0.0, ncv=3, ldv=0, ishift=1, maxiter=3000, nb=1, mode=1, start=0, sigma=0.0, sigmai=0.0) arpack <- function(func, extra=NULL, sym=FALSE, options=igraph.arpack.default, env=parent.frame(), complex=!sym) { if (!is.list(options) || (is.null(names(options)) && length(options) != 0)) { stop("options must be a named list") } if (any(names(options) == "")) { stop("all options must be named") } if (any(! names(options) %in% names(igraph.arpack.default))) { stop("unkown ARPACK option(s): ", paste(setdiff(names(options), names(igraph.arpack.default)), collapse=", ")) } options.tmp <- igraph.arpack.default options.tmp[ names(options) ] <- options options <- options.tmp if (sym && complex) { complex <- FALSE warning("Symmetric matrix, setting `complex' to FALSE") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_arpack", func, extra, options, env, sym, PACKAGE="igraph") if (complex) { rew <- arpack.unpack.complex(res$vectors, res$values, min(res$options$nev, res$options$nconv)) res$vectors <- rew$vectors res$values <- rew$values res$values <- apply(res$values, 1, function(x) x[1]+x[2]*1i) dim(res$vectors) <- c(nrow(res$vectors)*2, ncol(res$vectors)/2) res$vectors <- apply(res$vectors, 2, function(x) { l <- length(x)/2 x[1:l] + x[(l+1):length(x)]*1i }) } else { if (is.matrix(res$values)) { if (!all(res$values[,2]==0)) { warning("Dropping imaginary parts of eigenvalues") } res$values <- res$values[,1] } res$vectors <- res$vectors[,1:length(res$values)] } res } subgraph.centrality <- function(graph, diag=FALSE) { A <- get.adjacency(graph) if (!diag) { diag(A) <- 0 } eig <- eigen(A) res <- as.vector(eig$vectors^2 %*% exp(eig$values)) if (getIgraphOpt("add.vertex.names") && is.named(graph)) { names(res) <- get.vertex.attribute(graph, "name") } res } igraph.eigen.default <- list(pos="LM", howmany=1L, il=-1L, iu=-1L, vl=-Inf, vu=Inf, vestimate=0L, balance="none") igraph/R/fit.R0000644000176000001440000000443312325356537012701 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Pit a power-law (khmm a Yule really) distribution, # this is a common degree distribution in networks ################################################################### power.law.fit <- function(x, xmin=NULL, start=2, force.continuous=FALSE, implementation=c("plfit", "R.mle"), ...) { implementation <- igraph.match.arg(implementation) if (implementation == "r.mle") { power.law.fit.old(x, xmin, start, ...) } else if (implementation == "plfit") { if (is.null(xmin)) xmin <- -1 power.law.fit.new(x, xmin=xmin, force.continuous=force.continuous) } } power.law.fit.old <- function(x, xmin=NULL, start=2, ...) { if (length(x) == 0) { stop("zero length vector") } if (length(x) == 1) { stop("vector should be at least of length two") } require(stats4) if (is.null(xmin)) { xmin <- min(x) } n <- length(x) x <- x[ x >= xmin] if (length(x) != n) { n <- length(x) } # mlogl <- function(alpha) { # if (xmin > 1) { # C <- 1/(1/(alpha-1)-sum(beta(1:(xmin-1), alpha))) # } else { # C <- alpha-1 # } # -n*log(C)-sum(lbeta(x, alpha)) # } mlogl <- function(alpha) { C <- 1/sum( (xmin:10000)^-alpha ) -n*log(C)+alpha*sum(log(x)) } alpha <- mle(mlogl, start=list(alpha=start), ...) alpha } igraph/R/basic.R0000644000176000001440000000307712271600260013164 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### is.igraph <- function(graph){ "igraph" %in% class(graph) } is.directed <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_is_directed", graph, PACKAGE="igraph") } get.edge <- function(graph, id) { if (!is.igraph(graph)) { stop("Not a graph object") } id <- as.numeric(id) ec <- ecount(graph) if (id < 1 || id > ec) { stop("No such edge") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_get_edge", graph, as.numeric(id)-1, PACKAGE="igraph") res+1 } igraph/R/test.R0000644000176000001440000000264012251656216013067 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2013 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### igraphtest <- function() { do.call(require, list("testthat")) tdir <- system.file("tests", package="igraph") do.call("test_dir", list(tdir)) } igraph.version <- function() { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_version", PACKAGE="igraph") } checkpkg <- function(package_file, args=character()) { package_file <- as.character(package_file) args <- as.character(args) do.call(":::", list("tools", ".check_packages"))(c(package_file, args)) } igraph/R/socnet.R0000644000176000001440000026053212263024035013400 0ustar ripleyusers # IGraph R package # Copyright (C) 2009-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### # TODO LIST: # * adding edges to a graph # * exporting graphics # * scroll bar for the graph list area == IMPOSSIBLE right now, should be a list # * window title in the error dialog # * keyboard shortcuts # * implement min & max in .tkigraph.dialog .tkigraph.env <- new.env() tkigraph <- function() { require(tcltk) || stop("tcl/tk library not available") options(scipen=10000) if (!exists("window", envir=.tkigraph.env, inherits=FALSE)) { assign("window", TRUE, envir=.tkigraph.env) assign("graphs", list(), envir=.tkigraph.env) assign("selected", list(), envir=.tkigraph.env) assign("tklines", list(), envir=.tkigraph.env) } else { stop("tkigraph window is already open!") } # Create top window top <- tktoplevel(background="lightgrey", width=700, height=400) tktitle(top) <- "iGraph GUI (Social Network Basics)" topframe <- tkframe(top, relief="sunken", borderwidth=1) scr <- tkscrollbar(top, repeatinterval=5, command=function(...) tkyview(topframe)) tkplace(topframe, x=0, y=0, relwidth=1.0) # Store myself in the environment if needed if (!exists("top", envir=.tkigraph.env, inherits=FALSE)) { assign("top", top, envir=.tkigraph.env) assign("topframe", topframe, envir=.tkigraph.env) } # kill myself if window was closed tkbind(top, "", function() .tkigraph.close()) # pull-down menu main.menu <- tkmenu(top) graph.menu <- tkmenu(main.menu) create.menu <- tkmenu(main.menu) tkadd(create.menu, "command", label="By hand", command=function() { .tkigraph.by.hand() }) tkadd(create.menu, "separator") tkadd(create.menu, "command", label="Ring", command=function() { .tkigraph.ring() }) tkadd(create.menu, "command", label="Tree", command=function() { .tkigraph.tree() }) tkadd(create.menu, "command", label="Lattice", command=function() { .tkigraph.lattice() }) tkadd(create.menu, "command", label="Star", command=function() { .tkigraph.star() }) tkadd(create.menu, "command", label="Full", command=function() { .tkigraph.full() }) tkadd(create.menu, "separator") tkadd(create.menu, "command", label="Graph atlas...", command=function() { .tkigraph.atlas() }) tkadd(create.menu, "separator") tkadd(create.menu, "command", label="Moody-White network", command=function() { g <- graph.adjacency(.tkigraph.net.moody.white, mode="undirected") g <- set.graph.attribute(g, "name", "Moody-White network") .tkigraph.add.graph(g) }) tkadd(create.menu, "separator") tkadd(create.menu, "command", label="Random (Erdos-Renyi G(n,p))", command=function() { .tkigraph.erdos.renyi.game() }) tkadd(create.menu, "command", label="Random (Erdos-Renyi G(n,m))", command=function() { .tkigraph.erdos.renyi.gnm.game() }) tkadd(create.menu, "command", label="Random (Barabasi-Albert)", command=function() { .tkigraph.barabasi.game() }) tkadd(create.menu, "command", label="Random (Configuration model)", command=function() { .tkigraph.degree.sequence.game() }) tkadd(create.menu, "command", label="Watts-Strogatz random graph", command=function() { .tkigraph.watts.strogatz() }) tkadd(create.menu, "separator") tkadd(create.menu, "command", label="Simplify", command=function() { .tkigraph.simplify() }) tkadd(graph.menu, "cascade", label="Create", menu=create.menu) tkadd(graph.menu, "command", label="Delete", command=function() { .tkigraph.delete() }) tkadd(graph.menu, "separator") tkadd(graph.menu, "command", label="Show graph", command=function() { .tkigraph.show() }) tkadd(graph.menu, "command", label="Basic statistics", command=function() { .tkigraph.stat() }) tkadd(graph.menu, "separator") tkadd(graph.menu, "command", label="Import session", command=function() { .tkigraph.load() }) # tkadd(graph.menu, "command", label="Load from the Web", command=function() { # .tkigraph.load.online() # }) tkadd(graph.menu, "command", label="Export session", command=function() { .tkigraph.save() }) tkadd(graph.menu, "separator") tkadd(graph.menu, "command", label="Import adjacency matrix", command=function() .tkigraph.import.adjacency()) tkadd(graph.menu, "command", label="Import edge list", command=function() .tkigraph.import.edgelist()) tkadd(graph.menu, "command", label="Import Pajek file", command=function() .tkigraph.import.pajek()) tkadd(graph.menu, "command", label="Export adjacency matrix", command=function() .tkigraph.export.adjacency()) tkadd(graph.menu, "command", label="Export edge list", command=function() .tkigraph.export.edgelist()) tkadd(graph.menu, "command", label="Export Pajek file", command=function() .tkigraph.export.pajek()) tkadd(main.menu, "cascade", label="Graph", menu=graph.menu) plot.menu <- tkmenu(main.menu) tkadd(plot.menu, "command", label="Simple", command=function() { .tkigraph.plot(simple=TRUE) }) tkadd(plot.menu, "command", label="Advanced", command=function() { .tkigraph.plot(simple=FALSE) }) tkadd(main.menu, "cascade", label="Draw", menu=plot.menu) centrality.menu <- tkmenu(main.menu) tkadd(centrality.menu, "command", label="Degree (out)", command=function() { .tkigraph.degree("out") }) tkadd(centrality.menu, "command", label="Degree (in)", command=function() { .tkigraph.degree("in") }) tkadd(centrality.menu, "command", label="Degree (total)", command=function() { .tkigraph.degree("total") }) tkadd(centrality.menu, "command", label="Plot log-log degree distribution", command=function() { .tkigraph.degree.dist(power=FALSE) }) tkadd(centrality.menu, "command", label="Fit a power-law to degree distribution", command=function() { .tkigraph.degree.dist(power=TRUE) }) tkadd(centrality.menu, "separator") tkadd(centrality.menu, "command", label="Closeness", command=function() { .tkigraph.closeness() }) tkadd(centrality.menu, "command", label="Betweenness", command=function() { .tkigraph.betweenness() }) tkadd(centrality.menu, "command", label="Burt's constraint", command=function() { .tkigraph.constraints() }) tkadd(centrality.menu, "command", label="Page rank", command=function() { .tkigraph.page.rank() }) tkadd(centrality.menu, "separator") tkadd(centrality.menu, "command", label="Edge betweenness", command=function() { .tkigraph.edge.betweenness() }) tkadd(main.menu, "cascade", label="Centrality", menu=centrality.menu) distances.menu <- tkmenu(main.menu) tkadd(distances.menu, "command", label="Distance matrix", command=function() { .tkigraph.dist.matrix() }) tkadd(distances.menu, "command", label="Distances from/to vertex", command=function() { .tkigraph.distance.tofrom() }) tkadd(distances.menu, "command", label="Diameter (undirected)", command=function() { .tkigraph.diameter() }) tkadd(distances.menu, "command", label="Draw diameter", command=function() { .tkigraph.plot.diameter(simple=FALSE) }) tkadd(distances.menu, "command", label="Average path length (undirected)", command=function() { .tkigraph.diameter(mode="path") }) tkadd(main.menu, "cascade", label="Distances", menu=distances.menu) component.menu <- tkmenu(main.menu) tkadd(component.menu, "command", label="Show components", command=function() { .tkigraph.clusters() }) tkadd(component.menu, "command", label="Show membership", command=function() { .tkigraph.clusters.membership() }) tkadd(component.menu, "command", label="Calculate component sizes", command=function() { .tkigraph.calculate.clusters() }) tkadd(component.menu, "command", label="Draw components", command=function() { .tkigraph.plot.comp(simple=FALSE) }) tkadd(component.menu, "command", label="Create graph from giant component", command=function() { .tkigraph.create.giantcomp() }) tkadd(component.menu, "command", label="Create graph from component of a vertex", command=function() { .tkigraph.create.mycomp() }) tkadd(component.menu, "command", label="Create graph from a component", command=function() { .tkigraph.create.comp() }) community.menu <- tkmenu(main.menu) tkadd(community.menu, "command", label="Spinglass algorithm", command=function() { .tkigraph.spinglass() }) tkadd(community.menu, "command", label="Spinglass algorithm, single vertex", command=function() { .tkigraph.my.spinglass() }) cohesion.menu <- tkmenu(main.menu) tkadd(cohesion.menu, "command", label="Cohesion of all components", command=function() { .tkigraph.cohesion() }) subgraph.menu <- tkmenu(main.menu) tkadd(subgraph.menu, "cascade", label="Components", menu=component.menu) tkadd(subgraph.menu, "cascade", label="Communities", menu=community.menu) tkadd(subgraph.menu, "cascade", label="Cohesion", menu=cohesion.menu) tkadd(main.menu, "cascade", label="Subgraphs", menu=subgraph.menu) motif.menu <- tkmenu(main.menu) tkadd(motif.menu, "command", label="Draw motifs", command=function() { .tkigraph.motifs.draw() }) tkadd(motif.menu, "command", label="Find motifs", command=function() { .tkigraph.motifs.find() }) tkadd(main.menu, "cascade", label="Motifs", menu=motif.menu) help.menu <- tkmenu(main.menu) tkadd(help.menu, "command", label="Contents", command=function() { .tkigraph.help() }) tkadd(help.menu, "command", label="In external browser", command=function() { .tkigraph.help.external() }) tkadd(help.menu, "separator") tkadd(help.menu, "command", label="About", command=function() { .tkigraph.about() }) tkadd(main.menu, "cascade", label="Help", menu=help.menu) tkadd(main.menu, "command", label="Quit", command=.tkigraph.close) tkconfigure(top, "-menu", main.menu) # Set up the main area tkgrid(tklabel(top, text=""), tklabel(top, text="#", justify="center", relief="raised"), tklabel(top, text="Name", width=50, relief="raised", justify="left"), tklabel(top, text="|V|", width=6, relief="raised", justify="left"), tklabel(top, text="|E|", width=6, relief="raised", justify="left"), tklabel(top, text="Dir.", width=6, relief="raised", justify="left"), sticky="nsew", "in"=topframe) tkgrid.columnconfigure(topframe, 2, weight=1) invisible(NULL) } .tkigraph.close <- function() { message <- "Are you sure?" yesno <- tkmessageBox(message=message, icon="question", type="yesno", default="yes") if (as.character(yesno) == "no") { return() } top <- get("top", .tkigraph.env) tkbind(top, "", "") tkdestroy(top) rm(list=ls(envir=.tkigraph.env), envir=.tkigraph.env) } .tkigraph.get.selected <- function() { gnos <- get("selected", .tkigraph.env) which(as.logical(sapply(gnos, tclvalue))) } .tkigraph.error <- function(message) { tkmessageBox(message=message, icon="error", type="ok") } .tkigraph.warning <- function(message) { tkmessageBox(message=message, icon="warning", type="ok") } .tkigraph.dialogbox <- function(TITLE="Setup parameters", ...) { params <- list(...) answers <- lapply(params, "[[", "default") dialog <- tktoplevel() frame <- tkframe(dialog) tkgrid(frame) tktitle(dialog) <- TITLE vars <- lapply(answers, tclVar) retval <- list() widgets <- list() OnOK <- function() { retval <<- lapply(vars, tclvalue) for (i in seq(along=params)) { if (params[[i]]$type == "listbox") { retval[[i]] <<- as.numeric(tclvalue(tkcurselection(widgets[[i]]))) } } tkdestroy(dialog) } tkgrid(tklabel(dialog, text=TITLE, font=tkfont.create(family="times", size="16", weight="bold")), columnspan=2, sticky="nsew", "in"=frame, padx=10, pady=10) OK.but <- tkbutton(dialog, text=" OK ", command=OnOK) for (i in seq(along=params)) { tkgrid(tklabel(dialog, text=params[[i]]$name), column=0, row=i, sticky="nw", padx=10, "in"=frame) if (params[[i]]$type == "numeric" || params[[i]]$type == "text") { tmp <- tkentry(dialog, width="10", textvariable=vars[[i]]) tkgrid(tmp, column=1, row=i, sticky="nsew", padx=10, "in"=frame) tkbind(tmp, "", OnOK) } else if (params[[i]]$type == "boolean") { b <- tkcheckbutton(dialog, onvalue="TRUE", offvalue="FALSE", variable=vars[[i]]) if (params[[i]]$default == "TRUE") { tkselect(b) } tkgrid(b, column=1, row=i, sticky="w", padx=10, "in"=frame) } else if (params[[i]]$type == "listbox") { f <- tkframe(dialog) tkgrid(f, "in"=frame, padx=10, sticky="nsew", column=1, row=i) scr <- tkscrollbar(f, repeatinterval=5) fun <- eval(eval(substitute(expression(function(...) tkset(scr,...)), list(scr=scr)))) lb <- tklistbox(f, selectmode="single", exportselection=FALSE, height=3, yscrollcommand=fun) fun <- eval(eval(substitute(expression(function(...) tkyview(lb, ...)), list(lb=lb)))) tkconfigure(scr, "-command", fun) tkselection.set(lb, as.numeric(params[[i]]$default)+1) lapply(params[[i]]$values, function(l) tkinsert(lb, "end", l)) tkselection.set(lb, as.numeric(params[[i]]$default)) tkgrid(lb, scr, sticky="nsew", "in"=f) tkgrid.configure(scr, sticky="nsw") tkgrid.columnconfigure(f, 0, weight=1) widgets[[i]] <- lb } } tkgrid(OK.but, column=0, columnspan=2, sticky="nsew", "in"=frame, pady=10, padx=10) tkgrid.columnconfigure(frame, 1, weight=1) tkwait.window(dialog) for (i in seq(retval)) { if (params[[i]]$type == "numeric") { retval[[i]] <- eval(parse(text=retval[[i]])) } else if (params[[i]]$type == "text") { retval[[i]] <- eval(retval[[i]]) } else if (params[[i]]$type == "boolean") { if (retval[[i]] == "FALSE") { retval[[i]] <- FALSE } else { retval[[i]] <- TRUE } } else if (params[[i]]$type == "listbox") { ## nothing to do } } names(retval) <- names(params) return (retval) } .tkigraph.add.graph <- function(g) { top <- get("top", .tkigraph.env) topframe <- get("topframe", .tkigraph.env) ## add 'name' attribute if not present if (!"name" %in% list.vertex.attributes(g)) { V(g)$name <- as.integer(seq(vcount(g))) } if (!"name" %in% list.edge.attributes(g)) { E(g)$name <- as.integer(seq(ecount(g))) } graphs <- get("graphs", .tkigraph.env) selected <- get("selected", .tkigraph.env) assign("graphs", append(graphs, list(g)), .tkigraph.env) no <- length(graphs)+1 selected[[no]] <- tclVar("FALSE") assign("selected", selected, .tkigraph.env) name <- get.graph.attribute(g, "name") tmpvar <- tclVar(as.character(name)) but <- tkcheckbutton(top, onvalue="TRUE", offvalue="FALSE", variable=selected[[no]]) lab <- tklabel(top, text=as.character(no), width=2) ent <- tkentry(top, width=30, textvariable=tmpvar) lab2 <- tklabel(top, text=as.character(vcount(g)), justify="right", padx=2) lab3 <- tklabel(top, text=as.character(ecount(g)), justify="right", padx=2) lab4 <- tklabel(top, text=if (is.directed(g)) "YES" else "NO") tkgrid(but, lab, ent, lab2, lab3, lab4, "in"=topframe, sticky="nsew") tklines <- get("tklines", .tkigraph.env) tklines[[no]] <- list(but, lab, ent, lab2, lab3, lab4) assign("tklines", tklines, .tkigraph.env) } .tkigraph.delete <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) == 0) { return() } if (length(gnos) > 1) { message <- paste("Are you sure to delete", length(gnos), "graphs?") } else { message <- paste("Are you sure to delete graph #", gnos, "?") } yesno <- tkmessageBox(message=message, icon="question", type="yesno", default="yes") if (as.character(yesno) == "no") { return() } ## remove from the screen graphs <- get("graphs", .tkigraph.env) topframe <- get("topframe", .tkigraph.env) todel <- get("tklines", .tkigraph.env)[gnos] todel <- unlist(recursive=FALSE, todel) for (i in todel) { tkgrid.remove(topframe, i) } ## delete the graphs graphs[gnos] <- NA assign("graphs", graphs, .tkigraph.env) selected <- get("selected", .tkigraph.env) for (i in gnos) { selected[[i]] <- tclVar("FALSE") } assign("selected", selected, .tkigraph.env) } .tkigraph.load <- function() { filename <- tkgetOpenFile(defaultextension="Rdata", title="Load graphs") env <- new.env() load(paste(as.character(filename), collapse=" "), envir=env) .tkigraph.graphs <- get("graphs", envir=env) for (i in seq(.tkigraph.graphs)) { .tkigraph.add.graph(.tkigraph.graphs[[i]]) } if (".tkigraph.graphs" %in% ls(all.names=TRUE)) { rm(.tkigraph.graphs) } } .tkigraph.load.online <- function() { ## TODO } .tkigraph.save <- function() { graphs <- get("graphs", .tkigraph.env) topframe <- get("topframe", .tkigraph.env) for (i in seq(graphs)) { if (is.na(graphs)[i]) { next } entry <- tkgrid.slaves(topframe, row=i, col=2) graphs[[i]] <- set.graph.attribute(graphs[[i]], "name", as.character(tcl(entry, "get"))) } graphs <- graphs[ !is.na(graphs) ] filename <- tkgetSaveFile(initialfile="graphs.Rdata", defaultextension="Rdata", title="Save graphs") save(graphs, file=paste(as.character(filename), collapse=" ")) } .tkigraph.import.adjacency <- function() { filename <- tkgetOpenFile(defaultextension="adj", title="Import adjacency matrix") filename <- paste(as.character(filename), collapse=" ") if (filename=="") { return() } tab <- read.table(filename) tab <- as.matrix(tab) if (ncol(tab) != nrow(tab)) { .tkigraph.error("Cannot interpret as adjacency matrix") return() } dir <- if (all(t(tab)==tab)) "undirected" else "directed" if (all(unique(tab) %in% c(0,1))) { weighted <- NULL } else { weighted <- "weight" } g <- .tkigraph.graph.adjacency(tab, mode=dir, weighted=weighted) g <- set.graph.attribute(g, "name", "Imported adjacency matrix") .tkigraph.add.graph(g) } .tkigraph.graph.adjacency <- function(adjmatrix, mode, weighted) { if (is.null(weighted)) { g <- graph.adjacency(adjmatrix, mode=mode) } else { ## there is bug in the currect igraph version, this is a workaround if (mode=="undirected") { adjmatrix[ lower.tri(adjmatrix) ] <- 0 } g <- graph.adjacency(adjmatrix, mode=mode, weighted=weighted) } g } .tkigraph.import.edgelist <- function() { filename <- tkgetOpenFile(defaultextension="el", title="Import edge list") filename <- paste(as.character(filename), collapse=" ") if (filename=="") { return() } tab <- read.table(filename, colClasses="character") cn <- rep("", ncol(tab)) if (ncol(tab)>=3) { cn[3] <- "weight" } colnames(tab) <- cn read <- .tkigraph.dialogbox(TITLE="Importing an edge list", directed=list(name="Directed", type="boolean", default="FALSE")) g <- graph.data.frame(tab, directed=read$directed) g <- set.graph.attribute(g, "name", "Imported edge list") .tkigraph.add.graph(g) } .tkigraph.import.pajek <- function() { filename <- tkgetOpenFile(defaultextension="net", title="Import Pajek file") filename <- paste(as.character(filename), collapse=" ") if (filename=="") { return() } g <- read.graph(file=filename, format="pajek") color <- NULL # To eliminate a check NOTE if ("color" %in% list.vertex.attributes(g)) { V(g)[ color=="" ]$color <- "black" } if ("color" %in% list.edge.attributes(g)) { E(g)[ color=="" ]$color <- "black" } g <- set.graph.attribute(g, "name", "Imported Pajek fie") .tkigraph.add.graph(g) } .tkigraph.export.adjacency <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] if ("weight" %in% list.graph.attributes(graph)) { tab <- get.adjacency(graph, attr="weight", names=FALSE, sparse=FALSE) } else { tab <- get.adjacency(graph, names=FALSE, sparse=FALSE) } filename <- tkgetSaveFile(initialfile="graph.adj", defaultextension="adj", title="Export adjacency matrix") filename <- paste(as.character(filename), collapse=" ") if (filename=="") { return() } write.table(tab, file=filename, row.names=FALSE, col.names=FALSE) } .tkigraph.export.edgelist <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] el <- get.edgelist(graph) if ("weight" %in% list.edge.attributes(graph)) { el <- cbind(el, E(graph)$weight) } filename <- tkgetSaveFile(initialfile="graph.el", defaultextension="el", title="Export edge list") filename <- paste(as.character(filename), collapse=" ") if (filename=="") { return() } write.table(el, file=filename, row.names=FALSE, col.names=FALSE) } .tkigraph.export.pajek <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] filename <- tkgetSaveFile(initialfile="pajek.net", defaultextension="net", title="Export Pajek file") filename <- paste(as.character(filename), collapse=" ") if (filename=="") { return() } write.graph(graph, file=filename, format="pajek") } .tkigraph.show <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) el <- get.edgelist(graphs[[gnos]]) el <- data.frame(from=el[,1], to=el[,2]) # if (any(V(graphs[[gnos]])$name != seq(length=vcount(graphs[[gnos]])))) { # el2 <- get.edgelist(graphs[[gnos]], names=FALSE) # el <- cbind(el, el2) # } if ("weight" %in% list.edge.attributes(graphs[[gnos]])) { el <- cbind(el, value=E(graphs[[gnos]])$weight) } .tkigraph.showData(el, title=paste(sep="", "Graph #", gnos), right=FALSE) } .tkigraph.stat <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) == 0) { .tkigraph.error("Please select some graphs") return() } read <- .tkigraph.dialogbox(TITLE="Choose statistics", vertices=list(name="Vertices", type="boolean", default="FALSE"), edges=list(name="Edges", type="boolean", default="FALSE"), recip=list(name="Reciprocity", type="boolean", default="FALSE"), dens=list(name="Density", type="boolean", default="FALSE"), trans=list(name="Transitivity (global)", type="boolean", default="FALSE"), ltrans=list(name="Mean local transitivity", type="boolean", default="FALSE"), deg=list(name="Average degree", type="boolean", default="FALSE"), maxdeg=list(name="Maximum degree (total)", type="boolean", default="FALSE"), maxindeg=list(name="Maximum degree (in)", type="boolean", default="FALSE"), maxoutdeg=list(name="Maximum degree (out)", type="boolean", default="FALSE"), mindeg=list(name="Minimum degree (total)", type="boolean", default="FALSE"), minindeg=list(name="Minimum degree (in)", type="boolean", default="FALSE"), minoutdeg=list(name="Minimum degree (out)", type="boolean", default="FALSE") ) graphs <- get("graphs", .tkigraph.env)[gnos] v <- e <- recip <- dens <- trans <- ltrans <- deg <- maxdeg <- maxindeg <- maxoutdeg <- mindeg <- minindeg <- minoutdeg <- numeric() for (i in seq(along=gnos)) { if (read$vertices) { v[i] <- vcount( graphs[[ i ]] ) } if (read$edges) { e[i] <- ecount( graphs[[ i ]] ) } if (read$recip) { recip[i] <- reciprocity( graphs[[ i ]] ) } if (read$dens) { dens[i] <- graph.density( graphs[[ i ]] ) } if (read$trans) { trans[i] <- transitivity( graphs[[ i ]], type="global") } if (read$ltrans) { ltrans[i] <- transitivity( graphs[[ i ]], type="localaverage") } if (read$deg) { deg[i] <- mean(degree( graphs[[ i ]], mode="total")) } if (read$maxdeg) { maxdeg[i] <- max(degree( graphs[[ i ]], mode="total")) } if (read$maxindeg) { maxindeg[i] <- max(degree( graphs[[ i ]], mode="in")) } if (read$maxoutdeg) { maxoutdeg[i] <- max(degree( graphs[[ i ]], mode="out")) } if (read$mindeg) { mindeg[i] <- min(degree( graphs[[ i ]], mode="total")) } if (read$minindeg) { minindeg[i] <- min(degree( graphs[[ i ]], mode="in")) } if (read$minoutdeg) { minoutdeg[i] <- min(degree( graphs[[ i ]], mode="out")) } } value <- numeric() cn <- character() if (read$vertices) { value <- cbind(value, v) cn <- c(cn, "Vertices") } if (read$edges) { value <- cbind(value, e) cn <- c(cn, "Edges") } if (read$recip) { value <- cbind(value, recip) cn <- c(cn, "Reciprocity") } if (read$dens) { value <- cbind(value, dens) cn <- c(cn, "Density") } if (read$trans) { value <- cbind(value, trans) cn <- c(cn, "Transitivity") } if (read$ltrans) { value <- cbind(value, ltrans) cn <- c(cn, "Local trans.") } if (read$deg) { value <- cbind(value, deg) cn <- c(cn, "Mean degree") } if (read$maxdeg) { value <- cbind(value, maxdeg) cn <- c(cn, "Max. degree") } if (read$maxindeg) { value <- cbind(value, maxindeg) cn <- c(cn, "Max. in-deg.") } if (read$maxoutdeg) { value <- cbind(value, maxoutdeg) cn <- c(cn, "Max. out-deg.") } if (read$mindeg) { value <- cbind(value, mindeg) cn <- c(cn, "Min. deg.") } if (read$minindeg) { value <- cbind(value, minindeg) cn <- c(cn, "Min. in-deg.") } if (read$minoutdeg) { value <- cbind(value, minoutdeg) cn <- c(cn, "Min. out-deg.") } value <- t(value) rownames(value) <- cn colnames(value) <- gnos .tkigraph.showData(value, title="Graphs properties", sort.button=FALSE) } .tkigraph.plot <- function(simple=TRUE, gnos=NULL, ...) { if (is.null(gnos)) { gnos <- .tkigraph.get.selected() } graphs <- get("graphs", .tkigraph.env) if (length(gnos)==0) { return (.tkigraph.error("Please select one or more graphs to draw.")) } max.vcount <- max(sapply(graphs[gnos], vcount)) if (max.vcount > 5000) { vertex.size <- 1 } else if (max.vcount > 30) { vertex.size <- 3 } else { vertex.size <- 15 } if (!simple) { read <- .tkigraph.dialogbox(TITLE="Drawing graphs", interactive=list(name="Interactive", type="boolean", default="FALSE"), vertex.size=list(name="Vertex size", type="numeric", default=vertex.size), labels=list(name="Vertex labels", type="listbox", default="3", values=c("None", "IDs", "Names", "Labels")), elabels=list(name="Edge labels", type="listbox", default="0", values=c("None", "IDs", "Names", "Values")), layout=list(name="Layout", type="listbox", default="0", values=c("Default", "Force-based (KK)", "Force-based (FR)", "Tree (RT)", "Circle", "Random"))) } else { read <- list(interactive=FALSE, vertex.size=vertex.size, labels=3, # labels elabels=0, # none layout=0) } if (!read$interactive) { fun <- function(...) { dev.new() ; plot.igraph(...) } } else { fun <- tkplot } layout.default <- function(graph, layout.par) { if ("x" %in% list.vertex.attributes(graph) && "y" %in% list.vertex.attributes(graph)) { cbind( V(graph)$x , V(graph)$y ) } else if ("layout" %in% list.graph.attributes(graph)) { l <- get.graph.attribute(graph, "layout") if (is.function(l)) { l(graph) } else { l } } else if (vcount(graph) < 300 && is.connected(graph)) { layout.kamada.kawai(graph) } else if (vcount(graph) < 1000) { layout.fruchterman.reingold(graph) } else { layout.circle(graph) } } layouts <- list(layout.default, layout.kamada.kawai, layout.fruchterman.reingold, layout.reingold.tilford, layout.circle, layout.random) if (read$vertex.size < 10) { label.dist <- 0.4 } else { label.dist <- 0 } for (i in gnos) { if (read$labels == "0") { labels <- NA } else if (read$labels == "1") { labels <- seq(vcount(graphs[[i]])) } else if (read$labels == "2") { labels <- V(graphs[[i]])$name } else if (read$labels == "3") { if ("label" %in% list.vertex.attributes(graphs[[i]])) { labels <- V(graphs[[i]])$label } else { labels <- V(graphs[[i]])$name } } if (read$elabels == "0") { elabels <- NA } else if (read$labels == "1") { elabels <- seq(ecount(graphs[[i]])) } else if (read$labels == "2") { elabels <- E(graphs[[i]])$name } else if (read$labels == "3") { if ("weight" %in% list.edge.attributes(graphs[[i]])) { elabels <- E(graphs[[i]])$weight } else { .tkigraph.warning("No edge weights, not a valued graph"); elabels <- NA } } if (vcount(graphs[[i]]) > 10) { eas <- 0.5 } else { eas <- 1 } g <- graphs[[i]] g <- remove.vertex.attribute(g, "name") fun(g, layout=layouts[[ read$layout+1 ]], vertex.size=read$vertex.size, ## vertex.color=read$vertex.color, vertex.label=labels, vertex.label.dist=label.dist, edge.label=elabels, edge.arrow.size=eas, ...) } } .tkigraph.by.hand <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) > 1) { .tkigraph.error("Please select zero or one graph") return() } if (length(gnos)==0) { newdf <- edit(data.frame(list(from=character(), to=character()))) if (ncol(newdf) > 2) { colnames(newdf) <- c("from", "to", "weight") } read <- .tkigraph.dialogbox(TITLE="Creating a graph by hand", directed=list(name="Directed", type="boolean", default="FALSE")) g <- graph.data.frame(newdf, directed=read$directed) g <- set.graph.attribute(g, "name", "New graph") .tkigraph.add.graph(g) } else { graphs <- get("graphs", .tkigraph.env) df <- get.edgelist(graphs[[gnos]]) colnames <- c("from", "to") if ("weight" %in% list.edge.attributes(graphs[[gnos]])) { df <- cbind(df, E(g)$weight) colnames <- c("from", "to", "weight") } df <- as.data.frame(df) colnames(df) <- colnames df <- edit(df) if (ncol(df) > 2) { colnames(df) <- c("from", "to", "weight") } graphs[[gnos]] <- graph.data.frame(df, directed=is.directed(graphs[[gnos]])) assign("graphs", graphs, .tkigraph.env) } invisible(NULL) } .tkigraph.tree <- function() { read <- .tkigraph.dialogbox(TITLE="Regular tree", n=list(name="Vertices", type="numeric", default=63, min=0), b=list(name="Branches", type="numeric", default=2, min=1), mode=list(name="Mode", type="listbox", values=c("Directed (out)", "Directed (in)", "Undirected"), default="2")) read$mode <- c("out", "in", "undirected")[read$mode+1] g <- graph.tree(n=read$n, children=read$b, mode=read$mode) lay <- layout.reingold.tilford(g, root=1, mode="all") g <- set.graph.attribute(g, "layout", lay) g <- set.graph.attribute(g, "name", "Regular tree") .tkigraph.add.graph(g) } .tkigraph.ring <- function() { read <- .tkigraph.dialogbox(TITLE="Regular ring", n=list(name="Vertices", type="numeric", default=100, min=0)) g <- graph.ring(n=read$n) g <- set.graph.attribute(g, "layout", layout.circle) g <- set.graph.attribute(g, "name", "Regular ring") .tkigraph.add.graph(g) } .tkigraph.lattice <- function() { read <- .tkigraph.dialogbox(TITLE="Regular lattice", dim=list(name="Dimensions", type="numeric", default=2, min=1, max=5), s1=list(name="Size 1", type="numeric", default=10, min=1), s2=list(name="Size 2", type="numeric", default=10, min=1), s3=list(name="Size 3", type="numeric", default=10, min=1), s4=list(name="Size 4", type="numeric", default=10, min=1), s5=list(name="Size 5", type="numeric", default=10, min=1)) if (read$dim > 5) { read$dim <- 5 } dimv <- c(read$s1, read$s2, read$s3, read$s4, read$s5)[1:read$dim] g <- graph.lattice(dimvector=dimv) g <- set.graph.attribute(g, "name", "Regular Lattice") .tkigraph.add.graph(g) } .tkigraph.star <- function() { read <- .tkigraph.dialogbox(TITLE="Star graph", n=list(name="Vertices", type="numeric", default=100, min=0), mode=list(name="Mode", type="listbox", values=c("Directed (out)", "Directed (in)", "Undirected"), default="2")) read$mode <- c("out", "in", "undirected")[read$mode+1] g <- graph.star(read$n, mode=read$mode) g <- set.graph.attribute(g, "name", "Star graph") .tkigraph.add.graph(g) } .tkigraph.full <- function() { read <- .tkigraph.dialogbox(TITLE="Full graph", n=list(name="Vertices", type="numeric", default=30, min=0), directed=list(name="Directed", type="boolean", default="FALSE"), loops=list(name="Loops", type="boolean", default="FALSE")) g <- graph.full(read$n, read$directed, read$loops) g <- set.graph.attribute(g, "name", "Full graph") .tkigraph.add.graph(g) } .tkigraph.atlas <- function() { read <- .tkigraph.dialogbox(TITLE="Graph Atlas", n=list(name="Number", type="numeric", default=sample(0:1252, 1), min=0, max=1252)) g <- graph.atlas(read$n) g <- set.graph.attribute(g, "name", paste("Graph Atlas #", read$n)) .tkigraph.add.graph(g) } .tkigraph.erdos.renyi.game <- function() { read <- .tkigraph.dialogbox(TITLE="Erdos-Renyi random graph, G(n,p)", n=list(name="Vertices", type="numeric", default=100, min=0), p=list(name="Connection probability", type="numeric", default=0.02, min=0, max=1), directed=list(name="Directed", type="boolean", default="FALSE")) g <- erdos.renyi.game(read$n,read$p,directed=read$directed) g <- set.graph.attribute(g, "name", "Random graph (Erdos-Renyi G(n,p))") .tkigraph.add.graph(g) } .tkigraph.erdos.renyi.gnm.game <- function() { read <- .tkigraph.dialogbox(TITLE="Erdos-Renyi random graph, G(n,m)", n=list(name="Vertices", type="numeric", default=100, min=0), m=list(name="Edges", type="numeric", default=200, min=0), directed=list(name="Directed", type="boolean", default="FALSE")) g <- erdos.renyi.game(read$n, read$m, type="gnm", directed=read$directed) g <- set.graph.attribute(g, "name", "Random graph (Erdos-Renyi G(n,m))") .tkigraph.add.graph(g) } .tkigraph.barabasi.game <- function() { read <- .tkigraph.dialogbox(TITLE="Scale Free graph", n=list(name="Vertices", type="numeric", default=100, min=0), m=list(name="Edges per time step", type="numeric", default=1, min=0), directed=list(name="Directed", type="boolean", default="TRUE")) g <- barabasi.game(n=read$n, m=read$m, directed=read$directed) g <- set.graph.attribute(g, "name", "Scale-free random graph") .tkigraph.add.graph(g) } .tkigraph.degree.sequence.game <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) == 0) { .tkigraph.error("Please select at least one graph") return() } graphs <- get("graphs", .tkigraph.env) for (i in gnos) { if (is.directed(graphs[[i]])) { indeg <- degree(graphs[[i]], mode="in") outdeg <- degree(graphs[[i]], mode="out") g <- degree.sequence.game(out.deg=outdeg, in.deg=indeg) } else { deg <- degree(graphs[[i]]) g <- degree.sequence.game(deg) } g <- set.graph.attribute(g, "name", paste(sep="", "Configuration model (#", i,")")) .tkigraph.add.graph(g) } } .tkigraph.watts.strogatz <- function() { read <- .tkigraph.dialogbox(TITLE="Watts-Strogatz graph", dim=list(name="Dimensions", type="numeric", default=1, min=1), size=list(name="Lattice size", type="numeric", default=1000, min=1), nei=list(name="Neighborhood", type="numeric", default=5, min=1), p=list(name="Rewiring probability", type="numeric", default=0.01, min=0, max=1)) g <- watts.strogatz.game(dim=read$dim, size=read$size, nei=read$nei, p=read$p) g <- set.graph.attribute(g, "name", "Watts-Strogatz small-world graph") if (read$dim == 1) { g <- set.graph.attribute(g, "layout", layout.circle) } .tkigraph.add.graph(g) } .tkigraph.simplify <- function() { gnos <- .tkigraph.get.selected() graphs <- get("graphs", .tkigraph.env) for (i in gnos) { g <- simplify(graphs[[i]]) g <- set.graph.attribute(g, "name", paste(sep="", "Simplification of #", i)) .tkigraph.add.graph(g) } } ##################################################### .tkigraph.degree <- function(mode) { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) deg <- degree(graphs[[gnos]], mode=mode) value <- data.frame(Vertex=V(graphs[[gnos]])$name, deg) colnames(value) <- c("Vertex", paste(sep="","Degree (", mode, ")")) plot.command <- function() { read <- .tkigraph.dialogbox(TITLE="Plot degree distribution", logx=list(name="Logarithmic `X' axis", type="boolean", default="FALSE"), logy=list(name="Logarithmic `Y' axis", type="boolean", default="FALSE"), hist=list(name="Histogram", type="boolean", default="FALSE")) if (!read$hist) { h <- hist(value[,2], -1:max(value[,2]), plot=FALSE)$density log <- "" if (read$logx) { log <- paste(sep="", log, "x") } if (read$logy) { log <- paste(sep="", log, "y") } dev.new() plot(0:max(value[,2]), h, xlab="Degree", ylab="Relative frequency", type="b", main="Degree distribution", log=log) } else { dev.new() hist(value[,2], main="Degree distribution", xlab="Degree") } } value <- value[ order(value[,2], decreasing=TRUE), ] mv <- paste("Mean degree:", round(mean(deg), 2)) .tkigraph.showData(value, title=paste(sep="", "Degree for graph #", gnos), plot.text="Plot distribution", plot.command=plot.command, showmean=mv) } .tkigraph.degree.dist <- function(power=FALSE) { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) read <- .tkigraph.dialogbox(TITLE="Choose degree type", type=list(name="Degree type", type="listbox", default="0", values=c("Out", "In", "Total"))) mode <- c("out", "in", "all")[read$type+1] deg <- degree(graphs[[gnos]], mode=mode) dev.new() h <- hist(deg, -1:max(deg), plot=FALSE)$density plot(0:max(deg), h, xlab="Degree", ylab="Relative frequency", type="b", main="Degree distribution", log="xy") if (power) { if (max(deg)<10) { .tkigraph.error("Degrees are too small for a power-law fit") return() } fit <- power.law.fit(deg, xmin=10) lines(0:max(deg), (0:max(deg))^(-coef(fit)), col="red") legend("topright", c(paste("exponent:", round(coef(fit), 2)), paste("standard error:", round(sqrt(vcov(fit)), 2))), bty="n", cex=1.5) } } .tkigraph.closeness <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) cl <- closeness(graphs[[gnos]], mode="out") value <- data.frame(Vertex=V(graphs[[gnos]])$name, Closeness=cl) value <- value[ order(value[,2], decreasing=TRUE), ] mv <- paste("Mean value:", round(mean(cl),2)) .tkigraph.showData(value, title=paste(sep="", "Closeness for graph #", gnos), showmean=mv) } .tkigraph.betweenness <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) btw <- betweenness(graphs[[gnos]]) vc <- vcount(graphs[[gnos]]) m <- (vc-1)*(vc-2) nbtw <- btw/m value <- data.frame(V(graphs[[gnos]])$name, btw, nbtw) colnames(value) <- c("Vertex", "Betweenness", "Normalized Betweenness") value <- value[ order(value[,2], decreasing=TRUE), ] mv <- paste("Mean value:", round(mean(btw),2), "&", round(mean(nbtw),5)) .tkigraph.showData(value, title=paste(sep="", "Betweenness for graph #", gnos), showmean=mv) } .tkigraph.constraints <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) const <- constraint(graphs[[gnos]]) value <- data.frame(V(graphs[[gnos]])$name, const) colnames(value) <- c("Vertex", "Constraint") value <- value[ order(value[,2], decreasing=TRUE), ] mv <- paste("Mean value:", round(mean(const),2)) .tkigraph.showData(value, title=paste(sep="", "Constraint for graph #", gnos), showmean=mv) } .tkigraph.power.centrality <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) bp <- bonpow(graphs[[gnos]]) value <- data.frame(V(graphs[[gnos]])$name, bp) colnames(value) <- c("Vertex", "Power centrality") value <- value[ order(value[,2], decreasing=TRUE), ] mv <- paste("Mean value:", round(mean(bp),2)) .tkigraph.showData(value, title=paste(sep="", "Power centrality for graph #", gnos), showmean=mv) } .tkigraph.page.rank <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) bp <- page.rank(graphs[[gnos]])$vector value <- data.frame(V(graphs[[gnos]])$name, bp) colnames(value) <- c("Vertex", "Page rank") value <- value[ order(value[,2], decreasing=TRUE), ] mv <- paste("Mean value:", round(mean(bp),2)) .tkigraph.showData(value, title=paste(sep="", "Page rank centrality for graph #", gnos), showmean=mv) } .tkigraph.edge.betweenness <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- get("graphs", .tkigraph.env) ebtw <- edge.betweenness(graphs[[gnos]]) el <- get.edgelist(graphs[[gnos]]) value <- data.frame(E(graphs[[gnos]])$name, el[,1], el[,2], ebtw) colnames(value) <- c("Edge", "From", "To", "Betweenness") value <- value[ order(value[,4], decreasing=TRUE), ] mv <- paste("Mean value:", round(mean(ebtw),2)) .tkigraph.showData(value, title=paste(sep="", "Edge betweenness for graph #",gnos), showmean=mv) } ##################################################### .tkigraph.dist.matrix <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] if (vcount(graph) > 100) { .tkigraph.error("Graphs is too large to do this") return() } value <- shortest.paths(graph, mode="out") rownames(value) <- colnames(value) <- V(graph)$name .tkigraph.showData(value, sort.button=FALSE, title=paste(sep="", "Distance matrix for graph #", gnos)) } .tkigraph.distance.tofrom <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] read <- .tkigraph.dialogbox(TITLE="Distance from a vertex", v=list(name="Vertex ID", type="numeric", default=1, min=1, max=vcount(graph))) if (read$v < 1 || read$v > vcount(graph)) { .tkigraph.error("Invalid vertex ID") return() } value <- shortest.paths(graph, read$v, mode="out") dim(value) <- NULL value <- data.frame( V(graph)$name, value) colnames(value) <- c("Vertex", "Distance") mv <- paste("Mean distance:", round(mean(value[,2]),2)) .tkigraph.showData(value, title=paste("Distance from vertex", read$v, "in graph #", gnos), showmean=mv) } .tkigraph.diameter <- function(mode="dia") { gnos <- .tkigraph.get.selected() if (length(gnos)==0) { .tkigraph.error("Please select one or more graphs") return() } isconn <- logical() dia <- numeric() graphs <- get("graphs", .tkigraph.env) for (i in seq(along=gnos)) { if (mode=="dia") { dia[i] <- diameter(graphs[[ gnos[i] ]], directed=FALSE) } else if (mode=="path") { dia[i] <- average.path.length(graphs[[ gnos[i] ]], directed=FALSE) } isconn[i] <- is.connected(graphs[[ gnos[i] ]]) } value <- data.frame( gnos, isconn, dia) if (mode=="dia") { title <- "Diameter" colnames(value) <- c("Graph #", "Connected", "Diameter") } else if (mode=="path") { title <- "Average path length" colnames(value) <- c("Graph #", "Connected", "Mean path length") } title <- paste(title, "of graph") if (length(gnos) > 1) { title <- paste(sep="", title, "s") } .tkigraph.showData(value, title=title) } .tkigraph.plot.diameter <- function(simple=FALSE) { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] edges <- E(graph, path=get.diameter(graph, directed=FALSE), directed=FALSE) color <- rep("black", ecount(graph)) color[edges] <- "red" width <- rep(1, ecount(graph)) width[edges] <- 2 .tkigraph.plot(gnos=gnos, simple=simple, edge.color=color, edge.width=width) } .tkigraph.clusters <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] comm <- clusters(graph) members <- sapply(sapply(seq(along=comm$csize), function(i) which(comm$membership==i)), paste, collapse=", ") value <- data.frame("Component"=seq(along=comm$csize), "Members"=members) .tkigraph.showData(value, title=paste("Components of graph #", gnos), right=FALSE) } .tkigraph.clusters.membership <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] comm <- clusters(graph) value <- data.frame("Vertex"=seq(along=comm$membership), "Component"=comm$membership) .tkigraph.showData(value, title=paste("Components of graph #", gnos)) } .tkigraph.calculate.clusters <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] cs <- clusters(graph)$csize value <- data.frame(seq(along=cs), cs) colnames(value) <- c("Cluster #", "Size") plot.command <- function() { read <- .tkigraph.dialogbox(TITLE="Plot degree distribution", logx=list(name="Logarithmic `X' axis", type="boolean", default="FALSE"), logy=list(name="Logarithmic `Y' axis", type="boolean", default="FALSE"), hist=list(name="Histogram", type="boolean", default="FALSE")) if (!read$hist) { h <- hist(value[,2], 0:max(value[,2]), plot=FALSE)$density log <- "" if (read$logx) { log <- paste(sep="", log, "x") } if (read$logy) { log <- paste(sep="", log, "y") } dev.new() plot(1:max(value[,2]), h, xlab="Component size", ylab="Relative frequency", type="b", main="Component size distribution", log=log) } else { dev.new() hist(value[,2], main="Component size distribution", xlab="Degree") } } value <- value[ order(value[,2], decreasing=TRUE), ] mv <- paste("Mean component size:", round(mean(cs),2)) .tkigraph.showData(value, title=paste(sep="", "Component sizes, graph #", gnos), plot.text="Plot distribution", plot.command=plot.command, showmean=mv) } .tkigraph.plot.comp <- function(simple=FALSE) { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] clu <- clusters(graph) colbar <- rainbow(length(clu$csize)*2) vertex.color <- colbar[ clu$membership ] .tkigraph.plot(gnos=gnos, simple=simple, vertex.color=vertex.color) } .tkigraph.create.giantcomp <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] clu <- clusters(graph) v <- which(clu$membership == which.max(clu$csize)) g <- induced.subgraph(graph, v) .tkigraph.add.graph(g) } .tkigraph.create.mycomp <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] read <- .tkigraph.dialogbox(TITLE="Component of a vertex", vertex=list(name="Vertex", type="numeric", default=1, min=1, max=vcount(graph))) if (read$vertex<1 || read$vertex >vcount(graph)) { .tkigraph.error("Invalid vertex id") return() } g <- induced.subgraph(graph, subcomponent(graph, read$vertex)) .tkigraph.add.graph(g) } .tkigraph.create.comp <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] read <- .tkigraph.dialogbox(TITLE="Graph from component", comp=list(name="Component id", type="numeric", default=1, min=1)) clu <- clusters(graph) if (read$comp<1 || read$comp > length(clu$csize)) { .tkigraph.error("Invalid component id") return() } v <- which(clu$membership==read$comp) g <- induced.subgraph(graph, v) .tkigraph.add.graph(g) } .tkigraph.motifs.draw <- function() { read <- .tkigraph.dialogbox(TITLE="Draw all motifs", size=list(name="Size", type="numeric", default=3, min=3, max=4), directed=list(name="Directed", type="boolean", default="FALSE")) if (read$size < 3 || read$size > 4) { .tkigraph.error("Invalid motif size, should be 3 or 4") return() } if (read$size == 3) { co <- matrix( c(1,1, 0,0, 2,0), ncol=2, byrow=TRUE) } else { co <- matrix( c(0,1, 1,1, 0,0, 1,0), ncol=2, byrow=TRUE) } if (read$size == 3 && read$dir) { no <- 16 rows <- cols <- 4 } else if (read$size == 3 && !read$dir) { no <- 4 rows <- cols <- 2 } else if (read$size == 4 && read$dir) { no <- 216 rows <- cols <- 15 } else if (read$size == 4 && !read$dir) { no <- 11 rows <- 4 cols <- 3 } names <- as.character(seq(no)) dev.new() layout( matrix(1:(rows*cols), nrow=rows, byrow=TRUE) ) layout.show(rows*cols) for (i in seq(no)) { g <- graph.isocreate(read$size, i-1, directed=read$dir) par(mai=c(0,0,0,0), mar=c(0,0,0,0)) par(cex=2) plot(g, layout=co, vertex.color="red", vertex.label=NA, frame=TRUE, edge.color="black", margin=0.1, edge.arrow.size=.5) text(0,0, names[i], col="blue", cex=.5) } } .tkigraph.motifs.find <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } read <- .tkigraph.dialogbox(TITLE="Find motifs", size=list(name="Size", type="numeric", default=3, min=3, max=4)) if (read$size < 3 || read$size > 4) { .tkigraph.error("Invalid motif size, should be 3 or 4") return() } graphs <- get("graphs", .tkigraph.env) motifs <- graph.motifs(graphs[[gnos]], size=read$size) if (read$size == 3) { co <- matrix( c(1,1, 0,0, 2,0), ncol=2, byrow=TRUE) } else { co <- matrix( c(0,1, 1,1, 0,0, 1,0), ncol=2, byrow=TRUE) } if (read$size == 3 && is.directed(graphs[[gnos]])) { no <- 16 rows <- cols <- 4 } else if (read$size == 3 && !is.directed(graphs[[gnos]])) { no <- 4 rows <- cols <- 2 } else if (read$size == 4 && is.directed(graphs[[gnos]])) { no <- 216 rows <- cols <- 15 } else if (read$size == 4 && !is.directed(graphs[[gnos]])) { no <- 11 rows <- 4 cols <- 3 } dev.new() barplot(motifs, names.arg=seq(no)) names <- as.character(seq(no)) dev.new() layout( matrix(1:(rows*cols), nrow=rows, byrow=TRUE) ) layout.show(rows*cols) for (i in seq(no)) { g <- graph.isocreate(read$size, i-1, directed=is.directed(graphs[[gnos]])) par(mai=c(0,0,0,0), mar=c(0,0,0,0)) par(cex=2) plot(g, layout=co, vertex.color="red", vertex.label=NA, frame=TRUE, edge.color="black", margin=0.1) text(0,0, motifs[i], col="green") } } .tkigraph.spinglass <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] if (!is.connected(graph)) { .tkigraph.error("Graph is not connected") return() } weights <- if ("weight" %in% list.edge.attributes(graph)) "TRUE" else "FALSE" read <- .tkigraph.dialogbox(TITLE="Spinglass community structure", gamma=list(name="Gamma parameter", type="numeric", default=1), weights=list(name="Use edge weights", type="boolean", default=weights), spins=list(name="Number of spins", type="numeric", default=25), parupdate=list(name="Parallel update", type="boolean", default="FALSE"), update.rule=list(name="Update rule", type="listbox", default="1", values=c("Simple", "Configuration model")), start.temp=list(name="Start temperature", type="numeric", default=1), stop.temp=list(name="Stop temperature", type="numeric", default=0.1), cool.fact=list(name="Cooling factor", type="numeric", default=0.99)) read$update.rule <- c("simple", "config")[read$update.rule+1] if (read$weights) { if (!"weight" %in% list.edge.attributes(graph)) { .tkigraph.warning("This graphs is not weighted") read$weights <- NULL } else { read$weights <- E(graph)$weight } } else { read$weights <- NULL } comm <- spinglass.community(graph, weights=read$weights, spins=read$spins, parupdate=read$parupdate, start.temp=read$start.temp, stop.temp=read$stop.temp, cool.fact=read$cool.fact, update.rule=read$update.rule, gamma=read$gamma) .tkigraph.spinglass.community.dialog(comm, read, gnos) } .tkigraph.spinglass.community.dialog <- function(comm, read, gnos) { dialog <- tktoplevel() frame <- tkframe(dialog) tkgrid(frame) tktitle(dialog) <- "Spinglass community structure algorithm results" read$update.rule <- if (read$update.rule=="simple") "Simple" else "Configuration model" tkgrid(tklabel(dialog, text="Spinglass community structure algorithm results", font=tkfont.create(family="times", size=16, weight="bold")), columnspan=3, sticky="nsew", "in"=frame, padx=10, pady=10) tkgrid(txt <- tktext(dialog), columnspan=1, rowspan=5, sticky="nsew", "in"=frame, padx=10, pady=10) tkconfigure(txt, height=15) tkinsert(txt, "end", "Parameters were:\n") tkinsert(txt, "end", paste(" Gamma=", read$gamma, "\n")) tkinsert(txt, "end", if (is.null(read$weights)) " Weights were not used.\n" else " Weights were used.\n") tkinsert(txt, "end", paste(" Number of spins=", read$spins, "\n")) tkinsert(txt, "end", if (read$parupdate) " Parallel updating.\n" else " Sequential updating.\n") tkinsert(txt, "end", paste(" Update rule:", read$update.rule, "\n")) tkinsert(txt, "end", paste(" Start temperature was", read$start.temp, "\n")) tkinsert(txt, "end", paste(" Stop temperaure was", read$stop.temp, "\n")) tkinsert(txt, "end", paste(" Cooling factor was", read$cool.fact, "\n")) tkinsert(txt, "end", "\nResults:\n") tkinsert(txt, "end", paste(" Number of communities found:", length(comm$csize), "\n")) tkinsert(txt, "end", paste(" Modularity of the result:", comm$modularity, "\n")) tkinsert(txt, "end", paste(" Stopped at temperature:", comm$temperature, "\n")) tkconfigure(txt, state="disabled") show.communities <- function() { members <- sapply(sapply(seq(along=comm$csize), function(i) which(comm$membership==i)), paste, collapse=", ") value <- data.frame("Community"=seq(along=comm$csize), "Members"=members) .tkigraph.showData(value, title=paste("Communities, spinglass algorithm on graph #", gnos), right=FALSE) } show.membership <- function() { value <- data.frame("Vertex"=seq(along=comm$membership), "Community"=comm$membership) .tkigraph.showData(value, title=paste("Communities, spinglass algorithm on graph #", gnos)) } show.csize <- function() { value <- data.frame("Comm. #"=seq(along=comm$csize), "Size"=comm$csize) value <- value[ order(value[,2], decreasing=TRUE), ] .tkigraph.showData(value, title=paste("Communities, spinglass algorithm on graph #", gnos)) } plot.communities <- function(simple=FALSE) { colbar <- rainbow(length(comm$csize)*2) vertex.color=colbar[ comm$membership ] .tkigraph.plot(gnos=gnos, simple=simple, vertex.color=vertex.color) } create.subgraph <- function() { ## TODO } tkgrid(tkbutton(dialog, text="Show communities", command=show.communities), "in"=frame, sticky="ew", column=1, row=1, padx=10, pady=10) tkgrid(tkbutton(dialog, text="Show membership", command=show.membership), "in"=frame, sticky="ew", column=1, row=2, padx=10, pady=10) tkgrid(tkbutton(dialog, text="Show community sizes", command=show.csize), "in"=frame, sticky="ew", column=1, row=3, padx=10, pady=10) tkgrid(tkbutton(dialog, text="Draw communities", command=function() plot.communities(simple=FALSE)), "in"=frame, sticky="ew", column=1, row=4, padx=10, pady=10) ## tkgrid(tkbutton(dialog, text="Create subgraph", command=create.subgraph), ## "in"=frame, sticky="nsew", column=1, row=6, padx=10, pady=10) tkgrid(tkbutton(dialog, text="Close", command=function() tkdestroy(dialog)), "in"=frame, sticky="nsew", columnspan=2, padx=10, pady=10) } .tkigraph.my.spinglass <- function() { gnos <- .tkigraph.get.selected() if (length(gnos)!=1) { .tkigraph.error("Please select exactly one graph") return() } graph <- get("graphs", .tkigraph.env)[[gnos]] if (!is.connected(graph)) { .tkigraph.error("Graph is not connected") return() } weights <- if ("weight" %in% list.edge.attributes(graph)) "TRUE" else "FALSE" read <- .tkigraph.dialogbox(TITLE="Spinglass community of a vertex", vertex=list(name="Vertex", type="numeric", default=1, min=1, max=vcount(graph)), gamma=list(name="Gamma parameter", type="numeric", default=1), weights=list(name="Use edge weights", type="boolean", default=weights), spins=list(name="Number of spins", type="numeric", default=25), update.rule=list(name="Update rule", type="listbox", default="1", values=c("Simple", "Configuration model"))) if (read$vertex<1 || read$vertex > vcount(graph)) { .tkigraph.error("Invalid vertex id") return() } read$update.rule <- c("simple", "config")[read$update.rule+1] if (read$weights) { if (!"weight" %in% list.edge.attributes(graph)) { .tkigraph.warning("This graphs is not weighted") read$weights <- NULL } else { read$weights <- E(graph)$weight } } else { read$weights <- NULL } comm <- spinglass.community(graph, vertex=read$vertex, weights=read$weights, spins=read$spins, update.rule=read$update.rule, gamma=read$gamma) .tkigraph.spinglass.mycommunity.dialog(comm, read, gnos) } .tkigraph.spinglass.mycommunity.dialog <- function(comm, read, gnos) { dialog <- tktoplevel() frame <- tkframe(dialog) tkgrid(frame) tktitle(dialog) <- "Spinglass community of a single vertex" scr <- tkscrollbar(dialog, repeatinterval=5, command=function(...) tkyview(txt,...)) read$update.rule <- if (read$update.rule=="simple") "Simple" else "Configuration model" tkgrid(tklabel(dialog, text="Spinglass community of a single vertex", font=tkfont.create(family="times", size=16, weight="bold")), columnspan=3, sticky="nsew", "in"=frame, padx=10, pady=10) tkgrid(txt <- tktext(dialog, yscrollcommand=function(...) tkset(scr,...)), columnspan=1, rowspan=3, sticky="nsew", "in"=frame, padx=10, pady=10) tkconfigure(txt, height=17) tkgrid(scr, row=1, column=1, rowspan=3, sticky="ns", "in"=frame, pady=10) tkinsert(txt, "end", "Parameters were:\n") tkinsert(txt, "end", paste(" Vertex:", read$vertex, "\n")); tkinsert(txt, "end", paste(" Gamma=", read$gamma, "\n")) tkinsert(txt, "end", if (is.null(read$weights)) " Weights were not used.\n" else " Weights were used.\n") tkinsert(txt, "end", paste(" Number of spins=", read$spins, "\n")) tkinsert(txt, "end", paste(" Update rule:", read$update.rule, "\n")) tkinsert(txt, "end", "\nResults:\n") tkinsert(txt, "end", paste(" Size of the community:", length(comm$community), "\n")) tkinsert(txt, "end", paste(" Cohesion:", comm$cohesion, "\n")) tkinsert(txt, "end", paste(" Adhesion:", comm$adhesion, "\n")) tkinsert(txt, "end", paste(" Inner links:", comm$inner.links, "\n")) tkinsert(txt, "end", paste(" Outer links:", comm$outer.links, "\n")) tkinsert(txt, "end", "\nThe community:\n") con <- textConnection(NULL, open="w", local=TRUE) cat(sort(comm$community), file=con, fill=TRUE, sep=", ") tkinsert(txt, "end", textConnectionValue(con)) close(con) tkconfigure(txt, state="disabled") plot.communities <- function(simple=FALSE) { graph <- get("graphs", .tkigraph.env)[[gnos]] color <- rep("skyblue2", vcount(graph)) color[ comm$community ] <- "red" .tkigraph.plot(gnos=gnos, simple=simple, vertex.color=color) } create.graph <- function() { graph <- get("graphs", .tkigraph.env)[[gnos]] g <- induced.subgraph(graph, comm$community) .tkigraph.add.graph(g) } tkgrid(tkbutton(dialog, text="Draw community", command=function() plot.communities(simple=FALSE)), "in"=frame, sticky="ew", column=2, row=1, padx=10, pady=10) tkgrid(tkbutton(dialog, text="Create graph from community", command=create.graph), "in"=frame, sticky="ew", column=2, row=2, padx=10, pady=10) tkgrid(tkbutton(dialog, text="Close", command=function() tkdestroy(dialog)), "in"=frame, sticky="nsew", columnspan=3, padx=10, pady=10) } .tkigraph.cohesion <- function() { gnos <- .tkigraph.get.selected() if (length(gnos) != 1) { .tkigraph.error("Please select exactly one graph") return() } graphs <- decompose.graph(get("graphs", .tkigraph.env)[[gnos]]) coh <- sapply(graphs, graph.cohesion) value <- data.frame("Component"=seq(length=length(graphs)), "Cohesion"=coh) .tkigraph.showData(value, title=paste("Cohesion of components in graph #", gnos), right=FALSE) } .tkigraph.help <- function(page="index.html") { dialog <- tktoplevel() tktitle(dialog) <- "Help (main page)" close <- function() { tkdestroy(dialog) } scr <- tkscrollbar(dialog, repeatinterval=5, command=function(...) tkyview(txt,...)) txt <- tktext(dialog, yscrollcommand=function(...) tkset(scr, ...), width=80, height=40) main.menu <- tkmenu(dialog) tkadd(main.menu, "command", label="Back", command=function() { tcl("render_back", txt) }) tkadd(main.menu, "command", label="Forw", command=function() { tcl("render_forw", txt) }) tkadd(main.menu, "command", label="Home", command=function() { tcl("render", txt, "index.html"); return() }) tkadd(main.menu, "command", label="Close", command=function() { tkdestroy(dialog); return() }) tkconfigure(dialog, "-menu", main.menu) tkpack(scr, side="right", fill="y", expand=0) tkpack(txt, side="left", fill="both", expand=1) browser.button <- tkbutton(dialog, command=function() { browseURL(tclvalue("browser_url")) }) tcl("global", "tkigraph_help_root", "tkigraph_help_history", "tkigraph_help_history_pos", "browser_button", "browser_url") tcl("set", "tkigraph_help_root", system.file("tkigraph_help", package="igraph")) tcl("set", "browser_button", browser.button) tcl("source", system.file("html_library.tcl", package="igraph")) tcl("source", system.file("my_html_library.tcl", package="igraph")) tcl("HMinit_win", txt) tcl("start_history", txt) tcl("render", txt, "index.html") tkconfigure(txt, state="disabled") } .tkigraph.help.external <- function(page="index.html") { f <- system.file("tkigraph_help/index.html", package="igraph") browseURL(f) } .tkigraph.about <- function() { dialog <- tktoplevel() tktitle(dialog) <- "About tkigraph" image <-tkimage.create("photo", "img", format="gif", file=system.file("igraph.gif", package="igraph")) logo <- tklabel(dialog, relief="flat", padx=10, pady=10, image=image) label <- tklabel(dialog, padx=30, pady=10, text=paste(sep="", "tkigraph (c) 2009 Gabor Csardi\n", "igraph (c) 2003-2009 Gabor Csardi and Tamas Nepusz\n\n", "This is igraph version ", packageDescription("igraph")$Version, " and\n", R.version$version.string)) close <- tkbutton(dialog, text="Close", command=function() { tkdestroy(dialog); return() }) tkpack(logo, side="top", anchor="c", expand=0) tkpack(label, side="top", anchor="c", expand=0) tkpack(close, side="bottom", anchor="c", expand=0) } ##################################################### # This is from the 'relimp' package by David Firth, thanks .tkigraph.showData <- function (dataframe, colname.bgcolor = "grey50", rowname.bgcolor = "grey50", body.bgcolor = "white", colname.textcolor = "white", rowname.textcolor = "white", body.textcolor = "black", font = "Courier 12", maxheight = 30, maxwidth = 80, title = NULL, rowname.bar = "left", colname.bar = "top", rownumbers = FALSE, placement = "-20-40", plot.text="Plot", plot.command=NULL, suppress.X11.warnings = FALSE, right=TRUE, showmean=NULL, sort.button=TRUE, inthis=NULL) { if (suppress.X11.warnings) { ## as in John Fox's Rcmdr package messages.connection <- textConnection(".messages", open = "w", local = TRUE) sink(messages.connection, type = "message") on.exit({ sink(type="message") close(messages.connection) }) } object.name <- deparse(substitute(dataframe)) if (!is.data.frame(dataframe)){ temp <- try(dataframe <- as.data.frame(dataframe), silent = FALSE) if (inherits(temp, "try-error")) { stop(paste(object.name, "cannot be coerced to a data frame")) } object.name <- paste("as.data.frame(", object.name, ")", sep = "") } if (is.numeric(rownumbers) && length(rownumbers) != nrow(dataframe)) stop("rownumbers argument must be TRUE, FALSE or have length nrow(dataframe)") oldwidth <- unlist(options("width")) options(width = 10000) conn <- textConnection(NULL, open="w", local=TRUE) sink(conn) options(max.print=10000000) print(dataframe, right=right) sink() zz <- strsplit(textConnectionValue(conn), "\n", fixed=TRUE) close(conn) if (length(zz) > 1 + nrow(dataframe)) stop( "data frame too wide") options(width = oldwidth) if (is.null(inthis)) { base <- tktoplevel() tkwm.geometry(base, placement) tkwm.title(base, { if (is.null(title)) object.name else title }) } else { base <- inthis } nrows <- length(zz) - 1 if (is.numeric(rownumbers)) rowname.text <- paste(rownumbers, row.names(dataframe)) else if (rownumbers) rowname.text <- paste(1:nrows, row.names(dataframe)) else rowname.text <- row.names(dataframe) namewidth = max(nchar(rowname.text)) yy <- substring(zz, 2 + max(nchar(row.names(dataframe)))) datawidth <- max(nchar(yy)) winwidth <- min(1 + datawidth, maxwidth) hdr <- tktext(base, bg = colname.bgcolor, fg = colname.textcolor, font = font, height = 1, width = winwidth, takefocus = TRUE) ftr <- tktext(base, bg = colname.bgcolor, fg = colname.textcolor, font = font, height = 1, width = winwidth, takefocus = TRUE) textheight <- min(maxheight, nrows) txt <- tktext(base, bg = body.bgcolor, fg = body.textcolor, font = font, height = textheight, width = winwidth, setgrid = 1, takefocus = TRUE) lnames <- tktext(base, bg = rowname.bgcolor, fg = rowname.textcolor, font = font, height = textheight, width = namewidth, takefocus = TRUE) rnames <- tktext(base, bg = rowname.bgcolor, fg = rowname.textcolor, font = font, height = textheight, width = namewidth, takefocus = TRUE) xscroll <- tkscrollbar(base, orient = "horizontal", repeatinterval = 1, command = function(...) { tkxview(txt, ...) tkxview(hdr, ...) tkxview(ftr, ...) }) string.to.vector <- function(string.of.indices) { string.of.indices <- tclvalue(string.of.indices) as.numeric(strsplit(string.of.indices, split = " ")[[1]]) } tkconfigure(txt, xscrollcommand = function(...) { tkset(xscroll, ...) xy <- string.to.vector(tkget(xscroll)) tkxview.moveto(hdr, xy[1]) tkxview.moveto(ftr, xy[1]) }) tkconfigure(hdr, xscrollcommand = function(...) { tkset(xscroll, ...) xy <- string.to.vector(tkget(xscroll)) tkxview.moveto(txt, xy[1]) tkxview.moveto(ftr, xy[1]) }) tkconfigure(ftr, xscrollcommand = function(...) { tkset(xscroll, ...) xy <- string.to.vector(tkget(xscroll)) tkxview.moveto(hdr, xy[1]) tkxview.moveto(txt, xy[1]) }) yscroll <- tkscrollbar(base, orient = "vertical", repeatinterval = 1, command = function(...) { tkyview(txt, ...) tkyview(lnames, ...) tkyview(rnames, ...) }) tkconfigure(txt, yscrollcommand = function(...) { tkset(yscroll, ...) xy <- string.to.vector(tkget(yscroll)) tkyview.moveto(lnames, xy[1]) tkyview.moveto(rnames, xy[1]) }) tkconfigure(lnames, yscrollcommand = function(...) { tkset(yscroll, ...) xy <- string.to.vector(tkget(yscroll)) tkyview.moveto(txt, xy[1]) tkyview.moveto(rnames, xy[1]) }) tkconfigure(rnames, yscrollcommand = function(...) { tkset(yscroll, ...) xy <- string.to.vector(tkget(yscroll)) tkyview.moveto(txt, xy[1]) tkyview.moveto(lnames, xy[1]) }) tkbind(txt, "", function(x, y) { tkscan.dragto(txt, x, y) }) ## The next block just enables copying from the text boxes { copyText.hdr <- function(){ tcl("event", "generate", .Tk.ID(hdr), "<>")} tkbind(hdr, "", function() tkfocus(hdr)) editPopupMenu.hdr <- tkmenu(hdr, tearoff = FALSE) tkadd(editPopupMenu.hdr, "command", label = "Copy ", command = copyText.hdr) RightClick.hdr <- function(x,y) # x and y are the mouse coordinates { rootx <- as.integer(tkwinfo("rootx", hdr)) rooty <- as.integer(tkwinfo("rooty", hdr)) xTxt <- as.integer(x) + rootx yTxt <- as.integer(y) + rooty tcl("tk_popup", editPopupMenu.hdr, xTxt, yTxt) } tkbind(hdr, "", RightClick.hdr) tkbind(hdr, "", copyText.hdr) ## copyText.ftr <- function(){ tcl("event", "generate", .Tk.ID(ftr), "<>")} tkbind(ftr, "", function() tkfocus(ftr)) editPopupMenu.ftr <- tkmenu(ftr, tearoff = FALSE) tkadd(editPopupMenu.ftr, "command", label = "Copy ", command = copyText.ftr) RightClick.ftr <- function(x,y) # x and y are the mouse coordinates { rootx <- as.integer(tkwinfo("rootx", ftr)) rooty <- as.integer(tkwinfo("rooty", ftr)) xTxt <- as.integer(x) + rootx yTxt <- as.integer(y) + rooty tcl("tk_popup", editPopupMenu.ftr, xTxt, yTxt) } tkbind(ftr, "", RightClick.ftr) tkbind(ftr, "", copyText.ftr) ## copyText.txt <- function(){ tcl("event", "generate", .Tk.ID(txt), "<>")} tkbind(txt, "", function() tkfocus(txt)) editPopupMenu.txt <- tkmenu(txt, tearoff = FALSE) tkadd(editPopupMenu.txt, "command", label = "Copy ", command = copyText.txt) RightClick.txt <- function(x,y) # x and y are the mouse coordinates { rootx <- as.integer(tkwinfo("rootx", txt)) rooty <- as.integer(tkwinfo("rooty", txt)) xTxt <- as.integer(x) + rootx yTxt <- as.integer(y) + rooty tcl("tk_popup", editPopupMenu.txt, xTxt, yTxt) } tkbind(txt, "", RightClick.txt) tkbind(txt, "", copyText.txt) ## copyText.lnames <- function(){ tcl("event", "generate", .Tk.ID(lnames), "<>")} tkbind(lnames, "", function() tkfocus(lnames)) editPopupMenu.lnames <- tkmenu(lnames, tearoff = FALSE) tkadd(editPopupMenu.lnames, "command", label = "Copy ", command = copyText.lnames) RightClick.lnames <- function(x,y) # x and y are the mouse coordinates { rootx <- as.integer(tkwinfo("rootx", lnames)) rooty <- as.integer(tkwinfo("rooty", lnames)) xTxt <- as.integer(x) + rootx yTxt <- as.integer(y) + rooty tcl("tk_popup", editPopupMenu.lnames, xTxt, yTxt) } tkbind(lnames, "", RightClick.lnames) tkbind(lnames, "", copyText.lnames) ## copyText.rnames <- function(){ tcl("event", "generate", .Tk.ID(rnames), "<>")} tkbind(rnames, "", function() tkfocus(rnames)) editPopupMenu.rnames <- tkmenu(rnames, tearoff = FALSE) tkadd(editPopupMenu.rnames, "command", label = "Copy ", command = copyText.rnames) RightClick.rnames <- function(x,y) # x and y are the mouse coordinates { rootx <- as.integer(tkwinfo("rootx", rnames)) rooty <- as.integer(tkwinfo("rooty", rnames)) xTxt <- as.integer(x) + rootx yTxt <- as.integer(y) + rooty tcl("tk_popup", editPopupMenu.rnames, xTxt, yTxt) } tkbind(rnames, "", RightClick.rnames) tkbind(rnames, "", copyText.rnames) } tktag.configure(hdr, "notwrapped", wrap = "none") tktag.configure(ftr, "notwrapped", wrap = "none") tktag.configure(txt, "notwrapped", wrap = "none") tktag.configure(lnames, "notwrapped", wrap = "none") tktag.configure(rnames, "notwrapped", wrap = "none") tkinsert(txt, "end", paste(paste(yy[-1], collapse = "\n"), sep = ""), "notwrapped") tkgrid(txt, row = 1, column = 1, sticky = "nsew") if ("top" %in% colname.bar) { tkinsert(hdr, "end", paste(yy[1], sep = ""), "notwrapped") tkgrid(hdr, row = 0, column = 1, sticky = "ew") } if ("bottom" %in% colname.bar) { tkinsert(ftr, "end", paste(yy[1], sep = ""), "notwrapped") tkgrid(ftr, row = 2, column = 1, sticky = "ew") } if ("left" %in% rowname.bar) { tkinsert(lnames, "end", paste(rowname.text, collapse = "\n"), "notwrapped") tkgrid(lnames, row = 1, column = 0, sticky = "ns") } if ("right" %in% rowname.bar) { tkinsert(rnames, "end", paste(rowname.text, collapse = "\n"), "notwrapped") tkgrid(rnames, row = 1, column = 2, sticky = "ns") } tkconfigure(hdr, state = "disabled") tkconfigure(ftr, state = "disabled") tkconfigure(txt, state = "disabled") tkconfigure(lnames, state = "disabled") tkconfigure(rnames, state = "disabled") if (maxheight < nrows) { tkgrid(yscroll, row = 1, column = 3, sticky = "ns") } if (maxwidth < datawidth) { tkgrid(xscroll, row = 3, column = 1, sticky = "ew") } sortColumn <- function(n, decreasing=FALSE) { dataframe <<- dataframe[ order(dataframe[[n]], decreasing=decreasing), ] rownames(dataframe) <- seq(length=nrow(dataframe)) .tkigraph.showData(dataframe, colname.bgcolor = colname.bgcolor, rowname.bgcolor = rowname.bgcolor, body.bgcolor = body.bgcolor, colname.textcolor = colname.textcolor, rowname.textcolor = rowname.textcolor, body.textcolor = body.textcolor, font = font, maxheight = maxheight, maxwidth = maxwidth, title = title, rowname.bar = rowname.bar, colname.bar = colname.bar, rownumbers = rownumbers, placement = placement, plot.text=plot.text, plot.command=plot.command, suppress.X11.warnings = suppress.X11.warnings, right=right, showmean=showmean, sort.button=sort.button, inthis=base) } pf <- tkframe(base) if (is.null(inthis)) { tkgrid(pf, column=5, row=0, rowspan=10, sticky="new") } if (!is.null(showmean) && is.null(inthis)) { for (i in seq(along=showmean)) { tkgrid(tklabel(base, text=showmean[1]), sticky="nsew", column=0, padx=1, pady=1, columnspan=4) } } sortBut <- tkbutton(base, text="Sort otherwise", command=function() {}) sortPopup <- function() { sortMenu <- tkmenu(base, tearoff=FALSE) sapply(seq(along=colnames(dataframe)), function(n) { tkadd(sortMenu, "command", label=colnames(dataframe)[n], command=function() sortColumn(colnames(dataframe)[n])) label <- paste(colnames(dataframe)[n], "decreasing", sep=", ") tkadd(sortMenu, "command", label=label, command=function() sortColumn(colnames(dataframe)[n], decreasing=TRUE)) }) rootx <- as.integer(tkwinfo("rootx", sortBut)) rooty <- as.integer(tkwinfo("rooty", sortBut)) tkpopup(sortMenu, rootx, rooty) } if (!is.null(plot.command)) { but <- tkbutton(base, text=plot.text, command=plot.command) tkgrid(but, "in"=pf, sticky="ew", column=10, row=1, padx=1, pady=1) } if (sort.button) { tkgrid(sortBut, "in"=pf, sticky="ew", column=10, row=2, padx=1, pady=1) } tkconfigure(sortBut, command=sortPopup) savebut <- tkbutton(base, text="Export table to file", command=function() { filename <- tkgetSaveFile(initialfile="data.txt", defaultextension="txt", title="Export as table") filename <- paste(as.character(filename), collapse=" ") write.table(dataframe, file=filename, row.names=FALSE, col.names=FALSE) }) tkgrid(savebut, "in"=pf, sticky="ew", column=10, row=3, padx=1, pady=1) but <- tkbutton(base, text="Close", command=function() tkdestroy(base)) tkgrid(but, "in"=pf, sticky="ew", column=10, row=4, padx=1, pady=1) tkgrid.columnconfigure(pf, 0, weight=1) tkgrid.rowconfigure(base, 1, weight = 1) tkgrid.columnconfigure(base, 1, weight = 1) tkwm.maxsize(base, 2 + datawidth, nrows) tkwm.minsize(base, 2 + nchar(names(dataframe)[1]), 1) invisible(NULL) } .tkigraph.net.moody.white <- matrix( c(0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, 1,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0, 0,0,0,0,0,0,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,0, 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0, 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0), nrow=23, ncol=23) igraph/R/print.R0000644000176000001440000004362512272007746013255 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Convert graphs to human readable forms ################################################################### .get.attr.codes <- function(object) { ga <- va <- ea <- "" gal <- list.graph.attributes(object) if (length(gal) != 0) { ga <- paste(sep="", gal, " (g/", .Call("R_igraph_get_attr_mode", object, 2L, PACKAGE="igraph"), ")") } val <- list.vertex.attributes(object) if (length(val) != 0) { va <- paste(sep="", val, " (v/", .Call("R_igraph_get_attr_mode", object, 3L, PACKAGE="igraph"), ")") } eal <- list.edge.attributes(object) if (length(eal) != 0) { ea <- paste(sep="", list.edge.attributes(object), " (e/", .Call("R_igraph_get_attr_mode", object, 4L, PACKAGE="igraph"), ")") } c(ga, va, ea) } .print.header <- function(object) { if (!is.igraph(object)) { stop("Not a graph object") } title <- paste(sep="", "IGRAPH ", c("U","D")[is.directed(object)+1], c("-","N")[is.named(object)+1], c("-","W")[is.weighted(object)+1], c("-","B")[is.bipartite(object)+1], " ", vcount(object), " ", ecount(object), " -- ") w <- getOption("width") if (nchar(title) < w && "name" %in% list.graph.attributes(object)) { title <- substring(paste(sep="", title, as.character(object$name)[1]), 1, w-1) } cat(title, "\n", sep="") atxt <- .get.attr.codes(object) atxt <- paste(atxt[atxt!=""], collapse=", ") if (atxt != "") { atxt <- strwrap(paste(sep="", "+ attr: ", atxt), exdent=2) cat(atxt, sep="\n") } } .print.graph.attributes <- function(x) { list <- list.graph.attributes(x) if (length(list)!=0) { cat("+ graph attributes:\n") lapply(list, function(n) { cat(sep="", "[[", n, "]]\n") print(get.graph.attribute(x, n)) }) } } .print.vertex.attributes <- function(x) { vc <- vcount(x) list <- list.vertex.attributes(x) if (length(list) != 0) { cat("+ vertex attributes:\n") mp <- getOption("max.print") options(max.print=1000000000) if (vc <= mp) { omitted.vertices <- 0 ind <- as.numeric(V(x)) } else { omitted.vertices <- vc-mp ind <- seq(length=mp) } if (vc==0 || all(sapply(list, function(v) is.numeric(get.vertex.attribute(x, v)) || is.character(get.vertex.attribute(x, v)) || is.logical(get.vertex.attribute(x, v))))) { ## create a table tab <- data.frame(v=paste(sep="", "[", ind, "]"), row.names="v") for (i in list) { tab[i] <- get.vertex.attribute(x, i, ind) } print(tab) } else { for (i in ind) { cat(sep="", "[[", i, "]]\n") lapply(list, function(n) { cat(sep="", "[[", i, "]][[", n, "]]\n") print(get.vertex.attribute(x, n, i))}) } } options(max.print=mp) if (omitted.vertices != 0) { cat(paste('[ reached getOption("max.print") -- omitted', omitted.vertices, "vertices ]\n\n")) } } } .print.edges.edgelist <- function(x, names) { ec <- ecount(x) list <- list.edge.attributes(x) list <- list[list!="name"] arrow <- ifelse(is.directed(x), "->", "--") if (is.named(x)) { cat("+ edges (vertex names) and their attributes:\n") } else { cat("+ edges and their attributes:\n") } if (names && ! "name" %in% list.vertex.attributes(x)) { names <- FALSE } if (names && "name" %in% list.vertex.attributes(x) && !is.numeric(get.vertex.attribute(x, "name")) && !is.character(get.vertex.attribute(x, "name")) && !is.logical(get.vertex.attribute(x, "name"))) { warning("Can't print vertex names, complex `name' vertex attribute") names <- FALSE } mp <- getOption("max.print") if (mp >= ec) { omitted.edges <- 0 el <- get.edgelist(x, names=names) } else { omitted.edges <- ec-mp el <- get.edges(x, seq_len(mp)) if (names) { el[] <- V(x)$name[el] } } ename <- if ("name" %in% list.edge.attributes(x)) { paste(sep="", "'", E(x)$name, "'") } else { seq(length=nrow(el)) } if (ec==0 || all(sapply(list, function(v) is.numeric(get.edge.attribute(x, v)) | is.character(get.edge.attribute(x,v)) | is.logical(get.edge.attribute(x, v))))) { ## create a table tab <- data.frame(row.names=paste(sep="", "[", ename, "]")) if (is.numeric(el)) { w <- nchar(max(el)) } else { w <- max(nchar(el)) } tab["edge"] <- paste(sep="", format(el[,1], width=w), arrow, format(el[,2], width=w)) for (i in list) { tab[i] <- get.edge.attribute(x, i) } print(tab) } else { i <- 1 apply(el, 1, function(v) { cat(sep="", "[", ename[i], "] ", v[1], " ", arrow, " ", v[2]); lapply(list, function(n) { cat(sep="", "\n[[", i, "]][[", n, "]]\n") print(get.edge.attribute(x, n, i))}) cat("\n") i <<- i+1 }) } if (omitted.edges != 0) { cat(paste('[ reached getOption("max.print") -- omitted', omitted.edges, 'edges ]\n\n')) } } .print.edges.compressed <- function(x, names) { ## TODO: getOption("max.print") if (is.named(x)) cat("+ edges (vertex names):\n") else cat("+ edges:\n") el <- get.edgelist(x, names=names) arrow <- c("--", "->")[is.directed(x)+1] edges <- paste(sep="", format(el[,1]), arrow, format(el[,2])) print(edges, quote=FALSE) } .print.edges.adjlist <- function(x) { ## TODO: getOption("max.print") cat("+ edges:\n") vc <- vcount(x) arrow <- c(" -- ", " -> ")[is.directed(x)+1] al <- get.adjlist(x, mode="out") w <- nchar(max(which(degree(x, mode="in") != 0))) mpl <- trunc((getOption("width")-nchar(arrow)-nchar(vc)) / (w+1)) if (any(sapply(al, length) > mpl)) { ## Wrapping needed mw <- nchar(vcount(x)) sm <- paste(collapse="", rep(" ", mw+4)) alstr <- lapply(seq_along(al), function(x) { len <- length(al[[x]]) fac <- rep(1:(len/mpl+1), each=mpl, length=len) nei <- tapply(format(al[[x]], width=mw), fac, paste, collapse=" ") mark <- paste(sep="", format(x, width=mw), arrow) mark <- c(mark, rep(sm, max(0, length(nei)-1))) paste(sep="", mark, nei) }) cat(unlist(alstr), sep="\n") } else { alstr <- sapply(al, function(x) { paste(format(x, width=w), collapse=" ") }) mark <- paste(sep="", format(seq_len(vc)), arrow) alstr <- paste(sep="", mark, alstr) maxw <- max(nchar(alstr)) sep <- " " ncol <- trunc((getOption("width")-1+nchar(sep)) / (maxw+nchar(sep))) if (ncol > 1) { alstr <- format(alstr, width=maxw, justify="left") fac <- rep(1:(vc/ncol+1), each=ncol, length=vc) alstr <- tapply(alstr, fac, paste, collapse=sep) } cat(alstr, sep="\n") } } .print.edges.adjlist.named <- function(x) { ## TODO getOption("max.print") cat("+ edges (vertex names):\n") arrow <- c(" -- ", " -> ")[is.directed(x)+1] vn <- V(x)$name al <- get.adjlist(x, mode="out") alstr <- sapply(al, function(x) { paste(collapse=", ", vn[x]) }) alstr <- paste(sep="", format(vn), arrow, alstr) alstr <- strwrap(alstr, exdent=max(nchar(vn))+nchar(arrow)) cat(alstr, sep="\n") } str.igraph <- function(object, ...) { print.igraph(object, full=TRUE, ...) } print.igraph <- function(x, full=getIgraphOpt("print.full"), graph.attributes=getIgraphOpt("print.graph.attributes"), vertex.attributes=getIgraphOpt("print.vertex.attributes"), edge.attributes=getIgraphOpt("print.edge.attributes"), names=TRUE, ...) { if (!is.igraph(x)) { stop("Not a graph object") } .print.header(x) if (full) { if (graph.attributes) .print.graph.attributes(x) if (vertex.attributes) .print.vertex.attributes(x) if (ecount(x)==0) { ## Do nothing } else if (edge.attributes && length(list.edge.attributes(x)) !=0 ) { .print.edges.edgelist(x, names) } else if (median(degree(x, mode="out")) < 3) { .print.edges.compressed(x, names) } else if (is.named(x)) { .print.edges.adjlist.named(x) } else { .print.edges.adjlist(x) } } invisible(x) } summary.igraph <- function(object, ...) { if (!is.igraph(object)) { stop("Not a graph object") } title <- paste(sep="", "IGRAPH ", c("U","D")[is.directed(object)+1], c("-","N")[is.named(object)+1], c("-","W")[is.weighted(object)+1], c("-","B")[is.bipartite(object)+1], " ", vcount(object), " ", ecount(object), " -- ") w <- getOption("width") if (nchar(title) < w && "name" %in% list.graph.attributes(object)) { title <- substring(paste(sep="", title, as.character(object$name)[1]), 1, w-1) } cat(title, "\n", sep="") atxt <- .get.attr.codes(object) atxt <- paste(atxt[atxt!=""], collapse=", ") if (atxt != "") { atxt <- strwrap(paste(sep="", "attr: ", atxt), exdent=2) cat(atxt, sep="\n") } invisible(object) } " #################################################################### ## Various designs for printing graphs ## Summary IGRAPH UNW- 5 5 -- A ring Attr: name (g/c), name (v/c), weight (e/n) IGRAPH D-W- 100 200 -- Gnm random graph ## Printing, edge list IGRAPH-UNW--V5-E5----------------------------------------- A ring - + attributes: name (g), name (v), weight (e). + edges: edge weight [1]' a--b 1 [2]' b--c 2 [3]' c--d -1 [4]' d--e 0.5 [5]' a--e 1 ## Compressed edge list IGRAPH UNW- 5 10 -- A ring + attributes: name (g/c), name (v/n), weight (e/n) + edges: [1]' 1--2 2--3 3--4 4--5 1--5 2--5 5--1 [8]' 1--4 4--2 1--3 ## This is good if vertices are named IGRAPH UNW- 10 18 -- Krackhardt kite + attributes: name (g/c), name (v/c), weight (e/n) + edges: Andre -- [1] Beverly, Carol, Diane, Fernando Beverly -- [1] Andre, Diane, Ed, Garth Carol -- [1] Andre, Diane, Fernando Diane -- [1] Andre, Beverly, Carol, Diane, Ed -- [6] Garth Ed -- [1] Beverly, Diane, Garth Fernando -- [1] Andre, Carol, Diane, Garth Garth -- [1] Beverly, Diane, Ed, Fernando Heather -- [1] Fernando, Garth Ike -- [1] Heather, Jane Jane -- [1] Ike IGRAPH UNW- 10 18 -- Krackhardt kite + attributes: name (g/c), name (v/c), weight (e/n) + edges: Andre -- Beverly, Carol, Diane, Fernando Beverly -- Andre, Diane, Ed, Garth Carol -- Andre, Diane, Fernando Diane -- Andre, Beverly, Carol, Diane, Ed, Garth Ed -- Beverly, Diane, Garth Fernando -- Andre, Carol, Diane, Garth Garth -- Beverly, Diane, Ed, Fernando Heather -- Fernando, Garth Ike -- Heather, Jane Jane -- Ike ## This is the good one if vertices are not named IGRAPH U--- 100 200 -- Gnm random graph + edges: [ 1] 28 46 89 90 [ 2] 47 69 72 89 [ 3] 29 [ 4] 17 20 [ 5] 11 40 42 51 78 89 [ 6] 27 32 70 87 93 [ 7] 18 27 87 [ 8] 18 24 82 [ 9] 18 20 85 94 [ 10] 24 70 77 91 [ 11] 5 12 34 61 62 [ 12] 11 41 44 61 65 80 ... ## Alternative designs, summary IGRAPH-UNW--V5-E5,---------------------------------------- A ring - + attributes: name (g/c), name (v/c), weight (e/n) IGRAPH. |V|=5, |E|=5, undirected, named, weighted. Attributes: name (g/c), name (v/c), weight (e/n) IGRAPH: 'A ring' Graph attributes: |V|=5, |E|=5, undirected, name. Vertex attributes: name. Edge attributes: weight. ## Alternative designs, printing IGRAPH-UNW--V5-E5----------------------------------------- A ring - '- attributes: name (g), name (v), weight (e). ' edge weight [1] 'a' -- 'b' 1 [2] 'b' -- 'c' 2 [3] 'c' -- 'd' -1 [4] 'd' -- 'e' 0.5 [5] 'a' -- 'e' 1 IGRAPH-UNW--V-5-E-10-------------------------------------- A ring - |- attributes: name (g), name (v), weight (e). |- edges: [1] 'a'--'b' 'b'--'c' 'c'--'d' 'd'--'e' 'a'--'e' 'b'-'e' [7] 'e'--'a' 'a'--'d' 'd'--'b' 'a'--'c' IGRAPH-UNW--V-5-E-10-------------------------------------- A ring - + attributes: name (g), name (v), weight (e). + vertices: | name | [1] a | [2] b | [3] c | [4] d | [5] e + edges: [1] 'a'--'b' 'b'--'c' 'c'--'d' 'd'--'e' 'a'--'e' 'b'-'e' [7] 'e'--'a' 'a'--'d' 'd'--'b' 'a'--'c' IGRAPH-UNW--V-5-E-10-------------------------------------- A ring - + graph attributes: name + vertex attributes: name + edge attributes: weight + vertices: | name |1] a |2] b |3] c |4] d |5] e + edges: |1] a--b b--c c--d d--e a--e b-e |7] e--a a--d d--b a--c IGRAPH-UNW--V-5-E-10-------------------------------------- A ring - + graph attributes: name (c) + vertex attributes: name (c) + edge attributes: weight (n) + edges: [1] a--b b--c c--d d--e a--e b-e [7] e--a a--d d--b a--c IGRAPH-UNW--V-5-E-10-------------------------------------- A ring - + attributes: name (g/c), name (v/c), weight (e/n) + edges: [ 1] a--b b--c c--d d--e a--e b--e e--a a--d d--b [10] a--c IGRAPH-DNW--V-5-E-10-------------------------------------- A ring - + attributes: name (g/c), name (v/n), weight (e/n) + edges: [1]' 1->2 2->3 3->4 4->5 1->5 2->5 5->1 [8]' 1->4 4->2 1->3 IGRAPH-UNW--V-5-E-20-------------------------------------- A ring - + attributes: name (g/c), name (v/c), weight (e/n) + edges: [ 1] a-b b-c c-d d-e a-e b-e e-a a-d d-b a-c [11] a-b b-c c-d d-e a-e b-e e-a a-d d-b a-c IGRAPH-UNW--V-8-E-10-------------------------------------- A ring - + attributes: name (g/c), name (v/c), weight (e/n) + edges: [a] b c e f h [b] a c e [c] a b d [d] a b c h [e] a b d [f] a [g] [h] a d IGRAPH-UNW--V-10-E-18------------------------------------- A ring - + attributes: name (g/c), name (v/c), weight (e/n) + edges: [a] a--{b,c,e,f,h} b--{a,c,e} c--{a,b,d} d--{a,b,c,h} [e] e--{a,b,d} f--{a} g--{} h--{a,d} IGRAPH-UNW--V10-E18------------------------------Krackhardt kite-- + attributes: name (g/c), name (v/c), weight (e/n) + edges: [ Andre][1] Beverly Carol Diane Fernando [ Beverly][1] Andre Diane Ed Garth [ Carol][1] Andre Diane Fernando [ Diane][1] Andre Beverly Carol Diane Ed [ Diane][6] Garth [ Ed][1] Beverly Diane Garth [Fernando][1] Andre Carol Diane Garth [ Garth][1] Beverly Diane Ed Fernando [ Heather][1] Fernando Garth [ Ike][1] Heather Jane [ Jane][1] Ike IGRAPH-UNW--V10-E18-------------------------------Krackhardt kite-- + attributes: name (g/c), name (v/c), weight (e/n) + edges: [ Andre][1] Beverly/1 Carol/3 Diane/3 Fernando/1 [ Beverly][1] Andre/1 Diane/1 Ed/2 Garth/2 [ Carol][1] Andre/2 Diane/2 Fernando/1 [ Diane][1] Andre/5 Beverly/1 Carol/0.4 Diane/2 [ Diane][5] Ed/1.5 Garth/2.5 [ Ed][1] Beverly/-1 Diane/1.5 Garth/2 [Fernando][1] Andre/1 Carol/2 Diane/1 Garth/1 [ Garth][1] Beverly/2 Diane/3 Ed/1 Fernando/-1 [ Heather][1] Fernando/3 Garth/1 [ Ike][1] Heather/1 Jane/-1 [ Jane][1] Ike/-2 IGRAPH-UNW--V10-E18-------------------------------Krackhardt kite-- + attributes: name (g/c), name (v/c), weight (e/n) + edges: [ Andre][1] Beverly (1) Carol (3) Diane (3) Fernando (1) [ Beverly][1] Andre (1) Diane (1) Ed (2) Garth (2) [ Carol][1] Andre (2) Diane (2) Fernando (1) [ Diane][1] Andre (5) Beverly (1) Carol (0.5) Diane (2) [ Diane][5] Ed (1.5) Garth (2.5) [ Ed][1] Beverly (-1) Diane (1.5) Garth (2) [Fernando][1] Andre (1) Carol (2) Diane (1) Garth (1) [ Garth][1] Beverly (2) Diane (3) Ed (1) Fernando (-1) [ Heather][1] Fernando (3) Garth (1) [ Ike][1] Heather (1) Jane (-1) [ Jane][1] Ike (-2) IGRAPH UNW- V10 E18 -- Krackhardt kite + attr: name (g/c), name (v/c), weight (e/n) + edges: [ Andre][1] Beverly (1) Carol (3) Diane (3) Fernando (1) [ Beverly][1] Andre (1) Diane (1) Ed (2) Garth (2) [ Carol][1] Andre (2) Diane (2) Fernando (1) [ Diane][1] Andre (5) Beverly (1) Carol (0.5) Diane (2) [ Diane][5] Ed (1.5) Garth (2.5) [ Ed][1] Beverly (-1) Diane (1.5) Garth (2) [Fernando][1] Andre (1) Carol (2) Diane (1) Garth (1) [ Garth][1] Beverly (2) Diane (3) Ed (1) Fernando (-1) [ Heather][1] Fernando (3) Garth (1) [ Ike][1] Heather (1) Jane (-1) [ Jane][1] Ike (-2) IGRAPH-U----V100-E200----------------------------Gnm random graph-- + edges: [ 1] 28 46 89 90 [ 2] 47 69 72 89 [ 3] 29 [ 4] 17 20 [ 5] 11 40 42 51 78 89 [ 6] 27 32 70 87 93 [ 7] 18 27 87 [ 8] 18 24 82 [ 9] 18 20 85 94 [ 10] 24 70 77 91 [ 11] 5 12 34 61 62 [ 12] 11 41 44 61 65 80 ... IGRAPH-U----100-200------------------------------Gnm random graph-- + edges: [ 1] 28 46 89 90 [ 2] 47 69 72 89 [ 3] 29 [ 4] 17 20 [ 5] 11 40 42 51 78 89 [ 6] 27 32 70 87 93 [ 7] 18 27 87 [ 8] 18 24 82 [ 9] 18 20 85 94 [ 10] 24 70 77 91 [ 11] 5 12 34 61 62 [ 12] 11 41 44 61 65 80 ... " igraph/R/conversion.R0000644000176000001440000003451612263023733014277 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### get.adjacency.dense <- function(graph, type=c("both", "upper", "lower"), attr=NULL, edges=FALSE, names=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } type <- igraph.match.arg(type) type <- switch(type, "upper"=0, "lower"=1, "both"=2) if (edges || is.null(attr)) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_get_adjacency", graph, as.numeric(type), as.logical(edges), PACKAGE="igraph") } else { attr <- as.character(attr) if (! attr %in% list.edge.attributes(graph)) { stop("no such edge attribute") } exattr <- get.edge.attribute(graph, attr) if (is.logical(exattr)) { res <- matrix(FALSE, nrow=vcount(graph), ncol=vcount(graph)) } else if (is.character(exattr)) { res <- matrix("", nrow=vcount(graph), ncol=vcount(graph)) } else if (is.numeric(exattr)) { res <- matrix(0, nrow=vcount(graph), ncol=vcount(graph)) } else { stop("Sparse matrices must be either numeric or logical,", "and the edge attribute is not") } if (is.directed(graph)) { for (i in seq(length=ecount(graph))) { e <- get.edge(graph, i) res[ e[1], e[2] ] <- get.edge.attribute(graph, attr, i) } } else { if (type==0) { ## upper for (i in seq(length=ecount(graph))) { e <- get.edge(graph, i) res[ min(e), max(e) ] <- get.edge.attribute(graph, attr, i) } } else if (type==1) { ## lower for (i in seq(length=ecount(graph))) { e <- get.edge(graph, i) res[ max(e), min(e) ] <- get.edge.attribute(graph, attr, i) } } else if (type==2) { ## both for (i in seq(length=ecount(graph))) { e <- get.edge(graph, i) res[ e[1], e[2] ] <- get.edge.attribute(graph, attr, i) if (e[1] != e[2]) { res[ e[2], e[1] ] <- get.edge.attribute(graph, attr, i) } } } } } if (names && "name" %in% list.vertex.attributes(graph)) { colnames(res) <- rownames(res) <- V(graph)$name } res } get.adjacency.sparse <- function(graph, type=c("both", "upper", "lower"), attr=NULL, edges=FALSE, names=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } type <- igraph.match.arg(type) vc <- vcount(graph) el <- get.edgelist(graph, names=FALSE) if (edges) { value <- seq_len(nrow(el)) } else if (!is.null(attr)) { attr <- as.character(attr) if (!attr %in% list.edge.attributes(graph)) { stop("no such edge attribute") } value <- get.edge.attribute(graph, name=attr) if (!is.numeric(value) && !is.logical(value)) { stop("Sparse matrices must be either numeric or logical,", "and the edge attribute is not") } } else { value <- rep(1, nrow(el)) } if (is.directed(graph)) { res <- Matrix::sparseMatrix(dims=c(vc, vc), i=el[,1], j=el[,2], x=value) } else { if (type=="upper") { ## upper res <- Matrix::sparseMatrix(dims=c(vc, vc), i=pmin(el[,1],el[,2]), j=pmax(el[,1],el[,2]), x=value) } else if (type=="lower") { ## lower res <- Matrix::sparseMatrix(dims=c(vc, vc), i=pmax(el[,1],el[,2]), j=pmin(el[,1],el[,2]), x=value) } else if (type=="both") { ## both res <- Matrix::sparseMatrix(dims=c(vc, vc), i=pmin(el[,1],el[,2]), j=pmax(el[,1],el[,2]), x=value, symmetric=TRUE) res <- as(res, "dgCMatrix") } } if (names && "name" %in% list.vertex.attributes(graph)) { colnames(res) <- rownames(res) <- V(graph)$name } res } get.adjacency <- function(graph, type=c("both", "upper", "lower"), attr=NULL, edges=FALSE, names=TRUE, sparse=getIgraphOpt("sparsematrices")) { if (!is.igraph(graph)) { stop("Not a graph object") } if (!sparse) { get.adjacency.dense(graph, type=type, attr=attr, edges=edges, names=names) } else { get.adjacency.sparse(graph, type=type, attr=attr, edges=edges, names=names) } } get.edgelist <- function(graph, names=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- matrix(.Call("R_igraph_get_edgelist", graph, TRUE, PACKAGE="igraph"), ncol=2) res <- res+1 if (names && "name" %in% list.vertex.attributes(graph)) { res <- matrix(V(graph)$name[ res ], ncol=2) } res } as.directed <- function(graph, mode=c("mutual", "arbitrary")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- switch(mode, "arbitrary"=0, "mutual"=1) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_to_directed", graph, as.numeric(mode), PACKAGE="igraph") } get.adjlist <- function(graph, mode=c("all", "out", "in", "total")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- as.numeric(switch(mode, "out"=1, "in"=2, "all"=3, "total"=3)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_get_adjlist", graph, mode, PACKAGE="igraph") res <- lapply(res, function(x) x+1) if (is.named(graph)) names(res) <- V(graph)$name res } get.adjedgelist <- function(graph, mode=c("all", "out", "in", "total")) { if (!is.igraph(graph)) { stop("Not a graph object") } mode <- igraph.match.arg(mode) mode <- as.numeric(switch(mode, "out"=1, "in"=2, "all"=3, "total"=3)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_get_adjedgelist", graph, mode, PACKAGE="igraph") res <- lapply(res, function(x) x+1) if (is.named(graph)) names(res) <- V(graph)$name res } igraph.from.graphNEL <- function(graphNEL, name=TRUE, weight=TRUE, unlist.attrs=TRUE) { if (! "graph" %in% .packages()) { library(graph, pos="package:base") } if (!inherits(graphNEL, "graphNEL")) { stop("Not a graphNEL graph") } al <- lapply(edgeL(graphNEL), "[[", "edges") if (edgemode(graphNEL)=="undirected") { al <- mapply(SIMPLIFY=FALSE, seq_along(al), al, FUN=function(n, l) { c(l, rep(n, sum(l==n))) }) } mode <- if (edgemode(graphNEL)=="directed") "out" else "all" g <- graph.adjlist(al, mode=mode, duplicate=TRUE) if (name) { V(g)$name <- nodes(graphNEL) } ## Graph attributes g.n <- names(graphNEL@graphData) g.n <- g.n [ g.n != "edgemode" ] for (n in g.n) { g <- set.graph.attribute(g, n, graphNEL@graphData[[n]]) } ## Vertex attributes v.n <- names(nodeDataDefaults(graphNEL)) for (n in v.n) { val <- unname(nodeData(graphNEL, attr=n)) if (unlist.attrs && all(sapply(val, length)==1)) { val <- unlist(val) } g <- set.vertex.attribute(g, n, value=val) } ## Edge attributes e.n <- names(edgeDataDefaults(graphNEL)) if (!weight) { e.n <- e.n [ e.n != "weight" ] } if (length(e.n) > 0) { el <- get.edgelist(g) el <- paste(sep="|", el[,1], el[,2]) for (n in e.n) { val <- unname(edgeData(graphNEL, attr=n)[el]) if (unlist.attrs && all(sapply(val, length)==1)) { val <- unlist(val) } g <- set.edge.attribute(g, n, value=val) } } g } igraph.to.graphNEL <- function(graph) { if (!is.igraph(graph)) { stop("Not an igraph graph") } if (! "graph" %in% .packages()) { library(graph, pos="package:base") } if ("name" %in% list.vertex.attributes(graph) && is.character(V(graph)$name)) { name <- V(graph)$name } else { name <- as.character(seq(vcount(graph))) } edgemode <- if (is.directed(graph)) "directed" else "undirected" if ("weight" %in% list.edge.attributes(graph) && is.numeric(E(graph)$weight)) { al <- get.adjedgelist(graph, "out") for (i in seq(along=al)) { edges <- get.edges(graph, al[[i]]) edges <- ifelse( edges[,2]==i, edges[,1], edges[,2]) weights <- E(graph)$weight[al[[i]]] al[[i]] <- list(edges=edges, weights=weights) } } else { al <- get.adjlist(graph, "out") al <- lapply(al, function(x) list(edges=x)) } names(al) <- name res <- new("graphNEL", nodes=name, edgeL=al, edgemode=edgemode) ## Add graph attributes (other than 'directed') ## Are this "officially" supported at all? g.n <- list.graph.attributes(graph) if ("directed" %in% g.n) { warning("Cannot add graph attribute `directed'") g.n <- g.n[ g.n != "directed" ] } for (n in g.n) { res@graphData[[n]] <- get.graph.attribute(graph, n) } ## Add vertex attributes (other than 'name', that is already ## added as vertex names) v.n <- list.vertex.attributes(graph) v.n <- v.n[ v.n != "name" ] for (n in v.n) { nodeDataDefaults(res, attr=n) <- NA nodeData(res, attr=n) <- get.vertex.attribute(graph, n) } ## Add edge attributes (other than 'weight') e.n <- list.edge.attributes(graph) e.n <- e.n[ e.n != "weight" ] if (length(e.n) > 0) { el <- get.edgelist(graph) el <- paste(sep="|", el[,1], el[,2]) for (n in e.n) { edgeDataDefaults(res, attr=n) <- NA res@edgeData@data[el] <- mapply(function(x,y) { xx <- c(x,y); names(xx)[length(xx)] <- n; xx }, res@edgeData@data[el], get.edge.attribute(graph, n), SIMPLIFY=FALSE) } } res } get.incidence.dense <- function(graph, types, names, attr) { if (is.null(attr)) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) ## Function call res <- .Call("R_igraph_get_incidence", graph, types, PACKAGE="igraph") if (names && "name" %in% list.vertex.attributes(graph)) { rownames(res$res) <- V(graph)$name[ res$row_ids+1 ] colnames(res$res) <- V(graph)$name[ res$col_ids+1 ] } else { rownames(res$res) <- res$row_ids+1 colnames(res$res) <- res$col_ids+1 } res$res } else { attr <- as.character(attr) if (!attr %in% list.edge.attributes(graph)) { stop("no such edge attribute") } vc <- vcount(graph) n1 <- sum(!types) n2 <- vc-n1 res <- matrix(0, n1, n2) recode <- numeric(vc) recode[!types] <- seq_len(n1) recode[types] <- seq_len(n2) for (i in seq(length=ecount(graph))) { eo <- get.edge(graph, i) e <- recode[eo] if (!types[eo[1]]) { res[ e[1], e[2] ] <- get.edge.attribute(graph, attr, i) } else{ res[ e[2], e[1] ] <- get.edge.attribute(graph, attr, i) } } if (names && "name" %in% list.vertex.attributes(graph)) { rownames(res) <- V(graph)$name[ which(!types) ] colnames(res) <- V(graph)$name[ which( types) ] } else { rownames(res) <- which(!types) colnames(res) <- which(types) } res } } get.incidence.sparse <- function(graph, types, names, attr) { vc <- vcount(graph) if (length(types) != vc) { stop("Invalid types vector") } el <- get.edgelist(graph, names=FALSE) if (any(types[el[,1]] == types[el[,2]])) { stop("Invalid types vector, not a bipartite graph") } n1 <- sum(!types) n2 <- vc-n1 recode <- numeric(vc) recode[!types] <- seq_len(n1) recode[types] <- seq_len(n2) + n1 el[,1] <- recode[el[,1]] el[,2] <- recode[el[,2]] change <- el[,1] > n1 el[change,] <- el[change,2:1] el[,2] <- el[,2]-n1 if (!is.null(attr)) { attr <- as.character(attr) if (!attr %in% list.edge.attributes(graph)) { stop("no such edge attribute") } value <- get.edge.attribute(graph, name=attr) } else { value <- rep(1, nrow(el)) } res <- Matrix::spMatrix(n1, n2, i=el[,1], j=el[,2], x=value) if (names && "name" %in% list.vertex.attributes(graph)) { rownames(res) <- V(graph)$name[which(!types)] colnames(res) <- V(graph)$name[which(types)] } else { rownames(res) <- which(!types) colnames(res) <- which(types) } res } get.incidence <- function(graph, types=NULL, attr=NULL, names=TRUE, sparse=FALSE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(types) && "type" %in% list.vertex.attributes(graph)) { types <- V(graph)$type } if (!is.null(types)) { types <- as.logical(types) } else { stop("Not a bipartite graph, supply `types' argument") } names <- as.logical(names) sparse <- as.logical(sparse) if (sparse) { get.incidence.sparse(graph, types=types, names=names, attr=attr) } else { get.incidence.dense(graph, types=types, names=names, attr=attr) } } get.data.frame <- function(x, what=c("edges", "vertices", "both")) { if (!is.igraph(x)) { stop("Not a graph object") } what <- igraph.match.arg(what) if (what %in% c("vertices", "both")) { ver <- .Call("R_igraph_mybracket2", x, 9L, 3L, PACKAGE="igraph") class(ver) <- "data.frame" rn <- if (is.named(x)) { V(x)$name } else { seq_len(vcount(x)) } rownames(ver) <- rn } if (what %in% c("edges", "both")) { el <- get.edgelist(x) edg <- c(list(from=el[,1]), list(to=el[,2]), .Call("R_igraph_mybracket2", x, 9L, 4L, PACKAGE="igraph")) class(edg) <- "data.frame" rownames(edg) <- seq_len(ecount(x)) } if (what=="both") { list(vertices=ver, edges=edg) } else if (what=="vertices") { ver } else { edg } } igraph/R/community.R0000644000176000001440000007206612325365704014146 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Community structure ################################################################### membership <- function(communities) { if (!is.null(communities$membership)) { res <- communities$membership } else if (!is.null(communities$merges) && !is.null(communities$modularity)) { res <- community.to.membership2(communities$merges, communities$vcount, which.max(communities$modularity)) } else { stop("Cannot calculate community membership") } if (!is.null(communities$names)) { names(res) <- communities$names } res } print.communities <- function(x, ...) { cat("Graph community structure calculated with the", algorithm(x), "algorithm\n") if (algorithm(x)=="spinglass") { cat("Number of communities:", max(membership(x)), "\n") cat("Modularity:", modularity(x), "\n") cat("Membership vector:\n") print(membership(x)) } else if (algorithm(x) %in% c("walktrap", "edge betweenness", "fast greedy")) { cat("Number of communities (best split):", max(membership(x)), "\n") cat("Modularity (best split):", modularity(x), "\n") cat("Membership vector:\n") print(membership(x)) } else if (algorithm(x) %in% c("leading eigenvector")) { cat("Number of communities (best split):", max(membership(x)), "\n") cat("Modularity (best split):", modularity(x), "\n") cat("Membership vector:\n") print(membership(x)) } else if (algorithm(x) == "label propagation") { cat("Number of communities:", max(membership(x)), "\n") cat("Modularity:", modularity(x), "\n") cat("Membership vector:\n") print(membership(x)) } else if (algorithm(x) == "multi level") { cat("Number of communities (best split):", max(membership(x)), "\n") cat("Modularity (best split):", modularity(x), "\n") cat("Membership vector:\n") print(membership(x)) } else if (algorithm(x) == "optimal") { cat("Number of communities:", max(membership(x)), "\n") cat("Modularity:", modularity(x), "\n") cat("Membership vector:\n") print(membership(x)) } else if (algorithm(x) == "infomap") { cat("Number of communities:", max(membership(x)), "\n") if (!is.null(x$modularity)) { cat("Modularity:", modularity(x), "\n") } cat("Membership vector:\n") print(membership(x)) } else { cat("Number of communities:", max(membership(x)), "\n") if (!is.null(x$modularity)) { cat("Modularity:", modularity(x), "\n") } cat("Membership vector:\n") print(membership(x)) } invisible(x) } create.communities <- function(membership, algorithm=NULL, merges=NULL, modularity=NULL, ...) { stopifnot(is.numeric(membership)) stopifnot(is.null(algorithm) || (is.character(algorithm) && length(algorithm)==1)) stopifnot(is.null(merges) || (is.matrix(merges) && is.numeric(merges) && ncol(merges)==2)) stopifnot(is.null(modularity) || (is.numeric(modularity) && length(modularity) %in% c(1, length(membership)))) res <- list(membership=membership, algorithm=if (is.null(algorithm)) "unknown" else algorithm, modularity=modularity, ...) if (!is.null(merges)) { res$merges <- merges } class(res) <- "communities" res } modularity <- function(x, ...) UseMethod("modularity") modularity.igraph <- function(x, membership, weights=NULL, ...) { # Argument checks if (!is.igraph(x)) { stop("Not a graph object") } membership <- as.numeric(membership) if (!is.null(weights)) weights <- as.numeric(weights) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_modularity", x, membership-1, weights, PACKAGE="igraph") res } modularity.communities <- function(x, ...) { if (!is.null(x$modularity)) { max(x$modularity) } else { stop("Modularity was not calculated") } } length.communities <- function(x) { m <- membership(x) max(m) } sizes <- function(communities) { m <- membership(communities) table(`Community sizes`=m) } communities <- function(communities) { m <- membership(communities) tapply(seq_along(m), m, simplify=FALSE, function(x) x) } algorithm <- function(communities) { communities$algorithm } merges <- function(communities) { if (!is.null(communities$merges)) { communities$merges } else { stop("Not a hierarchical community structure") } } crossing <- function(communities, graph) { m <- membership(communities) el <- get.edgelist(graph, names=FALSE) m1 <- m[el[,1]] m2 <- m[el[,2]] res <- m1 != m2 if (!is.null(names(m1))) { names(res) <- paste(names(m1), names(m2), sep="|") } res } code.length <- function(communities) { communities$codelength } is.hierarchical <- function(communities, full=FALSE) { alg <- algorithm(communities) if (alg %in% c("walktrap", "edge betweenness","fast greedy") || (alg == "leading eigenvector" && !full)) { TRUE } else if (alg %in% c("spinglass", "label propagation", "multi level", "optimal") || (alg == "leading eigenvector" && full)) { FALSE } else { stop("Unknown community detection algorithm") } } complete.dend <- function(comm, use.modularity) { merges <- comm$merges if (nrow(merges) < comm$vcount-1) { if (use.modularity) { stop(paste("`use.modularity' requires a full dendrogram,", "i.e. a connected graph")) } miss <- seq_len(comm$vcount + nrow(merges))[-as.vector(merges)] miss <- c(miss, seq_len(length(miss)-2) + comm$vcount+nrow(merges)) miss <- matrix(miss, byrow=TRUE, ncol=2) merges <- rbind(merges, miss) } storage.mode(merges) <- "integer" merges } # The following functions were adapted from the stats R package as.dendrogram.communities <- function(object, hang=-1, use.modularity=FALSE, ...) { if (!is.hierarchical(object, full=TRUE)) { stop("Not a fully hierarchical community structure") } .memberDend <- function(x) { r <- attr(x,"x.member") if(is.null(r)) { r <- attr(x,"members") if(is.null(r)) r <- 1:1 } r } ## If multiple components, then we merge them in arbitrary order merges <- complete.dend(object, use.modularity) storage.mode(merges) <- "integer" if (is.null(object$names)) { object$names <- 1:(nrow(merges)+1) } z <- list() if (!use.modularity || is.null(object$modularity)) { object$height <- 1:nrow(merges) } else { object$height <- object$modularity[-1] object$height <- cumsum(object$height - min(object$height)) } nMerge <- length(oHgt <- object$height) if (nMerge != nrow(merges)) stop("'merge' and 'height' do not fit!") hMax <- oHgt[nMerge] one <- 1L two <- 2L leafs <- nrow(merges)+1 for (k in 1:nMerge) { x <- merges[k, ]# no sort() anymore! if (any(neg <- x < leafs+1)) h0 <- if (hang < 0) 0 else max(0, oHgt[k] - hang * hMax) if (all(neg)) { # two leaves zk <- as.list(x) attr(zk, "members") <- two attr(zk, "midpoint") <- 0.5 # mean( c(0,1) ) objlabels <- object$names[x] attr(zk[[1]], "label") <- objlabels[1] attr(zk[[2]], "label") <- objlabels[2] attr(zk[[1]], "members") <- attr(zk[[2]], "members") <- one attr(zk[[1]], "height") <- attr(zk[[2]], "height") <- h0 attr(zk[[1]], "leaf") <- attr(zk[[2]], "leaf") <- TRUE } else if (any(neg)) { # one leaf, one node X <- as.character(x) ## Originally had "x <- sort(..) above => leaf always left, x[1]; ## don't want to assume this isL <- x[1] < leafs+1 ## is leaf left? zk <- if(isL) list(x[1], z[[X[2]]]) else list(z[[X[1]]], x[2]) attr(zk, "members") <- attr(z[[X[1 + isL]]], "members") + one attr(zk, "midpoint") <- (.memberDend(zk[[1]]) + attr(z[[X[1 + isL]]], "midpoint"))/2 attr(zk[[2 - isL]], "members") <- one attr(zk[[2 - isL]], "height") <- h0 attr(zk[[2 - isL]], "label") <- object$names[x[2 - isL]] attr(zk[[2 - isL]], "leaf") <- TRUE } else { # two nodes x <- as.character(x) zk <- list(z[[x[1]]], z[[x[2]]]) attr(zk, "members") <- attr(z[[x[1]]], "members") + attr(z[[x[2]]], "members") attr(zk, "midpoint") <- (attr(z[[x[1]]], "members") + attr(z[[x[1]]], "midpoint") + attr(z[[x[2]]], "midpoint"))/2 } attr(zk, "height") <- oHgt[k] z[[k <- as.character(k+leafs)]] <- zk } z <- z[[k]] class(z) <- "dendrogram" z } as.hclust.communities <- function(x, hang=-1, use.modularity=FALSE, ...) { as.hclust(as.dendrogram(x, hang=hang, use.modularity=use.modularity)) } asPhylo <- function(x, ...) UseMethod("asPhylo") asPhylo.communities <- function(x, use.modularity=FALSE, ...) { if (!is.hierarchical(x, full=TRUE)) { stop("Not a fully hierarchical community structure") } require(ape, quietly = TRUE) ## If multiple components, then we merge them in arbitrary order merges <- complete.dend(x, use.modularity) if (!use.modularity || is.null(x$modularity)) { height <- 1:nrow(merges) } else { height <- x$modularity[-1] height <- cumsum(height - min(height)) } if (is.null(x$names)) { labels <- 1:(nrow(merges)+1) } else { labels <- x$names } N <- nrow(merges) edge <- matrix(0L, 2*N, 2) edge.length <- numeric(2*N) node <- integer(N) node[N] <- N + 2L cur.nod <- N + 3L j <- 1L for (i in N:1) { edge[j:(j+1), 1] <- node[i] for (l in 1:2) { k <- j + l -1L y <- merges[i, l] if (y > N+1) { edge[k, 2] <- node[y-N-1] <- cur.nod cur.nod <- cur.nod + 1L edge.length[k] <- height[i] - height[y-N-1] } else { edge[k, 2] <- y edge.length[k] <- height[i] } } j <- j + 2L } obj <- list(edge=edge, edge.length=edge.length/2, tip.label=labels, Nnode=N) class(obj) <- "phylo" reorder(obj) } cutat <- function(communities, no, steps) { if (!inherits(communities, "communities")) { stop("Not a community structure") } if (!is.hierarchical(communities, full=TRUE)) { stop("Not a fully hierarchical communitity structure") } if ((!missing(no) && !missing(steps)) || ( missing(no) && missing(steps))) { stop("Please give either `no' or `steps' (but not both)") } if (!missing(steps)) { mm <- merges(communities) if (steps > nrow(mm)) { warning("Cannot make that many steps") steps <- nrow(mm) } community.to.membership2(mm, communities$vcount, steps) } else { mm <- merges(communities) noc <- communities$vcount - nrow(mm) # final number of communities if (no 0) { rect.hclust(hc, k=rect, border=colbar) } invisible(ret) } dendPlotDendrogram <- function(communities, hang=-1, ..., use.modularity=FALSE) { plot(as.dendrogram(communities, hang=hang, use.modularity=use.modularity), ...) } dendPlotPhylo <- function(communities, colbar=rainbow(length(communities)), col=colbar[membership(communities)], mark.groups=communities(communities), use.modularity=FALSE, edge.color="#AAAAAAFF", edge.lty=c(1,2), ...) { phy <- asPhylo(communities, use.modularity=use.modularity) getedges <- function(tip) { repeat { ee <- which(! phy$edge[,1] %in% tip & phy$edge[,2] %in% tip) if (length(ee)<=1) { break } tip <- c(tip, unique(phy$edge[ee,1])) } ed <- which(phy$edge[,1] %in% tip & phy$edge[,2] %in% tip) eds <- phy$edge[ed, 1] good <- which(phy$edge[ed,1] %in% which(tabulate(eds) != 1)) ed[good] } gredges <- lapply(mark.groups, getedges) if (length(mark.groups) > 0) { ecol <- rep(edge.color, nrow(phy$edge)) for (gr in seq_along(gredges)) { ecol[gredges[[gr]]] <- colbar[gr] } } else { ecol <- edge.color } elty <- rep(edge.lty[2], nrow(phy$edge)) elty[ unlist(gredges) ] <- edge.lty[1] plot(phy, edge.color=ecol, edge.lty=elty, tip.color=col, ...) } compare <- function(comm1, comm2, method=c("vi", "nmi", "split.join", "rand", "adjusted.rand")) UseMethod("compare") compare.communities <- function(comm1, comm2, method=c("vi", "nmi", "split.join", "rand", "adjusted.rand")) { compare.numeric(comm1, comm2, method) } compare.numeric <- function(comm1, comm2, method=c("vi", "nmi", "split.join", "rand", "adjusted.rand")) { comm1 <- if (inherits(comm1, "communities")) { membership(comm1) } else { as.numeric(comm1) } comm2 <- if (inherits(comm2, "communities")) { membership(comm2) } else { as.numeric(comm2) } method <- switch(igraph.match.arg(method), vi = 0, nmi = 1, split.join = 2, rand = 3, adjusted.rand = 4) on.exit(.Call("R_igraph_finalizer", PACKAGE = "igraph")) res <- .Call("R_igraph_compare_communities", comm1, comm2, method, PACKAGE = "igraph") res } compare.default <- function(comm1, comm2, method=c("vi", "nmi", "split.join", "rand", "adjusted.rand")) { compare.numeric(as.numeric(comm1), as.numeric(comm2), method) } igraph/R/demo.R0000644000176000001440000001214212240234657013031 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### igraphdemo <- function(which) { require(igraph) require(tcltk) if (missing(which)) { demodir <- system.file("demo", package="igraph") if (demodir=="") { stop("Could not find igraph demos, broken igraph installation?") } return( sub("\\.R$", "", list.files(demodir)) ) } if (!grepl("\\.R$", which)) { which <- paste(which, sep=".", "R") } if (!file.exists(which) && ! grepl("^/", which)) { which <- system.file( paste("demo", sep="/", which), package="igraph" ) } if (which=="" || !file.exists(which)) { stop("Could not find demo file") } .igraphdemo.next <- function(top, txt) { act <- as.character(tktag.nextrange(txt, "active", "0.0")) if (length(act)==0) { return() } options(keep.source=TRUE) text <- tclvalue(tkget(txt, act[1], act[2])) cat("=======================================================\n"); expr <- parse(text=text) for (i in seq_along(expr)) { co <- as.character(attributes(expr)$srcref[[i]]) co[1] <- paste("> ", sep="", co[1]) if (length(co)>1) { co[-1] <- paste(" +", sep="", co[-1]) } cat(co, sep="\n") res <- withVisible(eval(expr[[i]], envir=.GlobalEnv)) if (res$visible) { print(res$value) } } cat("> -------------------------------------------------------\n"); cat(options()$prompt) tktag.remove(txt, "activechunk", act[1], act[2]) tktag.remove(txt, "active", act[1], act[2]) nex <- as.character(tktag.nextrange(txt, "activechunk", act[1])) if (length(nex)!=0) { tktag.add(txt, "active", nex[1], nex[2]) tksee(txt, paste(sep="", as.numeric(nex[2]), ".0")) tksee(txt, paste(sep="", as.numeric(nex[1]), ".0")) } } .igraphdemo.close <- function(top) { tkdestroy(top) } .igraphdemo.reset <- function(top, txt, which) { demolines <- readLines(which) demolines <- demolines[!grepl("^pause\\(\\)$", demolines)] demolines <- paste(" ", sep="", demolines) ch <- grep("^[ ]*###", demolines) ch <- c(ch, length(demolines)+1) if (length(ch)==1) { warning("Demo source file does not contain chunks") } else { demolines <- demolines[ch[1]:length(demolines)] ch <- grep("^[ ]*###", demolines) ch <- c(ch, length(demolines)+1) } tkconfigure(txt, state="normal") tkdelete(txt, "0.0", "end") tkinsert(txt, "insert", paste(demolines, collapse="\n")) tkconfigure(txt, state="disabled") for (i in seq_along(ch[-1])) { from <- paste(sep="", ch[i], ".0") to <- paste(sep="", ch[i+1]-1, ".0") tktag.add(txt, "chunk", from, to) tktag.add(txt, "activechunk", from, to) } tktag.configure(txt, "chunk", "-borderwidth", "1") tktag.configure(txt, "chunk", "-relief", "sunken") if (length(ch) >= 2) { tktag.add(txt, "active", paste(sep="", ch[1], ".0"), paste(sep="", ch[2]-1, ".0")) tktag.configure(txt, "active", "-foreground", "red") tktag.configure(txt, "active", "-background", "lightgrey") } comm <- grep("^#", demolines) for (i in comm) { tktag.add(txt, "comment", paste(sep="", i, ".0"), paste(sep="", i, ".end")) } tktag.configure(txt, "comment", "-font", "bold") tktag.configure(txt, "comment", "-foreground", "darkolivegreen") } top <- tktoplevel(background="lightgrey") tktitle(top) <- paste("igraph demo:", which) main.menu <- tkmenu(top) tkadd(main.menu, "command", label="Close", command=function() .igraphdemo.close(top)) tkadd(main.menu, "command", label="Reset", command=function() .igraphdemo.reset(top, txt, which)) tkconfigure(top, "-menu", main.menu) scr <- tkscrollbar(top, repeatinterval=5, command=function(...) tkyview(txt,...)) txt <- tktext(top, yscrollcommand=function(...) tkset(scr, ...), width=80, height=40) but <- tkbutton(top, text="Next", command=function() .igraphdemo.next(top, txt)) tkpack(but, side="bottom", fill="x", expand=0) tkpack(scr, side="right", fill="y", expand=0) tkpack(txt, side="left", fill="both", expand=1) .igraphdemo.reset(top, txt, which) invisible() } igraph/R/sparsedf.R0000644000176000001440000000600012240234657013710 0ustar ripleyusers # IGraph R package # Copyright (C) 2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### # This is a sparse data frame. It is like a regular data frame, # but it allows for some columns to be constant, and then it # stores that column more economically. sdf <- function(..., row.names = NULL, NROW = NULL) { cols <- list(...) if (is.null(names(cols)) || any(names(cols) == "") || any(duplicated(names(cols)))) { stop("Columns must be have (unique) names") } lens <- sapply(cols, length) n1lens <- lens[ lens != 1 ] if (length(unique(n1lens)) > 1) { stop("Columns must be constants or have the same length") } if (length(n1lens) == 0) { if (is.null(NROW)) { stop("Cannot determine number of rows") } attr(cols, "NROW") <- NROW } else { if (!is.null(NROW) && n1lens[1] != NROW) { stop("NROW does not match column lengths") } attr(cols, "NROW") <- unname(n1lens[1]) } class(cols) <- "igraphSDF" attr(cols, "row.names") <- row.names cols } as.data.frame.igraphSDF <- function(x, row.names, optional, ...) { as.data.frame(lapply(x, rep, length.out=attr(x, "NROW"))) } `[.igraphSDF` <- function(x, i, j, ..., drop=TRUE) { if (!is.character(j)) { stop("The column index must be character") } if (!missing(i) && !is.numeric(i)) { stop("The row index must be numeric") } if (missing(i)) { rep(x[[j]], length.out=attr(x, "NROW")) } else { if (length(x[[j]])==1) { rep(x[[j]], length(i)) } else { x[[j]][i] } } } `[<-.igraphSDF` <- function(x, i, j, value) { if (!is.character(j)) { stop("The column index must be character") } if (!missing(i) && !is.numeric(i)) { stop("Row index must be numeric, if given") } if (missing(i)) { if (length(value) != attr(x, "NROW") && length(value) != 1) { stop("Replacement value has the wrong length") } x[[j]] <- value } else { if (length(value) != length(i) && length(value) != 1) { stop("Replacement value has the wrong length") } tmp <- rep(x[[j]], length=attr(x, "NROW")) tmp[i] <- value if (length(unique(tmp)) == 1) { tmp <- tmp[1] } x[[j]] <- tmp } x } igraph/R/package.R0000644000176000001440000000302312325263644013500 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### .onAttach <- function(library, pkg) { ## we can't do this in .onLoad unlockBinding(".igraph.pars", asNamespace("igraph")) unlockBinding(".igraph.pb", asNamespace("igraph")) invisible() } .onLoad <- function(libname, pkgname) { library.dynam("igraph", pkgname, libname, local=FALSE); .Call("R_igraph_init", FALSE, FALSE, PACKAGE="igraph") } .onUnload <- function(libpath) { library.dynam.unload("igraph", libpath) } .Call <- function(.NAME, ...) { if (.NAME != "R_igraph_finalizer") { base::.Call("R_igraph_check_finally_stack", PACKAGE="igraph") } base::.Call(.NAME, ...) } igraph/R/interface.R0000644000176000001440000001262712240234657014055 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Structure building ################################################################### add.edges <- function(graph, edges, ..., attr=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } attrs <- list(...) attrs <- append(attrs, attr) nam <- names(attrs) if (length(attrs) != 0 && (is.null(nam) || any(nam==""))) { stop("please supply names for attributes") } edges.orig <- ecount(graph) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) graph <- .Call("R_igraph_add_edges", graph, as.igraph.vs(graph, edges)-1, PACKAGE="igraph") edges.new <- ecount(graph) if (edges.new-edges.orig != 0) { idx <- seq(edges.orig+1, edges.new) } else { idx <- numeric() } eattrs <- .Call("R_igraph_mybracket2", graph, 9L, 4L, PACKAGE="igraph") for (i in seq(attrs)) { eattrs[[nam[i]]][idx] <- attrs[[nam[i]]] } .Call("R_igraph_mybracket2_set", graph, 9L, 4L, eattrs, PACKAGE="igraph") } add.vertices <- function(graph, nv, ..., attr=list()) { if (!is.igraph(graph)) { stop("Not a graph object") } attrs <- list(...) attrs <- append(attrs, attr) nam <- names(attrs) if (length(attrs) != 0 && (is.null(nam) || any(nam==""))) { stop("please supply names for attributes") } vertices.orig <- vcount(graph) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) graph <- .Call("R_igraph_add_vertices", graph, as.numeric(nv), PACKAGE="igraph") vertices.new <- vcount(graph) if (vertices.new-vertices.orig != 0) { idx <- seq(vertices.orig+1, vertices.new) } else { idx <- numeric() } vattrs <- .Call("R_igraph_mybracket2", graph, 9L, 3L, PACKAGE="igraph") for (i in seq(attrs)) { vattrs[[nam[i]]][idx] <- attrs[[nam[i]]] } .Call("R_igraph_mybracket2_set", graph, 9L, 3L, vattrs, PACKAGE="igraph") } delete.edges <- function(graph, edges) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_delete_edges", graph, as.igraph.es(graph, edges)-1, PACKAGE="igraph") } delete.vertices <- function(graph, v) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_delete_vertices", graph, as.igraph.vs(graph, v)-1, PACKAGE="igraph") } ################################################################### # Structure query ################################################################### ecount <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_ecount", graph, PACKAGE="igraph") } neighbors <- function(graph, v, mode=1) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.character(mode)) { mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_neighbors", graph, as.igraph.vs(graph, v)-1, as.numeric(mode), PACKAGE="igraph") res+1 } incident <- function(graph, v, mode=c("all", "out", "in", "total")) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.directed(graph)) { mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3) } else { mode=1 } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_incident", graph, as.igraph.vs(graph, v)-1, as.numeric(mode), PACKAGE="igraph") res+1 } is.directed <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_is_directed", graph, PACKAGE="igraph") } get.edges <- function(graph, es) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_edges", graph, as.igraph.es(graph, es)-1, PACKAGE="igraph") matrix(res, ncol=2, byrow=TRUE)+1 } get.edge.ids <- function(graph, vp, directed=TRUE, error=FALSE, multi=FALSE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_get_eids", graph, as.igraph.vs(graph, vp)-1, as.logical(directed), as.logical(error), as.logical(multi), PACKAGE="igraph")+1 } igraph/R/console.R0000644000176000001440000001651012271617526013556 0ustar ripleyusers # IGraph R package # Copyright (C) 2010-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### .igraph.pb <- NULL .igraph.progress <- function(percent, message, clean=FALSE) { if (clean) { if (!is.null(.igraph.pb)) { close(.igraph.pb) } return(invisible()) } type <- getIgraphOpt("verbose") if (is.logical(type) && type) { .igraph.progress.txt(percent, message) } else { switch (type, "tk"=.igraph.progress.tk(percent, message), "tkconsole"=.igraph.progress.tkconsole(percent, message), stop("Cannot interpret 'verbose' option, this should not happen")) } } .igraph.status <- function(message) { type <- getIgraphOpt("verbose") if (is.logical(type) && type) { message(message, appendLF=FALSE) } else { switch(type, "tk"=message(message, appendLF=FALSE), "tkconsole"=.igraph.progress.tkconsole.message(message, start=TRUE), stop("Cannot interpret 'verbose' option, this should not happen")) } 0L } .igraph.progress.txt <- function(percent, message) { pb <- get(".igraph.pb", asNamespace("igraph")) if (percent==0) { if (!is.null(pb)) { close(pb) } cat(sep="", " ", message, "\n") pb <- txtProgressBar(min=0, max=100, style=3) } setTxtProgressBar(pb, percent) if (percent==100) { close(pb); pb <- NULL } assign(".igraph.pb", pb, envir=asNamespace("igraph")) 0L } .igraph.progress.tk <- function(percent, message) { pb <- get(".igraph.pb", asNamespace("igraph")) if (percent==0) { if (!is.null(pb)) { close(pb) } pb <- tkProgressBar(min=0, max=100, title=message, label="0 %") } setTkProgressBar(pb, percent, label=paste(percent, "%")) if (percent==100) { close(pb); pb <- NULL } assign(".igraph.pb", pb, envir=asNamespace("igraph")) 0L } .igraph.progress.tkconsole <- function(percent, message) { pb <- get(".igraph.pb", asNamespace("igraph")) startmess <- FALSE ## Open the console, if it is not open if (is.null(pb)) { startmess <- TRUE pb <- .igraph.progress.tkconsole.create(NA) } ## Update progress bar pb$pb$set(pb$pb$widget, percent) tkconfigure(pb$pb$label, text=substr(message, 1, 20)) tcl("update", "idletasks") ## Done assign(".igraph.pb", pb, envir=asNamespace("igraph")) if (startmess) .igraph.progress.tkconsole.message("Console started.\n") 0L } .igraph.progress.tkconsole.create <- function(oldverb) { console <- tktoplevel() tktitle(console) <- "igraph console" fn <- tkfont.create(family="courier", size=8) lfr <- tkframe(console) image <- tkimage.create("photo", "img", format="gif", file=system.file("igraph2.gif", package="igraph")) logo <- tklabel(lfr, relief="flat", padx=10, pady=10, image=image) scr <- tkscrollbar(console, repeatinterval=5, command=function(...) tkyview(txt, ...)) txt <- tktext(console, yscrollcommand=function(...) tkset(scr, ...), width=60, height=7, font=fn) tkconfigure(txt, state="disabled") pbar <- .igraph.progress.tkconsole.pbar(console) bclear <- tkbutton(lfr, text="Clear", command=function() { tkconfigure(txt, state="normal") tkdelete(txt, "0.0", "end") tkconfigure(txt, state="disabled") }) bstop <- tkbutton(lfr, text="Stop", command=function() {}) bclose <- tkbutton(lfr, text="Close", command=function() { if (!is.na(oldverb) && getIgraphOpt("verbose") == "tkconsole") { igraph.options(verbose=oldverb) } tkdestroy(console) }) tkpack(logo, side="top", fill="none", expand=0, anchor="n", ipadx=10, ipady=10) tkpack(bclear, side="top", fill="x", expand=0, padx=10) ## tkpack(bstop, side="top", fill="x", expand=0, padx=10) tkpack(bclose, side="top", fill="x", expand=0, padx=10) tkpack(lfr, side="left", fill="none", expand=0, anchor="n") tkpack(pbar$frame, side="bottom", fill="x", expand=0) tkpack(scr, side="right", fill="y", expand=0) tkpack(txt, side="left", fill="both", expand=1) tkbind(console, "", function() { if (!is.na(oldverb) && getIgraphOpt("verbose") == "tkconsole") { igraph.options(verbose=oldverb) } assign(".igraph.pb", NULL, envir=asNamespace("igraph")) }) res <- list(top=console, txt=txt, pb=pbar$pb, oldverb=oldverb) class(res) <- "igraphconsole" res } .igraph.progress.tkconsole.message <- function(message, start=FALSE) { txt <- get(".igraph.pb", asNamespace("igraph"))$txt if (is.null(txt)) { if (start) { pb <- .igraph.progress.tkconsole.create(NA) assign(".igraph.pb", pb, envir=asNamespace("igraph")) txt <- pb$txt } else { return() } } tkconfigure(txt, state="normal") now <- paste(sep="", substr(date(), 5, 19), ": ") s1 <- grepl("^ ", message) if (!s1) { tkinsert(txt, "insert", now) } tkinsert(txt, "insert", message) tksee(txt, "end") tkconfigure(txt, state="disabled") tcl("update", "idletasks") } close.igraphconsole <- function(con, ...) { invisible() } ## Much of this is from tkProgressbar .igraph.progress.tkconsole.pbar <- function(top) { useText <- FALSE have_ttk <- as.character(tcl("info", "tclversion")) >= "8.5" if (!have_ttk && as.character(tclRequire("PBar")) == "FALSE") useText <- TRUE fn <- tkfont.create(family = "helvetica", size = 10) frame <- tkframe(top) if (useText) { .lab <- tklabel(frame, text = " ", font = fn, anchor="w", padx = 20) tkpack(.lab, side = "left", anchor="w", padx=5) fn2 <- tkfont.create(family = "helvetica", size = 12) .vlab <- tklabel(frame, text = "0%", font = fn2, padx = 20) tkpack(.vlab, side = "right") } else { .lab <- tklabel(frame, text = " ", font = fn, anchor="w", pady = 5) tkpack(.lab, side = "top", anchor="w", padx=5) tkpack(tklabel(frame, text = "", font = fn), side = "bottom") .val <- tclVar() pBar <- if (have_ttk) { ttkprogressbar(frame, length = 300, variable=.val) } else { tkwidget(frame, "ProgressBar", width = 300, variable=.val) } tkpack(pBar, side = "bottom", anchor="w", padx=5) } get <- function(w) { return(tclvalue(.val)); } set <- function(w, val) { tclvalue(.val) <<- val } pb <- list(widget=pBar, get=get, set=set, label=.lab) list(frame=frame, pb=pb) } igraph.console <- function() { oldverb <- getIgraphOpt("verbose") igraph.options(verbose="tkconsole") pb <- .igraph.progress.tkconsole.create(oldverb) assign(".igraph.pb", pb, envir=asNamespace("igraph")) .igraph.progress.tkconsole.message("Console started.\n") invisible() } igraph/R/plot.common.R0000644000176000001440000015376112325261674014372 0ustar ripleyusers # IGraph R package # Copyright (C) 2003-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Common functions for plot and tkplot ################################################################### i.parse.plot.params <- function(graph, params) { ## store the arguments p <- list(vertex=list(), edge=list(), plot=list()) for (n in names(params)) { if (substr(n, 1, 7)=="vertex.") { nn <- substring(n, 8) p[["vertex"]][[nn]] <- params[[n]] } else if (substr(n, 1, 5)=="edge.") { nn <- substring(n, 6) p[["edge"]][[nn]] <- params[[n]] } else { p[["plot"]][[n]] <- params[[n]] } } ## check that names are present mis <- ! names(p[["vertex"]]) %in% names(i.default.values$vertex) & ! paste("vertex.", sep="", names(p[["vertex"]])) %in% names(igraph.options()) if (any(mis)) { stop("Unknown vertex parameters: ", paste(sep=", ", collapse=", ", names(p[["vertex"]])[mis])) } mis <- ! names(p[["edge"]]) %in% names(i.default.values$edge) & ! paste("edge.", sep="", names(p[["edge"]])) %in% names(igraph.options()) if (any(mis)) { stop("Unknown edge parameters: ", paste(sep=", ", collapse=", ", names(p[["edge"]])[mis])) } mis <- ! names(p[["plot"]]) %in% names(i.default.values$plot) & ! paste("plot.", sep="", names(p[["plot"]])) %in% names(igraph.options()) if (any(mis)) { stop("Unknown plot parameters: ", paste(sep=", ", collapse=", ", names(p[["plot"]]) [ mis ])) } func <- function(type, name, range=NULL, dontcall=FALSE) { if (! type %in% names(p)) { stop("Invalid plot option type") } ret <- function() { v <- p[[type]][[name]] if (is.function(v) && !dontcall) { v <- v(graph) } if (is.null(range)) { return (v) } else { if (length(v)==1) { return(rep(v, length(range))) } else { return (rep(v, length=max(range)+1)[[range+1]]) } } } if (name %in% names(p[[type]])) { ## we already have the parameter return(ret()) } else { ## we don't have the parameter, check attributes first if (type=="vertex" && name %in% list.vertex.attributes(graph)) { p[[type]][[name]] <- get.vertex.attribute(graph, name) return(ret()) } else if (type=="edge" && name %in% list.edge.attributes(graph)) { p[[type]][[name]] <- get.edge.attribute(graph, name) return(ret()) } else if (type=="plot" && name %in% list.graph.attributes(graph)) { p[[type]][[name]] <- get.graph.attribute(graph, name) return(ret()) } else { ## no attributes either, check igraph parameters n <- paste(sep="", type, ".", name) v <- getIgraphOpt(n) if (!is.null(v)) { p[[type]][[name]] <- v return(ret()) } ## no igraph parameter either, use default value p[[type]][[name]] <- i.default.values[[type]][[name]] return(ret()) } } } return (func) } i.get.edge.labels <- function(graph, edge.labels=NULL) { if (is.null(edge.labels)) { edge.labels <- rep(NA, ecount(graph)) } edge.labels } i.get.labels <- function(graph, labels=NULL) { if (is.null(labels)) { if ("name" %in% list.vertex.attributes(graph)) { labels <- get.vertex.attribute(graph, "name") } else { labels <- seq_len(vcount(graph)) } } labels } i.get.arrow.mode <- function(graph, arrow.mode=NULL) { if (is.character(arrow.mode) && length(arrow.mode)==1 && substr(arrow.mode, 1, 2)=="a:") { arrow.mode <- get.vertex.attribute(graph, substring(arrow.mode,3)) } if (is.character(arrow.mode)) { tmp <- numeric(length(arrow.mode)) tmp[ arrow.mode %in% c("<", "<-") ] <- 1 tmp[ arrow.mode %in% c(">", "->") ] <- 2 tmp[ arrow.mode %in% c("<>", "<->") ] <- 3 arrow.mode <- tmp } if (is.null(arrow.mode)) { if (is.directed(graph)) { arrow.mode <- 2 } else { arrow.mode <- 0 } } arrow.mode } i.get.main <- function(graph) { if (getIgraphOpt("annotate.plot")) { n <- graph$name[1] n } else { "" } } i.get.xlab <- function(graph) { if (getIgraphOpt("annotate.plot")) { paste(vcount(graph), "vertices,", ecount(graph), "edges") } else { "" } } igraph.check.shapes <- function(x) { xx <- unique(x) bad.shapes <- ! xx %in% ls(.igraph.shapes) if (any(bad.shapes)) { bs <- paste(xx[bad.shapes], collapse=", ") stop("Bad vertex shape(s): ", bs, ".") } x } autocurve.edges <- function(graph, start=0.5) { cm <- count.multiple(graph) el <- apply(get.edgelist(graph, names=FALSE), 1, paste, collapse=":") ord <- order(el) res <- numeric(length(ord)) p <- 1 while (p <= length(res)) { m <- cm[ord[p]] idx <- p:(p+m-1) if (m==1) { r <- 0 } else { r <- seq(-start, start, length=m) } res[ord[idx]] <- r p <- p + m } res } .igraph.logo.raster <- structure(c(16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 311332508L, 1217499541L, 1804702102L, 1066570390L, 211129749L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 379033495L, 1334940052L, -2104389227L, -1450012011L, -2087546218L, 1368494484L, 412456341L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 41975936L, 1905496981L, -141388906L, -7171435L, -7171435L, -7171435L, -325938283L, 1452380564L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 41975936L, 1905496981L, -158166379L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -141389163L, 1972540052L, 41975936L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -2037148780L, -7171435L, -24798561L, -12009013L, -13250855L, -11616826L, -24340838L, -7171435L, 1586664085L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 311332508L, -963472747L, -7171435L, -7171435L, -7171435L, -7171435L, -7236971L, -7171435L, -7171435L, -7171435L, -7171435L, -946695531L, 361927314L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 798134930L, -40791403L, -25321308L, -16061704L, -16715521L, -16715521L, -16715521L, -15408144L, -24471653L, -258829418L, 344755353L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1483500650L, -7171435L, -7171435L, -7824996L, -12858668L, -15212050L, -16519427L, -15212050L, -12858668L, -7890531L, -7171435L, -7171435L, -1382903147L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 2056426132L, -7171435L, -13643043L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -12139572L, -7171435L, 1385337493L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1452380564L, -7171435L, -7171435L, -8936279L, -15800587L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -15865867L, -9132373L, -7171435L, -7171435L, 1485934996L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1433234795L, -7171435L, -15603981L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -14100510L, -7171435L, -2104389227L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -812412011L, -7171435L, -7432808L, -15080979L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -15277585L, -7498344L, -7171435L, -694971499L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1919774060L, -7171435L, -14623768L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -13120041L, -7171435L, 1704104597L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 177838489L, -74280299L, -7171435L, -10439750L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -10701380L, -7171435L, -40725867L, 211129749L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1368494484L, -7171435L, -10374471L, -16715521L, -16715521L, -16715521L, -16715521L, -16584963L, -9067350L, -7171435L, 714248856L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 999527315L, -7171435L, -7171435L, -12270386L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -12531503L, -7171435L, -7171435L, 1033015958L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 33554431L, -1080913258L, -7171435L, -10701636L, -15277329L, -16519427L, -14885141L, -9720911L, -7171435L, -1718381676L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1217499541L, -7171435L, -7171435L, -12793389L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -13054505L, -7171435L, -7171435L, 1251053972L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 479367826L, -929918315L, -7171435L, -7171435L, -7236971L, -7171435L, -7171435L, -1366060139L, 227117469L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 361927314L, -7171435L, -7171435L, -10962753L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -11289661L, -7171435L, -7171435L, 412456341L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1536398230L, -7171435L, -778857580L, -1013804395L, -1752067691L, 1334940052L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -544042347L, -7171435L, -8086625L, -16061704L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16126983L, -8217439L, -7171435L, -426601835L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1097690475L, -23948651L, 579833750L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 76714645L, 1452446357L, -1986882923L, -1785556331L, 1720881813L, 361927317L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -2070703211L, -7171435L, -7171435L, -10570822L, -16649985L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16649985L, -10636101L, -7171435L, -7171435L, -2020503147L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 596808338L, -23948651L, -1114467692L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 747803285L, -829255019L, -7171435L, -7171435L, -7171435L, -7171435L, -326004074L, 1418891925L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 127046290L, -728591723L, -7171435L, -7171435L, -9786446L, -15603981L, -16715521L, -16715521L, -16715521L, -15538958L, -9655375L, -7171435L, -7171435L, -661482859L, 144678815L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -2053991786L, -7171435L, 1502778005L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 278041237L, -443444587L, -7171435L, -10963009L, -14492954L, -15015956L, -12335666L, -24340839L, -40725867L, 999461525L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 848598164L, -225275243L, -7171435L, -7171435L, -7171435L, -8347998L, -9720911L, -8348254L, -7171435L, -7171435L, -7171435L, -225275243L, 949129878L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 61516458L, -443379051L, -292384107L, 127046290L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1835887979L, -7171435L, -12008757L, -16715521L, -16715521L, -16715521L, -16715521L, -14492954L, -24013930L, -745368939L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 546279319L, -1114467692L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -1064136043L, 546279319L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1301451413L, -7171435L, -1835822188L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -795700587L, -24340838L, -16519427L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -9917004L, -7171435L, 361927317L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 33554431L, 1469289365L, -1752067691L, -896363883L, -242052459L, -141389163L, -7171435L, -309095531L, 429496729L, 1301451413L, -2104389227L, -1215130987L, -879586667L, -1701670251L, 1704104597L, 798134930L, 75530368L, 16777215L, -1332571499L, -7171435L, 798134930L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -174943595L, -9067350L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -11420476L, -7171435L, 999461525L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1986948715L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -158166379L, -1517120875L, -74280299L, -879586667L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -812477803L, -24340839L, -16519427L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -9851469L, -7171435L, 328372885L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 261724569L, -1248685419L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7566182L, -8355679L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -1869376618L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1902996843L, -7171435L, -11681849L, -16715521L, -16715521L, -16715521L, -16715521L, -14166045L, -7236714L, -208498027L, 882086803L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1150456470L, -493710699L, -7171435L, -7171435L, -7303018L, -10789959L, -13026608L, -14934812L, -16513548L, -16645131L, -15921426L, -14013478L, -11973946L, -8618845L, -7171435L, -7171435L, -23948651L, -1768779114L, 144678815L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 227709589L, -544107883L, -7171435L, -10570822L, -13969951L, -14492954L, -11943478L, -24210280L, -23948651L, -7171435L, -23948651L, -1517186668L, 529831060L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 596808338L, -174943595L, -7171435L, -7171435L, -8684636L, -14605855L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16316174L, -11382080L, -7237226L, -7171435L, -7171435L, -1852665195L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 663917205L, -929918315L, -7171435L, -7171435L, -7171435L, -7171435L, -393112938L, 1284674197L, 1049661588L, -879586667L, -7171435L, -141389163L, -1986948715L, 261724569L, 16777215L, 16777215L, 16777215L, 41975936L, -1013804395L, -7171435L, -7171435L, -11184706L, -16316174L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -14342690L, -8158305L, -7171435L, -23948651L, 1066570390L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 59937429L, 1234342549L, 2140312213L, -1936551275L, 1486000789L, 294818453L, 16777215L, 16777215L, 33554431L, 1519621014L, -527265131L, -7171435L, -342715755L, 1821545109L, 93952409L, 16777215L, 1922142614L, -7171435L, -7171435L, -9868880L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -14210851L, -7237227L, -7171435L, -560819563L, 211129749L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 144678815L, 1989383061L, -258829675L, -7171435L, -644705643L, 1804767894L, -141389163L, -7171435L, -7829349L, -15658261L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -11184706L, -7171435L, -7171435L, -1785622123L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 344755353L, -1835822188L, -91057515L, -7171435L, -7171435L, -7171435L, -13289772L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16250383L, -8421470L, -7171435L, -292384107L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 647271572L, -409824619L, -7171435L, -7566183L, -16513548L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -11447872L, -7171435L, -7171435L, 613782933L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -460090475L, -7171435L, -9342293L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -13421357L, -7171435L, -7171435L, 1502778005L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 965907093L, -1785556331L, -879586667L, -158166379L, -695037291L, -1584229739L, 1435669141L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 412456341L, -7171435L, -7171435L, -11184706L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -15263513L, -7171435L, -7171435L, -1903062635L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 143823509L, -1936551275L, -40725867L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -1299017067L, 412258965L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1200853907L, -7171435L, -7171435L, -12895025L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16579339L, -7566183L, -7171435L, -1114467692L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1231908203L, -7171435L, -7171435L, -7171435L, -8282719L, -9655375L, -8544092L, -7236714L, -7171435L, -7171435L, -577596779L, 194155157L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 747737495L, -7171435L, -7171435L, -11908411L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -15987217L, -7171435L, -7171435L, -1483566443L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1720881813L, -7171435L, -7171435L, -8348254L, -14231324L, -16715521L, -16715521L, -16715521L, -15212050L, -9263188L, -7171435L, -7171435L, -1768779115L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 61516458L, -158166379L, -7171435L, -10000462L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -14145060L, -7171435L, -7171435L, -91057515L, -1315794284L, 1603375510L, 295081622L, 16777215L, 16777215L, 16777215L, 16777215L, 127046293L, -242052459L, -7171435L, -7629158L, -15538958L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16519427L, -8740442L, -7171435L, -23948651L, 747803285L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -963472747L, -7171435L, -8158305L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -12237111L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -74280299L, -1164865131L, 1754502038L, 412456341L, 16777215L, 915575445L, -7171435L, -7171435L, -12008757L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -13773857L, -7171435L, -7171435L, 1720881813L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1819110763L, -7171435L, -7171435L, -15263513L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -9868879L, -7171435L, -74280299L, 1368560277L, -1651338603L, -325938539L, -7171435L, -7171435L, -7171435L, -40725867L, -1013804395L, -1382903147L, -7171435L, -7171435L, -14100510L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16061960L, -7171435L, -7171435L, -1668115819L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1402180499L, -7171435L, -7171435L, -9539923L, -16579339L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -13816104L, -7171435L, -7171435L, -946695531L, 16777215L, 16777215L, 61516458L, 1116967831L, -1802333548L, -460090475L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -14558233L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16388613L, -7302250L, -7171435L, -1433234795L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1198353770L, -7171435L, -7171435L, -12500278L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -15987217L, -8092514L, -7171435L, -74280299L, 898666645L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 33554431L, 949129878L, -1970105706L, -443379050L, -7171435L, -7171435L, -12793389L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -14558233L, -7171435L, -7171435L, 1972540053L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 479367826L, -258829675L, -7171435L, -7500391L, -14737438L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16447757L, -10263627L, -7171435L, -7171435L, -2070703211L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 294818453L, -23948651L, -7171435L, -8478812L, -16323334L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -9917005L, -7171435L, -7171435L, 1083347605L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1603375510L, -7171435L, -7171435L, -7434600L, -12237111L, -16513548L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -15000603L, -9013337L, -7171435L, -7171435L, -778923371L, 109084842L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1768779115L, -7171435L, -7171435L, -10178634L, -16061960L, -16715521L, -16715521L, -16715521L, -16388612L, -11224382L, -7171435L, -7171435L, -997027179L, 43160213L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 33554431L, -728591723L, -7171435L, -7171435L, -7171435L, -9276502L, -14605855L, -16513549L, -16645131L, -16645131L, -16645131L, -16645131L, -16645131L, -15789843L, -12171320L, -7368809L, -7171435L, -7171435L, -376270187L, 781226134L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 194155157L, -577596779L, -7171435L, -7171435L, -7890531L, -10636100L, -12335666L, -11028288L, -8413533L, -7171435L, -7171435L, -174943595L, 613585557L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 33554431L, 579833750L, 261724569L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 160337550L, -1416457579L, -7171435L, -7171435L, -124611948L, -7171435L, -7171435L, -7171435L, -7500391L, -9342293L, -11316288L, -12171320L, -10263627L, -8355679L, -7171435L, -7171435L, -7171435L, -7171435L, -1416457579L, 344755353L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 647139989L, -913141099L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -476933483L, 1150456469L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1184010901L, -1131244907L, -275606891L, -7171435L, -23948651L, -644705643L, -1768779114L, 311332508L, 16777215L, 16777215L, 16777215L, 16777215L, 379033495L, -929852523L, -7171435L, -23948651L, 2056426132L, 428838809L, -1282305642L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -325938539L, 1485934996L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 59937429L, 2039648917L, -711814507L, -40725867L, -7171435L, -7171435L, -510487915L, -1752001899L, 261264021L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 211129749L, -1701670251L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -426601835L, 1234408342L, 16777215L, 16777215L, 697274261L, -544042347L, -7171435L, -124611947L, 1485934996L, 16777215L, 16777215L, 16777215L, 1167365268L, -2137943659L, -1248619627L, -376270187L, -7171435L, -7171435L, -91057515L, -846032235L, -1752067691L, 1653772948L, 395350160L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 227709589L, 949129877L, 378704533L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1550741099L, -7171435L, -7171435L, -8021089L, -11616570L, -13446949L, -12662830L, -10178634L, -7171435L, -7171435L, -91057515L, 831689367L, 1133613460L, -275606891L, -7171435L, -342715755L, 999527315L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 529831060L, 865178006L, 144678815L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1368494484L, -7171435L, -7171435L, -9851725L, -15996425L, -16715521L, -16715521L, -16715521L, -16715521L, -13904672L, -7563622L, -7171435L, -476933483L, -91057514L, -7171435L, -644705643L, 613782933L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -846032235L, -7171435L, -8217439L, -16061704L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -12727853L, -7171435L, -7171435L, -7171435L, -1030581611L, 311332508L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 109084842L, -91057515L, -7171435L, -12139828L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16649985L, -7890531L, -7171435L, -695037291L, 109084842L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 831689367L, -7171435L, -7171435L, -13970208L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -9720911L, -7171435L, -1080913258L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 596808338L, -7171435L, -7171435L, -13512485L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -9197652L, -7171435L, -1299017067L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 33554431L, -258829675L, -7171435L, -11355453L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16192519L, -7498343L, -7171435L, 2089980564L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, -1265462635L, -7171435L, -7367273L, -14950677L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -16715521L, -10897730L, -7171435L, -7171435L, 1049661588L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 630296984L, -174943595L, -7171435L, -8086625L, -14100766L, -16715521L, -16715521L, -16715521L, -16323077L, -11028288L, -7171435L, -7171435L, -1550741099L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1821545109L, -7171435L, -7171435L, -7236971L, -8740186L, -10439750L, -9655375L, -7825252L, -7171435L, -7171435L, -476933483L, 277843855L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 1385337493L, -376270187L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -7171435L, -1332571499L, 395350160L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 344755353L, 1922142614L, -1533898091L, -728591723L, -1080913258L, -1903062635L, 1284805780L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L, 16777215L), .Dim = c(64L, 64L), class = "nativeRaster", channels = 4L) i.vertex.default <- list(color="SkyBlue2", size=15, size2=15, label=i.get.labels, label.degree=-pi/4, label.color="darkblue", label.dist=0, label.family="serif", label.font=1, label.cex=1, frame.color="black", shape="circle", pie=1, pie.color=list(c("white", "lightblue", "mistyrose", "lightcyan", "lavender", "cornsilk")), pie.border=list(c("white", "lightblue","mistyrose", "lightcyan", "lavender", "cornsilk")), pie.angle=45, pie.density=-1, pie.lty=1, raster=.igraph.logo.raster) i.edge.default <- list(color="darkgrey", label=i.get.edge.labels, lty=1, width=1, loop.angle=0, loop.angle2=0, label.family="serif", label.font=1, label.cex=1, label.color="darkblue", label.x=NULL, label.y=NULL, arrow.size=1, arrow.mode=i.get.arrow.mode, curved=autocurve.edges, arrow.width=1) i.plot.default <- list(layout=layout.auto, margin=c(0,0,0,0), rescale=TRUE, asp=1, frame=FALSE, main=i.get.main, sub="", xlab=i.get.xlab, ylab="") i.default.values <- new.env() i.default.values[["vertex"]] <- i.vertex.default i.default.values[["edge"]] <- i.edge.default i.default.values[["plot"]] <- i.plot.default igraph/R/bipartite.R0000644000176000001440000000517612325262301014070 0ustar ripleyusers # IGraph R package # Copyright (C) 2009-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### bipartite.projection <- function(graph, types=NULL, multiplicity=TRUE, probe1=NULL, which=c("both", "true", "false"), remove.type=TRUE) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(types) && "type" %in% list.vertex.attributes(graph)) { types <- V(graph)$type } if (!is.null(types)) { if (!is.logical(types)) { warning("vertex types converted to logical") } types <- as.logical(types) if (any(is.na(types))) { stop("`NA' is not allowed in vertex types") } } else { stop("Not a bipartite graph, supply `types' argument") } if (!is.null(probe1)) { probe1 <- as.igraph.vs(graph, probe1)-1 } else { probe1 <- -1 } which <- switch(igraph.match.arg(which), "both"=0L, "false"=1L, "true"=2L) if (which != "both" && probe1 != -1) { warning("`probe1' ignored if only one projection is requested") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_bipartite_projection", graph, types, as.integer(probe1), which, PACKAGE="igraph") if (remove.type) { if (is.igraph(res[[1]])) { res[[1]] <- remove.vertex.attribute(res[[1]], "type") } if (is.igraph(res[[2]])) { res[[2]] <- remove.vertex.attribute(res[[2]], "type") } } if (which == 0L) { if (multiplicity) { E(res[[1]])$weight <- res[[3]] E(res[[2]])$weight <- res[[4]] } res[1:2] } else if (which == 1L) { if (multiplicity) { E(res[[1]])$weight <- res[[3]] } res[[1]] } else { if (multiplicity) { E(res[[2]])$weight <- res[[4]] } res[[2]] } } igraph/R/motifs.R0000644000176000001440000000511712240234657013412 0ustar ripleyusers # IGraph R package # Copyright (C) 2006-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### graph.motifs <- function(graph, size=3, cut.prob=rep(0, size)) { if (!is.igraph(graph)) { stop("Not a graph object") } cut.prob <- as.numeric(cut.prob) if (length(cut.prob) != size) { cut.prob <- c(cut.prob[-length(cut.prob)], rep(cut.prob[-length(cut.prob)], length(cut.prob)-1)) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_motifs_randesu", graph, as.integer(size), as.numeric(cut.prob), PACKAGE="igraph") res[is.nan(res)] <- NA res } graph.motifs.no <- function(graph, size=3, cut.prob=rep(0, size)) { if (!is.igraph(graph)) { stop("Not a graph object") } cut.prob <- as.numeric(cut.prob) if (length(cut.prob) != size) { cut.prob <- c(cut.prob[-length(cut.prob)], rep(cut.prob[-length(cut.prob)], length(cut.prob)-1)) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_motifs_randesu_no", graph, as.integer(size), as.numeric(cut.prob), PACKAGE="igraph") } graph.motifs.est <- function(graph, size=3, cut.prob=rep(0, size), sample.size=vcount(graph)/10, sample=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } cut.prob <- as.numeric(cut.prob) if (length(cut.prob) != size) { cut.prob <- c(cut.prob[-length(cut.prob)], rep(cut.prob[-length(cut.prob)], length(cut.prob)-1)) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_motifs_randesu_estimate", graph, as.integer(size), as.numeric(cut.prob), as.integer(sample.size), as.numeric(sample), PACKAGE="igraph") } igraph/R/revolver.R0000644000176000001440000004114412240234657013755 0ustar ripleyusers # IGraph R package # Copyright (C) 2007-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### evolver.d <- function(nodes, kernel, outseq=NULL, outdist=NULL, m=1, directed=TRUE) { if (!is.null(outseq)) { outseq <- as.numeric(outseq) } if (!is.null(outdist)) { outdist <- as.numeric(outdist) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_evolver_d", as.numeric(nodes), as.numeric(kernel), outseq, outdist, m, as.logical(directed), PACKAGE="igraph") } revolver.d <- function(graph, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_d", graph, as.numeric(niter), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.d <- function(graph, kernel) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_d", graph, as.numeric(kernel), PACKAGE="igraph") } revolver.ad <- function(graph, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_ad", graph, as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), structure(as.numeric(debug), dim=dim(debug)), PACKAGE="igraph") } revolver.error.ad <- function(graph, kernel) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_ad", graph, kernel, PACKAGE="igraph") } revolver.ade <- function(graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_ade", graph, as.numeric(cats), as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), structure(as.numeric(debug), dim=dim(debug)), PACKAGE="igraph") } revolver.error.ade <- function(graph, kernel, cats) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_ade", graph, kernel, as.numeric(cats), PACKAGE="igraph") } revolver.e <- function(graph, cats, niter=5, st=FALSE, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_e", graph, as.numeric(cats), as.numeric(niter), as.logical(st), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.e <- function(graph, kernel, cats) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_e", graph, as.numeric(kernel), as.numeric(cats), PACKAGE="igraph") } revolver.de <- function(graph, cats, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_de", graph, as.numeric(cats), as.numeric(niter), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.de <- function(graph, kernel, cats) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_de", graph, kernel, as.numeric(cats), PACKAGE="igraph") } revolver.l <- function(graph, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_l", graph, as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.l <- function(graph, kernel) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_l", graph, as.numeric(kernel), PACKAGE="igraph") } revolver.dl <- function(graph, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_dl", graph, as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.dl <- function(graph, kernel) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_dl", graph, kernel, PACKAGE="igraph") } revolver.el <- function(graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_el", graph, as.numeric(cats), as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.el <- function(graph, kernel, cats) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_el", graph, kernel, as.numeric(cats), PACKAGE="igraph") } revolver.r <- function(graph, window, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_r", graph, as.numeric(niter), as.numeric(window), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.r <- function(graph, kernel, window) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_r", graph, as.numeric(kernel), as.numeric(window), PACKAGE="igraph") } revolver.ar <- function(graph, window, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_ar", graph, as.numeric(niter), as.numeric(agebins), as.numeric(window), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), structure(as.numeric(debug), dim=dim(debug)), PACKAGE="igraph") } revolver.error.ar <- function(graph, kernel, window) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_ar", graph, kernel, as.numeric(window), PACKAGE="igraph") } revolver.di <- function(graph, cats, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_di", graph, as.numeric(cats), as.numeric(niter), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.di <- function(graph, kernel, cats) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_di", graph, kernel, as.numeric(cats), PACKAGE="igraph") } revolver.adi <- function(graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_adi", graph, as.numeric(cats), as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), structure(as.numeric(debug), dim=dim(debug)), PACKAGE="igraph") } revolver.error.adi <- function(graph, kernel, cats) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_adi", graph, kernel, as.numeric(cats), PACKAGE="igraph") } revolver.il <- function(graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_il", graph, as.numeric(cats), as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.il <- function(graph, kernel, cats) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_il", graph, kernel, as.numeric(cats), PACKAGE="igraph") } revolver.ir <- function(graph, cats, window, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_ir", graph, as.numeric(cats), as.numeric(window), as.numeric(niter), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), as.numeric(debug), PACKAGE="igraph") } revolver.error.ir <- function(graph, kernel, cats, window) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_ir", graph, kernel, as.numeric(cats), as.numeric(window), PACKAGE="igraph") } revolver.air <- function(graph, cats, window, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) { if (!is.igraph(graph)) { stop("Not a graph object!") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_air", graph, as.numeric(cats), as.numeric(window), as.numeric(niter), as.numeric(agebins), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), structure(as.numeric(debug), dim=dim(debug)), PACKAGE="igraph") } revolver.error.air <- function(graph, kernel, cats, window) { if (!is.igraph(graph)) { stop("Not a graph object!") } kernel <- structure(as.numeric(kernel), dim=dim(kernel)) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_error2_air", graph, kernel, as.numeric(cats), as.numeric(window), PACKAGE="igraph") } revolver.d.d <- function(graph, vtime=V(graph)$time, etime=E(graph)$time, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) { if (!is.igraph(graph)) { stop("Not a graph object!") } if (is.null(vtime)) { stop("vtime missing") } if (is.null(etime)) { stop("etime missing") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_d_d", graph, as.numeric(niter), as.numeric(vtime), as.numeric(etime), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), structure(as.numeric(debug), dim=dim(debug)), PACKAGE="igraph") } revolver.p.p <- function(graph, events=get.graph.attribute(graph, "events"), vtime=V(graph)$time, etime=E(graph)$time, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) { if (!is.igraph(graph)) { stop("Not a graph object!") } if (is.null(events) || !is.list(events)) { stop("events missing or not a list") } if (is.null(vtime)) { ## TODO: calculate from events stop("vtime missing") } if (is.null(etime)) { ## TODO: calculate from events stop("etime missing") } authors <- unlist(events) eventsizes <- sapply(events, length) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_revolver_p_p", graph, as.numeric(niter), as.numeric(vtime), as.numeric(etime), as.numeric(authors), as.numeric(eventsizes), as.logical(sd), as.logical(norm), as.logical(cites), as.logical(expected), as.logical(error), structure(as.numeric(debug), dim=dim(debug)), PACKAGE="igraph") } igraph/R/other.R0000644000176000001440000000453512325356537013243 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### running.mean <- function(v, binwidth) { v <- as.numeric(v) binwidth <- as.numeric(binwidth) if (length(v) < binwidth) { stop("Vector too short for this binwidth.") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_running_mean", v, binwidth, PACKAGE="igraph"); } igraph.sample <- function(low, high, length) { if (length>high-low+1) { stop("length too big for this interval") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_random_sample", as.numeric(low), as.numeric(high), as.numeric(length), PACKAGE="igraph") } igraph.match.arg <- function(arg, choices, several.ok=FALSE) { if (missing(choices)) { formal.args <- formals(sys.function(sys.parent())) choices <- eval(formal.args[[deparse(substitute(arg))]]) } arg <- tolower(arg) choices <- tolower(choices) match.arg(arg=arg, choices=choices, several.ok=several.ok) } igraph.i.spMatrix <- function(M) { if (M$type == "triplet") { Matrix::sparseMatrix(dims=M$dim, i=M$i+1L, j=M$p+1L, x=M$x) } else { new("dgCMatrix", Dim=M$dim, Dimnames=list(NULL, NULL), factors=list(), i=M$i, p=M$p, x=M$x) } } srand <- function(seed) { seed <- as.numeric(seed) if (length(seed) != 1) { stop("Length of `seed' must be 1") } if (seed < 0) { stop("Seed must be non-negative") } res <- .Call("R_igraph_srand", seed, PACKAGE="igraph") invisible(res) } igraph/R/flow.R0000644000176000001440000001137612240234657013064 0ustar ripleyusers # IGraph R package # Copyright (C) 2006-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### graph.mincut <- function(graph, source=NULL, target=NULL, capacity=NULL, value.only=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(capacity)) { if ("capacity" %in% list.edge.attributes(graph)) { capacity <- E(graph)$capacity } } if (is.null(source) && !is.null(target) || is.null(target) && !is.null(source)) { stop("Please give both source and target or neither") } if (!is.null(capacity)) { capacity <- as.numeric(capacity) } value.only <- as.logical(value.only) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) if (is.null(target) && is.null(source)) { if (value.only) { res <- .Call("R_igraph_mincut_value", graph, capacity, PACKAGE="igraph") } else { res <- .Call("R_igraph_mincut", graph, capacity, PACKAGE="igraph") res$cut <- res$cut + 1 res$partition1 <- res$partition1 + 1 res$partition2 <- res$partition2 + 1 res } } else { if (value.only) { res <- .Call("R_igraph_st_mincut_value", graph, as.igraph.vs(graph, source)-1, as.igraph.vs(graph, target)-1, capacity, PACKAGE="igraph") } else { stop("Calculating minimum s-t cuts is not implemented yet") } } res } vertex.connectivity <- function(graph, source=NULL, target=NULL, checks=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(source) && is.null(target)) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_vertex_connectivity", graph, as.logical(checks), PACKAGE="igraph") } else if (!is.null(source) && !is.null(target)) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_st_vertex_connectivity", graph, as.igraph.vs(graph, source)-1, as.igraph.vs(graph, target)-1, PACKAGE="igraph") } else { stop("either give both source and target or neither") } } edge.connectivity <- function(graph, source=NULL, target=NULL, checks=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(source) && is.null(target)) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_edge_connectivity", graph, as.logical(checks), PACKAGE="igraph") } else if (!is.null(source) && !is.null(target)) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_st_edge_connectivity", graph, as.igraph.vs(graph, source)-1, as.igraph.vs(graph, target)-1, PACKAGE="igraph") } else { stop("either give both source and target or neither") } } edge.disjoint.paths <- function(graph, source, target) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_edge_disjoint_paths", graph, as.igraph.vs(graph, source)-1, as.igraph.vs(graph, target)-1, PACKAGE="igraph") } vertex.disjoint.paths <- function(graph, source=NULL, target=NULL) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_vertex_disjoint_paths", graph, as.igraph.vs(graph, source)-1, as.igraph.vs(graph, target)-1, PACKAGE="igraph") } graph.adhesion <- function(graph, checks=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_adhesion", graph, as.logical(checks), PACKAGE="igraph") } graph.cohesion <- function(graph, checks=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) .Call("R_igraph_cohesion", graph, as.logical(checks), PACKAGE="igraph") } igraph/R/iterators.R0000644000176000001440000003341712263024035014121 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Constructors ################################################################### V <- function(graph) { if (!is.igraph(graph)) { stop("Not a graph object") } vc <- vcount(graph) res <- seq_len(vc) class(res) <- "igraph.vs" ne <- new.env() assign("graph", graph, envir=ne) attr(res, "env") <- ne res } E <- function(graph, P=NULL, path=NULL, directed=TRUE) { if (!is.igraph(graph)) { stop("Not a graph object") } if (!is.null(P) && !is.null(path)) { stop("Cannot give both `P' and `path' at the same time") } if (is.null(P) && is.null(path)) { ec <- ecount(graph) res <- seq_len(ec) } else if (!is.null(P)) { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_es_pairs", graph, as.igraph.vs(graph, P)-1, as.logical(directed), PACKAGE="igraph")+1 } else { on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_es_path", graph, as.igraph.vs(graph, path)-1, as.logical(directed), PACKAGE="igraph")+1 } class(res) <- "igraph.es" ne <- new.env() assign("graph", graph, envir=ne) attr(res, "env") <- ne res } "[[.igraph.vs" <- function(x, i) { if (length(i) != 1) { stop("Invalid `[[` indexing, need single vertex") } if (is.numeric(i) || is.integer(i)) { res <- i [ i %in% x ] attributes(res) <- attributes(x) } else if (is.character(i)) { res <- as.igraph.vs(get("graph", attr(x, "env")), i) attributes(res) <- attributes(x) } else { stop("Invalid `[[` indexing, index must be numeric of character scalar") } attr(res, "single") <- TRUE res } "[.igraph.vs" <- function(x, i) { i <- substitute(i) if (is.numeric(i) || is.integer(i)) { # simple indexing by vertex ids res <- i[ i %in% x ] attributes(res) <- attributes(x) } else if (is.logical(i)) { # simple indexing by logical vector res <- as.numeric(x) [ i ] attributes(res) <- attributes(x) } else if (is.character(i)) { res <- as.igraph.vs(get("graph", attr(x, "env")), i) attributes(res) <- attributes(x) } else { # language expression, we also do attribute based indexing graph <- get("graph", attr(x, "env")) nei <- function(v, mode=c("all", "in", "out", "total")) { ## TRUE iff the vertex is a neighbor (any type) ## of at least one vertex in v mode <- igraph.match.arg(mode) mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3) if (is.logical(v)) { v <- which(v) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) tmp <- .Call("R_igraph_vs_nei", graph, x, as.igraph.vs(graph, v)-1, as.numeric(mode), PACKAGE="igraph") tmp[as.numeric(x)] } innei <- function(v, mode=c("in", "all", "out", "total")) { nei(v, mode) } outnei <- function(v, mode=c("out", "all", "in", "total")) { nei(v, mode) } inc <- adj <- function(e) { ## TRUE iff the vertex (in the vs) is incident ## to at least one edge in e if (is.logical(e)) { e <- which(e) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) tmp <- .Call("R_igraph_vs_adj", graph, x, as.igraph.es(graph, e)-1, as.numeric(3), PACKAGE="igraph") tmp[as.numeric(x)] } from <- function(e) { ## TRUE iff the vertex is the source of at least one edge in e if (is.logical(e)) { e <- which(e) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) tmp <- .Call("R_igraph_vs_adj", graph, x, as.igraph.es(graph, e)-1, as.numeric(1), PACKAGE="igraph") tmp[as.numeric(x)] } to <- function(e) { ## TRUE iff the vertex is the target of at least one edge in e if (is.logical(e)) { e <- which(e) } on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) tmp <- .Call("R_igraph_vs_adj", graph, x, as.igraph.es(graph, e)-1, as.numeric(2), PACKAGE="igraph") tmp[as.numeric(x)] } i <- eval(i, envir=c(.Call("R_igraph_mybracket2", graph, 9L, 3L, PACKAGE="igraph"), nei=nei, innei=innei, outnei=outnei, adj=adj, inc=inc, from=from, to=to), enclos=parent.frame()) if (is.numeric(i) || is.integer(i)) { i <- as.numeric(i) res <- i[ i %in% x ] attributes(res) <- attributes(x) } else if (is.logical(i)) { res <- as.numeric(x) [ i ] attributes(res) <- attributes(x) } else if (is.character(i)) { res <- as.igraph.vs(get("graph", attr(x, "env")), i) attributes(res) <- attributes(x) } else { stop("invalid indexing of vertex seq") } } res } "[[.igraph.es" <- function(x, i) { if (length(i) != 1) { stop("Invalid `[[` indexing, need single edge") } if (is.numeric(i) || is.integer(i)) { res <- i [ i %in% x ] attributes(res) <- attributes(x) } else if (is.character(i)) { res <- as.igraph.es(get("graph", attr(x, "env")), i) attributes(res) <- attributes(x) } else { stop("Invalid `[[` indexing, index must be numeric of character scalar") } attr(res, "single") <- TRUE res } "[.igraph.es" <- function(x, i) { i <- substitute(i) if (is.numeric(i) || is.integer(i)) { # simple indexing by vertex ids res <- i[ i %in% x ] attributes(res) <- attributes(x) } else if (is.logical(i)) { # simple indexing by a logical vector res <- as.numeric(x) [ i ] attributes(res) <- attributes(x) } else if (is.character(i)) { res <- as.igraph.es(get("graph", attr(x, "env")), i) attributes(res) <- attributes(x) } else { # language expression, we also do attribute based indexing graph <- get("graph", attr(x, "env")) i <- substitute(i) inc <- adj <- function(v) { ## TRUE iff the edge is incident to at least one vertex in v on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) tmp <- .Call("R_igraph_es_adj", graph, x, as.igraph.vs(graph, v)-1, as.numeric(3), PACKAGE="igraph") tmp[ as.numeric(x) ] } from <- function(v) { ## TRUE iff the edge originates from at least one vertex in v on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) tmp <- .Call("R_igraph_es_adj", graph, x, as.igraph.vs(graph, v)-1, as.numeric(1), PACKAGE="igraph") tmp[ as.numeric(x) ] } to <- function(v) { ## TRUE iff the edge points to at least one vertex in v on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) tmp <- .Call("R_igraph_es_adj", graph, x, as.igraph.vs(graph, v)-1, as.numeric(2), PACKAGE="igraph") tmp[ as.numeric(x) ] } i <- eval(i, envir=c(.Call("R_igraph_mybracket2", graph, 9L, 4L, PACKAGE="igraph"), inc=inc, adj=adj, from=from, to=to, .igraph.from=list(.Call("R_igraph_mybracket", graph, 3L, PACKAGE="igraph")[ as.numeric(x) ]), .igraph.to=list(.Call("R_igraph_mybracket", graph, 4L, PACKAGE="igraph")[as.numeric(x)]), .igraph.graph=list(graph), `%--%`=`%--%`, `%->%`=`%->%`, `%<-%`=`%<-%`), enclos=parent.frame()) if (is.numeric(i) || is.integer(i)) { i <- as.numeric(i) res <- i[ i %in% x ] attributes(res) <- attributes(x) } else if (is.logical(i)) { res <- as.numeric(x) [ i ] attributes(res) <- attributes(x) } else { stop("invalid indexing of edge seq") } } res } "%--%" <- function(f, t) { from <- get(".igraph.from", parent.frame()) to <- get(".igraph.to", parent.frame()) graph <- get(".igraph.graph", parent.frame()) f <- as.igraph.vs(graph, f)-1 t <- as.igraph.vs(graph, t)-1 (from %in% f & to %in% t) | (to %in% f & from %in% t) } "%->%" <- function(f, t) { from <- get(".igraph.from", parent.frame()) to <- get(".igraph.to", parent.frame()) graph <- get(".igraph.graph", parent.frame()) f <- as.igraph.vs(graph, f)-1 t <- as.igraph.vs(graph, t)-1 if (is.directed(graph)) { from %in% f & to %in% t } else { (from %in% f & to %in% t) | (to %in% f & from %in% t) } } "%<-%" <- function(t, value) { from <- get(".igraph.from", parent.frame()) to <- get(".igraph.to", parent.frame()) graph <- get(".igraph.graph", parent.frame()) value <- as.igraph.vs(graph, value)-1 t <- as.igraph.vs(graph, t)-1 if (is.directed(graph)) { from %in% value & to %in% t } else { (from %in% value & to %in% t) | (to %in% value & from %in% t) } } "[<-.igraph.vs" <- "[[<-.igraph.vs" <- function(x, i, value) { if (! "name" %in% names(attributes(value)) || ! "value" %in% names(attributes(value))) { stop("invalid indexing") } value } "[<-.igraph.es" <- "[[<-.igraph.es" <- function(x, i, value) { if (! "name" %in% names(attributes(value)) || ! "value" %in% names(attributes(value))) { stop("invalid indexing") } value } "$.igraph" <- function(x, name) { get.graph.attribute(x, name) } "$<-.igraph" <- function(x, name, value) { set.graph.attribute(x, name, value) } "$.igraph.vs" <- function(x, name) { res <- get.vertex.attribute(get("graph", attr(x, "env")), name, x) if ("single" %in% names(attributes(x)) && attr(x, "single")) { res[[1]] } else { res } } "$.igraph.es" <- function(x, name) { res <- get.edge.attribute(get("graph", attr(x, "env")), name, x) if ("single" %in% names(attributes(x)) && attr(x, "single")) { res[[1]] } else { res } } "$<-.igraph.vs" <- function(x, name, value) { attr(x, "name") <- name attr(x, "value") <- value x } "$<-.igraph.es" <- function(x, name, value) { attr(x, "name") <- name attr(x, "value") <- value x } "V<-" <- function(x, value) { if (!is.igraph(x)) { stop("Not a graph object") } if (! "name" %in% names(attributes(value)) || ! "value" %in% names(attributes(value))) { stop("invalid indexing") } set.vertex.attribute(x, attr(value, "name"), index=value, value=attr(value, "value")) } "E<-" <- function(x, path=NULL, P=NULL, directed=NULL, value) { if (!is.igraph(x)) { stop("Not a graph object") } if (! "name" %in% names(attributes(value)) || ! "value" %in% names(attributes(value))) { stop("invalid indexing") } set.edge.attribute(x, attr(value, "name"), index=value, value=attr(value, "value")) } print.igraph.vs <- function(x, ...) { cat("Vertex sequence:\n") graph <- get("graph", attr(x, "env")) x <- as.numeric(x) if ("name" %in% list.vertex.attributes(graph)) { x <- V(graph)$name[x] } print(x) } print.igraph.es <- function(x, ...) { cat("Edge sequence:\n") graph <- get("graph", attr(x, "env")) if (is.directed(graph)) { arrow <- "->" } else { arrow <- "--" } x <- as.numeric(x) el <- get.edges(graph, x) if ("name" %in% list.vertex.attributes(graph)) { el <- matrix(V(graph)$name[el], ncol=2) } tab <- data.frame(e=paste(sep="", "[", x, "]"), row.names="e") if (is.numeric(el)) { w <- nchar(max(el)) } else { w <- max(nchar(el)) } tab[" "] <- paste(format(el[,1], width=w), arrow, format(el[,2], width=w)) print(tab) } # these are internal as.igraph.vs <- function(graph, v, na.ok=FALSE) { if (is.character(v) && "name" %in% list.vertex.attributes(graph)) { v <- as.numeric(match(v, V(graph)$name)) if (!na.ok && any(is.na(v))) { stop("Invalid vertex names") } v } else { if (is.logical(v)) { res <- as.vector(V(graph))[v] } else if (is.numeric(v) && any(v<0)){ res <- as.vector(V(graph))[v] } else { res <- as.numeric(v) } if (!na.ok && any(is.na(res))) { stop("Invalid vertex name(s)") } res } } as.igraph.es <- function(graph, e) { if (is.character(e)) { Pairs <- grep("|", e, fixed=TRUE) Names <- if (length(Pairs)==0) seq_along(e) else -Pairs res <- numeric(length(e)) ## Based on vertex ids/names if (length(Pairs)!=0) { vv <- strsplit(e[Pairs], "|", fixed=TRUE) vl <- sapply(vv, length) if (any(vl != 2)) { stop("Invalid edge name: ", e[Pairs][vl!=2][1]) } vp <- unlist(vv) if (! "name" %in% list.vertex.attributes(graph)) { vp <- as.numeric(vp) } res[Pairs] <- get.edge.ids(graph, vp) } ## Based on edge ids/names if (length(Names) != 0) { if ("name" %in% list.edge.attributes(graph)) { res[Names] <- as.numeric(match(e[Names], E(graph)$name)) } else { res[Names] <- as.numeric(e[Names]) } } } else { res <- as.numeric(e) } if (any(is.na(res))) { stop("Invalid edge names") } res } igraph/R/cocitation.R0000644000176000001440000000353612240234657014250 0ustar ripleyusers # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### cocitation <- function(graph, v=V(graph)) { if (!is.igraph(graph)) { stop("Not a graph object") } v <- as.igraph.vs(graph, v) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_cocitation", graph, v-1, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { rownames(res) <- get.vertex.attribute(graph, "name", v) colnames(res) <- get.vertex.attribute(graph, "name") } res } bibcoupling <- function(graph, v=V(graph)) { if (!is.igraph(graph)) { stop("Not a graph object") } v <- as.igraph.vs(graph, v) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) res <- .Call("R_igraph_bibcoupling", graph, v-1, PACKAGE="igraph") if (getIgraphOpt("add.vertex.names") && is.named(graph)) { rownames(res) <- get.vertex.attribute(graph, "name", v) colnames(res) <- get.vertex.attribute(graph, "name") } res } igraph/R/glet.R0000644000176000001440000000502712251656216013045 0ustar ripleyusers graphlets.candidate.basis <- function(graph, weights=NULL) { ## Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } ## Drop all attributes, we don't want to deal with them, TODO graph2 <- graph graph2[[9]] <- list(c(1,0,1), list(), list(), list()) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) ## Function call res <- .Call("R_igraph_graphlets_candidate_basis", graph2, weights, PACKAGE="igraph") res } graphlets.project <- function(graph, weights=NULL, cliques, niter=1000, Mu=rep(1, length(cliques))) { # Argument checks if (!is.igraph(graph)) { stop("Not a graph object") } if (is.null(weights) && "weight" %in% list.edge.attributes(graph)) { weights <- E(graph)$weight } if (!is.null(weights) && any(!is.na(weights))) { weights <- as.numeric(weights) } else { weights <- NULL } Mu <- as.numeric(Mu) niter <- as.integer(niter) on.exit( .Call("R_igraph_finalizer", PACKAGE="igraph") ) # Function call res <- .Call("R_igraph_graphlets_project", graph, weights, cliques, Mu, niter, PACKAGE="igraph") res } ################# ## Example code function() { library(igraph) fitandplot <- function(g, gl) { g <- simplify(g) V(g)$color <- "white" E(g)$label <- E(g)$weight E(g)$label.cex <- 2 E(g)$color <- "black" plot.new() layout(matrix(1:6, nrow=2, byrow=TRUE)) co <- layout.kamada.kawai(g) par(mar=c(1,1,1,1)) plot(g, layout=co) for (i in 1:length(gl$Bc)) { sel <- gl$Bc[[i]] V(g)$color <- "white" V(g)[sel]$color <- "#E495A5" E(g)$width <- 1 E(g)[ V(g)[sel] %--% V(g)[sel] ]$width <- 2 E(g)$label <- "" E(g)[ width == 2 ]$label <- round(gl$Muc[i], 2) E(g)$color <- "black" E(g)[ width == 2 ]$color <- "#E495A5" plot(g, layout=co) } } D1 <- matrix(0, 5, 5) D2 <- matrix(0, 5, 5) D3 <- matrix(0, 5, 5) D1[1:3, 1:3] <- 2 D2[3:5, 3:5] <- 3 D3[2:5, 2:5] <- 1 g <- graph.adjacency(D1 + D2 + D3, mode="undirected", weighted=TRUE) gl <- graphlets(g, iter=1000) fitandplot(g, gl) ## Project another graph on the graphlets set.seed(42) g2 <- set.edge.attribute(g, "weight", value=sample(E(g)$weight)) gl2 <- graphlets.project(g2, gl$Bc, 1000) fitandplot(g2, gl2) } igraph/R/nexus.R0000644000176000001440000002757212240234657013264 0ustar ripleyusers # IGraph R package # Copyright (C) 2011-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### makeNexusDatasetInfo <- function(entries) { dsi <- lapply(entries, "[", 2) nam <- sapply(entries, "[", 1) attr <- nam=="attribute" myattr <- unlist(dsi[attr]) dsi <- dsi[!attr] nam <- nam[!attr] names(dsi) <- nam class(dsi) <- "nexusDatasetInfo" if (length(myattr) != 0) { myattr <- strsplit(myattr, "\n", fixed=TRUE) attrdat <- lapply(myattr, function(x) strsplit(x[1], " ")[[1]]) myattr <- sapply(myattr, "[", 2) dsi$attributes <- mapply(attrdat, myattr, SIMPLIFY=FALSE, FUN=function(dat, desc) { list(type=dat[1], datatype=dat[2], name=dat[3], description=desc) }) } dsi$id <- as.numeric(dsi$id) dsi$tags <- strsplit(dsi$tags, ";", fixed=TRUE)[[1]] dsi } print.nexusDatasetInfo <- function(x, ...) { ve <- strsplit(parseVE(x$`vertices/edges`), "/")[[1]] nc <- c("U", "-", "-", "-") if ("directed" %in% x$tags && "undirected" %in% x$tags) { nc[1] <- "B" } else if ("directed" %in% x$tags) { nc[1] <- "D" } if (is.null(x$attributes)) { nc[2] <- "?" } else if (any(sapply(x$attributes, function(X) X$name=="name" && X$type=="vertex"))) { nc[2] <- "N" } if ("weighted" %in% x$tags) { nc[3] <- "W" } if ("bipartite" %in% x$tags) { nc[4] <- "B" } nc <- paste(nc, collapse="") head <- paste(sep="", "NEXUS ", nc, " ", ve[1], " ", ve[2], " #", x$id, " ", x$sid, " -- ", x$name) if (nchar(head) > getOption("width")) { head <- paste(sep="", substr(head, 1, getOption("width")-1), "+") } cat(head, sep="", "\n") if (length(x$tags) != 0) { tt <- strwrap(paste(sep="", "+ tags: ", paste(x$tags, collapse="; ")), initial="", prefix=" ") cat(tt, sep="\n") } if ("networks" %in% names(x)) { nets <- strsplit(x$networks, " ")[[1]] nn <- strwrap(paste(sep="", "+ nets: ", paste(nets, collapse="; ")), initial="", prefix=" ") cat(nn, sep="\n") } attr <- x[["attributes"]] printed <- c("id", "sid", "vertices/edges", "name", "tags", "networks", "attributes") x <- x[ setdiff(names(x), printed) ] if (length(attr)>0) { dcode <- function(d) { if (d=="numeric") return("n") if (d=="string") return("c") "x" } cat("+ attr: ") astr <- sapply(attr, function(a) { paste(sep="", a$name, " (", substr(a$type, 1, 1), "/", dcode(a$datatype), ")") }) cat(strwrap(paste(astr, collapse=", "), exdent=2), "\n") } for (i in names(x)) { xx <- strsplit(x[[i]], "\n")[[1]] ff <- strwrap(paste(sep="", "+ ", i, ": ", xx[1]), initial="", prefix=" ") xx <- unlist(sapply(xx[-1], strwrap, prefix=" ")) cat(ff, sep="\n") if (length(xx)>0) { cat(xx, sep="\n") } } invisible(x) } summary.nexusDatasetInfoList <- function(object, ...) { o <- as.numeric(attr(object, "offset")) s <- as.numeric(attr(object, "size")) t <- as.numeric(attr(object, "totalsize")) n <- attr(object, "name") cat(sep="", "NEXUS ", o+1, "-", o+s, "/", t, " -- ", n, "\n") invisible(object) } parseVE <- function(ve) { if (length(ve)==0) { return(character(0)) } ve <- strsplit(unname(ve), " ") ve <- lapply(ve, strsplit, "/") v <- lapply(ve, function(x) sapply(x, "[", 1)) e <- lapply(ve, function(x) sapply(x, "[", 2)) int <- function(x) { if (length(unique(x))==1) { as.character(x[1]) } else { paste(sep="", min(x), "-", max(x)) } } v <- sapply(v, int) e <- sapply(e, int) paste(v, sep="/", e) } print.nexusDatasetInfoList <- function(x, ...) { summary(x) if (length(x)==0) { return(invisible(x)) } ve <- parseVE(unname(sapply(x, "[[", "vertices/edges"))) nets <- sapply(x, function(y) length(strsplit(y$networks, " ")[[1]])) sid <- sapply(x, "[[", "sid") if (any(nets>1)) { sid[nets > 1] <- paste(sep="", sid[nets>1], ".", nets[nets>1]) } df <- data.frame(no=paste(sep="", "[", format(seq_along(x)), "] "), sid=format(sid), size=paste(sep="", " ", format(ve)), id=paste(sep="", " #", format(sapply(x, "[[", "id")), " "), name=sapply(x, "[[", "name")) out <- do.call(paste, c(as.list(df), sep="")) long <- nchar(out) > getOption("width") out <- paste(sep="", substr(out, 1, getOption("width")-1), ifelse(long, "+", "")) cat(out, sep="\n") invisible(x) } nexus.format.result <- function(l, name="") { if (length(l)==0) { res <- list() class(res) <- "nexusDatasetInfoList" return(res) } l <- lapply(l, function(x) c(sub("[ ]*:[^:]*$", "", x), sub("^[^:]*:[ ]*", "", x))) spos <- which(sapply(l, function(x) x[1]=="id")) epos <- c((spos-1), length(l)) ehead <- epos[1] epos <- epos[-1] res <- mapply(spos, epos, SIMPLIFY=FALSE, FUN=function(s, e) makeNexusDatasetInfo(l[s:e])) class(res) <- "nexusDatasetInfoList" for (h in 1:ehead) { attr(res, l[[h]][1]) <- l[[h]][2] attr(res, "name") <- name } res } nexus.list <- function(tags=NULL, offset=0, limit=10, operator=c("or", "and"), order=c("date", "name", "popularity"), nexus.url=getIgraphOpt("nexus.url")) { operator=igraph.match.arg(operator) order=igraph.match.arg(order) if (is.null(tags)) { u <- paste(sep="", nexus.url, "/api/dataset_info?format=text", "&offset=", offset, "&limit=", limit, "&order=", order) name <- "data set list" } else { tags <- paste(tags, collapse="|") u <- paste(sep="", nexus.url, "/api/dataset_info?tag=", tags, "&operator=", operator, "&format=text", "&offset=", offset, "&limit=", limit, "&order=", order) name <- paste("tags:", gsub("|", "; ", tags, fixed=TRUE)) } f <- url(URLencode(u)) l <- readLines(f) close(f) nexus.format.result(l, name) } nexus.info <- function(id, nexus.url=getIgraphOpt("nexus.url")) { if (inherits(id, "nexusDatasetInfo")) { id <- id$id } else if (inherits(id, "nexusDatasetInfoList")) { rid <- sapply(id, "[[", "id") res <- lapply(rid, nexus.info, nexus.url=nexus.url) class(res) <- class(id) attributes(res) <- attributes(id) return(res) } u <- paste(sep="", nexus.url, "/api/dataset_info?format=text&id=", id) f <- url(URLencode(u)) l <- readLines(f) close(f) l2 <- character() for (i in seq_along(l)) { if (!grepl("^ ", l[i])) { l2 <- c(l2, l[i]) } else { l2[length(l2)] <- paste(sep="\n", l2[length(l2)], sub(" ", "", l[i], fixed=TRUE)) } } l2 <- lapply(l2, function(x) c(sub("[ ]*:.*$", "", x), sub("^[^:]*:[ ]*", "", x))) res <- makeNexusDatasetInfo(l2) if (! "attributes" %in% names(res)) { res$attributes <- list() } return(res) } nexus.get <- function(id, offset=0, order=c("date", "name", "popularity"), nexus.url=getIgraphOpt("nexus.url")) { order=igraph.match.arg(order) if (inherits(id, "nexusDatasetInfo")) { id <- id$id } else if (inherits(id, "nexusDatasetInfoList")) { id <- sapply(id, "[[", "id") return(lapply(id, nexus.get, nexus.url=nexus.url)) } u <- paste(sep="", nexus.url, "/api/dataset?id=", id, "&format=R-igraph") env <- new.env() rdata <- url(URLencode(u)) load(rdata, envir=env) close(rdata) return(get(ls(env)[1], env)) } nexus.search <- function(q, offset=0, limit=10, order=c("date", "name", "popularity"), nexus.url=getIgraphOpt("nexus.url")) { order=igraph.match.arg(order) u <- paste(sep="", nexus.url, "/api/search?q=", q, "&format=text","&offset=", offset, "&limit=", limit, "&order=", order) f <- url(URLencode(u)) l <- readLines(f) close(f) if (length(l)==0) { res <- list() class(res) <- "nexusDatasetInfoList" return(res) } nexus.format.result(l, name=paste("q:", q)) } `[.nexusDatasetInfoList` <- function(x, i) { res <- unclass(x)[i] class(res) <- class(x) attributes(res) <- attributes(x) res } ' DATA SET LIST: -------------- NEXUS 1-10/18 -- data set list [ 1] kaptail.4 #18 39/109-223 Kapferer tailor shop [ 2] condmatcollab2003 #17 31163/120029 Condensed matter collaborations, 2003 [ 3] condmatcollab #16 16726/47594 Condensed matter collaborations, 1999 [ 4] powergrid #15 4941/6594 Western US power grid [ 5] celegansneural #14 297/2359 C. Elegans neural network [ 6] polblogs #13 1490/19090 US political blog network [ 7] dolphins #12 62/159 Dolphin social network [ 8] football #11 115/616 Network of American college ... [ 9] adjnoun #10 112/425 Word adjacencies from David ... [10] huckleberry # 9 74/301 Coappearance network from ... TAG SEARCH: ----------- NEXUS 1-4/4 -- tags: directed [1] kaptail.4 #18 39/109-223 Kapferer tailor shop [2] polblogs #13 1490/19090 US political blog network [3] macaque # 4 45/463 Macaque visuotactile brain areas [4] UKfaculty # 2 81/817 UK faculty social network FULL TEXT SEARCH: ----------------- NEXUS 1-2/2 -- q: US [1] powergrid #15 4941/6594 Western US power grid [2] polblogs #13 1490/19090 US political blog network DATA SET SUMMARY: ----------------- NEXUS B--- 39 109-223 -- #18 Kapferer tailor shop + tags: directed; social network; undirected + networks: 1/KAPFTI2; 2/KAPFTS2; 3/KAPFTI1; 4/KAPFTS1 NEXUS U--- 4941 6594 -- #15 Western US power grid + tags: technology DATA SET INFO: -------------- NEXUS B--- 39 109-223 -- #18 Kapferer tailor shop + tags: directed; social network; undirected + attr: name (v/c) [Actor names] + networks: 1/KAPFTI2; 2/KAPFTS2; 3/KAPFTI1; 4/KAPFTS1 + nets: #1 KAPFTI2; #2 KAPFTS2; #3 KAPFTI1; #4 KAPFTS1 + date: 2011-01-23 + licence: Creative Commons by-sa 3.0 + licence url: http://creativecommons.org/licenses/by-sa/3.0/ + summary: Interactions in a tailor shop in Zambia (then Northern Rhodesia) over a period of ten months. + details: Bruce Kapferer (1972) observed interactions in a tailor shop in Zambia (then Northern Rhodesia) over a period of ten months. His focus was the changing patterns of alliance among workers during extended negotiations for higher wages. . The matrices represent two different types of interaction, recorded at two different times (seven months apart) over a period of one month. TI1 and TI2 record the "instrumental" (work- and assistance-related) interactions at the two times; TS1 and TS2 the "sociational" (friendship, socioemotional) interactions. . The data are particularly interesting since an abortive strike occurred after the first set of observations, and a successful strike took place after the second. + formats: Pajek; R-igraph + citation: Kapferer B. (1972). Strategy and transaction in an African factory. Manchester: Manchester University Press. ' igraph/R/tkplot.R0000644000176000001440000015172512325365704013437 0ustar ripleyusers # IGraph R package # Copyright (C) 2003-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA # ################################################################### ################################################################### # Internal variables ################################################################### # the environment containing all the plots .tkplot.env <- new.env() assign(".next", 1, .tkplot.env) ################################################################### # Main function ################################################################### tkplot <- function(graph, canvas.width=450, canvas.height=450, ...) { if (!is.igraph(graph)) { stop("Not a graph object") } # Libraries require(tcltk) || stop("tcl/tk library not available") # Visual parameters params <- i.parse.plot.params(graph, list(...)) labels <- params("vertex", "label") label.color <- .tkplot.convert.color(params("vertex", "label.color")) label.font <- .tkplot.convert.font(params("vertex", "label.font"), params("vertex", "label.family"), params("vertex", "label.cex")) label.degree <- params("vertex", "label.degree") label.dist <- params("vertex", "label.dist") vertex.color <- .tkplot.convert.color(params("vertex", "color")) vertex.size <- params("vertex", "size") vertex.frame.color <- .tkplot.convert.color(params("vertex", "frame.color")) edge.color <- .tkplot.convert.color(params("edge", "color")) edge.width <- params("edge", "width") edge.labels <- params("edge", "label") edge.lty <- params("edge", "lty") loop.angle <- params("edge", "loop.angle") arrow.mode <- params("edge", "arrow.mode") edge.label.font <- .tkplot.convert.font(params("edge", "label.font"), params("edge", "label.family"), params("edge", "label.cex")) edge.label.color <- params("edge", "label.color") arrow.size <- params("edge", "arrow.size")[1] curved <- params("edge", "curved") curved <- rep(curved, length=ecount(graph)) layout <- unname(params("plot", "layout")) layout[,2] <- -layout[,2] margin <- params("plot", "margin") margin <- rep(margin, length=4) # the new style parameters can't do this yet arrow.mode <- i.get.arrow.mode(graph, arrow.mode) # Edge line type edge.lty <- i.tkplot.get.edge.lty(edge.lty) # Create window & canvas top <- tktoplevel(background="lightgrey") canvas <- tkcanvas(top, relief="raised", width=canvas.width, height=canvas.height, borderwidth=2) tkpack(canvas, fill="both", expand=1) # Create parameters vertex.params <- sdf(vertex.color=vertex.color, vertex.size=vertex.size, label.font=label.font, NROW=vcount(graph)) params <- list(vertex.params=vertex.params, edge.color=edge.color, label.color=label.color, labels.state=1, edge.width=edge.width, padding=margin*300+max(vertex.size)+5, grid=0, label.degree=label.degree, label.dist=label.dist, edge.labels=edge.labels, vertex.frame.color=vertex.frame.color, loop.angle=loop.angle, edge.lty=edge.lty, arrow.mode=arrow.mode, edge.label.font=edge.label.font, edge.label.color=edge.label.color, arrow.size=arrow.size, curved=curved) # The popup menu popup.menu <- tkmenu(canvas) tkadd(popup.menu, "command", label="Fit to screen", command=function() { tkplot.fit.to.screen(tkp.id)}) # Different popup menu for vertices vertex.popup.menu <- tkmenu(canvas) tkadd(vertex.popup.menu, "command", label="Vertex color", command=function() { tkp <- .tkplot.get(tkp.id) vids <- .tkplot.get.selected.vertices(tkp.id) if (length(vids)==0) return(FALSE) initialcolor <- tkp$params$vertex.params[vids[1], "vertex.color"] color <- .tkplot.select.color(initialcolor) if (color=="") return(FALSE) # Cancel .tkplot.update.vertex.color(tkp.id, vids, color) }) tkadd(vertex.popup.menu, "command", label="Vertex size", command=function() { tkp <- .tkplot.get(tkp.id) vids <- .tkplot.get.selected.vertices(tkp.id) if (length(vids)==0) return(FALSE) initialsize <- tkp$params$vertex.params[1, "vertex.size"] size <- .tkplot.select.number("Vertex size", initialsize, 1, 20) if (is.na(size)) return(FALSE) .tkplot.update.vertex.size(tkp.id, vids, size) }) # Different popup menu for edges edge.popup.menu <- tkmenu(canvas) tkadd(edge.popup.menu, "command", label="Edge color", command=function() { tkp <- .tkplot.get(tkp.id) eids <- .tkplot.get.selected.edges(tkp.id) if (length(eids)==0) return(FALSE) initialcolor <- ifelse(length(tkp$params$edge.color)>1, tkp$params$edge.color[eids[1]], tkp$params$edge.color) color <- .tkplot.select.color(initialcolor) if (color=="") return(FALSE) # Cancel .tkplot.update.edge.color(tkp.id, eids, color) }) tkadd(edge.popup.menu, "command", label="Edge width", command=function() { tkp <- .tkplot.get(tkp.id) eids <- .tkplot.get.selected.edges(tkp.id) if (length(eids)==0) return(FALSE) initialwidth <- ifelse(length(tkp$params$edge.width)>1, tkp$params$edge.width[eids[1]], tkp$params$edge.width) width <- .tkplot.select.number("Edge width", initialwidth, 1, 10) if (is.na(width)) return(FALSE) # Cancel .tkplot.update.edge.width(tkp.id, eids, width) }) # Create plot object tkp <- list(top=top, canvas=canvas, graph=graph, coords=layout, labels=labels, params=params, popup.menu=popup.menu, vertex.popup.menu=vertex.popup.menu, edge.popup.menu=edge.popup.menu) tkp.id <- .tkplot.new(tkp) tktitle(top) <- paste("Graph plot", as.character(tkp.id)) # The main pull-down menu main.menu <- tkmenu(top) tkadd(main.menu, "command", label="Close", command=function() { tkplot.close(tkp.id, TRUE)}) select.menu <- .tkplot.select.menu(tkp.id, main.menu) tkadd(main.menu, "cascade", label="Select", menu=select.menu) layout.menu <- .tkplot.layout.menu(tkp.id, main.menu) tkadd(main.menu, "cascade", label="Layout", menu=layout.menu) view.menu <- tkmenu(main.menu) tkadd(main.menu, "cascade", label="View", menu=view.menu) tkadd(view.menu, "command", label="Fit to screen", command=function() { tkplot.fit.to.screen(tkp.id)}) tkadd(view.menu, "command", label="Center on screen", command=function() { tkplot.center(tkp.id)}) tkadd(view.menu, "separator") view.menu.labels <- tclVar(1) view.menu.grid <- tclVar(0) tkadd(view.menu, "checkbutton", label="Labels", variable=view.menu.labels, command=function() { .tkplot.toggle.labels(tkp.id)}) # grid canvas object not implemented in tcltk (?) :( # tkadd(view.menu, "checkbutton", label="Grid", # variable=view.menu.grid, command=function() { # .tkplot.toggle.grid(tkp.id)}) tkadd(view.menu, "separator") rotate.menu <- tkmenu(view.menu) tkadd(view.menu, "cascade", label="Rotate", menu=rotate.menu) sapply(c(-90,-45,-15,-5,-1,1,5,15,45,90), function(deg) { tkadd(rotate.menu, "command", label=paste(deg, "degree"), command=function() { tkplot.rotate(tkp.id, degree=deg) }) }) export.menu <- tkmenu(main.menu) tkadd(main.menu, "cascade", label="Export", menu=export.menu) tkadd(export.menu, "command", label="Postscript", command=function() { tkplot.export.postscript(tkp.id)}) tkconfigure(top, "-menu", main.menu) # plot it .tkplot.create.edges(tkp.id) .tkplot.create.vertices(tkp.id) # we would need an update here tkplot.fit.to.screen(tkp.id, canvas.width, canvas.height) # Kill myself if window was closed tkbind(top, "", function() tkplot.close(tkp.id, FALSE)) ################################################################### # The callbacks for interactive editing ################################################################### tkitembind(canvas, "vertex||label||edge", "<1>", function(x, y) { tkp <- .tkplot.get(tkp.id) canvas <- .tkplot.get(tkp.id, "canvas") .tkplot.deselect.all(tkp.id) .tkplot.select.current(tkp.id) # tkitemraise(canvas, "current") }) tkitembind(canvas, "vertex||label||edge", "", function(x,y) { canvas <- .tkplot.get(tkp.id, "canvas") curtags <- as.character(tkgettags(canvas, "current")) seltags <- as.character(tkgettags(canvas, "selected")) if ("vertex" %in% curtags && "vertex" %in% seltags) { if ("selected" %in% curtags) { .tkplot.deselect.current(tkp.id) } else { .tkplot.select.current(tkp.id) } } else if ("edge" %in% curtags && "edge" %in% seltags) { if ("selected" %in% curtags) { .tkplot.deselect.current(tkp.id) } else { .tkplot.select.current(tkp.id) } } else if ("label" %in% curtags && "vertex" %in% seltags) { vtag <- curtags[pmatch("v-", curtags)] tkid <- as.numeric(tkfind(canvas, "withtag", paste(sep="", "vertex&&", vtag))) vtags <- as.character(tkgettags(canvas, tkid)) if ("selected" %in% vtags) { .tkplot.deselect.vertex(tkp.id, tkid) } else { .tkplot.select.vertex(tkp.id, tkid) } } else { .tkplot.deselect.all(tkp.id) .tkplot.select.current(tkp.id) } }) tkitembind(canvas, "vertex||edge||label", "", function(x, y) { canvas <- .tkplot.get(tkp.id, "canvas") tkitemlower(canvas, "current") }) tkitembind(canvas, "vertex||edge||label", "", function(x, y) { canvas <- .tkplot.get(tkp.id, "canvas") tkitemraise(canvas, "current") }) tkbind(canvas, "<3>", function(x, y) { canvas <- .tkplot.get(tkp.id, "canvas") tags <- as.character(tkgettags(canvas, "current")) if ("label" %in% tags) { vtag <- tags[ pmatch("v-", tags) ] vid <- as.character(tkfind(canvas, "withtag", paste(sep="", "vertex&&", vtag))) tags <- as.character(tkgettags(canvas, vid)) } if ("selected" %in% tags) { # The selection is active } else { # Delete selection, single object .tkplot.deselect.all(tkp.id) .tkplot.select.current(tkp.id) } tags <- as.character(tkgettags(canvas, "selected")) ## TODO: what if different types of objects are selected if ("vertex" %in% tags || "label" %in% tags) { menu <- .tkplot.get(tkp.id, "vertex.popup.menu") } else if ("edge" %in% tags) { menu <- .tkplot.get(tkp.id, "edge.popup.menu") } else { menu <- .tkplot.get(tkp.id, "popup.menu") } x <- as.integer(x) + as.integer(tkwinfo("rootx", canvas)) y <- as.integer(y) + as.integer(tkwinfo("rooty", canvas)) .Tcl(paste("tk_popup", .Tcl.args(menu, x, y))) }) if (tkp$params$label.dist==0) tobind <- "vertex||label" else tobind <- "vertex" tkitembind(canvas, tobind, "", function(x, y) { tkp <- .tkplot.get(tkp.id) x <- as.numeric(x) y <- as.numeric(y) width <- as.numeric(tkwinfo("width", tkp$canvas)) height <- as.numeric(tkwinfo("height", tkp$canvas)) if (x < 10) { x <- 10 } if (x > width-10) { x <- width-10 } if (y < 10) { y <- 10 } if (y > height-10) { y <- height-10 } # get the id tags <- as.character(tkgettags(tkp$canvas, "selected")) id <- as.numeric(strsplit(tags[pmatch("v-", tags)], "-", fixed=TRUE)[[1]][2]) if (is.na(id)) { return() } # move the vertex .tkplot.set.vertex.coords(tkp.id, id, x, y) .tkplot.update.vertex(tkp.id, id, x, y) }) if (tkp$params$label.dist!=0) { tkitembind(canvas, "label", "", function(x,y) { tkp <- .tkplot.get(tkp.id) x <- as.numeric(x) y <- as.numeric(y) # get the id tags <- as.character(tkgettags(tkp$canvas, "selected")) id <- as.numeric(strsplit(tags[pmatch("v-", tags)], "-", fixed=TRUE)[[1]][2]) if (is.na(id)) { return() } phi <- pi+atan2(tkp$coords[id,2]-y, tkp$coords[id,1]-x) .tkplot.set.label.degree(tkp.id, id, phi) .tkplot.update.label(tkp.id, id, tkp$coords[id,1], tkp$coords[id,2]) }) } # We don't need these any more, they are stored in the environment rm(tkp, params, layout, vertex.color, edge.color, top, canvas, main.menu, layout.menu, view.menu, export.menu, label.font, label.degree, vertex.frame.color, vertex.params) tkp.id } ################################################################### # Internal functions handling data about layouts for the GUI ################################################################### .tkplot.addlayout <- function(name, layout.data) { if (!exists(".layouts", envir=.tkplot.env)) { assign(".layouts", list(), .tkplot.env) } assign("tmp", layout.data, .tkplot.env) cmd <- paste(sep="", ".layouts[[\"", name, "\"]]", " <- tmp") eval(parse(text=cmd), .tkplot.env) rm("tmp", envir=.tkplot.env) } .tkplot.getlayout <- function(name) { cmd <- paste(sep="", ".layouts[[\"", name, "\"]]") eval(parse(text=cmd), .tkplot.env) } .tkplot.layouts.newdefaults <- function(name, defaults) { assign("tmp", defaults, .tkplot.env) for (i in seq(along=defaults)) { cmd <- paste(sep="", '.layouts[["', name, '"]]$params[[', i, ']]$default <- tmp[[', i, ']]') eval(parse(text=cmd), .tkplot.env) } } .tkplot.getlayoutlist <- function() { eval(parse(text="names(.layouts)"), .tkplot.env) } .tkplot.getlayoutname <- function(name) { cmd <- paste(sep="", '.layouts[["', name, '"]]$name') eval(parse(text=cmd), .tkplot.env) } .tkplot.addlayout("random", list(name="Random", f=layout.random, params=list())) .tkplot.addlayout("circle", list(name="Circle", f=layout.circle, params=list())) .tkplot.addlayout("fruchterman.reingold", list(name="Fruchterman-Reingold", f=layout.fruchterman.reingold, params=list( niter=list(name="Number of iterations", type="numeric", default=500), maxdelta=list(name="Maximum change (n)", type="expression", default=expression(vcount(.tkplot.g))), area=list(name="Area parameter (n^2)", type="expression", default=expression(vcount(.tkplot.g)^2)), coolexp=list(name="Cooling exponent", type="numeric", default=3), repulserad=list(name="Cancellation radius (n^3)", type="expression", # FIXME: this should be area * n, but parameters # can't depend on each other.... default=expression(vcount(.tkplot.g)^3)) ) ) ) .tkplot.addlayout("kamada.kawai", list(name="Kamada-Kawai", f=layout.kamada.kawai, params=list( niter=list(name="Number of iterations", type="numeric", default=1000), initemp=list(name="Initial temperature", type="numeric", default=10), coolexp=list(name="Cooling exponent", type="numeric", default=0.99) ) ) ) .tkplot.addlayout("spring", list(name="Spring Embedder", f=layout.spring, params=list( mass=list(names="The vertex mass", type="numeric", default=0.1), equil=list(names="The equilibrium spring extension", type="numeric", default=1), k=list(names="The spring coefficient", type="numeric", default=0.001), repeqdis=list(names="Repulsion balance point", type="numeric", default=0.1), kfr=list(names="Friction base coefficient", type="numeric", default=0.01), repulse=list(names="Use repulsion", type="logical", default=FALSE) ) ) ) .tkplot.addlayout("reingold.tilford", list(names="Reingold-Tilford", f=layout.reingold.tilford, params=list( root=list(name="Root vertex", type="numeric", default=1) ) ) ) ################################################################### # Other public functions, misc. ################################################################### tkplot.close <- function(tkp.id, window.close=TRUE) { if (window.close) { cmd <- paste(sep="", "tkp.", tkp.id, "$top") top <- eval(parse(text=cmd), .tkplot.env) tkbind(top, "", "") tkdestroy(top) } cmd <- paste(sep="", "tkp.", tkp.id) rm(list=cmd, envir=.tkplot.env) invisible(NULL) } tkplot.off <- function() { eapply(.tkplot.env, function(tkp) { tkdestroy(tkp$top) }) rm(list=ls(.tkplot.env), envir=.tkplot.env) invisible(NULL) } tkplot.fit.to.screen <- function(tkp.id, width=NULL, height=NULL) { tkp <- .tkplot.get(tkp.id) if (is.null(width)) { width <- as.numeric(tkwinfo("width", tkp$canvas)) } if (is.null(height)) { height <- as.numeric(tkwinfo("height", tkp$canvas)) } coords <- .tkplot.get(tkp.id, "coords") # Shift to zero coords[,1] <- coords[,1]-min(coords[,1]) coords[,2] <- coords[,2]-min(coords[,2]) # Scale coords[,1] <- coords[,1] / max(coords[,1]) * (width-(tkp$params$padding[2]+tkp$params$padding[4])) coords[,2] <- coords[,2] / max(coords[,2]) * (height-(tkp$params$padding[1]+tkp$params$padding[3])) # Padding coords[,1] <- coords[,1]+tkp$params$padding[2] coords[,2] <- coords[,2]+tkp$params$padding[3] # Store .tkplot.set(tkp.id, "coords", coords) # Update .tkplot.update.vertices(tkp.id) invisible(NULL) } tkplot.center <- function(tkp.id) { tkp <- .tkplot.get(tkp.id) width <- as.numeric(tkwinfo("width", tkp$canvas)) height <- as.numeric(tkwinfo("height", tkp$canvas)) coords <- .tkplot.get(tkp.id, "coords") canvas.center.x <- width/2 canvas.center.y <- height/2 coords <- .tkplot.get(tkp.id, "coords") r1 <- range(coords[,1]) r2 <- range(coords[,2]) coords.center.x <- (r1[1]+r1[2])/2 coords.center.y <- (r2[1]+r2[2])/2 # Shift to center coords[,1] <- coords[,1]+canvas.center.x-coords.center.x coords[,2] <- coords[,2]+canvas.center.y-coords.center.y # Store .tkplot.set(tkp.id, "coords", coords) # Update .tkplot.update.vertices(tkp.id) invisible(NULL) } tkplot.reshape <- function(tkp.id, newlayout, ...) { tkp <- .tkplot.get(tkp.id) .tkplot.set(tkp.id, "coords", newlayout(tkp$graph, ...)) tkplot.fit.to.screen(tkp.id) .tkplot.update.vertices(tkp.id) invisible(NULL) } tkplot.export.postscript <- function(tkp.id) { tkp <- .tkplot.get(tkp.id) filename <- tkgetSaveFile(initialfile="Rplots.eps", defaultextension="eps", title="Export graph to PostScript file") tkpostscript(tkp$canvas, file=filename) invisible(NULL) } tkplot.getcoords <- function(tkp.id, norm=FALSE) { coords <- .tkplot.get(tkp.id, "coords") coords[,2] <- max(coords[,2]) - coords[,2] if (norm) { # Shift coords[,1] <- coords[,1]-min(coords[,1]) coords[,2] <- coords[,2]-min(coords[,2]) # Scale coords[,1] <- coords[,1] / max(coords[,1])-0.5 coords[,2] <- coords[,2] / max(coords[,2])-0.5 } coords } tkplot.setcoords <- function(tkp.id, coords) { stopifnot(is.matrix(coords), ncol(coords)==2) .tkplot.set(tkp.id, "coords", coords) .tkplot.update.vertices(tkp.id) invisible(NULL) } tkplot.rotate <- function(tkp.id, degree=NULL, rad=NULL) { coords <- .tkplot.get(tkp.id, "coords") if (is.null(degree) && is.null(rad)) { rad <- pi/2 } else if (is.null(rad) && !is.null(degree)) { rad <- degree/180*pi } center <- c(mean(range(coords[,1])), mean(range(coords[,2]))) phi <- atan2(coords[,2]-center[2], coords[,1]-center[1]) r <- sqrt((coords[,1]-center[1])**2 + (coords[,2]-center[2])**2) phi <- phi + rad coords[,1] <- r * cos(phi) coords[,2] <- r * sin(phi) .tkplot.set(tkp.id, "coords", coords) tkplot.center(tkp.id) invisible(NULL) } tkplot.canvas <- function(tkp.id) { .tkplot.get(tkp.id)$canvas } ################################################################### # Internal functions, handling the internal environment ################################################################### .tkplot.new <- function(tkp) { id <- get(".next", .tkplot.env) assign(".next", id+1, .tkplot.env) assign("tmp", tkp, .tkplot.env) cmd <- paste("tkp.", id, "<- tmp", sep="") eval(parse(text=cmd), .tkplot.env) rm("tmp", envir=.tkplot.env) id } .tkplot.get <- function(tkp.id, what=NULL) { if (is.null(what)) { get(paste("tkp.", tkp.id, sep=""), .tkplot.env) } else { cmd <- paste("tkp.", tkp.id, "$", what, sep="") eval(parse(text=cmd), .tkplot.env) } } .tkplot.set <- function(tkp.id, what, value) { assign("tmp", value, .tkplot.env) cmd <- paste(sep="", "tkp.", tkp.id, "$", what, "<-tmp") eval(parse(text=cmd), .tkplot.env) rm("tmp", envir=.tkplot.env) TRUE } .tkplot.set.params <- function(tkp.id, what, value) { assign("tmp", value, .tkplot.env) cmd <- paste(sep="", "tkp.", tkp.id, "$params$", what, "<-tmp") eval(parse(text=cmd), .tkplot.env) rm("tmp", envir=.tkplot.env) TRUE } .tkplot.set.vertex.coords <- function(tkp.id, id, x, y) { cmd <- paste(sep="", "tkp.", tkp.id, "$coords[",id,",]<-c(",x,",",y,")") eval(parse(text=cmd), .tkplot.env) TRUE } .tkplot.set.label.degree <- function(tkp.id, id, phi) { tkp <- .tkplot.get(tkp.id) if (length(tkp$params$label.degree)==1) { label.degree <- rep(tkp$params$label.degree, times=vcount(tkp$graph)) label.degree[id] <- phi assign("tmp", label.degree, .tkplot.env) cmd <- paste(sep="", "tkp.", tkp.id, "$params$label.degree <- tmp") eval(parse(text=cmd), .tkplot.env) rm("tmp", envir=.tkplot.env) } else { cmd <- paste(sep="", "tkp.", tkp.id, "$params$label.degree[", id, "] <- ", phi) eval(parse(text=cmd), .tkplot.env) } TRUE } ################################################################### # Internal functions, creating and updating canvas objects ################################################################### # Creates a new vertex tk object .tkplot.create.vertex <- function(tkp.id, id, label, x=0, y=0) { tkp <- .tkplot.get(tkp.id) vertex.size <- tkp$params$vertex.params[id, "vertex.size"] vertex.color <- tkp$params$vertex.params[id, "vertex.color"] vertex.frame.color <- ifelse(length(tkp$params$vertex.frame.color)>1, tkp$params$vertex.frame.color[id], tkp$params$vertex.frame.color) item <- tkcreate(tkp$canvas, "oval", x-vertex.size, y-vertex.size, x+vertex.size, y+vertex.size, width=1, outline=vertex.frame.color, fill=vertex.color) tkaddtag(tkp$canvas, "vertex", "withtag", item) tkaddtag(tkp$canvas, paste("v-", id, sep=""), "withtag", item) if (!is.na(label)) { label.degree <- ifelse(length(tkp$params$label.degree)>1, tkp$params$label.degree[id], tkp$params$label.degree) label.color <- if (length(tkp$params$label.color)>1) { tkp$params$label.color[id] } else { tkp$params$label.color } label.dist <- tkp$params$label.dist label.x <- x+label.dist*cos(label.degree)* (vertex.size+6+4*(ceiling(log10(id)))) label.y <- y+label.dist*sin(label.degree)* (vertex.size+6+4*(ceiling(log10(id)))) if (label.dist==0) { afill <- label.color } else { afill <- "red" } litem <- tkcreate(tkp$canvas, "text", label.x, label.y, text=as.character(label), state="normal", fill=label.color, activefill=afill, font=tkp$params$vertex.params[id, "label.font"]) tkaddtag(tkp$canvas, "label", "withtag", litem) tkaddtag(tkp$canvas, paste("v-", id, sep=""), "withtag", litem) } item } # Create all vertex objects and move them into correct position .tkplot.create.vertices <- function(tkp.id) { tkp <- .tkplot.get(tkp.id) n <- vcount(tkp$graph) # Labels labels <- i.get.labels(tkp$graph, tkp$labels) mapply(function(v, l, x, y) .tkplot.create.vertex(tkp.id, v, l, x, y), 1:n, labels, tkp$coords[,1], tkp$coords[,2]) } .tkplot.update.label <- function(tkp.id, id, x, y) { tkp <- .tkplot.get(tkp.id) vertex.size <- tkp$params$vertex.params[id, "vertex.size"] label.degree <- ifelse(length(tkp$params$label.degree)>1, tkp$params$label.degree[id], tkp$params$label.degree) label.dist <- tkp$params$label.dist label.x <- x+label.dist*cos(label.degree)* (vertex.size+6+4*(ceiling(log10(id)))) label.y <- y+label.dist*sin(label.degree)* (vertex.size+6+4*(ceiling(log10(id)))) tkcoords(tkp$canvas, paste("label&&v-", id, sep=""), label.x, label.y) } .tkplot.update.vertex <- function(tkp.id, id, x, y) { tkp <- .tkplot.get(tkp.id) vertex.size <- tkp$params$vertex.params[id, "vertex.size"] # Vertex tkcoords(tkp$canvas, paste("vertex&&v-", id, sep=""), x-vertex.size, y-vertex.size, x+vertex.size, y+vertex.size) # Label .tkplot.update.label(tkp.id, id, x, y) # Edges edge.from.ids <- as.numeric(tkfind(tkp$canvas, "withtag", paste("from-", id, sep=""))) edge.to.ids <- as.numeric(tkfind(tkp$canvas, "withtag", paste("to-", id, sep=""))) for (i in seq(along=edge.from.ids)) { .tkplot.update.edge(tkp.id, edge.from.ids[i]) } for (i in seq(along=edge.to.ids)) { .tkplot.update.edge(tkp.id, edge.to.ids[i]) } } .tkplot.update.vertices <- function(tkp.id) { tkp <- .tkplot.get(tkp.id) n <- vcount(tkp$graph) mapply(function(v, x, y) .tkplot.update.vertex(tkp.id, v, x, y), 1:n, tkp$coords[,1], tkp$coords[,2]) } # Creates tk object for edge 'id' .tkplot.create.edge <- function(tkp.id, from, to, id) { tkp <- .tkplot.get(tkp.id) from.c <- tkp$coords[from,] to.c <- tkp$coords[to,] edge.color <- ifelse(length(tkp$params$edge.color)>1, tkp$params$edge.color[id], tkp$params$edge.color) edge.width <- ifelse(length(tkp$params$edge.width)>1, tkp$params$edge.width[id], tkp$params$edge.width) edge.lty <- ifelse(length(tkp$params$edge.lty)>1, tkp$params$edge.lty[[id]], tkp$params$edge.lty) arrow.mode <- ifelse(length(tkp$params$arrow.mode)>1, tkp$params$arrow.mode[[id]], tkp$params$arrow.mode) arrow.size <- tkp$params$arrow.size curved <- tkp$params$curved[[id]] arrow <- c("none", "first", "last", "both")[arrow.mode+1] if (from != to) { ## non-loop edge if (is.logical(curved)) curved <- curved * 0.5 if (curved != 0) { smooth <- TRUE midx <- (from.c[1]+to.c[1])/2 midy <- (from.c[2]+to.c[2])/2 spx <- midx - curved * 1/2 * (from.c[2]-to.c[2]) spy <- midy + curved * 1/2 * (from.c[1]-to.c[1]) coords <- c(from.c[1], from.c[2], spx, spy, to.c[1], to.c[2]) } else { smooth <- FALSE coords <- c(from.c[1], from.c[2], to.c[1], to.c[2]) } args <- c(list(tkp$canvas, "line"), coords, list(width=edge.width, activewidth=2*edge.width, arrow=arrow, arrowshape=arrow.size * c(10, 10, 5), fill=edge.color, activefill="red", dash=edge.lty, tags=c("edge", paste(sep="", "edge-", id), paste(sep="", "from-", from), paste(sep="", "to-", to))), smooth=smooth) do.call(tkcreate, args) } else { ## loop edge ## the coordinates are not correct but we will call update anyway... tkcreate(tkp$canvas, "line", from.c[1], from.c[2], from.c[1]+20, from.c[1]-10, from.c[2]+30, from.c[2], from.c[1]+20, from.c[1]+10, from.c[1], from.c[2], width=edge.width, activewidth=2*edge.width, arrow=arrow, arrowshape=arrow.size * c(10,10,5), dash=edge.lty, fill=edge.color, activefill="red", smooth=TRUE, tags=c("edge", "loop", paste(sep="", "edge-", id), paste(sep="", "from-", from), paste(sep="", "to-", to))) } edge.label <- ifelse(length(tkp$params$edge.labels)>1, tkp$params$edge.labels[id], tkp$params$edge.labels) if (!is.na(edge.label)) { label.color <- ifelse(length(tkp$params$edge.label.color)>1, tkp$params$edge.label.color[id], tkp$params$edge.label.color) ## not correct for loop edges but we will update anyway... label.x <- (to.c[1]+from.c[1])/2 label.y <- (to.c[2]+from.c[2])/2 litem <- tkcreate(tkp$canvas, "text", label.x, label.y, text=as.character(edge.label), state="normal", fill=label.color, font=tkp$params$edge.label.font) tkaddtag(tkp$canvas, "label", "withtag", litem) tkaddtag(tkp$canvas, paste(sep="", "edge-", id), "withtag", litem) } } # Creates all edges .tkplot.create.edges <- function(tkp.id) { tkp <- .tkplot.get(tkp.id) n <- ecount(tkp$graph) edgematrix <- get.edgelist(tkp$graph, names=FALSE) mapply(function(from, to, id) .tkplot.create.edge(tkp.id, from, to, id), edgematrix[,1], edgematrix[,2], 1:nrow(edgematrix)) } # Update an edge with given itemid (not edge id!) .tkplot.update.edge <- function(tkp.id, itemid) { tkp <- .tkplot.get(tkp.id) tags <- as.character(tkgettags(tkp$canvas, itemid)) from <- as.numeric(substring(grep("from-", tags, value=TRUE, fixed=TRUE),6)) to <- as.numeric(substring(grep("to-", tags, value=TRUE, fixed=TRUE),4)) from.c <- tkp$coords[from,] to.c <- tkp$coords[to,] edgeid <- as.numeric(substring(tags[ pmatch("edge-", tags) ], 6)) if (from != to) { phi <- atan2(to.c[2]-from.c[2], to.c[1]-from.c[1]) r <- sqrt( (to.c[1]-from.c[1])^2 + (to.c[2]-from.c[2])^2 ) vertex.size <- tkp$params$vertex.params[to, "vertex.size"] vertex.size2 <- tkp$params$vertex.params[from, "vertex.size"] curved <- tkp$params$curved[[edgeid]] to.c[1] <- from.c[1] + (r-vertex.size)*cos(phi) to.c[2] <- from.c[2] + (r-vertex.size)*sin(phi) from.c[1] <- from.c[1] + vertex.size2*cos(phi) from.c[2] <- from.c[2] + vertex.size2*sin(phi) if (is.logical(curved)) curved <- curved * 0.5 if (curved == 0) { tkcoords(tkp$canvas, itemid, from.c[1], from.c[2], to.c[1], to.c[2]) } else { midx <- (from.c[1]+to.c[1])/2 midy <- (from.c[2]+to.c[2])/2 spx <- midx - curved * 1/2 * (from.c[2]-to.c[2]) spy <- midy + curved * 1/2 * (from.c[1]-to.c[1]) tkcoords(tkp$canvas, itemid, from.c[1], from.c[2], spx, spy, to.c[1], to.c[2]) } } else { vertex.size <- tkp$params$vertex.params[to, "vertex.size"] loop.angle <- ifelse(length(tkp$param$loop.angle)>1, tkp$params$loop.angle[edgeid], tkp$params$loop.angle) xx <- from.c[1] + cos(loop.angle/180*pi)*vertex.size yy <- from.c[2] + sin(loop.angle/180*pi)*vertex.size cc <- matrix(c(xx,yy, xx+20,yy-10, xx+30,yy, xx+20,yy+10, xx,yy), ncol=2, byrow=TRUE) phi <- atan2(cc[,2]-yy, cc[,1]-xx) r <- sqrt((cc[,1]-xx)**2 + (cc[,2]-yy)**2) phi <- phi+loop.angle/180*pi cc[,1] <- xx+r*cos(phi) cc[,2] <- yy+r*sin(phi) tkcoords(tkp$canvas, itemid, cc[1,1], cc[1,2], cc[2,1], cc[2,2], cc[3,1], cc[3,2], cc[4,1], cc[4,2], cc[5,1]+0.001, cc[5,2]+0.001) } edge.label <- ifelse(length(tkp$params$edge.labels)>1, tkp$params$edge.labels[edgeid], tkp$params$edge.labels) if (!is.na(edge.label)) { if (from != to) { label.x <- (to.c[1]+from.c[1])/2 label.y <- (to.c[2]+from.c[2])/2 } else { ## loops label.x <- xx+cos(loop.angle/180*pi)*30 label.y <- yy+sin(loop.angle/180*pi)*30 } litem <- as.numeric(tkfind(tkp$canvas, "withtag", paste(sep="", "label&&edge-", edgeid))) tkcoords(tkp$canvas, litem, label.x, label.y) } } .tkplot.toggle.labels <- function(tkp.id) { .tkplot.set.params(tkp.id, "labels.state", 1 - .tkplot.get(tkp.id, "params")$labels.state) tkp <- .tkplot.get(tkp.id) state <- ifelse(tkp$params$labels.state==1, "normal", "hidden") tkitemconfigure(tkp$canvas, "label", "-state", state) } .tkplot.toggle.grid <- function(tkp.id) { .tkplot.set.params(tkp.id, "grid", 1 - .tkplot.get(tkp.id, "params")$grid) tkp <- .tkplot.get(tkp.id) state <- ifelse(tkp$params$grid==1, "normal", "hidden") if (state=="hidden") { tkdelete(tkp$canvas, "grid") } else { tkcreate(tkp$canvas, "grid", 0, 0, 10, 10, tags=c("grid")) } } .tkplot.update.vertex.color <- function(tkp.id, vids, newcolor) { tkp <- .tkplot.get(tkp.id) vparams <- tkp$params$vertex.params vparams[vids, "vertex.color"] <- newcolor .tkplot.set(tkp.id, "params$vertex.params", vparams) tkitemconfigure(tkp$canvas, "selected&&vertex", "-fill", newcolor) } .tkplot.update.edge.color <- function(tkp.id, eids, newcolor) { tkp <- .tkplot.get(tkp.id) colors <- tkp$params$edge.color if (length(colors)==1 && length(eids)==ecount(tkp$graph)) { ## Uniform color -> uniform color .tkplot.set(tkp.id, "params$edge.color", newcolor) } else if (length(colors)==1) { ## Uniform color -> nonuniform color colors <- rep(colors, ecount(tkp$graph)) colors[eids] <- newcolor .tkplot.set(tkp.id, "params$edge.color", colors) } else if (length(eids)==ecount(tkp$graph)) { ## Non-uniform -> uniform .tkplot.set(tkp.id, "params$edge.color", newcolor) } else { ## Non-uniform -> non-uniform colors[eids] <- newcolor .tkplot.set(tkp.id, "params$edge.color", colors) } tkitemconfigure(tkp$canvas, "selected&&edge", "-fill", newcolor) } .tkplot.update.edge.width <- function(tkp.id, eids, newwidth) { tkp <- .tkplot.get(tkp.id) widths <- tkp$params$edge.width if (length(widths)==1 && length(eids)==ecount(tkp$graph)) { ## Uniform width -> uniform width .tkplot.set(tkp.id, "params$edge.width", newwidth) } else if (length(widths)==1) { ## Uniform width -> nonuniform width widths <- rep(widths, ecount(tkp$graph)) widths[eids] <- newwidth .tkplot.set(tkp.id, "params$edge.width", widths) } else if (length(eids)==ecount(tkp$graph)) { ## Non-uniform -> uniform .tkplot.set(tkp.id, "params$edge.width", newwidth) } else { ## Non-uniform -> non-uniform widths[eids] <- newwidth .tkplot.set(tkp.id, "params$edge.width", widths) } tkitemconfigure(tkp$canvas, "selected&&edge", "-width", newwidth) } .tkplot.update.vertex.size <- function(tkp.id, vids, newsize) { tkp <- .tkplot.get(tkp.id) vparams <- tkp$params$vertex.params vparams[vids, "vertex.size"] <- newsize .tkplot.set(tkp.id, "params$vertex.params", vparams) sapply(vids, function(id) { .tkplot.update.vertex(tkp.id, id, tkp$coords[id,1], tkp$coords[id,2]) }) } .tkplot.get.numeric.vector <- function(...) { labels <- list(...) if (length(labels)==0) return(FALSE) answers <- as.list(rep("", length(labels))) dialog <- tktoplevel() vars <- lapply(answers, tclVar) retval <- list() OnOK <- function() { retval <<- lapply(vars, tclvalue) tkdestroy(dialog) } OK.but <- tkbutton(dialog, text=" OK ", command=OnOK) for (i in seq(along=labels)) { tkgrid(tklabel(dialog, text=labels[[i]])) tmp <- tkentry(dialog, width="40",textvariable=vars[[i]]) tkgrid(tmp) tkbind(tmp, "", OnOK) } tkgrid(OK.but) tkwait.window(dialog) retval <- lapply(retval, function(v) { eval(parse(text=paste("c(", v, ")"))) }) return (retval) } .tkplot.select.number <- function(label, initial, low=1, high=100) { dialog <- tktoplevel() SliderValue <- tclVar(as.character(initial)) SliderValueLabel <- tklabel(dialog,text=as.character(tclvalue(SliderValue))) tkgrid(tklabel(dialog,text=label), SliderValueLabel) tkconfigure(SliderValueLabel, textvariable=SliderValue) slider <- tkscale(dialog, from=high, to=low, showvalue=F, variable=SliderValue, resolution=1, orient="horizontal") OnOK <- function() { SliderValue <<- as.numeric(tclvalue(SliderValue)) tkdestroy(dialog) } OnCancel <- function() { SliderValue <<- NA tkdestroy(dialog) } OK.but <- tkbutton(dialog, text=" OK ", command=OnOK) cancel.but <- tkbutton(dialog, text=" Cancel ", command=OnCancel) tkgrid(slider) tkgrid(OK.but, cancel.but) tkwait.window(dialog) return(SliderValue) } ################################################################### # Internal functions, vertex and edge selection ################################################################### .tkplot.deselect.all <- function(tkp.id) { canvas <- .tkplot.get(tkp.id, "canvas") ids <- as.numeric(tkfind(canvas, "withtag", "selected")) for (i in ids) { .tkplot.deselect.this(tkp.id, i) } } .tkplot.select.all.vertices <- function(tkp.id) { canvas <- .tkplot.get(tkp.id, "canvas") vertices <- as.numeric(tkfind(canvas, "withtag", "vertex")) for (i in vertices) { .tkplot.select.vertex(tkp.id, i) } } .tkplot.select.some.vertices <- function(tkp.id, vids) { canvas <- .tkplot.get(tkp.id, "canvas") vids <- unique(vids) for (i in vids) { tkid <- as.numeric(tkfind(canvas, "withtag", paste(sep="", "vertex&&v-", i))) .tkplot.select.vertex(tkp.id, tkid) } } .tkplot.select.all.edges <- function(tkp.id, vids) { canvas <- .tkplot.get(tkp.id, "canvas") edges <- as.numeric(tkfind(canvas, "withtag", "edge")) for (i in edges) { .tkplot.select.edge(tkp.id, i) } } .tkplot.select.some.edges <- function(tkp.id, from, to) { canvas <- .tkplot.get(tkp.id, "canvas") fromtags <- sapply(from, function(i) { paste(sep="", "from-", i) }) totags <- sapply(from, function(i) { paste(sep="", "to-", i) }) edges <- as.numeric(tkfind(canvas, "withtag", "edge")) for (i in edges) { tags <- as.character(tkgettags(canvas, i)) ftag <- tags[ pmatch("from-", tags) ] ttag <- tags[ pmatch("to-", tags) ] if (ftag %in% fromtags && ttag %in% totags) { .tkplot.select.edge(tkp.id, i) } } } .tkplot.select.vertex <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tkaddtag(canvas, "selected", "withtag", tkid) tkitemconfigure(canvas, tkid, "-outline", "red", "-width", 2) } .tkplot.select.edge <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tkaddtag(canvas, "selected", "withtag", tkid) tkitemconfigure(canvas, tkid, "-dash", "-") } .tkplot.select.label <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tkaddtag(canvas, "selected", "withtag", tkid) } .tkplot.deselect.vertex <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tkdtag(canvas, tkid, "selected") tkp <- .tkplot.get(tkp.id) tags <- as.character(tkgettags(canvas, tkid)) id <- as.numeric(substring(tags[pmatch("v-", tags)], 3)) vertex.frame.color <- ifelse(length(tkp$params$vertex.frame.color)>1, tkp$params$vertex.frame.color[id], tkp$params$vertex.frame.color) tkitemconfigure(canvas, tkid, "-outline", vertex.frame.color, "-width", 1) } .tkplot.deselect.edge <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tkdtag(canvas, tkid, "selected") tkp <- .tkplot.get(tkp.id) tags <- as.character(tkgettags(canvas, tkid)) id <- as.numeric(substring(tags[pmatch("edge-", tags)], 6)) edge.lty <- ifelse(length(tkp$params$edge.lty)>1, tkp$params$edge.lty[[id]], tkp$params$edge.lty) tkitemconfigure(canvas, tkid, "-dash", edge.lty) } .tkplot.deselect.label <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tkdtag(canvas, tkid, "selected") } .tkplot.select.current <- function(tkp.id) { canvas <- .tkplot.get(tkp.id, "canvas") tkid <- as.numeric(tkfind(canvas, "withtag", "current")) .tkplot.select.this(tkp.id, tkid) } .tkplot.deselect.current <- function(tkp.id) { canvas <- .tkplot.get(tkp.id, "canvas") tkid <- as.numeric(tkfind(canvas, "withtag", "current")) .tkplot.deselect.this(tkp.id, tkid) } .tkplot.select.this <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tags <- as.character(tkgettags(canvas, tkid)) if ("vertex" %in% tags) { .tkplot.select.vertex(tkp.id, tkid) } else if ("edge" %in% tags) { .tkplot.select.edge(tkp.id, tkid) } else if ("label" %in% tags) { tkp <- .tkplot.get(tkp.id) if (tkp$params$label.dist == 0) { id <- tags[pmatch("v-", tags)] tkid <- as.character(tkfind(canvas, "withtag", paste(sep="", "vertex&&", id))) .tkplot.select.vertex(tkp.id, tkid) } else { .tkplot.select.label(tkp.id, tkid) } } } .tkplot.deselect.this <- function(tkp.id, tkid) { canvas <- .tkplot.get(tkp.id, "canvas") tags <- as.character(tkgettags(canvas, tkid)) if ("vertex" %in% tags) { .tkplot.deselect.vertex(tkp.id, tkid) } else if ("edge" %in% tags) { .tkplot.deselect.edge(tkp.id, tkid) } else if ("label" %in% tags) { tkp <- .tkplot.get(tkp.id) if (tkp$params$label.dist == 0) { id <- tags[pmatch("v-", tags)] tkid <- as.character(tkfind(canvas, "withtag", paste(sep="", "vertex&&", id))) .tkplot.deselect.vertex(tkp.id, tkid) } else { .tkplot.deselect.label(tkp.id, tkid) } } } .tkplot.get.selected.vertices <- function(tkp.id) { canvas <- .tkplot.get(tkp.id, "canvas") tkids <- as.numeric(tkfind(canvas, "withtag", "vertex&&selected")) ids <- sapply(tkids, function(tkid) { tags <- as.character(tkgettags(canvas, tkid)) id <- as.numeric(substring(tags [pmatch("v-", tags)], 3)) id}) ids } .tkplot.get.selected.edges <- function(tkp.id) { canvas <- .tkplot.get(tkp.id, "canvas") tkids <- as.numeric(tkfind(canvas, "withtag", "edge&&selected")) ids <- sapply(tkids, function(tkid) { tags <- as.character(tkgettags(canvas, tkid)) id <- as.numeric(substring(tags [pmatch("edge-", tags)], 6)) id}) ids } ################################################################### # Internal functions: manipulating the UI ################################################################### .tkplot.select.menu <- function(tkp.id, main.menu) { select.menu <- tkmenu(main.menu) tkadd(select.menu, "command", label="Select all vertices", command=function() { .tkplot.deselect.all(tkp.id) .tkplot.select.all.vertices(tkp.id) }) tkadd(select.menu, "command", label="Select all edges", command=function() { .tkplot.deselect.all(tkp.id) .tkplot.select.all.edges(tkp.id) }) tkadd(select.menu, "command", label="Select some vertices...", command=function() { vids <- .tkplot.get.numeric.vector("Select vertices") .tkplot.select.some.vertices(tkp.id, vids[[1]]) }) tkadd(select.menu, "command", label="Select some edges...", command=function() { fromto <- .tkplot.get.numeric.vector("Select edges from vertices", "to vertices") .tkplot.select.some.edges(tkp.id, fromto[[1]], fromto[[2]]) }) tkadd(select.menu, "separator") tkadd(select.menu, "command", label="Deselect everything", command=function() { .tkplot.deselect.all(tkp.id) }) select.menu } .tkplot.layout.menu <- function(tkp.id, main.menu) { layout.menu <- tkmenu(main.menu) sapply(.tkplot.getlayoutlist(), function(n) { tkadd(layout.menu, "command", label=.tkplot.getlayoutname(n), command=function() { .tkplot.layout.dialog(tkp.id, n) }) }) layout.menu } .tkplot.layout.dialog <- function(tkp.id, layout.name) { layout <- .tkplot.getlayout(layout.name) # No parameters if (length(layout$params)==0) { return(tkplot.reshape(tkp.id, layout$f, params=list())) } submit <- function() { realparams <- params <- vector(mode="list", length(layout$params)) names(realparams) <- names(params) <- names(layout$params) for (i in seq(along=layout$params)) { realparams[[i]] <- params[[i]] <- switch(layout$params[[i]]$type, "numeric"=as.numeric(tkget(values[[i]])), "character"=as.character(tkget(values[[i]])), "logical"=as.logical(tclvalue(values[[i]])), "choice"=as.character(tclvalue(values[[i]])), "initial"=as.logical(tclvalue(values[[i]])) ) if (layout$params[[i]]$type=="initial" && params[[i]]) { realparams[[i]] <- tkplot.getcoords(tkp.id, norm=TRUE) } } if (as.logical(tclvalue(save.default))) { .tkplot.layouts.newdefaults(layout.name, params) } tkdestroy(dialog) tkplot.reshape(tkp.id, layout$f, params=realparams) } dialog <- tktoplevel(.tkplot.get(tkp.id, "top")) tkwm.title(dialog, paste("Layout parameters for graph plot", tkp.id)) tkwm.transient(dialog, .tkplot.get(tkp.id, "top")) tkgrid(tklabel(dialog, text=paste(layout$name, "layout"), font=tkfont.create(family="helvetica",size=20,weight="bold")), row=0, column=0, columnspan=2, padx=10, pady=10) row <- 1 values <- list() for (i in seq(along=layout$params)) { tkgrid(tklabel(dialog, text=paste(sep="", layout$params[[i]]$name, ":")), row=row, column=0, sticky="ne", padx=5, pady=5) if (layout$params[[i]]$type %in% c("numeric", "character")) { values[[i]] <- tkentry(dialog) tkinsert(values[[i]], 0, as.character(layout$params[[i]]$default)) tkgrid(values[[i]], row=row, column=1, sticky="nw", padx=5, pady=5) } else if (layout$params[[i]]$type=="logical") { values[[i]] <- tclVar(as.character(layout$params[[i]]$default)) tmp <- tkcheckbutton(dialog, onvalue="TRUE", offvalue="FALSE", variable=values[[i]]) tkgrid(tmp, row=row, column=1, sticky="nw", padx=5, pady=5) } else if (layout$params[[i]]$type=="choice") { tmp.frame <- tkframe(dialog) tkgrid(tmp.frame, row=row, column=1, sticky="nw", padx=5, pady=5) values[[i]] <- tclVar(layout$params[[i]]$default) for (j in 1:length(layout$params[[i]]$values)) { tmp <- tkradiobutton(tmp.frame, variable=values[[i]], value=layout$params[[i]]$values[j], text=layout$params[[i]]$values[j]) tkpack(tmp, anchor="nw") } } else if (layout$params[[i]]$type=="initial") { values[[i]] <- tclVar(as.character(layout$params[[i]]$default)) tkgrid(tkcheckbutton(dialog, onvalue="TRUE", offvalue="FALSE", variable=values[[i]]), row=row, column=1, sticky="nw", padx=5, pady=5) } else if (layout$param[[i]]$type=="expression") { values[[i]] <- tkentry(dialog) .tkplot.g <- .tkplot.get(tkp.id, "graph") tkinsert(values[[i]], 0, as.character(eval(layout$params[[i]]$default))) tkgrid(values[[i]], row=row, column=1, sticky="nw", padx=5, pady=5) } row <- row + 1 } # for along layout$params tkgrid(tklabel(dialog, text="Set these as defaults"), sticky="ne", row=row, column=0, padx=5, pady=5) save.default <- tclVar("FALSE") tkgrid(tkcheckbutton(dialog, onvalue="TRUE", offvalue="FALSE", variable=save.default, text=""), row=row, column=1, sticky="nw", padx=5, pady=5) row <- row + 1 tkgrid(tkbutton(dialog, text="OK", command=submit), row=row, column=0) tkgrid(tkbutton(dialog, text="Cancel", command=function() { tkdestroy(dialog); invisible(TRUE) }), row=row, column=1) } .tkplot.select.color <- function(initialcolor) { color <- tclvalue(tcl("tk_chooseColor", initialcolor=initialcolor, title="Choose a color")) return(color); } ################################################################### # Internal functions: other ################################################################### .tkplot.convert.color <- function(col) { if (is.numeric(col)) { ## convert numeric color based on current palette p <- palette() col <- col %% length(p) col[col==0] <- length(p) col <- palette()[col] } else if (is.character(col) && any(substr(col,1,1)=="#" & nchar(col)==9)) { ## drop alpha channel, tcltk doesn't support it idx <- substr(col,1,1)=="#" & nchar(col)==9 col[idx] <- substr(col[idx],1,7) } ## replace NA's with "" col[is.na(col)] <- "" col } .tkplot.convert.font <- function(font, family, cex) { tk.fonts <- as.character(tkfont.names()) if (as.character(font) %in% tk.fonts) { ## already defined Tk font as.character(font) } else { ## we create a font from familiy, font & cex font <- as.numeric(font) family <- as.character(family) cex <- as.numeric(cex) ## multiple sizes if (length(cex) > 1) { return(sapply(cex, .tkplot.convert.font, font=font, family=family)) } ## set slant & weight if (font==2) { slant <- "roman" weight <- "bold" } else if (font==3) { slant <- "italic" weight <- "normal" } else if (font==4) { slant <- "italic" weight <- "bold" } else { slant <- "roman" weight <- "normal" } ## set tkfamily if (family=="symbol" || font==5) { tkfamily <- "symbol" } else if (family=="serif") { tkfamily <- "Times" } else if (family=="sans") { tkfamily <- "Helvetica" } else if (family=="mono") { tkfamily <- "Courier" } else { ## pass the family and see what happens tkfamily <- family } newfont <- tkfont.create(family=tkfamily, slant=slant, weight=weight, size=as.integer(12*cex)) as.character(newfont) } } i.tkplot.get.edge.lty <- function(edge.lty) { if (is.numeric(edge.lty)) { lty <- c( " ", "", "-", ".", "-.", "--", "--.") edge.lty <- lty[edge.lty %% 7 + 1] } else if (is.character(edge.lty)) { wh <- edge.lty %in% c("blank", "solid", "dashed", "dotted", "dotdash", "longdash", "twodash") lty <- c( " ", "", "-", ".", "-.", "--", "--.") names(lty) <- c("blank", "solid", "dashed", "dotted", "dotdash", "longdash", "twodash") edge.lty[wh] <- lty[ edge.lty[wh] ] } edge.lty } igraph/MD50000644000176000001440000015116512325555115012101 0ustar ripleyusers99b8be64f349b103adc8cb859338b1ca *AUTHORS bc5a02c974ba8f35a3d5753a4c841ae5 *DESCRIPTION 16c0850ef35f8fdea0c5409562fb5069 *NAMESPACE f435d6ed602b117d12286c24f6bb9047 *NEWS 5296a012c71b4692c4eafbc82b0d8a57 *R/attributes.R 8c383a3f7515998e5351efc574acb3e2 *R/auto.R d41d8cd98f00b204e9800998ecf8427e *R/auto.R.in 99b409e71bd977062cbd91aec4c15e2f *R/basic.R f527380a4b278b4f0ca91f1334692268 *R/bipartite.R da58f707c3559bda253777edf5e23995 *R/centrality.R d1673cd562b2af755729006c6ce3d814 *R/cliques.R e361e9c71f18f8a9adb41beaca81ccc8 *R/cocitation.R 7bdd13dfb755b3284bce50ef437d4cc2 *R/cohesive.blocks.R 63c2288e562bf2cd6975c2091f2f414d *R/community.R d13e1846bbb660dd08b8965a600c4211 *R/components.R e1d90db3a6516c67633ef9e8ea210053 *R/console.R 3ab675564f4d53c736fb80a8b1da4183 *R/conversion.R 4bb32ac02f1f75c66028845f53f6d950 *R/decomposition.R 238adf54a30725ce601625e44766fa96 *R/demo.R b2fba3f8858d4d96d2f44c53aa06bf84 *R/epi.R c77c162cd280ab90988de2e85150e249 *R/fit.R c5804dc24c3091d1c5b2a5f1c72b0fc5 *R/flow.R e666b6d8c7368a0ca4aab432bef3f5fb *R/foreign.R d2de1cf3c8681cb56aa76a062fa08d21 *R/games.R 9bb72b81a8881a9bb3edda3284391f3d *R/glet.R 1482c9aba048986332e830a0a6e6022d *R/hrg.R 3ec59ac2bbceb42302d9e382e4601f11 *R/indexing.R 1d2e8b3184b251ca8f35f6201b818bfc *R/interface.R a61ca8680ccdf5e3c7e761ebb5ecc0bc *R/iterators.R 76dee45ded697e86b4a3de83a88e9731 *R/layout.R 7423ba2fe38652b9a4e16a2e0375cfe2 *R/minimum.spanning.tree.R 0a427902da9281722a41fb6053ba259b *R/motifs.R a894950219f4b721ba0b276aefbc6022 *R/nexus.R 79816363395853ad7da863c1b6dc615d *R/operators.R db590b82e87c2d39aa69bfe2d1c42ad0 *R/other.R 187d948faa58e01dc56bcc8b1f6ac0fb *R/package.R 36ba81d919ae2550997665b94f72bcf3 *R/par.R dfd92782b7c109dc78cafd1093149f9e *R/plot.R 9acf307c396d4883fed103825a780fa9 *R/plot.common.R cc3648a20be58b52519d515a49a64b09 *R/plot.shapes.R 0833171a8c2eaf76a0e8e57c8bbfaec0 *R/print.R c376415cd72d4ef2d3617e156976a704 *R/revolver.R 4edd8637f3529608c8230be1b637f598 *R/scg.R 1198418ab2a1d9e8f611eaeb04967650 *R/socnet.R fa788f7f9a26d053131e76c740d365db *R/sparsedf.R f2701fc19142703f08279dbbd1466e86 *R/structural.properties.R f2de94e2a540bd58cd3b1dd9e36fc29c *R/structure.generators.R 5e3734f8f19d4c68b53fee8943f3421e *R/structure.info.R b48350454f84ceede2793510bbc42df7 *R/test.R 6c4111c900e2f45486aa982d981d4033 *R/tkplot.R 570b94fbf8b9cb6846f0363107e61dfa *R/topology.R 85236ab7181493ccea9cdfc495587a89 *configure 86216451555b0f3a4e50740d9c51c4fd *configure.in d41d8cd98f00b204e9800998ecf8427e *configure.win f6d91efbe9a92d9da367b19ae2b77d48 *demo/00Index 1b36208ff2a1e5ff5251d56bb592a3bf *demo/centrality.R 8885f3a265bf4d3b61ead4fa8c8baaa4 *demo/cohesive.R 00cfe20fc1f83f6a06d6ae5a513da990 *demo/community.R c02d2d3f119cb3f083b057b8e8662bec *demo/crashR.R 8b40827212837f6f3950b23dfb78798a *demo/hrg.R 222af9cf74ba2a4e7bfc2d44f2d4e6df *demo/smallworld.R 99b8be64f349b103adc8cb859338b1ca *inst/AUTHORS 5cc5f3e2c4239fe9a47bfefdbb923dd0 *inst/CITATION be1c160f903340c69231c009998d9c11 *inst/benchmarks/time_call.R e9b385aab8ca83ec08312329dc2a6a0c *inst/benchmarks/time_sir.R 9f15757aae0fc33c5e1c8fd7546173a1 *inst/html_library.license.terms 9a1b851f5373287728fa8fbe7b3f67f0 *inst/html_library.tcl 4fb9f93f54eeef966f8259609247a787 *inst/igraph.gif c07ee84ac019e590ef906200e5183ea2 *inst/igraph2.gif 87bc6231efc284995f9d609715ea4d2f *inst/my_html_library.tcl 3a5ec4e3cc8d87587b31237c5541f86e *inst/tests/celegansneural.gml.gz 4e7cca203677fa3cb785b3f19092ccae *inst/tests/dyad.census.R 34d03342fd57be47bdcf3079baea1efa *inst/tests/football.gml.gz 157659d4b0f46abf1b2893b2e52a9fc1 *inst/tests/power.gml.gz d31d406a0437314ee55bcfdc1ce329b1 *inst/tests/test_add.edges.R 92b1309d632744e902b344e0cf844afd *inst/tests/test_add.vertices.R e20404df1236cbc6b197a7625d3955ef *inst/tests/test_all.st.cuts.R 4b654460652b989dadcb8414572be41f *inst/tests/test_alpha.centrality.R 344c79c5412c6f0034e6c34fe2f01777 *inst/tests/test_are.connected.R 2c7d3db034f04519e6645a94ac05cd78 *inst/tests/test_arpack.R bb7f261a4c2f80c8119bb826e3229190 *inst/tests/test_articulation.points.R 168eb57ca4b9e4f5acbb5f39023bad70 *inst/tests/test_as.directed.R 195fe109313388d97342ff6c4f029072 *inst/tests/test_as.undirected.R c25d797aa0a639d42f11c929a14a0acd *inst/tests/test_assortativity.R 7fa4f73022e6e9d69a39b65f1eec62a3 *inst/tests/test_attributes.R 338afca5b3a3b295dd28d39d42a8129b *inst/tests/test_authority.score.R 4e5736a68e3f63c407ebdfaecdfb5962 *inst/tests/test_average.path.length.R b9a6f87f9fb6ce050b033d81e07d182a *inst/tests/test_ba.game.R bf992a5333d87b2167909dd241122405 *inst/tests/test_betweenness.R 2e7e4b60e4a660159f12d3aaa6b2d527 *inst/tests/test_biconnected.components.R a1539c6a9fb97f13004172a0cecb4892 *inst/tests/test_bipartite.projection.R 543e658b701f078e4d5df9459afa7f4a *inst/tests/test_bipartite.random.game.R aaf49d751e5b3f9f309c8abb41f760b8 *inst/tests/test_bonpow.R 09dffc611cdd0e4d032d72d0a6cd7e1e *inst/tests/test_bug-1019624.R bf5025d43c2e00b64664512739fcc48d *inst/tests/test_bug-1032819.R 4b3e78e53d1791833f32768b4cfac5b5 *inst/tests/test_bug-1033045.R efe6e3f58002704896e364ecd4d8f693 *inst/tests/test_bug-1073705-indexing.R 43ff7cc288aac3bf6a177299a6dce4c7 *inst/tests/test_bug-1073800-clique.R 298abe140033edb6f42098cff366b810 *inst/tests/test_callbacks.R ccfa3a1bab225fa5c485c315166b8477 *inst/tests/test_canonical.permutation.R 2125354fe142cfac984704d2de89a647 *inst/tests/test_cliques.R 1f76b5fab1c5e3cdb562fcd564bbfe34 *inst/tests/test_closeness.R 6c38899d657a98820a069c9d4f7e8cc7 *inst/tests/test_clusters.R dc291e1bc4fc120ad1cfbdf55d335044 *inst/tests/test_communities.R 039ed2f30198a5e14a062ff8358750bc *inst/tests/test_constraint.R 0ae5c465092f6fb1a24cdc66882b0e2b *inst/tests/test_contract.vertices.R 80c5de9b0a8e14071965892dc346c722 *inst/tests/test_count.multiple.R 5cb21068d61ea3ea54db0fa60e7031b3 *inst/tests/test_decompose.graph.R dae8990a3234284931b15cda42742d93 *inst/tests/test_degree.R 604e38fc4c00f8dee757342b186dbd76 *inst/tests/test_degree.sequence.game.R 9e95da90e0ef279b01ceb60fdf2f0924 *inst/tests/test_delete.edges.R 119c85d94d9b5fe56999cca1f349699d *inst/tests/test_delete.vertices.R bccf336aef04efe0df1c4420b8f6b4ec *inst/tests/test_diameter.R 87b65f39adf2e302ba41ee8c6926be2c *inst/tests/test_dominator.tree.R fe8993633f7bfff4c992e22275194265 *inst/tests/test_dyad.census.R e6220f3a324286fb9c0feb98eb5d3ccb *inst/tests/test_edge.betweenness.R 6bd7e827ce0cdb40eb04689c32956990 *inst/tests/test_edge.betweenness.community.R 5c58d100384eab89fd2a9e92130eb98c *inst/tests/test_edge.connectivity.R 0b33f65818398701019aa1a75fb48399 *inst/tests/test_edgenames.R 39d23cce35206c1c511132d9a94421c1 *inst/tests/test_evcent.R 550b55509c1fbb72cc8a839c22c58452 *inst/tests/test_fartherst.nodes.R 72dc7605af2bc9ad725d90117f95cad6 *inst/tests/test_fastgreedy.community.R c149cb204bd0d601fee3c51952597874 *inst/tests/test_forestfire.R f56a4d780b739b85f63ab42afc5fa543 *inst/tests/test_get.adjacency.R 45d06dee3e4b3f53287c0bf0f2cc6ae0 *inst/tests/test_get.adjlist.R d71f19e9c211368a5d9e266c698c293d *inst/tests/test_get.all.shortest.paths.R ed0a610924d15a6fc9896186da532ee9 *inst/tests/test_get.diameter.R 7d58dc19fcbe87981f4c4e0388eeafba *inst/tests/test_get.edge.R d196bde121b88bb853af41965d9a7c7c *inst/tests/test_get.edgelist.R ee55e35eaa74ce819bccff8eb11e2f85 *inst/tests/test_get.incidence.R 57457d94d1faa7326b14afab5587fd44 *inst/tests/test_get.shortest.paths.R 04afa867bee1c9f411c914f5cc883744 *inst/tests/test_girth.R 8e5983e030da6871765e3566ce833912 *inst/tests/test_graph.adhesion.R c555ea15e5dfac6bd814096b52858e75 *inst/tests/test_graph.adjacency.R 05a2e3cdeb95b210145ae701a7b98e8d *inst/tests/test_graph.adjlist.R 9ba1d7ad6342b0bef83bf1aaa52aae50 *inst/tests/test_graph.atlas.R 7b2b92d896ca4a0d29460d53dacf8f9d *inst/tests/test_graph.bfs.R 9f7cda832318e117e4d721c155dbd8ed *inst/tests/test_graph.bipartite.R f31c3eb5335f68d2bd8bbe924ca1a383 *inst/tests/test_graph.complementer.R a491210160c084f705849ae4bcf90a5c *inst/tests/test_graph.compose.R 6367eea35161330f9b1ecefc7f36bbc8 *inst/tests/test_graph.coreness.R e2d388583de8f401529d0e873249b8b0 *inst/tests/test_graph.data.frame.R f8906cacaf642693784fde7450cfc61e *inst/tests/test_graph.de.bruijn.R f17e8a4d137c862a3aa739b95b79b9e5 *inst/tests/test_graph.density.R 7a008546216365c194fe810dfbd8096a *inst/tests/test_graph.edgelist.R c3ec8547742438391412972e60e790e1 *inst/tests/test_graph.eigen.R 7814cb971ce14d84b3eb376cab11f9c6 *inst/tests/test_graph.formula.R e0f44e4581e777f7d80c5a518808434f *inst/tests/test_graph.isoclass.R 9ef58fbedec877449ea7d1701cea9713 *inst/tests/test_graph.kautz.R 076362c5d606b06989a6213c88510c94 *inst/tests/test_graph.knn.R cec9f04f82187c5b4ad58349426c5699 *inst/tests/test_graph.maxflow.R 4214d39292af7c09dded667eb04f6c74 *inst/tests/test_graph.mincut.R d9514f94fb33a8c1cb20ab6360a2ebfd *inst/tests/test_graph.subisomorphic.lad.R 40249e1c6bc0f92005a4cd0398ea3d3a *inst/tests/test_graph.subisomorphic.vf2.R 88d077e7f4fd460fd1a5fd4666897221 *inst/tests/test_graphNEL.R 3b881412a68838d077433f9b8fc141b4 *inst/tests/test_graphlets.R c4c1dc078239cf57d31d2e93a152c7e5 *inst/tests/test_hrg.R ce2cf585ab05171852750dc56ee6b215 *inst/tests/test_igraph.options.R 7e613d55a1d6432ed49f30b2483600f8 *inst/tests/test_independent.vertex.sets.R 6024f685d222d59eb7ec1914eba0a7e8 *inst/tests/test_indexing.R 75680864fda91a0e688eca440e3205a0 *inst/tests/test_indexing2.R 0e4d57dbc7ce7ac7eec5cec1e4c93079 *inst/tests/test_is.bipartite.R 5e0efc89b358f7c01d5dc9168c4f88db *inst/tests/test_is.chordal.R c34c21011d44938e3fd4086a01e92555 *inst/tests/test_iterators.R 8957faf40bf0e046e585ed3a3cbf9332 *inst/tests/test_label.propagation.community.R 04fd308d6a7daeefb0cd5d49ecd376cd *inst/tests/test_largest.cliques.R 24b9cf59e40c52490bfb5116f0fb9237 *inst/tests/test_largest.independent.vertex.sets.R 93a2aabed97712862e9463458b464244 *inst/tests/test_layout.mds.R a7a5593903397253275e6279b015a608 *inst/tests/test_layout.merge.R ea94d80fada25fccafc16102c39694f6 *inst/tests/test_leading.eigenvector.community.R bbd2ed78ca4760d764865fabd1f80924 *inst/tests/test_maximal_cliques.R 0ba3066de2bd850da7d6215924d0b718 *inst/tests/test_minimal.st.separators.R 2696f5672ca0a5d073c6ad11c2f66a1e *inst/tests/test_minimum.size.separators.R 697b0e3793cdf91fbdb7d93200503f69 *inst/tests/test_modularity_matrix.R 91d2b6f3de45f27700c0ebb4ec9a9e2b *inst/tests/test_motifs.R 25cec45c57960f0ad8b18575d4802e53 *inst/tests/test_multilevel.community.R 2695c3e58891d31786d82a39b80f3e27 *inst/tests/test_neighborhood.R 238412bcc9a6f6276f36f00ee5af64a0 *inst/tests/test_neighbors.R fd7b4045a32f44c6ed86c6ff35703474 *inst/tests/test_operators.R e2053cd8e5fe548a3f52b62cc64c2d17 *inst/tests/test_operators3.R 4a6fc11bf464763d6423bdbc1f0b5b5e *inst/tests/test_operators4.R b3dbbd6857e5669268fafab10b5de9e3 *inst/tests/test_optimal.community.R 61197f2d34abcdf1d90ad312a846575b *inst/tests/test_pajek.R 2f2bd04fc787c8a03aa62ec2a5e1ae96 *inst/tests/test_print.R aff3f8aee89ec2151834aef5a203125f *inst/tests/test_psumtree.R 8b85d86e270b64291868a4cbb9e0f9a8 *inst/tests/test_sbm.game.R 9691d0d61689818c880138630bc1ce8f *inst/tests/test_scg.R e7a3fbd5a979efeb9279505dc836316d *inst/tests/test_sdf.R 054fea42e0dc8d2c724181d64a222535 *inst/tests/test_sir.R dbf59cf415e1b750481f1d594afe8a10 *inst/tests/test_transitivity.R ccb4118a2fc7ce8921ad7c2d0833c57d *inst/tests/test_unfold.tree.R 0070fa164ebeba8f1e241aa02a35161b *inst/tests/test_walktrap.community.R a1c8fe35ba8ee6dc6cb8243fe3edb069 *inst/tests/test_watts.strogatz.game.R 2521dfd3cdd6457b8947f9bd78094b48 *inst/tkigraph_help/communities.html b836ba1ad253b94013d430cf0ba3e7c2 *inst/tkigraph_help/index.html 11db5e3c8a97b9b22418aecf954b961a *inst/tkigraph_help/style.css 4cbf1010c7a8edb8b8576853eb8898d6 *inst/tkigraph_help/tkigraph-main.gif 490e59e4c25b5d1117d496e45360f7a5 *man/SCGintro.Rd 0113551b0eda6a6798ff87c2cbd3f4a6 *man/aaa-igraph-package.Rd acc8922a7d352edde749e9c70d5a0ebe *man/adjacent.triangles.Rd c2b28b9762e007731552029cf28b534a *man/aging.prefatt.game.Rd 09245948cf7622f1d8fce75dd904f644 *man/all.st.cuts.Rd fb3c3ca851721dbcd8e07eab5d9c4803 *man/all.st.mincuts.Rd a991620f8a233c0a4825c264f5af64e2 *man/alpha.centrality.Rd b7bac114fce81d2283e85b6cded01a36 *man/arpack.Rd e34d1c3782098341b133161a938ec14f *man/articulation.points.Rd f24e48da6348b734da938856ca6d1100 *man/as.directed.Rd 8d3bb408a3c0d29df5341c113cc4c5b6 *man/as.igraph.Rd cd28abd98686de539361bd269bffd446 *man/assortativity.Rd 9ec098103489e86877ceabf3a71a699b *man/attribute.combination.Rd b0fa4ce312607473d91fdbe3e6bdb821 *man/attributes.Rd f5bde797cf09b2c401dd32f111ad85e2 *man/autocurve.edges.Rd 4354d5df9f52e6fbc69ec249e402e4a5 *man/ba.game.Rd 46e4de4c7fba89dff11b32364ab3beea *man/betweenness.Rd 1d2fbdf761aae74bb37b7bab64f5e182 *man/biconnected.components.Rd 3c9265632a81a664f3bf3d90a7f8e26a *man/bipartite.projection.Rd 07232859d44dd80d2539177d11c9a3f5 *man/bipartite.random.game.Rd 44717817da10707585703923d9e80fb2 *man/bonpow.Rd 7f34f8c90526469b80f4954be778da38 *man/canonical.permutation.Rd 0a4cf8835c11767fec9b683e3a27f958 *man/centralization.Rd 06c415a0840c9beac51fa27db0826b01 *man/cliques.Rd 9ee6a34b3a9d625396d53a38b353ad17 *man/closeness.Rd 4ea4827d8f9120e66dc459f204135c32 *man/clusters.Rd 62ffba3e1c39a99270d5c3a3b5cd9c7e *man/cocitation.Rd 993b89ee61989b1673c3b335f87218ae *man/cohesive.blocks.Rd 4073dc3d5bf31010638608e8ce4bfc4e *man/communities.Rd 8b79265c9f5a14989606ebc0ad804845 *man/community.edge.betweenness.Rd e22a859e7d9e48dca8d07e6d6378a283 *man/community.structure.Rd c3f6f238760ca3b9a059d9581e520ace *man/compare.communities.Rd 908778aa0d5994f377306caa1408f090 *man/components.Rd 1590bd8ff482d89faf8e014a38976109 *man/constraint.Rd 86577a79e646341d55d357b29b2c72ab *man/contract.vertices.Rd 438df04d7dc69a768a9817535ca287e8 *man/conversion.Rd 22358ede3d6f988629f1beb8a8ee6828 *man/convex.hull.Rd fba08b66e8506b092a6b55858d4532b7 *man/decompose.graph.Rd ff7928f64c74493af5ec121faafb5076 *man/degree.Rd b708da4742c6f8eff8a303d10f83b6a0 *man/degree.sequence.game.Rd 32174a837eb0a219ca36c7eb7b5f11e7 *man/dendPlot.Rd c805d22b79e24625a6c367742a64ea49 *man/dendPlot.communities.Rd ca95ec72c1fe4f41b7ef4cb3115ab00f *man/dendPlot.igraphHRG.Rd 0be9392f822f6216444317f74f5ce3e7 *man/diameter.Rd 71524ce77088cfcd7d9056483be0f508 *man/dominator.tree.Rd a52ef84d749ac079f82e1ca08ce8d699 *man/dyad.census.Rd 22cad0ed8f6ac4610066ac305d4ad870 *man/eccentricity.Rd b16ce3410809a4147d6f24b3241d1ddf *man/edge.connectivity.Rd a05ddd762855b8abe5a36f628c99e5e0 *man/erdos.renyi.game.Rd 53b55c90c42137ba6288d972bef78776 *man/evcent.Rd 3b1106d3592ca518754c952b031f6129 *man/evolver.Rd 31da869fc76ffbde2d38e295ef6b833f *man/fastgreedy.community.Rd d5c7d58e5b700a33b3350426bd68107f *man/forest.fire.game.Rd f217e124bd51f125eda133685863d207 *man/get.adjlist.Rd bcc35fc622c4deffb1d0c80e9c994db3 *man/get.edge.ids.Rd f26ceb99f240c3639d1990fee544611c *man/get.incidence.Rd 7bcba0893e491f89658fc5d4a088e37b *man/girth.Rd 8e71d93212eb276333fa49122fba6962 *man/graph.adjacency.Rd fd1e9140bc3a05dd9a798fb2af0e372d *man/graph.adjlist.Rd 01aa54eb9c9ce6ebd248fef4a180109d *man/graph.automorphisms.Rd 6703b7e69298233f17ff00ed119552ea *man/graph.bfs.Rd 9233c293d35c2d67ad468681190c0244 *man/graph.bipartite.Rd 1a6954216d4c3f51102b9ae14dc31a58 *man/graph.complementer.Rd 999533f3e566009237a4192b159f9645 *man/graph.compose.Rd 1fae7916c9207e10e666a486eb1da27c *man/graph.constructors.Rd 5a74dbf9027ea59b88f1d471ba2fd3c9 *man/graph.data.frame.Rd 2f9dca685fdf7c4bf4cecbd6c834843c *man/graph.de.bruijn.Rd 4aa7ba2d0be3d6f76e9edbd3e5e1b21c *man/graph.density.Rd fc4a39e6adbd5b47530150a3a36bd4e6 *man/graph.dfs.Rd 63857044820f4d4b9304b562eb3e14a6 *man/graph.difference.Rd cc3b2c1d3af65543a55339a199f82535 *man/graph.disjoint.union.Rd 7a0ba30d43312e489f59bf7ac24ce8cf *man/graph.diversity.Rd b6223d79764a2a392818d553cec7e2b1 *man/graph.eigen.Rd b2c65bb8ffa89b27a8d3ed715951da77 *man/graph.famous.Rd 678fcd111352b7755b544ad854478583 *man/graph.formula.Rd 737f1b74fa5d9645f8dc4642ad480be7 *man/graph.full.bipartite.Rd 00d2fcf4e8f9a70c11abfa0131a9b417 *man/graph.graphdb.Rd 64c8830f9eeb9c16f8b6cc6857a2e0ce *man/graph.incidence.Rd a9bb06285b3fb1dd1180cd881f173db7 *man/graph.intersection.Rd a58924281b2ee65e8753f8610c1b08dc *man/graph.isomorphism.Rd 16564b70c1f7d0f42e25c78885ee4fac *man/graph.kautz.Rd 94c9486e89c543296874572edc63c825 *man/graph.kcores.Rd 795059f08a847efb459f5def06ec61b7 *man/graph.knn.Rd 687e881b37044914d3d6000ef9764ed9 *man/graph.laplacian.Rd 1983f5ad22f00b21fc4d50c996e02496 *man/graph.lcf.Rd 9ad24c3a74a39bc11d5569c6940c5091 *man/graph.maxflow.Rd 95020ecaf81290a8b1dd943ee54fdb84 *man/graph.motifs.Rd 119a74bc57ed38322380c56a866ce3bc *man/graph.strength.Rd 8308256a1ade3ab6997801297bb13652 *man/graph.structure.Rd d91b16919bead07805c5ea80995c7ba9 *man/graph.union.Rd 99a1a3f17338172c65a89c0d33925f58 *man/graphNEL.Rd 984f9aa7375b5711a8aab5281b6d9077 *man/graphlets.Rd a9cb025e4eb3379ba88aeb62fee2bf3a *man/grg.game.Rd 4c61d41f4560b7eedaa05aacfbedc387 *man/growing.random.game.Rd c61a5eeb50205147a45e629d6a252171 *man/hrg.Rd c3cc06fcff62664939e29d6e480fef22 *man/igraph.console.Rd 9748f1acad017fe19c86313f2b27c73e *man/igraph.par.Rd 59fa45e754df67baff9e59e047d176b6 *man/igraph.sample.Rd 221ee11aa9ed0250d6cc653b6ef03f90 *man/igraph.version.Rd 129c263e141dd36fc2f0f3960a0ededa *man/igraph.vertex.shapes.Rd d03bd59be3c1ea2762f726250fd659c1 *man/igraphdemo.Rd 29a8baed67540673caaa8c83cb37b2eb *man/igraphtest.Rd bec1565476a4fd6b341390673df536ff *man/independent.sets.Rd eaba27a168cbc50a031e7c651761f11c *man/infomap.Rd 7e64749dd773129c2ab536c7c46fcd01 *man/interconnected.islands.Rd 6be427a7a1acdb14f2532900249c3ca7 *man/is.bipartite.Rd 08403ce9e5ddf172669ce3601ee2f834 *man/is.chordal.Rd f93eb0c15fad1c3d46f842889bef764b *man/is.dag.Rd 00191dad9c675a906eee5f413db2ce2f *man/is.degree.sequence.Rd 9cf012f43a340967e4be995cea0d5ce5 *man/is.graph.Rd 6d019435258e40157a1b65a1c9c7570e *man/is.multiple.Rd 6743daccfdc2222d9ecd2811d5072356 *man/is.mutual.Rd c0691c04238439c9a1bd2a918421d854 *man/is.named.Rd f9ffb5c80cdd2e34e059ebfaab1b614e *man/is.separator.Rd 5e20d1fc01c29cc4c85e988f5c051aca *man/is.weighted.Rd 273b4fa0739aa8e31cccb3f256f98da9 *man/iterators.Rd 82e94991ac7d85ea10dc0f6fa64f7afc *man/k.regular.game.Rd 81cef44a4887b1eeb367fefcfd36dcbe *man/kleinberg.Rd 57c6f91a6f4ef7de8bbb552e81335947 *man/label.propagation.community.Rd c68dab75e67ec060291ef25f3a0d59f7 *man/layout.Rd d7c813bba7dfe1fa64a9a535e89b9311 *man/layout.bipartite.Rd 7f1debc3cb710724d7509cb38319a267 *man/layout.drl.Rd 4eeb14a71b7740d8969248d5b89a7665 *man/layout.grid.Rd abf2f5674abc7c049f74baba093d7822 *man/layout.mds.Rd 449b8cdd626361e2e5b3b86378d5a6fc *man/layout.merge.Rd abd58c7f5864e9b7d0b6f8ec256a6216 *man/layout.star.Rd 58e7a514dbe8ca8077cf1f53757d96b9 *man/layout.sugiyama.Rd a7e85e5a0714ab6cba57af818310a119 *man/leading.eigenvector.Rd f2f70629a861eb66d4c205bee1c503b1 *man/line.graph.Rd 78b1b429c7c1a7c333619b2483633904 *man/matching.Rd 60a0337a59ae28708a3d78c48c5c64a1 *man/maximum.cardinality.search.Rd 89e5c3eef4457aaa932aeee9b032f2e3 *man/minimal.st.separators.Rd 842a32dbc4cb921897f6d9bcac0b9255 *man/minimum.size.separators.Rd 52ee27b0f5bf3e04e7be9e878725a2cf *man/minimum.spanning.tree.Rd 52c8ab132a6966739bef8ee2f8981756 *man/modularity.Rd f560cf7c63099dfdd3b0a674275e316e *man/multilevel.community.Rd 7900bed1b0940ccc7776d3dec5eaf8b3 *man/neighborhood.Rd 1ec57ad12a1927136903ec84dece1d8e *man/nexus.Rd 299d7851220ad2eb4ced185feea58a70 *man/optimal.community.Rd 4d2f3c8e437282aa798c8066943d869a *man/page.rank.Rd 288a40186ba8f7078d043476237c05be *man/permute.vertices.Rd 358552108b31e3d75f36bd12a184cb5f *man/plot.common.Rd aeb9b7b19f32c2b8c00c366ef1cd03b1 *man/plot.graph.Rd d24bd1f85b5dff80683020d660d86624 *man/plot.sir.Rd 7c219a4fc350e53ef87bcc0c042c9234 *man/power.law.fit.Rd 5d6296b70d49f683d351b458d70f8943 *man/preference.game.Rd cd164cdfbcf792e4fc29a6a45228640d *man/print.graph.Rd 1383336495f05be66550a63cf583ec55 *man/read.graph.Rd 77e606ba497c192295bb2ad7515e5e9a *man/reciprocity.Rd b1f605b0ec7c78115abe2b0a87d3eedc *man/rewire.Rd 434d161208d569515f7a42a6148f6574 *man/rewire.edges.Rd 39a855aeb28eb15d3b3f2ab3fa9d1eb0 *man/rglplot.Rd 952c0bfb97bd3832323ccdbfad724d35 *man/running.mean.Rd 92b9c05fdd87fd53d127f437b6bce8be *man/sbm.game.Rd d63bd966921d98f2c4965dd169d66b59 *man/scg.Rd ad681e1cc6ffad6b7dc81716f83e8ff5 *man/scg.grouping.Rd 59d41f382e4326c1292b15e4847cf9be *man/scg_extra.Rd db1c37e60a394104fb771cb4096ec635 *man/semiProjectors.Rd f70e82a7d81dfa5b609ceb5419dbc3f1 *man/shortest.paths.Rd a679be4e396aa6c03503e4ffb9c0ccd8 *man/similarity.Rd a01c751a108c457f3760db1038ad204a *man/simplify.Rd a2641565bc5a23b767234cf5292a17f1 *man/sir.Rd 882887a6ce4a57d7934bd32121a95bb2 *man/spinglass.community.Rd 4cca0ec13649f704220ddb041ecbb6cc *man/srand.Rd 3bdfb6457757014caaf1d85cc9b8cea8 *man/static.fitness.game.Rd b6398eb1622f9521f71b32b880964f1d *man/static.power.law.game.Rd de8724b8505aec8c7a81cd821d53cb77 *man/stochasticMatrix.Rd a203c69759d100b84cf3afea2ea8bf47 *man/structure.info.Rd e9e0874c3509e622902d9b64a6c16ee2 *man/subgraph.Rd 56e5286d57cbb4daf39e353a90d42a3b *man/subgraph.centrality.Rd b0248ac705603d8eca7e256bb54f9c5f *man/tkigraph.Rd d20eee7198ad87986ff574b6c6d27857 *man/tkplot.Rd 7c42e19c656b1139263d4ffaef34ed36 *man/topological.sort.Rd dd16887c31810542bd53c00c2deee88b *man/traits.Rd 98fee1818998ec54602d511b3a09ffd3 *man/transitivity.Rd 6d76bb6086d6d6df07d117f862006598 *man/triad.census.Rd c7c363024814476cd2d56289bbbf9a0a *man/undocumented.Rd 61076d6735b29e2fc094b02a6d767673 *man/unfold.tree.Rd 3cacf42c3bfaca2bd7252076c56bd9e6 *man/vertex.connectivity.Rd 9ac174cab048d5b9bb865bf3621862c3 *man/vertex.shape.pie.Rd c3dacc31658bd53c75b5cd77b9a634d8 *man/walktrap.community.Rd 317908ae26b0bd17a7300d2d8d1a0a58 *man/watts.strogatz.game.Rd 184e95f181ede0924a808e7e79316109 *man/write.graph.Rd af447f07a45af2b4f7edaee5d0a877a7 *src/Color.cpp be39147aa9a658a401d5d8e304bfbb68 *src/Color.h e9746e10e53c52a8e9468e81ac0e9fe2 *src/DensityGrid.cpp 11b90b027549d524c4e8af3cbbfcc307 *src/DensityGrid.h fc67113e1f6fbfdcda59cf39aab3ff00 *src/DensityGrid_3d.cpp 5cfb53cff37ca7d43a52db4f8e44ba94 *src/DensityGrid_3d.h 605d507fd74ae304c92e9a08d23443fa *src/Light.cpp a06dcdf977661620d9a30542d0708979 *src/Light.h 40c74a5c37746c3aa29690de80b78c34 *src/Makevars.in 39e80b3b5eb15c30c370e8e5d172b813 *src/Makevars.win 47ce39714f6f9d6ec3bd7c91624d87f4 *src/NetDataTypes.cpp 69166cb2f393e3ab50bebc72b16d3f7e *src/NetDataTypes.h 19c564585ab9700eb4356ee8eeb32403 *src/NetRoutines.cpp 6d28d398db2ae73d18c6c0448b55b8ba *src/NetRoutines.h b9106690e86aab37621da51d42ba6673 *src/Point.cpp a62fbead2d3a236d5303cc9085c8b2eb *src/Point.h 8106fda17eb6d6f99a80f111d30d3ac5 *src/RIgraphRay.cpp a799c64d3087459d00e419f3fe8d6570 *src/Ray.cpp 2e3885be19867ed6a3562aeadf9e5271 *src/Ray.h 1feae5499e54a7b2015c14e67dbf25e7 *src/RayTracer.cpp 79ce54bb866d3c8341d6ca072b034bb3 *src/RayTracer.h c5e9fe64aa620a4c2578d84ce4eb3a69 *src/RayVector.cpp 61172ce5b49dfa8b864abcfb8808d5bc *src/RayVector.h bd7cf3fc7d493820b559dc1a65b6736a *src/Shape.cpp e8afe23482477c8dc53db328272ccd7e *src/Shape.h 5c198eab1fe06123c45405eaa1a09200 *src/Sphere.cpp a4e697cdced0ed1c4d1caf55e7dba557 *src/Sphere.h dc312c8337bad1b31afdf169f3e9e194 *src/Triangle.cpp c422300a2a528aa3da4ec98ed2dd1c8f *src/Triangle.h b0bcf8e1b1af2cdebe701b34a00f39f8 *src/adjlist.c 0e443173d6720b1300cf605bbaece04b *src/amd_1.c 61f51178cc4c8cdc142f77870d973e37 *src/amd_2.c 08554ac890dcb81c2b1158c9be01a31b *src/amd_aat.c da7c5120c89bb6c50a71d9826416b8ca *src/amd_control.c 49e3189ef6df390a6ee6d32efcf37d2c *src/amd_defaults.c 7a507df8b9319bcd17a6cad1dbda3fa7 *src/amd_dump.c 63b58adb1033575a7733723f79b923d0 *src/amd_info.c a1c04b302dbe9454bd0555476ca97077 *src/amd_order.c 5a26434fda9351c3cd2c440f09e3d031 *src/amd_post_tree.c b82d8ed7fea392e568f4fd8962cbb928 *src/amd_postorder.c 2def6a2ca54e5d6ceccf7bdf1cdf5a7d *src/amd_preprocess.c 44c8934cd1690b307fb15b90a66924f3 *src/amd_valid.c cd9fc819a7108d5bd6e3df27bc574f8f *src/arpack.c 84d831cf06a12ccddc98d577cf34638f *src/array.c 28757abe7ee47a2a3752132e6521dbe7 *src/array.pmt 46ebbbc9a7d738bc44853dfa2125c4eb *src/atlas-edges.h f94137db5d049089a48e8542b40679c8 *src/atlas.c 971302943c0b86513f8d197306d6855d *src/attributes.c 7f8eedd101f3f975de419df961bc789d *src/basic_query.c 8cdb18f2170bd6eb311cb7ac9b12944f *src/bfgs.c 86b692ba9ac8f2d08ccda011c318ae3a *src/bigint.c 976d10cf54420e8a8f2fc9f6e142d8fa *src/bigint.h ba0b88f5f1576546416246735befd49e *src/bignum.c 29893b4374ace98d58ab689ea1db4540 *src/bignum.h e8e1e6680b83ac2a2e4fdae9eb27e20c *src/bipartite.c bf276dc5fef4209096cc86529ee5eb6f *src/blas.c ce7bc18dbcd8c9b52277ac9e716040f4 *src/bliss.cc d1ea0ed591a4f82f6d1d0e04cbfd1cbb *src/bliss_bignum.hh bfed152afaeef7400072c3a9f959fdd8 *src/bliss_defs.hh 7778a577e9617940886ea24bb867caa2 *src/bliss_eqrefhash.cc 3f648579754a81b62cb537ce3e58bb7b *src/bliss_eqrefhash.hh b3ec002911064b0af0e307619040f2a0 *src/bliss_graph.cc d3be042bb3c162febb499d49193a5fc2 *src/bliss_graph.hh f1db9f7971b2bd2f3bfad2a44b45d223 *src/bliss_heap.cc 8d97ac4d664820e45d31dc13cc5b0a57 *src/bliss_heap.hh e4e77314853892222e7f68ead863a590 *src/bliss_kqueue.hh 021cc0c168fe6b164c9200732047b34c *src/bliss_kstack.hh b8dc4070f987308720f7e1a4f4d010c2 *src/bliss_orbit.cc d60c11f3c59f5772884b9806695a5c4b *src/bliss_orbit.hh d951312081f37121ffe1c304ae1e76a2 *src/bliss_partition.cc ad0761099301a344b21137f75758bf7c *src/bliss_partition.hh e8f493a4957107b381266cf23402ecd1 *src/bliss_timer.cc 4fbcb31c629208df6e08de6fc5546e8a *src/bliss_timer.hh 9dea494c0b450eacc3399124a835ca34 *src/bliss_utils.cc 40ec60a85a52ae23d365b36be0663e80 *src/bliss_utils.hh 1deafb8a04546f4283e9ffc0167b441d *src/cattributes.c a4e35081fa127db8acbef2047c5206ba *src/centrality.c 5f28b17df959357c02ebeb9b567cd7e7 *src/cliques.c c674dafd0655f930666b309cd023d4f6 *src/clustertool.cpp 6ad201a1dcc21ac1abc526ff8fc5ec37 *src/cocitation.c 02021ef565fd8ffb32fe28519d7d067f *src/cohesive_blocks.c 9ecdde0109759e7b14ad73efc5c1404b *src/colamd.c ff913a29f540ae3c05cf05ffe4cd56b7 *src/community.c c878447bf8cd5bd7d6ad60299325589f *src/complex.c 9e68a37154e84ab518d06ade0f4fe641 *src/components.c d41d8cd98f00b204e9800998ecf8427e *src/config.h 1f2d99cf6b8e5bbdd6a4b1b04f586eee *src/config.h.in a4080b528c0b3ef3c28feb24195b1151 *src/conversion.c 6f20b66f4a09e408f75df47f33b5f04d *src/cores.c 71e3b8f8d5465f43ca1d555d45ea13d7 *src/cs/UFconfig.h aca82a2cdbbcee0810a60c19c2304b0f *src/cs/cs.h 0de41bfeeb074d64f4e66e522c0ff77d *src/cs_add.c c57185921cfac5c1881d63c906acd2a0 *src/cs_amd.c 1d71a8f26f6985adde5b866d3ca886cf *src/cs_chol.c 34b2dc25f7038adaeb9fb251dbdc4003 *src/cs_cholsol.c 6a514645c9c04b6434f0467b9294c060 *src/cs_compress.c 5d1d491b83bddb4de7dfe784d979267a *src/cs_counts.c ec39cce522361be6e4a3758ef018387e *src/cs_cumsum.c 24b004cfcbd8cc8fb359db0f6234208a *src/cs_dfs.c c4f02f5755f68aaa170dbc448a6e0d88 *src/cs_dmperm.c 50f7fc58048430e8be2208013a2ca117 *src/cs_droptol.c 569c98d9f0daefdcca1557a9887e12a9 *src/cs_dropzeros.c d7406542b69bed807fe1a3649917cb5f *src/cs_dupl.c 1d83b91ed80fa761c737e28460f142bb *src/cs_entry.c e84dcca7eb019c54a63244705be9de83 *src/cs_ereach.c 56513bbedfdf82fd50257880a7d4110c *src/cs_etree.c 07a5fe24c1504802ea681fbb392fa3ab *src/cs_fkeep.c 9ccfa44ef05265c7cecf2b8c472444c6 *src/cs_gaxpy.c 77d68d1fa7f658c27a7958b1ddb58b2d *src/cs_happly.c 863e2db4f9333a4c57a145b080830aea *src/cs_house.c d91eb5a0ee22f874955dd304d872bc6a *src/cs_ipvec.c 1ae89c82d2e9acefca697be9bb8c46f9 *src/cs_leaf.c 7bc484d09f7b0ec2e90c3dfe71cb0050 *src/cs_load.c 2c8e2c41defd62711d50dbd701b5d24e *src/cs_lsolve.c 8399329c480228cd4aa3333e9ed55fdb *src/cs_ltsolve.c 5c83d2012c350815acdfadd9621486fd *src/cs_lu.c c8d35a8b5bfc5158055530718355f2ae *src/cs_lusol.c 85fc4de3b7192e3e578f219057d2c0df *src/cs_malloc.c 1fa16ad625004c0751972d6167d302a7 *src/cs_maxtrans.c cd7f8fbe8b9f24d39398e521ee2d09b0 *src/cs_multiply.c 3a79c830ce5dd9bb949a9685e1edaeea *src/cs_norm.c 97293626e9f064a409f1714280395e2e *src/cs_permute.c c7f844492120b9225c45a32abeeb455c *src/cs_pinv.c fce1c3d237985737250c206c1fa9b364 *src/cs_post.c c3ccdcb4c0983305fdd38aed6fcaea4a *src/cs_print.c 88bb28f69b30603dd19009a3375b8ac3 *src/cs_pvec.c d51bb2e9d507761a669a4e87d164aa00 *src/cs_qr.c 97db2405a8b64e9d702aabc7179cdb89 *src/cs_qrsol.c 03617d6031fc596d85e13b3a3da43fb7 *src/cs_randperm.c a8d7f11049e9dd9adb65b37055089f24 *src/cs_reach.c df2cd8d777a7ae7124bb3d3af0336c36 *src/cs_scatter.c 5ea459c942ba6699f9d1c103896e8cec *src/cs_scc.c 95fe5e92c37c842cf7f6ffd95bf45a26 *src/cs_schol.c 25433aeb13e81623bea4fce4f4a386b7 *src/cs_spsolve.c ed31244ed5ef4ab0c2caa42b1a92dec0 *src/cs_sqr.c 637e7a1b32636fe1d088b0895d771c3d *src/cs_symperm.c 74e77d67862dcac028568c0a50b60844 *src/cs_tdfs.c 7f9683f97ae6f192c2e4ded4ba51ed71 *src/cs_transpose.c 5ab9c4c7c85630af49f10c1002795c06 *src/cs_updown.c 4c8096ddf1737b69d8f41bc51ab1c96a *src/cs_usolve.c 4ae2ad895b3ad8adcc0927dee0b33b3e *src/cs_util.c 74e3d5634309a7716491939ecd8b993a *src/cs_utsolve.c ae2b99c6930b9d78a067b9f304e4d021 *src/debug.h 9886eed995d6b524e1a0f57b79cad7d7 *src/decomposition.c 634a82287e116db541c1c954a3cd9bdc *src/dgetv0.f 53aa0b92a01343a9780f33409ff71448 *src/distances.c ae7917a56c25a07b9860819bebf32f40 *src/dlaqrb.f 334cfcb89b71acd8bcf5e8398923f7f5 *src/dmout.f 33affe232f61fa5cab387c8c3e140ad6 *src/dnaitr.f 8661cfa88ca0ffa0f8847dc88ed53bcc *src/dnapps.f dfccde1654a64a6e14709c827311dc6a *src/dnaup2.f cbd4968767585d82b4ea9762ce7a973b *src/dnaupd.f 8285764ecec3f0da1831503affd69067 *src/dnconv.f 7e7766bc466e28155a85211734e36426 *src/dneigh.f 92be5de027d3bf234c3adb3c1df81216 *src/dneupd.f 599f6e77589fa5338379452ab77ec143 *src/dngets.f d8ad2b202c8a6ba69842dc7258256b8a *src/dqueue.c 75b2af8f83aa22849da28cc69e823a3e *src/dqueue.pmt efac25ebd52c48ef108adc8afb018f16 *src/drl_Node.h f1ca9cf6ad08537f57409d9ce04a6345 *src/drl_Node_3d.h ac508e551027fe56c57650557ae848b5 *src/drl_graph.cpp 0c4a475dc83152ac2a64f07bc13fbc60 *src/drl_graph.h 9b685020b218853e6c53e036b52b5f5e *src/drl_graph_3d.cpp 4ee344063b486976c812f82113d3106c *src/drl_graph_3d.h eab1ab611cb3aec65f42ad2f0652c0fe *src/drl_layout.cpp 4c1690cd89b50832fb6ba2398d14414b *src/drl_layout.h a77f381105b0246c7a0719c0b669365b *src/drl_layout_3d.cpp 7e4c69a183df51fc7662e2a3f5c6e6be *src/drl_layout_3d.h 4b5e3c6311f4c7a87eac902316f38b95 *src/drl_parse.cpp 14f8e5de9f1b7e850614ccf71c93bff0 *src/drl_parse.h d1e7ea74631a08da9e1166300adc0af4 *src/dsaitr.f f226039f08b329d7a276b9c920c757b0 *src/dsapps.f 26e0e4fd884197eccdf79c211e4bf09e *src/dsaup2.f 221f58799c95c17f73a5043d9edb959f *src/dsaupd.f 573fb11e41307018f2fdb32ce3111be5 *src/dsconv.f f976b4529dead76e497c2f35fe067b00 *src/dseigt.f 0f7c847fa63252f466a7c312a9baa052 *src/dsesrt.f fd78b52dd2795d4db9d6706e5d7bbe26 *src/dseupd.f 604cef634a570edd5e9e1f0e57b85800 *src/dsgets.f d37e30b6becbd695f77bb83e86fc8845 *src/dsortc.f 8baf60e7aaca0c70f8ce165fa60f0eb4 *src/dsortr.f d4ead7e7ae03b16c06bc2eee64bc99fd *src/dstatn.f 40dc3cb9ded24c012fd5810e6175d7f9 *src/dstats.f de4792cfaab6cdda8d557902c2310fcc *src/dstqrb.f 10246dd04cc987d389f1f369f4b1813b *src/dvout.f d971c3cba371000e3ee5232179b380c3 *src/eigen.c a132926fc62c5122a0e5db6e7decb1f9 *src/error.c 3d289f9acb689ff87c11d26ec9431392 *src/evolver_cit.c 9cd0df4f1d1e6929361821451483c0bb *src/f2c.h 56707b18b6e21642069a8510f6073147 *src/f2c_dummy.c f2de7b7be34e28680986231c4994aba7 *src/fast_community.c d0bfcf69eb5733db3c1c2de12f407bc2 *src/feedback_arc_set.c 0563aa4b46d3a709c7032ef7654b08e5 *src/flow.c f51da6e3cf3d9e27793f855b469a3663 *src/foreign-dl-header.h eccee3dbf7aa788c634b77f65e9bacfc *src/foreign-dl-lexer.c 311ecd79cb537a22b01912b84f2a73dd *src/foreign-dl-lexer.l 43559ba4e43746f8aaa961ba4c58ed88 *src/foreign-dl-parser.c 1c67dc103337b4b9c50ce94528f7af63 *src/foreign-dl-parser.h efaed2bbc90cd2438328a2c2e9a65ed8 *src/foreign-dl-parser.y 3db22bd8908d6d05a56a0856702ee72a *src/foreign-gml-header.h 441522a2a07ce65ca392904dfcd17e59 *src/foreign-gml-lexer.c c84b6be330a9184096434f55d6de9e87 *src/foreign-gml-lexer.l ad2111f203beade7ddd1415e3cb58395 *src/foreign-gml-parser.c 9407d6efc58f9a454260b2f3d3fe5ee9 *src/foreign-gml-parser.h 19ced477a34f97376c619eea0482f1f3 *src/foreign-gml-parser.y 05415b608e7a60f77f276c51a3fc0f83 *src/foreign-graphml.c 6b3d524a6e9a9e187df1dbfbfd0afdbc *src/foreign-lgl-header.h cc6d81d2c845885a4e5642be9413c9d4 *src/foreign-lgl-lexer.c fb60d3213c781454f82624410aed67d2 *src/foreign-lgl-lexer.l 436f645336fd806fa7525ce7c6646ea7 *src/foreign-lgl-parser.c 2957ad10def23240e3a3bcae7de13c30 *src/foreign-lgl-parser.h c9aded2d0a81a04ae4dfd0f2e7b92554 *src/foreign-lgl-parser.y ba4af782887b2b97dc03481ee7f435bb *src/foreign-ncol-header.h cad3d020644bde8c9ab99c3ed5b17c78 *src/foreign-ncol-lexer.c bf1195fe1617392e785438d93fe0021d *src/foreign-ncol-lexer.l 3fd61d712872dd731107690606e61b39 *src/foreign-ncol-parser.c cc506a5dc6e6fb4e4018c873b2a17898 *src/foreign-ncol-parser.h 50d4e7b6fe1a6589a7c2c8e7830072c9 *src/foreign-ncol-parser.y 6228545b60873edce3007a3b200efdde *src/foreign-pajek-header.h bd4d6001c30c716580dd8e7ba19ab335 *src/foreign-pajek-lexer.c 1f9514ce1b24aaf239701148518861f1 *src/foreign-pajek-lexer.l 7b38b7f9219634f19ff46e47ec646ac9 *src/foreign-pajek-parser.c cc0e7c882c550da2fb22d170ea077017 *src/foreign-pajek-parser.h 8af7eae958466874c8c9c28bce72fb91 *src/foreign-pajek-parser.y eda3aa570143c8e8eed6f2caad803636 *src/foreign.c 85aa6b11ac217d0125f2a637b2f44780 *src/forestfire.c a13696b3db177831c57b0f4420a35bb1 *src/fortran_intrinsics.c 2e4feecb12ae7427d53b85958f0941ec *src/games.c 9c4ac6869dba15f8f3726febbaa66170 *src/gengraph_box_list.cpp 88bd52487c626b7650fd6d14e248925e *src/gengraph_box_list.h 1217fd9a8088ac3c861170a4c2a29118 *src/gengraph_definitions.h 669960a45a8d87d695aead1c4ff45263 *src/gengraph_degree_sequence.cpp e91725a3e6fc82814f0838607077462c *src/gengraph_degree_sequence.h ff6d04d06e0510300725a54ed0fb29cf *src/gengraph_graph_molloy_hash.cpp 8cd957d8c81c41dfc84b9d3ab52d89a7 *src/gengraph_graph_molloy_hash.h d26b760cb945c780486c439f08a31d57 *src/gengraph_graph_molloy_optimized.cpp 1e029605529d2b30e0c51ce3f0118e28 *src/gengraph_graph_molloy_optimized.h 4cd9c442c3d07a86cdd25c4f23532f3a *src/gengraph_hash.h ed698c541b7f649c44e0ad236a1c4889 *src/gengraph_header.h 3cbf1351fcc949438140bd886b550b46 *src/gengraph_mr-connected.cpp dfecde6d15a11f7fe08510ee52505a0d *src/gengraph_powerlaw.cpp 0cde273c55b4df3bbf9d6db3175ed954 *src/gengraph_powerlaw.h 591fd6841007b0301c2bef9b05bc648d *src/gengraph_qsort.h 74f893284897e15ef1058d9359bcee1c *src/gengraph_random.cpp 0ec1db22781e6e2dfff3c076dc08cc0a *src/gengraph_random.h 031d4f910e7889608d7ba4268d471c05 *src/gengraph_vertex_cover.h 3bd5bd3098edf9792af4c01e59beac03 *src/glet.c e64c7452742bcfc99310ae9901cbd962 *src/glpapi01.c b17a4e69773620daa39e87c45dd48ea0 *src/glpapi02.c f7a3441497a86d3599a6882facacff18 *src/glpapi03.c 8af8227d0296639060aefd772bf35678 *src/glpapi04.c a30e64d71793ce20aa6e1b07ed8dd411 *src/glpapi05.c f1d134c5e35fb0bb3af94aaa2eb0be7d *src/glpapi06.c 21e7bc0b19232580d3c0f26bf4bbd1e1 *src/glpapi07.c 1b8320574d7bc693fa5155dd8e627ab8 *src/glpapi08.c 677c618173bc03d34ffc5daace06f84c *src/glpapi09.c e144bb6919e4e9e431b93e154865a530 *src/glpapi10.c 9e9de1b47d47eaa4a4cebb74517c4e6e *src/glpapi11.c 973d4c1f6d64fd170b2154e958ce5ed0 *src/glpapi12.c 9fe8165a0e987461907848a28f15ca66 *src/glpapi13.c 4582c7b8b705b3e30bda0c3f5b83597c *src/glpapi14.c 7d03c791001d49225c7eac6051abb039 *src/glpapi15.c 4cd43f0ca77c68416e5829c1a5b52c80 *src/glpapi16.c 0d4439bdbcd5163e2ba728798c1dae53 *src/glpapi17.c 36f651163d8f8aa7336570d40c68a233 *src/glpapi18.c 983502183d4dd4e7d495a3fe8713408d *src/glpapi19.c 2772b3118b3d9544de2a85ad1564d537 *src/glpavl.c 49f330d09b6d03c651f332b5ecd14354 *src/glpbfd.c 33ab56a232eb49786711d459bf22f3a1 *src/glpbfx.c b3a43b8f06092f5499c2c1ccdc0cbf32 *src/glpcpx.c 3ccb652f19ee4c399e73fbf56af00658 *src/glpdmp.c 85d574732e0ed25a328e9d0396cc0e38 *src/glpdmx.c 6bbbc2b3400d31149365e4086060c7fc *src/glpenv01.c 02dddf3d64ee5afc72a38462cd3721c5 *src/glpenv02.c 1a9208853fe32f4d522b2f7622b9076a *src/glpenv03.c 53bbb8f8863659738a8c953300f19048 *src/glpenv04.c c12ae087f99c8407fa1617930d1cc0da *src/glpenv05.c 6faef60ba452fce1d9a780ea65281ba7 *src/glpenv06.c 1db57509946800d4800cdf0afc30cdf1 *src/glpenv07.c 49b7624ba8d7723df57cee48011247e9 *src/glpenv08.c 46b0d56a190c98086865b183206206d5 *src/glpfhv.c 342b93b287e4bd6e48fbe6a4ec24b07f *src/glpgmp.c 996cd36081e5ebc2f56971470741b81d *src/glphbm.c ce63bb794e16482df6dbce2fbc84a77d *src/glpini01.c 5644da9ce91aa2f11063edea8efaec4c *src/glpini02.c 4a47177352b566b75dd2ad1a158f71a1 *src/glpios01.c 8887161631f60bbf80adb45b6f7ccbe1 *src/glpios02.c 29e44df78067e20670cca9b7db196fdf *src/glpios03.c 11fb8f1cd067c99e11124c294c51e91d *src/glpios04.c f6fd7f01acbf12d0efa8901c92f5f10e *src/glpios05.c 10435d381e839929cc8b0f9792236cc3 *src/glpios06.c 7ee236816936ec5b2f96458f3d130aed *src/glpios07.c 32091c062df372e38a001d78a3e1e341 *src/glpios08.c 95d0e04a78319fc9be7fde38d26173a1 *src/glpios09.c a6f59b769898cd2e50d7d61066a9d9c7 *src/glpios10.c 2a7eed373818656834cf6131bbc89c61 *src/glpios11.c 7fcebd33c0c4282e72e105cfa7aa12a0 *src/glpios12.c b4e358a396f1d487cc49ea19c4bd8e2c *src/glpipm.c f17b9ad6c570189c90e054e5ad22a48c *src/glpk/amd/amd.h db4e7aaf7501f2cdf05e0fccb0082ed3 *src/glpk/amd/amd_internal.h c29bfe2c47b141be27d1bbf316f256e3 *src/glpk/colamd/colamd.h 4dd8a600fd42f610ce619ae89cd1e9c4 *src/glpk/glpapi.h 701d3775db123ccbd0493d8c4ea34a7e *src/glpk/glpavl.h 848678ffb247e83064e327cc95fa06b3 *src/glpk/glpbfd.h bad6d53eb95c74d2a8859557279a9979 *src/glpk/glpbfx.h de2e8b56e85a0b20dbb430d6aa84f7d0 *src/glpk/glpdmp.h af185798690a781a8975b4a569e51a76 *src/glpk/glpenv.h 2bd66523544ce97770e12c8603cd653e *src/glpk/glpfhv.h e1cf5f623aeafc6d82a408d564385746 *src/glpk/glpgmp.h b7d65d02425b2437b080a3f69ea9b3e1 *src/glpk/glphbm.h 6427eb0c0dc32e1aea7c20d16851de40 *src/glpk/glpios.h 2a6aeaca925e3d8b2c3b9c9016121d27 *src/glpk/glpipm.h 4eb723473ff13ca6d8c1d1f8a7bf46b8 *src/glpk/glpk.h 7a286fa7ac1eceaf243eb1ccdc9614cf *src/glpk/glplib.h 0acc7485a685ddb71122d22fa7f3dbc1 *src/glpk/glplpf.h 112d86ae170930563c37147d0201232a *src/glpk/glpluf.h 4f75ac92850d57038c498931027fbb3a *src/glpk/glplux.h 3d68afbfc8e7cc52600442e25500edde *src/glpk/glpmat.h b6fe8b8ff7c7c1405839b916fc6ee538 *src/glpk/glpmpl.h ef7abdd1cb7279367b64496d1ab5ac40 *src/glpk/glpnet.h f10605e75b60797e59acf8c897b8a9fb *src/glpk/glpnpp.h 2d027b66a207e6cb244d9c2ff8407cb2 *src/glpk/glpqmd.h fed50a39c1e72847ccff3a0e6f5356a4 *src/glpk/glprgr.h 2575fe5808c2e6b86e0f8bbf328e31f0 *src/glpk/glprng.h c8ccbd6d72148da380dc7199729a48a2 *src/glpk/glpscf.h 6ee41754eec62d95a8799714907143cd *src/glpk/glpspm.h 8a4640b9634afc20ca92bc5d9b7e608d *src/glpk/glpspx.h a1763cdc5fb411f8c4b5f62510675b4d *src/glpk/glpsql.h a0122421497d5ce9813fcad4eb3f9a1c *src/glpk/glpssx.h effb42a09a2fad5d5c9577a6c8c375f4 *src/glpk/glpstd.h 35eb2e8022860add92b53b914cb99993 *src/glpk/glptsp.h 901398d9a9f4b8df25e57ce11950d886 *src/glpk_support.c e86efde3285da6e7e5b70609c9d30ca1 *src/glplib01.c a888f4dca9f00ba57960612fd54bf34f *src/glplib02.c f15b3eec93525eabe3dc1c27afa3a5a3 *src/glplib03.c aa0a9b8867ff151414904269733382e2 *src/glplpf.c 8fdffa2d9c8b98e026bc9176b2bf46f3 *src/glplpx01.c 1a056d0d76910a9ddc344e9d79862ed2 *src/glplpx02.c b6997c531009890187f84017f092a698 *src/glplpx03.c 51f02c1522277d2bcc51e762f8363912 *src/glpluf.c 8b3d8e9c4b6b1c8f980bbb349d82da31 *src/glplux.c 0a51e9223e38c9a0a96ee8c94e602213 *src/glpmat.c 802bdfca436334911154b539a1e11091 *src/glpmpl01.c 9d97da099e0d0084ccb1adfd8a4cf7e6 *src/glpmpl02.c 19da1384b5e4ebd43e7c0c6b13b56350 *src/glpmpl03.c 29e89bf6e061131e8eb6c3f50d5ce1dc *src/glpmpl04.c 8a0c5bbdbeb1c2ef95479504b7ecf927 *src/glpmpl05.c 19784444c34513714f178081c69432f3 *src/glpmpl06.c cd3d8668a985b27986386ea14a3a5509 *src/glpmps.c c60ca0036894a63d2653736af1e7afcd *src/glpnet01.c f7b6379c577fa69e870effe585295275 *src/glpnet02.c ce92ee9eaa913676ef52970365010ee3 *src/glpnet03.c c2676be654346c4f6e0d12226abcd919 *src/glpnet04.c 5b90741dd7f9c536e020e02feb947e17 *src/glpnet05.c ccc54db6eb60287bdfa50caf5c7fd6aa *src/glpnet06.c 2d26b236a83453d99141d917a6d6dcd5 *src/glpnet07.c 4d569359378e4ea5155abe63ce6436f5 *src/glpnet08.c ea0d3b98de940c5d62e344c046226a75 *src/glpnet09.c 2060cf4986223f5b8b1d6e8c7a8e6cbc *src/glpnpp01.c 9779f9dcf4f7647a42499260ac3ec674 *src/glpnpp02.c e8d65669ef3915938da77c0d1a79dce6 *src/glpnpp03.c cc66449ea1082cc861d0ee8ef7bdcdb8 *src/glpnpp04.c 6b01a6f4adf6b37d51d269c8bcfa1199 *src/glpnpp05.c 18f30853f890cbbd2ff1469036daff6d *src/glpqmd.c ba62d9230bd3da1bedfd72fca32cc638 *src/glprgr.c a932ccfb7e961ef982bfa572a75c5b8b *src/glprng01.c a4319cc6438d545af19a46a0e860e205 *src/glprng02.c eb21955a5df4900fe0fedc7545005869 *src/glpscf.c f0b5b2351e57e092b4755039297adefd *src/glpscl.c 44700faf1076640bf15615a837ea7e32 *src/glpsdf.c 3dade41dd2de4039094bd0417d47dafe *src/glpspm.c b1b3ecfc42873be5665f7e2c371bef41 *src/glpspx01.c acf9363f8de2e02cbfdf4d6b0852d5a0 *src/glpspx02.c 7bfa819a7610ad46b8daa7b53c31321f *src/glpsql.c 417a7397688277ced9a0991447ec476d *src/glpssx01.c 2cf632f6ddb252f241558ce06b72aa3e *src/glpssx02.c 07f66c70186d7f7c18b979baf3087984 *src/glptsp.c 9e49afee60da929ba8be5d9561949edd *src/gml_tree.c 3325bc090cc6c9f8dfac22689e534a28 *src/gss.c 9cbe127f9ca1413b025474021b3dbbf8 *src/hacks.c 502e94a6e46f91ec49aef1978e388a99 *src/heap.c 507189bbcdaeddda2b3e1bee2640388c *src/heap.pmt 6e09173c5033b55d9a469c8dd2152847 *src/hrg_dendro.h db1d06d3f6afa649b603447a27db2ba3 *src/hrg_graph.h 2a55b5aa6dd24af3fbed23c777f3356a *src/hrg_graph_simp.h 45a515d92fd7d19d6b58ce9a99790443 *src/hrg_rbtree.h aa24b037d218c2525ba09ead3425f554 *src/hrg_splittree_eq.h 5d261d8b02b68a913b7ea2fa61750032 *src/igraph.h 06c01d2a2f3b3bffa3d14ef21f26b646 *src/igraph_adjlist.h 75c807b296c812f666ab218a80b61d2e *src/igraph_arpack.h d049469367b24df3aaa04a335e19ae1f *src/igraph_arpack_internal.h 623fd0301b11d9b7e7d518410be151bc *src/igraph_array.h 9001525eae9a131c06ec404e2027450e *src/igraph_array_pmt.h 0e9277d3c012dfb936cb3dcccf9a6d04 *src/igraph_attributes.h f206398b5b1ebd455a888fd4e039df3b *src/igraph_bipartite.h d5ad44bb127140ed7d1897b902453eb7 *src/igraph_blas.h 35de0146121ed0d2e5ce0f3a2de72740 *src/igraph_blas_internal.h 8c23d74aac650a5a31f7aa2c5fd5e4cb *src/igraph_buckets.c 82e0de14549bd32301dfc35f040141da *src/igraph_centrality.h 08c3846da4ba18485b169257ab2e6247 *src/igraph_cliques.h e45a8025313dd0029619f82777b1abe5 *src/igraph_cocitation.h 1914da75cb7d341220431e0ace1c15d7 *src/igraph_cohesive_blocks.h 3b0614e0c2f1b3fd0cc1abd56178ab44 *src/igraph_community.h 6b70a9038174acc3f988b831296c366a *src/igraph_complex.h c0501c548df97a52edccec56c888b743 *src/igraph_components.h 267b1633bbcbdffea58242ae64fb902f *src/igraph_constants.h 49689ad5d8365f90ee241f37eddc996f *src/igraph_constructors.h 16b3e1150995475d75000a4d3f3da6e9 *src/igraph_conversion.h 5628489dc4a2f581a70e63de6d0de7f8 *src/igraph_datatype.h ca6d0973e081cc25330a166205e3b553 *src/igraph_dqueue.h 1f7fa9c5233d679f978df2fce00db6f6 *src/igraph_dqueue_pmt.h 6b82a7f6360f79e1814f3d127d96e87c *src/igraph_eigen.h d58a291f8865cf4fe45092185076ef19 *src/igraph_epidemics.h b19e0c7e19e68ea56da4bc4d4adb9d42 *src/igraph_error.c 312cd51739e41ddaa050cbc8fc8af42a *src/igraph_error.h 89d68f6df7bcbf4d4076ac0b0e49c76d *src/igraph_estack.c 5724366acc9829a7710570238fb8757c *src/igraph_estack.h b820670b0847175947527134ac67632e *src/igraph_f2c.h cb0744105e9a3bec9c23b323122abf18 *src/igraph_fixed_vectorlist.c 2887c1f547c4ffd5bceaa6edb2d3c471 *src/igraph_flow.h 47ee6b99a58e0f65a3c5bc3d393c9778 *src/igraph_flow_internal.h 87156e6fa6bdf23bd94526021c603f59 *src/igraph_foreign.h fdebccf6bed02c480bda1f8e6d3a55e8 *src/igraph_games.h ba6345ae75a97b8435b3f1008394d521 *src/igraph_glpk_support.h 7427a7fd438c1e5c394d9dc3b9296d38 *src/igraph_gml_tree.h 9aaf4a333500cdd37d1813ce68e598bc *src/igraph_graphlets.h 6cc5ff7eb59a0e80a55dd276d626058a *src/igraph_grid.c 70dde0752bdfd37e737b8f919f559296 *src/igraph_hacks_internal.h 798db32cb0483138312afbcee380996c *src/igraph_hashtable.c 5fcba758c3db2a7b7e087ddf0ce31b2d *src/igraph_heap.c 1717da324eb54cd1e1f71c10c8ae53b5 *src/igraph_heap.h 7db2e0e75dfd1cc2da3126e9f5e62f14 *src/igraph_heap_pmt.h 8c9642f7427ed582fcfeb23711106b51 *src/igraph_hrg.cc e09223330884413f8ad790dbecb6fe70 *src/igraph_hrg.h ec23eb8c9f95ac5333a7c4a955888c90 *src/igraph_hrg_types.cc 3245b2c2ff708563207e8691bb9ce5b5 *src/igraph_interface.h 96e9fa6414a7f6b9a58cb0aea6d9dc50 *src/igraph_interrupt.h 491ac34973b02bee0561a49d3c5b75a9 *src/igraph_interrupt_internal.h 626c2ad06dbf67663fa9e477fb579806 *src/igraph_iterators.h 803628fbecb75a03af18aa90f2d73a67 *src/igraph_lapack.h ba664f4e2b69d9835ffabfc336bd37ee *src/igraph_lapack_internal.h 5f36fb91cef12b727f3edbe07386bae4 *src/igraph_layout.h 1ea6795e5745f48a13b85a5d565ef2db *src/igraph_marked_queue.c 84c6ecb9eadc9a6615dada2a134ed3a4 *src/igraph_marked_queue.h 5f74e30a6cc5e2afcaa04c850b1d62e2 *src/igraph_matching.h 4dd979dc14d2f0e96da0c03548e98610 *src/igraph_math.h 2a114796ba515085832c79f565b47b3f *src/igraph_matrix.h cae6389b9031f10ad056793bd59378b3 *src/igraph_matrix_pmt.h d3bb0da57b31cb5ab0ce4429a7078a27 *src/igraph_memory.h 030d41f451590d25e199bdb9dd9b7a15 *src/igraph_microscopic_update.h 8ee4888b80c021ed79c30c5b6b7de3d2 *src/igraph_mixing.h f16cc28c3fe2320314760e352ec2fa7f *src/igraph_motifs.h 5962e2e4968d711454971ec88b02c5d8 *src/igraph_neighborhood.h cb2f513f20d1b9e1fc768b4a01ba949a *src/igraph_nongraph.h e86604269fd6942eadb6f4c6f37986de *src/igraph_operators.h 9941a7947be349e5f828864780994958 *src/igraph_paths.h 024bc0d4b6363037ea93edd33b1adb1b *src/igraph_pmt.h c7e18522a9ed70278190d35aad0c2788 *src/igraph_pmt_off.h 8f739083243bd8c0146a4c4b44bfe258 *src/igraph_progress.h d3872c797d7d9d20338f3988b3bffa1f *src/igraph_psumtree.c 7e39b7c3cdc2c7793b47222dec147610 *src/igraph_psumtree.h 5445a7660a7f5a2aed7c1aca66a040d4 *src/igraph_qsort.h 0ebf3c6bcac0918c60215f63f75790f9 *src/igraph_random.h bc2b831c79515f52022144ea4b479c2d *src/igraph_revolver.h 320603e1a9f5f6b5d9564f933fc70282 *src/igraph_scg.h 1c38c4ad71bc32f3701965af38c42035 *src/igraph_separators.h ef0a9f0275dd11cd340293b9c350431b *src/igraph_set.c 573f3f42b20e9fce226470bc3518d317 *src/igraph_sparsemat.h 817c003ae01fb4cee9c8412545ffacd6 *src/igraph_spmatrix.h 2b815210b218876ee107f06fba86d04f *src/igraph_stack.c feda6bb336f17776c2fd0558de484dd9 *src/igraph_stack.h 961b488b4455466255c740b779ffd37d *src/igraph_stack_pmt.h 161ecbcf55c9af968ba800562168695a *src/igraph_statusbar.h ae8f253b9c1ebb15d454116b4af0c247 *src/igraph_structural.h 276b378aa4479d729baf29fdca1dd84b *src/igraph_strvector.c 325e434438ca624456bfa3e7e675c036 *src/igraph_strvector.h f988c64c2045ca1b71eeda347e45905f *src/igraph_threading.h d22c3634d4677a6585641cd8c463887d *src/igraph_topology.h 9066c301497c3f3c34d31cb2a71329f9 *src/igraph_transitivity.h 78c519533ba34d958b11a20b183c3dfb *src/igraph_trie.c 7f2373fa6dfb143ea9e3a5cdd4d5ad85 *src/igraph_types.h 4aa482a23cfc790fe9de29e1f8867038 *src/igraph_types_internal.h a2bfa55d95c670368db1ca866efc29a4 *src/igraph_vector.h 383762630cb8bf896dda3d74e16d5173 *src/igraph_vector_pmt.h 64f1a9c71d6f9b2c68f09d63540c231e *src/igraph_vector_ptr.h 58c8dccae0eda3cbda698d40a73c5deb *src/igraph_vector_type.h 628d7afbfb67b953c74b1d474b430495 *src/igraph_version.h 050ea429bcfd86ad679748fa9cbaa8b6 *src/igraph_visitor.h 0ceee7df894ce42f555f5effe0240702 *src/infomap.cc d7095b8ca721c2507d81152d722308e8 *src/infomap_FlowGraph.cc bbf84d35c2016510d97be2b41be84017 *src/infomap_FlowGraph.h 793969346850d33fd683d087a8e4b52f *src/infomap_Greedy.cc 75a431e98e5b7b785bc372f211d432d0 *src/infomap_Greedy.h 7edc0e07208e9487e1fb8656d6493ec5 *src/infomap_Node.cc 9df7c5839a5cf0aa081a637ea9389446 *src/infomap_Node.h 14be091d75db4df0c8261c571a3c235f *src/interrupt.c 491b61dbc3c8a265485f6b29eb5b84aa *src/iterators.c e9e8f2dac33c5cc7bfe1da70a95cc05f *src/ivout.f 04b05118dedd5d894b7451a3077a2abf *src/kolmogorov.c 22cb5d77f20ba43b40ee2882254776f3 *src/lad.c f6c80d45596a09a7a18de3edb95fe105 *src/lapack.c e0c0fb18b46e111743fa4e74fa550186 *src/layout.c a3f7a0497381c0fecd2c2634913c48e3 *src/lbfgs.c 284719df65b4db536352f3bed6309975 *src/matching.c 6235a73cc32cfaad6c3413719e36d16c *src/math.c bb7fbeb13293233a4927f790d2eaaa93 *src/matrix.c c2c748ceb15110c1cd5ce628a10719c5 *src/matrix.pmt 6f99b8e27ee426917236833013db4ce5 *src/maximal_cliques.c 09c8159e923bc5d13cb081d6f8c4657a *src/maximal_cliques_template.h 4ae56c99425d054546ce50eee102c70d *src/memory.c 38fc41da5fa51c0be97f084498a9d8d5 *src/microscopic_update.c 8a97bea8b83111ca8fb2f2886db91fc1 *src/mixing.c 2abdabfdf646e8dc092679df31ecd5e4 *src/motifs.c 579a7e919b6c29f9ee0b736e6f5bc11c *src/operators.c 13711e38459b1fc13a31384f665405d7 *src/optimal_modularity.c 52f301d8733c4362b51ab461fd41b376 *src/options.c 9fbb2134332fae527cc3200bff1690b8 *src/other.c a68a32d67b810ad2271da3d932506179 *src/plfit.c a6595cc90c64e0aab7a336856a219e53 *src/plfit/arithmetic_ansi.h 098651533f8c80ffeef3d3e43bf39783 *src/plfit/arithmetic_sse_double.h 896685565ace49c58c8587e52c9fdfcb *src/plfit/arithmetic_sse_float.h 9351bcba0c90fce3ad1dc3abdfbad525 *src/plfit/error.h 24b12def90aee37671bc29d1c267997a *src/plfit/gss.h 1af9acf6c110147ab7392148cf24c0ce *src/plfit/kolmogorov.h f3601e97b1249e5888d73fd10e8126d9 *src/plfit/lbfgs.h 2af6a5ba8d50aaa2493f188475fbab8d *src/plfit/platform.h ec39eb232b03766144d5ce70434cd470 *src/plfit/plfit.h 2cb90ff85be06e53110d04f2ee7574e3 *src/plfit/zeta.h 5219cdfa910aac54c77dc3ba3d87105b *src/pottsmodel_2.cpp b39bce59a0e9fdfe1b028aca69f040d3 *src/pottsmodel_2.h 3b4575360f029b448db3946a22672a8d *src/progress.c 9be80fc38a8b4cce2458b8995b0db38c *src/prpack.cpp f239c9f27078f7fc0ade177796514f7a *src/prpack.h 9cdd08531c091877493dc38d6d9a76a6 *src/prpack/prpack.h 9204c4b68e204d547368fe45215f1933 *src/prpack/prpack_base_graph.h 11ef113ef2afa4d58490fcd2b150c070 *src/prpack/prpack_csc.h 6dd85cebd13f0a8636f2410131e7a096 *src/prpack/prpack_csr.h 16f1218cac357f6853a1ef4a56a97a1e *src/prpack/prpack_edge_list.h f4725104dcd769f5b8c536e1e7e7a629 *src/prpack/prpack_igraph_graph.h 23b18b1935c305ff6f8ea72ace07fcb0 *src/prpack/prpack_preprocessed_ge_graph.h f3f1eb733b38a2686c5443728bae2090 *src/prpack/prpack_preprocessed_graph.h 4bce367b866843fa89ec64a448a209cb *src/prpack/prpack_preprocessed_gs_graph.h b1c479ed32faf5a58f406615ce6f48bd *src/prpack/prpack_preprocessed_scc_graph.h f705b565d4dc23052d5ec58dc2f63d33 *src/prpack/prpack_preprocessed_schur_graph.h 9a2d350363928141ef4785e6f0e2cfd5 *src/prpack/prpack_result.h aa69309ad88c68bd3aef2d3b9f78d8ac *src/prpack/prpack_solver.h d8e4562102dd19aa2b0ebb8b9ffc761a *src/prpack/prpack_utils.h 0bd04ba0eae9e7e3c81786f64d64f5d7 *src/prpack_base_graph.cpp 13554f0c55678a78ba701e20669282db *src/prpack_igraph_graph.cpp 4d3341314d147c7713eabf7bf2b28808 *src/prpack_preprocessed_ge_graph.cpp 71f33cc3adc3806482f0154329cc996e *src/prpack_preprocessed_gs_graph.cpp d38e02fad914bac940da4a32ac39afea *src/prpack_preprocessed_scc_graph.cpp 86aa47464bc6d84a6ebd0912dc66ea66 *src/prpack_preprocessed_schur_graph.cpp 7dbb50744056002b707ae5232924d448 *src/prpack_result.cpp 1188482777588f7f569217973ad29698 *src/prpack_solver.cpp 6be9e55d9e9a8962d8a11145d94f89bf *src/prpack_utils.cpp 051b93697a782d5ee7443e6d14273f95 *src/pstdint.h 44ba6e8d2fe1c99c7598c6f337c39c9a *src/qsort.c f9a82bcf4a3a750ab39528bc8d304053 *src/qsort_r.c 85bd6a1c98ce55dced77f667051025ea *src/random.c c37956ab8d0dbfcaee2d4710140c24c7 *src/revolver_cit.c e0671ef91b7374b5823821f20011f968 *src/revolver_grow.c cd7cb4802771d928add18fcadd0357d4 *src/revolver_ml_cit.c b9c425eb6157b194fb5cca7378c6efcd *src/rinterface.c 3bc0f1802a0de2718213b62b126cbb5c *src/rinterface.h d17a9c6ea82a7e1178559a889c086e96 *src/rinterface_extra.c 21218e152a5873af8c41295a60784f38 *src/sbm.c aea038197b2d7bf291ffae04eb916e1e *src/scg.c 0c45c2990b0c5da3975c486a76988a88 *src/scg_approximate_methods.c 57172adf7c8ab509ba78ca5249e491dc *src/scg_exact_scg.c f02cb493011fc03a7afd0f73429e7444 *src/scg_headers.h 026c19e5e315a61afa720d1a1a02b2b2 *src/scg_kmeans.c 689526007c1e806566487866a1507dfd *src/scg_optimal_method.c bd4eee538520a213640c06c511215412 *src/scg_utils.c fe76b21dffc13a149499515906044064 *src/second.f 900f269653a3550d5804bdd246d46e80 *src/separators.c 2afac75f121c2b327f3a2310c2d2cfb8 *src/sir.c eee880ddc507c1a7add8ffa2a86ffc78 *src/spanning_trees.c 1f36e5b63e7e4c76ea9ceb017bbd3f6b *src/sparsemat.c 6bb03a8313135637cc13b229466c5fb2 *src/spectral_properties.c c09d0e9140d619f214444c71395afbe6 *src/spmatrix.c eca023c1e47dc08d757a306ce363251c *src/st-cuts.c cb9349c6655ec534ec5f9350ce6d368a *src/stack.pmt b5163c86a9a4ff980ad7a02f9303d2b7 *src/stat.h 0a0892716e6bfe89fa6cccb8d9685b2f *src/statusbar.c d2bbf1e2d44182f30147731f748b3960 *src/structural_properties.c 9bf218baeb55418532e13ff952319dbb *src/structure_generators.c 8fa4711e4da6193579f9dda3dda2b5d6 *src/sugiyama.c b75142ca495ff1275ac3ffd5982d853b *src/topology.c b5278d5c248ba78fb24d8470dbc8283f *src/triangles.c 7f2cb9bfd820624e7628cb8fc904fb4b *src/triangles_template.h 123e666d740c6b4d2462358a45769698 *src/triangles_template1.h 063203b293418cb30f8696ce52bff124 *src/type_indexededgelist.c 79ff3df7bfa26151b9893cffeba4d002 *src/types.c fc6fc5c9aa5a5187e6a2b1643f72ed4a *src/unit_limiter.cpp 10c5f89c9426685c966b72e159221f1a *src/unit_limiter.h 40ebfd83365fd2f13c9a233f0585275a *src/vector.c 97226f108c97b7abda68537cfdcb3c8a *src/vector.pmt 50416ee45e4b1e7e1c465d3c440963aa *src/vector_ptr.c 86dd274efc3061285ec0d3930f21fc2f *src/version.c 911557f42ca1079bdfe405d791f18710 *src/visitors.c 3e539674df5de90da12ae880863af9c6 *src/walktrap.cpp 15455e530da0bd2c8f68cef84a7a6870 *src/walktrap_communities.cpp d4677977768e0f15fb39a1d42bb4a7e7 *src/walktrap_communities.h 9ad1bb10814df4f9aa098fc1da0a4d3b *src/walktrap_graph.cpp 5b6742755090692a9f8f7bec3ac4531f *src/walktrap_graph.h 4708174b9d0a7ad332731d356b1f51fd *src/walktrap_heap.cpp 50c30f2b5e618bc971622ee539994586 *src/walktrap_heap.h 5125d305fe99bd9f8a0f550eb758a30f *src/zeroin.c 23aa3f72d6724222f7563bb22fa011df *src/zeta.c igraph/DESCRIPTION0000644000176000001440000000133312325555115013266 0ustar ripleyusersPackage: igraph Version: 0.7.1 Date: 2014-04-22 Title: Network analysis and visualization Author: See AUTHORS file. Maintainer: Gabor Csardi Description: Routines for simple graphs and network analysis. igraph can handle large graphs very well and provides functions for generating random and regular graphs, graph visualization, centrality indices and much more. Depends: methods Imports: Matrix Suggests: igraphdata, stats4, rgl, tcltk, graph, ape License: GPL (>= 2) URL: http://igraph.org SystemRequirements: gmp, libxml2 BugReports: https://github.com/igraph/igraph/issues Packaged: 2014-04-22 18:00:26 UTC; gaborcsardi NeedsCompilation: yes Repository: CRAN Date/Publication: 2014-04-22 23:08:29 igraph/configure0000755000176000001440000051120112325372070013464 0ustar ripleyusers#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for igraph @VERSION@. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: csardi.gabor@gmail.com about your system, including any $0: error possibly output before this message. Then install $0: a modern shell, or manually run the script under such a $0: shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='igraph' PACKAGE_TARNAME='igraph' PACKAGE_VERSION='@VERSION@' PACKAGE_STRING='igraph @VERSION@' PACKAGE_BUGREPORT='csardi.gabor@gmail.com' PACKAGE_URL='' ac_unique_file="src/rinterface.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS GLPK_LIBS HAVE_GLPK GMP_LIBS HAVE_GMP HAVE_LIBXML XML2_CFLAGS XML2_LIBS XML2CONFIG EGREP GREP CPP CXXCPP ac_ct_CXX CXXFLAGS CXX ac_ct_FC FCFLAGS FC OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_graphml enable_gmp ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS FC FCFLAGS CXX CXXFLAGS CCC CXXCPP CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures igraph @VERSION@ to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/igraph] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of igraph @VERSION@:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-graphml Disable support for GraphML format --disable-gmp Compile without the GMP library Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory FC Fortran compiler command FCFLAGS Fortran compiler flags CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF igraph configure @VERSION@ generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_fc_try_compile LINENO # --------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_fc_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_fc_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_fc_try_compile # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ------------------------------------- ## ## Report this to csardi.gabor@gmail.com ## ## ------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_cxx_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ------------------------------------- ## ## Report this to csardi.gabor@gmail.com ## ## ------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by igraph $as_me @VERSION@, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers src/config.h" : ${R_HOME=`R RHOME`} if test -z "${R_HOME}"; then echo "could not determine R_HOME" exit 1 fi CC=`"${R_HOME}/bin/R" CMD config CC` CXX=`"${R_HOME}/bin/R" CMD config CXX` FC=`"${R_HOME}/bin/R" CMD config FC` CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS` CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXXFLAGS` CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` FCFLAGS=`"${R_HOME}/bin/R" CMD config FCFLAGS` FLIBS=`"${R_HOME}/bin/R" CMD config FLIBS` ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Fortran compiler, we need to check if it is the GNU compiler ac_ext=${ac_fc_srcext-f} ac_compile='$FC -c $FCFLAGS $ac_fcflags_srcext conftest.$ac_ext >&5' ac_link='$FC -o conftest$ac_exeext $FCFLAGS $LDFLAGS $ac_fcflags_srcext conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_fc_compiler_gnu if test -n "$ac_tool_prefix"; then for ac_prog in gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn nagfor xlf90 f90 pgf90 pghpf epcf90 g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_FC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$FC"; then ac_cv_prog_FC="$FC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_FC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi FC=$ac_cv_prog_FC if test -n "$FC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FC" >&5 $as_echo "$FC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$FC" && break done fi if test -z "$FC"; then ac_ct_FC=$FC for ac_prog in gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn nagfor xlf90 f90 pgf90 pghpf epcf90 g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_FC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_FC"; then ac_cv_prog_ac_ct_FC="$ac_ct_FC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_FC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_FC=$ac_cv_prog_ac_ct_FC if test -n "$ac_ct_FC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FC" >&5 $as_echo "$ac_ct_FC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_FC" && break done if test "x$ac_ct_FC" = x; then FC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac FC=$ac_ct_FC fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for Fortran compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done rm -f a.out # If we don't use `.F' as extension, the preprocessor is not run on the # input file. (Note that this only needs to work for GNU compilers.) ac_save_ext=$ac_ext ac_ext=F { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU Fortran compiler" >&5 $as_echo_n "checking whether we are using the GNU Fortran compiler... " >&6; } if ${ac_cv_fc_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat > conftest.$ac_ext <<_ACEOF program main #ifndef __GNUC__ choke me #endif end _ACEOF if ac_fn_fc_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_fc_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_fc_compiler_gnu" >&5 $as_echo "$ac_cv_fc_compiler_gnu" >&6; } ac_ext=$ac_save_ext ac_test_FCFLAGS=${FCFLAGS+set} ac_save_FCFLAGS=$FCFLAGS FCFLAGS= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $FC accepts -g" >&5 $as_echo_n "checking whether $FC accepts -g... " >&6; } if ${ac_cv_prog_fc_g+:} false; then : $as_echo_n "(cached) " >&6 else FCFLAGS=-g cat > conftest.$ac_ext <<_ACEOF program main end _ACEOF if ac_fn_fc_try_compile "$LINENO"; then : ac_cv_prog_fc_g=yes else ac_cv_prog_fc_g=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_fc_g" >&5 $as_echo "$ac_cv_prog_fc_g" >&6; } if test "$ac_test_FCFLAGS" = set; then FCFLAGS=$ac_save_FCFLAGS elif test $ac_cv_prog_fc_g = yes; then if test "x$ac_cv_fc_compiler_gnu" = xyes; then FCFLAGS="-g -O2" else FCFLAGS="-g" fi else if test "x$ac_cv_fc_compiler_gnu" = xyes; then FCFLAGS="-O2" else FCFLAGS= fi fi if test $ac_compiler_gnu = yes; then GFC=yes else GFC= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "x$ac_cv_fc_compiler_gnu" == xyes; then $as_echo "#define HAVE_GFORTRAN 1" >>confdefs.h fi # Tricky check for C++ compiler, because Autoconf has a weird bug: # http://lists.gnu.org/archive/html/autoconf/2006-03/msg00067.html ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include const char hw[] = "Hello, World\n"; int main () { std::cout << hw; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu cxx_error=no else as_fn_error $? "no C++ compiler found or it cannot create executables" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu LIBS_SAVE=$LIBS LIBS="$LIBS -lm" for ac_func in rintf finite expm1 rint log2 logbl snprintf log1p round fmin stpcpy do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_decl "$LINENO" "stpcpy" "ac_cv_have_decl_stpcpy" "$ac_includes_default" if test "x$ac_cv_have_decl_stpcpy" = xyes; then : $as_echo "#define HAVE_STPCPY_SIGNATURE 1" >>confdefs.h fi LIBS=$LIBS_SAVE ac_fn_c_check_header_mongrel "$LINENO" "sys/times.h" "ac_cv_header_sys_times_h" "$ac_includes_default" if test "x$ac_cv_header_sys_times_h" = xyes; then : $as_echo "#define HAVE_TIMES_H 1" >>confdefs.h fi graphml_support=yes # Check whether --enable-graphml was given. if test "${enable_graphml+set}" = set; then : enableval=$enable_graphml; graphml_support=$enableval else graphml_support=yes fi HAVE_LIBXML=0 if test $graphml_support = yes; then # Extract the first word of "xml2-config", so it can be a program name with args. set dummy xml2-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_XML2CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $XML2CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_XML2CONFIG="$XML2CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_XML2CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_XML2CONFIG" && ac_cv_path_XML2CONFIG="none" ;; esac fi XML2CONFIG=$ac_cv_path_XML2CONFIG if test -n "$XML2CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XML2CONFIG" >&5 $as_echo "$XML2CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "$XML2CONFIG" = "none"; then graphml_support=no else XML2_LIBS=`$XML2CONFIG --libs` XML2_CFLAGS=`$XML2CONFIG --cflags` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for xmlSAXUserParseFile in -lxml2" >&5 $as_echo_n "checking for xmlSAXUserParseFile in -lxml2... " >&6; } if ${ac_cv_lib_xml2_xmlSAXUserParseFile+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lxml2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char xmlSAXUserParseFile (); int main () { return xmlSAXUserParseFile (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_xml2_xmlSAXUserParseFile=yes else ac_cv_lib_xml2_xmlSAXUserParseFile=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xml2_xmlSAXUserParseFile" >&5 $as_echo "$ac_cv_lib_xml2_xmlSAXUserParseFile" >&6; } if test "x$ac_cv_lib_xml2_xmlSAXUserParseFile" = xyes; then : OLDCFLAGS=${CFLAGS} OLDCPPFLAGS=${CPPFLAGS} CFLAGS=${XML2_CFLAGS} CPPFLAGS=${XML2_CFLAGS} ac_fn_c_check_header_mongrel "$LINENO" "libxml/parser.h" "ac_cv_header_libxml_parser_h" "$ac_includes_default" if test "x$ac_cv_header_libxml_parser_h" = xyes; then : HAVE_LIBXML=1 $as_echo "#define HAVE_LIBXML 1" >>confdefs.h CFLAGS="${OLDCFLAGS} ${XML2_CFLAGS}" CPPFLAGS="${OLDCFLAGS} ${XML2_CFLAGS}" else graphml_support=no CFLAGS=${OLDCFLAGS} CPPFLAGS=${OLDCPPFLAGS} fi else graphml_support=no fi fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu HAVE_GMP=0 GMP_LIBS="" gmp_support=no # Check whether --enable-gmp was given. if test "${enable_gmp+set}" = set; then : enableval=$enable_gmp; fi if test "x$enable_gmp" != "xno"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __gmpz_add in -lgmp" >&5 $as_echo_n "checking for __gmpz_add in -lgmp... " >&6; } if ${ac_cv_lib_gmp___gmpz_add+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgmp $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char __gmpz_add (); int main () { return __gmpz_add (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_gmp___gmpz_add=yes else ac_cv_lib_gmp___gmpz_add=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp___gmpz_add" >&5 $as_echo "$ac_cv_lib_gmp___gmpz_add" >&6; } if test "x$ac_cv_lib_gmp___gmpz_add" = xyes; then : ac_fn_cxx_check_header_mongrel "$LINENO" "gmp.h" "ac_cv_header_gmp_h" "$ac_includes_default" if test "x$ac_cv_header_gmp_h" = xyes; then : HAVE_GMP=1 $as_echo "#define HAVE_GMP 1" >>confdefs.h gmp_support=yes GMP_LIBS="-lgmp" fi fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu glpk_support=yes $as_echo "#define HAVE_GLPK 1" >>confdefs.h HAVE_GLPK=1 GLPK_LIBS="" $as_echo "#define IGRAPH_THREAD_LOCAL /**/" >>confdefs.h ac_config_files="$ac_config_files src/Makevars" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by igraph $as_me @VERSION@, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ igraph config.status @VERSION@ configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; "src/Makevars") CONFIG_FILES="$CONFIG_FILES src/Makevars" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi igraph/man/0000755000176000001440000000000012325365704012336 5ustar ripleyusersigraph/man/graph.dfs.Rd0000644000176000001440000001021612240234657014477 0ustar ripleyusers\name{graph.dfs} \alias{graph.dfs} \title{Depth-first search} \description{Depth-first search is an algorithm to traverse a graph. It starts from a root vertex and tries to go quickly as far from as possible.} \usage{ graph.dfs (graph, root, neimode = c("out", "in", "all", "total"), unreachable = TRUE, order = TRUE, order.out = FALSE, father = FALSE, dist = FALSE, in.callback = NULL, out.callback = NULL, extra = NULL, rho = parent.frame()) } \arguments{ \item{graph}{The input graph.} \item{root}{The single root vertex to start the search from. } \item{neimode}{For directed graphs specifies the type of edges to follow. \sQuote{out} follows outgoing, \sQuote{in} incoming edges. \sQuote{all} ignores edge directions completely. \sQuote{total} is a synonym for \sQuote{all}. This argument is ignored for undirected graphs.} \item{unreachable}{Logical scalar, whether the search should visit the vertices that are unreachable from the given root vertex (or vertices). If \code{TRUE}, then additional searches are performed until all vertices are visited.} \item{order}{Logical scalar, whether to return the DFS ordering of the vertices.} \item{order.out}{Logical scalar, whether to return the ordering based on leaving the subtree of the vertex.} \item{father}{Logical scalar, whether to return the father of the vertices.} \item{dist}{Logical scalar, whether to return the distance from the root of the search tree.} \item{in.callback}{If not \code{NULL}, then it must be callback function. This is called whenever a vertex is visited. See details below.} \item{out.callback}{If not \code{NULL}, then it must be callback function. This is called whenever the subtree of a vertex is completed by the algorithm. See details below.} \item{extra}{Additional argument to supply to the callback function.} \item{rho}{The environment in which the callback function is evaluated.} } \details{ The callback functions must have the following arguments: \describe{ \item{graph}{The input graph is passed to the callback function here.} \item{data}{A named numeric vector, with the following entries: \sQuote{vid}, the vertex that was just visited and \sQuote{dist}, its distance from the root of the search tree.} \item{extra}{The extra argument.} } See examples below on how to use the callback functions. } \value{ A named list with the following entries: \item{root}{Numeric scalar. The root vertex that was used as the starting point of the search.} \item{neimode}{Character scalar. The \code{neimode} argument of the function call. Note that for undirected graphs this is always \sQuote{all}, irrespectively of the supplied value.} \item{order}{Numeric vector. The vertex ids, in the order in which they were visited by the search.} \item{order.out}{Numeric vector, the vertex ids, in the order of the completion of their subtree.} \item{father}{Numeric vector. The father of each vertex, i.e. the vertex it was discovered from.} \item{dist}{Numeric vector, for each vertex its distance from the root of the search tree.} Note that \code{order}, \code{order.out}, \code{father}, and \code{dist} might be \code{NULL} if their corresponding argument is \code{FALSE}, i.e. if their calculation is not requested. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{ \code{\link{graph.bfs}} for breadth-first search.} \examples{ ## A graph with two separate trees graph.dfs(graph.tree(10) \%du\% graph.tree(10), root=1, "out", TRUE, TRUE, TRUE, TRUE) ## How to use a callback f.in <- function(graph, data, extra) { cat("in:", paste(collapse=", ", data), "\n") FALSE } f.out <- function(graph, data, extra) { cat("out:", paste(collapse=", ", data), "\n") FALSE } tmp <- graph.dfs(graph.tree(10), root=1, "out", in.callback=f.in, out.callback=f.out) ## Terminate after the first component, using a callback f.out <- function(graph, data, extra) { data['vid'] == 1 } tmp <- graph.dfs(graph.tree(10) \%du\% graph.tree(10), root=1, out.callback=f.out) } \keyword{graphs} igraph/man/edge.connectivity.Rd0000644000176000001440000000617312240234657016253 0ustar ripleyusers\name{edge.connectivity} \alias{edge.connectivity} \alias{edge.disjoint.paths} \alias{graph.adhesion} \concept{Edge connectivity} \concept{Edge-disjoint paths} \concept{Graph adhesion} \title{Edge connectivity.} \description{The edge connectivity of a graph or two vertices, this is recently also called group adhesion.} \usage{ edge.connectivity(graph, source=NULL, target=NULL, checks=TRUE) edge.disjoint.paths(graph, source, target) graph.adhesion(graph, checks=TRUE) } \arguments{ \item{graph}{The input graph.} \item{source}{The id of the source vertex, for \code{edge.connectivity} it can be \code{NULL}, see details below.} \item{target}{The id of the target vertex, for \code{edge.connectivity} it can be \code{NULL}, see details below.} \item{checks}{Logical constant. Whether to check that the graph is connected and also the degree of the vertices. If the graph is not (strongly) connected then the connectivity is obviously zero. Otherwise if the minimum degree is one then the edge connectivity is also one. It is a good idea to perform these checks, as they can be done quickly compared to the connectivity calculation itself. They were suggested by Peter McMahan, thanks Peter. } } \details{ The edge connectivity of a pair of vertices (\code{source} and \code{target}) is the minimum number of edges needed to remove to eliminate all (directed) paths from \code{source} to \code{target}. \code{edge.connectivity} calculates this quantity if both the \code{source} and \code{target} arguments are given (and not \code{NULL}). The edge connectivity of a graph is the minimum of the edge connectivity of every (ordered) pair of vertices in the graph. \code{edge.connectivity} calculates this quantity if neither the \code{source} nor the \code{target} arguments are given (ie. they are both \code{NULL}). A set of edge disjoint paths between two vertices is a set of paths between them containing no common edges. The maximum number of edge disjoint paths between two vertices is the same as their edge connectivity. The adhesion of a graph is the minimum number of edges needed to remove to obtain a graph which is not strongly connected. This is the same as the edge connectivity of the graph. The three functions documented on this page calculate similar properties, more precisely the most general is \code{edge.connectivity}, the others are included only for having more descriptive function names. } \value{ A scalar real value. } \references{Douglas R. White and Frank Harary: The cohesiveness of blocks in social networks: node connectivity and conditional density, TODO: citation} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.maxflow}}, \code{\link{vertex.connectivity}}, \code{\link{vertex.disjoint.paths}}, \code{\link{graph.cohesion}}} \examples{ g <- barabasi.game(100, m=1) g2 <- barabasi.game(100, m=5) edge.connectivity(g, 100, 1) edge.connectivity(g2, 100, 1) edge.disjoint.paths(g2, 100, 1) g <- erdos.renyi.game(50, 5/50) g <- as.directed(g) g <- induced.subgraph(g, subcomponent(g, 1)) graph.adhesion(g) } \keyword{graphs} igraph/man/plot.graph.Rd0000644000176000001440000000573112263024035014677 0ustar ripleyusers\name{plot.igraph} \alias{plot.igraph} \concept{Visualization} \title{Plotting of graphs} \description{\code{plot.graph} is able to plot graphs to any R device. It is the non-interactive companion of the \code{tkplot} function.} \usage{ \S3method{plot}{igraph}(x, axes=FALSE, add=FALSE, xlim=c(-1,1), ylim=c(-1,1), mark.groups=list(), mark.shape=1/2, mark.col=rainbow(length(mark.groups), alpha=0.3), mark.border=rainbow(length(mark.groups), alpha=1), mark.expand=15, \dots) } \arguments{ \item{x}{The graph to plot.} \item{axes}{Logical, whether to plot axes, defaults to FALSE.} \item{add}{Logical scalar, whether to add the plot to the current device, or delete the device's current contents first.} \item{xlim}{The limits for the horizontal axis, it is unlikely that you want to modify this.} \item{ylim}{The limits for the vertical axis, it is unlikely that you want to modify this.} \item{mark.groups}{A list of vertex id vectors. It is interpreted as a set of vertex groups. Each vertex group is highlighted, by plotting a colored smoothed polygon around and \dQuote{under} it. See the arguments below to control the look of the polygons.} \item{mark.shape}{A numeric scalar or vector. Controls the smoothness of the vertex group marking polygons. This is basically the \sQuote{shape} parameter of the \code{\link[graphics]{xspline}} function, its possible values are between -1 and 1. If it is a vector, then a different value is used for the different vertex groups.} \item{mark.col}{A scalar or vector giving the colors of marking the polygons, in any format accepted by \code{\link[graphics]{xspline}}; e.g. numeric color ids, symbolic color names, or colors in RGB.} \item{mark.border}{A scalar or vector giving the colors of the borders of the vertex group marking polygons. If it is \code{NA}, then no border is drawn.} \item{mark.expand}{A numeric scalar or vector, the size of the border around the marked vertex groups. It is in the same units as the vertex sizes. If a vector is given, then different values are used for the different vertex groups.} \item{\dots}{Additional plotting parameters. See \link{igraph.plotting} for the complete list.} } \details{ One convenient way to plot graphs is to plot with \code{\link{tkplot}} first, handtune the placement of the vertices, query the coordinates by the \code{\link{tkplot.getcoords}} function and use them with \code{plot} to plot the graph to any R device.} \value{Returns \code{NULL}, invisibly.} % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{layout}} for different layouts, \code{\link{igraph.plotting}} for the detailed description of the plotting parameters and \code{\link{tkplot}} and \code{\link{rglplot}} for other graph plotting functions. } \examples{ g <- graph.ring(10) \dontrun{plot(g, layout=layout.kamada.kawai, vertex.color="green")} } \keyword{graphs} igraph/man/minimum.size.separators.Rd0000644000176000001440000000560512240234657017437 0ustar ripleyusers\name{minimum.size.separators} \alias{minimum.size.separators} \concept{Minimum size vertex separator} \concept{Vertex separator} \title{Minimum size vertex separators} \description{Find all vertex sets of minimal size whose removal separates the graph into more components} \usage{ minimum.size.separators (graph) } \arguments{ \item{graph}{The input graph. It may be directed, but edge directions are ignored.} } \details{ This function implements the Kanevsky algorithm for finding all minimal-size vertex separators in an undirected graph. See the reference below for the details. In the special case of a fully connected input graph with \eqn{n} vertices, all subsets of size \eqn{n-1} are listed as the result. } \value{ A list of numeric vectors. Each numeric vector is a vertex separator. } \references{ Arkady Kanevsky: Finding all minimum-size separating vertex sets in a graph. \emph{Networks} 23 533--541, 1993. JS Provan and DR Shier: A Paradigm for listing (s,t)-cuts in graphs, \emph{Algorithmica} 15, 351--372, 1996. J. Moody and D. R. White. Structural cohesion and embeddedness: A hierarchical concept of social groups. \emph{American Sociological Review}, 68 103--127, Feb 2003. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{is.separator}} } \examples{ # The graph from the Moody-White paper mw <- graph.formula(1-2:3:4:5:6, 2-3:4:5:7, 3-4:6:7, 4-5:6:7, 5-6:7:21, 6-7, 7-8:11:14:19, 8-9:11:14, 9-10, 10-12:13, 11-12:14, 12-16, 13-16, 14-15, 15-16, 17-18:19:20, 18-20:21, 19-20:22:23, 20-21, 21-22:23, 22-23) # Cohesive subgraphs mw1 <- induced.subgraph(mw, as.character(c(1:7, 17:23))) mw2 <- induced.subgraph(mw, as.character(7:16)) mw3 <- induced.subgraph(mw, as.character(17:23)) mw4 <- induced.subgraph(mw, as.character(c(7,8,11,14))) mw5 <- induced.subgraph(mw, as.character(1:7)) minimum.size.separators(mw) minimum.size.separators(mw1) minimum.size.separators(mw2) minimum.size.separators(mw3) minimum.size.separators(mw4) minimum.size.separators(mw5) # Another example, the science camp network camp <- graph.formula(Harry:Steve:Don:Bert - Harry:Steve:Don:Bert, Pam:Brazey:Carol:Pat - Pam:Brazey:Carol:Pat, Holly - Carol:Pat:Pam:Jennie:Bill, Bill - Pauline:Michael:Lee:Holly, Pauline - Bill:Jennie:Ann, Jennie - Holly:Michael:Lee:Ann:Pauline, Michael - Bill:Jennie:Ann:Lee:John, Ann - Michael:Jennie:Pauline, Lee - Michael:Bill:Jennie, Gery - Pat:Steve:Russ:John, Russ - Steve:Bert:Gery:John, John - Gery:Russ:Michael) lapply(minimum.size.separators(camp), function(x) V(camp)[x]) } \keyword{graphs} igraph/man/graph.structure.Rd0000644000176000001440000004004612251656216015770 0ustar ripleyusers\name{graph.structure} \alias{add.edges} \alias{add.vertices} \alias{delete.edges} \alias{delete.vertices} \alias{[.igraph} \alias{[[.igraph} \alias{[<-.igraph} \alias{+.igraph} \alias{-.igraph} \alias{edge} \alias{edges} \alias{vertex} \alias{vertices} \alias{path} \title{Method for structural manipulation of graphs} \description{These are the methods for simple manipulation of graphs: adding and deleting edges and vertices.} \usage{ \method{[}{igraph}(x, i, j, \dots, from, to, sparse=getIgraphOpt("sparsematrices"), edges=FALSE, drop=TRUE, attr=if (is.weighted(x)) "weight" else NULL) \method{[[}{igraph}(x, i, j, \dots, directed=TRUE, edges=FALSE, exact=TRUE) \method{[}{igraph}(x, i, j, \dots, from, to, attr=if (is.weighted(x)) "weight" else NULL) <- value \method{+}{igraph}(e1, e2) \method{-}{igraph}(e1, e2) vertex(\dots) vertices(\dots) edge(\dots) edges(\dots) path(\dots) add.edges(graph, edges, \dots, attr=list()) add.vertices(graph, nv, \dots, attr=list()) delete.edges(graph, edges) delete.vertices(graph, v) } \arguments{ \item{x,graph,e1}{The graph to work on.} \item{i,j}{Vertex ids or names or logical vectors. See details below.} \item{\dots}{These are currently ignored for the indexing operators. For \code{vertex}, \code{vertices}, \code{edge}, \code{edges} and \code{path} see details below. For \code{add.edges} and \code{add.vertices} these additional parameters will be added as edge/vertex attributes. Note that these arguments have to be named.} \item{from}{A numeric or character vector giving vertex ids or names. Together with the \code{to} argument, it can be used to query/set a sequence of edges. See details below. This argument cannot be present together with any of the \code{i} and \code{j} arguments and if it is present, then the \code{to} argument must be present as well.} \item{to}{A numeric or character vector giving vertex ids or names. Together with the \code{from} argument, it can be used to query/set a sequence of edges. See details below. This argument cannot be present together with any of the \code{i} and \code{j} arguments and if it is present, then the \code{from} argument must be present as well.} \item{sparse}{Logical scalar, whether to use sparse matrix.} \item{directed}{Logical scalar, whether to consider edge directions in directed graphs. It is ignored for undirected graphs.} \item{edges}{Logical scalar, whether to return edge ids.} \item{drop,exact}{These arguments are ignored.} \item{value}{A logical or numeric scalar or \code{NULL}. If \code{FALSE}, \code{NULL} or zero, then the specified edges will be deleted. If \code{TRUE} or a non-zero numeric value, then the specified edges will be added. (Only if they don't yet exist.)} \item{e2}{See details below.} \item{attr}{For the indexing operators: if not \code{NULL}, then it should be the name of an edge attribute. This attribute is queried, or updated to the given value. For \code{add.edges} and \code{add.vertices}: additional edge/vertex attributes to add. This will be concatenated to the other supplied attributes.} \item{nv}{Numeric constant, the number of vertices to add.} \item{v}{Vector sequence, the vertices to remove.} } \details{ There are, by and large, three ways to manipulate the structure of a graph in igraph. The first way is using the \sQuote{\code{[}} and \sQuote{\code{[[}} indexing operators on the graph object, very much like the graph was an adjacency matrix (\code{[}) or an adjacency list (\code{[}). The single bracket indexes the (possibly weighted) adjacency matrix of the graph. The double bracket operator is similar, but queries the adjacencly list of the graph. The details on how to use the indexing operators are discussed below. The addition (\sQuote{\code{+}}) and division (\sQuote{\code{-}}) operators can also be used to add and remove vertices and edges. This form is sometimes more readable, and is usually the best if the user also wants to add attributes, together with the new vertices/edges. Please see the details below. In addition, the four functions, \code{add.vertices}, \code{add.edges}, \code{delete.vertices} and \code{delete.edges} can also be used to manipulate the structure. } \section{The indexing operators}{ The one-bracket (\sQuote{\code{[}}) and two-brackets (\sQuote{\code{[[}}) indexing operators allow relatively straightforward query and update operations on graphs. The one bracket operator works on the (imaginary) adjacency matrix of the graph. Here is what you can do with it: \enumerate{ \item Check whether there is an edge between two vertices (\eqn{v} and \eqn{w}) in the graph: \preformatted{ graph[v, w]} A numeric scalar is returned, one if the edge exists, zero otherwise. \item Extract the (sparse) adjacency matrix of the graph, or part of it: \preformatted{ graph[] graph[1:3,5:6] graph[c(1,3,5),]} The first variants returns the full adjacency matrix, the other two return part of it. \item The \code{from} and \code{to} arguments can be used to check the existence of many edges. In this case, both \code{from} and \code{to} must be present and they must have the same length. They must contain vertex ids or names. A numeric vector is returned, of the same length as \code{from} and \code{to}, it contains ones for existing edges edges and zeros for non-existing ones. Example: \preformatted{ graph[from=1:3, to=c(2,3,5)]}. \item For weighted graphs, the \code{[} operator returns the edge weights. For non-esistent edges zero weights are returned. Other edge attributes can be queried as well, by giving the \code{attr} argument. \item Querying edge ids instead of the existance of edges or edge attributes. E.g. \preformatted{ graph[1, 2, edges=TRUE]} returns the id of the edge between vertices 1 and 2, or zero if there is no such edge. \item Adding one or more edges to a graph. For this the element(s) of the imaginary adjacency matrix must be set to a non-zero numeric value (or \code{TRUE}): \preformatted{ graph[1, 2] <- 1 graph[1:3,1] <- 1 graph[from=1:3, to=c(2,3,5)] <- TRUE} This does not affect edges that are already present in the graph, i.e. no multiple edges are created. \item Adding weighted edges to a graph. The \code{attr} argument contains the name of the edge attribute to set, so it does not have to be \sQuote{weight}: \preformatted{ graph[1, 2, attr="weight"]<- 5 graph[from=1:3, to=c(2,3,5)] <- c(1,-1,4)} If an edge is already present in the network, then only its weigths or other attribute are updated. If the graph is already weighted, then the \code{attr="weight"} setting is implicit, and one does not need to give it explicitly. \item Deleting edges. The replacement syntax allow the deletion of edges, by specifying \code{FALSE} or \code{NULL} as the replacement value: \preformatted{ graph[v, w] <- FALSE} removes the edge from vertex \eqn{v} to vertex \eqn{w}. As this can be used to delete edges between two sets of vertices, either pairwise: \preformatted{ graph[from=v, to=w] <- FALSE} or not: \preformatted{ graph[v, w] <- FALSE } if \eqn{v} and \eqn{w} are vectors of edge ids or names. } The double bracket operator indexes the (imaginary) adjacency list of the graph. This can used for the following operations: \enumerate{ \item Querying the adjacent vertices for one or more vertices: \preformatted{ graph[[1:3,]] graph[[,1:3]]} The first form gives the successors, the second the predessors or the 1:3 vertices. (For undirected graphs they are equivalent.) \item Querying the incident edges for one or more vertices, if the \code{edges} argument is set to \code{TRUE}: \preformatted{ graph[[1:3, , edges=TRUE]] graph[[, 1:3, edges=TRUE]]} \item Querying the edge ids between two sets or vertices, if both indices are used. E.g. \preformatted{ graph[[v, w, edges=TRUE]]} gives the edge ids of all the edges that exist from vertices \eqn{v} to vertices \eqn{w}. } Both the \sQuote{\code{[}} and \sQuote{\code{[[}} operators allow logical indices and negative indices as well, with the usual R semantics. E.g. \preformatted{ graph[degree(graph)==0, 1] <- 1} adds an edge from every isolate vertex to vertex one, and \preformatted{ G <- graph.empty(10) G[-1,1] <- TRUE} creates a star graph. Of course, the indexing operators support vertex names, so instead of a numeric vertex id a vertex can also be given to \sQuote{\code{[}} and \sQuote{\code{[[}}. } \section{The plus operator for adding vertices and edges}{ The plus operator can be used to add vertices or edges to graph. The actual operation that is performed depends on the type of the right hand side argument. \itemize{ \item If is is another igraph graph object and they are both named graphs, then the union of the two graphs are calculated, see \code{\link{graph.union}}. \item If it is another igraph graph object, but either of the two are not named, then the disjoint union of the two graphs is calculated, see \code{\link{graph.disjoint.union}}. \item If it is a numeric scalar, then the specified number of vertices are added to the graph. \item If it is a character scalar or vector, then it is interpreted as the names of the vertices to add to the graph. \item If it is an object created with the \code{vertex} or \code{vertices} function, then new vertices are added to the graph. This form is appropriate when one wants to add some vertex attributes as well. The operands of the \code{vertices} function specifies the number of vertices to add and their attributes as well. The unnamed arguments of \code{vertices} are concatenated and used as the \sQuote{\code{name}} vertex attribute (i.e. vertex names), the named arguments will be added as additional vertex attributes. Examples: \preformatted{ g <- g + vertex(shape="circle", color="red") g <- g + vertex("foo", color="blue") g <- g + vertex("bar", "foobar") g <- g + vertices("bar2", "foobar2", color=1:2, shape="rectangle")} See more examples below. \code{vertex} is just an alias to \code{vertices}, and it is provided for readability. The user should use it if a single vertex is added to the graph. \item If it is an object created with the \code{edge} or \code{edges} function, then new edges will be added to the graph. The new edges and possibly their attributes can be specified as the arguments of the \code{edges} function. The unnamed arguments of \code{edges} are concatenated and used as vertex ids of the end points of the new edges. The named arguments will be added as edge attributes. Examples: \preformatted{ g <- graph.empty() + vertices(letters[1:10]) + vertices("foo", "bar", "bar2", "foobar2") g <- g + edge("a", "b") g <- g + edges("foo", "bar", "bar2", "foobar2") g <- g + edges(c("bar", "foo", "foobar2", "bar2"), color="red", weight=1:2)} See more examples below. \code{edge} is just an alias to \code{edges} and it is provided for readability. The user should use it if a single edge is added to the graph. \item If it is an object created with the \code{path} function, then new edges that form a path are added. The edges and possibly their attributes are specified as the arguments to the \code{path} function. The non-named arguments are concatenated and interpreted as the vertex ids along the path. The remaining arguments are added as edge attributes. Examples: \preformatted{ g <- graph.empty() + vertices(letters[1:10]) g <- g + path("a", "b", "c", "d") g <- g + path("e", "f", "g", weight=1:2, color="red") g <- g + path(c("f", "c", "j", "d"), width=1:3, color="green")} } It is important to note that, although the plus operator is commutative, i.e. is possible to write \preformatted{ graph <- "foo" + graph.empty()} it is not associative, e.g. \preformatted{ graph <- "foo" + "bar" + graph.empty()} results a syntax error, unless parentheses are used: \preformatted{ graph <- "foo" + ( "bar" + graph.empty() )} For clarity, we suggest to always put the graph object on the left hand side of the operator: \preformatted{ graph <- graph.empty() + "foo" + "bar"} } \section{The minus operator for deleting vertices and edges}{ The minus operator (\sQuote{\code{-}}) can be used to remove vertices or edges from the graph. The operation performed is selected based on the type of the right hand side argument: \itemize{ \item If it is an igraph graph object, then the difference of the two graphs is calculated, see \code{\link{graph.difference}}. \item If it is a numeric or character vector, then it is interpreted as a vector of vertex ids and the specified vertices will be deleted from the graph. Example: \preformatted{ g <- graph.ring(10) V(g)$name <- letters[1:10] g <- g - c("a", "b")} \item If \code{e2} is a vertex sequence (e.g. created by the \code{\link{V}} function), then these vertices will be deleted from the graph. \item If it is an edge sequence (e.g. created by the \code{\link{E}} function), then these edges will be deleted from the graph. \item If it is an object created with the \code{vertex} (or the \code{vertices}) function, then all arguments of \code{vertices} are concatenated and the result is interpreted as a vector of vertex ids. These vertices will be removed from the graph. \item If it is an object created with the \code{edge} (or the \code{edges}) function, then all arguments of \code{edges} are concatenated and then interpreted as edges to be removed from the graph. Example: \preformatted{ g <- graph.ring(10) V(g)$name <- letters[1:10] E(g)$name <- LETTERS[1:10] g <- g - edge("e|f") g <- g - edge("H")} \item If it is an object created with the \code{path} function, then all \code{path} arguments are concatenated and then interpreted as a path along which edges will be removed from the graph. Example: \preformatted{ g <- graph.ring(10) V(g)$name <- letters[1:10] g <- g - path("a", "b", "c", "d")} } } \section{More functions to manipulate graph structure}{ \code{add.edges} adds the specified edges to the graph. The ids of the vertices are preserved. The additionally supplied named arguments will be added as edge attributes for the new edges. If an attribute was not present in the original graph, its value for the original edges will be \code{NA}. \code{add.vertices} adds the specified number of isolate vertices to the graph. The ids of the old vertices are preserved. The additionally supplied named arguments will be added as vertex attributes for the new vertices. If an attribute was not present in the original graph, its value is set to \code{NA} for the original vertices. \code{delete.edges} removes the specified edges from the graph. If a specified edge is not present, the function gives an error message, and the original graph remains unchanged. The ids of the vertices are preserved. \code{delete.vertices} removes the specified vertices from the graph together with their adjacent edges. The ids of the vertices are \emph{not} preserved. } \value{For the indexing operators see the description above. The other functions return a new graph.} % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ # 10 vertices named a,b,c,... and no edges g <- graph.empty() + vertices(letters[1:10]) # Add edges to make it a ring g <- g + path(letters[1:10], letters[1], color="grey") # Add some extra random edges g <- g + edges(sample(V(g), 10, replace=TRUE), color="red") g$layout <- layout.circle if (interactive()) { plot(g) } # The old-style operations g <- graph.ring(10) add.edges(g, c(2,6,3,7) ) delete.edges(g, E(g, P=c(1,10, 2,3)) ) delete.vertices(g, c(2,7,8) ) } \keyword{graphs} igraph/man/bipartite.random.game.Rd0000644000176000001440000000442012251656216016776 0ustar ripleyusers\name{bipartite.random.game} \alias{bipartite.random.game} \concept{Random graph model} \concept{Bipartite graph} \title{Bipartite random graphs} \description{Generate bipartite graphs using the Erdos-Renyi model} \usage{ bipartite.random.game(n1, n2, type = c("gnp", "gnm"), p, m, directed = FALSE, mode = c("out", "in", "all")) } \arguments{ \item{n1}{Integer scalar, the number of bottom vertices.} \item{n2}{Integer scalar, the number of top vertices.} \item{type}{Character scalar, the type of the graph, \sQuote{gnp} creates a $G(n,p)$ graph, \sQuote{gnm} creates a $G(n,m)$ graph. See details below.} \item{p}{Real scalar, connection probability for $G(n,p)$ graphs. Should not be given for $G(n,m)$ graphs.} \item{m}{Integer scalar, the number of edges for $G(n,p)$ graphs. Should not be given for $G(n,p)$ graphs.} \item{directed}{Logical scalar, whether to create a directed graph. See also the \code{mode} argument.} \item{mode}{Character scalar, specifies how to direct the edges in directed graphs. If it is \sQuote{out}, then directed edges point from bottom vertices to top vertices. If it is \sQuote{in}, edges point from top vertices to bottom vertices. \sQuote{out} and \sQuote{in} do not generate mutual edges. If this argument is \sQuote{all}, then each edge direction is considered independently and mutual edges might be generated. This argument is ignored for undirected graphs. } } \details{ Similarly to unipartite (one-mode) networks, we can define the $G(n,p)$, and $G(n,m)$ graph classes for bipartite graphs, via their generating process. In $G(n,p)$ every possible edge between top and bottom vertices is realized with probablity $p$, independently of the rest of the edges. In $G(n,m)$, we uniformly choose $m$ edges to realize. } \value{A bipartite igraph graph.} % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{erdos.renyi.game}} for the unipartite version.} \examples{ ## empty graph bipartite.random.game(10, 5, p=0) ## full graph bipartite.random.game(10, 5, p=1) ## random bipartite graph bipartite.random.game(10, 5, p=.1) ## directed bipartite graph, G(n,m) bipartite.random.game(10, 5, type="Gnm", m=20, directed=TRUE, mode="all") } \keyword{graphs}igraph/man/get.incidence.Rd0000644000176000001440000000403512240234657015324 0ustar ripleyusers\name{get.incidence} \alias{get.incidence} \concept{Bipartite graph} \concept{Two-mode network} \concept{Incidence matrix} \title{Incidence matrix of a bipartite graph} \description{This function can return a sparse or dense incidence matrix of a bipartite network. The incidence matrix is an \eqn{n} times \eqn{m} matrix, \eqn{n} and \eqn{m} are the number of vertices of the two kinds.} \usage{ get.incidence(graph, types=NULL, attr=NULL, names=TRUE, sparse=FALSE) } \arguments{ \item{graph}{The input graph. The direction of the edges is ignored in directed graphs.} \item{types}{An optional vertex type vector to use instead of the \code{type} vertex attribute. You must supply this argument if the graph has no \code{type} vertex attribute.} \item{attr}{Either \code{NULL} or a character string giving an edge attribute name. If \code{NULL}, then a traditional incidence matrix is returned. If not \code{NULL} then the values of the given edge attribute are included in the incidence matrix. If the graph has multiple edges, the edge attribute of an arbitrarily chosen edge (for the multiple edges) is included.} \item{names}{Logical scalar, if \code{TRUE} and the vertices in the graph are named (i.e. the graph has a vertex attribute called \code{name}), then vertex names will be added to the result as row and column names. Otherwise the ids of the vertices are used as row and column names.} \item{sparse}{Logical scalar, if it is \code{TRUE} then a sparse matrix is created, you will need the \code{Matrix} package for this.} } \details{ Bipartite graphs have a \code{type} vertex attribute in igraph, this is boolean and \code{FALSE} for the vertices of the first kind and \code{TRUE} for vertices of the second kind. } \value{ A sparse or dense matrix. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{\code{\link{graph.incidence}} for the opposite operation.} \examples{ g <- graph.bipartite( c(0,1,0,1,0,0), c(1,2,2,3,3,4) ) get.incidence(g) } \keyword{graphs} igraph/man/compare.communities.Rd0000644000176000001440000000604112240234657016605 0ustar ripleyusers\name{compare.communities} \alias{compare.communities} \alias{compare.numeric} \alias{compare} \concept{Community structure} \title{Compares community structures using various metrics} \description{ This function assesses the distance between two community structures. } \usage{ \method{compare}{communities}(comm1, comm2, method = c("vi", "nmi", "split.join", "rand", "adjusted.rand")) \method{compare}{numeric}(comm1, comm2, method = c("vi", "nmi", "split.join", "rand", "adjusted.rand")) } \arguments{ \item{comm1}{A \code{\link{communities}} object containing a community structure; or a numeric vector, the membership vector of the first community structure. The membership vector should contain the community id of each vertex, the numbering of the communities starts with one.} \item{comm2}{A \code{\link{communities}} object containing a community structure; or a numeric vector, the membership vector of the second community structure, in the same format as for the previous argument.} \item{method}{Character scalar, the comparison method to use. Possible values: \sQuote{vi} is the variation of information (VI) metric of Meila (2003), \sQuote{nmi} is the normalized mutual information measure proposed by Danon et al. (2005), \sQuote{split.join} is the split-join distance of can Dongen (2000), \sQuote{rand} is the Rand index of Rand (1971), \sQuote{adjusted.rand} is the adjusted Rand index by Hubert and Arabie (1985).} } % \details{} \value{A real number.} \references{ Meila M: Comparing clusterings by the variation of information. In: Scholkopf B, Warmuth MK (eds.). \emph{Learning Theory and Kernel Machines: 16th Annual Conference on Computational Learning Theory and 7th Kernel Workshop}, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1. Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure identification. \emph{J Stat Mech} P09008, 2005. van Dongen S: Performance criteria for graph clustering and Markov cluster experiments. Technical Report INS-R0012, National Research Institute for Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. Rand WM: Objective criteria for the evaluation of clustering methods. \emph{J Am Stat Assoc} 66(336):846-850, 1971. Hubert L and Arabie P: Comparing partitions. \emph{Journal of Classification} 2:193-218, 1985. } \author{Tamas Nepusz \email{ntamas@gmail.com}} \seealso{ \code{\link{walktrap.community}}, \code{\link{edge.betweenness.community}}, \code{\link{fastgreedy.community}}, \code{\link{spinglass.community}} for various community detection methods. } \examples{ g <- graph.famous("Zachary") sg <- spinglass.community(g) le <- leading.eigenvector.community(g) compare(sg, le, method="rand") compare(membership(sg), membership(le)) } \keyword{graphs} igraph/man/rewire.edges.Rd0000644000176000001440000000202412240234657015204 0ustar ripleyusers\name{rewire.edges} \alias{rewire.edges} \title{Rewires the endpoints of the edges of a graph randomly} \description{This function rewires the endpoints of the edges with a constant probability uniformly randomly to a new vertex in a graph. } \usage{ rewire.edges(graph, prob, loops=FALSE, multiple=FALSE) } \arguments{ \item{graph}{The input graph} \item{prob}{The rewiring probability, a real number between zero and one.} \item{loops}{Logical scalar, whether loop edges are allowed in the rewired graph.} \item{multiple}{Logical scalar, whether multiple edges are allowed int the generated graph.} } \details{ Note that this function might create graphs with multiple and/or loop edges. } \value{A new graph object.} %\references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} %\seealso{} \examples{ # Some random shortcuts shorten the distances on a lattice g <- graph.lattice( length=100, dim=1, nei=5 ) average.path.length(g) g <- rewire.edges( g, prob=0.05 ) average.path.length(g) } \keyword{graphs} igraph/man/scg.grouping.Rd0000644000176000001440000001224012251656216015230 0ustar ripleyusers\name{scgGrouping} \alias{scgGrouping} \title{SCG Problem Solver} \description{ This function solves the Spectral Coarse Graining (SCG) problem; either exactly, or approximately but faster. } \usage{ scgGrouping(V, nt, mtype = c("symmetric", "laplacian", "stochastic"), algo = c("optimum", "interv_km", "interv","exact_scg"), p = NULL, maxiter = 100) } \arguments{ \item{V}{A numeric matrix of (eigen)vectors to be preserved by the coarse graining (the vectors are to be stored column-wise in \code{V}).} \item{nt}{A vector of positive integers of length one or equal to \code{length(ev)}. When \code{algo} = \dQuote{optimum}, \code{nt} contains the number of groups used to partition each eigenvector separately. When \code{algo} is equal to \dQuote{interv\_km} or \dQuote{interv}, \code{nt} contains the number of intervals used to partition each eigenvector. The same partition size or number of intervals is used for each eigenvector if \code{nt} is a single integer. When \code{algo} = \dQuote{exact\_cg} this parameter is ignored.} \item{mtype}{The type of semi-projectors used in the SCG. For now \dQuote{symmetric}, \dQuote{laplacian} and \dQuote{stochastic} are available.} \item{algo}{The algorithm used to solve the SCG problem. Possible values are \dQuote{optimum}, \dQuote{interv\_km}, \dQuote{interv} and \dQuote{exact\_scg}.} \item{p}{A probability vector of length equal to \code{nrow(V)}. \code{p} is the stationary probability distribution of a Markov chain when \code{mtype} = \dQuote{stochastic}. This parameter is ignored in all other cases.} \item{maxiter}{A positive integer giving the maximum number of iterations of the k-means algorithm when \code{algo} = \dQuote{interv\_km}. This parameter is ignored in all other cases.} } \details{ The algorithm \dQuote{optimum} solves exactly the SCG problem for each eigenvector in \code{V}. The running time of this algorithm is \eqn{O(\max nt \cdot m^2)}{O(max(nt) m^2)} for the symmetric and laplacian matrix problems (i.e. when \code{mtype} is \dQuote{symmetric} or \dQuote{laplacian}. It is \eqn{O(m^3)} for the stochastic problem. Here \eqn{m} is the number of rows in \code{V}. In all three cases, the memory usage is \eqn{O(m^2)}. The algorithms \dQuote{interv} and \dQuote{interv\_km} solve approximately the SCG problem by performing a (for now) constant binning of the components of the eigenvectors, that is \code{nt[i]} constant-size bins are used to partition \code{V[,i]}. When \code{algo} = \dQuote{interv\_km}, the (Lloyd) k-means algorithm is run on each partition obtained by \dQuote{interv} to improve accuracy. Once a minimizing partition (either exact or approximate) has been found for each eigenvector, the final grouping is worked out as follows: two vertices are grouped together in the final partition if they are grouped together in each minimizing partition. In general the size of the final partition is not known in advance when \code{ncol(V)}>1. Finally, the algorithm \dQuote{exact\_scg} groups the vertices with equal components in each eigenvector. The last three algorithms essentially have linear running time and memory load. } \value{ A vector of \code{nrow(V)} integers giving the group label of each object (vertex) in the partition. } \references{ D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, Shrinking Matrices while Preserving their Eigenpairs with Application to the Spectral Coarse Graining of Graphs. Submitted to \emph{SIAM Journal on Matrix Analysis and Applications}, 2008. \url{http://people.epfl.ch/david.morton} } \author{David Morton de Lachapelle \email{david.morton@epfl.ch}, \email{david.mortondelachapelle@swissquote.ch}} \seealso{\link{SCG} for a detailed introduction. \code{\link{scg}}, \code{\link{scgNormEps}}} \examples{ ## We are not running these examples any more, because they ## take a long time to run and this is against the CRAN repository ## policy. Copy and paste them by hand to your R prompt if ## you want to run them. \dontrun{ # eigenvectors of a random symmetric matrix M <- matrix(rexp(10^6), 10^3, 10^3) M <- (M + t(M))/2 V <- eigen(M, symmetric=TRUE)$vectors[,c(1,2)] # displays size of the groups in the final partition gr <- scgGrouping(V, nt=c(2,3)) col <- rainbow(max(gr)) plot(table(gr), col=col, main="Group size", xlab="group", ylab="size") ## comparison with the grouping obtained by kmeans ## for a partition of same size gr.km <- kmeans(V,centers=max(gr), iter.max=100, nstart=100)$cluster op <- par(mfrow=c(1,2)) plot(V[,1], V[,2], col=col[gr], main = "SCG grouping", xlab = "1st eigenvector", ylab = "2nd eigenvector") plot(V[,1], V[,2], col=col[gr.km], main = "K-means grouping", xlab = "1st eigenvector", ylab = "2nd eigenvector") par(op) ## kmeans disregards the first eigenvector as it ## spreads a much smaller range of values than the second one ### comparing optimal and k-means solutions ### in the one-dimensional case. x <- rexp(2000, 2) gr.true <- scgGrouping(cbind(x), 100) gr.km <- kmeans(x, 100, 100, 300)$cluster scgNormEps(cbind(x), gr.true) scgNormEps(cbind(x), gr.km) } } \keyword{graphs} igraph/man/interconnected.islands.Rd0000644000176000001440000000171712251656216017272 0ustar ripleyusers\name{interconnected.islands} \alias{interconnected.islands.game} \concept{Random graph model} \title{A graph with subgraphs that are each a random graph.} \description{ Create a number of Erdos-Renyi random graphs with identical parameters, and connect them with the specified number of edges. } \usage{ interconnected.islands.game (islands.n, islands.size, islands.pin, n.inter) } \arguments{ \item{islands.n}{The number of islands in the graph.} \item{islands.size}{The size of islands in the graph.} \item{islands.pin}{The probability to create each possible edge into each island.} \item{n.inter}{The number of edges to create between two islands.} } % \details{} \value{ An igraph graph. } % \references{} \author{Samuel Thiriot (\url{http://samuelthiriot.res-ear.ch/})} \seealso{\code{\link{erdos.renyi.game}}} \examples{ g <- interconnected.islands.game(3, 10, 5/10, 1) oc <- optimal.community(g) oc } \keyword{graphs} igraph/man/minimal.st.separators.Rd0000644000176000001440000000253512240234657017065 0ustar ripleyusers\name{minimal.st.separators} \alias{minimal.st.separators} \concept{Minimal (s,t) separators} \concept{Vertex separator} \title{Minimum size vertex separators} \description{List all vertex sets that are minimal (s,t) separators for some s and t, in an undirected graph.} \usage{ minimal.st.separators(graph) } \arguments{ \item{graph}{The input graph. It may be directed, but edge directions are ignored.} } \details{ A \eqn{(s,t)} vertex separator is a set of vertices, such that after their removal from the graph, there is no path between \eqn{s} and \eqn{t} in the graph. A \eqn{(s,t)} vertex separator is minimal if none of its subsets is an \eqn{(s,t)} vertex separator. } \value{ A list of numeric vectors. Each vector contains a vertex set (defined by vertex ids), each vector is an (s,t) separator of the input graph, for some \eqn{s} and \eqn{t}. } \references{ Anne Berry, Jean-Paul Bordat and Olivier Cogis: Generating All the Minimal Separators of a Graph, In: Peter Widmayer, Gabriele Neyer and Stephan Eidenbenz (editors): \emph{Graph-theoretic concepts in computer science}, 1665, 167--172, 1999. Springer. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} %\seealso{} \examples{ ring <- graph.ring(4) minimal.st.separators(ring) chvatal <- graph.famous("chvatal") minimal.st.separators(chvatal) } \keyword{graphs} igraph/man/forest.fire.game.Rd0000644000176000001440000000607312240234657015767 0ustar ripleyusers\name{forest.fire.game} \alias{forest.fire.game} \concept{Random graph model} \concept{Forest fire model} \title{Forest Fire Network Model} \description{This is a growing network model, which resembles of how the forest fire spreads by igniting trees close by.} \usage{ forest.fire.game (nodes, fw.prob, bw.factor = 1, ambs = 1, directed = TRUE) } \arguments{ \item{nodes}{The number of vertices in the graph.} \item{fw.prob}{The forward burning probability, see details below.} \item{bw.factor}{The backward burning ratio. The backward burning probability is calculated as \code{bw.factor*fw.prob}.} \item{ambs}{The number of ambassador vertices.} \item{directed}{Logical scalar, whether to create a directed graph.} } \details{ The forest fire model intends to reproduce the following network characteristics, observed in real networks: \itemize{ \item Heavy-tailed in-degree distribution. \item Heavy-tailed out-degree distribution. \item Communities. \item Densification power-law. The network is densifying in time, according to a power-law rule. \item Shrinking diameter. The diameter of the network decreases in time. } The network is generated in the following way. One vertex is added at a time. This vertex connects to (cites) \code{ambs} vertices already present in the network, chosen uniformly random. Now, for each cited vertex \eqn{v} we do the following procedure: \enumerate{ \item We generate two random number, \eqn{x} and \eqn{y}, that are geometrically distributed with means \eqn{p/(1-p)} and \eqn{rp(1-rp)}. (\eqn{p} is \code{fw.prob}, \eqn{r} is \code{bw.factor}.) The new vertex cites \eqn{x} outgoing neighbors and \eqn{y} incoming neighbors of \eqn{v}, from those which are not yet cited by the new vertex. If there are less than \eqn{x} or \eqn{y} such vertices available then we cite all of them. \item The same procedure is applied to all the newly cited vertices. } } \note{ The version of the model in the published paper is incorrect in the sense that it cannot generate the kind of graphs the authors claim. A corrected version is available from \url{http://www.cs.cmu.edu/~jure/pubs/powergrowth-tkdd.pdf}, our implementation is based on this. } \value{A simple graph, possibly directed if the \code{directed} argument is \code{TRUE}.} \references{ Jure Leskovec, Jon Kleinberg and Christos Faloutsos. Graphs over time: densification laws, shrinking diameters and possible explanations. \emph{KDD '05: Proceeding of the eleventh ACM SIGKDD international conference on Knowledge discovery in data mining}, 177--187, 2005. } \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{ \code{\link{barabasi.game}} for the basic preferential attachment model. } \examples{ g <- forest.fire.game(10000, fw.prob=0.37, bw.factor=0.32/0.37) dd1 <- degree.distribution(g, mode="in") dd2 <- degree.distribution(g, mode="out") if (interactive()) { plot(seq(along=dd1)-1, dd1, log="xy") points(seq(along=dd2)-1, dd2, col=2, pch=2) } } \keyword{graphs} igraph/man/infomap.Rd0000644000176000001440000000473212251656216014263 0ustar ripleyusers\name{infomap.community} \alias{infomap.community} \concept{Community structure} \title{Infomap community finding} \description{Find community structure that minimizes the expected description length of a random walker trajectory} \usage{ infomap.community (graph, e.weights = NULL, v.weights = NULL, nb.trials = 10, modularity = TRUE) } \arguments{ \item{graph}{The input graph.} \item{e.weights}{If not \code{NULL}, then a numeric vector of edge weights. The length must match the number of edges in the graph. By default the \sQuote{\code{weight}} edge attribute is used as weights. If it is not present, then all edges are considered to have the same weight.} \item{v.weights}{If not \code{NULL}, then a numeric vector of vertex weights. The length must match the number of vertices in the graph. By default the \sQuote{\code{weight}} vertex attribute is used as weights. If it is not present, then all vertices are considered to have the same weight.} \item{nb.trials}{The number of attempts to partition the network (can be any integer value equal or larger than 1).} \item{modularity}{Logical scalar, whether to calculate the modularity score of the detected community structure.} } \details{ Please see the details of this method in the references given below. } \value{ \code{infomap.community} returns a \code{\link{communities}} object, please see the \code{\link{communities}} manual page for details. } \references{ The original paper: M. Rosvall and C. T. Bergstrom, Maps of information flow reveal community structure in complex networks, \emph{PNAS} 105, 1118 (2008) \url{http://dx.doi.org/10.1073/pnas.0706851105}, \url{http://arxiv.org/abs/0707.0609} A more detailed paper: M. Rosvall, D. Axelsson, and C. T. Bergstrom, The map equation, \emph{Eur. Phys. J. Special Topics} 178, 13 (2009). \url{http://dx.doi.org/10.1140/epjst/e2010-01179-1}, \url{http://arxiv.org/abs/0906.1405}. } \author{ Martin Rosvall (\url{http://www.tp.umu.se/~rosvall/}) wrote the original C++ code. This was ported to be more igraph-like by Emmanuel Navarro (\url{http://www.irit.fr/~Emmanuel.Navarro/}). The R interface and some cosmetics was done by Gabor Csardi \email{csardi.gabor@gmail.com}. } \seealso{Other community finding methods and \code{\link{communities}}.} \examples{ ## Zachary's karate club g <- graph.famous("Zachary") imc <- infomap.community(g) membership(imc) communities(imc) } \keyword{graphs} igraph/man/get.edge.ids.Rd0000644000176000001440000000374312240234657015072 0ustar ripleyusers\name{get.edge.ids} \alias{get.edge.ids} \title{Find the edge ids based on the incident vertices of the edges} \description{ Find the edges in an igraph graph that have the specified end points. This function handles multi-graph (graphs with multiple edges) and can consider or ignore the edge directions in directed graphs. } \usage{ get.edge.ids(graph, vp, directed = TRUE, error = FALSE, multi = FALSE) } \arguments{ \item{graph}{The input graph.} \item{vp}{The indicent vertices, given as vertex ids or symbolic vertex names. They are interpreted pairwise, i.e. the first and second are used for the first edge, the third and fourth for the second, etc.} \item{directed}{Logical scalar, whether to consider edge directions in directed graphs. This argument is ignored for undirected graphs.} \item{error}{Logical scalar, whether to report an error if an edge is not found in the graph. If \code{FALSE}, then no error is reported, and zero is returned for the non-existant edge(s).} \item{multi}{Logical scalar, whether to handle multiple edges properly. If \code{FALSE}, and a pair of vertices are given twice (or more), then always the same edge id is reported back for them. If \code{TRUE}, then the edge ids of multiple edges are correctly reported.} } \details{ igraph vertex ids are natural numbers, starting from one, up to the number of vertices in the graph. Similarly, edges are also numbered from one, up to the number of edges. This function allows finding the edges of the graph, via their incident vertices. } \value{A numeric vector of edge ids, one for each pair of input vertices. If there is no edge in the input graph for a given pair of vertices, then zero is reported. (If the \code{error} argument is \code{FALSE}.) } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \examples{ g <- graph.ring(10) ei <- get.edge.ids(g, c(1,2, 4,5)) E(g)[ei] ## non-existant edge get.edge.ids(g, c(2,1, 1,4, 5,4)) } \keyword{graphs} igraph/man/graph.constructors.Rd0000644000176000001440000001650612240234657016503 0ustar ripleyusers\name{graph.constructors} \alias{graph.constructors} \alias{graph.empty} \alias{graph} \alias{graph.star} \alias{graph.lattice} \alias{graph.ring} \alias{graph.tree} \alias{graph.full} \alias{graph.full.citation} \alias{graph.atlas} \alias{graph.edgelist} \alias{graph.extended.chordal.ring} \concept{Tree} \concept{Lattice} \concept{Star graph} \concept{Graph Atlas} \concept{Empty graph} \concept{Full graph} \title{Various methods for creating graphs} \description{These method can create various (mostly regular) graphs: empty graphs, graphs with the given edges, graphs from adjacency matrices, star graphs, lattices, rings, trees.} \usage{ graph.empty(n=0, directed=TRUE) graph(edges, n=max(edges), directed=TRUE) graph.star(n, mode = c("in", "out", "mutual", "undirected"), center = 1) graph.lattice(dimvector = NULL, length = NULL, dim = NULL, nei = 1, directed = FALSE, mutual = FALSE, circular = FALSE, \dots) graph.ring(n, directed = FALSE, mutual = FALSE, circular=TRUE) graph.tree(n, children = 2, mode=c("out", "in", "undirected")) graph.full(n, directed = FALSE, loops = FALSE) graph.full.citation(n, directed = TRUE) graph.atlas(n) graph.edgelist(el, directed=TRUE) graph.extended.chordal.ring(n, w) } \arguments{ \item{edges}{Numeric vector defining the edges, the first edge points from the first element to the second, the second edge from the third to the fourth, etc.} \item{directed}{Logical, if TRUE a directed graph will be created. Note that for while most constructors the default is TRUE, for \code{graph.lattice} and \code{graph.ring} it is FALSE. For \code{graph.star} the \code{mode} argument should be used for creating an undirected graph.} \item{n}{The number of vertices in the graph for most functions. For \code{graph} this parameter is ignored if there is a bigger vertex id in \code{edges}. This means that for this function it is safe to supply zero here if the vertex with the largest id is not an isolate. For \code{graph.atlas} this is the number (id) of the graph to create. } \item{mode}{ For \code{graph.star} it defines the direction of the edges, \code{in}: the edges point \emph{to} the center, \code{out}: the edges point \emph{from} the center, \code{mutual}: a directed star is created with mutual edges, \code{undirected}: the edges are undirected. For \code{igraph.tree} this parameter defines the direction of the edges. \code{out} indicates that the edges point from the parent to the children, \code{in} indicates that they point from the children to their parents, while \code{undirected} creates an undirected graph. } \item{center}{For \code{graph.star} the center vertex of the graph, by default the first vertex.} \item{dimvector}{A vector giving the size of the lattice in each dimension, for \code{graph.lattice}.} \item{nei}{The distance within which (inclusive) the neighbors on the lattice will be connected. This parameter is not used right now.} \item{mutual}{Logical, if TRUE directed lattices will be mutually connected.} \item{circular}{Logical, if TRUE the lattice or ring will be circular.} \item{length}{Integer constant, for regular lattices, the size of the lattice in each dimension.} \item{dim}{Integer constant, the dimension of the lattice.} \item{children}{Integer constant, the number of children of a vertex (except for leafs) for \code{graph.tree}.} \item{loops}{If TRUE also loops edges (self edges) are added.} \item{graph}{An object.} \item{el}{An edge list, a two column matrix, character or numeric. See details below.} \item{w}{A matrix which specifies the extended chordal ring. See details below.} \item{\dots}{Currently ignored.} } \details{All these functions create graphs in a deterministic way. \code{graph.empty} is the simplest one, this creates an empty graph. \code{graph} creates a graph with the given edges. \code{graph.star} creates a star graph, in this every single vertex is connected to the center vertex and nobody else. \code{graph.lattice} is a flexible function, it can create lattices of arbitrary dimensions, periodic or unperiodic ones. It has two forms. In the first form you only supply \code{dimvector}, but not \code{length} and \code{dim}. In the second form you omit \code{dimvector} and supply \code{length} and \code{dim}. \code{graph.ring} is actually a special case of \code{graph.lattice}, it creates a one dimensional circular lattice. \code{graph.tree} creates regular trees. \code{graph.full} simply creates full graphs. \code{graph.full.citation} creates a full citation graph. This is a directed graph, where every i->j edge is present if and only if ji+w[ij]} is added if \code{i+w[ij]} is less than the number of total nodes. See also Kotsis, G: Interconnection Topologies for Parallel Processing Systems, PARS Mitteilungen 11, 1-6, 1993. } \value{Every function documented here returns a \code{graph} object.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.adjacency}} to create graphs from adjacency matrices, \code{\link{graph.formula}} for a handy way to create small graphs, \code{\link{graph.data.frame}} for an easy way to create graphs with many edge/vertex attributes. } \examples{ g1 <- graph.empty() g2 <- graph( c(1,2,2,3,3,4,5,6), directed=FALSE ) g5 <- graph.star(10, mode="out") g6 <- graph.lattice(c(5,5,5)) g7 <- graph.lattice(length=5, dim=3) g8 <- graph.ring(10) g9 <- graph.tree(10, 2) g10 <- graph.full(5, loops=TRUE) g11 <- graph.full.citation(10) g12 <- graph.atlas(sample(0:1252, 1)) el <- matrix( c("foo", "bar", "bar", "foobar"), nc=2, byrow=TRUE) g13 <- graph.edgelist(el) g15 <- graph.extended.chordal.ring(15, matrix(c(3,12,4,7,8,11), nr=2)) } \keyword{graphs} igraph/man/dendPlot.communities.Rd0000644000176000001440000000765312240234657016742 0ustar ripleyusers\name{dendPlot.communities} \alias{dendPlot.communities} \concept{Dendrograms} \concept{Community structure} \title{Community structure dendrogram plots} \description{Plot a hierarchical community structure as a dendrogram.} \usage{ \method{dendPlot}{communities}(x, mode = getIgraphOpt("dend.plot.type"), \dots, use.modularity = FALSE) } \arguments{ \item{x}{An object containing the community structure of a graph. See \code{\link{communities}} for details.} \item{mode}{Which dendrogram plotting function to use. See details below.} \item{\dots}{Additional arguments to supply to the dendrogram plotting function.} \item{use.modularity}{Logical scalar, whether to use the modularity values to define the height of the branches.} } \details{ \code{dendPlot} supports three different plotting functions, selected via the \code{mode} argument. By default the plotting function is taken from the \code{dend.plot.type} igraph option, and it has for possible values: \itemize{ \item \code{auto} Choose automatically between the plotting functions. As \code{plot.phylo} is the most sophisticated, that is choosen, whenever the \code{ape} package is available. Otherwise \code{plot.hclust} is used. \item \code{phylo} Use \code{plot.phylo} from the \code{ape} package. \item \code{hclust} Use \code{plot.hclust} from the \code{stats} package. \item \code{dendrogram} Use \code{plot.dendrogram} from the \code{stats} package. } The different plotting functions take different sets of arguments. When using \code{plot.phylo} (\code{mode="phylo"}), we have the following syntax: \preformatted{ dendPlot(x, mode="phylo", colbar = rainbow(11, start=0.7, end=0.1), edge.color = NULL, use.edge.length = FALSE, \dots) } The extra arguments not documented above: \itemize{ \item \code{colbar} Color bar for the edges. \item \code{edge.color} Edge colors. If \code{NULL}, then the \code{colbar} argument is used. \item \code{use.edge.length} Passed to \code{plot.phylo}. \item \code{dots} Attitional arguments to pass to \code{plot.phylo}. } The syntax for \code{plot.hclust} (\code{mode="hclust"}): \preformatted{ dendPlot(x, mode="hclust", rect = 0, colbar = rainbow(rect), hang = 0.01, ann = FALSE, main = "", sub = "", xlab = "", ylab = "", \dots) } The extra arguments not documented above: \itemize{ \item \code{rect} A numeric scalar, the number of groups to mark on the dendrogram. The dendrogram is cut into exactly \code{rect} groups and they are marked via the \code{rect.hclust} command. Set this to zero if you don't want to mark any groups. \item \code{colbar} The colors of the rectanges that mark the vertex groups via the \code{rect} argument. \item \code{hang} Where to put the leaf nodes, this corresponds to the \code{hang} argument of \code{plot.hclust}. \item \code{ann} Whether to annotate the plot, the \code{ann} argument of \code{plot.hclust}. \item \code{main} The main title of the plot, the \code{main} argument of \code{plot.hclust}. \item \code{sub} The sub-title of the plot, the \code{sub} argument of \code{plot.hclust}. \item \code{xlab} The label on the horizontal axis, passed to \code{plot.hclust}. \item \code{ylab} The label on the vertical axis, passed to \code{plot.hclust}. \item \code{dots} Attitional arguments to pass to \code{plot.hclust}. } The syntax for \code{plot.dendrogram} (\code{mode="dendrogram"}): \preformatted{ dendPlot(x, \dots) } The extra arguments are simply passed to \code{as.dendrogram}. } \value{ Returns whatever the return value was from the plotting function, \code{plot.phylo}, \code{plot.dendrogram} or \code{plot.hclust}. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } % \seealso{} \examples{ karate <- graph.famous("Zachary") fc <- fastgreedy.community(karate) dendPlot(fc) } \keyword{graphs} igraph/man/centralization.Rd0000644000176000001440000001441012251656216015652 0ustar ripleyusers\name{centralization} \alias{centralization} \alias{centralize.scores} \alias{centralization.degree} \alias{centralization.closeness} \alias{centralization.betweenness} \alias{centralization.evcent} \alias{centralization.degree.tmax} \alias{centralization.closeness.tmax} \alias{centralization.betweenness.tmax} \alias{centralization.evcent.tmax} \title{Centralization of a graph.tmax} \description{ Centralization is a method for creating a graph level centralization measure from the centrality scores of the vertices. } \usage{ centralize.scores (scores, theoretical.max, normalized = TRUE) centralization.degree (graph, mode = c("all", "out", "in", "total"), loops = TRUE, normalized = TRUE) centralization.closeness (graph, mode = c("out", "in", "all", "total"), normalized = TRUE) centralization.betweenness (graph, directed = TRUE, nobigint = TRUE, normalized = TRUE) centralization.evcent (graph, directed = FALSE, scale = TRUE, options = igraph.arpack.default, normalized = TRUE) centralization.degree.tmax (graph = NULL, nodes = 0, mode = c("all", "out", "in", "total"), loops = FALSE) centralization.closeness.tmax (graph = NULL, nodes = 0, mode = c("out", "in", "all", "total")) centralization.betweenness.tmax (graph = NULL, nodes = 0, directed = TRUE) centralization.evcent.tmax (graph = NULL, nodes = 0, directed = FALSE, scale = TRUE) } \arguments{ \item{scores}{The vertex level centrality scores.} \item{theoretical.max}{Real scalar. The graph level centrality score of the most centralized graph with the same number of vertices as the graph under study. This is only used if the \code{normalized} argument is set to \code{TRUE}.} \item{normalized}{Logical scalar. Whether to normalize the graph level centrality score by dividing the supplied theoretical maximum.} \item{graph}{The input graph. For the \dQuote{tmax} functions it can be \code{NULL}, see the details below.} \item{mode}{This is the same as the \code{mode} argument of \code{degree} and \code{closeness}.} \item{loops}{Logical scalar, whether to consider loops edges when calculating the degree.} \item{directed}{logical scalar, whether to use directed shortest paths for calculating betweenness.} \item{nobigint}{Logical scalar, whether to use big integers for the betweenness calculation. This argument is passed to the \code{\link{betweenness}} function.} \item{scale}{Whether to rescale the eigenvector centrality scores, such that the maximum score is one.} \item{nodes}{The number of vertices. This is ignored if the graph is given.} \item{options}{This is passed to \code{\link{evcent}}, the options for the ARPACK eigensolver.} } \details{ Centralization is a general method for calculating a graph-level centrality score based on node-level centrality measure. The formula for this is \deqn{C(G)=\sum_v (\max_w c_w - c_v),}{ C(G)=sum( max(c(w), w) - c(v),v),} where \eqn{c_v}{c(v)} is the centrality of vertex \eqn{v}. The graph-level centrality score can be normalized by dividing by the maximum theoretical score for a graph with the same number of vertices, using the same parameters, e.g. directedness, whether we consider loop edges, etc. For degree, closeness and betweenness the most centralized structure is some version of the star graph, in-star, out-star or undirected star. For eigenvector centrality the most centralized structure is the graph with a single edge (and potentially many isolates). \code{centralize.scores} using the general centralization formula to calculate a graph-level score from vertex-level scores. \code{centralization.degree}, \code{centralization.closeness}, \code{centralization.betweenness} calculate both the vertex-level and the graph-level indices. \code{centralization.degree.tmax}, \code{centralization.closeness.tmax}, \code{centralization.betweenness.tmax} and \code{centralization.evcent.tmax} return the theoretical maximum scores. They operate in two modes. In the first mode, a graph is given and the maximum score is calculated based on that. E.g. the number of vertices and directedness is taken from this graph. The other way to call these functions is to omit the \code{graph} argument, but explicitly specify the rest of the arguments. } \value{ For \code{centralize.scores} a real scalar. For \code{centralization.degree}, \code{centralization.closeness} and \code{centralization.betweenness} a named list with the following components: \item{res}{The node-level centrality scores.} \item{centralization}{The graph level centrality index.} \item{theoretical_max}{The maximum theoretical graph level centralization score for a graph with the given number of vertices, using the same parameters. If the \code{normalized} argument was \code{TRUE}, then the result was divided by this number.} For \code{centralization.evcent} a named list with the following components: \item{vector}{The node-level centrality scores.} \item{value}{The corresponding eigenvalue.} \item{options}{ARPACK options, see the return value of \code{\link{evcent}} for details.} \item{centralization}{The graph level centrality index.} \item{theoretical_max}{The same as above, the theoretical maximum centralization score for a graph with the same number of vertices.} For \code{centralization.degree.tmax}, \code{centralization.closeness.tmax}, \code{centralization.betweenness.tmax} and \code{centralization.evcent.tmax} a real scalar. } \references{ Freeman, L.C. (1979). Centrality in Social Networks I: Conceptual Clarification. \emph{Social Networks} 1, 215--239. Wasserman, S., and Faust, K. (1994). \emph{Social Network Analysis: Methods and Applications.} Cambridge University Press. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} %\seealso{} \examples{ # A BA graph is quite centralized g <- ba.game(1000, m=4) centralization.degree(g)$centralization centralization.closeness(g, mode="all")$centralization centralization.evcent(g, directed=FALSE)$centralization # The most centralized graph according to eigenvector centrality g0 <- graph( c(2,1), n=10, dir=FALSE ) g1 <- graph.star(10, mode="undirected") centralization.evcent(g0)$centralization centralization.evcent(g1)$centralization } \keyword{graphs} igraph/man/graph.lcf.Rd0000644000176000001440000000213512240234657014470 0ustar ripleyusers\name{graph.lcf} \alias{graph.lcf} \concept{LCF notation} \title{Creating a graph from LCF notation} \description{LCF is short for Lederberg-Coxeter-Frucht, it is a concise notation for 3-regular Hamiltonian graphs. It constists of three parameters, the number of vertices in the graph, a list of shifts giving additional edges to a cycle backbone and another integer giving how many times the shifts should be performed. See \url{http://mathworld.wolfram.com/LCFNotation.html} for details.} \usage{ graph.lcf(n, shifts, repeats) } \arguments{ \item{n}{Integer, the number of vertices in the graph.} \item{shifts}{Integer vector, the shifts.} \item{repeats}{Integer constant, how many times to repeat the shifts.} } %\details{} \value{A graph object.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{graph}} can create arbitrary graphs, see also the other functions on the its manual page for creating special graphs. } \examples{ # This is the Franklin graph: g1 <- graph.lcf(12, c(5,-5), 6) g2 <- graph.famous("Franklin") graph.isomorphic.vf2(g1, g2) } \keyword{graphs} igraph/man/vertex.shape.pie.Rd0000644000176000001440000000323412240234657016015 0ustar ripleyusers\name{Pie charts as vertices} \alias{vertex.shape.pie} \concept{Vertex shapes} \title{Using pie charts as vertices in graph plots} \description{ More complex vertex images can be used to express addtional information about vertices. E.g. pie charts can be used as vertices, to denote vertex classes, fuzzy classification of vertices, etc. } \details{ The vertex shape \sQuote{pie} makes igraph draw a pie chart for every vertex. There are some extra graphical vertex parameters that specify how the pie charts will look like: \describe{ \item{pie}{Numeric vector, gives the sizes of the pie slices.} \item{pie.color}{A list of color vectors to use for the pies. If it is a list of a single vector, then this is used for all pies. It the color vector is shorter than the number of areas in a pie, then it is recycled.} \item{pie.border}{The color of the border line of the pie charts, in the same format as \code{pie.color}.} \item{pie.angle}{The slope of shading lines, given as an angle in degrees (counter-clockwise).} \item{pie.density}{The density of the shading lines, in lines per inch. Non-positive values inhibit the drawing of shading lines.} \item{pie.lty}{The line type of the border of the slices.} } } \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{ \code{\link{igraph.plotting}}, \code{\link{plot.igraph}} } \examples{ g <- graph.ring(10) values <- lapply(1:10, function(x) sample(1:10,3)) if (interactive()) { plot(g, vertex.shape="pie", vertex.pie=values, vertex.pie.color=list(heat.colors(5)), vertex.size=seq(10,30,length=10), vertex.label=NA) } } \keyword{graphs} igraph/man/graph.isomorphism.Rd0000644000176000001440000003367312251656216016311 0ustar ripleyusers\name{graph-isomorphism} \alias{graph.isoclass} \alias{graph.isocreate} \alias{graph.isomorphic} \alias{graph.isomorphic.vf2} \alias{graph.count.isomorphisms.vf2} \alias{graph.count.subisomorphisms.vf2} \alias{graph.get.isomorphisms.vf2} \alias{graph.get.subisomorphisms.vf2} \alias{graph.isoclass.subgraph} \alias{graph.isomorphic.34} \alias{graph.isomorphic.bliss} \alias{graph.subisomorphic.vf2} \alias{graph.subisomorphic.lad} \concept{Graph isomorphism} \concept{Subgraph isomorphism} \concept{VF2 algorithm} \concept{BLISS algorithm} \title{Graph Isomorphism} \description{These functions deal with graph isomorphism.} \usage{ graph.isomorphic(graph1, graph2) graph.isomorphic.34(graph1, graph2) graph.isomorphic.bliss(graph1, graph2, sh1="fm", sh2="fm") graph.isomorphic.vf2(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) graph.count.isomorphisms.vf2(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) graph.get.isomorphisms.vf2(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) graph.subisomorphic.vf2(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) graph.count.subisomorphisms.vf2(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) graph.get.subisomorphisms.vf2(graph1, graph2, vertex.color1, vertex.color2, edge.color1, edge.color2) graph.subisomorphic.lad (pattern, target, domains = NULL, induced = FALSE, map = TRUE, all.maps = FALSE, time.limit = Inf) graph.isoclass(graph) graph.isoclass.subgraph(graph, vids) graph.isocreate(size, number, directed=TRUE) } \arguments{ \item{graph}{A graph object.} \item{graph1,graph2}{Graph objects} \item{vertex.color1,vertex.color2}{ Optional integer vectors giving the colors of the vertices for colored (sub)graph isomorphism. If they are not given, but the graph has a \dQuote{color} vertex attribute, then it will be used. If you want to ignore these attributes, then supply \code{NULL} for both of these arguments. See also examples below.} \item{edge.color1,edge.color2}{ Optional integer vectors giving the colors of the edges for edge-colored (sub)graph isomorphism. If they are not given, but the graph has a \dQuote{color} edge attribute, then it will be used. If you want to ignore these attributes, then supply \code{NULL} for both of these arguments.} \item{size}{A numeric integer giving the number of vertices in the graph to create. Only three or four are suppported right now.} \item{number}{The number of the isomorphism class of the graph to be created.} \item{directed}{Whether to create a directed graph.} \item{sh1}{Character constant, the heuristics to use in the BLISS algorithm, for \code{graph1}. See the \code{sh} argument of \code{\link{canonical.permutation}} for possible values.} \item{sh2}{Character constant, the heuristics to use in the BLISS algorithm, for \code{graph2}. See the \code{sh} argument of \code{\link{canonical.permutation}} for possible values.} \item{vids}{Numeric vector, the vertex ids of vertices to form the induced subgraph for determining the isomorphism class.} \item{pattern}{The smaller graph, it might be directed or undirected. Undirected graphs are treated as directed graphs with mutual edges.} \item{target}{The bigger graph, it might be directed or undirected. Undirected graphs are treated as directed graphs with mutual edges.} \item{domains}{If not \code{NULL}, then it specifies matching restrictions. It must be a list of \code{target} vertex sets, given as numeric vertex ids or symbolic vertex names. The length of the list must be \code{vcount(pattern)} and for each vertex in \code{pattern} it gives the allowed matching vertices in \code{target}.} \item{induced}{Logical scalar, whether to search for an induced subgraph. It is \code{FALSE} by default.} \item{map}{Logical scalar, whether to return a mapping between \code{pattern} and \code{target}. Defaults to \code{TRUE}.} \item{all.maps}{Logical scalar, whether to return all mappings between \code{pattern} and \code{target}. Defaults to \code{FALSE}.} \item{time.limit}{The processor time limit for the computation, in seconds. It defaults to \code{Inf}, which means no limit. } } \details{ \code{graph.isomorphic} decides whether two graphs are isomorphic. The input graphs must be both directed or both undirected. This function is a higher level interface to the other graph isomorphism decision functions. Currently it does the following: \enumerate{ \item If the two graphs do not agree in the number of vertices and the number of edges then \code{FALSE} is returned. \item Otherwise if the graphs have 3 or 4 vertices, then \code{igraph.isomorphic.34} is called. \item Otherwise if the graphs are directed, then \code{igraph.isomorphic.vf2} is called. \item Otherwise \code{igraph.isomorphic.bliss} is called. } \code{igraph.isomorphic.34} decides whether two graphs, both of which contains only 3 or 4 vertices, are isomorphic. It works based on a precalculated and stored table. \code{igraph.isomorphic.bliss} uses the BLISS algorithm by Junttila and Kaski, and it works for undirected graphs. For both graphs the \code{\link{canonical.permutation}} and then the \code{\link{permute.vertices}} function is called to transfer them into canonical form; finally the canonical forms are compared. \code{graph.isomorphic.vf2} decides whethe two graphs are isomorphic, it implements the VF2 algorithm, by Cordella, Foggia et al., see references. \code{graph.count.isomorphisms.vf2} counts the different isomorphic mappings between \code{graph1} and \code{graph2}. (To count automorphisms you can supply the same graph twice, but it is better to call \code{\link{graph.automorphisms}}.) It uses the VF2 algorithm. \code{graph.get.isomorphisms.vf2} calculates all isomorphic mappings between \code{graph1} and \code{graph2}. It uses the VF2 algorithm. \code{graph.subisomorphic.vf2} decides whether \code{graph2} is isomorphic to some subgraph of \code{graph1}. It uses the VF2 algorithm. \code{graph.count.subisomorphisms.vf2} counts the different isomorphic mappings between \code{graph2} and the subgraphs of \code{graph1}. It uses the VF2 algorithm. \code{graph.get.subisomorphisms.vf2} calculates all isomorphic mappings between \code{graph2} and the subgraphs of \code{graph1}. It uses the VF2 algorithm. \code{graph.subisomorphic.lad} checks whether \code{pattern} is isomorphic to a subgraph or induced subgraph of \code{target}. It can also optionally return a mapping, or all possible mappings between the two graphs. Its \code{domains} argument allows for a flexible way to restrict the matching to a subset of allowed vertices, individually for each vertex in \code{pattern}. \code{graph.isoclass} returns the isomorphism class of a graph, a non-negative integer number. Graphs (with the same number of vertices) having the same isomorphism class are isomorphic and isomorphic graphs always have the same isomorphism class. Currently it can handle only graphs with 3 or 4 vertices. \code{graph.isoclass.subgraph} calculates the isomorphism class of a subgraph of the input graph. Currently it only works for subgraphs with 3 or 4 vertices. \code{graph.isocreate} create a graph from the given isomorphic class. Currently it can handle only graphs with 3 or 4 vertices. } \note{ Functions \code{graph.isoclass}, \code{graph.isoclass.subgraph} and \code{graph.isocreate} are considered experimental and might be reorganized/rewritten later. } \value{ \code{graph.isomorphic} and \code{graph.isomorphic.34} return a logical scalar, \code{TRUE} if the input graphs are isomorphic, \code{FALSE} otherwise. \code{graph.isomorphic.bliss} returns a named list with elements: \item{iso}{A logical scalar, whether the two graphs are isomorphic.} \item{map12}{A numeric vector, an mapping from \code{graph1} to \code{graph2} if \code{iso} is \code{TRUE}, an empty numeric vector otherwise.} \item{map21}{A numeric vector, an mapping from \code{graph2} to \code{graph1} if \code{iso} is \code{TRUE}, an empty numeric vector otherwise.} \item{info1}{Some information about the canonical form calculation for \code{graph1}. A named list, see the return value of \code{\link{canonical.permutation}} for details. Note that if the two graphs have different number of vertices or edges, then the BLISS algorithm is not run at all, and the contents of \code{info1} is incorrect.} \item{info2}{Some information about the canonical form calculation for \code{graph2}. A named list, see the return value of \code{\link{canonical.permutation}} for details. Note that if the two graphs have different number of vertices or edges, then the BLISS algorithm is not run at all, and the contents of \code{info2} is incorrect.} \code{graph.isomorphic.vf2} returns a names list with three elements: \item{iso}{A logical scalar, whether the two graphs are isomorphic.} \item{map12}{A numeric vector, an mapping from \code{graph1} to \code{graph2} if \code{iso} is \code{TRUE}, an empty numeric vector otherwise.} \item{map21}{A numeric vector, an mapping from \code{graph2} to \code{graph1} if \code{iso} is \code{TRUE}, an empty numeric vector otherwise.} \code{graph.count.isomorphisms.vf2} returns a numeric scalar, an integer, the number of isomorphic mappings between the two input graphs. \code{graph.get.isomorphisms.vf2} returns a list of numeric vectors. Every numeric vector is a permutation which takes \code{graph2} into \code{graph1}. \code{graph.subisomorphic.vf2} returns a named list with three elements: \item{iso}{Logical scalar, \code{TRUE} if a subgraph of \code{graph1} is isomorphic to \code{graph2}.} \item{map12}{Numeric vector, empty if \code{iso} is \code{FALSE}. Otherwise a mapping from a subgraph of \code{graph1} to \code{graph2}. -1 denotes the vertices which are not part of the mapping.} \item{map21}{Numeric vector, empty if \code{iso} is \code{FALSE}. Otherwise a mapping from \code{graph2} into \code{graph1}.} \code{graph.count.subisomorphisms.vf2} returns a numeric scalar, an integer. \code{graph.get.subisomorphisms.vf2} returns a list of numeric vectors, each numeric vector is an isomorphic mapping from \code{graph2} to a subgraph of \code{graph1}. \code{graph.subisomorphic.lad} return a named list with three entries: \item{iso}{Logical scalar, whether the algorithm found a subgraph (or induced subgraph is the \code{induced} argument in \code{TRUE}) in \code{target} that is isomorphic to \code{pattern}.} \item{map}{If a mapping is requested via the \code{map} argument, then a numeric vector of vertex ids from \code{target}, the matching vertices for each \code{pattern} vertex in \code{pattern} vertex id order. Otherwise \code{NULL}.} \item{maps}{If all mappings are requested via the \code{all.maps} argument, then all possible mappings from \code{pattern} to \code{target}, in a list of vectors, where each vector is in the same format as \code{map} just above.} \code{graph.isoclass} and \code{graph.isoclass.subgraph} return a non-negative integer number. \code{graph.isocreate} returns a graph object. } \references{ Tommi Junttila and Petteri Kaski: Engineering an Efficient Canonical Labeling Tool for Large and Sparse Graphs, \emph{Proceedings of the Ninth Workshop on Algorithm Engineering and Experiments and the Fourth Workshop on Analytic Algorithms and Combinatorics.} 2007. LP Cordella, P Foggia, C Sansone, and M Vento: An improved algorithm for matching large graphs, \emph{Proc. of the 3rd IAPR TC-15 Workshop on Graphbased Representations in Pattern Recognition}, 149--159, 2001. C. Solnon: AllDifferent-based Filtering for Subgraph Isomorphism, \emph{Artificial Intelligence} 174(12-13):850--864, 2010. } \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.motifs}}} \examples{ # create some non-isomorphic graphs g1 <- graph.isocreate(3, 10) g2 <- graph.isocreate(3, 11) graph.isoclass(g1) graph.isoclass(g2) graph.isomorphic(g1, g2) # create two isomorphic graphs, by # permuting the vertices of the first g1 <- barabasi.game(30, m=2, directed=FALSE) g2 <- permute.vertices(g1, sample(vcount(g1))) # should be TRUE graph.isomorphic(g1, g2) graph.isomorphic.bliss(g1, g2) graph.isomorphic.vf2(g1, g2) # colored graph isomorphism g1 <- graph.ring(10) g2 <- graph.ring(10) graph.isomorphic.vf2(g1, g2) V(g1)$color <- rep(1:2, length=vcount(g1)) V(g2)$color <- rep(2:1, length=vcount(g2)) graph.count.isomorphisms.vf2(g1, g2) graph.count.isomorphisms.vf2(g1, g2, vertex.color1=NULL, vertex.color2=NULL) V(g1)$name <- letters[1:vcount(g1)] V(g2)$name <- LETTERS[1:vcount(g2)] graph.get.isomorphisms.vf2(g1, g2) V(g1)$color <- 1 V(g2)$color <- 2 graph.isomorphic.vf2(g1, g2) graph.isomorphic.vf2(g2, g2, vertex.color1=NULL, vertex.color2=NULL) # The LAD example pattern <- graph.formula(1:2:3:4:5, 1 - 2:5, 2 - 1:5:3, 3 - 2:4, 4 - 3:5, 5 - 4:2:1) target <- graph.formula(1:2:3:4:5:6:7:8:9, 1 - 2:5:7, 2 - 1:5:3, 3 - 2:4, 4 - 3:5:6:8:9, 5 - 1:2:4:6:7, 6 - 7:5:4:9, 7 - 1:5:6, 8 - 4:9, 9 - 6:4:8) domains <- list(`1` = c(1,3,9), `2` = c(5,6,7,8), `3` = c(2,4,6,7,8,9), `4` = c(1,3,9), `5` = c(2,4,8,9)) graph.subisomorphic.lad(pattern, target, all.maps=TRUE) graph.subisomorphic.lad(pattern, target, induced=TRUE, all.maps=TRUE) graph.subisomorphic.lad(pattern, target, domains=domains, all.maps=TRUE) # Directed LAD example pattern <- graph.formula(1:2:3, 1 -+ 2:3) uring <- graph.ring(10) dring <- graph.ring(10, directed=TRUE) graph.subisomorphic.lad(pattern, uring) graph.subisomorphic.lad(pattern, dring) } \keyword{graphs} igraph/man/graphNEL.Rd0000644000176000001440000000526112240234657014267 0ustar ripleyusers\name{conversion between igraph and graphNEL graphs} \alias{igraph.from.graphNEL} \alias{igraph.to.graphNEL} \concept{Conversion} \concept{graph package} \concept{graphNEL object} \title{Convert igraph graphs to graphNEL objects or back} \description{The graphNEL class is defined in the \code{graph} package, it is another way to represent graphs. These functions are provided to convert between the igraph and the graphNEL objects.} \usage{ igraph.from.graphNEL(graphNEL, name = TRUE, weight = TRUE, unlist.attrs = TRUE) igraph.to.graphNEL(graph) } \arguments{ \item{graphNEL}{The graphNEL graph.} \item{name}{Logical scalar, whether to add graphNEL vertex names as an igraph vertex attribute called \sQuote{\code{name}}.} \item{weight}{Logical scalar, whether to add graphNEL edge weights as an igraph edge attribute called \sQuote{\code{weight}}. (graphNEL graphs are always weighted.)} \item{unlist.attrs}{Logical scalar. graphNEL attribute query functions return the values of the attributes in R lists, if this argument is \code{TRUE} (the default) these will be converted to atomic vectors, whenever possible, before adding them to the igraph graph.} \item{graph}{An igraph graph object.} } \details{ \code{igraph.from.graphNEL} takes a graphNEL graph and converts it to an igraph graph. It handles all graph/vertex/edge attributes. If the graphNEL graph has a vertex attribute called \sQuote{\code{name}} it will be used as igraph vertex attribute \sQuote{\code{name}} and the graphNEL vertex names will be ignored. Because graphNEL graphs poorly support multiple edges, the edge attributes of the multiple edges are lost: they are all replaced by the attributes of the first of the multiple edges. \code{igraph.to.graphNEL} converts and igraph graph to a graphNEL graph. It converts all graph/vertex/edge attributes. If the igraph graph has a vertex attribute \sQuote{\code{name}}, then it will be used to assign vertex names in the graphNEL graph. Otherwise igraph vertex ids will be used for this purpose. } \value{ \code{igraph.from.graphNEL} returns an igraph graph object. \code{igraph.to.graphNEL} returns a graphNEL graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{get.adjacency}}, \code{\link{graph.adjacency}}, \code{\link{get.adjlist}} and \code{\link{graph.adjlist}}. } \examples{ ## Undirected g <- graph.ring(10) V(g)$name <- letters[1:10] GNEL <- igraph.to.graphNEL(g) g2 <- igraph.from.graphNEL(GNEL) g2 ## Directed g3 <- graph.star(10, mode="in") V(g3)$name <- letters[1:10] GNEL2 <- igraph.to.graphNEL(g3) g4 <- igraph.from.graphNEL(GNEL2) g4 } \keyword{graphs} igraph/man/undocumented.Rd0000644000176000001440000000274112240234657015321 0ustar ripleyusers\name{igraph.undocumented} \alias{cited.type.game} \alias{citing.cited.type.game} \alias{lastcit.game} \title{Undocumented and unsupportted igraph functions} \description{These functions are still in the alpha stage or their arguments are expected to change, so they're not documented yet. They are also not very useful for the general audience. } \usage{ lastcit.game(n, edges=1, agebins=n/7100, pref=(1:(agebins+1))^-3, directed=TRUE) cited.type.game(n, edges=1, types=rep(0, n), pref=rep(1, length(types)), directed=TRUE, attr=TRUE) citing.cited.type.game(n, edges=1, types=rep(0, n), pref=matrix(1, nrow=length(types), ncol=length(types)), directed=TRUE, attr=TRUE) } \arguments{ \item{n}{Number of vertices.} \item{edges}{Number of edges per step.} \item{agebins}{Number of aging bins.} \item{pref}{Vector (\code{lastcit.game} and \code{cited.type.game} or matrix (\code{citing.cited.type.game}) giving the (unnormalized) citation probabilities for the different vertex types.} \item{directed}{Logical scalar, whether to generate directed networks.} \item{types}{Vector of length \sQuote{\code{n}}, the types of the vertices. Types are numbered from zero.} \item{attr}{Logical scalar, whether to add the vertex types to the generated graph as a vertex attribute called \sQuote{\code{type}}. } } %\details{} \value{A new graph.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} %\seealso{} %\examples{} \keyword{graphs} igraph/man/fastgreedy.community.Rd0000644000176000001440000000442312240234657017006 0ustar ripleyusers\name{fastgreedy.community} \alias{fastgreedy.community} \concept{Community structure} \concept{Fast greedy community detection} \title{Community structure via greedy optimization of modularity} \description{ This function tries to find dense subgraph, also called communities in graphs via directly optimizing a modularity score. } \usage{ fastgreedy.community(graph, merges=TRUE, modularity=TRUE, membership=TRUE, weights=E(graph)$weight) } \arguments{ \item{graph}{The input graph} \item{merges}{Logical scalar, whether to return the merge matrix.} \item{modularity}{Logical scalar, whether to return a vector containing the modularity after each merge.} \item{membership}{Logical scalar, whether to calculate the membership vector corresponding to the maximum modularity score, considering all possible community structures along the merges.} \item{weights}{If not \code{NULL}, then a numeric vector of edge weights. The length must match the number of edges in the graph. By default the \sQuote{\code{weight}} edge attribute is used as weights. If it is not present, then all edges are considered to have the same weight. } } \details{ This function implements the fast greedy modularity optimization algorithm for finding community structure, see A Clauset, MEJ Newman, C Moore: Finding community structure in very large networks, http://www.arxiv.org/abs/cond-mat/0408187 for the details. } \value{ \code{fastgreedy.community} returns a \code{\link{communities}} object, please see the \code{\link{communities}} manual page for details. } \references{ A Clauset, MEJ Newman, C Moore: Finding community structure in very large networks, http://www.arxiv.org/abs/cond-mat/0408187 } \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com} for the R interface. } \seealso{ \code{\link{communities}} for extracting the results. See also \code{\link{walktrap.community}}, \code{\link{spinglass.community}}, \code{\link{leading.eigenvector.community}} and \code{\link{edge.betweenness.community}} for other methods. } \examples{ g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6, 11)) fc <- fastgreedy.community(g) membership(fc) sizes(fc) } \keyword{graphs} igraph/man/erdos.renyi.game.Rd0000644000176000001440000000334512240234657016001 0ustar ripleyusers\name{erdos.renyi.game} \alias{erdos.renyi.game} \alias{random.graph.game} \concept{Random graph model} \concept{Erdos-Renyi graph} \title{Generate random graphs according to the Erdos-Renyi model} \description{This model is very simple, every possible edge is created with the same constant probability. } \usage{ erdos.renyi.game(n, p.or.m, type=c("gnp", "gnm"), directed = FALSE, loops = FALSE, \dots) } \arguments{ \item{n}{The number of vertices in the graph.} \item{p.or.m}{Either the probability for drawing an edge between two arbitrary vertices (G(n,p) graph), or the number of edges in the graph (for G(n,m) graphs).} \item{type}{The type of the random graph to create, either \code{gnp} (G(n,p) graph) or \code{gnm} (G(n,m) graph).} \item{directed}{Logical, whether the graph will be directed, defaults to FALSE.} \item{loops}{Logical, whether to add loop edges, defaults to FALSE.} \item{\dots}{Additional arguments, ignored.} } \details{ In G(n,p) graphs, the graph has \sQuote{n} vertices and for each edge the probability that it is present in the graph is \sQuote{p}. In G(n,m) graphs, the graph has \sQuote{n} vertices and \sQuote{m} edges, and the \sQuote{m} edges are chosen uniformly randomly from the set of all possible edges. This set includes loop edges as well if the \code{loops} parameter is TRUE. \code{random.graph.game} is an alias to this function. } \value{A graph object.} \references{ Erdos, P. and Renyi, A., On random graphs, \emph{Publicationes Mathematicae} 6, 290--297 (1959). } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{barabasi.game}}} \examples{ g <- erdos.renyi.game(1000, 1/1000) degree.distribution(g) } \keyword{graphs} igraph/man/static.fitness.game.Rd0000644000176000001440000000621112240234657016474 0ustar ripleyusers\name{static.fitness.game} \alias{static.fitness.game} \concept{Random graph model} \title{Random graphs from vertex fitness scores} \description{ This function generates a non-growing random graph with edge probabilities proportional to node fitness scores. } \usage{ static.fitness.game (no.of.edges, fitness.out, fitness.in, loops = FALSE, multiple = FALSE) } \arguments{ \item{no.of.edges}{The number of edges in the generated graph.} \item{fitness.out}{A numeric vector containing the fitness of each vertex. For directed graphs, this specifies the out-fitness of each vertex.} \item{fitness.in}{If \code{NULL} (the default), the generated graph will be undirected. If not \code{NULL}, then it should be a numeric vector and it specifies the in-fitness of each vertex. If this argument is not \code{NULL}, then a directed graph is generated, otherwise an undirected one. } \item{loops}{Logical scalar, whether to allow loop edges in the graph.} \item{multiple}{Logical scalar, whether to allow multiple edges in the graph.} } \details{ This game generates a directed or undirected random graph where the probability of an edge between vertices \eqn{i} and \eqn{j} depends on the fitness scores of the two vertices involved. For undirected graphs, each vertex has a single fitness score. For directed graphs, each vertex has an out- and an in-fitness, and the probability of an edge from \eqn{i} to \eqn{j} depends on the out-fitness of vertex \eqn{i} and the in-fitness of vertex \eqn{j}. The generation process goes as follows. We start from \eqn{N} disconnected nodes (where \eqn{N} is given by the length of the fitness vector). Then we randomly select two vertices \eqn{i} and \eqn{j}, with probabilities proportional to their fitnesses. (When the generated graph is directed, \eqn{i} is selected according to the out-fitnesses and \eqn{j} is selected according to the in-fitnesses). If the vertices are not connected yet (or if multiple edges are allowed), we connect them; otherwise we select a new pair. This is repeated until the desired number of links are created. It can be shown that the \emph{expected} degree of each vertex will be proportional to its fitness, although the actual, observed degree will not be. If you need to generate a graph with an exact degree sequence, consider \code{\link{degree.sequence.game}} instead. This model is commonly used to generate static scale-free networks. To achieve this, you have to draw the fitness scores from the desired power-law distribution. Alternatively, you may use \code{\link{static.power.law.game}} which generates the fitnesses for you with a given exponent. } \value{An igraph graph, directed or undirected.} \references{ Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. \emph{Phys Rev Lett} 87(27):278701, 2001. } \author{Tamas Nepusz \email{ntamas@gmail.com}} \examples{ N <- 10000 g <- static.fitness.game(5*N, sample((1:50)^-2, N, replace=TRUE)) degree.distribution(g) \dontrun{plot(degree.distribution(g, cumulative=TRUE), log="xy")} } \keyword{graphs} igraph/man/is.multiple.Rd0000644000176000001440000000446312240234657015077 0ustar ripleyusers\name{is.multiple} \alias{has.multiple} \alias{is.loop} \alias{is.multiple} \alias{count.multiple} \concept{Simple graph} \title{Find the multiple or loop edges in a graph} \description{A loop edge is an edge from a vertex to itself. An edge is a multiple edge if it has exactly the same head and tail vertices as another edge. A graph without multiple and loop edges is called a simple graph.} \usage{ is.loop(graph, eids=E(graph)) has.multiple(graph) is.multiple(graph, eids=E(graph)) count.multiple(graph, eids=E(graph)) } \arguments{ \item{graph}{The input graph.} \item{eids}{The edges to which the query is restricted. By default this is all edges in the graph.} } \details{ \code{is.loop} decides whether the edges of the graph are loop edges. \code{has.multiple} decides whether the graph has any multiple edges. \code{is.multiple} decides whether the edges of the graph are multiple edges. \code{count.multiple} counts the multiplicity of each edge of a graph. Note that the semantics for \code{is.multiple} and \code{count.multiple} is different. \code{is.multiple} gives \code{TRUE} for all occurences of a multiple edge except for one. Ie. if there are three \code{i-j} edges in the graph then \code{is.multiple} returns \code{TRUE} for only two of them while \code{count.multiple} returns \sQuote{3} for all three. See the examples for getting rid of multiple edges while keeping their original multiplicity as an edge attribute. } \value{ \code{has.multiple} returns a logical scalar. \code{is.loop} and \code{is.multiple} return a logical vector. \code{count.multiple} returns a numeric vector. } %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{simplify}} to eliminate loop and multiple edges.} \examples{ # Loops g <- graph( c(1,1,2,2,3,3,4,5) ) is.loop(g) # Multiple edges g <- barabasi.game(10, m=3, algorithm="bag") has.multiple(g) is.multiple(g) count.multiple(g) is.multiple(simplify(g)) all(count.multiple(simplify(g)) == 1) # Direction of the edge is important is.multiple(graph( c(1,2, 2,1) )) is.multiple(graph( c(1,2, 2,1), dir=FALSE )) # Remove multiple edges but keep multiplicity g <- barabasi.game(10, m=3, algorithm="bag") E(g)$weight <- count.multiple(g) g <- simplify(g) any(is.multiple(g)) E(g)$weight } \keyword{graphs}igraph/man/layout.sugiyama.Rd0000644000176000001440000001771112240234657015765 0ustar ripleyusers\name{layout.sugiyama} \alias{layout.sugiyama} \concept{Graph layout} \title{The Sugiyama graph layout generator} \description{ Sugiyama layout algorithm for layered directed acyclic graphs. The algorithm minimized edge crossings. } \usage{ layout.sugiyama (graph, layers = NULL, hgap = 1, vgap = 1, maxiter = 100, weights = NULL, attributes = c("default", "all", "none")) } \arguments{ \item{graph}{The input graph.} \item{layers}{A numeric vector or \code{NULL}. If not \code{NULL}, then it should specify the layer index of the vertices. Layers are numbered from one. If \code{NULL}, then igraph calculates the layers automatically.} \item{hgap}{Real scalar, the minimum horizontal gap between vertices in the same layer.} \item{vgap}{Real scalar, the distance between layers.} \item{maxiter}{Integer scalar, the maximum number of iterations in the crossing minimization stage. 100 is a reasonable default; if you feel that you have too many edge crossings, increase this.} \item{weights}{Optional edge weight vector. If \code{NULL}, then the 'weight' edge attribute is used, if there is one. Supply \code{NA} here and igraph ignores the edge weights.} \item{attributes}{Which graph/vertex/edge attributes to keep in the extended graph. \sQuote{default} keeps the \sQuote{size}, \sQuote{size2}, \sQuote{shape}, \sQuote{label} and \sQuote{color} vertex attributes and the \sQuote{arrow.mode} and \sQuote{arrow.size} edge attributes. \sQuote{all} keep all graph, vertex and edge attributes, \sQuote{none} keeps none of them.} } \details{ This layout algorithm is designed for directed acyclic graphs where each vertex is assigned to a layer. Layers are indexed from zero, and vertices of the same layer will be placed on the same horizontal line. The X coordinates of vertices within each layer are decided by the heuristic proposed by Sugiyama et al. to minimize edge crossings. You can also try to lay out undirected graphs, graphs containing cycles, or graphs without an a priori layered assignment with this algorithm. igraph will try to eliminate cycles and assign vertices to layers, but there is no guarantee on the quality of the layout in such cases. The Sugiyama layout may introduce \dQuote{bends} on the edges in order to obtain a visually more pleasing layout. This is achieved by adding dummy nodes to edges spanning more than one layer. The resulting layout assigns coordinates not only to the nodes of the original graph but also to the dummy nodes. The layout algorithm will also return the extended graph with the dummy nodes. For more details, see the reference below. } \value{ A list with the components: \item{layout}{The layout, a two-column matrix, for the original graph vertices.} \item{layout.dummy}{The layout for the dummy vertices, a two column matrix.} \item{extd_graph}{The original graph, extended with dummy vertices. The \sQuote{dummy} vertex attribute is set on this graph, it is a logical attributes, and it tells you whether the vertex is a dummy vertex. The \sQuote{layout} graph attribute is also set, and it is the layout matrix for all (original and dummy) vertices.} } \references{ K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual Understanding of Hierarchical Systems". IEEE Transactions on Systems, Man and Cybernetics 11(2):109-125, 1981. } \author{ Tamas Nepusz \email{ntamas@gmail.com} } \examples{ ## Data taken from http://tehnick-8.narod.ru/dc_clients/ DC <- graph.formula("DC++" -+ "LinuxDC++":"BCDC++":"EiskaltDC++":"StrongDC++":"DiCe!++", "LinuxDC++" -+ "FreeDC++", "BCDC++" -+ "StrongDC++", "FreeDC++" -+ "BMDC++":"EiskaltDC++", "StrongDC++" -+ "AirDC++":"zK++":"ApexDC++":"TkDC++", "StrongDC++" -+ "StrongDC++ SQLite":"RSX++", "ApexDC++" -+ "FlylinkDC++ ver <= 4xx", "ApexDC++" -+ "ApexDC++ Speed-Mod":"DiCe!++", "StrongDC++ SQLite" -+ "FlylinkDC++ ver >= 5xx", "ApexDC++ Speed-Mod" -+ "FlylinkDC++ ver <= 4xx", "ApexDC++ Speed-Mod" -+ "GreylinkDC++", "FlylinkDC++ ver <= 4xx" -+ "FlylinkDC++ ver >= 5xx", "FlylinkDC++ ver <= 4xx" -+ AvaLink, "GreylinkDC++" -+ AvaLink:"RayLinkDC++":"SparkDC++":PeLink) ## Use edge types E(DC)$lty <- 1 E(DC)["BCDC++" \%->\% "StrongDC++"]$lty <- 2 E(DC)["FreeDC++" \%->\% "EiskaltDC++"]$lty <- 2 E(DC)["ApexDC++" \%->\% "FlylinkDC++ ver <= 4xx"]$lty <- 2 E(DC)["ApexDC++" \%->\% "DiCe!++"]$lty <- 2 E(DC)["StrongDC++ SQLite" \%->\% "FlylinkDC++ ver >= 5xx"]$lty <- 2 E(DC)["GreylinkDC++" \%->\% "AvaLink"]$lty <- 2 ## Layers, as on the plot layers <- list(c("DC++"), c("LinuxDC++", "BCDC++"), c("FreeDC++", "StrongDC++"), c("BMDC++", "EiskaltDC++", "AirDC++", "zK++", "ApexDC++", "TkDC++", "RSX++"), c("StrongDC++ SQLite", "ApexDC++ Speed-Mod", "DiCe!++"), c("FlylinkDC++ ver <= 4xx", "GreylinkDC++"), c("FlylinkDC++ ver >= 5xx", "AvaLink", "RayLinkDC++", "SparkDC++", "PeLink")) ## Check that we have all nodes all(sort(unlist(layers)) == sort(V(DC)$name)) ## Add some graphical parameters V(DC)$color <- "white" V(DC)$shape <- "rectangle" V(DC)$size <- 20 V(DC)$size2 <- 10 V(DC)$label <- lapply(V(DC)$name, function(x) paste(strwrap(x, 12), collapse="\n")) E(DC)$arrow.size <- 0.5 ## Create a similar layout using the predefined layers lay1 <- layout.sugiyama(DC, layers=apply(sapply(layers, function(x) V(DC)$name \%in\% x), 1, which)) ## Simple plot, not very nice par(mar=rep(.1, 4)) plot(DC, layout=lay1$layout, vertex.label.cex=0.5) ## Sugiyama plot plot(lay1$extd_graph, vertex.label.cex=0.5) ## The same with automatic layer calculation ## Keep vertex/edge attributes in the extended graph lay2 <- layout.sugiyama(DC, attributes="all") plot(lay2$extd_graph, vertex.label.cex=0.5) ## Another example, from the following paper: ## Markus Eiglsperger, Martin Siebenhaller, Michael Kaufmann: ## An Efficient Implementation of Sugiyama's Algorithm for ## Layered Graph Drawing, Journal of Graph Algorithms and ## Applications 9, 305--325 (2005). ex <- graph.formula( 0 -+ 29: 6: 5:20: 4, 1 -+ 12, 2 -+ 23: 8, 3 -+ 4, 4, 5 -+ 2:10:14:26: 4: 3, 6 -+ 9:29:25:21:13, 7, 8 -+ 20:16, 9 -+ 28: 4, 10 -+ 27, 11 -+ 9:16, 12 -+ 9:19, 13 -+ 20, 14 -+ 10, 15 -+ 16:27, 16 -+ 27, 17 -+ 3, 18 -+ 13, 19 -+ 9, 20 -+ 4, 21 -+ 22, 22 -+ 8: 9, 23 -+ 9:24, 24 -+ 12:15:28, 25 -+ 11, 26 -+ 18, 27 -+ 13:19, 28 -+ 7, 29 -+ 25 ) layers <- list( 0, c(5, 17), c(2, 14, 26, 3), c(23, 10, 18), c(1, 24), 12, 6, c(29,21), c(25,22), c(11,8,15), 16, 27, c(13,19), c(9, 20), c(4, 28), 7 ) layex <- layout.sugiyama(ex, layers=apply(sapply(layers, function(x) V(ex)$name \%in\% as.character(x)), 1, which)) origvert <- c(rep(TRUE, vcount(ex)), rep(FALSE, nrow(layex$layout.dummy))) realedge <- get.edgelist(layex$extd_graph)[,2] <= vcount(ex) plot(layex$extd_graph, vertex.label.cex=0.5, edge.arrow.size=.5, vertex.size=ifelse(origvert, 5, 0), vertex.shape=ifelse(origvert, "square", "none"), vertex.label=ifelse(origvert, V(ex)$name, ""), edge.arrow.mode=ifelse(realedge, 2, 0)) } \keyword{graphs} igraph/man/autocurve.edges.Rd0000644000176000001440000000234512240234657015732 0ustar ripleyusers\name{autocurve.edges} \alias{autocurve.edges} \title{Optimal edge curvature when plotting graphs} \description{ If graphs have multiple edges, then drawing them as straight lines does not show them when plotting the graphs; they will be on top of each other. One solution is to bend the edges, with diffenent curvature, so that all of them are visible.} \usage{ autocurve.edges (graph, start = 0.5) } \arguments{ \item{graph}{The input graph.} \item{start}{The curvature at the two extreme edges. All edges will have a curvature between \code{-start} and \code{start}, spaced equally.} } \details{ \code{autocurve.edges} calculates the optimal \code{edge.curved} vector for plotting a graph with multiple edges, so that all edges are visible. } \value{ A numeric vector, its length is the number of edges in the graph. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{igraph.plotting}} for all plotting parameters, \code{\link{plot.igraph}}, \code{\link{tkplot}} and \code{\link{rglplot}} for plotting functions. } \examples{ g <- graph( c(0,1,1,0,1,2,1,3,1,3,1,3, 2,3,2,3,2,3,2,3,0,1)+1 ) autocurve.edges(g) \dontrun{ set.seed(42) plot(g) } } \keyword{graphs} igraph/man/graph.compose.Rd0000644000176000001440000000535512251656216015401 0ustar ripleyusers\name{graph.compose} \alias{graph.compose} \alias{\%c\%} \concept{Graph operators} \title{Compose two graphs as binary relations} \description{Relational composition of two graph.} \usage{ graph.compose(g1, g2, byname = "auto") } \arguments{ \item{g1}{The first input graph.} \item{g2}{The second input graph.} \item{byname}{A logical scalar, or the character scalar \code{auto}. Whether to perform the operation based on symbolic vertex names. If it is \code{auto}, that means \code{TRUE} if both graphs are named and \code{FALSE} otherwise. A warning is generated if \code{auto} and one graph, but not both graphs are named.} } \details{ \code{graph.compose} creates the relational composition of two graphs. The new graph will contain an (a,b) edge only if there is a vertex c, such that edge (a,c) is included in the first graph and (c,b) is included in the second graph. The corresponding operator is \%c\%. The function gives an error if one of the input graphs is directed and the other is undirected. If the \code{byname} argument is \code{TRUE} (or \code{auto} and the graphs are all named), then the operation is performed based on symbolic vertex names. Otherwise numeric vertex ids are used. \code{graph.compose} keeps the attributes of both graphs. All graph, vertex and edge attributes are copied to the result. If an attribute is present in multiple graphs and would result a name clash, then this attribute is renamed by adding suffixes: _1, _2, etc. The \code{name} vertex attribute is treated specially if the operation is performed based on symbolic vertex names. In this case \code{name} must be present in both graphs, and it is not renamed in the result graph. Note that an edge in the result graph corresponds to two edges in the input, one in the first graph, one in the second. This mapping is not injective and several edges in the result might correspond to the same edge in the first (and/or the second) graph. The edge attributes in the result graph are updated accordingly. Also note that the function may generate multigraphs, if there are more than one way to find edges (a,b) in g1 and (b,c) in g2 for an edge (a,c) in the result. See \code{\link{simplify}} if you want to get rid of the multiple edges. The function may create loop edges, if edges (a,b) and (b,a) are present in g1 and g2, respectively, then (a,a) is included in the result. See \code{\link{simplify}} if you want to get rid of the self-loops. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g1 <- graph.ring(10) g2 <- graph.star(10, mode="undirected") gc <- graph.compose(g1, g2) str(gc) str(simplify(gc)) } \keyword{graphs} igraph/man/igraph.sample.Rd0000644000176000001440000000173412240234657015362 0ustar ripleyusers\name{igraph.sample} \alias{igraph.sample} \title{Sampling a random integer sequence} \description{This function provides a very efficient way to pull an integer random sample sequence from an integer interval.} \usage{ igraph.sample(low, high, length) } \arguments{ \item{low}{The lower limit of the interval (inclusive).} \item{high}{The higher limit of the interval (inclusive).} \item{length}{The length of the sample.} } \details{The algorithm runs in \code{O(length)} expected time, even if \code{high-low} is big. It is much faster (but of course less general) than the builtin \code{sample} function of R.} \value{ An increasing numeric vector containing integers, the sample. } \references{Jeffrey Scott Vitter: An Efficient Algorithm for Sequential Random Sampling, \emph{ACM Transactions on Mathematical Software}, 13/1, 58--67.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \examples{ rs <- igraph.sample(1, 100000000, 10) rs } \keyword{datagen} igraph/man/as.igraph.Rd0000644000176000001440000000155012240234657014500 0ustar ripleyusers\name{as.igraph} \alias{as.igraph} \alias{as.igraph.igraphHRG} \concept{Hierarchical random graphs} \title{Conversion to igraph} \description{These fucntions convert various objects to igraph graphs.} \usage{ \method{as.igraph}{igraphHRG}(x, \dots) } \arguments{ \item{x}{The object to convert.} \item{\dots}{Additional arguments. None currently.} } \details{ You can use \code{as.igraph} to convert various objects to igraph graphs. Right now the following objects are supported: \itemize{ \item code{igraphHRG} These objects are created by the \code{\link{hrg.fit}} and \code{\link{hrg.consensus}} functions. } } \value{ All these functions return an igraph graph. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}.} % \seealso{} \examples{ g <- graph.full(5) + graph.full(5) hrg <- hrg.fit(g) as.igraph(hrg) } \keyword{graphs} igraph/man/is.chordal.Rd0000644000176000001440000000550312240234657014654 0ustar ripleyusers\name{is.chordal} \alias{is.chordal} \concept{maximum cardinality search} \concept{graph decomposition} \concept{chordal graph} \title{Chordality of a graph} \description{ A graph is chordal (or triangulated) if each of its cycles of four or more nodes has a chord, which is an edge joining two nodes that are not adjacent in the cycle. An equivalent definition is that any chordless cycles have at most three nodes.} \usage{ is.chordal(graph, alpha = NULL, alpham1 = NULL, fillin = FALSE, newgraph = FALSE) } \arguments{ \item{graph}{The input graph. It may be directed, but edge directions are ignored, as the algorithm is defined for undirected graphs.} \item{alpha}{Numeric vector, the maximal chardinality ordering of the vertices. If it is \code{NULL}, then it is automatically calculated by calling \code{\link{maximum.cardinality.search}}, or from \code{alpham1} if that is given..} \item{alpham1}{Numeric vector, the inverse of \code{alpha}. If it is \code{NULL}, then it is automatically calculated by calling \code{\link{maximum.cardinality.search}}, or from \code{alpha}.} \item{fillin}{Logical scalar, whether to calculate the fill-in edges.} \item{newgraph}{Logical scalar, whether to calculate the triangulated graph.} } \details{ The chordality of the graph is decided by first performing maximum cardinality search on it (if the \code{alpha} and \code{alpham1} arguments are \code{NULL}), and then calculating the set of fill-in edges. The set of fill-in edges is empty if and only if the graph is chordal. It is also true that adding the fill-in edges to the graph makes it chordal. } \value{ A list with three members: \item{chordal}{Logical scalar, it is \code{TRUE} iff the input graph is chordal.} \item{fillin}{If requested, then a numeric vector giving the fill-in edges. \code{NULL} otherwise.} \item{newgraph}{If requested, then the triangulated graph, an \code{igraph} object. \code{NULL} otherwise.} } \references{ Robert E Tarjan and Mihalis Yannakakis. (1984). Simple linear-time algorithms to test chordality of graphs, test acyclicity of hypergraphs, and selectively reduce acyclic hypergraphs. \emph{SIAM Journal of Computation} 13, 566--579.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{maximum.cardinality.search}} } \examples{ ## The examples from the Tarjan-Yannakakis paper g1 <- graph.formula(A-B:C:I, B-A:C:D, C-A:B:E:H, D-B:E:F, E-C:D:F:H, F-D:E:G, G-F:H, H-C:E:G:I, I-A:H) maximum.cardinality.search(g1) is.chordal(g1, fillin=TRUE) g2 <- graph.formula(A-B:E, B-A:E:F:D, C-E:D:G, D-B:F:E:C:G, E-A:B:C:D:F, F-B:D:E, G-C:D:H:I, H-G:I:J, I-G:H:J, J-H:I) maximum.cardinality.search(g2) is.chordal(g2, fillin=TRUE) } \keyword{graphs} igraph/man/nexus.Rd0000644000176000001440000002173612240234657013776 0ustar ripleyusers\name{nexus} \alias{nexus} \alias{nexus.list} \alias{nexus.info} \alias{nexus.get} \alias{nexus.search} \alias{nexusDatasetInfo} \alias{print.nexusDatasetInfo} \alias{print.nexusDatasetInfoList} \alias{summary.nexusDatasetInfoList} \concept{The Nexus network repository} \title{Query and download from the Nexus network repository} \description{The Nexus network repository is an online collection of network data sets. These functions can be used to query it and download data from it, directly as an igraph graph.} \usage{ nexus.list(tags=NULL, offset=0, limit=10, operator=c("or", "and"), order=c("date", "name", "popularity"), nexus.url=getIgraphOpt("nexus.url")) nexus.info(id, nexus.url=getIgraphOpt("nexus.url")) nexus.get(id, offset=0, order=c("date", "name", "popularity"), nexus.url=getIgraphOpt("nexus.url")) nexus.search(q, offset=0, limit=10, order=c("date", "name", "popularity"), nexus.url=getIgraphOpt("nexus.url")) \method{print}{nexusDatasetInfo}(x, \dots) \method{summary}{nexusDatasetInfoList}(object, \dots) \method{print}{nexusDatasetInfoList}(x, \dots) } \arguments{ \item{tags}{A character vector, the tags that are searched. If not given (or \code{NULL}), then all datasets are listed.} \item{offset}{An offset to select part of the results. Results are listed from \code{offset}+1.} \item{limit}{The maximum number of results to return.} \item{operator}{A character scalar. If \sQuote{or} (the default), then all datasets that have at least one of the given tags, are returned. If it if \sQuote{and}, then only datasets that have all the given tags, are returned.} \item{order}{The ordering of the results, possible values are: \sQuote{date}, \sQuote{name}, \sQuote{popularity}.} \item{id}{The numeric or character id of the data set to query or download. Instead of the data set ids, it is possible to supply a \code{nexusDatasetInfo} or \code{nexusDatasetInfoList} object here directly and then the query is done on the corresponding data set(s).} \item{q}{Nexus search string. See examples below. For the complete documentation please see the Nexus homepage at \url{http://nexus.igraph.org}.} \item{nexus.url}{The URL of the Nexus server. Don't change this from the default, unless you set up your own Nexus server.} \item{x,object}{The \code{nexusDatasetInfo} object to print.} \item{\dots}{Currently ignored.} } \details{ Nexus is an online repository of networks, with an API that allow programatic queries against it, and programatic data download as well. The \code{nexus.list} and \code{nexus.info} functions query the online database. They both return \code{nexusDatasetInfo} objects. \code{nexus.info} returns more information than \code{nexus.list}. \code{nexus.search} searches Nexus, and returns a list of data sets, as \code{nexusDatasetInfo} objects. See below for some search examples. \code{nexus.get} downloads a data set from Nexus, based on its numeric id, or based on a Nexus search string. For search strings, only the first search hit is downloaded, but see also the \code{offset} argument. (If there are not data sets found, then the function returns an error.) The \code{nexusDatasetInfo} objects returned by \code{nexus.list} have the following fields: \describe{ \item{id}{The numeric id of the dataset.} \item{sid}{The character id of the dataset.} \item{name}{Character scalar, the name of the dataset.} \item{vertices/edges}{Character, the number of vertices and edges in the graph(s). Vertices and edges are separated by a slash, and if the data set consists of multiple networks, then they are separated by spaces.} \item{tags}{Character vector, the tags of the dataset. Directed graph have the tags \sQuote{directed}. Undirected graphs are tagged as \sQuote{undirected}. Other common tags are: \sQuote{weighted}, \sQuote{bipartite}, \sQuote{social network}, etc.} \item{networks}{The ids and names of the networks in the data set. The numeric and character id are separated by a slash, and multiple networks are separated by spaces.} } \code{nexusDatasetInfo} objects returned by \code{nexus.info} have the following additional fields: \describe{ \item{date}{Character scalar, e.g. \sQuote{2011-01-09}, the date when the dataset was added to the database.} \item{formats}{Character vector, the data formats in which the data set is available. The various formats are separated by semicolons.} \item{licence}{Character scalar, the licence of the dataset.} \item{licence url}{Character scalar, the URL of the licence of the dataset. Pleaase make sure you consult this before using a dataset.} \item{summary}{Character scalar, the short description of the dataset, this is usually a single sentence.} \item{description}{Character scalar, the full description of the dataset.} \item{citation}{Character scalar, the paper(s) describing the dataset. Please cite these papers if you are using the dataset in your research, the licence of most datasets requires this.} \item{attributes}{A list of lists, each list entry is a graph, vertex or edge attribute and has the following entries: \describe{ \item{type}{Type of the attribute, either \sQuote{graph}, \sQuote{vertex} or \sQuote{edge}.} \item{datatype}{Data type of the attribute, currently it can be \sQuote{numeric} and \sQuote{string}.} \item{name}{Character scalar, the name of the attribute.} \item{description}{Character scalar, the description of the attribute.} } } } The results of the Nexus queries are printed to the screen in a consise format, similar to the format of igraph graphs. A data set list (typically the result of \code{nexus.list} and \code{nexus.search}) looks like this: \preformatted{NEXUS 1-5/18 -- data set list [1] kaptail.4 39/109-223 #18 Kapferer tailor shop [2] condmatcollab2003 31163/120029 #17 Condensed matter collaborations+ [3] condmatcollab 16726/47594 #16 Condensed matter collaborations+ [4] powergrid 4941/6594 #15 Western US power grid [5] celegansneural 297/2359 #14 C. Elegans neural network } Each line here represents a data set, and the following information is given about them: the character id of the data set (e.g. \code{kaptail} or \code{powergrid}), the number of vertices and number of edges in the graph of the data sets. For data sets with multiple graphs, intervals are given here. Then the numeric id of the data set and the reamining space is filled with the name of the data set. Summary information about an individual Nexus data set is printed as \preformatted{NEXUS B--- 39 109-223 #18 kaptail -- Kapferer tailor shop + tags: directed; social network; undirected + nets: 1/KAPFTI2; 2/KAPFTS2; 3/KAPFTI1; 4/KAPFTS1} This is very similar to the header that is used for printing igraph graphs, but there are some differences as well. The four characters after the \code{NEXUS} word give the most important properties of the graph(s): the first is \sQuote{\code{U}} for undirected and \sQuote{\code{D}} for directed graphs, and \sQuote{\code{B}} if the data set contains both directed and undirected graphs. The second is \sQuote{\code{N}} named graphs. The third character is \sQuote{\code{W}} for weighted graphs, the fourth is \sQuote{\code{B}} if the data set contains bipartite graphs. Then the number of vertices and number of edges are printed, for data sets with multiple graphs, the smallest and the largest values are given. Then comes the numeric id, and the string id of the data set. The end of the first line contains the name of the data set. The second row lists the data set tags, and the third row the networks that are included in the data set. Detailed data set information is printed similarly, but it contains more fields. } \value{ \code{nexus.list} and \code{nexus.search} return a list of \code{nexusDatasetInfo} objects. The list also has these attributes: \describe{ \item{size}{The number of data sets returned by the query.} \item{totalsize}{The total number of data sets found for the query.} \item{offset}{The offset parameter of the query.} \item{limit}{The limit parameter of the query.} } \code{nexus.info} returns a single \code{nexusDatasetInfo} object. \code{nexus.get} returns an igraph graph object, or a list of graph objects, if the data set consists of multiple networks. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \url{http://nexus.igraph.org} } \examples{ \dontrun{nexus.list(tag="weighted") nexus.list(limit=3, order="name") nexus.list(limit=3, order="name")[[1]] nexus.info(2) g <- nexus.get(2) summary(g) ## Data sets related to 'US': nexus.search("US") ## Search for data sets that have 'network' in their name: nexus.search("name:network") ## Any word can match nexus.search("blog or US or karate") } } \keyword{graphs} igraph/man/topological.sort.Rd0000644000176000001440000000255012240234657016127 0ustar ripleyusers\name{topological.sort} \alias{topological.sort} \concept{Topological sort} \title{Topological sorting of vertices in a graph} \description{ A topological sorting of a directed acyclic graph is a linear ordering of its nodes where each node comes before all nodes to which it has edges. } \usage{ topological.sort(graph, mode=c("out", "all", "in")) } \arguments{ \item{graph}{The input graph, should be directed} \item{mode}{Specifies how to use the direction of the edges. For \dQuote{\code{out}}, the sorting order ensures that each node comes before all nodes to which it has edges, so nodes with no incoming edges go first. For \dQuote{\code{in}}, it is quite the opposite: each node comes before all nodes from which it receives edges. Nodes with no outgoing edges go first.} } \details{ Every DAG has at least one topological sort, and may have many. This function returns a possible topological sort among them. If the graph is not acyclic (it has at least one cycle), a partial topological sort is returned and a warning is issued.} \value{ A numeric vector containing vertex ids in topologically sorted order. } %\references{ %} \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com} for the R interface} %\seealso{} \examples{ g <- barabasi.game(100) topological.sort(g) } \keyword{graphs} igraph/man/cohesive.blocks.Rd0000644000176000001440000002611412251656216015711 0ustar ripleyusers\name{cohesive.blocks} \alias{cohesive.blocks} \alias{cohesiveBlocks} \alias{blocks} \alias{blockGraphs} \alias{cohesion} \alias{hierarchy} \alias{parent} \alias{plotHierarchy} \alias{exportPajek} \alias{maxcohesion} \alias{plot.cohesiveBlocks} \alias{summary.cohesiveBlocks} \alias{length.cohesiveBlocks} \alias{print.cohesiveBlocks} \concept{Structurally cohesive blocks} \title{Calculate Cohesive Blocks} \description{Calculates cohesive blocks for objects of class \code{igraph}.} \usage{ cohesive.blocks(graph, labels = TRUE) blocks(blocks) blockGraphs(blocks, graph) cohesion(blocks) hierarchy(blocks) parent(blocks) plotHierarchy(blocks, layout=layout.reingold.tilford(hierarchy(blocks), root=1), \dots) exportPajek(blocks, graph, file, project.file = TRUE) maxcohesion(blocks) \method{print}{cohesiveBlocks}(x, \dots) \method{summary}{cohesiveBlocks}(object, \dots) \method{length}{cohesiveBlocks}(x) \method{plot}{cohesiveBlocks}(x, y, colbar = rainbow(max(cohesion(x))+1), col = colbar[maxcohesion(x)+1], mark.groups = blocks(x)[-1], \dots) } \arguments{ \item{graph}{For \code{cohesive.blocks} a graph object of class \code{igraph}. It must be undirected and simple. (See \code{\link{is.simple}}.) For \code{blockGraphs} and \code{exportPajek} the same graph must be supplied whose cohesive block structure is given in the \code{blocks} argument. } \item{labels}{Logical scalar, whether to add the vertex labels to the result object. These labels can be then used when reporting and plotting the cohesive blocks.} \item{blocks,x,object}{A \code{cohesiveBlocks} object, created with the \code{cohesive.blocks} function.} \item{file}{Defines the file (or connection) the Pajek file is written to. If the \code{project.file} argument is \code{TRUE}, then it can be a filename (with extension), a file object, or in general any king of connection object. The file/connection will be opened if it wasn't already. If the \code{project.file} argument is \code{FALSE}, then several files are created and \code{file} must be a character scalar containing the base name of the files, without extension. (But it can contain the path to the files.) See also details below. } \item{project.file}{Logical scalar, whether to create a single Pajek project file containing all the data, or to create separated files for each item. See details below.} \item{y}{The graph whose cohesive blocks are supplied in the \code{x} argument.} \item{colbar}{Color bar for the vertex colors. Its length should be at least \eqn{m+1}, where \eqn{m} is the maximum cohesion in the graph. Alternatively, the vertex colors can also be directly specified via the \code{col} argument.} \item{col}{A vector of vertex colors, in any of the usual formats. (Symbolic color names (e.g. \sQuote{red}, \sQuote{blue}, etc.) , RGB colors (e.g. \sQuote{#FF9900FF}), integer numbers referring to the current palette. By default the given \code{colbar} is used and vertices with the same maximal cohesion will have the same color.} \item{mark.groups}{A list of vertex sets to mark on the plot by circling them. By default all cohesive blocks are marked, except the one corresponding to the all vertices.} \item{layout}{The layout of a plot, it is simply passed on to \code{plot.igraph}, see the possible formats there. By default the Reingold-Tilford layout generator is used.} \item{\dots}{Additional arguments. \code{plotHierarchy} and \code{plot} pass them to \code{plot.igraph}. \code{print} and \code{summary} ignore them. } } \details{ Cohesive blocking is a method of determining hierarchical subsets of graph vertices based on their structural cohesion (or vertex connectivity). For a given graph \eqn{G}, a subset of its vertices \eqn{S\subset V(G)}{S} is said to be maximally \eqn{k}-cohesive if there is no superset of \eqn{S} with vertex connectivity greater than or equal to \eqn{k}. Cohesive blocking is a process through which, given a \eqn{k}-cohesive set of vertices, maximally \eqn{l}-cohesive subsets are recursively identified with \eqn{l>k}. Thus a hiearchy of vertex subsets is found, whith the entire graph \eqn{G} at its root. The function \code{cohesive.blocks} implements cohesive blocking. It returns a \code{cohesiveBlocks} object. \code{cohesiveBlocks} should be handled as an opaque class, i.e. its internal structure should not be accessed directly, but through the functions listed here. The function \code{length} can be used on \code{cohesiveBlocks} objects and it gives the number of blocks. The function \code{blocks} returns the actual blocks stored in the \code{cohesiveBlocks} object. They are returned in a list of numeric vectors, each containing vertex ids. The function \code{blockGraphs} is similar, but returns the blocks as (induced) subgraphs of the input graph. The various (graph, vertex and edge) attributes are kept in the subgraph. The function \code{cohesion} returns a numeric vector, the cohesion of the different blocks. The order of the blocks is the same as for the \code{blocks} and \code{blockGraphs} functions. The block hierarchy can be queried using the \code{hierarchy} function. It returns an igraph graph, its vertex ids are ordered according the order of the blocks in the \code{blocks} and \code{blockGraphs}, \code{cohesion}, etc. functions. \code{parent} gives the parent vertex of each block, in the block hierarchy, for the root vertex it gives 0. \code{plotHierarchy} plots the hierarchy tree of the cohesive blocks on the active graphics device, by calling \code{igraph.plot}. The \code{exportPajek} function can be used to export the graph and its cohesive blocks in Pajek format. It can either export a single Pajek project file with all the information, or a set of files, depending on its \code{project.file} argument. If \code{project.file} is \code{TRUE}, then the following information is written to the file (or connection) given in the \code{file} argument: (1) the input graph, together with its attributes, see \code{\link{write.graph}} for details; (2) the hierarchy graph; and (3) one binary partition for each cohesive block. If \code{project.file} is \code{FALSE}, then the \code{file} argument must be a character scalar and it is used as the base name for the generated files. If \code{file} is \sQuote{basename}, then the following files are created: (1) \sQuote{basename.net} for the original graph; (2) \sQuote{basename_hierarchy.net} for the hierarchy graph; (3) \sQuote{basename_block_x.net} for each cohesive block, where \sQuote{x} is the number of the block, starting with one. \code{maxcohesion} returns the maximal cohesion of each vertex, i.e. the cohesion of the most cohesive block of the vertex. The generic function \code{summary} works on \code{cohesiveBlocks} objects and it prints a one line summary to the terminal. The generic function \code{print} is also defined on \code{cohesiveBlocks} objects and it is invoked automatically if the name of the \code{cohesiveBlocks} object is typed in. It produces an output like this: \preformatted{ Cohesive block structure: B-1 c 1, n 23 '- B-2 c 2, n 14 oooooooo.. .o......oo ooo '- B-4 c 5, n 7 ooooooo... .......... ... '- B-3 c 2, n 10 ......o.oo o.oooooo.. ... '- B-5 c 3, n 4 ......o.oo o......... ... } The left part shows the block structure, in this case for five blocks. The first block always corresponds to the whole graph, even if its cohesion is zero. Then cohesion of the block and the number of vertices in the block are shown. The last part is only printed if the display is wide enough and shows the vertices in the blocks, ordered by vertex ids. \sQuote{o} means that the vertex is included, a dot means that it is not, and the vertices are shown in groups of ten. The generic function \code{plot} plots the graph, showing one or more cohesive blocks in it. } \value{ \code{cohesive.blocks} returns a \code{cohesiveBlocks} object. \code{blocks} returns a list of numeric vectors, containing vertex ids. \code{blockGraphs} returns a list of igraph graphs, corresponding to the cohesive blocks. \code{cohesion} returns a numeric vector, the cohesion of each block. \code{hierarchy} returns an igraph graph, the representation of the cohesive block hierarchy. \code{parent} returns a numeric vector giving the parent block of each cohesive block, in the block hierarchy. The block at the root of the hierarchy has no parent and \code{0} is returned for it. \code{plotHierarchy}, \code{plot} and \code{exportPajek} return \code{NULL}, invisibly. \code{maxcohesion} returns a numeric vector with one entry for each vertex, giving the cohesion of its most cohesive block. \code{print} and \code{summary} return the \code{cohesiveBlocks} object itself, invisibly. \code{length} returns a numeric scalar, the number of blocks. } \references{ J. Moody and D. R. White. Structural cohesion and embeddedness: A hierarchical concept of social groups. \emph{American Sociological Review}, 68(1):103--127, Feb 2003. } \author{Gabor Csardi \email{csardi.gabor@gmail.com} for the current implementation, Peter McMahan (\url{http://home.uchicago.edu/~mcmahan/}) wrote the first version in R.} \seealso{\code{\link{graph.cohesion}}} \examples{ ## The graph from the Moody-White paper mw <- graph.formula(1-2:3:4:5:6, 2-3:4:5:7, 3-4:6:7, 4-5:6:7, 5-6:7:21, 6-7, 7-8:11:14:19, 8-9:11:14, 9-10, 10-12:13, 11-12:14, 12-16, 13-16, 14-15, 15-16, 17-18:19:20, 18-20:21, 19-20:22:23, 20-21, 21-22:23, 22-23) mwBlocks <- cohesive.blocks(mw) # Inspect block membership and cohesion mwBlocks blocks(mwBlocks) cohesion(mwBlocks) # Save results in a Pajek file \dontrun{ exportPajek(mwBlocks, mw, file="/tmp/mwBlocks.paj") } # Plot the results if (interactive()) { plot(mwBlocks, mw) } ## The science camp network camp <- graph.formula(Harry:Steve:Don:Bert - Harry:Steve:Don:Bert, Pam:Brazey:Carol:Pat - Pam:Brazey:Carol:Pat, Holly - Carol:Pat:Pam:Jennie:Bill, Bill - Pauline:Michael:Lee:Holly, Pauline - Bill:Jennie:Ann, Jennie - Holly:Michael:Lee:Ann:Pauline, Michael - Bill:Jennie:Ann:Lee:John, Ann - Michael:Jennie:Pauline, Lee - Michael:Bill:Jennie, Gery - Pat:Steve:Russ:John, Russ - Steve:Bert:Gery:John, John - Gery:Russ:Michael) campBlocks <- cohesive.blocks(camp) campBlocks if (interactive()) { plot(campBlocks, camp, vertex.label=V(camp)$name, margin=-0.2, vertex.shape="rectangle", vertex.size=24, vertex.size2=8, mark.border=1, colbar=c(NA, NA,"cyan","orange") ) } } \keyword{graphs} igraph/man/graph.automorphisms.Rd0000644000176000001440000000455712251656216016651 0ustar ripleyusers\name{graph.automorphisms} \alias{graph.automorphisms} \concept{Graph automorphism} \title{Number of automorphisms} \description{Calculate the number of automorphisms of a graph, i.e. the number of isomorphisms to itself.} \usage{ graph.automorphisms(graph, sh="fm") } \arguments{ \item{graph}{The input graph, it is treated as undirected.} \item{sh}{The splitting heuristics for the BLISS algorithm. Possible values are: \sQuote{\code{f}}: first non-singleton cell, \sQuote{\code{fl}}: first largest non-singleton cell, \sQuote{\code{fs}}: first smallest non-singleton cell, \sQuote{\code{fm}}: first maximally non-trivially connected non-singleton cell, \sQuote{\code{flm}}: first largest maximally non-trivially connected non-singleton cell, \sQuote{\code{fsm}}: first smallest maximally non-trivially connected non-singleton cell.} } \details{ An automorphism of a graph is a permutation of its vertices which brings the graph into itself. This function calculates the number of automorphism of a graph using the BLISS algorithm. See also the BLISS homepage at \url{http://www.tcs.hut.fi/Software/bliss/index.html}. } \value{ A named list with the following members: \item{group_size}{The size of the automorphism group of the input graph, as a string. This number is exact if igraph was compiled with the GMP library, and approximate otherwise.} \item{nof_nodes}{The number of nodes in the search tree.} \item{nof_leaf_nodes}{The number of leaf nodes in the search tree.} \item{nof_bad_nodes}{Number of bad nodes.} \item{nof_canupdates}{Number of canrep updates.} \item{max_level}{Maximum level.} } \references{ Tommi Junttila and Petteri Kaski: Engineering an Efficient Canonical Labeling Tool for Large and Sparse Graphs, \emph{Proceedings of the Ninth Workshop on Algorithm Engineering and Experiments and the Fourth Workshop on Analytic Algorithms and Combinatorics.} 2007. } \author{ Tommi Juntilla (\url{http://users.ics.aalto.fi/tjunttil/)} for BLISS and Gabor Csardi \email{csardi.gabor@gmail.com} for the igraph glue code and this manual page.} \seealso{\code{\link{canonical.permutation}}, \code{\link{permute.vertices}}} \examples{ ## A ring has n*2 automorphisms, you can "turn" it by 0-9 vertices ## and each of these graphs can be "flipped" g <- graph.ring(10) graph.automorphisms(g) } \keyword{graphs} igraph/man/as.directed.Rd0000644000176000001440000000602312240234657015011 0ustar ripleyusers\name{as.directed} \alias{as.directed} \alias{as.undirected} \concept{Directed graph} \concept{Undirected graph} \title{Convert between directed and undirected graphs} \description{\code{as.directed} converts an undirected graph to directed, \code{as.undirected} does the opposite, it converts a directed graph to undirected.} \usage{ as.directed(graph, mode = c("mutual", "arbitrary")) as.undirected(graph, mode = c("collapse", "each", "mutual"), edge.attr.comb = getIgraphOpt("edge.attr.comb")) } \arguments{ \item{graph}{The graph to convert.} \item{mode}{Character constant, defines the conversion algorithm. For \code{as.directed} it can be \code{mutual} or \code{arbitrary}. For \code{as.undirected} it can be \code{each}, \code{collapse} or \code{mutual}. See details below.} \item{edge.attr.comb}{Specifies what to do with edge attributes, if \code{mode="collapse"} or \code{mode="mutual"}. In these cases many edges might be mapped to a single one in the new graph, and their attributes are combined. Please see \code{\link{attribute.combination}} for details on this.} } \details{ Conversion algorithms for \code{as.directed}: \describe{ \item{\code{arbitrary}}{The number of edges in the graph stays the same, an arbitrarily directed edge is created for each undirected edge.} \item{\code{mutual}}{Two directed edges are created for each undirected edge, one in each direction.} } Conversion algorithms for \code{as.undirected}: \describe{ \item{\code{each}}{The number of edges remains constant, an undirected edge is created for each directed one, this version might create graphs with multiple edges.} \item{\code{collapse}}{One undirected edge will be created for each pair of vertices which are connected with at least one directed edge, no multiple edges will be created.} \item{\code{mutual}}{One undirected edge will be created for each pair of mutual edges. Non-mutual edges are ignored. This mode might create multiple edges if there are more than one mutual edge pairs between the same pair of vertices. } } } \value{ A new graph object. } %\references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{simplify}} for removing multiple and/or loop edges from a graph.} \examples{ g <- graph.ring(10) as.directed(g, "mutual") g2 <- graph.star(10) as.undirected(g) # Combining edge attributes g3 <- graph.ring(10, directed=TRUE, mutual=TRUE) E(g3)$weight <- seq_len(ecount(g3)) ug3 <- as.undirected(g3) print(ug3, e=TRUE) \dontrun{ x11(width=10, height=5) layout(rbind(1:2)) plot( g3, layout=layout.circle, edge.label=E(g3)$weight) plot(ug3, layout=layout.circle, edge.label=E(ug3)$weight) } g4 <- graph(c(1,2, 3,2,3,4,3,4, 5,4,5,4, 6,7, 7,6,7,8,7,8, 8,7,8,9,8,9, 9,8,9,8,9,9, 10,10,10,10)) E(g4)$weight <- seq_len(ecount(g4)) ug4 <- as.undirected(g4, mode="mutual", edge.attr.comb=list(weight=length)) print(ug4, e=TRUE) } \keyword{graphs} igraph/man/hrg.Rd0000644000176000001440000002325412240234657013411 0ustar ripleyusers\name{Hierarchical random graphs} \alias{HRG} \alias{hrg.consensus} \alias{hrg.game} \alias{hrg.create} \alias{hrg.predict} \alias{hrg.dendrogram} \alias{hrg.fit} \alias{print.igraphHRG} \alias{print.igraphHRGConsensus} \concept{Hierarchical random graphs} \title{Hierarchical random graphs} \description{Fitting and sampling hierarchical random graph models.} \usage{ hrg.fit (graph, hrg = NULL, start = FALSE, steps = 0) hrg.consensus (graph, hrg = NULL, start = FALSE, num.samples = 10000) hrg.create (graph, prob) hrg.dendrogram (hrg) hrg.game (hrg) hrg.predict (graph, hrg = NULL, start = FALSE, num.samples = 10000, num.bins = 25) \method{print}{igraphHRG}(x, type=c("auto", "tree", "plain"), level = 3, \dots) \method{print}{igraphHRGConsensus}(x, \dots) } \arguments{ \item{graph}{The graph to fit the model to. Edge directions are ignored in directed graphs.} \item{hrg}{A hierarchical random graph model, in the form of an \code{igraphHRG} object. \code{hrg.fit} allows this to be \code{NULL}, in which case a random starting point is used for the fitting. \code{hrg.consensus} and \code{hrg.predict} allow this to be \code{NULL} as well, then a HRG is fitted to the graph first, from a random starting point.} \item{start}{Logical, whether to start the fitting/sampling from the supplied \code{igraphHRG} object, or from a random starting point.} \item{steps}{The number of MCMC steps to make. If this is zero, then the MCMC procedure is performed until convergence.} \item{num.samples}{Number of samples to use for consensus generation or missing edge prediction.} \item{prob}{A vector of probabilities, one for each vertex, in the order of vertex ids.} \item{num.bins}{Number of bins for the edge probabilities. Give a higher number for a more accurate prediction.} \item{x}{\code{igraphHRG} or \code{igraphHRGConsensus} object to print.} \item{type}{How to print the dendrogram, see details below.} \item{level}{The number of top levels to print from the dendrogram.} \item{\dots}{Additional arguments, not used currently.} } \details{ A hierarchical random graph is an ensemble of undirected graphs with \eqn{n} vertices. It is defined via a binary tree with \eqn{n} leaf and \eqn{n-1} internal vertices, where the internal vertices are labeled with probabilities. The probability that two vertices are connected in the random graph is given by the probability label at their closest common ancestor. Please see references below for more about hierarchical random graphs. igraph contains functions for fitting HRG models to a given network (\code{hrg.fit}, for generating networks from a given HRG ensemble (\code{hrg.game}), converting an igraph graph to a HRG and back (\code{hrg.create}, \code{hrg.dendrogram}), for calculating a consensus tree from a set of sampled HRGs (\code{hrg.consensus}) and for predicting missing edges in a network based on its HRG models (\code{hrg.predict}). The igraph HRG implementation is heavily based on the code published by Aaron Clauset, at his website, \url{http://tuvalu.santafe.edu/~aaronc/hierarchy/}. \code{hrg.fit} fits a HRG to a given graph. It takes the specified \code{steps} number of MCMC steps to perform the fitting, or a convergence criteria if the specified number of steps is zero. \code{hrg.fit} can start from a given HRG, if this is given in the \code{hrg} argument and the \code{start} argument is \code{TRUE}. \code{hrg.consensus} creates a consensus tree from several fitted hierarchical random graph models, using phylogeny methods. If the \code{hrg} argument is given and \code{start} is set to \code{TRUE}, then it starts sampling from the given HRG. Otherwise it optimizes the HRG log-likelihood first, and then samples starting from the optimum. \code{hrg.create} creates a HRG from an igraph graph. The igraph graph must be a directed binary tree, with \eqn{n-1} internal and \eqn{n} leaf vertices. The \code{prob} argument contains the HRG probability labels for each vertex; these are ignored for leaf vertices. \code{hrg.dendrogram} creates the corresponsing igraph tree of a hierarchical random graph model. \code{hrg.game} samples a graph from a given hierarchical random graph model. \code{hrg.predict} uses a hierarchical random graph model to predict missing edges from a network. This is done by sampling hierarchical models around the optimum model, proportionally to their likelihood. The MCMC sampling is stated from \code{hrg}, if it is given and the \code{start} argument is set to \code{TRUE}. Otherwise a HRG is fitted to the graph first. } \section{Printing HRGs to the screen}{ \code{igraphHRG} objects can be printed to the screen in two forms: as a tree or as a list, depending on the \code{type} argument of the print function. By default the \code{auto} type is used, which selects \code{tree} for small graphs and \code{simple} (=list) for bigger ones. The \code{tree} format looks like this: \preformatted{Hierarchical random graph, at level 3: g1 p= 0 '- g15 p=0.33 1 '- g13 p=0.88 6 3 9 4 2 10 7 5 8 '- g8 p= 0.5 '- g16 p= 0.2 20 14 17 19 11 15 16 13 '- g5 p= 0 12 18 } This is a graph with 20 vertices, and the top three levels of the fitted hierarchical random graph are printed. The root node of the HRG is always vertex group #1 (\sQuote{\code{g1}} in the the printout). Vertex pairs in the left subtree of \code{g1} connect to vertices in the right subtree with probability zero, according to the fitted model. \code{g1} has two subgroups, \code{g15} and \code{g8}. \code{g15} has a subgroup of a single vertex (vertex 1), and another larger subgroup that contains vertices 6, 3, etc. on lower levels, etc. The \code{plain} printing is simpler and faster to produce, but less visual: \preformatted{Hierarchical random graph: g1 p=0.0 -> g12 g10 g2 p=1.0 -> 7 10 g3 p=1.0 -> g18 14 g4 p=1.0 -> g17 15 g5 p=0.4 -> g15 17 g6 p=0.0 -> 1 4 g7 p=1.0 -> 11 16 g8 p=0.1 -> g9 3 g9 p=0.3 -> g11 g16 g10 p=0.2 -> g4 g5 g11 p=1.0 -> g6 5 g12 p=0.8 -> g8 8 g13 p=0.0 -> g14 9 g14 p=1.0 -> 2 6 g15 p=0.2 -> g19 18 g16 p=1.0 -> g13 g2 g17 p=0.5 -> g7 13 g18 p=1.0 -> 12 19 g19 p=0.7 -> g3 20} It lists the two subgroups of each internal node, in as many columns as the screen width allows. Consensus dendrograms (\code{igraphHRGConsensus} objects) are printed simply by listing the children of each internal node of the dendrogram: \preformatted{HRG consensus tree: g1 -> 11 12 13 14 15 16 17 18 19 20 g2 -> 1 2 3 4 5 6 7 8 9 10 g3 -> g1 g2} The root of the dendrogram is \code{g3} (because it has no incoming edges), and it has two subgroups, \code{g1} and \code{g2}. } \value{ \code{hrg.fit} returns an \code{igraphHRG} object. This is a list with the following members: \item{left}{Vector that contains the left children of the internal tree vertices. The first vertex is always the root vertex, so the first element of the vector is the left child of the root vertex. Internal vertices are denoted with negative numbers, starting from -1 and going down, i.e. the root vertex is -1. Leaf vertices are denoted by non-negative number, starting from zero and up.} \item{right}{Vector that contains the right children of the vertices, with the same encoding as the \code{left} vector.} \item{prob}{The connection probabilities attached to the internal vertices, the first number belongs to the root vertex (i.e. internal vertex -1), the second to internal vertex -2, etc.} \item{edges}{The number of edges in the subtree below the given internal vertex.} \item{vertices}{The number of vertices in the subtree below the given internal vertex, including itself.} \code{hrg.consensus} returns a list of two objects. The first is an \code{igraphHRGConsensus} object, the second is an \code{igraphHRG} object. The \code{igraphHRGConsensus} object has the following members: \item{parents}{For each vertex, the id of its parent vertex is stored, or zero, if the vertex is the root vertex in the tree. The first n vertex ids (from 0) refer to the original vertices of the graph, the other ids refer to vertex groups.} \item{weights}{Numeric vector, counts the number of times a given tree split occured in the generated network samples, for each internal vertices. The order is the same as in the \code{parents} vector.} \code{hrg.create} returns an \code{igraphHRG} object. \code{hrg.dendrogram} returns an igraph graph. \code{hrg.game} returns an igraph graph. } \references{ A. Clauset, C. Moore, and M.E.J. Newman. Hierarchical structure and the prediction of missing links in networks. \emph{Nature} 453, 98--101 (2008); A. Clauset, C. Moore, and M.E.J. Newman. Structural Inference of Hierarchies in Networks. In E. M. Airoldi et al. (Eds.): ICML 2006 Ws, \emph{Lecture Notes in Computer Science} 4503, 1--13. Springer-Verlag, Berlin Heidelberg (2007). } \author{ Gabor Csardi \email{csardi.gabor@gmail.com}, based on code from Aaron Clauset, thanks Aaron! } % \seealso{} \examples{ ## We are not running these examples any more, because they ## take a long time (~15 seconds) to run and this is against the CRAN ## repository policy. Copy and paste them by hand to your R prompt if ## you want to run them. \dontrun{ ## A graph with two dense groups g <- erdos.renyi.game(10, p=1/2) + erdos.renyi.game(10, p=1/2) hrg <- hrg.fit(g) hrg ## The consensus tree for it hrg.consensus(g, hrg=hrg, start=TRUE) ## Prediction of missing edges g2 <- graph.full(4) + (graph.full(4) - path(1,2)) hrg.predict(g2) } } \keyword{graphs} igraph/man/is.dag.Rd0000644000176000001440000000125312240234657013771 0ustar ripleyusers\name{is.dag} \alias{is.dag} \concept{DAG} \title{Directed acyclic graphs} \description{This function tests whether the given graph is a DAG, a directed acyclic graph.} \usage{ is.dag(graph) } \arguments{ \item{graph}{The input graph. It may be undirected, in which case \code{FALSE} is reported.} } \details{ \code{is.dag} checks whether there is a directed cycle in the graph. If not, the graph is a DAG. } \value{A logical vector of length one.} \author{Tamas Nepusz \email{ntamas@gmail.com} for the C code, Gabor Csardi \email{csardi.gabor@gmail.com} for the R interface. } \examples{ g <- graph.tree(10) is.dag(g) g2 <- g + edge(5,1) is.dag(g2) } \keyword{graphs} igraph/man/walktrap.community.Rd0000644000176000001440000000437012251656216016500 0ustar ripleyusers\name{walktrap.community} \alias{walktrap.community} \concept{Random walk} \concept{Community structure} \title{Community strucure via short random walks} \description{This function tries to find densely connected subgraphs, also called communities in a graph via random walks. The idea is that short random walks tend to stay in the same community. } \usage{ walktrap.community(graph, weights = E(graph)$weight, steps = 4, merges = TRUE, modularity = TRUE, membership = TRUE) } \arguments{ \item{graph}{The input graph, edge directions are ignored in directed graphs.} \item{weights}{The edge weights.} \item{steps}{The length of the random walks to perform.} \item{merges}{Logical scalar, whether to include the merge matrix in the result.} \item{modularity}{Logical scalar, whether to include the vector of the modularity scores in the result. If the \code{membership} argument is true, then it will be always calculated.} \item{membership}{Logical scalar, whether to calculate the membership vector for the split corresponding to the highest modularity value.} } \details{ This function is the implementation of the Walktrap community finding algorithm, see Pascal Pons, Matthieu Latapy: Computing communities in large networks using random walks, http://arxiv.org/abs/physics/0512106 } \value{ \code{walktrap.community} returns a \code{\link{communities}} object, please see the \code{\link{communities}} manual page for details. } \references{Pascal Pons, Matthieu Latapy: Computing communities in large networks using random walks, http://arxiv.org/abs/physics/0512106 } \author{Pascal Pons (\url{http://psl.pons.free.fr/}) and Gabor Csardi \email{csardi.gabor@gmail.com} for the R and igraph interface} \seealso{ See \code{\link{communities}} on getting the actual membership vector, merge matrix, modularity score, etc. \code{\link{modularity}} and \code{\link{fastgreedy.community}}, \code{\link{spinglass.community}}, \code{\link{leading.eigenvector.community}}, \code{\link{edge.betweenness.community}} for other community detection methods. } \examples{ g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6, 11)) walktrap.community(g) } \keyword{graphs} igraph/man/optimal.community.Rd0000644000176000001440000000460012251656216016314 0ustar ripleyusers\name{optimal.community} \alias{optimal.community} \concept{Community structure} \concept{Modularity} \title{Optimal community structure} \description{ This function calculates the optimal community structure of a graph, by maximizing the modularity measure over all possible partitions.} \usage{ optimal.community(graph, weights = NULL) } \arguments{ \item{graph}{The input graph. Edge directions are ignored for directed graphs.} \item{weights}{Optional positive weight vector for optimizing weighted modularity. If the graph has a \code{weight} edge attribute, then this is used by default. Supply \code{NA} to ignore the weights of a weighted graph.} } \details{ This function calculates the optimal community structure for a graph, in terms of maximal modularity score. The calculation is done by transforming the modularity maximization into an integer programming problem, and then calling the GLPK library to solve that. Please the reference below for details. Note that modularity optimization is an NP-complete problem, and all known algorithms for it have exponential time complexity. This means that you probably don't want to run this function on larger graphs. Graphs with up to fifty vertices should be fine, graphs with a couple of hundred vertices might be possible. } \value{ \code{optimal.community} returns a \code{\link{communities}} object, please see the \code{\link{communities}} manual page for details. } \references{ Ulrik Brandes, Daniel Delling, Marco Gaertler, Robert Gorke, Martin Hoefer, Zoran Nikoloski, Dorothea Wagner: On Modularity Clustering, \emph{IEEE Transactions on Knowledge and Data Engineering} 20(2):172-188, 2008. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{communities}} for the documentation of the result, \code{\link{modularity}}. See also \code{\link{fastgreedy.community}} for a fast greedy optimizer. } \examples{ ## Zachary's karate club g <- graph.famous("Zachary") ## We put everything into a big 'try' block, in case ## igraph was compiled without GLPK support try({ ## The calculation only takes a couple of seconds oc <- optimal.community(g) ## Double check the result print(modularity(oc)) print(modularity(g, membership(oc))) ## Compare to the greedy optimizer fc <- fastgreedy.community(g) print(modularity(fc)) }, silent=TRUE) } \keyword{graphs} igraph/man/layout.grid.Rd0000644000176000001440000000271212240234657015066 0ustar ripleyusers\name{layout.grid} \alias{layout.grid} \alias{layout.grid.3d} \concept{Graph layout} \title{Simple grid layout} \description{This layout places vertices on a rectangulat grid, in two or three dimensions.} \usage{ layout.grid (graph, width = 0) layout.grid.3d (graph, width = 0, height = 0) } \arguments{ \item{graph}{The input graph.} \item{width}{The number of vertices in a single row of the grid. If this is zero or negative for \code{layout.grid}, then the width of the grid will be the square root of the number of vertices in the graph, rounded up to the next integer. Similarly, it will be the cube root for \code{layout.grid.3d}.} \item{height}{The number of vertices in a single column of the grid, for three dimensional layouts. If this is zero or negative, then it is determinted automatically.} } \details{ These functions place the vertices on a simple rectangular grid, one after the other. If you want to change the order of the vertices, then see the \code{\link{permute.vertices}} function. } \value{ A two-column matrix for \code{layout.grid}, a three-column matrix for \code{layout.grid.3d}. } % \references{} \author{ Tamas Nepusz \email{ntamas@gmail.com} } \seealso{ \code{\link{layout}} for other layout generators } \examples{ g <- graph.lattice( c(3,3) ) layout.grid(g) g2 <- graph.lattice( c(3,3,3) ) layout.grid.3d(g2) \dontrun{ plot(g, layout=layout.grid) rglplot(g, layout=layout.grid.3d) } } \keyword{graphs} igraph/man/shortest.paths.Rd0000644000176000001440000002420112251656216015614 0ustar ripleyusers\name{shortest.paths} \alias{shortest.paths} \alias{get.shortest.paths} \alias{get.all.shortest.paths} \alias{average.path.length} \alias{path.length.hist} \concept{Shortest path} \concept{Geodesic} \title{Shortest (directed or undirected) paths between vertices} \description{\code{shortest.paths} calculates the length of all the shortest paths from or to the vertices in the network. \code{get.shortest.paths} calculates one shortest path (the path itself, and not just its length) from or to the given vertex.} \usage{ shortest.paths(graph, v=V(graph), to=V(graph), mode = c("all", "out", "in"), weights = NULL, algorithm = c("automatic", "unweighted", "dijkstra", "bellman-ford", "johnson")) get.shortest.paths(graph, from, to=V(graph), mode = c("out", "all", "in"), weights = NULL, output=c("vpath", "epath", "both"), predecessors = FALSE, inbound.edges = FALSE) get.all.shortest.paths(graph, from, to = V(graph), mode = c("out", "all", "in"), weights=NULL) average.path.length(graph, directed=TRUE, unconnected=TRUE) path.length.hist (graph, directed = TRUE) } \arguments{ \item{graph}{The graph to work on.} \item{v}{Numeric vector, the vertices from which the shortest paths will be calculated.} \item{to}{Numeric vector, the vertices to which the shortest paths will be calculated. By default it includes all vertices. Note that for \code{shortest.paths} every vertex must be included here at most once. (This is not required for \code{get.shortest.paths}.} \item{mode}{Character constant, gives whether the shortest paths to or from the given vertices should be calculated for directed graphs. If \code{out} then the shortest paths \emph{from} the vertex, if \code{in} then \emph{to} it will be considered. If \code{all}, the default, then the corresponding undirected graph will be used, ie. not directed paths are searched. This argument is ignored for undirected graphs.} \item{weights}{Possibly a numeric vector giving edge weights. If this is \code{NULL} and the graph has a \code{weight} edge attribute, then the attribute is used. If this is \code{NA} then no weights are used (even if the graph has a \code{weight} attribute).} \item{algorithm}{Which algorithm to use for the calculation. By default igraph tries to select the fastest suitable algorithm. If there are no weights, then an unweighted breadth-first search is used, otherwise if all weights are positive, then Dijkstra's algorithm is used. If there are negative weights and we do the calculation for more than 100 sources, then Johnson's algorithm is used. Otherwise the Bellman-Ford algorithm is used. You can override igraph's choice by explicitly giving this parameter. Note that the igraph C core might still override your choice in obvious cases, i.e. if there are no edge weights, then the unweighted algorithm will be used, regardless of this argument. } \item{from}{Numeric constant, the vertex from or to the shortest paths will be calculated. Note that right now this is not a vector of vertex ids, but only a single vertex.} \item{output}{Character scalar, defines how to report the shortest paths. \dQuote{vpath} means that the vertices along the paths are reported, this form was used prior to igraph version 0.6. \dQuote{epath} means that the edges along the paths are reported. \dQuote{both} means that both forms are returned, in a named list with components \dQuote{vpath} and \dQuote{epath}.} \item{predecessors}{Logical scalar, whether to return the predecessor vertex for each vertex. The predecessor of vertex \code{i} in the tree is the vertex from which vertex \code{i} was reached. The predecessor of the start vertex (in the \code{from} argument) is itself by definition. If the predecessor is zero, it means that the given vertex was not reached from the source during the search. Note that the search terminates if all the vertices in \code{to} are reached.} \item{inbound.edges}{Logical scalar, whether to return the inbound edge for each vertex. The inbound edge of vertex \code{i} in the tree is the edge via which vertex \code{i} was reached. The start vertex and vertices that were not reached during the search will have zero in the corresponding entry of the vector. Note that the search terminates if all the vertices in \code{to} are reached.} \item{directed}{Whether to consider directed paths in directed graphs, this argument is ignored for undirected graphs.} \item{unconnected}{What to do if the graph is unconnected (not strongly connected if directed paths are considered). If TRUE only the lengths of the existing paths are considered and averaged; if FALSE the length of the missing paths are counted having length \code{vcount(graph)}, one longer than the longest possible geodesic in the network.} } \details{ The shortest path, or geodesic between two pair of vertices is a path with the minimal number of vertices. The functions documented in this manual page all calculate shortest paths between vertex pairs. \code{shortest.paths} calculates the lengths of pairwise shortest paths from a set of vertices (\code{from}) to another set of vertices (\code{to}). It uses different algorithms, depending on the \code{argorithm} argument and the \code{weight} edge attribute of the graph. The implemented algorithms are breadth-first search (\sQuote{\code{unweighted}}), this only works for unweighted graphs; the Dijkstra algorithm (\sQuote{\code{dijkstra}}), this works for graphs with non-negative edge weights; the Bellman-Ford algorithm (\sQuote{\code{bellman-ford}}), and Johnson's algorithm (\sQuote{\code{"johnson"}}). The latter two algorithms work with arbitrary edge weights, but (naturally) only for graphs that don't have a negative cycle. igraph can choose automatically between algorithms, and chooses the most efficient one that is appropriate for the supplied weights (if any). For automatic algorithm selection, supply \sQuote{\code{automatic}} as the \code{algorithm} argument. (This is also the default.) \code{get.shortest.paths} calculates a single shortest path (i.e. the path itself, not just its length) between the source vertex given in \code{from}, to the target vertices given in \code{to}. \code{get.shortest.paths} uses breadth-first search for unweighted graphs and Dijkstra's algorithm for weighted graphs. The latter only works if the edge weights are non-negative. \code{get.all.shortest.paths} calculates \emph{all} shortest paths between pairs of vertices. More precisely, between the \code{from} vertex to the vertices given in \code{to}. It uses a breadth-first search for unweighted graphs and Dijkstra's algorithm for weighted ones. The latter only supports non-negative edge weights. \code{average.path.length} calculates the average path length in a graph, by calculating the shortest paths between all pairs of vertices (both ways for directed graphs). This function does not consider edge weights currently and uses a breadth-first search. \code{path.length.hist} calculates a histogram, by calculating the shortest path length between each pair of vertices. For directed graphs both directions are considered, so every pair of vertices appears twice in the histogram. } \value{ For \code{shortest.paths} a numeric matrix with \code{length(to)} columns and \code{length(v)} rows. The shortest path length from a vertex to itself is always zero. For unreachable vertices \code{Inf} is included. For \code{get.shortest.paths} a named list with four entries is returned: \item{vpath}{This itself is a list, of length \code{length(to)}; list element \code{i} contains the vertex ids on the path from vertex \code{from} to vertex \code{to[i]} (or the other way for directed graphs depending on the \code{mode} argument). The vector also contains \code{from} and \code{i} as the first and last elements. If \code{from} is the same as \code{i} then it is only included once. If there is no path between two vertices then a numeric vector of length zero is returned as the list element. If this output is not requested in the \code{output} argument, then it will be \code{NULL}.} \item{epath}{This is a list similar to \code{vpath}, but the vectors of the list contain the edge ids along the shortest paths, instead of the vertex ids. This entry is set to \code{NULL} if it is not requested in the \code{output} argument.} \item{predecessors}{Numeric vector, the predecessor of each vertex in the \code{to} argument, or \code{NULL} if it was not requested.} \item{inbound_edges}{Numeric vector, the inbound edge for each vertex, or \code{NULL}, if it was not requested.} For \code{get.all.shortest.paths} a list is returned, each list element contains a shortest path from \code{from} to a vertex in \code{to}. The shortest paths to the same vertex are collected into consecutive elements of the list. For \code{average.path.length} a single number is returned. \code{path.length.hist} returns a named list with two entries: \code{res} is a numeric vector, the histogram of distances, \code{unconnected} is a numeric scalar, the number of pairs for which the first vertex is not reachable from the second. The sum of the two entries is always \eqn{n(n-1)} for directed graphs and \eqn{n(n-1)/2} for undirected graphs. } \references{ West, D.B. (1996). \emph{Introduction to Graph Theory.} Upper Saddle River, N.J.: Prentice Hall. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} %\seealso{} \examples{ g <- graph.ring(10) shortest.paths(g) get.shortest.paths(g, 5) get.all.shortest.paths(g, 1, 6:8) average.path.length(g) ## Weighted shortest paths el <- matrix(nc=3, byrow=TRUE, c(1,2,0, 1,3,2, 1,4,1, 2,3,0, 2,5,5, 2,6,2, 3,2,1, 3,4,1, 3,7,1, 4,3,0, 4,7,2, 5,6,2, 5,8,8, 6,3,2, 6,7,1, 6,9,1, 6,10,3, 8,6,1, 8,9,1, 9,10,4) ) g2 <- add.edges(graph.empty(10), t(el[,1:2]), weight=el[,3]) shortest.paths(g2, mode="out") } \keyword{graphs} igraph/man/layout.mds.Rd0000644000176000001440000000355012240234657014725 0ustar ripleyusers\name{layout.mds} \alias{layout.mds} \concept{Graph layout} \title{Graph layout by multidimensional scaling} \description{ Multidimensional scaling of some distance matrix defined on the vertices of a graph. } \usage{ layout.mds(graph, dist=NULL, dim=2, options=igraph.arpack.default) } \arguments{ \item{graph}{The input graph.} \item{dist}{The distance matrix for the multidimensional scaling. If \code{NULL} (the default), then the unweighted shortest path matrix is used.} \item{dim}{\code{layout.mds} supports dimensions up to the number of nodes minus one, but only if the graph is connected; for unconnected graphs, the only possible values is 2. This is because \code{layout.merge} only works in 2D.} \item{options}{This is currently ignored, as ARPACK is not used any more for solving the eigenproblem} } \details{ \code{layout.mds} uses metric multidimensional scaling for generating the coordinates. Multidimensional scaling aims to place points from a higher dimensional space in a (typically) 2 dimensional plane, so that the distance between the points are kept as much as this is possible. By default igraph uses the shortest path matrix as the distances between the nodes, but the user can override this via the \code{dist} argument. This function generates the layout separately for each graph component and then merges them via \code{\link{layout.merge}}. } \value{ A numeric matrix with \code{dim} columns. } \references{ Cox, T. F. and Cox, M. A. A. (2001) \emph{Multidimensional Scaling}. Second edition. Chapman and Hall. } \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{layout}}, \code{\link{plot.igraph}}} \examples{ g <- erdos.renyi.game(100, 2/100) l <- layout.mds(g) plot(g, layout=l, vertex.label=NA, vertex.size=3) } \keyword{graphs} igraph/man/watts.strogatz.game.Rd0000644000176000001440000000312212240234657016547 0ustar ripleyusers\name{watts.strogatz.game} \alias{watts.strogatz.game} \concept{Small-world model} \concept{Watts-strogatz model} \title{The Watts-Strogatz small-world model} \description{Generate a graph according to the Watts-Strogatz network model.} \usage{ watts.strogatz.game(dim, size, nei, p, loops = FALSE, multiple = FALSE) } \arguments{ \item{dim}{Integer constant, the dimension of the starting lattice.} \item{size}{Integer constant, the size of the lattice along each dimension.} \item{nei}{Integer constant, the neighborhood within which the vertices of the lattice will be connected.} \item{p}{Real constant between zero and one, the rewiring probability. } \item{loops}{Logical scalar, whether loops edges are allowed in the generated graph.} \item{multiple}{Logical scalar, whether multiple edges are allowed int the generated graph.} } \details{ First a lattice is created with the given \code{dim}, \code{size} and \code{nei} arguments. Then the edges of the lattice are rewired uniformly randomly with probability \code{p}. Note that this function might create graphs with loops and/or multiple edges. You can use \code{\link{simplify}} to get rid of these. } \value{A graph object.} \references{ Duncan J Watts and Steven H Strogatz: Collective dynamics of \sQuote{small world} networks, Nature 393, 440-442, 1998. } \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.lattice}}, \code{\link{rewire.edges}}} \examples{ g <- watts.strogatz.game(1, 100, 5, 0.05) average.path.length(g) transitivity(g, type="average") } \keyword{graphs} igraph/man/similarity.Rd0000644000176000001440000000511312240234657015011 0ustar ripleyusers\name{similarity} \alias{similarity.jaccard} \alias{similarity.dice} \alias{similarity.invlogweighted} \concept{Vertex similarity} \title{Similarity measures of two vertices} \description{ These functions calculates similarity scores for vertices based on their connection patterns. } \usage{ similarity.jaccard(graph, vids = V(graph), mode = c("all", "out", "in", "total"), loops = FALSE) similarity.dice(graph, vids = V(graph), mode = c("all", "out", "in", "total"), loops = FALSE) similarity.invlogweighted(graph, vids = V(graph), mode = c("all", "out", "in", "total")) } \arguments{ \item{graph}{The input graph.} \item{vids}{The vertex ids for which the similarity is calculated.} \item{mode}{The type of neighboring vertices to use for the calculation, possible values: \sQuote{\code{out}}, \sQuote{\code{in}}, \sQuote{\code{all}}.} \item{loops}{Whether to include vertices themselves in the neighbor sets.} } \details{ The Jaccard similarity coefficient of two vertices is the number of common neighbors divided by the number of vertices that are neighbors of at least one of the two vertices being considered. \code{similarity.jaccard} calculates the pairwise Jaccard similarities for some (or all) of the vertices. The Dice similarity coefficient of two vertices is twice the number of common neighbors divided by the sum of the degrees of the vertices. \code{similarity.dice} calculates the pairwise Dice similarities for some (or all) of the vertices. The inverse log-weighted similarity of two vertices is the number of their common neighbors, weighted by the inverse logarithm of their degrees. It is based on the assumption that two vertices should be considered more similar if they share a low-degree common neighbor, since high-degree common neighbors are more likely to appear even by pure chance. Isolated vertices will have zero similarity to any other vertex. Self-similarities are not calculated. See the following paper for more details: Lada A. Adamic and Eytan Adar: Friends and neighbors on the Web. Social Networks, 25(3):211-230, 2003. } \value{ A \code{length(vids)} by \code{length(vids)} numeric matrix containing the similarity scores. } \references{Lada A. Adamic and Eytan Adar: Friends and neighbors on the Web. \emph{Social Networks}, 25(3):211-230, 2003.} \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com} for the manual page.} \seealso{\code{\link{cocitation}} and \code{\link{bibcoupling}}} \examples{ g <- graph.ring(5) similarity.dice(g) similarity.jaccard(g) } \keyword{graphs} igraph/man/subgraph.Rd0000644000176000001440000000427112240234657014442 0ustar ripleyusers\name{subgraph} \alias{subgraph} \alias{induced.subgraph} \alias{subgraph.edges} \concept{Subgraph} \title{Subgraph of a graph} \description{\code{subgraph} creates a subgraph of a graph, containing only the specified vertices and all the edges among them.} \usage{ induced.subgraph(graph, vids, impl=c("auto", "copy_and_delete", "create_from_scratch")) subgraph.edges(graph, eids, delete.vertices = TRUE) subgraph(graph, v) } \arguments{ \item{graph}{The original graph.} \item{vids,v}{Numeric vector, the vertices of the original graph which will form the subgraph.} \item{impl}{Character scalar, to choose between two implementation of the subgraph calculation. \sQuote{\code{copy_and_delete}} copies the graph first, and then deletes the vertices and edges that are not included in the result graph. \sQuote{\code{create_from_scratch}} searches for all vertices and edges that must be kept and then uses them to create the graph from scratch. \sQuote{\code{auto}} chooses between the two implementations automatically, using heuristics based on the size of the original and the result graph.} \item{eids}{The edge ids of the edges that will be kept in the result graph.} \item{delete.vertices}{Logical scalar, whether to remove vertices that do not have any adjacent edges in \code{eids}.} } \details{ \code{induced.subgraph} calculates the induced subgraph of a set of vertices in a graph. This means that exactly the specified vertices and all the edges between then will be kept in the result graph. \code{subgraph.edges} calculates the subgraph of a graph. For this function one can specify the vertices and edges to keep. This function will be renamed to \code{subgraph} in the next major version of igraph. The \code{subgraph} function does the same as \code{induced.graph} currently (assuming \sQuote{\code{auto}} as the \code{impl} argument), but it is deprecated and will be removed in the next major version of igraph. } \value{A new graph object.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso \examples{ g <- graph.ring(10) g2 <- induced.subgraph(g, 1:7) g3 <- subgraph.edges(g, 1:5, 1:5) } \keyword{graphs} igraph/man/growing.random.game.Rd0000644000176000001440000000241212240234657016465 0ustar ripleyusers\name{growing.random.game} \alias{growing.random.game} \concept{Random graph model} \title{Growing random graph generation} \description{This function creates a random graph by simulating its stochastic evolution.} \usage{ growing.random.game(n, m = 1, directed = TRUE, citation = FALSE) } \arguments{ \item{n}{Numeric constant, number of vertices in the graph.} \item{m}{Numeric constant, number of edges added in each time step.} \item{directed}{Logical, whether to create a directed graph.} \item{citation}{Logical. If \code{TRUE} a citation graph is created, ie. in each time step the added edges are originating from the new vertex. } } \details{ This is discrete time step model, in each time step a new vertex is added to the graph and \code{m} new edges are created. If \code{citation} is \code{FALSE} these edges are connecting two uniformly randomly chosen vertices, otherwise the edges are connecting new vertex to uniformly randomly chosen old vertices. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{barabasi.game}}, \code{\link{erdos.renyi.game}}} \examples{ g <- growing.random.game(500, citation=FALSE) g2 <- growing.random.game(500, citation=TRUE) } \keyword{graphs} igraph/man/scg_extra.Rd0000644000176000001440000000376712251656216014620 0ustar ripleyusers\name{scgExtra} \alias{scgNormEps} \title{SCG Extra Functions} \description{ Some useful functions to perform general actions in Spectral Coarse Graining (SCG). } \usage{ scgNormEps(V, groups, mtype = c("symmetric", "laplacian", "stochastic"), p = NULL, norm = c("row", "col")) } \arguments{ \item{V}{A numeric matrix of (eigen)vectors assumed normalized. The vectors are to be stored column-wise in \code{V}).} \item{groups}{A vector of \code{nrow(V)} integers labeling each group vertex in the partition.} \item{mtype}{The type of semi-projector used for the SCG. For now \dQuote{symmetric}, \dQuote{laplacian} and \dQuote{stochastic} are available.} \item{p}{A probability vector of length \code{nrow(V)}. \code{p} is the stationary probability distribution of a Markov chain when \code{mtype} = \dQuote{stochastic}. This parameter is ignored otherwise.} \item{norm}{Either \dQuote{row} or \dQuote{col}. If set to \dQuote{row} the rows of the Laplacian matrix sum to zero and the rows of the stochastic matrix sum to one; otherwise it is the columns.} } \details{ \code{scgNormEps} computes \eqn{\Vert v_i-Pv_i\Vert}{|v[i]-Pv[i]|}, where \eqn{v_i}{v[i]} is the \eqn{i}th eigenvector in \code{V} and \eqn{P} is the projector corresponding to the \code{mtype} argument. } \value{ \code{normEps} returns with a numeric vector whose \eqn{i}th component is \eqn{\Vert v_i-Pv_i\Vert}{|v[i]-Pv[i]|} (see Details). } \references{ D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, Shrinking Matrices while Preserving their Eigenpairs with Application to the Spectral Coarse Graining of Graphs. Submitted to \emph{SIAM Journal on Matrix Analysis and Applications}, 2008. \url{http://people.epfl.ch/david.morton} } \author{David Morton de Lachapelle, \url{http://people.epfl.ch/david.morton}.} \seealso{\link{SCG} and \code{\link{scg}}. } \examples{ v <- rexp(20) km <- kmeans(v,5) sum(km$withinss) scgNormEps(cbind(v), km$cluster)^2 } \keyword{graphs} igraph/man/graphlets.Rd0000644000176000001440000000653612251656216014627 0ustar ripleyusers\name{Graphlets} \alias{graphlets} \alias{graphlets.project} \alias{graphlets.candidate.basis} \concept{Graphlets} \title{Graphlet decomposition of a graph} \description{ Graphlet decomposition models a weighted undirected graph via the union of potentially overlapping dense social groups. This is done by a two-step algorithm. In the first step a candidate set of groups (a candidate basis) is created by finding cliques if the thresholded input graph. In the second step these the graph is projected on the candidate basis, resulting a weight coefficient for each clique in the candidate basis. } \usage{ graphlets (graph, weights = NULL, niter = 1000) graphlets.candidate.basis (graph, weights = NULL) graphlets.project (graph, weights = NULL, cliques, niter = 1000, Mu = rep(1, length(cliques))) } \arguments{ \item{graph}{The input graph, edge directions are ignored. Only simple graph (i.e. graphs without self-loops and multiple edges) are supported.} \item{weights}{Edge weights. If the graph has a \code{weight} edge attribute and this argument is \code{NULL} (the default), then the \code{weight} edge attribute is used.} \item{niter}{Integer scalar, the number of iterations to perform.} \item{cliques}{A list of vertex ids, the graphlet basis to use for the projection.} \item{Mu}{Starting weights for the projection.} } \details{ igraph contains three functions for performing the graph decomponsition of a graph. The first is \code{graphlets}, which performed both steps on the method and returns a list of subgraphs, with their corresponding weights. The second and third functions correspond to the first and second steps of the algorithm, and they are useful if the user wishes to perform them individually: \code{graphlets.candidate.basis} and \code{graphlets.project}. } \value{ \code{graphlets} returns a list with two members: \item{cliques}{A list of subgraphs, the candidate graphlet basis. Each subgraph is give by a vector of vertex ids.} \item{Mu}{The weights of the subgraphs in graphlet basis.} \code{graphlets.candidate.basis} returns a list of two elements: \item{cliques}{A list of subgraphs, the candidate graphlet basis. Each subgraph is give by a vector of vertex ids.} \item{thresholds}{The weight thresholds used for finding the subgraphs.} \code{graphlets.project} return a numeric vector, the weights of the graphlet basis subgraphs. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } % \seealso{} \examples{ ## Create an example graph first D1 <- matrix(0, 5, 5) D2 <- matrix(0, 5, 5) D3 <- matrix(0, 5, 5) D1[1:3, 1:3] <- 2 D2[3:5, 3:5] <- 3 D3[2:5, 2:5] <- 1 g <- simplify(graph.adjacency(D1 + D2 + D3, mode="undirected", weighted=TRUE)) V(g)$color <- "white" E(g)$label <- E(g)$weight E(g)$label.cex <- 2 E(g)$color <- "black" layout(matrix(1:6, nrow=2, byrow=TRUE)) co <- layout.kamada.kawai(g) par(mar=c(1,1,1,1)) plot(g, layout=co) ## Calculate graphlets gl <- graphlets(g, niter=1000) ## Plot graphlets for (i in 1:length(gl$cliques)) { sel <- gl$cliques[[i]] V(g)$color <- "white" V(g)[sel]$color <- "#E495A5" E(g)$width <- 1 E(g)[ V(g)[sel] \%--\% V(g)[sel] ]$width <- 2 E(g)$label <- "" E(g)[ width == 2 ]$label <- round(gl$Mu[i], 2) E(g)$color <- "black" E(g)[ width == 2 ]$color <- "#E495A5" plot(g, layout=co) } } \keyword{graphs} igraph/man/independent.sets.Rd0000644000176000001440000000550712251656216016105 0ustar ripleyusers\name{independent.vertex.sets} \alias{independent.vertex.sets} \alias{largest.independent.vertex.sets} \alias{maximal.independent.vertex.sets} \alias{independence.number} \concept{Independent vertex set} \title{Independent vertex sets} \description{A vertex set is called independent if there no edges between any two vertices in it. These functions find independent vertex sets in undirected graphs} \usage{ independent.vertex.sets(graph, min=NULL, max=NULL) largest.independent.vertex.sets(graph) maximal.independent.vertex.sets(graph) independence.number(graph) } \arguments{ \item{graph}{The input graph, directed graphs are considered as undirected, loop edges and multiple edges are ignored.} \item{min}{Numeric constant, limit for the minimum size of the independent vertex sets to find. \code{NULL} means no limit.} \item{max}{Numeric constant, limit for the maximum size of the independent vertex sets to find. \code{NULL} means no limit. } } \details{ \code{independent.vertex.sets} finds all independent vertex sets in the network, obeying the size limitations given in the \code{min} and \code{max} arguments. \code{largest.independent.vertex.sets} finds the largest independent vertex sets in the graph. An independent vertex set is largest if there is no independent vertex set with more vertices. \code{maximal.independent.vertex.sets} finds the maximal independent vertex sets in the graph. An independent vertex set is maximal if it cannot be extended to a larger independent vertex set. The largest independent vertex sets are maximal, but the opposite is not always true. \code{independece.number} calculate the size of the largest independent vertex set(s). These functions use the algorithm described by Tsukiyama et al., see reference below. } \value{ \code{independent.vertex.sets}, \code{largest.independent.vertex.sets} and \code{maximal.independent.vertex.sets} return a list containing numeric vertex ids, each list element is an independent vertex set. \code{independence.number} returns an integer constant. } \references{ S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm for generating all the maximal independent sets. \emph{SIAM J Computing}, 6:505--517, 1977. } \author{Tamas Nepusz \email{ntamas@gmail.com} ported it from the Very Nauty Graph Library by Keith Briggs (\url{http://keithbriggs.info/}) and Gabor Csardi \email{csardi.gabor@gmail.com} wrote the R interface and this manual page. } \seealso{\code{\link{cliques}}} \examples{ # A quite dense graph set.seed(42) g <- erdos.renyi.game(100, 0.9) independence.number(g) independent.vertex.sets(g, min=independence.number(g)) largest.independent.vertex.sets(g) # Empty graph induced.subgraph(g, largest.independent.vertex.sets(g)[[1]]) length(maximal.independent.vertex.sets(g)) } \keyword{graphs} igraph/man/graph.difference.Rd0000644000176000001440000000362112251656216016020 0ustar ripleyusers\name{graph.difference} \alias{graph.difference} \alias{\%m\%} \concept{Graph operators} \title{Difference of graphs} \description{The difference of two graphs are created.} \usage{ graph.difference(big, small, byname = "auto") } \arguments{ \item{big}{The left hand side argument of the minus operator. A directed or undirected graph.} \item{small}{The right hand side argument of the minus operator. A directed ot undirected graph.} \item{byname}{A logical scalar, or the character scalar \code{auto}. Whether to perform the operation based on symbolic vertex names. If it is \code{auto}, that means \code{TRUE} if both graphs are named and \code{FALSE} otherwise. A warning is generated if \code{auto} and one graph, but not both graphs are named.} } \details{ \code{graph.difference} creates the difference of two graphs. Only edges present in the first graph but not in the second will be be included in the new graph. The corresponding operator is \%m\%. If the \code{byname} argument is \code{TRUE} (or \code{auto} and the graphs are all named), then the operation is performed based on symbolic vertex names. Otherwise numeric vertex ids are used. \code{graph.difference} keeps all attributes (graph, vertex and edge) of the first graph. Note that \code{big} and \code{small} must both be directed or both be undirected, otherwise an error message is given. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ ## Create a wheel graph wheel <- graph.union(graph.ring(10), graph.star(11, center=11, mode="undirected")) V(wheel)$name <- letters[seq_len(vcount(wheel))] ## Subtract a star graph from it sstar <- graph.star(6, center=6, mode="undirected") V(sstar)$name <- letters[c(1,3,5,7,9,11)] G <- wheel \%m\% sstar str(G) plot(G, layout=layout.auto(wheel)) } \keyword{graphs} igraph/man/is.separator.Rd0000644000176000001440000000420112240234657015232 0ustar ripleyusers\name{is.separator} \alias{is.separator} \alias{is.minimal.separator} \concept{Vertex separator} \title{Vertex separators} \description{These functions check whether a given set of vertices is a vertex separator, or a minimal vertex separator. } \usage{ is.separator(graph, candidate) is.minimal.separator(graph, candidate) } \arguments{ \item{graph}{The input graph. It may be directed, but edge directions are ignored.} \item{candidate}{A numeric vector giving the vertex ids of the candidate separator.} } \details{ \code{is.separator} decides whether the supplied vertex set is a vertex separator. A vertex set is a vertex separator if its removal results a disconnected graph. \code{is.minimal.separator} decides whether the supplied vertex set is a minimal vertex separator. A minimal vertex separator is a vertex separator, such that none of its subsets is a vertex separator. In the special case of a fully connected graph with \eqn{n} vertices, each set of \eqn{n-1} vertices is considered to be a vertex separator. } \value{ A logical scalar, whether the supplied vertex set is a (minimal) vertex separator or not. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{minimum.size.separators}} lists all vertex separator of minimum size. } \examples{ # The graph from the Moody-White paper mw <- graph.formula(1-2:3:4:5:6, 2-3:4:5:7, 3-4:6:7, 4-5:6:7, 5-6:7:21, 6-7, 7-8:11:14:19, 8-9:11:14, 9-10, 10-12:13, 11-12:14, 12-16, 13-16, 14-15, 15-16, 17-18:19:20, 18-20:21, 19-20:22:23, 20-21, 21-22:23, 22-23) # Cohesive subgraphs mw1 <- induced.subgraph(mw, as.character(c(1:7, 17:23))) mw2 <- induced.subgraph(mw, as.character(7:16)) mw3 <- induced.subgraph(mw, as.character(17:23)) mw4 <- induced.subgraph(mw, as.character(c(7,8,11,14))) mw5 <- induced.subgraph(mw, as.character(1:7)) check.sep <- function(G) { sep <- minimum.size.separators(G) sapply(sep, is.minimal.separator, graph=G) } check.sep(mw) check.sep(mw1) check.sep(mw2) check.sep(mw3) check.sep(mw4) check.sep(mw5) } \keyword{graphs} igraph/man/all.st.cuts.Rd0000644000176000001440000000313012240234657014772 0ustar ripleyusers\name{stCuts} \alias{stCuts} \concept{Edge cuts} \concept{(s,t)-cuts} \title{List all (s,t)-cuts of a graph} \description{ List all (s,t)-cuts in a directed graph. } \usage{ stCuts(graph, source, target) } \arguments{ \item{graph}{The input graph. It must be directed.} \item{source}{The source vertex.} \item{target}{The target vertex.} } \details{ Given a \eqn{G} directed graph and two, different and non-ajacent vertices, \eqn{s} and \eqn{t}, an \eqn{(s,t)}-cut is a set of edges, such that after removing these edges from \eqn{G} there is no directed path from \eqn{s} to \eqn{t}. } \value{ A list with entries: \item{cuts}{A list of numeric vectors containing edge ids. Each vector is an \eqn{(s,t)}-cut.} \item{partition1s}{A list of numeric vectors containing vertex ids, they correspond to the edge cuts. Each vertex set is a generator of the corresponding cut, i.e. in the graph \eqn{G=(V,E)}, the vertex set \eqn{X} and its complementer \eqn{V-X}, generates the cut that contains exactly the edges that go from \eqn{X} to \eqn{V-X}.} } \references{ JS Provan and DR Shier: A Paradigm for listing (s,t)-cuts in graphs, \emph{Algorithmica} 15, 351--372, 1996. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{stMincuts}} to list all minimum cuts. } \examples{ # A very simple graph g <- graph.formula(a -+ b -+ c -+ d -+ e) stCuts(g, source="a", target="e") # A somewhat more difficult graph g2 <- graph.formula(s --+ a:b, a:b --+ t, a --+ 1:2:3, 1:2:3 --+ b) stCuts(g2, source="s", target="t") } \keyword{graphs} igraph/man/communities.Rd0000644000176000001440000002515612325262335015166 0ustar ripleyusers\name{communities} \alias{communities} \alias{membership} \alias{algorithm} \alias{crossing} \alias{cutat} \alias{merges} \alias{sizes} \alias{is.hierarchical} \alias{print.communities} \alias{plot.communities} \alias{length.communities} \alias{modularity.communities} \alias{as.dendrogram.communities} \alias{as.hclust.communities} \alias{asPhylo} \alias{asPhylo.communities} \alias{showtrace} \alias{code.length} \alias{create.communities} \concept{Community structure} \title{Functions to deal with the result of network community detection} \description{igraph community detection functions return their results as an object from the \code{communities} class. This manual page describes the operations of this class.} \usage{ \method{print}{communities}(x, \dots) \method{length}{communities}(x) sizes(communities) membership(communities) \method{modularity}{communities}(x, \dots) algorithm(communities) crossing(communities, graph) is.hierarchical(communities, full = FALSE) merges(communities) cutat(communities, no, steps) \method{as.dendrogram}{communities}(object, hang=-1, use.modularity=FALSE, \dots) \method{as.hclust}{communities}(x, hang = -1, use.modularity = FALSE, \dots) \method{asPhylo}{communities}(x, use.modularity=FALSE, \dots) showtrace(communities) code.length(communities) \method{plot}{communities}(x, y, colbar=rainbow(length(x)), col=colbar[membership(x)], mark.groups=communities(x), edge.color=c("black", "red")[crossing(x,y)+1], \dots) create.communities(membership, algorithm = NULL, merges = NULL, modularity = NULL, \dots) } \arguments{ \item{communities,x,object}{A \code{communities} object, the result of an igraph community detection function.} \item{graph}{An igraph graph object, corresponding to \code{communities}.} \item{full}{Logical scalar, if \code{TRUE}, then \code{is.hierarchical} only returns \code{TRUE} for fully hierarchical algorithms. The \sQuote{leading eigenvector} algorithm is hierarchical, it gives a hierarchy of groups, but not a full dendrogram with all vertices, so it is not fully hierarchical.} \item{y}{An igraph graph object, corresponding to the communities in \code{x}.} \item{no}{Integer scalar, the desired number of communities. If too low or two high, then an error message is given. Exactly one of \code{no} and \code{steps} must be supplied.} \item{steps}{The number of merge operations to perform to produce the communities. Exactly one of \code{no} and \code{steps} must be supplied.} \item{colbar}{A vector of colors, in any format that is accepted by the regular R plotting methods. E.g. it may be an integer vector, a character vector of color names, a character vector of RGB colors. This vector gives the color bar for the vertices. The length of the vector should be the same as the number of communities.} \item{col}{A vector of colors, in any format that is accepted by the regular R plotting methods. This vector gives the colors of the vertices explicitly.} \item{mark.groups}{A list of numeric vectors. The communities can be highlighted using colored polygons. The groups for which the polygons are drawn are given here. The default is to use the groups given by the communities. Supply \code{NULL} here if you do not want to highlight any groups.} \item{edge.color}{The colors of the edges. By default the edges within communities are colored green and other edges are red.} \item{hang}{Numeric scalar indicating how the height of leaves should be computed from the heights of their parents; see \code{\link{plot.hclust}}.} \item{use.modularity}{Logical scalar, whether to use the modularity values to define the height of the branches.} \item{\dots}{Additional arguments. \code{plot.communities} passes these to \code{\link{plot.igraph}}. \code{create.communities} adds them to the \code{communtiies} object it creates. The other functions silently ignore them.} \item{membership}{Numeric vector, one value for each vertex, the membership vector of the community structure.} \item{algorithm}{If not \code{NULL} (meaning an unknown algorithm), then a character scalar, the name of the algorithm that produced the community structure.} \item{merges}{If not \code{NULL}, then the merge matrix of the hierarchical community structure. See \code{merges} below for more information on its format.} \item{modularity}{Numeric scalar or vector, the modularity value of the community structure. It can also be \code{NULL}, if the modularity of the (best) split is not available.} } \details{ Community structure detection algorithms try to find dense subgraphs in directed or undirected graphs, by optimizing some criteria, and usually using heuristics. igraph implements a number of commmunity detection methods (see them below), all of which return an object of the class \code{communities}. Because the community structure detection algorithms are different, \code{communities} objects do not always have the same structure. Nevertheless, they have some common operations, these are documented here. The \code{print} generic function is defined for \code{communities}, it prints a short summary. The \code{length} generic function call be called on \code{communities} and returns the number of communities. The \code{sizes} function returns the community sizes, in the order of their ids. \code{membership} gives the division of the vertices, into communities. It returns a numeric vector, one value for each vertex, the id of its community. Community ids start from one. Note that some algorithms calculate the complete (or incomplete) hierarchical structure of the communities, and not just a single partitioning. For these algorithms typically the membership for the highest modularity value is returned, but see also the manual pages of the individual algorithms. \code{modularity} gives the modularity score of the partitioning. (See \code{\link{modularity.igraph}} for details. For algorithms that do not result a single partitioning, the highest modularity value is returned. \code{algorithm} gives the name of the algorithm that was used to calculate the community structure. \code{crossing} returns a logical vector, with one value for each edge, ordered according to the edge ids. The value is \code{TRUE} iff the edge connects two different communities, according to the (best) membership vector, as returned by \code{membership()}. \code{is.hierarchical} checks whether a hierarchical algorithm was used to find the community structure. Some functions only make sense for hierarchical methods (e.g. \code{merges}, \code{cutat} and \code{as.dendrogram}). \code{merges} returns the merge matrix for hierarchical methods. An error message is given, if a non-hierarchical method was used to find the community structure. You can check this by calling \code{is.hierarchical} on the \code{communities} object. \code{cutat} cuts the merge tree of a hierarchical community finding method, at the desired place and returns a membership vector. The desired place can be expressed as the desired number of communities or as the number of merge steps to make. The function gives an error message, if called with a non-hierarchical method. \code{as.dendrogram} converts a hierarchical community structure to a \code{dendrogram} object. It only works for hierarchical methods, and gives an error message to others. See \code{\link[stats]{dendrogram}} for details. \code{as.hclust} is similar to \code{as.dendrogram}, but converts a hierarchical community structure to a \code{hclust} object. \code{asPhylo} converts a hierarchical community structure to a \code{phylo} object, you will need the \code{ape} package for this. \code{showtrace} works (currently) only for communities found by the leading eigenvector method (\code{\link{leading.eigenvector.community}}), and returns a character vector that gives the steps performed by the algorithm while finding the communities. \code{code.length} is defined for the InfoMAP method (\code{\link{infomap.community}} and returns the code length of the partition. It is possibly to call the \code{plot} function on \code{communities} objects. This will plot the graph (and uses \code{\link{plot.igraph}} internally), with the communities shown. By default it colores the vertices according to their communities, and also marks the vertex groups corresponding to the communities. It passes additional arguments to \code{\link{plot.igraph}}, please see that and also \code{\link{igraph.plotting}} on how to change the plot. \code{create.communities} creates a \code{communities} object. This is useful to integrate the results of community finding algorithms (that are not included in igraph). } \value{ \code{print} returns the \code{communities} object itself, invisibly. \code{length} returns an integer scalar. \code{sizes} returns a numeric vector. \code{membership} returns a numeric vector, one number for each vertex in the graph that was the input of the community detection. \code{modularity} returns a numeric scalar. \code{algorithm} returns a character scalar. \code{crossing} returns a logical vector. \code{is.hierarchical} returns a logical scalar. \code{merges} returns a two-column numeric matrix. \code{cutat} returns a numeric vector, the membership vector of the vertices. \code{as.dendrogram} returns a \code{\link[stats]{dendrogram}} object. \code{showtrace} returns a character vector. \code{code.length} returns a numeric scalar for communities found with the InfoMAP method and \code{NULL} for other methods. \code{plot} for \code{communities} objects returns \code{NULL}, invisibly. \code{create.communities} returns a \code{communities} object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ See \code{\link{dendPlot}} for plotting community structure dendrograms. See \code{\link{compare.communities}} for comparing two community structures on the same graph. The different methods for finding communities, they all return a \code{communities} object: \code{\link{edge.betweenness.community}}, \code{\link{fastgreedy.community}}, \code{\link{label.propagation.community}}, \code{\link{leading.eigenvector.community}}, \code{\link{multilevel.community}}, \code{\link{optimal.community}}, \code{\link{spinglass.community}}, \code{\link{walktrap.community}}. } \examples{ karate <- graph.famous("Zachary") wc <- walktrap.community(karate) modularity(wc) membership(wc) plot(wc, karate) } \keyword{graphs} igraph/man/contract.vertices.Rd0000644000176000001440000000270312240234657016265 0ustar ripleyusers\name{contract.vertices} \alias{contract.vertices} \title{Contract several vertices into a single one} \description{ This function creates a new graph, by merging several vertices into one. The vertices in the new graph correspond to sets of vertices in the input graph. } \usage{ contract.vertices(graph, mapping, vertex.attr.comb=getIgraphOpt("vertex.attr.comb")) } \arguments{ \item{graph}{The input graph, it can be directed or undirected.} \item{mapping}{A numeric vector that specifies the mapping. Its elements correspond to the vertices, and for each element the id in the new graph is given.} \item{vertex.attr.comb}{Specifies how to combine the vertex attributes in the new graph. Please see \code{\link{attribute.combination}} for details. } } \details{ The attributes of the graph are kept. Graph and edge attributes are unchanged, vertex attributes are combined, according to the \code{vertex.attr.comb} parameter. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.ring(10) g$name <- "Ring" V(g)$name <- letters[1:vcount(g)] E(g)$weight <- runif(ecount(g)) g2 <- contract.vertices(g, rep(1:5, each=2), vertex.attr.comb=toString) ## graph and edge attributes are kept, vertex attributes are ## combined using the 'toString' function. print(g2, g=TRUE, v=TRUE, e=TRUE) } \keyword{graphs} igraph/man/attributes.Rd0000644000176000001440000001544712251656216015025 0ustar ripleyusers\name{attributes} \alias{attributes} \alias{set.graph.attribute} \alias{get.graph.attribute} \alias{remove.graph.attribute} \alias{list.graph.attributes} \alias{graph.attributes} \alias{graph.attributes<-} \alias{set.vertex.attribute} \alias{get.vertex.attribute} \alias{remove.vertex.attribute} \alias{list.vertex.attributes} \alias{vertex.attributes} \alias{vertex.attributes<-} \alias{set.edge.attribute} \alias{get.edge.attribute} \alias{remove.edge.attribute} \alias{list.edge.attributes} \alias{edge.attributes} \alias{edge.attributes<-} \concept{Vertex/edge/graph attributes} \title{Graph, vertex and edge attributes} \description{Attributes are associated values belonging to a graph, vertices or edges. These can represent some property, like data about how the graph was constructed, the color of the vertices when the graph is plotted, or simply the weights of the edges in a weighted graph.} \usage{ get.graph.attribute(graph, name) set.graph.attribute(graph, name, value) list.graph.attributes(graph) graph.attributes(graph) remove.graph.attribute(graph, name) get.vertex.attribute(graph, name, index=V(graph)) set.vertex.attribute(graph, name, index=V(graph), value) remove.vertex.attribute(graph, name) list.vertex.attributes(graph) vertex.attributes(graph) get.edge.attribute(graph, name, index=E(graph)) set.edge.attribute(graph, name, index=E(graph), value) remove.edge.attribute(graph, name) list.edge.attributes(graph) edge.attributes(graph) } \arguments{ \item{graph}{The graph object to work on. Note that the original graph is never modified, a new graph object is returned instead; if you don't assign it to a variable your modifications will be lost! See examples below.} \item{name}{Character constant, the name of the attribute.} \item{index}{Numeric vector, the ids of the vertices or edges. It is not recycled, even if \code{value} is longer.} \item{value}{Numeric vector, the new value(s) of the attributes, it will be recycled if needed.} } \details{ There are three types of attributes in igraph: graph, vertex and edge attributes. Graph attributes are associated with graph, vertex attributes with vertices and edge attributes with edges. Examples for graph attributes are the date when the graph data was collected or other types of memos like the type of the data, or whether the graph is a simple graph, ie. one without loops and multiple edges. Examples of vertex attributes are vertex properties, like the vertex coordinates for the visualization of the graph, or other visualization parameters, or meta-data associated with the vertices, like the gender and the age of the individuals in a friendship network, the type of the neurons in a graph representing neural circuitry or even some pre-computed structual properties, like the betweenness centrality of the vertices. Examples of edge attributes are data associated with edges: most commonly edge weights, or visualization parameters. In recent igraph versions, arbitrary R objects can be assigned as graph, vertex or edge attributes. Some igraph functions use the values or graph, vertex and edge attributes if they are present but this is not done in the current version very extensively. Expect more in the (near) future. Graph attributes can be created with the \code{set.graph.attribute} function, and removed with \code{remove.graph.attribute}. Graph attributes are queried with \code{get.graph.attribute} and the assigned graph attributes are listed with \code{list.graph.attributes}. The function \code{graph.attributes} returns all graph attributes in a named list. This function has a counterpart that sets all graph attributes at once, see an example below. There is a simpler notation for using graph attributes: the \sQuote{\code{$}} operator. It can be used both to query and set graph attributes, see an example below. The functions for vertex attributes are \code{set.vertex.attribute}, \code{get.vertex.attribute}, \code{remove.vertex.attribute} and \code{list.vertex.attributes} and for edge attributes they are \code{set.edge.attribute}, \code{get.edge.attribute}, \code{remove.edge.attribute} and \code{list.edge.attributes}. Similarly to graph attributes, \code{vertex.attributes} returns all vertex attributes, in a named list, and \code{edge.attributes} returns all edge attributes, in a named list. There is however a (syntactically) much simpler way to handle vertex and edge attribute by using vertex and edge selectors, it works like this: \code{V(g)} selects all vertices in a graph, and \code{V(g)$name} queries the \code{name} attribute for all vertices. Similarly is \code{vs} is a vertex set \code{vs$name} gives the values of the \code{name} attribute for the vertices in the vertex set. This form can also be used to set the values of the attributes, like the regular R convention: \preformatted{V(g)$color <- "red"} It works for vertex subsets as well: \preformatted{V(g)[1:5]$color <- "green"} The notation for edges is similar: \code{E(g)} means all edges \code{E(g)$weight} is the \code{weight} attribute for all edges, etc. See also the manual page for \code{iterators} about how to create various vertex and edge sets. } \value{ \code{get.graph.attribute}, \code{get.vertex.attribute} and \code{get.edge.attribute} return an R object, or a list of R objects if attributes of more vertices/edges are requested. \code{set.graph.attribute}, \code{set.vertex.attribute}, \code{set.edge.attribute}, and also \code{remove.graph.attribute}, \code{remove.vertex.attribute} and \code{remove.edge.attribute} return a new graph object with the updates/removes performed. \code{list.graph.attributes}, \code{list.vertex.attributes} and \code{list.edge.attributes} return a character vector, the names of the attributes present. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{print.igraph}} can print attributes. See \code{\link{attribute.combination}} for details on how igraph combines attributes if several vertices or edges are mapped into one.} \examples{ g <- graph.ring(10) g <- set.graph.attribute(g, "name", "RING") # It is the same as g$name <- "RING" g$name g <- set.vertex.attribute(g, "color", value=c("red", "green")) get.vertex.attribute(g, "color") g <- set.edge.attribute(g, "weight", value=runif(ecount(g))) get.edge.attribute(g, "weight") # The following notation is more convenient g <- graph.star(10) V(g)$color <- c("red", "green") V(g)$color E(g)$weight <- runif(ecount(g)) E(g)$weight str(g, g=TRUE, v=TRUE, e=TRUE) # Setting all attributes at once g2 <- graph.empty(10) g2 graph.attributes(g2) <- graph.attributes(g) vertex.attributes(g2) <- vertex.attributes(g) edge.attributes(g2) <- list() g2 edge.attributes(g2) <- list(weight=numeric()) g2 } \keyword{graphs} igraph/man/graph.famous.Rd0000644000176000001440000001454312240234657015224 0ustar ripleyusers\encoding{UTF-8} \name{graph.famous} \alias{graph.famous} \title{Creating named graphs} \description{There are some famous, named graphs, sometimes counterexamples to some conjecture or unique graphs with given features. These can be created with this function} \usage{ graph.famous(name) } \arguments{ \item{name}{Character constant giving the name of the graph. It is case insensitive.} } \details{ \code{graph.famous} knows the following graphs: \describe{ \item{Bull}{The bull graph, 5 vertices, 5 edges, resembles to the head of a bull if drawn properly.} \item{Chvatal}{This is the smallest triangle-free graph that is both 4-chromatic and 4-regular. According to the Grunbaum conjecture there exists an m-regular, m-chromatic graph with n vertices for every m>1 and n>2. The Chvatal graph is an example for m=4 and n=12. It has 24 edges.} \item{Coxeter}{A non-Hamiltonian cubic symmetric graph with 28 vertices and 42 edges.} \item{Cubical}{The Platonic graph of the cube. A convex regular polyhedron with 8 vertices and 12 edges.} \item{Diamond}{A graph with 4 vertices and 5 edges, resembles to a schematic diamond if drawn properly.} \item{Dodecahedral, Dodecahedron}{Another Platonic solid with 20 vertices and 30 edges.} \item{Folkman}{The semisymmetric graph with minimum number of vertices, 20 and 40 edges. A semisymmetric graph is regular, edge transitive and not vertex transitive.} \item{Franklin}{This is a graph whose embedding to the Klein bottle can be colored with six colors, it is a counterexample to the neccessity of the Heawood conjecture on a Klein bottle. It has 12 vertices and 18 edges.} \item{Frucht}{The Frucht Graph is the smallest cubical graph whose automorphism group consists only of the identity element. It has 12 vertices and 18 edges.} \item{Grotzsch}{The Grötzsch graph is a triangle-free graph with 11 vertices, 20 edges, and chromatic number 4. It is named after German mathematician Herbert Grötzsch, and its existence demonstrates that the assumption of planarity is necessary in Grötzsch's theorem that every triangle-free planar graph is 3-colorable.} \item{Heawood}{The Heawood graph is an undirected graph with 14 vertices and 21 edges. The graph is cubic, and all cycles in the graph have six or more edges. Every smaller cubic graph has shorter cycles, so this graph is the 6-cage, the smallest cubic graph of girth 6.} \item{Herschel}{The Herschel graph is the smallest nonhamiltonian polyhedral graph. It is the unique such graph on 11 nodes, and has 18 edges.} \item{House}{The house graph is a 5-vertex, 6-edge graph, the schematic draw of a house if drawn properly, basicly a triangle of the top of a square.} \item{HouseX}{The same as the house graph with an X in the square. 5 vertices and 8 edges.} \item{Icosahedral, Icosahedron}{A Platonic solid with 12 vertices and 30 edges.} \item{Krackhardt\_Kite}{A social network with 10 vertices and 18 edges. Krackhardt, D. Assessing the Political Landscape: Structure, Cognition, and Power in Organizations. Admin. Sci. Quart. 35, 342-369, 1990.} \item{Levi}{The graph is a 4-arc transitive cubic graph, it has 30 vertices and 45 edges.} \item{McGee}{The McGee graph is the unique 3-regular 7-cage graph, it has 24 vertices and 36 edges.} \item{Meredith}{The Meredith graph is a quartic graph on 70 nodes and 140 edges that is a counterexample to the conjecture that every 4-regular 4-connected graph is Hamiltonian.} \item{Noperfectmatching}{A connected graph with 16 vertices and 27 edges containing no perfect matching. A matching in a graph is a set of pairwise non-adjacent edges; that is, no two edges share a common vertex. A perfect matching is a matching which covers all vertices of the graph.} \item{Nonline}{A graph whose connected components are the 9 graphs whose presence as a vertex-induced subgraph in a graph makes a nonline graph. It has 50 vertices and 72 edges.} \item{Octahedral, Octahedron}{Platonic solid with 6 vertices and 12 edges.} \item{Petersen}{A 3-regular graph with 10 vertices and 15 edges. It is the smallest hypohamiltonian graph, ie. it is non-hamiltonian but removing any single vertex from it makes it Hamiltonian.} \item{Robertson}{The unique (4,5)-cage graph, ie. a 4-regular graph of girth 5. It has 19 vertices and 38 edges.} \item{Smallestcyclicgroup}{A smallest nontrivial graph whose automorphism group is cyclic. It has 9 vertices and 15 edges.} \item{Tetrahedral, Tetrahedron}{Platonic solid with 4 vertices and 6 edges.} \item{Thomassen}{The smallest hypotraceable graph, on 34 vertices and 52 edges. A hypotracable graph does not contain a Hamiltonian path but after removing any single vertex from it the remainder always contains a Hamiltonian path. A graph containing a Hamiltonian path is called tracable.} \item{Tutte}{Tait's Hamiltonian graph conjecture states that every 3-connected 3-regular planar graph is Hamiltonian. This graph is a counterexample. It has 46 vertices and 69 edges.} \item{Uniquely3colorable}{Returns a 12-vertex, triangle-free graph with chromatic number 3 that is uniquely 3-colorable.} \item{Walther}{An identity graph with 25 vertices and 31 edges. An identity graph has a single graph automorphism, the trivial one.} \item{Zachary}{Social network of friendships between 34 members of a karate club at a US university in the 1970s. See W. W. Zachary, An information flow model for conflict and fission in small groups, Journal of Anthropological Research 33, 452-473 (1977). } } } \value{A graph object.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{graph}} can create arbitrary graphs, see also the other functions on the its manual page for creating special graphs. } \examples{ solids <- list(graph.famous("Tetrahedron"), graph.famous("Cubical"), graph.famous("Octahedron"), graph.famous("Dodecahedron"), graph.famous("Icosahedron")) } \keyword{graphs} igraph/man/graph.adjacency.Rd0000644000176000001440000001541312240234657015650 0ustar ripleyusers\name{graph.adjacency} \alias{graph.adjacency} \concept{Adjacency matrix} \concept{Sparse matrix} \title{Create graphs from adjacency matrices} \description{\code{graph.adjacency} is a flexible function for creating \code{igraph} graphs from adjacency matrices.} \usage{ graph.adjacency(adjmatrix, mode=c("directed", "undirected", "max", "min", "upper", "lower", "plus"), weighted=NULL, diag=TRUE, add.colnames=NULL, add.rownames=NA) } \arguments{ \item{adjmatrix}{A square adjacency matrix. From igraph version 0.5.1 this can be a sparse matrix created with the \code{Matrix} package.} \item{mode}{ Character scalar, specifies how igraph should interpret the supplied matrix. See also the \code{weighted} argument, the interpretation depends on that too. Possible values are: \code{directed}, \code{undirected}, \code{upper}, \code{lower}, \code{max}, \code{min}, \code{plus}. See details below. } \item{weighted}{This argument specifies whether to create a weighted graph from an adjacency matrix. If it is \code{NULL} then an unweighted graph is created and the elements of the adjacency matrix gives the number of edges between the vertices. If it is a character constant then for every non-zero matrix entry an edge is created and the value of the entry is added as an edge attribute named by the \code{weighted} argument. If it is \code{TRUE} then a weighted graph is created and the name of the edge attribute will be \code{weight}. See also details below. } \item{diag}{Logical scalar, whether to include the diagonal of the matrix in the calculation. If this is \code{FALSE} then the diagonal is zerod out first. } \item{add.colnames}{Character scalar, whether to add the column names as vertex attributes. If it is \sQuote{\code{NULL}} (the default) then, if present, column names are added as vertex attribute \sQuote{name}. If \sQuote{\code{NA}} then they will not be added. If a character constant, then it gives the name of the vertex attribute to add.} \item{add.rownames}{Character scalar, whether to add the row names as vertex attributes. Possible values the same as the previous argument. By default row names are not added. If \sQuote{\code{add.rownames}} and \sQuote{\code{add.colnames}} specify the same vertex attribute, then the former is ignored. } } \details{ \code{graph.adjacency} creates a graph from an adjacency matrix. The order of the vertices are preserved, i.e. the vertex corresponding to the first row will be vertex 0 in the graph, etc. \code{graph.adjacency} operates in two main modes, depending on the \code{weighted} argument. If this argument is \code{NULL} then an unweighted graph is created and an element of the adjacency matrix gives the number of edges to create between the two corresponding vertices. The details depend on the value of the \code{mode} argument: \describe{ \item{\code{directed}}{The graph will be directed and a matrix element gives the number of edges between two vertices.} \item{\code{undirected}}{This is exactly the same as \code{max}, for convenience. Note that it is \emph{not} checked whether the matrix is symmetric.} \item{\code{max}}{An undirected graph will be created and \code{max(A(i,j), A(j,i))} gives the number of edges.} \item{\code{upper}}{An undirected graph will be created, only the upper right triangle (including the diagonal) is used for the number of edges.} \item{\code{lower}}{An undirected graph will be created, only the lower left triangle (including the diagonal) is used for creating the edges.} \item{\code{min}}{undirected graph will be created with \code{min(A(i,j), A(j,i))} edges between vertex \code{i} and \code{j}.} \item{\code{plus}}{ undirected graph will be created with \code{A(i,j)+A(j,i)} edges between vertex \code{i} and \code{j}.} } If the \code{weighted} argument is not \code{NULL} then the elements of the matrix give the weights of the edges (if they are not zero). The details depend on the value of the \code{mode} argument: \describe{ \item{\code{directed}}{The graph will be directed and a matrix element gives the edge weights.} \item{\code{undirected}}{First we check that the matrix is symmetric. It is an error if not. Then only the upper triangle is used to create a weighted undirected graph.} \item{\code{max}}{An undirected graph will be created and \code{max(A(i,j), A(j,i))} gives the edge weights.} \item{\code{upper}}{An undirected graph will be created, only the upper right triangle (including the diagonal) is used (for the edge weights).} \item{\code{lower}}{An undirected graph will be created, only the lower left triangle (including the diagonal) is used for creating the edges.} \item{\code{min}}{An undirected graph will be created, \code{min(A(i,j), A(j,i))} gives the edge weights.} \item{\code{plus}}{An undirected graph will be created, \code{A(i,j)+A(j,i)} gives the edge weights.} } } \value{ An igraph graph object. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\link{graph} and \code{\link{graph.formula}} for other ways to create graphs. } \examples{ adjm <- matrix(sample(0:1, 100, replace=TRUE, prob=c(0.9,0.1)), nc=10) g1 <- graph.adjacency( adjm ) adjm <- matrix(sample(0:5, 100, replace=TRUE, prob=c(0.9,0.02,0.02,0.02,0.02,0.02)), nc=10) g2 <- graph.adjacency(adjm, weighted=TRUE) E(g2)$weight ## various modes for weighted graphs, with some tests nzs <- function(x) sort(x [x!=0]) adjm <- matrix(runif(100), 10) adjm[ adjm<0.5 ] <- 0 g3 <- graph.adjacency((adjm + t(adjm))/2, weighted=TRUE, mode="undirected") g4 <- graph.adjacency(adjm, weighted=TRUE, mode="max") all(nzs(pmax(adjm, t(adjm))[upper.tri(adjm)]) == sort(E(g4)$weight)) g5 <- graph.adjacency(adjm, weighted=TRUE, mode="min") all(nzs(pmin(adjm, t(adjm))[upper.tri(adjm)]) == sort(E(g5)$weight)) g6 <- graph.adjacency(adjm, weighted=TRUE, mode="upper") all(nzs(adjm[upper.tri(adjm)]) == sort(E(g6)$weight)) g7 <- graph.adjacency(adjm, weighted=TRUE, mode="lower") all(nzs(adjm[lower.tri(adjm)]) == sort(E(g7)$weight)) g8 <- graph.adjacency(adjm, weighted=TRUE, mode="plus") d2 <- function(x) { diag(x) <- diag(x)/2; x } all(nzs((d2(adjm+t(adjm)))[lower.tri(adjm)]) == sort(E(g8)$weight)) g9 <- graph.adjacency(adjm, weighted=TRUE, mode="plus", diag=FALSE) d0 <- function(x) { diag(x) <- 0 } all(nzs((d0(adjm+t(adjm)))[lower.tri(adjm)]) == sort(E(g9)$weight)) ## row/column names rownames(adjm) <- sample(letters, nrow(adjm)) colnames(adjm) <- seq(ncol(adjm)) g10 <- graph.adjacency(adjm, weighted=TRUE, add.rownames="code") summary(g10) } \keyword{graphs} igraph/man/ba.game.Rd0000644000176000001440000001243212240234657014117 0ustar ripleyusers\name{barabasi.game} \alias{barabasi.game} \alias{ba.game} \concept{Preferential attachment model} \concept{Random graph model} \title{Generate scale-free graphs according to the Barabasi-Albert model} \description{The BA-model is a very simple stochastic algorithm for building a graph.} \usage{ barabasi.game(n, power = 1, m = NULL, out.dist = NULL, out.seq = NULL, out.pref = FALSE, zero.appeal = 1, directed = TRUE, algorithm = c("psumtree", "psumtree-multiple", "bag"), start.graph = NULL) } \arguments{ \item{n}{Number of vertices.} \item{power}{The power of the preferential attachment, the default is one, ie. linear preferential attachment.} \item{m}{Numeric constant, the number of edges to add in each time step This argument is only used if both \code{out.dist} and \code{out.seq} are omitted or NULL.} \item{out.dist}{Numeric vector, the distribution of the number of edges to add in each time step. This argument is only used if the \code{out.seq} argument is omitted or NULL.} \item{out.seq}{Numeric vector giving the number of edges to add in each time step. Its first element is ignored as no edges are added in the first time step.} \item{out.pref}{Logical, if true the total degree is used for calculating the citation probability, otherwise the in-degree is used. } \item{zero.appeal}{The \sQuote{attractiveness} of the vertices with no adjacent edges. See details below.} \item{directed}{Whether to create a directed graph.} \item{algorithm}{The algorithm to use for the graph generation. \code{psumtree} uses a partial prefix-sum tree to generate the graph, this algorithm can handle any \code{power} and \code{zero.appeal} values and never generates multiple edges. \code{psumtree-multiple} also uses a partial prefix-sum tree, but the generation of multiple edges is allowed. Before the 0.6 version igraph used this algorithm if \code{power} was not one, or \code{zero.appeal} was not one. \code{bag} is the algorithm that was previously (before version 0.6) used if \code{power} was one and \code{zero.appeal} was one as well. It works by putting the ids of the vertices into a bag (mutliset, really), exactly as many times as their (in-)degree, plus once more. Then the required number of cited vertices are drawn from the bag, with replacement. This method might generate multiple edges. It only works if \code{power} and \code{zero.appeal} are equal one. } \item{start.graph}{\code{NULL} or an igraph graph. If a graph, then the supplied graph is used as a starting graph for the preferential attachment algorithm. The graph should have at least one vertex. If a graph is supplied here and the \code{out.seq} argument is not \code{NULL}, then it should contain the out degrees of the new vertices only, not the ones in the \code{start.graph}.} } \details{ This is a simple stochastic algorithm to generate a graph. It is a discrete time step model and in each time step a single vertex is added. We start with a single vertex and no edges in the first time step. Then we add one vertex in each time step and the new vertex initiates some edges to old vertices. The probability that an old vertex is chosen is given by \deqn{P[i] \sim k_i^\alpha+a}{P[i] ~ k[i]^alpha + a} where \eqn{k_i}{k[i]} is the in-degree of vertex \eqn{i} in the current time step (more precisely the number of adjacent edges of \eqn{i} which were not initiated by \eqn{i} itself) and \eqn{\alpha}{alpha} and \eqn{a} are parameters given by the \code{power} and \code{zero.appeal} arguments. The number of edges initiated in a time step is given by the \code{m}, \code{out.dist} and \code{out.seq} arguments. If \code{out.seq} is given and not NULL then it gives the number of edges to add in a vector, the first element is ignored, the second is the number of edges to add in the second time step and so on. If \code{out.seq} is not given or null and \code{out.dist} is given and not NULL then it is used as a discrete distribution to generate the number of edges in each time step. Its first element is the probability that no edges will be added, the second is the probability that one edge is added, etc. (\code{out.dist} does not need to sum up to one, it normalized automatically.) \code{out.dist} should contain non-negative numbers and at east one element should be positive. If both \code{out.seq} and \code{out.dist} are omitted or NULL then \code{m} will be used, it should be a positive integer constant and \code{m} edges will be added in each time step. \code{barabasi.game} generates a directed graph by default, set \code{directed} to \code{FALSE} to generate an undirected graph. Note that even if an undirected graph is generated \eqn{k_i}{k[i]} denotes the number of adjacent edges not initiated by the vertex itself and not the total (in- + out-) degree of the vertex, unless the \code{out.pref} argument is set to \code{TRUE}. } \value{A graph object.} \references{ Barabasi, A.-L. and Albert R. 1999. Emergence of scaling in random networks \emph{Science}, 286 509--512.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{random.graph.game}}} \examples{ g <- barabasi.game(10000) degree.distribution(g) } \keyword{graphs} igraph/man/graph.eigen.Rd0000644000176000001440000000621712251656216015021 0ustar ripleyusers\name{graph.eigen} \alias{graph.eigen} \alias{igraph.eigen.default} \concept{Eigenvalues} \concept{Eigenvectors} \title{Eigenvalues and eigenvectors of the adjacency matrix of a graph} \description{ Calculate selected eigenvalues and eigenvectors of a (supposedly sparse) graph. } \usage{ graph.eigen (graph, algorithm = c("arpack", "auto", "lapack", "comp_auto", "comp_lapack", "comp_arpack"), which = list(), options = igraph.arpack.default) igraph.eigen.default } \arguments{ \item{graph}{The input graph, can be directed or undirected.} \item{algorithm}{The algorithm to use. Currently only \code{arpack} is implemented, which uses the ARPACK solver. See also \code{\link{arpack}}.} \item{which}{A list to specify which eigenvalues and eigenvectors to calculate. By default the leading (i.e. largest magnitude) eigenvalue and the corresponding eigenvector is calculated.} \item{options}{Options for the ARPACK solver. See \code{\link{igraph.arpack.default}}.} } \details{ The \code{which} argument is a list and it specifies which eigenvalues and corresponding eigenvectors to calculate: There are eight options: \enumerate{ \item Eigenvalues with the largest magnitude. Set \code{pos} to \code{LM}, and \code{howmany} to the number of eigenvalues you want. \item Eigenvalues with the smallest magnitude. Set \code{pos} to \code{SM} and \code{howmany} to the number of eigenvalues you want. \item Largest eigenvalues. Set \code{pos} to \code{LA} and \code{howmany} to the number of eigenvalues you want. \item Smallest eigenvalues. Set \code{pos} to \code{SA} and \code{howmany} to the number of eigenvalues you want. \item Eigenvalues from both ends of the spectrum. Set \code{pos} to \code{BE} and \code{howmany} to the number of eigenvalues you want. If \code{howmany} is odd, then one more eigenvalue is returned from the larger end. \item Selected eigenvalues. This is not (yet) implemented currently. \item Eigenvalues in an interval. This is not (yet) implemented. \item All eigenvalues. This is not implemented yet. The standard \code{eigen} function does a better job at this, anyway. } Note that ARPACK might be unstable for graphs with multiple components, e.g. graphs with isolate vertices. } \value{ Depends on the algorithm used. For \code{arpack} a list with three entries is returned: \item{options}{See the return value for \code{arpack} for a complete description.} \item{values}{Numeric vector, the eigenvalues.} \item{vectors}{Numeric matrix, with the eigenvectors as columns.} } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{\code{\link{get.adjacency}} to create a (sparse) adjacency matrix.} \examples{ ## Small example graph, leading eigenvector by default kite <- graph.famous("Krackhardt_kite") graph.eigen(kite)[c("values", "vectors")] ## Double check eigen(get.adjacency(kite, sparse=FALSE))$vectors[,1] ## Should be the same as 'evcent' (but rescaled) cor(evcent(kite)$vector, graph.eigen(kite)$vectors) ## Smallest eigenvalues graph.eigen(kite, which=list(pos="SM", howmany=2))$values } \keyword{graphs} igraph/man/graph.maxflow.Rd0000644000176000001440000001345212251656216015406 0ustar ripleyusers\name{graph.maxflow} \alias{graph.maxflow} \alias{graph.mincut} \concept{Maximum flow} \concept{Minimum cut} \title{Maximum flow in a network} \description{In a graph where each edge has a given flow capacity the maximal flow between two vertices is calculated.} \usage{ graph.maxflow(graph, source, target, capacity=NULL) graph.mincut(graph, source=NULL, target=NULL, capacity=NULL, value.only = TRUE) } \arguments{ \item{graph}{The input graph.} \item{source}{The id of the source vertex.} \item{target}{The id of the target vertex (sometimes also called sink).} \item{capacity}{Vector giving the capacity of the edges. If this is \code{NULL} (the default) then the \code{capacity} edge attribute is used.} \item{value.only}{Logical scalar, if \code{TRUE} only the minumum cut value is returned, if \code{FALSE} the edges in the cut and a the two (or more) partitions are also returned. } } \details{ \code{graph.maxflow} calculates the maximum flow between two vertices in a weighted (ie. valued) graph. A flow from \code{source} to \code{target} is an assignment of non-negative real numbers to the edges of the graph, satisfying two properties: (1) for each edge the flow (ie. the assigned number) is not more than the capacity of the edge (the \code{capacity} parameter or edge attribute), (2) for every vertex, except the source and the target the incoming flow is the same as the outgoing flow. The value of the flow is the incoming flow of the \code{target} vertex. The maximum flow is the flow of maximum value. \code{graph.mincut} calculates the minimum st-cut between two vertices in a graph (if the \code{source} and \code{target} arguments are given) or the minimum cut of the graph (if both \code{source} and \code{target} are \code{NULL}). The minimum st-cut between \code{source} and \code{target} is the minimum total weight of edges needed to remove to eliminate all paths from \code{source} to \code{target}. The minimum cut of a graph is the minimum total weight of the edges needed to remove to separate the graph into (at least) two components. (Which is to make the graph \emph{not} strongly connected in the directed case.) The maximum flow between two vertices in a graph is the same as the minimum st-cut, so \code{graph.maxflow} and \code{graph.mincut} essentially calculate the same quantity, the only difference is that \code{graph.mincut} can be invoked without giving the \code{source} and \code{target} arguments and then minimum of all possible minimum cuts is calculated. For undirected graphs the Stoer-Wagner algorithm (see reference below) is used to calculate the minimum cut. } \value{ For \code{graph.maxflow} a named list with components: \item{value}{A numeric scalar, the value of the maximum flow.} \item{flow}{A numeric vector, the flow itself, one entry for each edge. For undirected graphs this entry is bit trickier, since for these the flow direction is not predetermined by the edge direction. For these graphs the elements of the this vector can be negative, this means that the flow goes from the bigger vertex id to the smaller one. Positive values mean that the flow goes from the smaller vertex id to the bigger one.} \item{cut}{A numeric vector of edge ids, the minimum cut corresponding to the maximum flow.} \item{partition1}{A numeric vector of vertex ids, the vertices in the first partition of the minimum cut corresponding to the maximum flow.} \item{partition2}{A numeric vector of vertex ids, the vertices in the second partition of the minimum cut corresponding to the maximum flow.} \item{stats}{A list with some statistics from the push-relabel algorithm. Five integer values currently: \code{nopush} is the number of push operations, \code{norelabel} the number of relabelings, \code{nogap} is the number of times the gap heuristics was used, \code{nogapnodes} is the total number of gap nodes omitted because of the gap heuristics and \code{nobfs} is the number of times a global breadth-first-search update was performed to assign better height (=distance) values to the vertices.} For \code{graph.mincut} a numeric constant, the value of the minimum cut, except if \code{value.only=FALSE}. In this case a named list with components: \item{value}{Numeric scalar, the cut value.} \item{cut}{Numeric vector, the edges in the cut.} \item{partition1}{The vertices in the first partition after the cut edges are removed. Note that these vertices might be actually in different components (after the cut edges are removed), as the graph may fall apart into more than two components.} \item{partition2}{The vertices in the second partition after the cut edges are removed. Note that these vertices might be actually in different components (after the cut edges are removed), as the graph may fall apart into more than two components.} } \references{ A. V. Goldberg and R. E. Tarjan: A New Approach to the Maximum Flow Problem \emph{Journal of the ACM} 35:921-940, 1988. M. Stoer and F. Wagner: A simple min-cut algorithm, \emph{Journal of the ACM}, 44 585-591, 1997. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{shortest.paths}}, \code{\link{edge.connectivity}}, \code{\link{vertex.connectivity}}} \examples{ E <- rbind( c(1,3,3), c(3,4,1), c(4,2,2), c(1,5,1), c(5,6,2), c(6,2,10)) colnames(E) <- c("from", "to", "capacity") g1 <- graph.data.frame(as.data.frame(E)) graph.maxflow(g1, source=V(g1)["1"], target=V(g1)["2"]) g <- graph.ring(100) graph.mincut(g, capacity=rep(1,vcount(g))) graph.mincut(g, value.only=FALSE, capacity=rep(1,vcount(g))) g2 <- graph( c(1,2,2,3,3,4, 1,6,6,5,5,4, 4,1) ) E(g2)$capacity <- c(3,1,2, 10,1,3, 2) graph.mincut(g2, value.only=FALSE) } \keyword{graphs} igraph/man/evcent.Rd0000644000176000001440000000705212263023733014107 0ustar ripleyusers\name{evcent} \alias{evcent} \concept{Eigenvector centrality} \title{Find Eigenvector Centrality Scores of Network Positions} \description{ \code{evcent} takes a graph (\code{graph}) and returns the eigenvector centralities of positions \code{v} within it } \usage{ evcent (graph, directed = FALSE, scale = TRUE, weights = NULL, options = igraph.arpack.default) } \arguments{ \item{graph}{Graph to be analyzed.} \item{directed}{Logical scalar, whether to consider direction of the edges in directed graphs. It is ignored for undirected graphs.} \item{scale}{Logical scalar, whether to scale the result to have a maximum score of one. If no scaling is used then the result vector has unit length in the Euclidean norm.} \item{weights}{A numerical vector or \code{NULL}. This argument can be used to give edge weights for calculating the weighted eigenvector centrality of vertices. If this is \code{NULL} and the graph has a \code{weight} edge attribute then that is used. If \code{weights} is a numerical vector then it used, even if the graph has a \code{weights} edge attribute. If this is \code{NA}, then no edge weights are used (even if the graph has a \code{weight} edge attribute. Note that if there are negative edge weights and the direction of the edges is considered, then the eigenvector might be complex. In this case only the real part is reported.} \item{options}{A named list, to override some ARPACK options. See \code{\link{arpack}} for details.} } \details{ Eigenvector centrality scores correspond to the values of the first eigenvector of the graph adjacency matrix; these scores may, in turn, be interpreted as arising from a reciprocal process in which the centrality of each actor is proportional to the sum of the centralities of those actors to whom he or she is connected. In general, vertices with high eigenvector centralities are those which are connected to many other vertices which are, in turn, connected to many others (and so on). (The perceptive may realize that this implies that the largest values will be obtained by individuals in large cliques (or high-density substructures). This is also intelligible from an algebraic point of view, with the first eigenvector being closely related to the best rank-1 approximation of the adjacency matrix (a relationship which is easy to see in the special case of a diagonalizable symmetric real matrix via the \eqn{SLS^-1}{$S \Lambda S^{-1}$} decomposition).) From igraph version 0.5 this function uses ARPACK for the underlying computation, see \code{\link{arpack}} for more about ARPACK in igraph. } \value{ A named list with components: \item{vector}{A vector containing the centrality scores.} \item{value}{The eigenvalue corresponding to the calculated eigenvector, i.e. the centrality scores.} \item{options}{A named list, information about the underlying ARPACK computation. See \code{\link{arpack}} for the details. } } \references{ Bonacich, P. (1987). Power and Centrality: A Family of Measures. \emph{American Journal of Sociology}, 92, 1170-1182. } \author{ Gabor Csardi \email{csardi.gabor@gmail.com} and Carter T. Butts (\url{http://www.faculty.uci.edu/profile.cfm?faculty_id=5057}) for the manual page.} \section{WARNING }{\code{evcent} will not symmetrize your data before extracting eigenvectors; don't send this routine asymmetric matrices unless you really mean to do so.} % \seealso{} \examples{ #Generate some test data g <- graph.ring(10, directed=FALSE) #Compute eigenvector centrality scores evcent(g) } \keyword{graphs} igraph/man/spinglass.community.Rd0000644000176000001440000001577312251656216016667 0ustar ripleyusers\name{spinglass.community} \alias{spinglass.community} \concept{Statistical mechanics} \concept{Spin-glass} \concept{Community structure} \title{Finding communities in graphs based on statistical meachanics} \description{This function tries to find communities in graphs via a spin-glass model and simulated annealing.} \usage{ spinglass.community(graph, weights=NULL, vertex=NULL, spins=25, parupdate=FALSE, start.temp=1, stop.temp=0.01, cool.fact=0.99, update.rule=c("config", "random", "simple"), gamma=1, implementation=c("orig", "neg"), gamma.minus=1) } \arguments{ \item{graph}{The input graph, can be directed but the direction of the edges is neglected.} \item{weights}{The weights of the edges. Either a numeric vector or \code{NULL}. If it is null and the input graph has a \sQuote{weight} edge attribute then that will be used. If \code{NULL} and no such attribute is present then the edges will have equal weights. Set this to \code{NA} if the graph was a \sQuote{weight} edge attribute, but you don't want to use it for community detection.} \item{vertex}{This parameter can be used to calculate the community of a given vertex without calculating all communities. Note that if this argument is present then some other arguments are ignored.} \item{spins}{Integer constant, the number of spins to use. This is the upper limit for the number of communities. It is not a problem to supply a (reasonably) big number here, in which case some spin states will be unpopulated. } \item{parupdate}{Logical constant, whether to update the spins of the vertices in parallel (synchronously) or not. This argument is ignored if the second form of the function is used (ie. the \sQuote{\code{vertex}} argument is present). It is also not implemented in the \dQuote{neg} implementation.} \item{start.temp}{Real constant, the start temperature. This argument is ignored if the second form of the function is used (ie. the \sQuote{\code{vertex}} argument is present). } \item{stop.temp}{Real constant, the stop temperature. The simulation terminates if the temperature lowers below this level. This argument is ignored if the second form of the function is used (ie. the \sQuote{\code{vertex}} argument is present). } \item{cool.fact}{Cooling factor for the simulated annealing. This argument is ignored if the second form of the function is used (ie. the \sQuote{\code{vertex}} argument is present). } \item{update.rule}{Character constant giving the \sQuote{null-model} of the simulation. Possible values: \dQuote{simple} and \dQuote{config}. \dQuote{simple} uses a random graph with the same number of edges as the baseline probability and \dQuote{config} uses a random graph with the same vertex degrees as the input graph.} \item{gamma}{Real constant, the gamma argument of the algorithm. This specifies the balance between the importance of present and non-present edges in a community. Roughly, a comunity is a set of vertices having many edges inside the community and few edges outside the community. The default 1.0 value makes existing and non-existing links equally important. Smaller values make the existing links, greater values the missing links more important.} \item{implementation}{Character scalar. Currently igraph contains two implementations for the Spin-glass community finding algorithm. The faster original implementation is the default. The other implementation, that takes into account negative weights, can be chosen by supplying \sQuote{neg} here.} \item{gamma.minus}{Real constant, the gamma.minus parameter of the algorithm. This specifies the balance between the importance of present and non-present negative weighted edges in a community. Smaller values of gamma.minus, leads to communities with lesser negative intra-connectivity. If this argument is set to zero, the algorithm reduces to a graph coloring algorithm, using the number of spins as the number of colors. This argument is ignored if the \sQuote{orig} implementation is chosen.} } \details{ This function tries to find communities in a graph. A community is a set of nodes with many edges inside the community and few edges between outside it (i.e. between the community itself and the rest of the graph.) This idea is reversed for edges having a negative weight, ie. few negative edges inside a community and many negative edges between communities. Note that only the \sQuote{neg} implementation supports negative edge weights. The \code{spinglass.cummunity} function can solve two problems related to community detection. If the \code{vertex} argument is not given (or it is \code{NULL}), then the regular community detection problem is solved (approximately), i.e. partitioning the vertices into communities, by optimizing the an energy function. If the \code{vertex} argument is given and it is not \code{NULL}, then it must be a vertex id, and the same energy function is used to find the community of the the given vertex. See also the examples below. } \value{ If the \code{vertex} argument is not given, ie. the first form is used then a \code{\link{spinglass.community}} returns a \code{\link{communities}} object. If the \code{vertex} argument is present, ie. the second form is used then a named list is returned with the following components: \item{community}{Numeric vector giving the ids of the vertices in the same community as \code{vertex}.} \item{cohesion}{The cohesion score of the result, see references.} \item{adhesion}{The adhesion score of the result, see references.} \item{inner.links}{The number of edges within the community of \code{vertex}.} \item{outer.links}{The number of edges between the community of \code{vertex} and the rest of the graph. } } \references{ J. Reichardt and S. Bornholdt: Statistical Mechanics of Community Detection, \emph{Phys. Rev. E}, 74, 016110 (2006), \url{http://arxiv.org/abs/cond-mat/0603718} M. E. J. Newman and M. Girvan: Finding and evaluating community structure in networks, \emph{Phys. Rev. E} 69, 026113 (2004) V.A. Traag and Jeroen Bruggeman: Community detection in networks with positive and negative links, \url{http://arxiv.org/abs/0811.2329} (2008). } \author{Jorg Reichardt (\url{http://theorie.physik.uni-wuerzburg.de/~reichardt/}) for the original code and Gabor Csardi \email{csardi.gabor@gmail.com} for the igraph glue code. Changes to the original function for including the possibility of negative ties were implemented by Vincent Traag (\url{http://www.traag.net/}).} \seealso{\code{\link{communities}}, \code{\link{clusters}}} \examples{ g <- erdos.renyi.game(10, 5/10) \%du\% erdos.renyi.game(9, 5/9) g <- add.edges(g, c(1, 12)) g <- induced.subgraph(g, subcomponent(g, 1)) spinglass.community(g, spins=2) spinglass.community(g, vertex=1) } \keyword{graphs} igraph/man/constraint.Rd0000644000176000001440000000356612251656216015022 0ustar ripleyusers\name{constraint} \alias{constraint} \concept{Burt's constraint} \title{Burt's constraint} \description{Given a graph, \code{constraint} calculates Burt's constraint for each vertex. } \usage{ constraint(graph, nodes=V(graph), weights=NULL) } \arguments{ \item{graph}{A graph object, the input graph.} \item{nodes}{The vertices for which the constraint will be calculated. Defaults to all vertices.} \item{weights}{The weights of the edges. If this is \code{NULL} and there is a \code{weight} edge attribute this is used. If there is no such edge attribute all edges will have the same weight.} } \details{Burt's constraint is higher if ego has less, or mutually stronger related (i.e. more redundant) contacts. Burt's measure of constraint, \eqn{C_i}{C[i]}, of vertex \eqn{i}'s ego network \eqn{V_i}{V[i]}, is defined for directed and valued graphs, \deqn{C_i=\sum_{j \in V_i \setminus \{i\}} (p_{ij}+\sum_{q \in V_i \setminus \{i,j\}} p_{iq} p_{qj})^2}{% C[i] = sum( [sum( p[i,j] + p[i,q] p[q,j], q in V[i], q != i,j )]^2, j in V[i], j != i). } for a graph of order (ie. number of vertices) \eqn{N}, where proportional tie strengths are defined as \deqn{p_{ij} = \frac{a_{ij}+a_{ji}}{\sum_{k \in V_i \setminus \{i\}}(a_{ik}+a_{ki})},}{% p[i,j]=(a[i,j]+a[j,i]) / sum(a[i,k]+a[k,i], k in V[i], k != i), } \eqn{a_{ij}}{a[i,j]} are elements of \eqn{A} and the latter being the graph adjacency matrix. For isolated vertices, constraint is undefined. } \value{A numeric vector of constraint scores} \author{Jeroen Bruggeman (\url{https://sites.google.com/site/jebrug/jeroen-bruggeman-social-science}) and Gabor Csardi \email{csardi.gabor@gmail.com} } \references{Burt, R.S. (2004). Structural holes and good ideas. \emph{American Journal of Sociology} 110, 349-399. } \examples{ g <- erdos.renyi.game(20, 5/20) constraint(g) } \keyword{graphs} igraph/man/assortativity.Rd0000644000176000001440000000774112240234657015561 0ustar ripleyusers\name{assortativity} \alias{assortativity} \alias{assortativity.degree} \alias{assortativity.nominal} \concept{Assortativity coefficient} \title{Assortativity coefficient} \description{ The assortativity coefficient is positive is similar vertices (based on some external property) tend to connect to each, and negative otherwise. } \usage{ assortativity (graph, types1, types2 = NULL, directed = TRUE) assortativity.nominal (graph, types, directed = TRUE) assortativity.degree (graph, directed = TRUE) } \arguments{ \item{graph}{The input graph, it can be directed or undirected.} \item{types}{Vector giving the vertex types. They as assumed to be integer numbers, starting with one. Non-integer values are converted to integers with \code{\link{as.integer}}.} \item{types1}{The vertex values, these can be arbitrary numeric values.} \item{types2}{A second value vector to be using for the incoming edges when calculating assortativity for a directed graph. Supply \code{NULL} here if you want to use the same values for outgoing and incoming edges. This argument is ignored (with a warning) if it is not \code{NULL} and undirected assortativity coefficient is being calculated.} \item{directed}{Logical scalar, whether to consider edge directions for directed graphs. This argument is ignored for undirected graphs. Supply \code{TRUE} here to do the natural thing, i.e. use directed version of the measure for directed graphs and the undirected version for undirected graphs.} } \details{ The assortativity coefficient measures the level of homophyly of the graph, based on some vertex labeling or values assigned to vertices. If the coefficient is high, that means that connected vertices tend to have the same labels or similar assigned values. M.E.J. Newman defined two kinds of assortativity coefficients, the first one is for categorical labels of vertices. \code{assortativity.nominal} calculates this measure. It is defines as \deqn{r=\frac{\sum_i e_{ii}-\sum_i a_i b_i}{1-\sum_i a_i b_i}}{ r=(sum(e(i,i), i) - sum(a(i)b(i), i)) / (1 - sum(a(i)b(i), i))} where \eqn{e_{ij}}{e(i,j)} is the fraction of edges connecting vertices of type \eqn{i} and \eqn{j}, \eqn{a_i=\sum_j e_{ij}}{a(i)=sum(e(i,j), j)} and \eqn{b_j=\sum_i e_{ij}}{b(j)=sum(e(i,j), i)}. The second assortativity variant is based on values assigned to the vertices. \code{assortativity} calculates this measure. It is defined as \deqn{r=\frac1{\sigma_q^2}\sum_{jk} jk(e_{jk}-q_j q_k)}{ sum(jk(e(j,k)-q(j)q(k)), j, k) / sigma(q)^2} for undirected graphs (\eqn{q_i=\sum_j e_{ij}}{q(i)=sum(e(i,j), j)}) and as \deqn{r=\frac1{\sigma_o\sigma_i}\sum_{jk}jk(e_{jk}-q_j^o q_k^i)}{ sum(jk(e(j,k)-qout(j)qin(k)), j, k) / sigma(qin) / sigma(qout) } for directed ones. Here \eqn{q_i^o=\sum_j e_{ij}}{qout(i)=sum(e(i,j), j)}, \eqn{q_i^i=\sum_j e_{ji}}{qin(i)=sum(e(j,i), j)}, moreover, \eqn{\sigma_q}{sigma(q)}, \eqn{sigma_o}{sigma(qout)} and \eqn{sigma_i}{sigma(qin)} are the standard deviations of \eqn{q}, \eqn{q^o}{qout} and \eqn{q^i}{qin}, respectively. The reason of the difference is that in directed networks the relationship is not symmetric, so it is possible to assign different values to the outgoing and the incoming end of the edges. \code{assortativity.degree} uses vertex degree (minus one) as vertex values and calls \code{assortativity}. } \value{ A single real number. } \references{ M. E. J. Newman: Mixing patterns in networks, \emph{Phys. Rev. E} 67, 026126 (2003) \url{http://arxiv.org/abs/cond-mat/0209450} M. E. J. Newman: Assortative mixing in networks, \emph{Phys. Rev. Lett.} 89, 208701 (2002) \url{http://arxiv.org/abs/cond-mat/0205405/} } \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } % \seealso{} \examples{ # random network, close to zero assortativity.degree(erdos.renyi.game(10000,3/10000)) # BA model, tends to be dissortative assortativity.degree(ba.game(10000, m=4)) } \keyword{graphs} igraph/man/graph.complementer.Rd0000644000176000001440000000205512251656216016420 0ustar ripleyusers\name{graph.complementer} \alias{graph.complementer} \concept{Graph operators} \title{Complementer of a graph} \description{A complementer graph contains all edges that were not present in the input graph.} \usage{ graph.complementer(graph, loops=FALSE) } \arguments{ \item{graph}{The input graph, can be directed or undirected.} \item{loops}{Logical constant, whether to generate loop edges.} } \details{ \code{graph.complementer} creates the complementer of a graph. Only edges which are \emph{not} present in the original graph will be included in the new graph. \code{graph.complementer} keeps graph and vertex attriubutes, edge attributes are lost. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ ## Complementer of a ring g <- graph.ring(10) graph.complementer(g) ## A graph and its complementer give together the full graph g <- graph.ring(10) gc <- graph.complementer(g) gu <- graph.union(g, gc) gu graph.isomorphic(gu, graph.full(vcount(g))) } \keyword{graphs} igraph/man/transitivity.Rd0000644000176000001440000001201412240234657015372 0ustar ripleyusers\name{transitivity} \alias{transitivity} \concept{Transitivity} \concept{Clustering coefficient} \title{Transitivity of a graph} \description{Transitivity measures the probability that the adjacent vertices of a vertex are connected. This is sometimes also called the clustering coefficient. } \usage{ transitivity(graph, type=c("undirected", "global", "globalundirected", "localundirected", "local", "average", "localaverage", "localaverageundirected", "barrat", "weighted"), vids=NULL, weights=NULL, isolates=c("NaN", "zero")) } \arguments{ \item{graph}{The graph to analyze.} \item{type}{The type of the transitivity to calculate. Possible values: \describe{ \item{\code{global}}{The global transitivity of an undirected graph (directed graphs are considered as undirected ones as well). This is simply the ratio of the triangles and the connected triples in the graph. For directed graph the direction of the edges is ignored. } \item{\code{local}}{The local transitivity of an undirected graph, this is calculated for each vertex given in the \code{vids} argument. The local transitivity of a vertex is the ratio of the triangles connected to the vertex and the triples centered on the vertex. For directed graph the direction of the edges is ignored. } \item{\code{undirected}}{This is the same as \code{global}.} \item{\code{globalundirected}}{This is the same as \code{global}.} \item{\code{localundirected}}{This is the same as \code{local}.} \item{\code{barrat}}{The weighted transitivity as defined A. Barrat. See details below.} \item{\code{weighted}}{The same as \code{barrat}.} } } \item{vids}{The vertex ids for the local transitivity will be calculated. This will be ignored for global transitivity types. The default value is \code{NULL}, in this case all vertices are considered. It is slightly faster to supply \code{NULL} here than \code{V(graph)}. } \item{weights}{Optional weights for weighted transitivity. It is ignored for other transitivity measures. If it is \code{NULL} (the default) and the graph has a \code{weight} edge attribute, then it is used automatically. } \item{isolates}{Character scalar, defines how to treat vertices with degree zero and one. If it is \sQuote{\code{NaN}} then they local transitivity is reported as \code{NaN} and they are not included in the averaging, for the transitivity types that calculate an average. If there are no vertices with degree two or higher, then the averaging will still result \code{NaN}. If it is \sQuote{\code{zero}}, then we report 0 transitivity for them, and they are included in the averaging, if an average is calculated. } } \details{ Note that there are essentially two classes of transitivity measures, one is a vertex-level, the other a graph level property. There are several generalizations of transitivity to weighted graphs, here we use the definition by A. Barrat, this is a local vertex-level quantity, its formula is \deqn{C_i^w=\frac{1}{s_i(k_i-1)}\sum_{j,h}\frac{w_{ij}+w_{ih}}{2}a_{ij}a_{ih}a_{jh}}{ weighted C_i = 1/s_i 1/(k_i-1) sum( (w_ij+w_ih)/2 a_ij a_ih a_jh, j, h)} \eqn{s_i}{s_i} is the strength of vertex \eqn{i}{i}, see \code{\link{graph.strength}}, \eqn{a_{ij}}{a_ij} are elements of the adjacency matrix, \eqn{k_i}{k_i} is the vertex degree, \eqn{w_{ij}}{w_ij} are the weights. This formula gives back the normal not-weighted local transitivity if all the edge weights are the same. The \code{barrat} type of transitivity does not work for graphs with multiple and/or loop edges. If you want to calculate it for a directed graph, call \code{\link{as.undirected}} with the \code{collapse} mode first. } \value{ For \sQuote{\code{global}} a single number, or \code{NaN} if there are no connected triples in the graph. For \sQuote{\code{local}} a vector of transitivity scores, one for each vertex in \sQuote{\code{vids}}. } \references{ Wasserman, S., and Faust, K. (1994). \emph{Social Network Analysis: Methods and Applications.} Cambridge: Cambridge University Press. Alain Barrat, Marc Barthelemy, Romualdo Pastor-Satorras, Alessandro Vespignani: The architecture of complex weighted networks, Proc. Natl. Acad. Sci. USA 101, 3747 (2004) } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.ring(10) transitivity(g) g2 <- erdos.renyi.game(1000, 10/1000) transitivity(g2) # this is about 10/1000 # Weighted version, the figure from the Barrat paper gw <- graph.formula(A-B:C:D:E, B-C:D, C-D) E(gw)$weight <- 1 E(gw)[ V(gw)[name == "A"] \%--\% V(gw)[name == "E" ] ]$weight <- 5 transitivity(gw, vids="A", type="local") transitivity(gw, vids="A", type="weighted") # Weighted reduces to "local" if weights are the same gw2 <- erdos.renyi.game(1000, 10/1000) E(gw2)$weight <- 1 t1 <- transitivity(gw2, type="local") t2 <- transitivity(gw2, type="weighted") all(is.na(t1) == is.na(t2)) all(na.omit(t1 == t2)) } \keyword{graphs} igraph/man/srand.Rd0000644000176000001440000000227612267350637013747 0ustar ripleyusers\name{srand} \alias{srand} \title{Set random seed of the C library's RNG} \description{Set the random seed of the C library's RNG, for a new sequence of pseudo-random numbers.} \usage{ srand(seed) } \arguments{ \item{seed}{Numeric scalar, the new random seed. It must be non-negative and will be converted to an integer.} } \details{ Note that this function has nothing to do with R's random number generator, see \code{set.seed} for that. Some package (e.g. ngspatial) use internal C code and generate random numbers using the standard C library's built-in random number generator instead of using R's RNGs. The \code{srand} function is provided to set the random seed for these packages. It simply calls the standard C function \code{srand}, with the supplied integer seed value. Note that the standard C library's RNGs are typically of very bad quality, and also slower than R's RNGs. It is not worth using them, really, other than taking over some legacy C code that already uses them, and that would be difficult to rewrite to use R's RNGs. } \value{ \code{NULL}, invisibly. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} % \examples{} igraph/man/convex.hull.Rd0000644000176000001440000000164612240234657015077 0ustar ripleyusers\name{convex.hull} \alias{convex.hull} \concept{Convex hull} \title{Convex hull of a set of vertices} \description{ Calculate the convex hull of a set of points, i.e. the covering polygon that has the smallest area. } \usage{ convex.hull(data) } \arguments{ \item{data}{The data points, a numeric matrix with two columns.} } %\details{} \value{ A named list with components: \item{resverts}{The indices of the input vertices that constritute the convex hull.} \item{rescoords}{The coordinates of the corners of the convex hull.} } \references{ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. Introduction to Algorithms, Second Edition. MIT Press and McGraw-Hill, 2001. ISBN 0262032937. Pages 949-955 of section 33.3: Finding the convex hull. } \author{Tamas Nepusz \email{ntamas@gmail.com}} % \seealso \examples{ M <- cbind( runif(100), runif(100) ) convex.hull(M) } \keyword{graphs} igraph/man/arpack.Rd0000644000176000001440000002570612263024035014066 0ustar ripleyusers\name{arpack} \alias{arpack} \alias{arpack-options} \alias{igraph.arpack.default} \alias{arpack.unpack.complex} \concept{Eigenvalues} \concept{Eigenvectors} \concept{ARPACK} \title{ARPACK eigenvector calculation} \description{Interface to the ARPACK library for calculating eigenvectors of sparse matrices} \usage{ arpack(func, extra = NULL, sym = FALSE, options = igraph.arpack.default, env = parent.frame(), complex=!sym) arpack.unpack.complex(vectors, values, nev) } \arguments{ \item{func}{The function to perform the matrix-vector multiplication. ARPACK requires to perform these by the user. The function gets the vector \eqn{x} as the first argument, and it should return \eqn{Ax}, where \eqn{A} is the \dQuote{input matrix}. (The input matrix is never given explicitly.) The second argument is \code{extra}.} \item{extra}{Extra argument to supply to \code{func}.} \item{sym}{Logical scalar, whether the input matrix is symmetric. Always supply \code{TRUE} here if it is, since it can speed up the computation. } \item{options}{Options to ARPACK, a named list to overwrite some of the default option values. See details below.} \item{env}{The environment in which \code{func} will be evaluated.} \item{complex}{Whether to convert the eigenvectors returned by ARPACK into R complex vectors. By default this is not done for symmetric problems (these only have real eigenvectors/values), but only non-symmetric ones. If you have a non-symmetric problem, but you're sure that the results will be real, then supply \code{FALSE} here. The conversion is done by calling \code{arpack.unpack.complex}. } \item{vectors}{Eigenvectors, as returned by ARPACK.} \item{values}{Eigenvalues, as returned by ARPACK} \item{nev}{The number of eigenvectors/values to extract. This can be less than or equal to the number of eigenvalues requested in the original ARPACK call.} } \details{ ARPACK is a library for solving large scale eigenvalue problems. The package is designed to compute a few eigenvalues and corresponding eigenvectors of a general \eqn{n} by \eqn{n} matrix \eqn{A}. It is most appropriate for large sparse or structured matrices \eqn{A} where structured means that a matrix-vector product \code{w <- Av} requires order \eqn{n} rather than the usual order \eqn{n^2} floating point operations. Please see \url{http://www.caam.rice.edu/software/ARPACK/} for details. This function is an interface to ARPACK. igraph does not contain all ARPACK routines, only the ones dealing with symmetric and non-symmetric eigenvalue problems using double precision real numbers. The eigenvalue calculation in ARPACK (in the simplest case) involves the calculation of the \eqn{Av} product where \eqn{A} is the matrix we work with and \eqn{v} is an arbitrary vector. The function supplied in the \code{fun} argument is expected to perform this product. If the product can be done efficiently, e.g. if the matrix is sparse, then \code{arpack} is usually able to calculate the eigenvalues very quickly. The \code{options} argument specifies what kind of calculation to perform. It is a list with the following members, they correspond directly to ARPACK parameters. On input it has the following fields: \describe{ \item{bmat}{Character constant, possible values: \sQuote{\code{I}}, stadard eigenvalue problem, \eqn{Ax=\lambda x}{A*x=lambda*x}; and \sQuote{\code{G}}, generalized eigenvalue problem, \eqn{Ax=\lambda B x}{A*x=lambda B*x}. Currently only \sQuote{\code{I}} is supported.} \item{n}{Numeric scalar. The dimension of the eigenproblem. You only need to set this if you call \code{\link{arpack}} directly. (I.e. not needed for \code{\link{evcent}}, \code{\link{page.rank}}, etc.)} \item{which}{Specify which eigenvalues/vectors to compute, character constant with exactly two characters. Possible values for symmetric input matrices: \describe{ \item{\sQuote{\code{LA}}}{Compute \code{nev} largest (algebraic) eigenvalues.} \item{\sQuote{\code{SA}}}{Compute \code{nev} smallest (algebraic) eigenvalues.} \item{\sQuote{\code{LM}}}{Compute \code{nev} largest (in magnitude) eigenvalues.} \item{\sQuote{\code{SM}}}{Compute \code{nev} smallest (in magnitude) eigenvalues.} \item{\sQuote{\code{BE}}}{Compute \code{nev} eigenvalues, half from each end of the spectrum. When \code{nev} is odd, compute one more from the high end than from the low end.} } Possible values for non-symmetric input matrices: \describe{ \item{\sQuote{\code{LM}}}{Compute \code{nev} eigenvalues of largest magnitude.} \item{\sQuote{\code{SM}}}{Compute \code{nev} eigenvalues of smallest magnitude.} \item{\sQuote{\code{LR}}}{Compute \code{nev} eigenvalues of largest real part.} \item{\sQuote{\code{SR}}}{Compute \code{nev} eigenvalues of smallest real part.} \item{\sQuote{\code{LI}}}{Compute \code{nev} eigenvalues of largest imaginary part.} \item{\sQuote{\code{SI}}}{Compute \code{nev} eigenvalues of smallest imaginary part.} } This parameter is sometimes overwritten by the various functions, e.g. \code{\link{page.rank}} always sets \sQuote{\code{LM}}. } \item{nev}{Numeric scalar. The number of eigenvalues to be computed.} \item{tol}{Numeric scalar. Stopping criterion: the relative accuracy of the Ritz value is considered acceptable if its error is less than \code{tol} times its estimated value. If this is set to zero then machine precision is used.} \item{ncv}{Number of Lanczos vectors to be generated.} \item{ldv}{Numberic scalar. It should be set to zero in the current implementation.} \item{ishift}{Either zero or one. If zero then the shifts are provided by the user via reverse communication. If one then exact shifts with respect to the reduced tridiagonal matrix \eqn{T}. Please always set this to one.} \item{maxiter}{Maximum number of Arnoldi update iterations allowed. } \item{nb}{Blocksize to be used in the recurrence. Please always leave this on the default value, one.} \item{mode}{The type of the eigenproblem to be solved. Possible values if the input matrix is symmetric: \describe{ \item{1}{\eqn{Ax=\lambda x}{A*x=lambda*x}, \eqn{A} is symmetric.} \item{2}{\eqn{Ax=\lambda Mx}{A*x=lambda*M*x}, \eqn{A} is symmetric, \eqn{M} is symmetric positive definite.} \item{3}{\eqn{Kx=\lambda Mx}{K*x=lambda*M*x}, \eqn{K} is symmetric, \eqn{M} is symmetric positive semi-definite.} \item{4}{\eqn{Kx=\lambda KGx}{K*x=lambda*KG*x}, \eqn{K} is symmetric positive semi-definite, \eqn{KG} is symmetric indefinite.} \item{5}{\eqn{Ax=\lambda Mx}{A*x=lambda*M*x}, \eqn{A} is symmetric, \eqn{M} is symmetric positive semi-definite. (Cayley transformed mode.)} } Please note that only \code{mode==1} was tested and other values might not work properly. Possible values if the input matrix is not symmetric: \describe{ \item{1}{\eqn{Ax=\lambda x}{A*x=lambda*x}.} \item{2}{\eqn{Ax=\lambda Mx}{A*x=lambda*M*x}, \eqn{M} is symmetric positive definite.} \item{3}{\eqn{Ax=\lambda Mx}{A*x=lambda*M*x}, \eqn{M} is symmetric semi-definite.} \item{4}{\eqn{Ax=\lambda Mx}{A*x=lambda*M*x}, \eqn{M} is symmetric semi-definite.} } Please note that only \code{mode==1} was tested and other values might not work properly. } \item{start}{Not used currently. Later it be used to set a starting vector.} \item{sigma}{Not used currently.} \item{sigmai}{Not use currently.} On output the following additional fields are added: \describe{ \item{info}{Error flag of ARPACK. Possible values: \describe{ \item{0}{Normal exit.} \item{1}{Maximum number of iterations taken.} \item{3}{No shifts could be applied during a cycle of the Implicitly restarted Arnoldi iteration. One possibility is to increase the size of \code{ncv} relative to \code{nev}.} } ARPACK can return more error conditions than these, but they are converted to regular igraph errors. } \item{iter}{Number of Arnoldi iterations taken.} \item{nconv}{Number of \dQuote{converged} Ritz values. This represents the number of Ritz values that satisfy the convergence critetion. } \item{numop}{Total number of matrix-vector multiplications.} \item{numopb}{Not used currently.} \item{numreo}{Total number of steps of re-orthogonalization.} } } Please see the ARPACK documentation for additional details. \code{arpack.unpack.complex} is a (semi-)internal function that converts the output of the non-symmetric ARPACK solver to a more readable format. It is called internally by \code{arpack}. } \value{ A named list with the following members: \item{values}{Numeric vector, the desired eigenvalues.} \item{vectors}{Numeric matrix, the desired eigenvectors as columns. If \code{complex=TRUE} (the default for non-symmetric problems), then the matrix is complex.} \item{options}{A named list with the supplied \code{options} and some information about the performed calculation, including an ARPACK exit code. See the details above. } } \references{ D.C. Sorensen, Implicit Application of Polynomial Filters in a k-Step Arnoldi Method. \emph{SIAM J. Matr. Anal. Apps.}, 13 (1992), pp 357-385. R.B. Lehoucq, Analysis and Implementation of an Implicitly Restarted Arnoldi Iteration. \emph{Rice University Technical Report} TR95-13, Department of Computational and Applied Mathematics. B.N. Parlett & Y. Saad, Complex Shift and Invert Strategies for Real Matrices. \emph{Linear Algebra and its Applications}, vol 88/89, pp 575-595, (1987). } \author{Rich Lehoucq, Kristi Maschhoff, Danny Sorensen, Chao Yang for ARPACK, Gabor Csardi \email{csardi.gabor@gmail.com} for the R interface.} \seealso{\code{\link{evcent}}, \code{\link{page.rank}}, \code{\link{hub.score}}, \code{\link{leading.eigenvector.community}} are some of the functions in igraph which use ARPACK. The ARPACK homepage is at \url{http://www.caam.rice.edu/software/ARPACK/}. } \examples{ # Identity matrix f <- function(x, extra=NULL) x arpack(f, options=list(n=10, nev=2, ncv=4), sym=TRUE) # Graph laplacian of a star graph (undirected), n>=2 # Note that this is a linear operation f <- function(x, extra=NULL) { y <- x y[1] <- (length(x)-1)*x[1] - sum(x[-1]) for (i in 2:length(x)) { y[i] <- x[i] - x[1] } y } arpack(f, options=list(n=10, nev=1, ncv=3), sym=TRUE) # double check eigen(graph.laplacian(graph.star(10, mode="undirected"))) ## First three eigenvalues of the adjacency matrix of a graph ## We need the 'Matrix' package for this if (require(Matrix)) { g <- erdos.renyi.game(1000, 5/1000) M <- get.adjacency(g, sparse=TRUE) f2 <- function(x, extra=NULL) { cat("."); as.vector(M \%*\% x) } baev <- arpack(f2, sym=TRUE, options=list(n=vcount(g), nev=3, ncv=8, which="LM", maxiter=200)) } } \keyword{graphs} igraph/man/alpha.centrality.Rd0000644000176000001440000000606012240234657016067 0ustar ripleyusers\name{alpha.centrality} \alias{alpha.centrality} \concept{Alpha centrality} \title{Find Bonacich alpha centrality scores of network positions} \description{\code{alpha.centrality} calculates the alpha centrality of some (or all) vertices in a graph. } \usage{ alpha.centrality(graph, nodes=V(graph), alpha=1, loops=FALSE, exo=1, weights=NULL, tol=1e-7, sparse=TRUE) } \arguments{ \item{graph}{The input graph, can be directed or undirected} \item{nodes}{Vertex sequence, the vertices for which the alpha centrality values are returned. (For technical reasons they will be calculated for all vertices, anyway.)} \item{alpha}{Parameter specifying the relative importance of endogenous versus exogenous factors in the determination of centrality. See details below.} \item{loops}{Whether to eliminate loop edges from the graph before the calculation.} \item{exo}{The exogenous factors, in most cases this is either a constant -- the same factor for every node, or a vector giving the factor for every vertex. Note that too long vectors will be truncated and too short vectors will be replicated to match the number of vertices.} \item{weights}{A character scalar that gives the name of the edge attribute to use in the adjacency matrix. If it is \code{NULL}, then the \sQuote{weight} edge attribute of the graph is used, if there is one. Otherwise, or if it is \code{NA}, then the calculation uses the standard adjacency matrix.} \item{tol}{Tolerance for near-singularities during matrix inversion, see \code{\link{solve}}.} \item{sparse}{Logical scalar, whether to use sparse matrices for the calculation. The \sQuote{Matrix} package is required for sparse matrix support} } \details{ The alpha centrality measure can be considered as a generalization of eigenvector centerality to directed graphs. It was proposed by Bonacich in 2001 (see reference below). The alpha centrality of the vertices in a graph is defined as the solution of the following matrix equation: \deqn{x=\alpha A^T x+e,}{x=alpha t(A)x+e,} where \eqn{A}{A} is the (not neccessarily symmetric) adjacency matrix of the graph, \eqn{e}{e} is the vector of exogenous sources of status of the vertices and \eqn{\alpha}{alpha} is the relative importance of the endogenous versus exogenous factors. } \value{ A numeric vector contaning the centrality scores for the selected vertices. } \references{ Bonacich, P. and Paulette, L. (2001). ``Eigenvector-like measures of centrality for asymmetric relations'' \emph{Social Networks}, 23, 191-201. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \section{Warning}{Singular adjacency matrices cause problems for this algorithm, the routine may fail is certain cases.} \seealso{\code{\link{evcent}} and \code{\link{bonpow}}} \examples{ # The examples from Bonacich's paper g.1 <- graph( c(1,3,2,3,3,4,4,5) ) g.2 <- graph( c(2,1,3,1,4,1,5,1) ) g.3 <- graph( c(1,2,2,3,3,4,4,1,5,1) ) alpha.centrality(g.1) alpha.centrality(g.2) alpha.centrality(g.3,alpha=0.5) } \keyword{graphs} igraph/man/igraphtest.Rd0000644000176000001440000000115712251656216015002 0ustar ripleyusers\name{igraphtest} \alias{igraphtest} \title{Run package tests} \description{Runs all package tests.} \usage{ igraphtest() } \details{ The \code{testthat} package is needed to run all tests. The location tests themselves can be extracted from the package via \code{system.file("tests", package="igraph")}. This function simply calls the \code{test_dir} function from the \code{testthat} package on the test directory. } \value{Whatever is returned by \code{test_dir} from the \code{testthat} package.} % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } % \seealso{} % \examples{} \keyword{graphs} igraph/man/print.graph.Rd0000644000176000001440000000677112251656216015073 0ustar ripleyusers\name{print.igraph} \alias{print.igraph} \alias{str.igraph} \alias{summary.igraph} \title{Print graphs to the terminal} \description{These functions attempt to print a graph to the terminal in a human readable form.} \usage{ \method{print}{igraph}(x, full=getIgraphOpt("print.full"), graph.attributes=getIgraphOpt("print.graph.attributes"), vertex.attributes=getIgraphOpt("print.vertex.attributes"), edge.attributes=getIgraphOpt("print.edge.attributes"), names=TRUE, \dots) \method{summary}{igraph}(object, \dots) \method{str}{igraph}(object, \dots) } \arguments{ \item{x}{The graph to print.} \item{full}{Logical scalar, whether to print the graph structure itself as well.} \item{graph.attributes}{Logical constant, whether to print graph attributes.} \item{vertex.attributes}{Logical constant, whether to print vertex attributes.} \item{edge.attributes}{Logical constant, whether to print edge attributes.} \item{names}{Logical constant, whether to print symbolic vertex names (ie. the \code{name} vertex attribute) or vertex ids.} \item{object}{The graph of which the summary will be printed.} \item{\dots}{Additional agruments.} } \details{ \code{summary.igraph} prints the number of vertices, edges and whether the graph is directed. \code{str.igraph} prints the same information, and also lists the edges, and optionally graph, vertex and/or edge attributes. \code{print.igraph} behaves either as \code{summary.igraph} or \code{str.igraph} depending on the \code{full} argument. See also the \sQuote{print.full} igraph option and \code{\link{getIgraphOpt}}. The graph summary printed by \code{summary.igraph} (and \code{print.igraph} and \code{str.igraph}) consists one or more lines. The first line contains the basic properties of the graph, and the rest contains its attributes. Here is an example, a small star graph with weighed directed edges and named vertices: \preformatted{ IGRAPH DNW- 10 9 -- In-star + attr: name (g/c), mode (g/c), center (g/n), name (v/c), weight (e/n) } The first line always starts with \code{IGRAPH}, showing you that the object is an igraph graph. Then a four letter long code string is printed. The first letter distinguishes between directed (\sQuote{\code{D}}) and undirected (\sQuote{\code{U}}) graphs. The second letter is \sQuote{\code{N}} for named graphs, i.e. graphs with the \code{name} vertex attribute set. The third letter is \sQuote{\code{W}} for weighted graphs, i.e. graphs with the \code{weight} edge attribute set. The fourth letter is \sQuote{\code{B}} for bipartite graphs, i.e. for graphs with the \code{type} vertex attribute set. Then, after two dashes, the name of the graph is printed, if it has one, i.e. if the \code{name} graph attribute is set. From the second line, the attributes of the graph are listed, separated by a comma. After the attribute names, the kind of the attribute -- graph (\sQuote{\code{g}}), vertex (\sQuote{\code{v}}) or edge (\sQuote{\code{e}}) -- is denoted, and the type of the attribute as well, character (\sQuote{\code{c}}), numeric (\sQuote{\code{n}}), logical (\sQuote{\code{l}}), or other (\sQuote{\code{x}}). As of igraph 0.4 \code{str.igraph} and \code{print.igraph} use the \code{max.print} option, see \code{\link[base]{options}} for details. } \value{All these functions return the graph invisibly.} % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} %\seealso{} \examples{ g <- graph.ring(10) g summary(g) } \keyword{graphs} igraph/man/graph.disjoint.union.Rd0000644000176000001440000000317612251656216016705 0ustar ripleyusers\name{graph.disjoint.union} \alias{graph.disjoint.union} \alias{\%du\%} \concept{Graph operators} \title{Disjoint union of graphs} \description{The union of two or more graphs are created. The graphs are assumed to have disjoint vertex sets.} \usage{ graph.disjoint.union(\dots) } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} } \details{ \code{graph.disjoint.union} creates a union of two or more disjoint graphs. Thus first the vertices in the second, third, etc. graphs are relabeled to have completely disjoint graphs. Then a simple union is created. This function can also be used via the \%du\% operator. \code{graph.disjont.union} handles graph, vertex and edge attributes. In particular, it merges vertex and edge attributes using the basic \code{c()} function. For graphs that lack some vertex/edge attribute, the corresponding values in the new graph are set to \code{NA}. Graph attributes are simply copied to the result. If this would result a name clash, then they are renamed by adding suffixes: _1, _2, etc. Note that if both graphs have vertex names (ie. a \code{name} vertex attribute), then the concatenated vertex names might be non-unique in the result. A warning is given if this happens. An error is generated if some input graphs are directed and others are undirected. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ ## A star and a ring g1 <- graph.star(10, mode="undirected") V(g1)$name <- letters[1:10] g2 <- graph.ring(10) V(g2)$name <- letters[11:20] str(g1 \%du\% g2) } \keyword{graphs} igraph/man/power.law.fit.Rd0000644000176000001440000001301212325356537015325 0ustar ripleyusers\name{power.law.fit} \alias{power.law.fit} \concept{Power-law} \title{Fitting a power-law distribution function to discrete data} \description{\code{power.law.fit} fits a power-law distribution to a data set.} \usage{ power.law.fit(x, xmin=NULL, start=2, force.continuous=FALSE, implementation=c("plfit", "R.mle"), \dots) } \arguments{ \item{x}{The data to fit, a numeric vector. For implementation \sQuote{\code{R.mle}} the data must be integer values. For the \sQuote{\code{plfit}} implementation non-integer values might be present and then a continuous power-law distribution is fitted.} \item{xmin}{Numeric scalar, or \code{NULL}. The lower bound for fitting the power-law. If \code{NULL}, the smallest value in \code{x} will be used for the \sQuote{\code{R.mle}} implementation, and its value will be automatically determined for the \sQuote{\code{plfit}} implementation. This argument makes it possible to fit only the tail of the distribution. } \item{start}{Numeric scalar. The initial value of the exponent for the minimizing function, for the \sQuote{\code{R.mle}} implementation. Ususally it is safe to leave this untouched.} \item{force.continuous}{Logical scalar. Whether to force a continuous distribution for the \sQuote{\code{plfit}} implementation, even if the sample vector contains integer values only (by chance). If this argument is false, igraph will assume a continuous distribution if at least one sample is non-integer and assume a discrete distribution otherwise.} \item{implementation}{Character scalar. Which implementation to use. See details below.} \item{\dots}{Additional arguments, passed to the maximum likelihood optimizing function, \code{\link[stats4]{mle}}, if the \sQuote{\code{R.mle}} implementation is chosen. It is ignored by the \sQuote{\code{plfit}} implementation.} } \details{ This function fits a power-law distribution to a vector containing samples from a distribution (that is assumed to follow a power-law of course). In a power-law distribution, it is generally assumed that \eqn{P(X=x)} is proportional to \eqn{x^{-alpha}}{x^-alpha}, where \eqn{x} is a positive number and \eqn{\alpha}{alpha} is greater than 1. In many real-world cases, the power-law behaviour kicks in only above a threshold value \eqn{x_{min}}{xmin}. The goal of this function is to determine \eqn{\alpha}{alpha} if \eqn{x_{min}}{xmin} is given, or to determine \eqn{x_{min}}{xmin} and the corresponding value of \eqn{\alpha}{alpha}. \code{power.law.fit} provides two maximum likelihood implementations. If the \code{implementation} argument is \sQuote{\code{R.mle}}, then the BFGS optimization (see \link[stats4]{mle}) algorithm is applied. The additional arguments are passed to the mle function, so it is possible to change the optimization method and/or its parameters. This implementation can \emph{not} to fit the \eqn{x_{min}}{xmin} argument, so use the \sQuote{\code{plfit}} implementation if you want to do that. The \sQuote{\code{plfit}} implementation also uses the maximum likelihood principle to determine \eqn{\alpha}{alpha} for a given \eqn{x_{min}}{xmin}; When \eqn{x_{min}}{xmin} is not given in advance, the algorithm will attempt to find itsoptimal value for which the \eqn{p}-value of a Kolmogorov-Smirnov test between the fitted distribution and the original sample is the largest. The function uses the method of Clauset, Shalizi and Newman to calculate the parameters of the fitted distribution. See references below for the details. } \value{ Depends on the \code{implementation} argument. If it is \sQuote{\code{R.mle}}, then an object with class \sQuote{\code{mle}}. It can be used to calculate confidence intervals and log-likelihood. See \code{\link[stats4]{mle-class}} for details. If \code{implementation} is \sQuote{\code{plfit}}, then the result is a named list with entries: \item{continuous}{Logical scalar, whether the fitted power-law distribution was continuous or discrete.} \item{alpha}{Numeric scalar, the exponent of the fitted power-law distribution.} \item{xmin}{Numeric scalar, the minimum value from which the power-law distribution was fitted. In other words, only the values larger than \code{xmin} were used from the input vector.} \item{logLik}{Numeric scalar, the log-likelihood of the fitted parameters.} \item{KS.stat}{Numeric scalar, the test statistic of a Kolmogorov-Smirnov test that compares the fitted distribution with the input vector. Smaller scores denote better fit.} \item{KS.p}{Numeric scalar, the p-value of the Kolmogorov-Smirnov test. Small p-values (less than 0.05) indicate that the test rejected the hypothesis that the original data could have been drawn from the fitted power-law distribution.} } \references{ Power laws, Pareto distributions and Zipf's law, M. E. J. Newman, \emph{Contemporary Physics}, 46, 323-351, 2005. Aaron Clauset, Cosma R .Shalizi and Mark E.J. Newman: Power-law distributions in empirical data. SIAM Review 51(4):661-703, 2009. } \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link[stats4]{mle}}} \examples{ # This should approximately yield the correct exponent 3 g <- barabasi.game(1000) # increase this number to have a better estimate d <- degree(g, mode="in") fit1 <- power.law.fit(d+1, 10) fit2 <- power.law.fit(d+1, 10, implementation="R.mle") fit1$alpha coef(fit2) fit1$logLik logLik(fit2) } \keyword{graphs} igraph/man/attribute.combination.Rd0000644000176000001440000001437312240234657017137 0ustar ripleyusers\name{Combining attributes} \alias{attribute.combination} \concept{Vertex/edge/graph attributes} \title{How igraph functions handle attributes when the graph changes} \description{Many times, when the structure of a graph is modified, vertices/edges map of the original graph map to vertices/edges in the newly created (modified) graph. For example \code{\link{simplify}} maps multiple edges to single edges. igraph provides a flexible mechanism to specify what to do with the vertex/edge attributes in these cases.} \details{ The functions that support the combination of attributes have one or two extra arguments called \code{vertex.attr.comb} and/or \code{edge.attr.comb} that specify how to perform the mapping of the attributes. E.g. \code{\link{contract.vertices}} contracts many vertices into a single one, the attributes of the vertices can be combined and stores as the vertex attributes of the new graph. The specification of the combination of (vertex or edge) attributes can be given as \enumerate{ \item{a character scalar,} \item{a function object or} \item{a list of character scalars and/or function objects.} } If it is a character scalar, then it refers to one of the predefined combinations, see their list below. If it is a function, then the given function is expected to perform the combination. It will be called once for each new vertex/edge in the graph, with a single argument: the attribute values of the vertices that map to that single vertex. The third option, a list can be used to specify different combination methods for different attributes. A named entry of the list corresponds to the attribute with the same name. An unnamed entry (i.e. if the name is the empty string) of the list specifies the default combination method. I.e. \preformatted{list(weight="sum", "ignore")} specifies that the weight of the new edge should be sum of the weights of the corresponding edges in the old graph; and that the rest of the attributes should be ignored (=dropped). } \section{Predefined combination functions}{ The following combination behaviors are predefined: \describe{ \item{\sQuote{ignore}}{The attribute is ignored and dropped.} \item{\sQuote{sum}}{The sum of the attributes is calculated. This does not work for character attributes and works for complex attributes only if they have a \code{sum} generic defined. (E.g. it works for sparse matrices from the \code{Matrix} package, because they have a \code{sum} method.)} \item{\sQuote{prod}}{The product of the attributes is calculated. This does not work for character attributes and works for complex attributes only if they have a \code{prod} function defined.} \item{\sQuote{min}}{The minimum of the attributes is calculated and returned. For character and complex attributes the standard R \code{min} function is used.} \item{\sQuote{max}}{The maximum of the attributes is calculated and returned. For character and complex attributes the standard R \code{max} function is used.} \item{\sQuote{random}}{Chooses one of the supplied attribute values, uniformly randomly. For character and complex attributes this is implemented by calling \code{sample}.} \item{\sQuote{first}}{Always chooses the first attribute value. It is implemented by calling the \code{head} function.} \item{\sQuote{last}}{Always chooses the last attribute value. It is implemented by calling the \code{tail} function.} \item{\sQuote{mean}}{The mean of the attributes is calculated and returned. For character and complex attributes this simply calls the \code{mean} function.} \item{\sQuote{median}}{The median of the attributes is selected. Calls the R \code{median} function for all attribute types.} \item{\sQuote{concat}}{Concatenate the attributes, using the \code{c} function. This results almost always a complex attribute.} } } \section{Specifying combination methods for all graphs}{ The are two standard igraph parameters that define the default behavior when combining vertices and edges: \code{vertex.attr.comb} specifies how to combine vertices by default, \code{edge.attr.comb} does the same for edges. E.g. if you want to drop all vertex attributes when combining vertices, you can specify \preformatted{igraph.options(vertex.attr.comb="ignore")} As another example, if -- when combining edges -- you want to keep the mean weight of the edges, concatenate their names into a single character scalar, and drop everything else, then use \preformatted{igraph.options(edge.attr.comb=list(weight="mean", name=toString, "ignore")} } \section{Simple and complex attributes}{ An attribute is simple if (for all vertices/edges) it can be specified as an atomic vector. Character and numeric attributes are always simple. E.g. a vertex attribute that is a numeric vector of arbitrary length for each vertex, is a complex attribute. Combination of attributes might turn a complex attribute into a single one, and the opposite is possible, too. E.g. when contatenating attribute values to form the new attribute value, the result will be typically a complex attribute. See also examples below. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link[igraph]{attributes}} on how to use graph/vertex/edges attributes in general. \code{\link{igraph.options}} on igraph parameters.} \examples{ g <- graph( c(1,2, 1,2, 1,2, 2,3, 3,4) ) E(g)$weight <- 1:5 ## print attribute values with the graph igraph.options(print.graph.attributes=TRUE) igraph.options(print.vertex.attributes=TRUE) igraph.options(print.edge.attributes=TRUE) ## new attribute is the sum of the old ones simplify(g, edge.attr.comb="sum") ## collect attributes into a string simplify(g, edge.attr.comb=toString) ## concatenate them into a vector, this creates a complex ## attribute simplify(g, edge.attr.comb="concat") E(g)$name <- letters[seq_len(ecount(g))] ## both attributes are collected into strings simplify(g, edge.attr.comb=toString) ## harmonic average of weights, names are dropped simplify(g, edge.attr.comb=list(weight=function(x) length(x)/sum(1/x), name="ignore")) } \keyword{graphs} igraph/man/aging.prefatt.game.Rd0000644000176000001440000001337012240234657016270 0ustar ripleyusers\name{aging.prefatt.game} \alias{aging.prefatt.game} \alias{aging.barabasi.game} \alias{aging.ba.game} \concept{Preferential attachment} \concept{Aging of vertices} \concept{Random graph model} \title{Generate an evolving random graph with preferential attachment and aging} \description{This function creates a random graph by simulating its evolution. Each time a new vertex is added it creates a number of links to old vertices and the probability that an old vertex is cited depends on its in-degree (preferential attachment) and age.} \usage{ aging.prefatt.game (n, pa.exp, aging.exp, m = NULL, aging.bin = 300, out.dist = NULL, out.seq = NULL, out.pref = FALSE, directed = TRUE, zero.deg.appeal = 1, zero.age.appeal = 0, deg.coef = 1, age.coef = 1, time.window = NULL) } \arguments{ \item{n}{The number of vertices in the graph.} \item{pa.exp}{The preferantial attachment exponent, see the details below.} \item{aging.exp}{The exponent of the aging, usually a non-positive number, see details below.} \item{m}{The number of edges each new vertex creates (except the very first vertex). This argument is used only if both the \code{out.dist} and \code{out.seq} arguments are NULL.} \item{aging.bin}{The number of bins to use for measuring the age of vertices, see details below.} \item{out.dist}{The discrete distribution to generate the number of edges to add in each time step if \code{out.seq} is NULL. See details below.} \item{out.seq}{The number of edges to add in each time step, a vector containing as many elements as the number of vertices. See details below.} \item{out.pref}{Logical constant, whether to include edges not initiated by the vertex as a basis of preferential attachment. See details below.} \item{directed}{Logical constant, whether to generate a directed graph. See details below.} \item{zero.deg.appeal}{The degree-dependent part of the \sQuote{attractiveness} of the vertices with no adjacent edges. See also details below.} \item{zero.age.appeal}{The age-dependent part of the \sQuote{attrativeness} of the vertices with age zero. It is usually zero, see details below.} \item{deg.coef}{The coefficient of the degree-dependent \sQuote{attractiveness}. See details below.} \item{age.coef}{The coefficient of the age-dependent part of the \sQuote{attractiveness}. See details below.} \item{time.window}{Integer constant, if NULL only adjacent added in the last \code{time.windows} time steps are counted as a basis of the preferential attachment. See also details below.} } \details{ This is a discrete time step model of a growing graph. We start with a network containing a single vertex (and no edges) in the first time step. Then in each time step (starting with the second) a new vertex is added and it initiates a number of edges to the old vertices in the network. The probability that an old vertex is connected to is proportional to \deqn{P[i] \sim (c\cdot k_i^\alpha+a)(d\cdot l_i^\beta+b)\cdot }{% P[i] ~ (c k[i]^alpha + a) (d l[i]^beta + a)} Here \eqn{k_i}{k[i]} is the in-degree of vertex \eqn{i} in the current time step and \eqn{l_i}{l[i]} is the age of vertex \eqn{i}. The age is simply defined as the number of time steps passed since the vertex is added, with the extension that vertex age is divided to be in \code{aging.bin} bins. \eqn{c}, \eqn{\alpha}{alpha}, \eqn{a}, \eqn{d}, \eqn{\beta}{beta} and \eqn{b} are parameters and they can be set via the following arguments: \code{pa.exp} (\eqn{\alpha}{alpha}, mandatory argument), \code{aging.exp} (\eqn{\beta}{beta}, mandatory argument), \code{zero.deg.appeal} (\eqn{a}, optional, the default value is 1), \code{zero.age.appeal} (\eqn{b}, optional, the default is 0), \code{deg.coef} (\eqn{c}, optional, the default is 1), and \code{age.coef} (\eqn{d}, optional, the default is 1). The number of edges initiated in each time step is governed by the \code{m}, \code{out.seq} and \code{out.pref} parameters. If \code{out.seq} is given then it is interpreted as a vector giving the number of edges to be added in each time step. It should be of length \code{n} (the number of vertices), and its first element will be ignored. If \code{out.seq} is not given (or NULL) and \code{out.dist} is given then it will be used as a discrete probability distribution to generate the number of edges. Its first element gives the probability that zero edges are added at a time step, the second element is the probability that one edge is added, etc. (\code{out.seq} should contain non-negative numbers, but if they don't sum up to 1, they will be normalized to sum up to 1. This behavior is similar to the \code{prob} argument of the \code{sample} command.) By default a directed graph is generated, but it \code{directed} is set to \code{FALSE} then an undirected is created. Even if an undirected graph is generaed \eqn{k_i}{k[i]} denotes only the adjacent edges not initiated by the vertex itself except if \code{out.pref} is set to \code{TRUE}. If the \code{time.window} argument is given (and not NULL) then \eqn{k_i}{k[i]} means only the adjacent edges added in the previous \code{time.window} time steps. This function might generate graphs with multiple edges. } \value{A new graph.} % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{barabasi.game}}, \code{\link{erdos.renyi.game}}} \examples{ # The maximum degree for graph with different aging exponents g1 <- aging.prefatt.game(10000, pa.exp=1, aging.exp=0, aging.bin=1000) g2 <- aging.prefatt.game(10000, pa.exp=1, aging.exp=-1, aging.bin=1000) g3 <- aging.prefatt.game(10000, pa.exp=1, aging.exp=-3, aging.bin=1000) max(degree(g1)) max(degree(g2)) max(degree(g3)) } \keyword{graphs} igraph/man/plot.common.Rd0000644000176000001440000005221012325263767015100 0ustar ripleyusers\name{Drawing graphs} \alias{igraph.plotting} \concept{Visualization} \title{Drawing graphs} \description{The common bits of the three plotting functions \code{plot.igraph}, \code{tkplot} and \code{rglplot} are discussed in this manual page} \details{ There are currently three different functions in the igraph package which can draw graph in various ways: \code{plot.igraph} does simple non-interactive 2D plotting to R devices. Actually it is an implementation of the \code{\link[graphics]{plot}} generic function, so you can write \code{plot(graph)} instead of \code{plot.igraph(graph)}. As it used the standard R devices it supports every output format for which R has an output device. The list is quite impressing: PostScript, PDF files, XFig files, SVG files, JPG, PNG and of course you can plot to the screen as well using the default devices, or the good-looking anti-aliased Cairo device. See \code{\link{plot.igraph}} for some more information. \code{\link{tkplot}} does interactive 2D plotting using the \code{tcltk} package. It can only handle graphs of moderate size, a thousend vertices is probably already too many. Some parameters of the plotted graph can be changed interactively after issuing the \code{tkplot} command: the position, color and size of the vertices and the color and width of the edges. See \code{\link{tkplot}} for details. \code{\link{rglplot}} is an experimental function to draw graphs in 3D using OpenGL. See \code{\link{rglplot}} for some more information. Please also check the examples below. } \section{How to specify graphical parameters}{ There are three ways to give values to the parameters described below, in section 'Parameters'. We give these three ways here in the order of their precedence. The first method is to supply named arguments to the plotting commands: \code{\link{plot.igraph}}, \code{\link{tkplot}} or \code{\link{rglplot}}. Parameters for vertices start with prefix \sQuote{\code{vertex.}}, parameters for edges have prefix \sQuote{\code{edge.}}, and global parameters have no prefix. Eg. the color of the vertices can be given via argument \code{vertex.color}, whereas \code{edge.color} sets the color of the edges. \code{layout} gives the layout of the graphs. The second way is to assign vertex, edge and graph attributes to the graph. These attributes have no prefix, ie. the color of the vertices is taken from the \code{color} vertex attribute and the color of the edges from the \code{color} edge attribute. The layout of the graph is given by the \code{layout} graph attribute. (Always assuming that the corresponding command argument is not present.) Setting vertex and edge attributes are handy if you want to assign a given \sQuote{look} to a graph, attributes are saved with the graph is you save it with \code{\link[base]{save}} or in GraphML format with \code{\link{write.graph}}, so the graph will have the same look after loading it again. If a parameter is not given in the command line, and the corresponding vertex/edge/graph attribute is also missing then the general igraph parameters handled by \code{\link{igraph.options}} are also checked. Vertex parameters have prefix \sQuote{\code{vertex.}}, edge parameters are prefixed with \sQuote{\code{edge.}}, general parameters like \code{layout} are prefixed with \sQuote{\code{plot}}. These parameters are useful if you want all or most of your graphs to have the same look, vertex size, vertex color, etc. Then you don't need to set these at every plotting, and you also don't need to assign vertex/edge attributes to every graph. If the value of a parameter is not specified by any of the three ways described here, its default valued is used, as given in the source code. Different parameters can have different type, eg. vertex colors can be given as a character vector with color names, or as an integer vector with the color numbers from the current palette. Different types are valid for different parameters, this is discussed in detail in the next section. It is however always true that the parameter can always be a function object in which it will be called with the graph as its single argument to get the \dQuote{proper} value of the parameter. (If the function returns another function object that will \emph{not} be called again\dots) } \section{The list of parameters}{ Vertex parameters first, note that the \sQuote{\code{vertex.}} prefix needs to be added if they are used as an argument or when setting via \code{\link{igraph.options}}. The value of the parameter may be scalar valid for every vertex or a vector with a separate value for each vertex. (Shorter vectors are recycled.) \describe{ \item{size}{The size of the vertex, a numeric scalar or vector, in the latter case each vertex sizes may differ. This vertex sizes are scaled in order have about the same size of vertices for a given value for all three plotting commands. It does not need to be an integer number. The default value is 15. This is big enough to place short labels on vertices.} \item{size2}{The \dQuote{other} size of the vertex, for some vertex shapes. For the various rectangle shapes this gives the height of the vertices, whereas \code{size} gives the width. It is ignored by shapes for which the size can be specified with a single number. The default is 15. } \item{color}{The fill color of the vertex. If it is numeric then the current palette is used, see \code{\link[grDevices]{palette}}. If it is a character vector then it may either contain named colors or RGB specified colors with three or four bytes. All strings starting with \sQuote{\code{#}} are assumed to be RGB color specifications. It is possible to mix named color and RGB colors. Note that \code{\link{tkplot}} ignores the fourth byte (alpha channel) in the RGB color specification. If you don't want (some) vertices to have any color, supply \code{NA} as the color name. The default value is \dQuote{\code{SkyBlue2}}. } \item{frame.color}{The color of the frame of the vertices, the same formats are allowed as for the fill color. If you don't want vertices to have a frame, supply \code{NA} as the color name. By default it is \dQuote{black}. } \item{shape}{The shape of the vertex, currently \dQuote{\code{circle}}, \dQuote{\code{square}}, \dQuote{\code{csquare}}, \dQuote{\code{rectangle}}, \dQuote{\code{crectangle}}, \dQuote{\code{vrectangle}}, \dQuote{\code{pie}} (see \link{vertex.shape.pie}), \sQuote{\code{sphere}}, and \dQuote{\code{none}} are supported, and only by the \code{\link{plot.igraph}} command. \dQuote{\code{none}} does not draw the vertices at all, although vertex label are plotted (if given). See \code{\link{igraph.vertex.shapes}} for details about vertex shapes and \code{\link{vertex.shape.pie}} for using pie charts as vertices. The \dQuote{\code{sphere}} vertex shape plots vertices as 3D ray-traced spheres, in the given color and size. This produces a raster image and it is only supported with some graphics devices. On some devices raster transparency is not supported and the spheres do not have a transparent background. See \code{\link{dev.capabilities}} and the \sQuote{\code{rasterImage}} capability to check that your device is supported. By default vertices are drawn as circles. } \item{label}{The vertex labels. They will be converted to character. Specify \code{NA} to omit vertex labels. The default vertex labels are the vertex ids. } \item{label.family}{The font family to be used for vertex labels. As different plotting commands can used different fonts, they interpret this parameter different ways. The basic notation is, however, understood by both \code{\link{plot.igraph}} and \code{\link{tkplot}}. \code{\link{rglplot}} does not support fonts at all right now, it ignores this parameter completely. For \code{\link{plot.igraph}} this parameter is simply passed to \code{\link[graphics]{text}} as argument \code{family}. For \code{\link{tkplot}} some conversion is performed. If this parameter is the name of an exixting Tk font, then that font is used and the \code{label.font} and \code{label.cex} parameters are ignored complerely. If it is one of the base families (serif, sans, mono) then Times, Helvetica or Courier fonts are used, there are guaranteed to exist on all systems. For the \sQuote{symbol} base family we used the symbol font is available, otherwise the first font which has \sQuote{symbol} in its name. If the parameter is not a name of the base families and it is also not a named Tk font then we pass it to \code{\link[tcltk]{tkfont.create}} and hope the user knows what she is doing. The \code{label.font} and \code{label.cex} parameters are also passed to \code{\link[tcltk]{tkfont.create}} in this case. The default value is \sQuote{serif}. } \item{label.font}{The font within the font family to use for the vertex labels. It is interpreted the same way as the the \code{font} graphical parameter: 1 is plain text, 2 is bold face, 3 is italic, 4 is bold and italic and 5 specifies the symbol font. For \code{\link{plot.igraph}} this parameter is simply passed to \code{\link[graphics]{text}}. For \code{\link{tkplot}}, if the \code{label.family} parameter is not the name of a Tk font then this parameter is used to set whether the newly created font should be italic and/or boldface. Otherwise it is ignored. For \code{\link{rglplot}} it is ignored. The default value is 1. } \item{label.cex}{The font size for vertex labels. It is interpreted as a multiplication factor of some device-dependent base font size. For \code{\link{plot.igraph}} it is simply passed to \code{\link[graphics]{text}} as argument \code{cex}. For \code{\link{tkplot}} it is multiplied by 12 and then used as the \code{size} argument for \code{\link[tcltk]{tkfont.create}}. The base font is thus 12 for tkplot. For \code{\link{rglplot}} it is ignored. The default value is 1. } \item{label.dist}{ The distance of the label from the center of the vertex. If it is 0 then the label is centered on the vertex. If it is 1 then the label is displayed beside the vertex. The default value is 0. } \item{label.degree}{ It defines the position of the vertex labels, relative to the center of the vertices. It is interpreted as an angle in radian, zero means \sQuote{to the right}, and \sQuote{\code{pi}} means to the left, up is \code{-pi/2} and down is \code{pi/2}. The default value is \code{-pi/4}. } \item{label.color}{The color of the labels, see the \code{color} vertex parameter discussed earlier for the possible values. The default value is \code{black}. } } Edge parameters require to add the \sQuote{\code{edge.}} prefix when used as arguments or set by \code{\link{igraph.options}}. The edge parameters: \describe{ \item{color}{The color of the edges, see the \code{color} vertex parameter for the possible values. By default this parameter is \code{darkgrey}. } \item{width}{The width of the edges. The default value is 1. } \item{arrow.size}{The size of the arrows. Currently this is a constant, so it is the same for every edge. If a vector is submitted then only the first element is used, ie. if this is taken from an edge attribute then only the attribute of the first edge is used for all arrows. This will likely change in the future. The default value is 1. } \item{arrow.width}{The width of the arrows. Currently this is a constant, so it is the same for every edge. If a vector is submitted then only the first element is used, ie. if this is taken from an edge attribute then only the attribute of the first edge is used for all arrows. This will likely change in the future. This argument is currently only used by \code{\link{plot.igraph}}. The default value is 1, which gives the same width as before this option appeared in igraph. } \item{lty}{The line type for the edges. Almost the same format is accepted as for the standard graphics \code{\link[graphics]{par}}, 0 and \dQuote{blank} mean no edges, 1 and \dQuote{solid} are for solid lines, the other possible values are: 2 (\dQuote{dashed}), 3 (\dQuote{dotted}), 4 (\dQuote{dotdash}), 5 (\dQuote{longdash}), 6 (\dQuote{twodash}). \code{\link{tkplot}} also accepts standard Tk line type strings, it does not however support \dQuote{blank} lines, instead of type \sQuote{0} type \sQuote{1}, ie. solid lines will be drawn. This argument is ignored for \code{\link{rglplot}}. The default value is type 1, a solid line. } \item{label}{The edge labels. They will be converted to character. Specify \code{NA} to omit edge labels. Edge labels are omitted by default.} \item{label.family}{Font family of the edge labels. See the vertex parameter with the same name for the details.} \item{label.font}{The font for the edge labels. See the corresponding vertex parameter discussed earlier for details.} \item{label.cex}{The font size for the edge labels, see the corresponding vertex parameter for details.} \item{label.color}{The color of the edge labels, see the \code{color} vertex parameters on how to specify colors. } \item{label.x}{The horizontal coordinates of the edge labels might be given here, explicitly. The \code{NA} elements will be replaced by automatically calculated coordinates. If \code{NULL}, then all edge horizontal coordinates are calculated automatically. This parameter is only supported by \code{plot.igraph}.} \item{label.y}{The same as \code{label.x}, but for vertical coordinates.} \item{curved}{Specifies whether to draw curved edges, or not. This can be a logical or a numeric vector or scalar. First the vector is replicated to have the same length as the number of edges in the graph. Then it is interpreted for each edge separately. A numeric value specifies the curvature of the edge; zero curvature means straight edges, negative values means the edge bends clockwise, positive values the opposite. \code{TRUE} means curvature 0.5, \code{FALSE} means curvature zero. By default the vector specifying the curvatire is calculated via a call to the \code{\link{autocurve.edges}} function. This function makes sure that multiple edges are curved and are all visible. This parameter is ignored for loop edges. The default value is \code{FALSE}. This parameter is currently ignored by \code{\link{rglplot}}.} \item{arrow.mode}{This parameter can be used to specify for which edges should arrows be drawn. If this parameter is given by the user (in either of the three ways) then it specifies which edges will have forward, backward arrows, or both, or no arrows at all. As usual, this parameter can be a vector or a scalar value. It can be an integer or character type. If it is integer then 0 means no arrows, 1 means backward arrows, 2 is for forward arrows and 3 for both. If it is a character vector then \dQuote{<} and \dQuote{<-} specify backward, \dQuote{>} and \dQuote{->} forward arrows and \dQuote{<>} and \dQuote{<->} stands for both arrows. All other values mean no arrows, perhaps you should use \dQuote{-} or \dQuote{--} to specify no arrows. Hint: this parameter can be used as a \sQuote{cheap} solution for drawing \dQuote{mixed} graphs: graphs in which some edges are directed some are not. If you want do this, then please create a \emph{directed} graph, because as of version 0.4 the vertex pairs in the edge lists can be swapped in undirected graphs. By default, no arrows will be drawn for undirected graphs, and for directed graphs, an arrow will be drawn for each edge, according to its direction. This is not very surprising, it is the expected behavior. } \item{loop.angle}{Gives the angle in radian for plotting loop edges. See the \code{label.dist} vertex parameter to see how this is interpreted. The default value is 0. } \item{loop.angle2}{Gives the second angle in radian for plotting loop edges. This is only used in 3D, \code{loop.angle} is enough in 2D. The default value is 0. } } Other parameters: \describe{ \item{layout}{ Either a function or a numeric matrix. It specifies how the vertices will be placed on the plot. If it is a numeric matrix, then the matrix has to have one line for each vertex, specifying its coordinates. The matrix should have at least two columns, for the \code{x} and \code{y} coordinates, and it can also have third column, this will be the \code{z} coordinate for 3D plots and it is ignored for 2D plots. If a two column matrix is given for the 3D plotting function \code{\link{rglplot}} then the third column is assumed to be 1 for each vertex. If \code{layout} is a function, this function will be called with the \code{graph} as the single parameter to determine the actual coordinates. The function should return a matrix with two or three columns. For the 2D plots the third column is ignored. The default value is \code{layout.random}, ie. a function returning with 2D random placement.} \item{margin}{The amount of empty space below, over, at the left and right of the plot, it is a numeric vector of length four. Usually values between 0 and 0.5 are meaningful, but negative values are also possible, that will make the plot zoom in to a part of the graph. If it is shorter than four then it is recycled. \code{\link{rglplot}} does not support this parameter, as it can zoom in and out the graph in a more flexible way. Its default value is 0. } \item{rescale}{Logical constant, whether to rescale the coordinates to the [-1,1]x[-1,1](x[-1,1]) interval. This parameter is not implemented for \code{tkplot}. Defaults to \code{TRUE}, the layout will be rescaled. } \item{asp}{A numeric constant, it gives the \code{asp} parameter for \code{\link{plot}}, the aspect ratio. Supply 0 here if you don't want to give an aspect ratio. It is ignored by \code{tkplot} and \code{rglplot}. Defaults to 1. } \item{frame}{Boolean, whether to plot a frame around the graph. It is ignored by \code{tkplot} and \code{rglplot}. Defaults to \code{FALSE}. } \item{main}{Overall title for the main plot. The default is empty if the \code{annotate.plot} igraph option is \code{FALSE}, and the graph's \code{name} attribute otherwise. See the same argument of the base \code{plot} function. Only supported by \code{plot}.} \item{sub}{Subtitle of the main plot, the default is empty. Only supported by \code{plot}.} \item{xlab}{Title for the x axis, the default is empty if the \code{annotate.plot} igraph option is \code{FALSE}, and the number of vertices and edges, if it is \code{TRUE}. Only supported by \code{plot}.} \item{ylab}{Title for the y axis, the default is empty. Only supported by \code{plot}.} } } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{plot.igraph}}, \code{\link{tkplot}}, \code{\link{rglplot}}, \code{\link{igraph.options}}} \examples{ \dontrun{ # plotting a simple ring graph, all default parameters, except the layout g <- graph.ring(10) g$layout <- layout.circle plot(g) tkplot(g) rglplot(g) # plotting a random graph, set the parameters in the command arguments g <- barabasi.game(100) plot(g, layout=layout.fruchterman.reingold, vertex.size=4, vertex.label.dist=0.5, vertex.color="red", edge.arrow.size=0.5) # plot a random graph, different color for each component g <- erdos.renyi.game(100, 1/100) comps <- clusters(g)$membership colbar <- rainbow(max(comps)+1) V(g)$color <- colbar[comps+1] plot(g, layout=layout.fruchterman.reingold, vertex.size=5, vertex.label=NA) # plot communities in a graph g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6,11)) com <- spinglass.community(g, spins=5) V(g)$color <- com$membership+1 g <- set.graph.attribute(g, "layout", layout.kamada.kawai(g)) plot(g, vertex.label.dist=1.5) # draw a bunch of trees, fix layout igraph.options(plot.layout=layout.reingold.tilford) plot(graph.tree(20, 2)) plot(graph.tree(50, 3), vertex.size=3, vertex.label=NA) tkplot(graph.tree(50, 2, mode="undirected"), vertex.size=10, vertex.color="green") } } \keyword{graphs} igraph/man/evolver.Rd0000644000176000001440000002443212240234657014312 0ustar ripleyusers\name{revolver} \alias{evolver} \alias{revolver} \alias{evolver.d} \alias{revolver.d} \alias{revolver.ad} \alias{revolver.ade} \alias{revolver.adi} \alias{revolver.air} \alias{revolver.ar} \alias{revolver.de} \alias{revolver.di} \alias{revolver.dl} \alias{revolver.e} \alias{revolver.el} \alias{revolver.il} \alias{revolver.ir} \alias{revolver.l} \alias{revolver.r} \alias{revolver.error.d} \alias{revolver.error.ad} \alias{revolver.error.ade} \alias{revolver.error.adi} \alias{revolver.error.air} \alias{revolver.error.ar} \alias{revolver.error.de} \alias{revolver.error.di} \alias{revolver.error.dl} \alias{revolver.error.e} \alias{revolver.error.el} \alias{revolver.error.il} \alias{revolver.error.ir} \alias{revolver.error.l} \alias{revolver.error.r} \alias{revolver.d.d} \alias{revolver.p.p} \alias{revolver.ml.AD.alpha.a.beta} \alias{revolver.ml.AD.dpareto} \alias{revolver.ml.AD.dpareto.eval} \alias{revolver.ml.ad} \alias{revolver.ml.ade} \alias{revolver.ml.ADE.alpha.a.beta} \alias{revolver.ml.ADE.dpareto} \alias{revolver.ml.ADE.dpareto.eval} \alias{revolver.ml.ADE.dpareto.evalf} \alias{revolver.ml.d} \alias{revolver.ml.D.alpha} \alias{revolver.ml.D.alpha.a} \alias{revolver.ml.de} \alias{revolver.ml.DE.alpha.a} \alias{revolver.ml.df} \alias{revolver.ml.f} \alias{revolver.ml.l} \alias{revolver.probs.ad} \alias{revolver.probs.ade} \alias{revolver.probs.ADE.dpareto} \alias{revolver.probs.d} \alias{revolver.probs.de} \title{Measuring the driving force in evolving networks} \description{These functions assume a simple evolving network model and measure the functional form of a so-called \emph{attractiveness function} governing the evolution of the network. } \usage{ evolver.d (nodes, kernel, outseq = NULL, outdist = NULL, m = 1, directed = TRUE) revolver.d (graph, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.ad (graph, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) revolver.ade (graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) revolver.e (graph, cats, niter=5, st=FALSE, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.de (graph, cats, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.l (graph, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.dl (graph, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.el (graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.r (graph, window, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.ar (graph, window, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) revolver.di (graph, cats, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.adi (graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) revolver.il (graph, cats, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.ir (graph, cats, window, niter=5, sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=numeric()) revolver.air (graph, cats, window, niter=5, agebins=max(vcount(graph)/7100, 10), sd=FALSE, norm=FALSE, cites=FALSE, expected=FALSE, error=TRUE, debug=matrix(ncol=2, nrow=0)) revolver.d.d (graph, vtime = V(graph)$time, etime = E(graph)$time, niter = 5, sd = FALSE, norm = FALSE, cites = FALSE, expected = FALSE, error = TRUE, debug = matrix(ncol = 2, nrow = 0)) revolver.p.p (graph, events = get.graph.attribute(graph, "events"), vtime = V(graph)$time, etime = E(graph)$time, niter = 5, sd = FALSE, norm = FALSE, cites = FALSE, expected = FALSE, error = TRUE, debug = matrix(ncol = 2, nrow = 0)) revolver.error.d (graph, kernel) revolver.error.ad (graph, kernel) revolver.error.ade (graph, kernel, cats) revolver.error.adi (graph, kernel, cats) revolver.error.air (graph, kernel, cats, window) revolver.error.ar (graph, kernel, window) revolver.error.de (graph, kernel, cats) revolver.error.di (graph, kernel, cats) revolver.error.dl (graph, kernel) revolver.error.e (graph, kernel, cats) revolver.error.el (graph, kernel, cats) revolver.error.il (graph, kernel, cats) revolver.error.ir (graph, kernel, cats, window) revolver.error.l (graph, kernel) revolver.error.r (graph, kernel, window) revolver.ml.ade (graph, niter, cats, agebins = 300, delta = 1e-10, filter = NULL) revolver.ml.d (graph, niter, delta = 1e-10, filter = NULL) revolver.ml.de (graph, niter, cats, delta = 1e-10, filter = NULL) revolver.ml.df (graph, niter, delta = 1e-10) revolver.ml.f (graph, niter, delta = 1e-10) revolver.ml.l (graph, niter, agebins = 300, delta = 1e-10) revolver.ml.AD.alpha.a.beta (graph, alpha, a, beta, abstol = 1e-08, reltol = 1e-08, maxit = 1000, agebins = 300, filter = NULL) revolver.ml.AD.dpareto (graph, alpha, a, paralpha, parbeta, parscale, abstol = 1e-08, reltol = 1e-08, maxit = 1000, agebins = 300, filter = NULL) revolver.ml.ADE.alpha.a.beta (graph, cats, alpha, a, beta, coeffs, abstol = 1e-08, reltol = 1e-08, maxit = 1000, agebins = 300, filter = NULL) revolver.ml.ADE.dpareto (graph, cats, alpha, a, paralpha, parbeta, parscale, coeffs, abstol = 1e-08, reltol = 1e-08, maxit = 1000, agebins = 300, filter = NULL) revolver.ml.D.alpha (graph, alpha, abstol = 1e-08, reltol = 1e-08, maxit = 1000, filter = NULL) revolver.ml.D.alpha.a (graph, alpha, a, abstol = 1e-08, reltol = 1e-08, maxit = 1000, filter = NULL) revolver.ml.DE.alpha.a (graph, cats, alpha, a, coeffs, abstol = 1e-08, reltol = 1e-08, maxit = 1000, filter = NULL) revolver.ml.AD.dpareto.eval (graph, alpha, a, paralpha, parbeta, parscale, agebins = 300, filter = NULL) revolver.ml.ADE.dpareto.eval (graph, cats, alpha, a, paralpha, parbeta, parscale, coeffs, agebins = 300, filter = NULL) revolver.ml.ADE.dpareto.evalf (graph, cats, par, agebins, filter = NULL) revolver.probs.ad (graph, kernel, ntk = FALSE) revolver.probs.ade (graph, kernel, cats) revolver.probs.d (graph, kernel, ntk = FALSE) revolver.probs.de (graph, kernel, cats) revolver.probs.ADE.dpareto (graph, par, cats, gcats, agebins) } \arguments{ \item{nodes}{The number of vertices in the generated network.} \item{kernel}{The kernel function, a vector, matrix or array, depending on the number of model parameters.} \item{outseq}{The out-degree sequence, or \code{NULL} if no out-degree sequence is used.} \item{outdist}{The out-degree distribution, or \code{NULL} if all vertices have the same out-degree. This argument is ignored if the \code{outseq} argument is not \code{NULL}.} \item{m}{Numeric scalar, the out-degree of the verticec. It is ignored if at least one of \code{outseq} and \code{outdist} is not \code{NULL}.} \item{directed}{Logical scalar, whether to create a directed graph.} \item{graph}{The input graph.} \item{niter}{The number of iterations to perform.} \item{sd}{Logical scalar, whether to return the standard deviation of the estimates.} \item{norm}{Logical scalar, whether to return the normalizing factors.} \item{cites}{Logical scalar, whether to return the number of citations to the different vertex types.} \item{expected}{Logical scalar, whether to return the expected number of citations for the different vertex types.} \item{error}{Logical scalar, whether to return the error of the fit.} \item{debug}{Currently not used.} \item{agebins}{The number of bins for vertex age.} \item{cats}{The number of categories to use.} \item{window}{The width of the time window to use, measured in number of vertices.} \item{vtime}{Numeric vector, the time steps when the vertices where added to the network.} \item{etime}{Numeric vector, the time steps when the edges where added to the network.} \item{events}{A list of numeric vectors, each vector represents an event, with the participation of the listed vertices.} \item{delta}{Real scalar, the error margin that is allowed for the convergence.} \item{filter}{Logical vector, length is the number of vertices. Only vertices corresponding to \code{TRUE} entries are used in the fitting.} \item{alpha}{Starting value for the \sQuote{\code{alpha}} parameter.} \item{a}{Starting value for the \sQuote{\code{a}} parameter.} \item{paralpha}{Starting value for the \sQuote{\code{paralpha}} (Pareto alpha) parameter.} \item{parbeta}{Starting value for the \sQuote{\code{parbeta}} (Pareto beta) parameter.} \item{parscale}{Starting value for the \sQuote{\code{parscale}} (Pareto scale) parameter.} \item{abstol}{Real scalar, absolute tolerance for the ML fitting.} \item{reltol}{Real scalar, relative tolerance for the ML fitting.} \item{maxit}{Numeric scalar, the maximum number of iterations.} \item{beta}{Real scalar, starting value for the \sQuote{\code{beta}} parameter.} \item{coeffs}{Numeric vector, starting values for the coefficients.} \item{par}{Pareto parameters for the different vertex types, in a matrix.} \item{ntk}{Logical scalar, whether to return the Ntk values.} \item{gcats}{Numeric vector, the vertex types.} \item{st}{Logical scalar, whether to return the S(t) values.} } \details{ The functions should be considered as experimental, so no detailed documentation yet. Sorry. } \value{ A named list. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % seealso{} % \examples{} \keyword{graphs} igraph/man/layout.merge.Rd0000644000176000001440000000444012240234657015240 0ustar ripleyusers\name{layout.merge} \alias{layout.merge} \alias{piecewise.layout} \title{Merging graph layouts} \description{Place several graphs on the same layout} \usage{ layout.merge(graphs, layouts, method = "dla") piecewise.layout(graph, layout=layout.kamada.kawai, \dots) } \arguments{ \item{graphs}{A list of graph objects.} \item{layouts}{A list of two-column matrices.} \item{method}{Character constant giving the method to use. Right now only \code{dla} is implemented.} \item{graph}{The input graph.} \item{layout}{A function object, the layout function to use.} \item{\dots}{Additional arguments to pass to the \code{layout} layout function.} } \details{ \code{layout.merge} takes a list of graphs and a list of coordinates and places the graphs in a common layout. The method to use is chosen via the \code{method} parameter, although right now only the \code{dla} method is implemented. The \code{dla} method covers the graph with circles. Then it sorts the graphs based on the number of vertices first and places the largest graph at the center of the layout. Then the other graphs are placed in decreasing order via a DLA (diffision limited aggregation) algorithm: the graph is placed randomly on a circle far away from the center and a random walk is conducted until the graph walks into the larger graphs already placed or walks too far from the center of the layout. The \code{piecewise.layout} function disassembles the graph first into maximal connected components and calls the supplied \code{layout} function for each component separately. Finally it merges the layouts via calling \code{layout.merge}. } \value{ A matrix with two columns and as many lines as the total number of vertices in the graphs. } % \references \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{plot.igraph}}, \code{\link{tkplot}}, \code{\link{layout}}, \code{\link{graph.disjoint.union}} } \examples{ # create 20 scale-free graphs and place them in a common layout graphs <- lapply(sample(5:20, 20, replace=TRUE), barabasi.game, directed=FALSE) layouts <- lapply(graphs, layout.kamada.kawai) lay <- layout.merge(graphs, layouts) g <- graph.disjoint.union(graphs) \dontrun{plot(g, layout=lay, vertex.size=3, labels=NA, edge.color="black")} } \keyword{graphs} igraph/man/k.regular.game.Rd0000644000176000001440000000266512240234657015436 0ustar ripleyusers\name{k.regular.game} \alias{k.regular.game} \concept{Random graph model} \concept{Regular graph} \title{Create a random regular graph} \description{Generate a random graph where each vertex has the same degree.} \usage{ k.regular.game (no.of.nodes, k, directed = FALSE, multiple = FALSE) } \arguments{ \item{no.of.nodes}{Integer scalar, the number of vertices in the generated graph.} \item{k}{Integer scalar, the degree of each vertex in the graph, or the out-degree and in-degree in a directed graph.} \item{directed}{Logical scalar, whether to create a directed graph.} \item{multiple}{Logical scalar, whether multiple edges are allowed.} } \details{ This game generates a directed or undirected random graph where the degrees of vertices are equal to a predefined constant k. For undirected graphs, at least one of k and the number of vertices must be even. The game simply uses \code{\link{degree.sequence.game}} with appropriately constructed degree sequences. } \value{ An igraph graph. } % \references{} \author{ Tamas Nepusz \email{ntamas@gmail.com} } \seealso{ \code{\link{degree.sequence.game}} for a generator with prescribed degree sequence. } \examples{ ## A simple ring ring <- k.regular.game(10, 2) plot(ring) ## k-regular graphs on 10 vertices, with k=1:9 k10 <- lapply(1:9, k.regular.game, no.of.nodes=10) layout(matrix(1:9, nrow=3, byrow=TRUE)) sapply(k10, plot, vertex.label=NA) } \keyword{graphs} igraph/man/closeness.Rd0000644000176000001440000000516212251656216014626 0ustar ripleyusers\name{closeness} \alias{closeness} \alias{closeness.estimate} \concept{Closeness centrality} \title{Closeness centrality of vertices} \description{Cloness centrality measures how many steps is required to access every other vertex from a given vertex.} \usage{ closeness(graph, vids=V(graph), mode = c("out", "in", "all", "total"), weights = NULL, normalized = FALSE) closeness.estimate(graph, vids=V(graph), mode = c("out", "in", "all", "total"), cutoff, weights = NULL, normalized = FALSE) } \arguments{ \item{graph}{The graph to analyze.} \item{vids}{The vertices for which closeness will be calculated.} \item{mode}{Character string, defined the types of the paths used for measuring the distance in directed graphs. \dQuote{in} measures the paths \emph{to} a vertex, \dQuote{out} measures paths \emph{from} a vertex, \emph{all} uses undirected paths. This argument is ignored for undirected graphs.} \item{normalized}{Logical scalar, whether to calculate the normalized closeness. Normalization is performed by multiplying the raw closeness by \eqn{n-1}, where \eqn{n} is the number of vertices in the graph.} \item{cutoff}{The maximum path length to consider when calculating the betweenness. If zero or negative then there is no such limit.} \item{weights}{Optional positive weight vector for calculating weighted closeness. If the graph has a \code{weight} edge attribute, then this is used by default.} } \details{The closeness centrality of a vertex is defined by the inverse of the average length of the shortest paths to/from all the other vertices in the graph: \deqn{\frac{1}{\sum_{i\ne v} d_vi}}{1/sum( d(v,i), i != v)} If there is no (directed) path between vertex \eqn{v}{\code{v}} and \eqn{i}{\code{i}} then the total number of vertices is used in the formula instead of the path length. \code{closeness.estimate} only considers paths of length \code{cutoff} or smaller, this can be run for larger graphs, as the running time is not quadratic (if \code{cutoff} is small). If \code{cutoff} is zero or negative then the function calculates the exact closeness scores. } \value{Numeric vector with the closeness values of all the vertices in \code{v}.} \references{ Freeman, L.C. (1979). Centrality in Social Networks I: Conceptual Clarification. \emph{Social Networks}, 1, 215-239. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{betweenness}}, \code{\link{degree}}} \examples{ g <- graph.ring(10) g2 <- graph.star(10) closeness(g) closeness(g2, mode="in") closeness(g2, mode="out") closeness(g2, mode="all") } \keyword{graphs} igraph/man/decompose.graph.Rd0000644000176000001440000000273012240234657015703 0ustar ripleyusers\name{decompose.graph} \alias{decompose.graph} \concept{Graph decomposition} \concept{Graph component} \title{Decompose a graph into components} \description{Creates a separate graph for each component of a graph.} \usage{ decompose.graph(graph, mode = c("weak", "strong"), max.comps = NA, min.vertices = 0) } \arguments{ \item{graph}{The original graph.} \item{mode}{Character constant giving the type of the components, wither \code{weak} for weakly connected components or \code{strong} for strongly connected components.} \item{max.comps}{The maximum number of components to return. The first \code{max.comps} components will be returned (which hold at least \code{min.vertices} vertices, see the next parameter), the others will be ignored. Supply \code{NA} here if you don't want to limit the number of components.} \item{min.vertices}{The minimum number of vertices a component should contain in order to place it in the result list. Eg. supply 2 here to ignore isolate vertices.} } % \details{} \value{ A list of graph objects. } %\references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{is.connected}} to decide whether a graph is connected, \code{\link{clusters}} to calculate the connected components of a graph.} \examples{ # the diameter of each component in a random graph g <- erdos.renyi.game(1000, 1/1000) comps <- decompose.graph(g, min.vertices=2) sapply(comps, diameter) } \keyword{graphs} igraph/man/components.Rd0000644000176000001440000000220212240234657015004 0ustar ripleyusers\name{components} \alias{subcomponent} \concept{Subcomponent} \title{In- or out- component of a vertex} \description{Finds all vertices reachable from a given vertex, or the opposite: all vertices from which a given vertex is reachable via a directed path.} \usage{ subcomponent(graph, v, mode = c("all", "out", "in")) } \arguments{ \item{graph}{The graph to analyze.} \item{v}{The vertex to start the search from.} \item{mode}{Character string, either \dQuote{in}, \dQuote{out} or \dQuote{all}. If \dQuote{in} all vertices from which \code{v} is reachable are listed. If \dQuote{out} all vertices reachable from \code{v} are returned. If \dQuote{all} returns the union of these. It is ignored for undirected graphs.} } \details{A breadh-first search is conducted starting from vertex \code{v}.} \value{Numeric vector, the ids of the vertices in the same component as \code{v}.} % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{clusters}}} \examples{ g <- erdos.renyi.game(100, 1/200) subcomponent(g, 1, "in") subcomponent(g, 1, "out") subcomponent(g, 1, "all") } \keyword{graphs} igraph/man/sir.Rd0000644000176000001440000001004112273333334013412 0ustar ripleyusers\name{sir} \alias{median.sir} \alias{quantile.sir} \alias{time_bins} \alias{time_bins.sir} \alias{sir} \concept{SIR model} \concept{Dynamics on graph} \title{SIR model on graphs} \description{Run simulations for an SIR (susceptible-infected-recovered) model, on a graph} \usage{ sir(graph, beta, gamma, no.sim=100) \S3method{time_bins}{sir}(x, middle=TRUE) \S3method{median}{sir}(x, na.rm=FALSE) \S3method{quantile}{sir}(x, comp=c("NI", "NS", "NR"), prob, \dots) } \arguments{ \item{graph}{The graph to run the model on. If directed, then edge directions are ignored and a warning is given.} \item{beta}{Non-negative scalar. The rate of infection of an individual that is susceptible and has a single infected neighbor. The infection rate of a susceptible individual with n infected neighbors is n times beta. Formally this is the rate parameter of an exponential distribution.} \item{gamma}{Positive scalar. The rate of recovery of an infected individual. Formally, this is the rate parameter of an exponential distribution.} \item{no.sim}{Integer scalar, the number simulation runs to perform.} \item{x}{A \code{sir} object, returned by the \code{sir} function.} \item{middle}{Logical scalar, whether to return the middle of the time bins, or the boundaries.} \item{na.rm}{Logical scalar, whether to ignore \code{NA} values. \code{sir} objects do not contain any \code{NA} values currently, so this argument is effectively ignored.} \item{comp}{Character scalar. The component to calculate the quantile of. \code{NI} is infected agents, \code{NS} is susceptibles, \code{NR} stands for recovered.} \item{prob}{Numeric vector of probabilities, in [0,1], they specify the quantiles to calculate. } \item{\dots}{Additional arguments, ignored currently.} } \details{ The SIR model is a simple model from epidemiology. The individuals of the population might be in three states: susceptible, infected and recovered. Recovered people are assumed to be immune to the disease. Susceptibles become infected with a rate that depends on their number of infected neigbors. Infected people become recovered with a constant rate. The function \code{sir} simulates the model. Function \code{time_bins} bins the simulation steps, using the Freedman-Diaconis heuristics to determine the bin width. Function \code{median} and \code{quantile} calculate the median and quantiles of the results, respectively, in bins calculated with \code{time_bins}. } \value{ For \code{sir} the results are returned in an object of class \sQuote{\code{sir}}, which is a list, with one element for each simulation. Each simulation is itself a list with the following elements. They are all numeric vectors, with equal length: \itemize{ \item{times}{The times of the events.} \item{NS}{The number of susceptibles in the population, over time.} \item{NI}{The number of infected individuals in the population, over time.} \item{NR}{The number of recovered individuals in the population, over time.} } Function \code{time_bins} returns a numeric vector, the middle or the boundaries of the time bins, depending on the \code{middle} argument. \code{median} returns a list of three named numeric vectors, \code{NS}, \code{NI} and \code{NR}. The names within the vectors are created from the time bins. \code{quantile} returns the same vector as \code{median} (but only one, the one requested) if only one quantile is requested. If multiple quantiles are requested, then a list of these vectors is returned, one for each quantile. } \references{ Bailey, Norman T. J. (1975). The mathematical theory of infectious diseases and its applications (2nd ed.). London: Griffin. } \author{ Gabor Csardi \email{csardi.gabor@gmail.com}. Eric Kolaczyk (\url{http://math.bu.edu/people/kolaczyk/}) wrote the initial version in R. } \seealso{\code{\link{plot.sir}} to conveniently plot the results} \examples{ g <- erdos.renyi.game(100, 100, type="gnm") sm <- sir(g, beta=5, gamma=1) plot(sm) } \keyword{graphs} igraph/man/igraph.version.Rd0000644000176000001440000000147012251656216015564 0ustar ripleyusers\name{igraph.version} \alias{igraph.version} \title{Query igraph's version string} \description{Queries igraph's original version string. See details below.} \usage{ igraph.version() } \details{ The igraph version string is the same as the version of the R package for all realeased igraph versions. For development versions and nightly builds, they might differ however. The reason for this is, that R package version numbers are not flexible enough to cover in-between releases versions, e.g. alpha and beta versions, release candidates, etc. } \value{ A character scalar, the igraph version string. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } % \seealso{} \examples{ ## Compare to the package version packageDescription("igraph")$Version igraph.version() } \keyword{graphs} igraph/man/cocitation.Rd0000644000176000001440000000300212240234657014752 0ustar ripleyusers\name{cocitation} \alias{cocitation} \alias{bibcoupling} \concept{Cocication} \concept{Bibliographic coupling} \title{Cocitation coupling} \description{Two vertices are cocited if there is another vertex citing both of them. \code{cocitation} siply counts how many types two vertices are cocited. The bibliographic coupling of two vertices is the number of other vertices they both cite, \code{bibcoupling} calculates this. } \usage{ cocitation(graph, v=V(graph)) bibcoupling(graph, v=V(graph)) } \arguments{ \item{graph}{The graph object to analyze} \item{v}{Vertex sequence or numeric vector, the vertex ids for which the cocitation or bibliographic coupling values we want to calculate. The default is all vertices.} } \details{ \code{cocitation} calculates the cocitation counts for the vertices in the \code{v} argument and all vertices in the graph. \code{bibcoupling} calculates the bibliographic coupling for vertices in \code{v} and all vertices in the graph. Calculating the cocitation or bibliographic coupling for only one vertex costs the same amount of computation as for all vertices. This might change in the future. } \value{ A numeric matrix with \code{length(v)} lines and \code{vcount(graph)} columns. Element \code{(i,j)} contains the cocitation or bibliographic coupling for vertices \code{v[i]} and \code{j}. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.ring(10) cocitation(g) bibcoupling(g) } \keyword{graphs} igraph/man/layout.Rd0000644000176000001440000003514712325365704014154 0ustar ripleyusers\name{layout} \alias{layout} \alias{layout.auto} \alias{layout.random} \alias{layout.circle} \alias{layout.sphere} \alias{layout.fruchterman.reingold} \alias{layout.fruchterman.reingold.grid} \alias{layout.kamada.kawai} \alias{layout.spring} \alias{layout.reingold.tilford} \alias{layout.lgl} \alias{layout.svd} \alias{layout.graphopt} \alias{layout.norm} \concept{Graph layout} \title{Generate coordinates for plotting graphs} \description{Some simple and not so simple functions determining the placement of the vertices for drawing a graph.} \usage{ layout.auto(graph, dim=2, \dots) layout.random(graph, params, dim=2) layout.circle(graph, params) layout.sphere(graph, params) layout.fruchterman.reingold(graph, \dots, dim=2, params) layout.kamada.kawai(graph, \dots, dim=2, params) layout.spring(graph, \dots, params) layout.reingold.tilford(graph, \dots, params) layout.fruchterman.reingold.grid(graph, \dots, params) layout.lgl(graph, \dots, params) layout.graphopt(graph, ..., params=list()) layout.svd(graph, d=shortest.paths(graph), ...) layout.norm(layout, xmin = NULL, xmax = NULL, ymin = NULL, ymax = NULL, zmin = NULL, zmax = NULL) } \arguments{ \item{graph}{The graph to place.} \item{params}{The list of function dependent parameters.} \item{dim}{Numeric constant, either 2 or 3. Some functions are able to generate 2d and 3d layouts as well, supply this argument to change the default behavior.} \item{\dots}{Function dependent parameters, this is an alternative notation to the \code{params} argument. For \code{layout.auto} these extra parameters are simply passed to the real layout function, if one is called.} \item{d}{The matrix used for singular value decomposition. By default it is the distance matrix of the graph.} \item{layout}{A matrix with two or three columns, the layout to normalize.} \item{xmin,xmax}{The limits for the first coordinate, if one of them or both are \code{NULL} then no normalization is performed along this direction.} \item{ymin,ymax}{The limits for the second coordinate, if one of them or both are \code{NULL} then no normalization is performed along this direction.} \item{zmin,zmax}{The limits for the third coordinate, if one of them or both are \code{NULL} then no normalization is performed along this direction.} } \details{ These functions calculate the coordinates of the vertices for a graph usually based on some optimality criterion. \code{layout.auto} tries to choose an appropriate layout function for the supplied graph, and uses that to generate the layout. The current implementations works like this: \enumerate{ \item If the graph has a graph attribute called \sQuote{layout}, then this is used. If this attribute is an R function, then it is called, with the graph and any other extra arguments. \item Otherwise, if the graph has vertex attributes called \sQuote{x} and \sQuote{y}, then these are used as coordinates. If the graph has an additional \sQuote{z} vertex attribute, that is also used. \item Otherwise, if the graph is connected and has less than 100 vertices, the Kamada-Kawai layout is used, by calling \code{layout.kamada.kawai}. \item Otherwise, if the graph has less than 1000 vertices, then the Fruchterman-Reingold layout is used, by calling \code{layout.fruchterman.reingold}. \item Otherwise the DrL layout is used, \code{layout.drl} is called. } \code{layout.random} simply places the vertices randomly on a square. It has no parameters. \code{layout.circle} places the vertices on a unit circle equidistantly. It has no paramaters. \code{layout.sphere} places the vertices (approximately) uniformly on the surface of a sphere, this is thus a 3d layout. It is not clear however what \dQuote{uniformly on a sphere} means. \code{layout.fruchterman.reingold} uses a force-based algorithm proposed by Fruchterman and Reingold, see references. Parameters and their default values: \describe{ \item{niter}{Numeric, the number of iterations to perform (500).} \item{coolexp}{Numeric, the cooling exponent for the simulated annealing (3).} \item{maxdelta}{Maximum change (\code{vcount(graph)}).} \item{area}{Area parameter (\code{vcount(graph)^2}).} \item{repulserad}{Cancellation radius (\code{area}*vcount(graph)).} \item{weights}{A vector giving edge weights or \code{NULL}. If not \code{NULL} then the attraction along the edges will be multiplied by the given edge weights (\code{NULL}).} \item{minx}{If not \code{NULL}, then it must be a numeric vector that gives lower boundaries for the \sQuote{x} coordinates of the vertices. The length of the vector must match the number of vertices in the graph.} \item{maxx}{Similar to \code{minx}, but gives the upper boundaries.} \item{miny}{Similar to \code{minx}, but gives the lower boundaries of the \sQuote{y} coordinates.} \item{maxy}{Similar to \code{minx}, but gives the upper boundaries of the \sQuote{y} coordinates.} \item{minz}{Similar to \code{minx}, but gives the lower boundaries of the \sQuote{z} coordinates, if the \code{dim} argument is 3. Otherwise it is ignored.} \item{maxz}{Similar to \code{minx}, but gives the upper boundaries of the \sQuote{z} coordinates, if the \code{dim} argument is 3. Otherwise it is ignored.} \item{start}{If given, then it should be a matrix with two columns and one line for each vertex. This matrix will be used as starting positions for the algorithm. If not given, then a random starting matrix is used.} } This function was ported from the SNA package. \code{layout.kamada.kawai} is another force based algorithm. Parameters and default values: \describe{ \item{niter}{Number of iterations to perform (1000).} \item{sigma}{Sets the base standard deviation of position change proposals (vcount(graph)/4).} \item{initemp}{The initial temperature (10).} \item{coolexp}{The cooling exponent (0.99).} \item{kkconst}{Sets the Kamada-Kawai vertex attraction constant (vcount(graph)**2).} \item{minx}{If not \code{NULL}, then it must be a numeric vector that gives lower boundaries for the \sQuote{x} coordinates of the vertices. The length of the vector must match the number of vertices in the graph.} \item{maxx}{Similar to \code{minx}, but gives the upper boundaries.} \item{miny}{Similar to \code{minx}, but gives the lower boundaries of the \sQuote{y} coordinates.} \item{maxy}{Similar to \code{minx}, but gives the upper boundaries of the \sQuote{y} coordinates.} \item{minz}{Similar to \code{minx}, but gives the lower boundaries of the \sQuote{z} coordinates, if the \code{dim} argument is 3. Otherwise it is ignored.} \item{maxz}{Similar to \code{minx}, but gives the upper boundaries of the \sQuote{z} coordinates, if the \code{dim} argument is 3. Otherwise it is ignored.} \item{start}{If given, then it should be a matrix with two columns and one line for each vertex. This matrix will be used as starting positions for the algorithm. If not given, then a random starting matrix is used.} } This function performs very well for connected graphs, but it gives poor results for unconnected ones. This function was ported from the SNA package. \code{layout.spring} is a spring embedder algorithm. Parameters and default values: \describe{ \item{mass}{The vertex mass (in \sQuote{quasi-kilograms}). (Defaults to 0.1.)} \item{equil}{The equilibrium spring extension (in \sQuote{quasi-meters}). (Defaults to 1.)} \item{k}{The spring coefficient (in \sQuote{quasi-Newtons per quasi-meter}). (Defaults to 0.001.)} \item{repeqdis}{The point at which repulsion (if employed) balances out the spring extension force (in \sQuote{quasi-meters}). (Defaults to 0.1.)} \item{kfr}{The base coefficient of kinetic friction (in \sQuote{quasi-Newton quasi-kilograms}). (Defaults to 0.01.)} \item{repulse}{Should repulsion be used? (Defaults to FALSE.)} } This function was ported from the SNA package. \code{layout.reingold.tilford} generates a tree-like layout, so it is mainly for trees. Parameters and default values: \describe{ \item{root}{The id of the root vertex, defaults to 1.} \item{circular}{Logical scalar, whether to plot the tree in a circular fashion, defaults to \code{FALSE}.} \item{flip.y}{Logical scalar, whether to flip the \sQuote{y} coordinates. The default is flipping because that puts the root vertex on the top.} } \code{layout.fruchterman.reingold.grid} is similar to \code{layout.fruchterman.reingold} but repelling force is calculated only between vertices that are closer to each other than a limit, so it is faster. Patameters and default values: \describe{ \item{niter}{Numeric, the number of iterations to perform (500).} \item{maxdelta}{Maximum change for one vertex in one iteration. (The number of vertices in the graph.)} \item{area}{The area of the surface on which the vertices are placed. (The square of the number of vertices.)} \item{coolexp}{The cooling exponent of the simulated annealing (1.5).} \item{repulserad}{Cancellation radius for the repulsion (the \code{area} times the number of vertices).} \item{cellsize}{The size of the cells for the grid. When calculating the repulsion forces between vertices only vertices in the same or neighboring grid cells are taken into account (the fourth root of the number of \code{area}.} \item{start}{If given, then it should be a matrix with two columns and one line for each vertex. This matrix will be used as starting positions for the algorithm. If not given, then a random starting matrix is used.} } \code{layout.lgl} is for large connected graphs, it is similar to the layout generator of the Large Graph Layout software (\url{http://lgl.sourceforge.net/}). Parameters and default values: \describe{ \item{maxiter}{The maximum number of iterations to perform (150).} \item{maxdelta}{The maximum change for a vertex during an iteration (the number of vertices).} \item{area}{The area of the surface on which the vertices are placed (square of the number of vertices). } \item{coolexp}{The cooling exponent of the simulated annealing (1.5).} \item{repulserad}{Cancellation radius for the repulsion (the \code{area} times the number of vertices).} \item{cellsize}{The size of the cells for the grid. When calculating the repulsion forces between vertices only vertices in the same or neighboring grid cells are taken into account (the fourth root of the number of \code{area}.} \item{root}{The id of the vertex to place at the middle of the layout. The default value is -1 which means that a random vertex is selected.} } \code{layout.graphopt} is a port of the graphopt layout algorithm by Michael Schmuhl. graphopt version 0.4.1 was rewritten in C and the support for layers was removed (might be added later) and a code was a bit reorganized to avoid some unneccessary steps is the node charge (see below) is zero. graphopt uses physical analogies for defining attracting and repelling forces among the vertices and then the physical system is simulated until it reaches an equilibrium. (There is no simulated annealing or anything like that, so a stable fixed point is not guaranteed.) See also \url{http://www.schmuhl.org/graphopt/} for the original graphopt. Parameters and default values: \describe{ \item{niter}{Integer scalar, the number of iterations to perform. Should be a couple of hundred in general. If you have a large graph then you might want to only do a few iterations and then check the result. If it is not good enough you can feed it in again in the \code{start} argument. The default value is 500. } \item{charge}{The charge of the vertices, used to calculate electric repulsion. The default is 0.001.} \item{mass}{The mass of the vertices, used for the spring forces. The default is 30.} \item{spring.length}{The length of the springs, an integer number. The default value is zero.} \item{spring.constant}{The spring constant, the default value is one.} \item{max.sa.movement}{Real constant, it gives the maximum amount of movement allowed in a single step along a single axis. The default value is 5.} \item{start}{If given, then it should be a matrix with two columns and one line for each vertex. This matrix will be used as starting positions for the algorithm. If not given, then a random starting matrix is used.} } \code{layout.svd} is a currently \emph{experimental} layout function based on singular value decomposition. It does not have the usual \code{params} argument, but take a single argument, the distance matrix of the graph. This function generates the layout separately for each graph component and then merges them via \code{\link{layout.merge}}. \code{layout.norm} normalizes a layout, it linearly transforms each coordinate separately to fit into the given limits. \code{layout.drl} is another force-driven layout generator, it is suitable for quite large graphs. See \code{\link{layout.drl}} for details. } \value{All these functions return a numeric matrix with at least two columns and the same number of lines as the number of vertices.} \references{ Fruchterman, T.M.J. and Reingold, E.M. (1991). Graph Drawing by Force-directed Placement. \emph{Software - Practice and Experience}, 21(11):1129-1164. Kamada, T. and Kawai, S. (1989). An Algorithm for Drawing General Undirected Graphs. \emph{Information Processing Letters}, 31(1):7-15. Reingold, E and Tilford, J (1981). Tidier drawing of trees. \emph{IEEE Trans. on Softw. Eng.}, SE-7(2):223--228. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{layout.drl}}, \code{\link{plot.igraph}}, \code{\link{tkplot}}} \examples{ g <- graph.ring(10) layout.random(g) layout.kamada.kawai(g) # Fixing ego g <- ba.game(20, m=2) minC <- rep(-Inf, vcount(g)) maxC <- rep(Inf, vcount(g)) minC[1] <- maxC[1] <- 0 co <- layout.fruchterman.reingold(g, minx=minC, maxx=maxC, miny=minC, maxy=maxC) co[1,] \dontrun{plot(g, layout=co, vertex.size=30, edge.arrow.size=0.2, vertex.label=c("ego", rep("", vcount(g)-1)), rescale=FALSE, xlim=range(co[,1]), ylim=range(co[,2]), vertex.label.dist=1, vertex.label.color="red") axis(1) axis(2) } } \keyword{graphs} igraph/man/grg.game.Rd0000644000176000001440000000264112251656216014316 0ustar ripleyusers\name{grg.game} \alias{grg.game} \concept{Geometric random graph} \title{Geometric random graphs} \description{Generate a random graph based on the distance of random point on a unit square} \usage{ grg.game(nodes, radius, torus = FALSE, coords = FALSE) } \arguments{ \item{nodes}{The number of vertices in the graph.} \item{radius}{The radius within which the vertices will be connected by an edge.} \item{torus}{Logical constant, whether to use a torus instead of a square.} \item{coords}{Logical scalar, whether to add the positions of the vertices as vertex attributes called \sQuote{\code{x}} and \sQuote{\code{y}}.} } \details{ First a number of points are dropped on a unit square, these points correspond to the vertices of the graph to create. Two points will be connected with an undirected edge if they are closer to each other in Euclidean norm than a given radius. If the \code{torus} argument is \code{TRUE} then a unit area torus is used instead of a square. } \value{ A graph object. If \code{coords} is \code{TRUE} then with vertex attributes \sQuote{\code{x}} and \sQuote{\code{y}}. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}, first version was written by Keith Briggs (\url{http://keithbriggs.info/}).} \seealso{\code{\link{random.graph.game}}} \examples{ g <- grg.game(1000, 0.05, torus=FALSE) g2 <- grg.game(1000, 0.05, torus=TRUE) } \keyword{graphs} igraph/man/is.bipartite.Rd0000644000176000001440000000327012240234657015222 0ustar ripleyusers\name{bipartite.mapping} \alias{bipartite.mapping} \concept{Bipartite graph} \concept{Two-mode network} \title{Decide whether a graph is bipartite} \description{This function decides whether the vertices of a network can be mapped to two vertex types in a way that no vertices of the same type are connected.} \usage{ bipartite.mapping(graph) } \arguments{ \item{graph}{The input graph.} } \details{ A bipartite graph in igraph has a \sQuote{\code{type}} vertex attribute giving the two vertex types. This function simply checks whether a graph \emph{could} be bipartite. It tries to find a mapping that gives a possible division of the vertices into two classes, such that no two vertices of the same class are connected by an edge. The existence of such a mapping is equivalent of having no circuits of odd length in the graph. A graph with loop edges cannot bipartite. Note that the mapping is not necessarily unique, e.g. if the graph has at least two components, then the vertices in the separate components can be mapped independently. } \value{ A named list with two elements: \item{res}{A logical scalar, \code{TRUE} if the can be bipartite, \code{FALSE} otherwise.} \item{type}{A possibly vertex type mapping, a logical vector. If no such mapping exists, then an empty vector.} } % e % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } % \seealso{} \examples{ ## A ring has just one loop, so it is fine g <- graph.ring(10) bipartite.mapping(g) ## A star is fine, too g2 <- graph.star(10) bipartite.mapping(g2) ## A graph containing a triangle is not fine g3 <- graph.ring(10) g3 <- add.edges(g3, c(1,3)) bipartite.mapping(g3) } \keyword{graphs} igraph/man/graph.adjlist.Rd0000644000176000001440000000425112240234657015357 0ustar ripleyusers\name{Graphs from adjacency lists} \alias{graph.adjlist} \title{Create graphs from adjacency lists} \description{An adjacency list is a list of numeric vectors, containing the neighbor vertices for each vertex. This function creates an igraph graph object from such a list. } \usage{ graph.adjlist(adjlist, mode = c("out", "in", "all", "total"), duplicate = TRUE) } \arguments{ \item{adjlist}{The adjacency list. It should be consistent, i.e. the maximum throughout all vectors in the list must be less than the number of vectors (=the number of vertices in the graph). Note that the list is expected to be 0-indexed.} \item{mode}{Character scalar, it specifies whether the graph to create is undirected (\sQuote{all} or \sQuote{total}) or directed; and in the latter case, whether it contains the outgoing (\sQuote{out}) or the incoming (\sQuote{in}) neighbors of the vertices.} \item{duplicate}{Logical scalar. For undirected graphs it gives whether edges are included in the list twice. E.g. if it is \code{TRUE} then for an undirected \code{{A,B}} edge \code{graph.adjlist} expects \code{A} included in the neighbors of \code{B} and \code{B} to be included in the neighbors of \code{A}. This argument is ignored if \code{mode} is \code{out} or \code{in}. } } \details{ Adjacency lists are handy if you intend to do many (small) modifications to a graph. In this case adjacency lists are more efficient than igraph graphs. The idea is that you convert your graph to an adjacency list by \code{\link{get.adjlist}}, do your modifications to the graphs and finally create again an igraph graph by calling \code{graph.adjlist}. } \value{ An igraph graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{get.edgelist}} } \examples{ ## Directed g <- graph.ring(10, dir=TRUE) al <- get.adjlist(g, mode="out") g2 <- graph.adjlist(al) graph.isomorphic(g, g2) ## Undirected g <- graph.ring(10) al <- get.adjlist(g) g2 <- graph.adjlist(al, mode="all") graph.isomorphic(g, g2) ecount(g2) g3 <- graph.adjlist(al, mode="all", duplicate=FALSE) ecount(g3) is.multiple(g3) } \keyword{graphs} igraph/man/is.weighted.Rd0000644000176000001440000000203412240234657015034 0ustar ripleyusers\name{is.weighted} \alias{is.weighted} \concept{Weighted graphs} \title{Weighted graphs} \description{In weighted graphs, a real number is assigned to each (directed or undirected) edge.} \usage{ is.weighted(graph) } \arguments{ \item{graph}{The input graph.} } \details{ In igraph edge weights are represented via an edge attribute, called \sQuote{weight}. The \code{is.weighted} function only checks that such an attribute exists. (It does not even checks that it is a numeric edge attribute.) Edge weights are used for different purposes by the different functions. E.g. shortest path functions use it as the cost of the path; community finding methods use it as the strength of the relationship between two vertices, etc. Check the manual pages of the functions working with weighted graphs for details. } \value{A logical scalar.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \examples{ g <- graph.ring(10) get.shortest.paths(g, 8, 2) E(g)$weight <- seq_len(ecount(g)) get.shortest.paths(g, 8, 2) } \keyword{graphs} igraph/man/cliques.Rd0000644000176000001440000000706512251656216014301 0ustar ripleyusers\name{cliques} \alias{cliques} \alias{largest.cliques} \alias{maximal.cliques} \alias{maximal.cliques.count} \alias{clique.number} \concept{Clique} \concept{Maximal clique} \concept{Largest clique} \title{The functions find cliques, ie. complete subgraphs in a graph} \description{These functions find all, the largest or all the maximal cliques in an undirected graph. The size of the largest clique can also be calculated.} \usage{ cliques(graph, min=NULL, max=NULL) largest.cliques(graph) maximal.cliques(graph, min=NULL, max=NULL, subset=NULL, file=NULL) maximal.cliques.count(graph, min=NULL, max=NULL, subset=NULL) clique.number(graph) } \arguments{ \item{graph}{The input graph, directed graphs will be considered as undirected ones, multiple edges and loops are ignored.} \item{min}{Numeric constant, lower limit on the size of the cliques to find. \code{NULL} means no limit, ie. it is the same as 0.} \item{max}{Numeric constant, upper limit on the size of the cliques to find. \code{NULL} means no limit.} \item{subset}{If not \code{NULL}, then it must be a vector of vertex ids, numeric or symbolic if the graph is named. The algorithm is run from these vertices only, so only a subset of all maximal cliques is returned. See the Eppstein paper for details. This argument makes it possible to easily parallelize the finding of maximal cliques.} \item{file}{If not \code{NULL}, then it must be a file name, i.e. a character scalar. The output of the algorithm is written to this file. (If it exists, then it will be overwritten.) Each clique will be a separate line in the file, given with the numeric ids of its vertices, separated by whitespace.} } \details{ \code{cliques} find all complete subgraphs in the input graph, obeying the size limitations given in the \code{min} and \code{max} arguments. \code{largest.cliques} finds all largest cliques in the input graph. A clique is largest if there is no other clique including more vertices. \code{maximal.cliques} finds all maximal cliques in the input graph. A clique in maximal if it cannot be extended to a larger clique. The largest cliques are always maximal, but a maximal clique is not neccessarily the largest. \code{maximal.cliques.count} counts the maximal cliques. \code{clique.number} calculates the size of the largest clique(s). The current implementation of these functions searches for maximal independent vertex sets (see \code{\link{independent.vertex.sets}}) in the complementer graph. } \value{ \code{cliques}, \code{largest.cliques} and \code{clique.number} return a list containing numeric vectors of vertex ids. Each list element is a clique. \code{maximal.cliques} returns \code{NULL}, invisibly, if its \code{file} argument is not \code{NULL}. The output is written to the specified file in this case. \code{clique.number} and \code{maximal.cliques.count} return an integer scalar. } \references{ For maximal cliques the following algorithm is implemented: David Eppstein, Maarten Loffler, Darren Strash: Listing All Maximal Cliques in Sparse Graphs in Near-optimal Time. \url{http://arxiv.org/abs/1006.5440} } \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{independent.vertex.sets}}} \examples{ # this usually contains cliques of size six g <- erdos.renyi.game(100, 0.3) clique.number(g) cliques(g, min=6) largest.cliques(g) # To have a bit less maximal cliques, about 100-200 usually g <- erdos.renyi.game(100, 0.03) maximal.cliques(g) } \keyword{graphs} igraph/man/subgraph.centrality.Rd0000644000176000001440000000306612240234657016620 0ustar ripleyusers\name{subgraph.centrality} \alias{subgraph.centrality} \concept{Subgraph centrality} \title{Find subgraph centrality scores of network positions} \description{Subgraph centrality of a vertex measures the number of subgraphs a vertex participates in, weighting them according to their size.} \usage{ subgraph.centrality (graph, diag=FALSE) } \arguments{ \item{graph}{The input graph, it should be undirected, but the implementation does not check this currently. } \item{diag}{Boolean scalar, whether to include the diagonal of the adjacency matrix in the analysis. Giving \code{FALSE} here effectively eliminates the loops edges from the graph before the calculation.} } \details{ The subgraph centrality of a vertex is defined as the number of closed loops originating at the vertex, where longer loops are exponentially downweighted. Currently the calculation is performed by explicitly calculating all eigenvalues and eigenvectors of the adjacency matrix of the graph. This effectively means that the measure can only be calculated for small graphs. } \value{ A numeric vector, the subgraph centrality scores of the vertices. } \references{ Ernesto Estrada, Juan A. Rodriguez-Velazquez: Subgraph centrality in Complex Networks. \emph{Physical Review E} 71, 056103 (2005). } \author{Gabor Csardi \email{csardi.gabor@gmail.com} based on the Matlab code by Ernesto Estrada} \seealso{\code{\link{evcent}}, \code{\link{page.rank}}} \examples{ g <- ba.game(100, m=4, dir=FALSE) sc <- subgraph.centrality(g) cor(degree(g), sc) } \keyword{graphs} igraph/man/articulation.points.Rd0000644000176000001440000000233012240234657016632 0ustar ripleyusers\name{articulation.points} \alias{articulation.points} \concept{Articulation point} \title{Articulation points of a graph} \description{Articuation points or cut vertices are vertices whose removal increases the number of connected components in a graph.} \usage{ articulation.points(graph) } \arguments{ \item{graph}{The input graph. It is treated as an undirected graph, even if it is directed.} } \details{ Articuation points or cut vertices are vertices whose removal increases the number of connected components in a graph. If the original graph was connected, then the removal of a single articulation point makes it undirected. If a graph contains no articulation points, then its vertex connectivity is at least two. } \value{A numeric vector giving the vertex ids of the articulation points of the input graph.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{biconnected.components}}, \code{\link{clusters}}, \code{\link{is.connected}}, \code{\link{vertex.connectivity}}} \examples{ g <- graph.disjoint.union( graph.full(5), graph.full(5) ) clu <- clusters(g)$membership g <- add.edges(g, c(match(1, clu), match(2, clu)) ) articulation.points(g) } \keyword{graphs} igraph/man/reciprocity.Rd0000644000176000001440000000316612240234657015165 0ustar ripleyusers\name{reciprocity} \alias{reciprocity} \concept{Reciprocity} \title{Reciprocity of graphs} \description{Calculates the reciprocity of a directed graph.} \usage{ reciprocity(graph, ignore.loops = TRUE, mode = c("default", "ratio")) } \arguments{ \item{graph}{The graph object.} \item{ignore.loops}{Logical constant, whether to ignore loop edges.} \item{mode}{See below.} } \details{ The measure of reciprocity defines the proporsion of mutual connections, in a directed graph. It is most commonly defined as the probability that the opposite counterpart of a directed edge is also included in the graph. Or in adjacency matrix notation: \eqn{\sum_{ij} (A\cdot A')_{ij}}{sum(i, j, (A.*A')ij) / sum(i, j, Aij)}, where \eqn{A\cdot A'}{A.*A'} is the element-wise product of matrix \eqn{A} and its transpose. This measure is calculated if the \code{mode} argument is \code{default}. Prior to igraph version 0.6, another measure was implemented, defined as the probability of mutual connection between a vertex pair, if we know that there is a (possibly non-mutual) connection between them. In other words, (unordered) vertex pairs are classified into three groups: (1) not-connected, (2) non-reciprocaly connected, (3) reciprocally connected. The result is the size of group (3), divided by the sum of group sizes (2)+(3). This measure is calculated if \code{mode} is \code{ratio}. } \value{A numeric scalar between zero and one.} % \references{} \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- random.graph.game(20, 5/20, directed=TRUE) reciprocity(g) } \keyword{graphs} igraph/man/preference.game.Rd0000644000176000001440000000527112240234657015656 0ustar ripleyusers\name{preference.game} \alias{preference.game} \alias{asymmetric.preference.game} \title{Trait-based random generation} \description{Generation of random graphs based on different vertex types.} \usage{ preference.game(nodes, types, type.dist=rep(1, types), fixed.sizes=FALSE, pref.matrix=matrix(1, types, types), directed=FALSE, loops=FALSE) asymmetric.preference.game(nodes, types, type.dist.matrix=matrix(1,types,types), pref.matrix = matrix(1, types, types), loops=FALSE) } \arguments{ \item{nodes}{The number of vertices in the graphs.} \item{types}{The number of different vertex types.} \item{type.dist}{The distribution of the vertex types, a numeric vector of length \sQuote{types} containing non-negative numbers. The vector will be normed to obtain probabilities.} \item{fixed.sizes}{Fix the number of vertices with a given vertex type label. The \code{type.dist} argument gives the group sizes (i.e. number of vertices with the different labels) in this case.} \item{type.dist.matrix}{The joint distribution of the in- and out-vertex types.} \item{pref.matrix}{A square matrix giving the preferences of the vertex types. The matrix has \sQuote{types} rows and columns.} \item{directed}{Logical constant, whether to create a directed graph.} \item{loops}{Logical constant, whether self-loops are allowed in the graph.} } \details{ Both models generate random graphs with given vertex types. For \code{preference.game} the probability that two vertices will be connected depends on their type and is given by the \sQuote{pref.matrix} argument. This matrix should be symmetric to make sense but this is not checked. The distribution of the different vertes types is given by the \sQuote{type.dist} vector. For \code{asymmetric.preference.game} each vertex has an in-type and an out-type and a directed graph is created. The probability that a directed edge is realized from a vertex with a given out-type to a vertex with a given in-type is given in the \sQuote{pref.matrix} argument, which can be asymmetric. The joint distribution for the in- and out-types is given in the \sQuote{type.dist.matrix} argument. } \value{An igraph graph.} %\references{} \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com} for the R interface} \examples{ pf <- matrix( c(1, 0, 0, 1), nr=2) g <- preference.game(20, 2, pref.matrix=pf) \dontrun{tkplot(g, layout=layout.fruchterman.reingold)} pf <- matrix( c(0, 1, 0, 0), nr=2) g <- asymmetric.preference.game(20, 2, pref.matrix=pf) \dontrun{tkplot(g, layout=layout.circle)} } \seealso{\code{\link{establishment.game}}. \code{\link{callaway.traits.game}} } \keyword{graphs} igraph/man/structure.info.Rd0000644000176000001440000000547712240234657015632 0ustar ripleyusers\name{structure.info} \alias{vcount} \alias{ecount} \alias{neighbors} \alias{incident} \alias{is.directed} \alias{are.connected} \alias{get.edge} \alias{get.edges} \title{Gaining information about graph structure} \description{Functions for exploring the basic structure of a network: number of vertices and edges, the neighbors of a node, test whether two vertices are connected by an edge. } \usage{ vcount(graph) ecount(graph) neighbors(graph, v, mode = 1) incident(graph, v, mode=c("all", "out", "in", "total")) is.directed(graph) are.connected(graph, v1, v2) get.edge(graph, id) get.edges(graph, es) } \arguments{ \item{graph}{The graph.} \item{v}{The vertex of which the adjacent vertices or incident edges are queried.} \item{mode}{Character string, specifying the type of adjacent vertices or incident edges to list in a directed graph. If \dQuote{out}, then only outgoing edges (or their corresponding vertices) are considered; \dQuote{in} considers incoming edges; \sQuote{all} ignores edge directions. This argument is ignored for undirected graphs.} \item{v1}{The id of the first vertex. For directed graphs only edges pointing from \code{v1} to \code{v2} are searched.} \item{v2}{The id of the second vertex. For directed graphs only edges pointing from \code{v1} to \code{v2} are searched.} \item{id}{A numeric edge id.} \item{es}{An edge sequence.} } \details{ These functions provide the basic structural information of a graph. \code{vcount} gives the number of vertices in the graph. \code{ecount} gives the number of edges in the graph. \code{neighbors} gives the neighbors of a vertex. The vertices connected by multiple edges are listed as many times as the number of connecting edges. \code{incident} gives the incident edges of a vertex. \code{is.directed} gives whether the graph is directed or not. It just gives its \code{directed} attribute. \code{are.connected} decides whether there is an edge from \code{v1} to \code{v2}. \code{get.edge} returns the end points of the edge with the supplied edge id. For directed graph the source vertex comes first, for undirected graphs, the order is arbitrary. \code{get.edges} returns a matrix with the endpoints of the edges in the edge sequence argument. } \value{ \code{vcount} and \code{ecount} return integer constants. \code{neighbors} returns an integer vector. \code{is.directed} and \code{are.connected} return boolean constants. \code{get.edge} returns a numeric vector of length two. \code{get.edges} returns a two-column matrix. } %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph}}} \examples{ g <- graph.ring(10) vcount(g) ecount(g) neighbors(g, 5) incident(g, 5) are.connected(g, 1, 2) are.connected(g, 2, 4) get.edges(g, 1:6) } \keyword{graphs} igraph/man/modularity.Rd0000644000176000001440000000606512325263750015023 0ustar ripleyusers\name{modularity} \alias{modularity} \alias{mod.matrix} \alias{modularity.igraph} \concept{Modularity} \title{Modularity of a community structure of a graph} \description{This function calculates how modular is a given division of a graph into subgraphs. } \usage{ \method{modularity}{igraph}(x, membership, weights = NULL, \dots) mod.matrix (graph, membership, weights = NULL) } \arguments{ \item{x,graph}{The input graph.} \item{membership}{Numeric vector, for each vertex it gives its community. The communities are numbered from one. } \item{weights}{If not \code{NULL} then a numeric vector giving edge weights.} \item{\dots}{Additional arguments, none currently.} } \details{ \code{modularity} calculates the modularity of a graph with respect to the given \code{membership} vector. The modularity of a graph with respect to some division (or vertex types) measures how good the division is, or how separated are the different vertex types from each other. It defined as \deqn{Q=\frac{1}{2m} \sum_{i,j} (A_{ij}-\frac{k_ik_j}{2m})\delta(c_i,c_j),}{Q=1/(2m) * sum( (Aij-ki*kj/(2m) ) delta(ci,cj),i,j),} here \eqn{m} is the number of edges, \eqn{A_{ij}}{Aij} is the element of the \eqn{A} adjacency matrix in row \eqn{i} and column \eqn{j}, \eqn{k_i}{ki} is the degree of \eqn{i}, \eqn{k_j}{kj} is the degree of \eqn{j}, \eqn{c_i}{ci} is the type (or component) of \eqn{i}, \eqn{c_j}{cj} that of \eqn{j}, the sum goes over all \eqn{i} and \eqn{j} pairs of vertices, and \eqn{\delta(x,y)}{delta(x,y)} is 1 if \eqn{x=y} and 0 otherwise. If edge weights are given, then these are considered as the element of the \eqn{A} adjacency matrix, and \eqn{k_i}{ki} is the sum of weights of adjacent edges for vertex \eqn{i}. \code{mod.matrix} calculates the modularity matrix. This is a dense matrix, and it is defined as the difference of the adjacency matrix and the configuration model null model matrix. In other words element \eqn{M_{ij}}{M[i,j]} is given as \eqn{A_{ij}-d_i d_j/(2m)}{A[i,j]-d[i]d[j]/(2m)}, where \eqn{A_{ij}}{A[i,j]} is the (possibly weighted) adjacency matrix, \eqn{d_i}{d[i]} is the degree of vertex \eqn{i}, and \eqn{m} is the number of edges (or the total weights in the graph, if it is weighed). } \value{ For \code{modularity} a numeric scalar, the modularity score of the given configuration. For \code{mod.matrix} a numeic square matrix, its order is the number of vertices in the graph. } \references{ Clauset, A.; Newman, M. E. J. & Moore, C. Finding community structure in very large networks, \emph{Phyisical Review E} 2004, 70, 066111 } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{walktrap.community}}, \code{\link{edge.betweenness.community}}, \code{\link{fastgreedy.community}}, \code{\link{spinglass.community}} for various community detection methods. } \examples{ g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6, 11)) wtc <- walktrap.community(g) modularity(wtc) modularity(g, membership(wtc)) } \keyword{graphs} igraph/man/bonpow.Rd0000644000176000001440000001302612251656216014132 0ustar ripleyusers\name{bonpow} \alias{bonpow} \concept{Bonacich Power centrality} %- Also NEED an `\alias' for EACH other topic documented here. \title{ Find Bonacich Power Centrality Scores of Network Positions } \description{ \code{bonpow} takes a graph (\code{dat}) and returns the Boncich power centralities of positions (selected by \code{nodes}). The decay rate for power contributions is specified by \code{exponent} (1 by default). } \usage{ bonpow(graph, nodes=V(graph), loops=FALSE, exponent=1, rescale=FALSE, tol=1e-7, sparse=TRUE) } %- maybe also `usage' for other objects documented here. \arguments{ \item{graph}{ the input graph. } \item{nodes}{ vertex sequence indicating which vertices are to be included in the calculation. By default, all vertices are included. } \item{loops}{ boolean indicating whether or not the diagonal should be treated as valid data. Set this true if and only if the data can contain loops. \code{loops} is \code{FALSE} by default. } \item{exponent}{ exponent (decay rate) for the Bonacich power centrality score; can be negative } \item{rescale}{ if true, centrality scores are rescaled such that they sum to 1. } \item{tol}{ tolerance for near-singularities during matrix inversion (see \code{\link{solve}}) } \item{sparse}{Logical scalar, whether to use sparse matrices for the calculation. The \sQuote{Matrix} package is required for sparse matrix support} } \details{ Bonacich's power centrality measure is defined by \eqn{C_{BP}\left(\alpha,\beta\right)=\alpha\left(\mathbf{I}-\beta\mathbf{A}\right)^{-1}\mathbf{A}\mathbf{1}}{C_BP(alpha,beta)=alpha (I-beta A)^-1 A 1}, where \eqn{\beta}{beta} is an attenuation parameter (set here by \code{exponent}) and \eqn{\mathbf{A}}{A} is the graph adjacency matrix. (The coefficient \eqn{\alpha}{alpha} acts as a scaling parameter, and is set here (following Bonacich (1987)) such that the sum of squared scores is equal to the number of vertices. This allows 1 to be used as a reference value for the ``middle'' of the centrality range.) When \eqn{\beta \rightarrow 1/\lambda_{\mathbf{A}1}}{beta->1/lambda_A1} (the reciprocal of the largest eigenvalue of \eqn{\mathbf{A}}{A}), this is to within a constant multiple of the familiar eigenvector centrality score; for other values of \eqn{\beta}, the behavior of the measure is quite different. In particular, \eqn{\beta} gives positive and negative weight to even and odd walks, respectively, as can be seen from the series expansion \eqn{C_{BP}\left(\alpha,\beta\right)=\alpha \sum_{k=0}^\infty \beta^k \mathbf{A}^{k+1} \mathbf{1}}{C_BP(alpha,beta) = alpha sum( beta^k A^(k+1) 1, k in 0..infinity )} which converges so long as \eqn{|\beta| < 1/\lambda_{\mathbf{A}1}}{|beta|<1/lambda_A1}. The magnitude of \eqn{\beta}{beta} controls the influence of distant actors on ego's centrality score, with larger magnitudes indicating slower rates of decay. (High rates, hence, imply a greater sensitivity to edge effects.) Interpretively, the Bonacich power measure corresponds to the notion that the power of a vertex is recursively defined by the sum of the power of its alters. The nature of the recursion involved is then controlled by the power exponent: positive values imply that vertices become more powerful as their alters become more powerful (as occurs in cooperative relations), while negative values imply that vertices become more powerful only as their alters become \emph{weaker} (as occurs in competitive or antagonistic relations). The magnitude of the exponent indicates the tendency of the effect to decay across long walks; higher magnitudes imply slower decay. One interesting feature of this measure is its relative instability to changes in exponent magnitude (particularly in the negative case). If your theory motivates use of this measure, you should be very careful to choose a decay parameter on a non-ad hoc basis. } \value{ A vector, containing the centrality scores. } \references{ Bonacich, P. (1972). ``Factoring and Weighting Approaches to Status Scores and Clique Identification.'' \emph{Journal of Mathematical Sociology}, 2, 113-120. Bonacich, P. (1987). ``Power and Centrality: A Family of Measures.'' \emph{American Journal of Sociology}, 92, 1170-1182. } \author{ Carter T. Butts (\url{http://www.faculty.uci.edu/profile.cfm?faculty_id=5057}), ported to igraph by Gabor Csardi \email{csardi.gabor@gmail.com}} \section{Warning }{Singular adjacency matrices cause no end of headaches for this algorithm; thus, the routine may fail in certain cases. This will be fixed when I get a better algorithm. \code{bonpow} will not symmetrize your data before extracting eigenvectors; don't send this routine asymmetric matrices unless you really mean to do so.} \seealso{ \code{\link{evcent}} and \code{\link{alpha.centrality}} } \note{ This function was ported (ie. copied) from the SNA package. } \examples{ # Generate some test data from Bonacich, 1987: g.c <- graph( c(1,2,1,3,2,4,3,5), dir=FALSE) g.d <- graph( c(1,2,1,3,1,4,2,5,3,6,4,7), dir=FALSE) g.e <- graph( c(1,2,1,3,1,4,2,5,2,6,3,7,3,8,4,9,4,10), dir=FALSE) g.f <- graph( c(1,2,1,3,1,4,2,5,2,6,2,7,3,8,3,9,3,10,4,11,4,12,4,13), dir=FALSE) # Compute Bonpow scores for (e in seq(-0.5,.5, by=0.1)) { print(round(bonpow(g.c, exp=e)[c(1,2,4)], 2)) } for (e in seq(-0.4,.4, by=0.1)) { print(round(bonpow(g.d, exp=e)[c(1,2,5)], 2)) } for (e in seq(-0.4,.4, by=0.1)) { print(round(bonpow(g.e, exp=e)[c(1,2,5)], 2)) } for (e in seq(-0.4,.4, by=0.1)) { print(round(bonpow(g.f, exp=e)[c(1,2,5)], 2)) } } \keyword{graphs} igraph/man/igraph.par.Rd0000644000176000001440000001174712252357153014670 0ustar ripleyusers\name{igraph options} \alias{igraph.options} \alias{getIgraphOpt} \alias{igraph.par} \title{Parameters for the igraph package} \description{igraph has some parameters which (usually) affect the behavior of many functions. These can be set for the whole session via \code{igraph.options}. } \usage{ igraph.options(\dots) getIgraphOpt(x, default = NULL) igraph.par(parid, parvalue = NULL) } \arguments{ \item{\dots}{A list may be given as the only argument, or any number of arguments may be in the \code{name=value} form, or no argument at all may be given. See the Value and Details sections for explanation.} \item{x}{A character string holding an option name.} \item{default}{If the specified option is not set in the options list, this value is returned. This facilitates retrieving an option and checking whether it is set and setting it separately if not.} \item{parid}{The name of the parameter. See the currently used parameters below.} \item{parvalue}{The new value of the parameter. If \code{NULL} then the current value of the parameter is listed.} } \details{ From igraph version 0.6, \code{igraph.par} is deprecated. Please use the more flexible \code{igraph.options} and \code{getIgraphOpt} functions instead. The parameter values set via a call to the \code{igraph.options} function will remain in effect for the rest of the session, affecting the subsequent behaviour of the other functions of the \code{igraph} package for which the given parameters are relevant. This offers the possibility of customizing the functioning of the \code{igraph} package, for instance by insertions of appropriate calls to \code{igraph.options} in a load hook for package \pkg{igraph}. The currently used parameters in alphabetical order: \describe{ \item{add.params}{Logical scalar, whether to add model parameter to the graphs that are created by the various graph constructors. By default it is \code{TRUE}.} \item{add.vertex.names}{Logical scalar, whether to add vertex names to node level indices, like degree, betweenness scores, etc. By default it is \code{TRUE}.} \item{annotate.plot}{Logical scalar, whether to annotate igraph plots with the graph's name (\code{name} graph attribute, if present) as \code{main}, and with the number of vertices and edges as \code{xlab}. Defaults to \code{FALSE}.} \item{dend.plot.type}{The plotting function to use when plotting community structure dendrograms via \code{\link{dendPlot}}}. Possible values are \sQuote{auto} (the default), \sQuote{phylo}, \sQuote{hclust} and \sQuote{dendrogram}. See \code{\link{dendPlot}} for details. \item{edge.attr.comb}{Specifies what to do with the edge attributes if the graph is modified. The default value is \code{list(weight="sum", name="concat", "ignore")}. See \code{\link{attribute.combination}} for details on this.} \item{nexus.url}{The base URL of the default Nexus server. See \code{\link{nexus}} for details.} \item{print.edge.attributes}{Logical constant, whether to print edge attributes when printing graphs. Defaults to \code{FALSE}.} \item{print.full}{Logical scalar, whether \code{\link{print.igraph}} should show the graph structure as well, or only a summary of the graph.} \item{print.graph.attributes}{Logical constant, whether to print graph attributes when printing graphs. Defaults to \code{FALSE}.} \item{print.vertex.attributes}{Logical constant, whether to print vertex attributes when printing graphs. Defaults to \code{FALSE}.} \item{sparsematrices}{Whether to use the \code{Matrix} package for (sparse) matrices. It is recommended, if the user works with larger graphs.} \item{verbose}{Logical constant, whether igraph functions should talk more than minimal. Eg. if \code{TRUE} thne some functions will use progress bars while computing. Defaults to \code{FALSE}.} \item{vertex.attr.comb}{Specifies what to do with the vertex attributes if the graph is modified. The default value is \code{list(name="concat", "ignore")} See \code{\link{attribute.combination}} for details on this.} } } \value{ \code{igraph.options} returns a list with the updated values of the parameters. If the argument list is not empty, the returned list is invisible. For \code{getIgraphOpt}, the current value set for option \code{x}, or \code{NULL} if the option is unset. If \code{parvalue} is \code{NULL} then \code{igraph.par} returns the current value of the parameter. Otherwise the new value of the parameter is returned invisibly. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{igraph.options} is similar to \code{\link{options}} and \code{getIgraphOpt} is similar to \code{\link{getOption}}.} \examples{ oldval <- getIgraphOpt("verbose") igraph.options(verbose=TRUE) layout.kamada.kawai(graph.ring(10)) igraph.options(verbose=oldval) } \keyword{graphs} igraph/man/graph.intersection.Rd0000644000176000001440000000416712251656216016442 0ustar ripleyusers\name{graph.intersection} \alias{graph.intersection} \alias{\%s\%} \concept{Graph operators} \title{Intersection of graphs} \description{The intersection of two or more graphs are created. The graphs may have identical or overlapping vertex sets.} \usage{ graph.intersection(\dots, byname = "auto", keep.all.vertices = TRUE) } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} \item{byname}{A logical scalar, or the character scalar \code{auto}. Whether to perform the operation based on symbolic vertex names. If it is \code{auto}, that means \code{TRUE} if all graphs are named and \code{FALSE} otherwise. A warning is generated if \code{auto} and some (but not all) graphs are named.} \item{keep.all.vertices}{Logical scalar, whether to keep vertices that only appear in a subset of the input graphs.} } \details{ \code{graph.intersection} creates the intersection of two or more graphs: only edges present in all graphs will be included. The corresponding operator is \%s\%. If the \code{byname} argument is \code{TRUE} (or \code{auto} and all graphs are named), then the operation is performed on symbolic vertex names instead of the internal numeric vertex ids. \code{graph.intersection} keeps the attributes of all graphs. All graph, vertex and edge attributes are copied to the result. If an attribute is present in multiple graphs and would result a name clash, then this attribute is renamed by adding suffixes: _1, _2, etc. The \code{name} vertex attribute is treated specially if the operation is performed based on symbolic vertex names. In this case \code{name} must be present in all graphs, and it is not renamed in the result graph. An error is generated if some input graphs are directed and others are undirected. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ ## Common part of two social networks net1 <- graph.formula(D-A:B:F:G, A-C-F-A, B-E-G-B, A-B, F-G, H-F:G, H-I-J) net2 <- graph.formula(D-A:F:Y, B-A-X-F-H-Z, F-Y) str(net1 \%s\% net2) } \keyword{graphs} igraph/man/layout.bipartite.Rd0000644000176000001440000000343612251656216016131 0ustar ripleyusers\name{layout.bipartite} \alias{layout.bipartite} \concept{Graph layout} \concept{Bipartite graph} \concept{Two-mode network} \title{Simple two-row layout for bipartite graphs} \description{Minimize edge-crossings in a simple two-row (or column) layout for bipartite graphs.} \usage{ layout.bipartite (graph, types = NULL, hgap = 1, vgap = 1, maxiter = 100) } \arguments{ \item{graph}{The bipartite input graph. It should have a logical \sQuote{\code{type}} vertex attribute, or the \code{types} argument must be given.} \item{types}{A logical vector, the vertex types. If this argument is \code{NULL} (the default), then the \sQuote{\code{type}} vertex attribute is used.} \item{hgap}{Real scalar, the minimum horizontal gap between vertices in the same layer.} \item{vgap}{Real scalar, the distance between the two layers.} \item{maxiter}{Integer scalar, the maximum number of iterations in the crossing minimization stage. 100 is a reasonable default; if you feel that you have too many edge crossings, increase this.} } \details{ The layout is created by first placing the vertices in two rows, according to their types. Then the positions within the rows are optimized to minimize edge crossings, using the Sugiyama algorithm (see \code{\link{layout.sugiyama}}). } \value{A matrix with two columns and as many rows as the number of vertices in the input graph.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{layout.sugiyama}}} \examples{ # Random bipartite graph inc <- matrix(sample(0:1, 50, replace=TRUE, prob=c(2,1)), 10, 5) g <- graph.incidence(inc) plot(g, layout=layout.bipartite, vertex.color=c("green","cyan")[V(g)$type+1]) # Two columns lay <- layout.bipartite(g) plot(g, layout=lay[,2:1]) } \keyword{graphs} igraph/man/canonical.permutation.Rd0000644000176000001440000000652612240234657017131 0ustar ripleyusers\name{canonical.permutation} \alias{canonical.permutation} \concept{Canonical permutation} \concept{BLISS} \title{Canonical permutation of a graph} \description{The canonical permutation brings every isomorphic graphs into the same (labeled) graph.} \usage{ canonical.permutation(graph, sh="fm") } \arguments{ \item{graph}{The input graph, treated as undirected.} \item{sh}{Type of the heuristics to use for the BLISS algorithm. See details for possible values.} } \details{ \code{canonical.permutation} computes a permutation which brings the graph into canonical form, as defined by the BLISS algorithm. All isomorphic graphs have the same canonical form. See the paper below for the details about BLISS. This and more information is available at \url{http://www.tcs.hut.fi/Software/bliss/index.html}. The possible values for the \code{sh} argument are: \describe{ \item{\code{f}}{First non-singleton cell.} \item{\code{fl}}{First largest non-singleton cell.} \item{\code{fs}}{First smallest non-singleton cell.} \item{\code{fm}}{First maximally non-trivially connectec non-singleton cell.} \item{\code{flm}}{Largest maximally non-trivially connected non-singleton cell.} \item{\code{fsm}}{Smallest maximally non-trivially connected non-singleton cell.} } See the paper in references for details about these. } \value{ A list with the following members: \item{labeling}{The canonical parmutation which takes the input graph into canonical form. A numeric vector, the first element is the new label of vertex 0, the second element for vertex 1, etc. } \item{info}{Some information about the BLISS computation. A named list with the following members: \describe{ \item{\code{nof_nodes}}{The number of nodes in the search tree.} \item{\code{nof_leaf_nodes}}{The number of leaf nodes in the search tree.} \item{\code{nof_bad_nodes}}{Number of bad nodes.} \item{\code{nof_canupdates}}{Number of canrep updates.} \item{\code{max_level}}{Maximum level.} \item{\code{group_size}}{The size of the automorphism group of the input graph, as a string. This number is exact if igraph was compiled with the GMP library, and approximate otherwise.} } } } \references{ Tommi Junttila and Petteri Kaski: Engineering an Efficient Canonical Labeling Tool for Large and Sparse Graphs, \emph{Proceedings of the Ninth Workshop on Algorithm Engineering and Experiments and the Fourth Workshop on Analytic Algorithms and Combinatorics.} 2007. } \author{Tommi Junttila for BLISS, Gabor Csardi \email{csardi.gabor@gmail.com} for the igraph and R interfaces.} \seealso{\code{\link{permute.vertices}} to apply a permutation to a graph, \code{\link{graph.isomorphic}} for deciding graph isomorphism, possibly based on canonical labels.} \examples{ ## Calculate the canonical form of a random graph g1 <- erdos.renyi.game(10, 20, type="gnm") cp1 <- canonical.permutation(g1) cf1 <- permute.vertices(g1, cp1$labeling) ## Do the same with a random permutation of it g2 <- permute.vertices(g1, sample(vcount(g1))) cp2 <- canonical.permutation(g2) cf2 <- permute.vertices(g2, cp2$labeling) ## Check that they are the same el1 <- get.edgelist(cf1) el2 <- get.edgelist(cf2) el1 <- el1[ order(el1[,1], el1[,2]), ] el2 <- el2[ order(el2[,1], el2[,2]), ] all(el1 == el2) } \keyword{graphs} igraph/man/page.rank.Rd0000644000176000001440000001406312272060600014463 0ustar ripleyusers\name{page.rank} \alias{page.rank} \alias{page.rank.old} \concept{Page rank} \title{The Page Rank algorithm} \description{ Calculates the Google PageRank for the specified vertices.} \usage{ page.rank (graph, algo = c("prpack", "arpack", "power"), vids = V(graph), directed = TRUE, damping = 0.85, personalized = NULL, weights = NULL, options = NULL) page.rank.old (graph, vids = V(graph), directed = TRUE, niter = 1000, eps = 0.001, damping = 0.85, old = FALSE) } \arguments{ \item{graph}{The graph object. } \item{algo}{Character scalar, which implementation to use to carry out the calculation. The default is \code{"prpack"}, which uses the PRPACK library (https://github.com/dgleich/prpack). This is a new implementation in igraph version 0.7, and the suggested one, as it is the most stable and the fastest for all but small graphs. \code{"arpack"} uses the ARPACK library, the default implementation from igraph version 0.5 until version 0.7. \code{power} uses a simple implementation of the power method, this was the default in igraph before version 0.5 and is the same as calling \code{page.rank.old}.} \item{vids}{The vertices of interest.} \item{directed}{Logical, if true directed paths will be considered for directed graphs. It is ignored for undirected graphs.} \item{damping}{The damping factor (\sQuote{d} in the original paper).} \item{personalized}{Optional vector giving a probability distribution to calculate personalized PageRank. For personalized PageRank, the probability of jumping to a node when abandoning the random walk is not uniform, but it is given by this vector. The vector should contains an entry for each vertex and it will be rescaled to sum up to one.} \item{weights}{A numerical vector or \code{NULL}. This argument can be used to give edge weights for calculating the weighted PageRank of vertices. If this is \code{NULL} and the graph has a \code{weight} edge attribute then that is used. If \code{weights} is a numerical vector then it used, even if the graph has a \code{weights} edge attribute. If this is \code{NA}, then no edge weights are used (even if the graph has a \code{weight} edge attribute.} \item{options}{Either a named list, to override some ARPACK options. See \code{\link{arpack}} for details; or a named list to override the default options for the power method (if \code{algo="power"}). The default options for the power method are \code{niter=1000} and \code{eps=0.001}. This argument is ignored if the PRPACK implementation is used.} \item{niter}{The maximum number of iterations to perform.} \item{eps}{The algorithm will consider the calculation as complete if the difference of PageRank values between iterations change less than this value for every node.} \item{old}{A logical scalar, whether the old style (pre igraph 0.5) normalization to use. See details below.} } \details{ For the explanation of the PageRank algorithm, see the following webpage: \url{http://infolab.stanford.edu/~backrub/google.html}, or the following reference: Sergey Brin and Larry Page: The Anatomy of a Large-Scale Hypertextual Web Search Engine. Proceedings of the 7th World-Wide Web Conference, Brisbane, Australia, April 1998. igraph 0.5 (and later) contains two PageRank calculation implementations. The \code{page.rank} function uses ARPACK to perform the calculation, see also \code{\link{arpack}}. The \code{page.rank.old} function performs a simple power method, this is the implementation that was available under the name \code{page.rank} in pre 0.5 igraph versions. Note that \code{page.rank.old} has an argument called \code{old}. If this argument is \code{FALSE} (the default), then the proper PageRank algorithm is used, i.e. \eqn{(1-d)/n} is added to the weighted PageRank of vertices to calculate the next iteration. If this argument is \code{TRUE} then \eqn{(1-d)} is added, just like in the PageRank paper; \eqn{d} is the damping factor, and \eqn{n} is the total number of vertices. A further difference is that the old implementation does not renormalize the page rank vector after each iteration. Note that the \code{old=FALSE} method is not stable, is does not necessarily converge to a fixed point. It should be avoided for new code, it is only included for compatibility with old igraph versions. Please note that the PageRank of a given vertex depends on the PageRank of all other vertices, so even if you want to calculate the PageRank for only some of the vertices, all of them must be calculated. Requesting the PageRank for only some of the vertices does not result in any performance increase at all. Since the calculation is an iterative process, the algorithm is stopped after a given count of iterations or if the PageRank value differences between iterations are less than a predefined value. } \value{ For \code{page.rank} a named list with entries: \item{vector}{A numeric vector with the PageRank scores.} \item{value}{The eigenvalue corresponding to the eigenvector with the page rank scores. It should be always exactly one.} \item{options}{Some information about the underlying ARPACK calculation. See \code{\link{arpack}} for details. This entry is \code{NULL} if not the ARPACK implementation was used.} For \code{page.rank.old} a numeric vector of Page Rank scores. } \references{ Sergey Brin and Larry Page: The Anatomy of a Large-Scale Hypertextual Web Search Engine. Proceedings of the 7th World-Wide Web Conference, Brisbane, Australia, April 1998. } \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{Other centrality scores: \code{\link{closeness}}, \code{\link{betweenness}}, \code{\link{degree}}} \examples{ g <- random.graph.game(20, 5/20, directed=TRUE) page.rank(g)$vector g2 <- graph.star(10) page.rank(g2)$vector # Personalized PageRank g3 <- graph.ring(10) page.rank(g3)$vector reset <- seq(vcount(g3)) page.rank(g3, personalized=reset)$vector } \keyword{graphs} igraph/man/adjacent.triangles.Rd0000644000176000001440000000212512325365704016365 0ustar ripleyusers\name{adjacent.triangles} \alias{adjacent.triangles} \title{Count adjacenct triangles} \description{Count how many triangles a vertex is part of, in a graph.} \usage{ adjacent.triangles (graph, vids = V(graph)) } \arguments{ \item{graph}{The input graph. It might be directed, but edge directions are ignored.} \item{vids}{The vertices to query, all of them by default. This might be a vector of numeric ids, or a character vector of symbolic vertex names for named graphs.} } \details{ Count how many triangles a vertex is part of. } \value{ A numeric vector, the number of triangles for all vertices queried. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{\code{\link{transitivity}}} \examples{ ## A small graph kite <- graph.famous("Krackhardt_Kite") atri <- adjacent.triangles(kite) plot(kite, vertex.label=atri) ## Should match, local transitivity is the ## number of adjacent triangles divided by the number ## of adjacency triples transitivity(kite, type="local") adjacent.triangles(kite) / (degree(kite) * (degree(kite)-1)/2) } \keyword{graphs} igraph/man/graph.diversity.Rd0000644000176000001440000000337012240234657015750 0ustar ripleyusers\name{graph.diversity} \alias{graph.diversity} \concept{Entropy} \title{Graph diversity} \description{ Calculates a measure of diversity for all vertices. } \usage{ graph.diversity(graph, weights = NULL, vids = V(graph)) } \arguments{ \item{graph}{The input graph. Edge directions are ignored.} \item{weights}{\code{NULL}, or the vector of edge weights to use for the computation. If \code{NULL}, then the \sQuote{weight} attibute is used. Note that this measure is not defined for unweighted graphs.} \item{vids}{The vertex ids for which to calculate the measure.} } \details{ The diversity of a vertex is defined as the (scaled) Shannon entropy of the weights of its incident edges: \deqn{D(i)=\frac{H(i)}{\log k_i}}{D(i)=H(i)/log(k[i])} and \deqn{H(i)=-\sum_{j=1}^{k_i} p_{ij}\log p_{ij},}{% H(i) = -sum(p[i,j] log(p[i,j]), j=1..k[i]),} where \deqn{p_{ij}=\frac{w_{ij}}{\sum_{l=1}^{k_i}}V_{il},}{% p[i,j] = w[i,j] / sum(w[i,l], l=1..k[i]),} and \eqn{k_i}{k[i]} is the (total) degree of vertex \eqn{i}, \eqn{w_{ij}}{w[i,j]} is the weight of the edge(s) between vertices \eqn{i} and \eqn{j}. For vertices with degree less than two the function returns \code{NaN}. } \value{ A numeric vector, its length is the number of vertices. } \references{ Nathan Eagle, Michael Macy and Rob Claxton: Network Diversity and Economic Development, \emph{Science} \bold{328}, 1029--1031, 2010. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g1 <- erdos.renyi.game(20, 2/20) g2 <- erdos.renyi.game(20, 2/20) g3 <- erdos.renyi.game(20, 5/20) E(g1)$weight <- 1 E(g2)$weight <- runif(ecount(g2)) E(g3)$weight <- runif(ecount(g3)) graph.diversity(g1) graph.diversity(g2) graph.diversity(g3) } \keyword{graphs} igraph/man/graph.knn.Rd0000644000176000001440000000421612240234657014514 0ustar ripleyusers\name{graph.knn} \alias{graph.knn} \title{Average nearest neighbor degree} \description{Calculate the average nearest neighbor degree of the given vertices and the same quantity in the function of vertex degree} \usage{ graph.knn(graph, vids=V(graph), weights=NULL) } \arguments{ \item{graph}{The input graph. It can be directed, but it will be treated as undirected, i.e. the direction of the edges is ignored.} \item{vids}{The vertices for which the calculation is performed. Normally it includes all vertices. Note, that if not all vertices are given here, then both \sQuote{\code{knn}} and \sQuote{\code{knnk}} will be calculated based on the given vertices only.} \item{weights}{Weight vector. If the graph has a \code{weight} edge attribute, then this is used by default. If this argument is given, then vertex strength (see \code{\link{graph.strength}}) is used instead of vertex degree. But note that \code{knnk} is still given in the function of the normal vertex degree. } } \details{ Note that for zero degree vertices the answer in \sQuote{\code{knn}} is \code{NaN} (zero divided by zero), the same is true for \sQuote{\code{knnk}} if a given degree never appears in the network. } \value{ A list with two members: \item{knn}{A numeric vector giving the average nearest neighbor degree for all vertices in \code{vids}.} \item{knnk}{A numeric vector, its length is the maximum (total) vertex degree in the graph. The first element is the average nearest neighbor degree of vertices with degree one, etc. } } \references{ Alain Barrat, Marc Barthelemy, Romualdo Pastor-Satorras, Alessandro Vespignani: The architecture of complex weighted networks, Proc. Natl. Acad. Sci. USA 101, 3747 (2004) } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \examples{ # Some trivial ones g <- graph.ring(10) graph.knn(g) g2 <- graph.star(10) graph.knn(g2) # A scale-free one, try to plot 'knnk' g3 <- ba.game(1000, m=5) graph.knn(g3) # A random graph g4 <- random.graph.game(1000, p=5/1000) graph.knn(g4) # A weighted graph g5 <- graph.star(10) E(g5)$weight <- seq(ecount(g5)) graph.knn(g5) } \keyword{graphs} igraph/man/layout.drl.Rd0000644000176000001440000001104712263023733014717 0ustar ripleyusers\name{layout.drl} \alias{layout.drl} \alias{igraph.drl.default} \alias{igraph.drl.coarsen} \alias{igraph.drl.coarsest} \alias{igraph.drl.refine} \alias{igraph.drl.final} \concept{Graph layout} \title{The DrL graph layout generator} \description{ DrL is a force-directed graph layout toolbox focused on real-world large-scale graphs, developed by Shawn Martin and colleagues at Sandia National Laboratories. } \usage{ layout.drl (graph, use.seed = FALSE, seed = matrix(runif(vcount(graph) * 2), ncol = 2), options = igraph.drl.default, weights = E(graph)$weight, fixed = NULL, dim = 2) } \arguments{ \item{graph}{The input graph, in can be directed or undirected.} \item{use.seed}{Logical scalar, whether to use the coordinates given in the \code{seed} argument as a starting point.} \item{seed}{A matrix with two columns, the starting coordinates for the vertices is \code{use.seed} is \code{TRUE}. It is ignored otherwise.} \item{options}{Options for the layout generator, a named list. See details below.} \item{weights}{Optional edge weights. Supply \code{NULL} here if you want to weight edges equally. By default the \code{weight} edge attribute is used if the graph has one.} \item{fixed}{Logical vector, it can be used to fix some vertices. All vertices for which it is \code{TRUE} are kept at the coordinates supplied in the \code{seed} matrix. It is ignored it \code{NULL} or if \code{use.seed} is \code{FALSE}. } \item{dim}{Either \sQuote{2} or \sQuote{3}, it specifies whether we want a two dimensional or a three dimensional layout. Note that because of the nature of the DrL algorithm, the three dimensional layout takes significantly longer to compute.} } \details{ This function implements the force-directed DrL layout generator. The generator has the following parameters: \describe{ \item{edge.cut}{Edge cutting is done in the late stages of the algorithm in order to achieve less dense layouts. Edges are cut if there is a lot of stress on them (a large value in the objective function sum). The edge cutting parameter is a value between 0 and 1 with 0 representing no edge cutting and 1 representing maximal edge cutting. } \item{init.iterations}{Number of iterations in the first phase.} \item{init.temperature}{Start temperature, first phase.} \item{init.attraction}{Attraction, first phase.} \item{init.damping.mult}{Damping, first phase.} \item{liquid.iterations}{Number of iterations, liquid phase.} \item{liquid.temperature}{Start temperature, liquid phase.} \item{liquid.attraction}{Attraction, liquid phase.} \item{liquid.damping.mult}{Damping, liquid phase.} \item{expansion.iterations}{Number of iterations, expansion phase.} \item{expansion.temperature}{Start temperature, expansion phase.} \item{expansion.attraction}{Attraction, expansion phase.} \item{expansion.damping.mult}{Damping, expansion phase.} \item{cooldown.iterations}{Number of iterations, cooldown phase.} \item{cooldown.temperature}{Start temperature, cooldown phase.} \item{cooldown.attraction}{Attraction, cooldown phase.} \item{cooldown.damping.mult}{Damping, cooldown phase.} \item{crunch.iterations}{Number of iterations, crunch phase.} \item{crunch.temperature}{Start temperature, crunch phase.} \item{crunch.attraction}{Attraction, crunch phase.} \item{crunch.damping.mult}{Damping, crunch phase.} \item{simmer.iterations}{Number of iterations, simmer phase.} \item{simmer.temperature}{Start temperature, simmer phase.} \item{simmer.attraction}{Attraction, simmer phase.} \item{simmer.damping.mult}{Damping, simmer phase.} There are five pre-defined parameter settings as well, these are called \code{igraph.drl.default}, \code{igraph.drl.coarsen}, \code{igraph.drl.coarsest}, \code{igraph.drl.refine} and \code{igraph.drl.final}. } } \value{ A numeric matrix with two columns. } \references{ See the following technical report: Martin, S., Brown, W.M., Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph) Layout. SAND Reports, 2008. 2936: p. 1-10. } \author{Shawn Martin (\url{http://www.cs.otago.ac.nz/homepages/smartin/}) and Gabor Csardi \email{csardi.gabor@gmail.com} for the R/igraph interface and the three dimensional version.} \seealso{\code{\link{layout}} for other layout generators.} \examples{ g <- as.undirected(ba.game(100, m=1)) l <- layout.drl(g, options=list(simmer.attraction=0)) \dontrun{ plot(g, layout=l, vertex.size=3, vertex.label=NA) } } \keyword{graphs} igraph/man/igraph.vertex.shapes.Rd0000644000176000001440000002401312263024035016663 0ustar ripleyusers\name{Vertex shapes} \alias{igraph.vertex.shapes} \alias{vertex.shapes} \alias{add.vertex.shape} \alias{igraph.shape.noclip} \alias{igraph.shape.noplot} \concept{Vertex shapes} \concept{Visualization} \title{Various vertex shapes when plotting igraph graphs} \description{Starting from version 0.5.1 igraph supports different vertex shapes when plotting graphs.} \usage{ vertex.shapes (shape = NULL) add.vertex.shape (shape, clip = igraph.shape.noclip, plot = igraph.shape.noplot, parameters = list()) igraph.shape.noclip (coords, el, params, end = c("both", "from", "to")) igraph.shape.noplot (coords, v = NULL, params) } \arguments{ \item{shape}{Character scalar, name of a vertex shape. If it is \code{NULL} for \code{vertex.shapes}, then the names of all defined vertex shapes are returned.} \item{clip}{An R function object, the clipping function.} \item{plot}{An R function object, the plotting function.} \item{parameters}{Named list, additional plot/vertex/edge parameters. The element named define the new parameters, and the elements themselves define their default values. Vertex parameters should have a prefix \sQuote{\code{vertex.}}, edge parameters a prefix \sQuote{\code{edge.}}. Other general plotting parameters should have a prefix \sQuote{\code{plot.}}. See Details below.} \item{coords,el,params,end,v}{See parameters of the clipping/plotting functions below.} } \details{ In igraph a vertex shape is defined by two functions: 1) provides information about the size of the shape for clipping the edges and 2) plots the shape if requested. These functions are called \dQuote{shape functions} in the rest of this manual page. The first one is the clipping function and the second is the plotting function. The clipping function has the following arguments: \describe{ \item{coords}{A matrix with four columns, it contains the coordinates of the vertices for the edge list supplied in the \code{el} argument.} \item{el}{A matrix with two columns, the edges of which some end points will be clipped. It should have the same number of rows as \code{coords}.} \item{params}{This is a function object that can be called to query vertex/edge/plot graphical parameters. The first argument of the function is \dQuote{\code{vertex}}, \dQuote{\code{edge}} or \dQuote{\code{plot}} to decide the type of the parameter, the second is a character string giving the name of the parameter. E.g. \preformatted{ params("vertex", "size") } } \item{end}{Character string, it gives which end points will be used. Possible values are \dQuote{\code{both}}, \dQuote{\code{from}} and \dQuote{\code{to}}. If \dQuote{\code{from}} the function is expected to clip the first column in the \code{el} edge list, \dQuote{\code{to}} selects the second column, \dQuote{\code{both}} selects both.} } The clipping function should return a matrix with the same number of rows as the \code{el} arguments. If \code{end} is \code{both} then the matrix must have four columns, otherwise two. The matrix contains the modified coordinates, with the clipping applied. The plotting function has the following arguments: \describe{ \item{coords}{The coordinates of the vertices, a matrix with two columns.} \item{v}{The ids of the vertices to plot. It should match the number of rows in the \code{coords} argument.} \item{params}{The same as for the clipping function, see above.} } The return value of the plotting function is not used. \code{vertex.shapes} can be used to list the names of all installed vertex shapes, by calling it without arguments, or setting the \code{shape} argument to \code{NULL}. If a shape name is given, then the clipping and plotting functions of that shape are returned in a named list. \code{add.vertex.shape} can be used to add new vertex shapes to igraph. For this one must give the clipping and plotting functions of the new shape. It is also possible to list the plot/vertex/edge parameters, in the \code{parameters} argument, that the clipping and/or plotting functions can make use of. An example would be a generic regular polygon shape, which can have a parameter for the number of sides. \code{igraph.shape.noclip} is a very simple clipping function that the user can use in their own shape definitions. It does no clipping, the edges will be drawn exactly until the listed vertex position coordinates. \code{igraph.shape.noplot} is a very simple (and probably not very useful) plotting function, that does not plot anything. } \value{ \code{vertex.shapes} returns a character vector if the \code{shape} argument is \code{NULL}. It returns a named list with entries named \sQuote{clip} and \sQuote{plot}, both of them R functions. \code{add.vertex.shape} returns \code{TRUE}, invisibly. \code{igraph.shape.noclip} returns the appropriate columns of its \code{coords} argument. \code{igraph.shape.noplot} returns \code{NULL}, invisibly. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{igraph.plotting}}, \code{\link{plot.igraph}} } \examples{ ################################################################# # all vertex shapes, minus "raster", that might not be available shapes <- setdiff(vertex.shapes(), "") g <- graph.ring(length(shapes)) set.seed(42) plot(g, vertex.shape=shapes, vertex.label=shapes, vertex.label.dist=1, vertex.size=15, vertex.size2=15, vertex.pie=lapply(shapes, function(x) if (x=="pie") 2:6 else 0), vertex.pie.color=list(heat.colors(5))) # add new vertex shape, plot nothing with no clipping add.vertex.shape("nil") plot(g, vertex.shape="nil") ################################################################# # triangle vertex shape mytriangle <- function(coords, v=NULL, params) { vertex.color <- params("vertex", "color") if (length(vertex.color) != 1 && !is.null(v)) { vertex.color <- vertex.color[v] } vertex.size <- 1/200 * params("vertex", "size") if (length(vertex.size) != 1 && !is.null(v)) { vertex.size <- vertex.size[v] } symbols(x=coords[,1], y=coords[,2], bg=vertex.color, stars=cbind(vertex.size, vertex.size, vertex.size), add=TRUE, inches=FALSE) } # clips as a circle add.vertex.shape("triangle", clip=vertex.shapes("circle")$clip, plot=mytriangle) plot(g, vertex.shape="triangle", vertex.color=rainbow(vcount(g)), vertex.size=seq(10,20,length=vcount(g))) ################################################################# # generic star vertex shape, with a parameter for number of rays mystar <- function(coords, v=NULL, params) { vertex.color <- params("vertex", "color") if (length(vertex.color) != 1 && !is.null(v)) { vertex.color <- vertex.color[v] } vertex.size <- 1/200 * params("vertex", "size") if (length(vertex.size) != 1 && !is.null(v)) { vertex.size <- vertex.size[v] } norays <- params("vertex", "norays") if (length(norays) != 1 && !is.null(v)) { norays <- norays[v] } mapply(coords[,1], coords[,2], vertex.color, vertex.size, norays, FUN=function(x, y, bg, size, nor) { symbols(x=x, y=y, bg=bg, stars=matrix(c(size,size/2), nrow=1, ncol=nor*2), add=TRUE, inches=FALSE) }) } # no clipping, edges will be below the vertices anyway add.vertex.shape("star", clip=igraph.shape.noclip, plot=mystar, parameters=list(vertex.norays=5)) plot(g, vertex.shape="star", vertex.color=rainbow(vcount(g)), vertex.size=seq(10,20,length=vcount(g))) plot(g, vertex.shape="star", vertex.color=rainbow(vcount(g)), vertex.size=seq(10,20,length=vcount(g)), vertex.norays=rep(4:8, length=vcount(g))) ################################################################# # Pictures as vertices. # Similar musicians from last.fm, we start from an artist and # will query two levels. We will use the XML, png and jpeg packages # for this, so these must be available. Otherwise the example is # skipped loadIfYouCan <- function(pkg) suppressWarnings(do.call(require, list(pkg))) if (loadIfYouCan("XML") && loadIfYouCan("png") && loadIfYouCan("jpeg")) { url <- paste(sep="", 'http://ws.audioscrobbler.com/', '2.0/?method=artist.getinfo&artist=\%s', '&api_key=1784468ada3f544faf9172ee8b99fca3') getartist <- function(artist) { cat("Downloading from last.fm. ... ") txt <- readLines(sprintf(url, URLencode(artist))) xml <- xmlTreeParse(txt, useInternal=TRUE) img <- xpathSApply(xml, "/lfm/artist/image[@size='medium'][1]", xmlValue) if (img != "") { con <- url(img, open="rb") bin <- readBin(con, what="raw", n=10^6) close(con) if (grepl("\\\\.png$", img)) { rast <- readPNG(bin, native=TRUE) } else if (grepl("\\\\.jpe?g$", img)) { rast <- readJPEG(bin, native=TRUE) } else { rast <- as.raster(matrix()) } } else { rast <- as.raster(numeric()) } sim <- xpathSApply(xml, "/lfm/artist/similar/artist/name", xmlValue) cat("done.\\n") list(name=artist, image=rast, similar=sim) } ego <- getartist("Placebo") similar <- lapply(ego$similar, getartist) edges1 <- cbind(ego$name, ego$similar) edges2 <- lapply(similar, function(x) cbind(x$name, x$similar)) edges3 <- rbind(edges1, do.call(rbind, edges2)) edges <- edges3[ edges3[,1] \%in\% c(ego$name, ego$similar) & edges3[,2] \%in\% c(ego$name, ego$similar), ] musnet <- simplify(graph.data.frame(edges, dir=FALSE, vertices=data.frame(name=c(ego$name, ego$similar)))) str(musnet) V(musnet)$raster <- c(list(ego$image), lapply(similar, "[[", "image")) plot(musnet, layout=layout.star, vertex.shape="raster", vertex.label=V(musnet)$name, margin=.2, vertex.size=50, vertex.size2=50, vertex.label.dist=2, vertex.label.degree=0) } else { message("You need the `XML', `png' and `jpeg' packages to run this") } } \keyword{graphs} igraph/man/graph.laplacian.Rd0000644000176000001440000000362512240234657015655 0ustar ripleyusers\name{graph.laplacian} \alias{graph.laplacian} \concept{Graph Laplacian} \title{Graph Laplacian} \description{The Laplacian of a graph.} \usage{ graph.laplacian(graph, normalized=FALSE, weights=NULL, sparse=getIgraphOpt("sparsematrices")) } \arguments{ \item{graph}{The input graph.} \item{normalized}{Whether to calculate the normalized Laplacian. See definitions below.} \item{weights}{An optional vector giving edge weights for weighted Laplacian matrix. If this is \code{NULL} and the graph has an edge attribute called \code{weight}, then it will be used automatically. Set this to \code{NA} if you want the unweighted Laplacian on a graph that has a \code{weight} edge attribute.} \item{sparse}{Logical scalar, whether to return the result as a sparse matrix. The \code{Matrix} package is required for sparse matrices.} } \details{ The Laplacian Matrix of a graph is a symmetric matrix having the same number of rows and columns as the number of vertices in the graph and element (i,j) is d[i], the degree of vertex i if if i==j, -1 if i!=j and there is an edge between vertices i and j and 0 otherwise. A normalized version of the Laplacian Matrix is similar: element (i,j) is 1 if i==j, -1/sqrt(d[i] d[j]) if i!=j and there is an edge between vertices i and j and 0 otherwise. The weighted version of the Laplacian simply works with the weighted degree instead of the plain degree. I.e. (i,j) is d[i], the weighted degree of vertex i if if i==j, -w if i!=j and there is an edge between vertices i and j with weight w, and 0 otherwise. The weighted degree of a vertex is the sum of the weights of its adjacent edges. } \value{A numeric matrix.} % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.ring(10) graph.laplacian(g) graph.laplacian(g, norm=TRUE) graph.laplacian(g, norm=TRUE, sparse=FALSE) } \keyword{graphs} igraph/man/leading.eigenvector.Rd0000644000176000001440000001354712251656216016552 0ustar ripleyusers\name{leading.eigenvector.community} \alias{leading.eigenvector.community} \alias{community.le.to.membership} \concept{Community structure} \title{Community structure detecting based on the leading eigenvector of the community matrix} \description{This function tries to find densely connected subgraphs in a graph by calculating the leading non-negative eigenvector of the modularity matrix of the graph.} \usage{ leading.eigenvector.community(graph, steps = -1, weights = NULL, start = NULL, options = igraph.arpack.default, callback = NULL, extra = NULL, env = parent.frame()) community.le.to.membership(merges, steps, membership) } \arguments{ \item{graph}{The input graph. Should be undirected as the method needs a symmetric matrix.} \item{steps}{The number of steps to take, this is actually the number of tries to make a step. It is not a particularly useful parameter. For \code{community.le.to.membership} the number of merges to produce from the supplied \code{membership} vector. } \item{weights}{An optional weight vector. The \sQuote{weight} edge attribute is used if present. Supply \sQuote{\code{NA}} here if you want to ignore the \sQuote{weight} edge attribute.} \item{start}{\code{NULL}, or a numeric membership vector, giving the start configuration of the algorithm.} \item{membership}{The starting community structure on which \code{steps} merges are performed. } \item{options}{A named list to override some ARPACK options.} \item{callback}{If not \code{NULL}, then it must be callback function. This is called after each iteration, after calculating the leading eigenvector of the modularity matrix. See details below.} \item{extra}{Additional argument to supply to the callback function.} \item{env}{The environment in which the callback function is evaluated.} \item{merges}{The merge matrix, possible from the result of \code{leading.eigenvector.community}.} } \details{ The function documented in these section implements the \sQuote{leading eigenvector} method developed by Mark Newman, see the reference below. The heart of the method is the definition of the modularity matrix, \code{B}, which is \code{B=A-P}, \code{A} being the adjacency matrix of the (undirected) network, and \code{P} contains the probability that certain edges are present according to the \sQuote{configuration model}. In other words, a \code{P[i,j]} element of \code{P} is the probability that there is an edge between vertices \code{i} and \code{j} in a random network in which the degrees of all vertices are the same as in the input graph. The leading eigenvector method works by calculating the eigenvector of the modularity matrix for the largest positive eigenvalue and then separating vertices into two community based on the sign of the corresponding element in the eigenvector. If all elements in the eigenvector are of the same sign that means that the network has no underlying comuunity structure. Check Newman's paper to understand why this is a good method for detecting community structure. \code{community.le.to.memberhip} creates a membership vector from the result of \code{leading.eigenvector.community}. It takes \code{membership} and permformes \code{steps} merges, according to the supplied \code{merges} matrix. } \section{Callback functions}{ The \code{callback} argument can be used to supply a function that is called after each eigenvector calculation. The following arguments are supplied to this function: \describe{ \item{membership}{The actual membership vector, with zero-based indexing.} \item{community}{The community that the algorithm just tried to split, community numbering starts with zero here.} \item{value}{The eigenvalue belonging to the leading eigenvector the algorithm just found.} \item{vector}{The leading eigenvector the algorithm just found.} \item{multiplier}{An R function that can be used to multiple the actual modularity matrix with an arbitrary vector. Supply the vector as an argument to perform this multiplication. This function can be used with ARPACK.} \item{extra}{The \code{extra} argument that was passed to \code{leading.eigenvector.community}. } } } \value{ \code{leading.eigenvector.community} returns a named list with the following members: \item{membership}{The membership vector at the end of the algorithm, when no more splits are possible.} \item{merges}{The merges matrix starting from the state described by the \code{membership} member. This is a two-column matrix and each line describes a merge of two communities, the first line is the first merge and it creates community \sQuote{\code{N}}, \code{N} is the number of initial communities in the graph, the second line creates community \code{N+1}, etc. } \item{options}{Information about the underlying ARPACK computation, see \code{\link{arpack}} for details. } \code{community.le.to.membership} returns a named list with two components: \item{membership}{A membership vector, a numerical vector indication which vertex belongs to which community. The communities are always numbered from one.} \item{csize}{A numeric vector giving the sizes of the communities.} } \references{ MEJ Newman: Finding community structure using the eigenvectors of matrices, Physical Review E 74 036104, 2006. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{modularity}}, \code{\link{walktrap.community}}, \code{\link{edge.betweenness.community}}, \code{\link{fastgreedy.community}}, \code{\link[stats]{as.dendrogram}} } \examples{ g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6, 11)) lec <- leading.eigenvector.community(g) lec leading.eigenvector.community(g, start=membership(lec)) } \keyword{graphs} igraph/man/clusters.Rd0000644000176000001440000000503312240234657014470 0ustar ripleyusers\name{clusters} \alias{no.clusters} \alias{clusters} \alias{is.connected} \alias{cluster.distribution} \concept{Connectedness} \concept{Graph component} \title{Connected components of a graph} \description{Calculate the maximal (weakly or strongly) connected components of a graph } \usage{ is.connected(graph, mode=c("weak", "strong")) clusters(graph, mode=c("weak", "strong")) no.clusters(graph, mode=c("weak", "strong")) cluster.distribution(graph, cumulative = FALSE, mul.size = FALSE, \dots) } \arguments{ \item{graph}{The graph to analyze.} \item{mode}{Character string, either \dQuote{weak} or \dQuote{strong}. For directed graphs \dQuote{weak} implies weakly, \dQuote{strong} strongly connected components to search. It is ignored for undirected graphs.} \item{cumulative}{Logical, if TRUE the cumulative distirubution (relative frequency) is calculated.} \item{mul.size}{Logical. If TRUE the relative frequencies will be multiplied by the cluster sizes.} \item{\dots}{Additional attributes to pass to \code{cluster}, right now only \code{mode} makes sense.} } \details{ \code{is.connected} decides whether the graph is weakly or strongly connected. \code{clusters} finds the maximal (weakly or strongly) connected components of a graph. \code{no.clusters} does almost the same as \code{clusters} but returns only the number of clusters found instead of returning the actual clusters. \code{cluster.distribution} creates a histogram for the maximal connected component sizes. The weakly connected components are found by a simple breadth-first search. The strongly connected components are implemented by two consecutive depth-first searches. } \value{ For \code{is.connected} a logical constant. For \code{clusters} a named list with three components: \item{membership}{numeric vector giving the cluster id to which each vertex belongs.} \item{csize}{numeric vector giving the sizes of the clusters.} \item{no}{numeric constant, the number of clusters.} For \code{no.clusters} an integer constant is returned. For \code{cluster.distribution} a numeric vector with the relative frequencies. The length of the vector is the size of the largest component plus one. Note that (for currently unknown reasons) the first element of the vector is the number of clusters of size zero, so this is always zero. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{subcomponent}}} \examples{ g <- erdos.renyi.game(20, 1/20) clusters(g) } \keyword{graphs} igraph/man/running.mean.Rd0000644000176000001440000000156012240234657015224 0ustar ripleyusers\name{running.mean} \alias{running.mean} \title{Running mean of a time series} \description{\code{running.mean} calculates the running mean in a vector with the given bin width.} \usage{ running.mean(v, binwidth) } \arguments{ \item{v}{The numeric vector.} \item{binwidth}{Numeric constant, the size of the bin, should be meaningful, ie. smaller than the length of \code{v}. } } \details{The running mean of \code{v} is a \code{w} vector of length \code{length(v)-binwidth+1}. The first element of \code{w} id the average of the first \code{binwidth} elements of \code{v}, the second element of \code{w} is the average of elements \code{2:(binwidth+1)}, etc.} \value{ A numeric vector of length \code{length(v)-binwidth+1} } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ running.mean(1:100, 10) } \keyword{manip} igraph/man/matching.Rd0000644000176000001440000001063312240234657014420 0ustar ripleyusers\name{graph.matching} \alias{is.matching} \alias{is.maximal.matching} \alias{maximum.bipartite.matching} \concept{Matching} \concept{Maximal matching} \concept{Maximum bipartite matching} \title{Graph matching} \description{A matching in a graph means the selection of a set of edges that are pairwise non-adjacenct, i.e. they have no common incident vertices. A matching is maximal if it is not a proper subset of any other matching.} \usage{ is.matching(graph, matching, types = NULL) is.maximal.matching(graph, matching, types = NULL) maximum.bipartite.matching(graph, types = NULL, weights = NULL, eps = .Machine$double.eps) } \arguments{ \item{graph}{The input graph. It might be directed, but edge directions will be ignored.} \item{types}{Vertex types, if the graph is bipartite. By default they are taken from the \sQuote{\code{type}} vertex attribute, if present.} \item{matching}{A potential matching. An integer vector that gives the pair in the matching for each vertex. For vertices without a pair, supply \code{NA} here.} \item{weights}{Potential edge weights. If the graph has an edge attribute called \sQuote{\code{weight}}, and this argument is \code{NULL}, then the edge attribute is used automatically.} \item{eps}{A small real number used in equality tests in the weighted bipartite matching algorithm. Two real numbers are considered equal in the algorithm if their difference is smaller than \code{eps}. This is required to avoid the accumulation of numerical errors. By default it is set to the smallest \eqn{x}, such that \eqn{1+x \ne 1}{1+x != 1} holds. If you are running the algorithm with no weights, this argument is ignored.} } \details{ \code{is.matching} checks a matching vector and verifies whether its length matches the number of vertices in the given graph, its values are between zero (inclusive) and the number of vertices (inclusive), and whether there exists a corresponding edge in the graph for every matched vertex pair. For bipartite graphs, it also verifies whether the matched vertices are in different parts of the graph. \code{is.maximal.matching} checks whether a matching is maximal. A matching is maximal if and only if there exists no unmatched vertex in a graph such that one of its neighbors is also unmatched. \code{maximum.bipartite.matching} calculates a maximum matching in a bipartite graph. A matching in a bipartite graph is a partial assignment of vertices of the first kind to vertices of the second kind such that each vertex of the first kind is matched to at most one vertex of the second kind and vice versa, and matched vertices must be connected by an edge in the graph. The size (or cardinality) of a matching is the number of edges. A matching is a maximum matching if there exists no other matching with larger cardinality. For weighted graphs, a maximum matching is a matching whose edges have the largest possible total weight among all possible matchings. Maximum matchings in bipartite graphs are found by the push-relabel algorithm with greedy initialization and a global relabeling after every \eqn{n/2} steps where \eqn{n} is the number of vertices in the graph. } \value{ \code{is.matching} and \code{is.maximal.matching} return a logical scalar. \code{maximum.bipartite.matching} returns a list with components: \item{matching_size}{The size of the matching, i.e. the number of edges connecting the matched vertices.} \item{matching_weight}{The weights of the matching, if the graph was weighted. For unweighted graphs this is the same as the size of the matching.} \item{matching}{The matching itself. Numeric vertex id, or vertex names if the graph was named. Non-matched vertices are denoted by \code{NA}.} } % \references{} \author{Tamas Nepusz \email{ntamas@gmail.com}} \examples{ g <- graph.formula( a-b-c-d-e-f ) m1 <- c("b", "a", "d", "c", "f", "e") # maximal matching m2 <- c("b", "a", "d", "c", NA, NA) # non-maximal matching m3 <- c("b", "c", "d", "c", NA, NA) # not a matching is.matching(g, m1) is.matching(g, m2) is.matching(g, m3) is.maximal.matching(g, m1) is.maximal.matching(g, m2) is.maximal.matching(g, m3) V(g)$type <- c(FALSE,TRUE) str(g, v=TRUE) maximum.bipartite.matching(g) g2 <- graph.formula( a-b-c-d-e-f-g ) V(g2)$type <- rep(c(FALSE,TRUE), length=vcount(g2)) str(g2, v=TRUE) maximum.bipartite.matching(g2) } \keyword{graphs} igraph/man/sbm.game.Rd0000644000176000001440000000315312251656216014317 0ustar ripleyusers\name{sbm.game} \alias{sbm.game} \concept{Stochastic block model} \concept{Random graph model} \title{Sample stochastic block model} \description{Sampling from the stochastic block model of networks} \usage{ sbm.game (n, pref.matrix, block.sizes, directed = FALSE, loops = FALSE) } \arguments{ \item{n}{Number of vertices in the graph.} \item{pref.matrix}{The matrix giving the Bernoulli rates. This is a \eqn{K\times K}{KxK} matrix, where \eqn{K} is the number of groups. The probability of creating an edge between vertices from groups \eqn{i} and \eqn{j} is given by element \eqn{(i,j)}. For undirected graphs, this matrix must be symmetric.} \item{block.sizes}{Numeric vector giving the number of vertices in each group. The sum of the vector must match the number of vertices.} \item{directed}{Logical scalar, whether to generate a directed graph.} \item{loops}{Logical scalar, whether self-loops are allowed in the graph.} } \details{ This function samples graphs from a stochastic block model by (doing the equivalent of) Bernoulli trials for each potential edge with the probabilities given by the Bernoulli rate matrix, \code{pref.matrix}. } \value{An igraph graph.} \references{ Faust, K., & Wasserman, S. (1992a). Blockmodels: Interpretation and evaluation. \emph{Social Networks}, 14, 5--61. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{random.graph.game}}} \examples{ ## Two groups with not only few connection between groups pm <- cbind( c(.1, .001), c(.001, .05) ) g <- sbm.game(1000, pref.matrix=pm, block.sizes=c(300,700)) g } \keyword{graphs} igraph/man/iterators.Rd0000644000176000001440000002101712251656216014641 0ustar ripleyusers\name{iterators} \alias{iterators} \alias{V} \alias{E} \alias{V<-} \alias{E<-} \alias{\%--\%} \alias{\%->\%} \alias{\%<-\%} \alias{[<-.igraph.es} \alias{[.igraph.es} \alias{[[<-.igraph.es} \alias{[[.igraph.es} \alias{$<-.igraph.es} \alias{$.igraph.es} \alias{[<-.igraph.vs} \alias{[.igraph.vs} \alias{[[<-.igraph.vs} \alias{[[.igraph.vs} \alias{$<-.igraph.vs} \alias{$.igraph.vs} \alias{print.igraph.es} \alias{print.igraph.vs} \title{Vertex and edge sequences and iterators} \description{Vertex and edge sequences are central concepts of igraph.} \usage{ V(graph) E(graph, P=NULL, path=NULL, directed=TRUE) } \arguments{ \item{graph}{A graph object.} \item{P}{Numeric vector for selecting edges by giving their end points. See details below.} \item{path}{Numeric vector, this is for selecting all edges along a path. See also details below.} \item{directed}{Logcal constant, can be supplied only if either \code{P} or \code{path} is also present and gives whether the pairs or the path are directed or not.} } \details{ One often needs to perform an operation on a subset of vertices of edges in a graph. A vertex sequence is simply a vector containing vertex ids, but it has a special class attribute which makes it possible to perform graph specific operations on it, like selecting a subset of the vertices based on some vertex attributes. A vertex sequence is created by \code{V(g)} this selects are vertices in increasing vertex id order. A vertex sequence can be indexed by a numeric vector, and a subset of all vertices can be selected. Vertex sequences provide powerful operations for dealing with vertex attributes. A vertex sequence can be indexed with the \sQuote{\code{$}} operator to select (or modify) the attributes of a subset of vertices. A vertex sequence can be indexed by a logical expression, and this expression may contain the names of the vertex attributes and ordinary variables as well. The return value of such a construct (ie. a vertex sequence indexed by a logical expression) is another vertex sequence containing only vertices from the original sequence for which the expression evaluates to TRUE. Let us see an example to make everything clear. We assign random numbers between 1 and 100 to the vertices, and select those vertices for which the number is less than 50. We set the color of these vertices to red. \preformatted{ g <- graph.ring(10) V(g)$number <- sample(1:100, vcount(g), replace=TRUE) V(g)$color <- "grey" V(g)[ number < 50 ]$color <- "red" plot(g, layout=layout.circle, vertex.color=V(g)$color, vertex.label=V(g)$number) } There is a similar notation for edges. \code{E(g)} selects all edges from the \sQuote{\code{g}} graph. Edge sequences can be also indexed with logical expressions containing edge attributes: \preformatted{ g <- graph.ring(10) E(g)$weight <- runif(ecount(g)) E(g)$width <- 1 E(g)[ weight >= 0.5 ]$width <- 3 plot(g, layout=layout.circle, edge.width=E(g)$width, edge.color="black") } It is important to note that, whenever we use iterators to assign new attribute values, the new values are recycled. So in the following example half of the vertices will be black, the other half red, in an alternated way. \preformatted{ g <- graph.ring(10) V(g)$color <- c("black", "red") plot(g, layout=layout.circle) } For the recycling, the standard R rules apply and a warning is given if the number of items to replace is not a multiple of the replacement length. E.g. the following code gives a warning, because we set the attribute for three vertices, but supply only two values: \preformatted{ g <- graph.tree(10) V(g)$color <- "grey" V(g)[1:3]$color <- c("green", "blue") } If a new vertex/edge attribute is created with an assignment, but only a subset of of vertices are specified, then the rest is set to \code{NA} if the new values are in a vector and to \code{NULL} if they are a list. Try the following: \preformatted{ V(g)[5]$foo <- "foo" V(g)$foo V(g)[5]$bar <- list(bar="bar") V(g)$bar } There are some special functions which are only defined in the indexing expressions of vertex and edge sequences. For vertex sequences these are: \code{nei}, \code{inc}, \code{from} and \code{to}, \code{innei} and \code{outnei}. (The \code{adj} special function is an alias for \code{inc}, for compatibility reasons.) \code{nei} has a mandatory and an optional argument, the first is another vertex sequence, the second is a mode argument similar to that of the \code{\link{neighbors}} function. \code{nei} returns a logical vector of the same length as the indexed vertex sequence and evaluates to \code{TRUE} for those vertices only which have a neighbor vertex in the vertex sequence supplied as a parameter. Thus for selecting all neighbors of vertices 1 and 2 one can write: \preformatted{ V(g) [ nei( 1:2 ) ] } The mode argument (just like for \code{\link{neighbors}}) gives the type of the neighbors to be included, it is interpreted only in directed graphs, and defaults to all types of neighbors. See the example below. \code{innei(v)} is a shorthand for the \sQuote{incoming} neighbors (\code{nei(v, mode="in")}), and \code{outnei(v)} is a shorthand for the \sQuote{outgoing} neighbors (\code{nei(v,mode="out")}). \code{inc} takes an edge sequence as an argument and returns \code{TRUE} for vertices which have at least one incident edge in it. \code{from} and \code{to} are similar to \code{inc} but only edges originated at (\code{from}) or pointing to (\code{to}) are taken into account. For edge sequences the special functions are: \code{inc}, \code{from}, \code{to}, \code{\%--\%}, \code{\%->\%} and \code{\%<-\%}. \code{inc} takes a vertex sequence as an argument and returns \code{TRUE} for edges which have an incident vertex in it. \code{from} and \code{to} are similar to \code{inc}, but only vertices at the source (\code{from}) or target (\code{to}) of the edge. The \code{\%--\%} operator selects edges connecting two vertex sequences, the direction of the edges is ignored. The \code{\%->\%} is different only for directed graphs and only edges pointing from the left hand side argument to the right hand side argument are selected. \code{\%<-\%} is exactly the opposite, it selects edges pointing from the right hand side to the left hand side. \code{E} has two optional arguments: \code{P} and \code{path}. If given \code{P} can be used to select edges based on their end points, eg. \code{E(g, P=c(1,2))} selects edge \code{1->2}. \code{path} can be used to select all edges along a path. The path should be given with the visited vertex ids in the appropriate order. See also the examples below. } \note{ A note about the performance of the \code{V} and \code{E} functions, and the selection of edges and vertices. Since all selectors are evaluated as logical vectors on all vertices/edges, their performance is bad on large graphs. (Time complexity is proportional to the total number of vertices/edges.) We suggest using the \code{\link{neighbors}}, \code{\link{incident}} functions and simple R vector operations for manipulating vertex/edge sequences in large graphs. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ # mean degree of vertices in the largest cluster in a random graph g <- erdos.renyi.game(100, 2/100) c <- clusters(g) vsl <- which(which.max(c$csize)==c$membership) mean(degree(g, vsl)) # set the color of these vertices to red, others greens V(g)$color <- "green" V(g)[vsl]$color <- "red" \dontrun{plot(g, vertex.size=3, labels=NA, vertex.color="a:color", layout=layout.fruchterman.reingold)} # the longest geodesic within the largest cluster long <- numeric() for (v in vsl) { paths <- get.shortest.paths(g, from=v, to=vsl) fl <- paths[[ which.max(sapply(paths, length)) ]] if (length(fl) > length(long)) { long <- fl } } # the mode argument of the nei() function g <- graph( c(1,2, 2,3, 2,4, 4,2) ) V(g)[ nei( c(2,4) ) ] V(g)[ nei( c(2,4), "in") ] V(g)[ nei( c(2,4), "out") ] # operators for edge sequences g <- barabasi.game(100, power=0.3) E(g) [ 1:3 \%--\% 2:6 ] E(g) [ 1:5 \%->\% 1:6 ] E(g) [ 1:3 \%<-\% 2:6 ] # the edges along the diameter g <- barabasi.game(100, directed=FALSE) d <- get.diameter(g) E(g, path=d) # performance for large graphs is bad largeg <- graph.lattice(c(1000, 100)) system.time(E(largeg)[inc(1)]) system.time(incident(largeg, 1)) } \keyword{graphs} igraph/man/dendPlot.igraphHRG.Rd0000644000176000001440000000737412240234657016221 0ustar ripleyusers\name{dendPlot.igraphHRG} \alias{dendPlot.igraphHRG} \concept{Dendrograms} \concept{Hierarchical random graphs} \title{HRG dendrogram plot} \description{Plot a hierarchical random graph as a dendrogram.} \usage{ \method{dendPlot}{igraphHRG}(x, mode = getIgraphOpt("dend.plot.type"), \dots) } \arguments{ \item{x}{An \code{igraphHRG}, a hierarchical random graph, as returned by the \code{\link{hrg.fit}} function.} \item{mode}{Which dendrogram plotting function to use. See details below.} \item{\dots}{Additional arguments to supply to the dendrogram plotting function.} } \details{ \code{dendPlot} supports three different plotting functions, selected via the \code{mode} argument. By default the plotting function is taken from the \code{dend.plot.type} igraph option, and it has for possible values: \itemize{ \item \code{auto} Choose automatically between the plotting functions. As \code{plot.phylo} is the most sophisticated, that is choosen, whenever the \code{ape} package is available. Otherwise \code{plot.hclust} is used. \item \code{phylo} Use \code{plot.phylo} from the \code{ape} package. \item \code{hclust} Use \code{plot.hclust} from the \code{stats} package. \item \code{dendrogram} Use \code{plot.dendrogram} from the \code{stats} package. } The different plotting functions take different sets of arguments. When using \code{plot.phylo} (\code{mode="phylo"}), we have the following syntax: \preformatted{ dendPlot(x, mode="phylo", colbar = rainbow(11, start=0.7, end=0.1), edge.color = NULL, use.edge.length = FALSE, \dots) } The extra arguments not documented above: \itemize{ \item \code{colbar} Color bar for the edges. \item \code{edge.color} Edge colors. If \code{NULL}, then the \code{colbar} argument is used. \item \code{use.edge.length} Passed to \code{plot.phylo}. \item \code{dots} Attitional arguments to pass to \code{plot.phylo}. } The syntax for \code{plot.hclust} (\code{mode="hclust"}): \preformatted{ dendPlot(x, mode="hclust", rect = 0, colbar = rainbow(rect), hang = 0.01, ann = FALSE, main = "", sub = "", xlab = "", ylab = "", \dots) } The extra arguments not documented above: \itemize{ \item \code{rect} A numeric scalar, the number of groups to mark on the dendrogram. The dendrogram is cut into exactly \code{rect} groups and they are marked via the \code{rect.hclust} command. Set this to zero if you don't want to mark any groups. \item \code{colbar} The colors of the rectanges that mark the vertex groups via the \code{rect} argument. \item \code{hang} Where to put the leaf nodes, this corresponds to the \code{hang} argument of \code{plot.hclust}. \item \code{ann} Whether to annotate the plot, the \code{ann} argument of \code{plot.hclust}. \item \code{main} The main title of the plot, the \code{main} argument of \code{plot.hclust}. \item \code{sub} The sub-title of the plot, the \code{sub} argument of \code{plot.hclust}. \item \code{xlab} The label on the horizontal axis, passed to \code{plot.hclust}. \item \code{ylab} The label on the vertical axis, passed to \code{plot.hclust}. \item \code{dots} Attitional arguments to pass to \code{plot.hclust}. } The syntax for \code{plot.dendrogram} (\code{mode="dendrogram"}): \preformatted{ dendPlot(x, \dots) } The extra arguments are simply passed to \code{as.dendrogram}. } \value{ Returns whatever the return value was from the plotting function, \code{plot.phylo}, \code{plot.dendrogram} or \code{plot.hclust}. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.full(5) + graph.full(5) hrg <- hrg.fit(g) dendPlot(hrg) } \keyword{graphs} igraph/man/maximum.cardinality.search.Rd0000644000176000001440000000355212240234657020053 0ustar ripleyusers\name{maximum.cardinality.search} \alias{maximum.cardinality.search} \concept{maximum cardinality search} \concept{graph decomposition} \concept{chordal graph} \title{Maximum cardinality search} \description{Maximum cardinality search is a simple ordering a vertices that is useful in determining the chordality of a graph.} \usage{ maximum.cardinality.search(graph) } \arguments{ \item{graph}{The input graph. It may be directed, but edge directions are ignored, as the algorithm is defined for undirected graphs.} } \details{ Maximum cardinality search visits the vertices in such an order that every time the vertex with the most already visited neighbors is visited. Ties are broken randomly. The algorithm provides a simple basis for deciding whether a graph is chordal, see References below, and also \code{\link{is.chordal}}. } \value{A list with two components: \item{alpha}{Numeric vector. The vertices ordered according to the maximum cardinality search.} \item{alpham1}{Numeric vector. The inverse of \code{alpha}.} } \references{ Robert E Tarjan and Mihalis Yannakakis. (1984). Simple linear-time algorithms to test chordality of graphs, test acyclicity of hypergraphs, and selectively reduce acyclic hypergraphs. \emph{SIAM Journal of Computation} 13, 566--579.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{is.chordal}} } \examples{ ## The examples from the Tarjan-Yannakakis paper g1 <- graph.formula(A-B:C:I, B-A:C:D, C-A:B:E:H, D-B:E:F, E-C:D:F:H, F-D:E:G, G-F:H, H-C:E:G:I, I-A:H) maximum.cardinality.search(g1) is.chordal(g1, fillin=TRUE) g2 <- graph.formula(A-B:E, B-A:E:F:D, C-E:D:G, D-B:F:E:C:G, E-A:B:C:D:F, F-B:D:E, G-C:D:H:I, H-G:I:J, I-G:H:J, J-H:I) maximum.cardinality.search(g2) is.chordal(g2, fillin=TRUE) } \keyword{graphs} igraph/man/label.propagation.community.Rd0000644000176000001440000000564012240234657020254 0ustar ripleyusers\name{label.propagation.community} \alias{label.propagation.community} \concept{Community structure} \title{Finding communities based on propagating labels} \description{ This is a fast, nearly linear time algorithm for detecting community structure in networks. In works by labeling the vertices with unique labels and then updating the labels by majority voting in the neighborhood of the vertex. } \usage{ label.propagation.community (graph, weights = NULL, initial = NULL, fixed = NULL) } \arguments{ \item{graph}{The input graph, should be undirected to make sense.} \item{weights}{An optional weight vector. It should contain a positive weight for all the edges. The \sQuote{weight} edge attribute is used if present. Supply \sQuote{\code{NA}} here if you want to ignore the \sQuote{weight} edge attribute.} \item{initial}{The initial state. If \code{NULL}, every vertex will have a different label at the beginning. Otherwise it must be a vector with an entry for each vertex. Non-negative values denote different labels, negative entries denote vertices without labels.} \item{fixed}{Logical vector denoting which labels are fixed. Of course this makes sense only if you provided an initial state, otherwise this element will be ignored. Also note that vertices without labels cannot be fixed.} } \details{ This function implements the community detection method described in: Raghavan, U.N. and Albert, R. and Kumara, S.: Near linear time algorithm to detect community structures in large-scale networks. Phys Rev E 76, 036106. (2007). This version extends the original method by the ability to take edge weights into consideration and also by allowing some labels to be fixed. From the abstract of the paper: \dQuote{In our algorithm every node is initialized with a unique label and at every step each node adopts the label that most of its neighbors currently have. In this iterative process densely connected groups of nodes form a consensus on a unique label to form communities.} } \value{ \code{label.propagation.community} returns a \code{\link{communities}} object, please see the \code{\link{communities}} manual page for details. } \references{ Raghavan, U.N. and Albert, R. and Kumara, S.: Near linear time algorithm to detect community structures in large-scale networks. \emph{Phys Rev E} 76, 036106. (2007) } \author{ Tamas Nepusz \email{ntamas@gmail.com} for the C implementation, Gabor Csardi \email{csardi.gabor@gmail.com} for this manual page. } \seealso{ \code{\link{communities}} for extracting the actual results. \code{\link{fastgreedy.community}}, \code{\link{walktrap.community}} and \code{\link{spinglass.community}} for other community detection methods.} \examples{ g <- erdos.renyi.game(10, 5/10) \%du\% erdos.renyi.game(9, 5/9) g <- add.edges(g, c(1, 12)) label.propagation.community(g) } \keyword{graphs} igraph/man/permute.vertices.Rd0000644000176000001440000000270112240234657016127 0ustar ripleyusers\name{permute.vertices} \alias{permute.vertices} \concept{Permutation} \title{Permute the vertices of a graph} \description{Create a new graph, by permuting vertex ids.} \usage{ permute.vertices(graph, permutation) } \arguments{ \item{graph}{The input graph, it can directed or undirected.} \item{permutation}{A numeric vector giving the permutation to apply. The first element is the new id of vertex 1, etc. Every number between one and \code{vcount(graph)} must appear exactly once.} } \details{ This function creates a new graph from the input graph by permuting its vertices according to the specified mapping. Call this function with the output of \code{\link{canonical.permutation}} to create the canonical form of a graph. \code{permute.vertices} keeps all graph, vertex and edge attributes of the graph. } \value{A new graph object.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{canonical.permutation}}} \examples{ # Random permutation of a random graph g <- random.graph.game(20, 50, type="gnm") g2 <- permute.vertices(g, sample(vcount(g))) graph.isomorphic(g, g2) # Permutation keeps all attributes g$name <- "Random graph, Gnm, 20, 50" V(g)$name <- letters[1:vcount(g)] E(g)$weight <- sample(1:5, ecount(g), replace=TRUE) g2 <- permute.vertices(g, sample(vcount(g))) graph.isomorphic(g, g2) g2$name V(g2)$name E(g2)$weight all(sort(E(g2)$weight) == sort(E(g)$weight)) } \keyword{graphs} igraph/man/aaa-igraph-package.Rd0000644000176000001440000001621112251656216016210 0ustar ripleyusers\docType{package} \name{igraph-package} \alias{igraph-package} \alias{igraph} \title{The igraph package} \description{igraph is a library and R package for network analysis.} \section{Introduction}{ The main goals of the igraph library is to provide a set of data types and functions for 1) pain-free implementation of graph algorithms, 2) fast handling of large graphs, with millions of vertices and edges, 3) allowing rapid prototyping via high level languages like R. } \section{Igraph graphs}{ Igraph graphs have a class \sQuote{\code{igraph}}. They are printed to the screen in a special format, here is an example, a ring graph created using \code{\link{graph.ring}}: \preformatted{ IGRAPH U--- 10 10 -- Ring graph + attr: name (g/c), mutual (g/x), circular (g/x) } The \sQuote{\code{IGRAPH}} denotes that this is an igraph graph. Then come four bits that denote the kind of the graph: the first is \sQuote{\code{U}} for undirected and \sQuote{\code{D}} for directed graphs. The second is \sQuote{\code{N}} for named graph (i.e. if the graph has the \sQuote{\code{name}} vertex attribute set). The third is \sQuote{\code{W}} for weighted graphs (i.e. if the \sQuote{\code{weight}} edge attribute is set). The fourth is \sQuote{\code{B}} for bipartite graphs (i.e. if the \sQuote{\code{type}} vertex attribute is set). Then comes two numbers, the number of vertices and the number of edges in the graph, and after a double dash, the name of the graph (the \sQuote{\code{name}} graph attribute) is printed if present. The second line is optional and it contains all the attributes of the graph. This graph has a \sQuote{\code{name}} graph attribute, of type character, and two other graph attributes called \sQuote{\code{mutual}} and \sQuote{\code{circular}}, of a complex type. A complex type is simply anything that is not numeric or character. See the documentation of \code{\link{print.igraph}} for details. If you want to see the edges of the graph as well, then use the \code{\link{str.igraph}} function, it is of course enough to type \code{str} instead of \code{str.igraph}: \preformatted{ > str(g) IGRAPH U--- 10 10 -- Ring graph + attr: name (g/c), mutual (g/x), circular (g/x) + edges: [1] 1-- 2 2-- 3 3-- 4 4-- 5 5-- 6 6-- 7 7-- 8 8-- 9 9--10 1--10 } } \section{Creating graphs}{ There are many functions in igraph for creating graphs, both deterministic and stochastic; stochastic graph constructors are called \sQuote{games}. To create small graphs with a given structure probably the \code{\link{graph.formula}} function is easiest. It uses R's formula interface, its manual page contains many examples. Another option is \code{\link{graph}}, which takes numeric vertex ids directly. \code{\link{graph.atlas}} creates graph from the Graph Atlas, \code{\link{graph.famous}} can create some special graphs. To create graphs from field data, \code{\link{graph.edgelist}}, \code{\link{graph.data.frame}} and \code{\link{graph.adjacency}} are probably the best choices. The igraph package includes some classic random graphs like the Erdos-Renyi GNP and GNM graphs (\code{\link{erdos.renyi.game}}) and some recent popular models, like preferential attachment (\code{\link{barabasi.game}}) and the small-world model (\code{\link{watts.strogatz.game}}). } \section{Vertex and edge IDs}{ Vertices and edges have numerical vertex ids in igraph. Vertex ids are always consecutive and they start with one. I.e. for a graph with \eqn{n} vertices the vertex ids are between \eqn{1} and \eqn{n}. If some operation changes the number of vertices in the graphs, e.g. a subgraph is created via \code{\link{induced.subgraph}}, then the vertices are renumbered to satisfty this criteria. The same is true for the edges as well, edge ids are always between one and \eqn{m}, the total number of edges in the graph. It is often desirable to follow vertices along a number of graph operations, and vertex ids don't allow this because of the renumbering. The solution is to assign attributes to the vertices. These are kept by all operations, if possible. See more about attributes in the next section. } \section{Attributes}{ In igraph it is possible to assign attributes to the vertices or edges of a graph, or to the graph itself. igraph provides flexible constructs for selecting a set of vertices or edges based on their attribute values, see \code{\link{get.vertex.attribute}} and \code{\link{iterators}} for details. Some vertex/edge/graph attributes are treated specially. One of them is the \sQuote{name} attribute. This is used for printing the graph instead of the numerical ids, if it exists. Vertex names can also be used to specify a vector or set of vertices, in all igraph functions. E.g. \code{\link{degree}} has a \code{v} argument that gives the vertices for which the degree is calculated. This argument can be given as a character vector of vertex names. Edges can also have a \sQuote{name} attribute, and this is treated specially as well. Just like for vertices, edges can also be selected based on their names, e.g. in the \code{\link{delete.edges}} and other functions. We note here, that vertex names can also be used to select edges. The form \sQuote{\code{from|to}}, where \sQuote{\code{from}} and \sQuote{\code{to}} are vertex names, select a single, possibly directed, edge going from \sQuote{\code{from}} to \sQuote{\code{to}}. The two forms can also be mixed in the same edge selector. Other attributes define visualization parameters, see \code{\link{igraph.plotting}} for details. Attribute values can be set to any R object, but note that storing the graph in some file formats might result the loss of complex attribute values. All attribute values are preserved if you use \code{\link[base]{save}} and \code{\link[base]{load}} to store/retrieve your graphs. } \section{Visualization}{ igraph provides three different ways for visualization. The first is the \code{\link{plot.igraph}} function. (Actually you don't need to write \code{plot.igraph}, \code{plot} is enough. This function uses regular R graphics and can be used with any R device. The second function is \code{\link{tkplot}}, which uses a Tk GUI for basic interactive graph manipulation. (Tk is quite resource hungry, so don't try this for very large graphs.) The third way requires the \code{rgl} package and uses OpenGL. See the \code{\link{rglplot}} function for the details. Make sure you read \code{\link{igraph.plotting}} before you start plotting your graphs. } \section{File formats}{ igraph can handle various graph file formats, usually both for reading and writing. We suggest that you use the GraphML file format for your graphs, except if the graphs are too big. For big graphs a simpler format is recommended. See \code{\link{read.graph}} and \code{\link{write.graph}} for details. } \section{Further information}{ The igraph homepage is at \url{http://igraph.org}. See especially the documentation section. Join the igraph-help mailing list if you have questions or comments. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} igraph/man/biconnected.components.Rd0000644000176000001440000000347212240234657017272 0ustar ripleyusers\name{biconnected.components} \alias{biconnected.components} \concept{Biconnected component} \title{Biconnected components} \description{Finding the biconnected components of a graph} \usage{ biconnected.components(graph) } \arguments{ \item{graph}{The input graph. It is treated as an undirected graph, even if it is directed.} } \details{ A graph is biconnected if the removal of any single vertex (and its adjacent edges) does not disconnect it. A biconnected component of a graph is a maximal biconnected subgraph of it. The biconnected components of a graph can be given by the partition of its edges: every edge is a member of exactly one biconnected component. Note that this is not true for vertices: the same vertex can be part of many biconnected components. } \value{ A named list with three components: \item{no}{Numeric scalar, an integer giving the number of biconnected components in the graph.} \item{tree_edges}{The components themselves, a list of numeric vectors. Each vector is a set of edge ids giving the edges in a biconnected component. These edges define a spanning tree of the component.} \item{component_edges}{A list of numeric vectors. It gives all edges in the components.} \item{components}{A list of numeric vectors, the vertices of the components.} \item{articulation_points}{The articulation points of the graph. See \code{\link{articulation.points}}.} } %\references \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{articulation.points}}, \code{\link{clusters}}, \code{\link{is.connected}}, \code{\link{vertex.connectivity}}} \examples{ g <- graph.disjoint.union( graph.full(5), graph.full(5) ) clu <- clusters(g)$membership g <- add.edges(g, c(which(clu==1), which(clu==2))) bc <- biconnected.components(g) } \keyword{graphs} igraph/man/SCGintro.Rd0000644000176000001440000001756712251656216014334 0ustar ripleyusers\name{Spectral coarse graining} \alias{SCG} \concept{Spectral coarse graining} \title{Spectral Coarse Graining} \description{ Functions to perform the Spectral Coarse Graining (SCG) of matrices and graphs. } \section{Introduction}{ The SCG functions provide a framework, called Spectral Coarse Graining (SCG), for reducing large graphs while preserving their \emph{spectral-related features}, that is features closely related with the eigenvalues and eigenvectors of a graph matrix (which for now can be the adjacency, the stochastic, or the Laplacian matrix). Common examples of such features comprise the first-passage-time of random walkers on Markovian graphs, thermodynamic properties of lattice models in statistical physics (e.g. Ising model), and the epidemic threshold of epidemic network models (SIR and SIS models). SCG differs from traditional clustering schemes by producing a \emph{coarse-grained graph} (not just a partition of the vertices), representative of the original one. As shown in [1], Principal Component Analysis can be viewed as a particular SCG, called \emph{exact SCG}, where the matrix to be coarse-grained is the covariance matrix of some data set. SCG should be of interest to practitioners of various fields dealing with problems where matrix eigenpairs play an important role, as for instance is the case of dynamical processes on networks. } \section{SCG in brief}{ The main idea of SCG is to operate on a matrix a shrinkage operation specifically designed to preserve some of the matrix eigenpairs while not altering other important matrix features (such as its structure). Mathematically, this idea was expressed as follows. Consider a (complex) \eqn{n \times n}{n x n} matrix \eqn{M} and form the product \deqn{\widetilde{M} = LMR^*,}{M'=LMR*,} where \eqn{\tilde n < n}{n' < n} and \eqn{L,R \in \mathbf{C}^{\tilde n \times n}}{L,R in C[n'xn]} are such that \eqn{LR^*=I_{\tilde n}}{LR*=I[n']} (\eqn{R^*}{R*} denotes the conjugate transpose of \eqn{R}). Under these assumptions, it can be shown that \eqn{P=R^*L}{P=R*L} is an \eqn{\tilde n}{n'}-rank projector and that, if \eqn{(\lambda, v)}{(lambda, v)} is a (right) eigenpair of \eqn{M} (i.e. \eqn{Mv=\lambda v}{Mv=lambda v}) and \eqn{P} is orthogonal, there exists an eigenvalue \eqn{\tilde \lambda}{lambda'} of \eqn{\widetilde M}{M'} such that \deqn{|\lambda-\tilde{\lambda}| \le \textrm{const} \Vert e_P(v)\Vert [1 + O(\Vert e_P(v)\Vert^2)],}{% |lambda-lambda'| <= const ||e[P](v)|| [1+O(||e[P](v)||^2)],} where \eqn{\Vert e_P(v)\Vert = \Vert v-Pv\Vert}{||e[P](v)||=||v-Pv||}. Hence, if \eqn{P} (or equivalently \eqn{L}, \eqn{R}) is chosen so as to make \eqn{\Vert e_P(v)\Vert}{||e[P](v)||} as small as possible, one can preserve to any desired level the original eigenvalue \eqn{\lambda}{lambda} in the coarse-grained matrix \eqn{\widetilde M}{M'}; under extra assumptions on \eqn{M}, this result can be generalized to eigenvectors [1]. This leads to the following generic definition of a SCG problem. Given \eqn{M\in\mathbf{C}^{n\times n}}{M in C[nxn]} and \eqn{(\lambda,v)}{(lambda,v)} a (right) eigenpair of \eqn{M} to be preserved by the coarse graining, the problem is to find a projector \eqn{\widehat{P}}{P'} solving \deqn{\min_{P\in \Omega} \Vert e_{P}(v)\Vert,}{% min(||e[P](v)||, p in Omega),} where \eqn{\Omega}{Omega} is a set of projectors in \eqn{\mathbf{C}^{n\times n}}{C[nxn]} described by some ad hoc constraints \eqn{c_{1},\dots c_{r}}{c[1], ..., c[r]} (e.g. \eqn{c_{1}:P\in\mathbf{R}^{n\times n}, c_{2}:P=P^{t}, c_{3}:P_{ij}\ge 0}{c[1]: P in R[nxn], c[2]: P=t(P), c[3]: P[i,j] >= 0}, etc). Choosing pertinent constraints to solve the SCG problem is of great importance in applications. For instance, in the absence of constraints the SCG problem is solved trivially by \eqn{\widehat{P}=vv^*}{P'=vv*} (\eqn{v} is assumed normalized). We have designed a particular constraint, called \emph{homogeneous mixing}, which ensures that vertices belonging to the same group are merged consistently from a physical point of view (see [1] for details). Under this constraint the SCG problem reduces to finding the partition of \eqn{\{1,\dots,n\}}{1, ..., n} (labeling the original vertices) minimizing \deqn{\Vert e_P(v)\Vert^{2} = \sum_{\alpha=1}^{\tilde{n}}\sum_{i\in\alpha}[v(i)-(Pv)(i)]^{2},}{% ||e[P](v)||^2 = sum([v(i)-(Pv)(i)]^2; alpha=1,...,n', i in alpha),} where \eqn{\alpha}{alpha} denotes a group (i.e. a block) in a partition of \eqn{ \{1,\dots,n\} }{{1, ..., n}}, and \eqn{|\alpha|}{|alpha|} is the number of elements in \eqn{\alpha}{alpha}. If \eqn{M} is symmetric or stochastic, for instance, then it may be desirable (or mandatory) to choose \eqn{L}, \eqn{R} so that \eqn{\widetilde M}{M'} is symmetric or stochastic as well. This \emph{structural constraint} has led to the construction of particular semi-projectors for symmetric [1], stochastic [3] and Laplacian [2] matrices, that are made available. In short, the coarse graining of matrices and graphs involves: \enumerate{ \item Retrieving a matrix or a graph matrix \eqn{M} from the problem. \item Computing the eigenpairs of \eqn{M} to be preserved in the coarse-grained graph or matrix. \item Setting some problem-specific constraints (e.g. dimension of the coarse-grained object). \item Solving the constrained SCG problem, that is finding \eqn{\widehat{P}}{P'}. \item Computing from \eqn{\widehat{P}}{P'} two semi-projectors \eqn{\widehat{L}}{L'} and \eqn{\widehat{R}}{R'} (e.g. following the method proposed in [1]). \item Working out the product \eqn{\widetilde{M}=\widehat{L} M \widehat{R}^*}{M'=L'MR'*} and, if needed, defining from \eqn{\widetilde{M}}{M'} a coarse-grained graph. } } \section{Functions for performing SCG}{ The main function is the \dQuote{all-in-one} \code{\link{scg}}. This function handles all the steps involved in the Spectral Coarse Graining (SCG) of some particular matrices and graphs as described above and in reference [1]. In more details, \code{\link{scg}} computes some prescribed eigenpairs of a matrix or a graph matrix (for now adjacency, Laplacian and stochastic matrices are available), works out an optimal partition to preserve the eigenpairs, and finally outputs a coarse-grained matrix or graph along with other useful information. These steps can also be carried out independently: (1) Use \code{\link{get.adjacency}}, \code{\link{graph.laplacian}} or \code{\link{get.stochastic}} to compute a matrix \eqn{M}. (2) Work out some prescribed eigenpairs of \eqn{M} e.g. by means of \code{eigen} or \code{arpack}. (3) Invoke one the four algorithms of the function \code{\link{scgGrouping}} to get a partition that will preserve the eigenpairs in the coarse-grained matrix. (4) Compute the semi-projectors \eqn{L} and \eqn{R} using \code{\link{scgSemiProjectors}} and from there the coarse-grained matrix \eqn{\widetilde{M}=LMR^*}{M'=LMR*}. If necessary, construct a coarse-grained graph from \eqn{\widetilde{M}}{M'} (e.g. as in [1]). } \references{ D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, Shrinking Matrices while Preserving their Eigenpairs with Application to the Spectral Coarse Graining of Graphs. Submitted to \emph{SIAM Journal on Matrix Analysis and Applications}, 2008. \url{http://people.epfl.ch/david.morton} D. Gfeller, and P. De Los Rios, Spectral Coarse Graining and Synchronization in Oscillator Networks. \emph{Physical Review Letters}, \bold{100}(17), 2008. \url{http://arxiv.org/abs/0708.2055} D. Gfeller, and P. De Los Rios, Spectral Coarse Graining of Complex Networks, \emph{Physical Review Letters}, \bold{99}(3), 2007. \url{http://arxiv.org/abs/0706.0812} } \author{David Morton de Lachapelle, \url{http://people.epfl.ch/david.morton}.} \keyword{graphs} igraph/man/dendPlot.Rd0000644000176000001440000000110612240234657014372 0ustar ripleyusers\name{dendPlot} \alias{dendPlot} \concept{Dendrograms} \title{Plot dendrograms} \description{ This is generic function that can plot various objects as dendrograms. } % \usage % \arguments{} \details{ Currently the function is defined for \code{communities} (see \code{\link{dendPlot.communities}}) and \code{igraphHRG} (see \code{\link{dendPlot.igraphHRG}}) objects. } % \value{} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ karate <- graph.famous("Zachary") fc <- fastgreedy.community(karate) dendPlot(fc) } \keyword{graphs} igraph/man/rewire.Rd0000644000176000001440000000221612240234657014121 0ustar ripleyusers\name{rewire} \alias{rewire} \title{Graph rewiring} \description{Randomly rewires a graph while preserving the degree distribution.} \usage{ rewire(graph, mode = c("simple", "loops"), niter = 100) } \arguments{ \item{graph}{The graph to be rewired.} \item{mode}{The rewiring algorithm to be used. It can be one of the following: \code{simple}: simple rewiring algorithm which chooses two arbitrary edges in each step (namely (a,b) and (c,d)) and substitutes them with (a,d) and (c,b) if they don't yet exist, avoiding the creation or destruction of loop edges or \code{loops}: similar to \code{simple} but allows the creation and destruction of loop edges.} \item{niter}{Number of rewiring trials to perform.} } \details{ This function generates a new graph based on the original one by randomly rewiring edges while preserving the original graph's degree distribution. } \value{A new graph object.} % \references{} \author{Tamas Nepusz \email{ntamas@gmail.com} and Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{degree.sequence.game}}} \examples{ g <- graph.ring(20) g2 <- rewire(g, niter=3) } \keyword{graphs} igraph/man/line.graph.Rd0000644000176000001440000000231412240234657014652 0ustar ripleyusers\name{line.graph} \alias{line.graph} \concept{Line graph} \title{Line graph of a graph} \description{This function calculates the line graph of another graph.} \usage{ line.graph(graph) } \arguments{ \item{graph}{The input graph, it can be directed or undirected.} } \details{ The line graph \code{L(G)} of a \code{G} undirected graph is defined as follows. \code{L(G)} has one vertex for each edge in \code{G} and two vertices in \code{L(G)} are connected by an edge if their corresponding edges share an end point. The line graph \code{L(G)} of a \code{G} directed graph is slightly different, \code{L(G)} has one vertex for each edge in \code{G} and two vertices in \code{L(G)} are connected by a directed edge if the target of the first vertex's corresponding edge is the same as the source of the second vertex's corresponding edge. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}, the first version of the C code was written by Vincent Matossian.} % \seealso{} \examples{ # generate the first De-Bruijn graphs g <- graph.full(2, directed=TRUE, loops=TRUE) line.graph(g) line.graph(line.graph(g)) line.graph(line.graph(line.graph(g))) } \keyword{graphs} igraph/man/graph.bfs.Rd0000644000176000001440000001112312240234657014473 0ustar ripleyusers\name{graph.bfs} \alias{graph.bfs} \title{Breadth-first search} \description{Breadth-first search is an algorithm to traverse a graph. We start from a root vertex and spread along every edge \dQuote{simultaneously}. } \usage{ graph.bfs (graph, root, neimode = c("out", "in", "all", "total"), unreachable = TRUE, restricted = NULL, order = TRUE, rank = FALSE, father = FALSE, pred = FALSE, succ = FALSE, dist = FALSE, callback = NULL, extra = NULL, rho = parent.frame()) } \arguments{ \item{graph}{The input graph.} \item{root}{Numeric vector, usually of length one. The root vertex, or root vertices to start the search from.} \item{neimode}{For directed graphs specifies the type of edges to follow. \sQuote{out} follows outgoing, \sQuote{in} incoming edges. \sQuote{all} ignores edge directions completely. \sQuote{total} is a synonym for \sQuote{all}. This argument is ignored for undirected graphs.} \item{unreachable}{Logical scalar, whether the search should visit the vertices that are unreachable from the given root vertex (or vertices). If \code{TRUE}, then additional searches are performed until all vertices are visited.} \item{restricted}{\code{NULL} (=no restriction), or a vector of vertices (ids or symbolic names). In the latter case, the search is restricted to the given vertices.} \item{order}{Logical scalar, whether to return the ordering of the vertices.} \item{rank}{Logical scalar, whether to return the rank of the vertices.} \item{father}{Logical scalar, whether to return the father of the vertices.} \item{pred}{Logical scalar, whether to return the predecessors of the vertices.} \item{succ}{Logical scalar, whether to return the successors of the vertices.} \item{dist}{Logical scalar, whether to return the distance from the root of the search tree.} \item{callback}{If not \code{NULL}, then it must be callback function. This is called whenever a vertex is visited. See details below.} \item{extra}{Additional argument to supply to the callback function.} \item{rho}{The environment in which the callback function is evaluated.} } \details{ The callback function must have the following arguments: \describe{ \item{graph}{The input graph is passed to the callback function here.} \item{data}{A named numeric vector, with the following entries: \sQuote{vid}, the vertex that was just visited, \sQuote{pred}, its predecessor, \sQuote{succ}, its successor, \sQuote{rank}, the rank of the current vertex, \sQuote{dist}, its distance from the root of the search tree.} \item{extra}{The extra argument.} } See examples below on how to use the callback function. } \value{ A named list with the following entries: \item{root}{Numeric scalar. The root vertex that was used as the starting point of the search.} \item{neimode}{Character scalar. The \code{neimode} argument of the function call. Note that for undirected graphs this is always \sQuote{all}, irrespectively of the supplied value.} \item{order}{Numeric vector. The vertex ids, in the order in which they were visited by the search.} \item{rank}{Numeric vector. The rank for each vertex.} \item{father}{Numeric vector. The father of each vertex, i.e. the vertex it was discovered from.} \item{pred}{Numeric vector. The previously visited vertex for each vertex, or 0 if there was no such vertex.} \item{succ}{Numeric vector. The next vertex that was visited after the current one, or 0 if there was no such vertex.} \item{dist}{Numeric vector, for each vertex its distance from the root of the search tree.} Note that \code{order}, \code{rank}, \code{father}, \code{pred}, \code{succ} and \code{dist} might be \code{NULL} if their corresponding argument is \code{FALSE}, i.e. if their calculation is not requested. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{ \code{\link{graph.dfs}} for depth-first search.} \examples{ ## Two rings graph.bfs(graph.ring(10) \%du\% graph.ring(10), root=1, "out", order=TRUE, rank=TRUE, father=TRUE, pred=TRUE, succ=TRUE, dist=TRUE) ## How to use a callback f <- function(graph, data, extra) { print(data) FALSE } tmp <- graph.bfs(graph.ring(10) \%du\% graph.ring(10), root=1, "out", callback=f) ## How to use a callback to stop the search ## We stop after visiting all vertices in the initial component f <- function(graph, data, extra) { data['succ'] == -1 } graph.bfs(graph.ring(10) \%du\% graph.ring(10), root=1, callback=f) } \keyword{graphs} igraph/man/diameter.Rd0000644000176000001440000000402312240234657014414 0ustar ripleyusers\name{diameter} \alias{diameter} \alias{get.diameter} \alias{farthest.nodes} \concept{Diameter} \title{Diameter of a graph} \description{The diameter of a graph is the length of the longest geodesic.} \usage{ diameter(graph, directed = TRUE, unconnected = TRUE, weights = NULL) get.diameter (graph, directed = TRUE, unconnected = TRUE, weights = NULL) farthest.nodes (graph, directed = TRUE, unconnected = TRUE, weights = NULL) } \arguments{ \item{graph}{The graph to analyze.} \item{directed}{Logical, whether directed or undirected paths are to be considered. This is ignored for undirected graphs.} \item{unconnected}{Logical, what to do if the graph is unconnected. If FALSE, the function will return a number that is one larger the largest possible diameter, which is always the number of vertices. If TRUE, the diameters of the connected components will be calculated and the largest one will be returned.} \item{weights}{Optional positive weight vector for calculating weighted distances. If the graph has a \code{weight} edge attribute, then this is used by default.} } \details{The diameter is calculated by using a breadth-first search like method. \code{get.diameter} returns a path with the actual diameter. If there are many shortest paths of the length of the diameter, then it returns the first one found. \code{farthest.points} returns two vertex ids, the vertices which are connected by the diameter path. } \value{A numeric constant for \code{diameter}, a numeric vector for \code{get.diameter} and a numeric vector of length two for \code{farthest.nodes}. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{shortest.paths}}} \examples{ g <- graph.ring(10) g2 <- delete.edges(g, c(1,2,1,10)) diameter(g2, unconnected=TRUE) diameter(g2, unconnected=FALSE) ## Weighted diameter set.seed(1) g <- graph.ring(10) E(g)$weight <- sample(seq_len(ecount(g))) diameter(g) get.diameter(g) diameter(g, weights=NA) get.diameter(g, weights=NA) } \keyword{graphs} igraph/man/betweenness.Rd0000644000176000001440000000763112240234657015154 0ustar ripleyusers\name{betweenness} \alias{betweenness} \alias{edge.betweenness} \alias{betweenness.estimate} \alias{edge.betweenness.estimate} \concept{Betweenness centrality} \concept{Edge betweenness} \title{Vertex and edge betweenness centrality} \description{The vertex and edge betweenness are (roughly) defined by the number of geodesics (shortest paths) going through a vertex or an edge. } \usage{ betweenness(graph, v=V(graph), directed = TRUE, weights = NULL, nobigint = TRUE, normalized = FALSE) edge.betweenness(graph, e=E(graph), directed = TRUE, weights = NULL) betweenness.estimate(graph, vids = V(graph), directed = TRUE, cutoff, weights = NULL, nobigint = TRUE) edge.betweenness.estimate(graph, e=E(graph), directed = TRUE, cutoff, weights = NULL) } \arguments{ \item{graph}{The graph to analyze.} \item{v}{The vertices for which the vertex betweenness will be calculated.} \item{e}{The edges for which the edge betweenness will be calculated.} \item{directed}{Logical, whether directed paths should be considered while determining the shortest paths.} \item{weights}{Optional positive weight vector for calculating weighted betweenness. If the graph has a \code{weight} edge attribute, then this is used by default.} \item{nobigint}{Logical scalar, whether to use big integers during the calculation. This is only required for lattice-like graphs that have very many shortest paths between a pair of vertices. If \code{TRUE} (the default), then big integers are not used.} \item{normalized}{Logical scalar, whether to normalize the betweenness scores. If \code{TRUE}, then the results are normalized according to \deqn{B^n=\frac{2B}{n^2-3n+2}}{Bnorm=2*B/(n*n-3*n+2)}, where \eqn{B^n}{Bnorm} is the normalized, \eqn{B} the raw betweenness, and \eqn{n} is the number of vertices in the graph.} \item{vids}{The vertices for which the vertex betweenness estimation will be calculated.} \item{cutoff}{The maximum path length to consider when calculating the betweenness. If zero or negative then there is no such limit.} } \details{The vertex betweenness of vertex \eqn{v}{\code{v}} is defined by \deqn{\sum_{i\ne j, i\ne v, j\ne v} g_{ivj}/g_{ij}}{sum( g_ivj / g_ij, i!=j,i!=v,j!=v)} The edge betweenness of edge \eqn{e}{\code{e}} is defined by \deqn{\sum_{i\ne j} g{iej}/g_{ij}.}{sum( g_iej / g_ij, i!=j).} \code{betweenness} calculates vertex betweenness, \code{edge.betweenness} calculates edge.betweenness. \code{betweenness.estimate} only considers paths of length \code{cutoff} or smaller, this can be run for larger graphs, as the running time is not quadratic (if \code{cutoff} is small). If \code{cutoff} is zero or negative then the function calculates the exact betweenness scores. \code{edge.betweenness.estimate} is similar, but for edges. For calculating the betweenness a similar algorithm to the one proposed by Brandes (see References) is used. } \value{A numeric vector with the betweenness score for each vertex in \code{v} for \code{betweenness}. A numeric vector with the edge betweenness score for each edge in \code{e} for \code{edge.betweenness}. \code{betweenness.estimate} returns the estimated betweenness scores for vertices in \code{vids}, \code{edge.betweenness.estimate} the estimated edge betweenness score for \emph{all} edges; both in a numeric vector. } \note{\code{edge.betweenness} might give false values for graphs with multiple edges.} \references{Freeman, L.C. (1979). Centrality in Social Networks I: Conceptual Clarification. \emph{Social Networks}, 1, 215-239. Ulrik Brandes, A Faster Algorithm for Betweenness Centrality. \emph{Journal of Mathematical Sociology} 25(2):163-177, 2001. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{closeness}}, \code{\link{degree}}} \examples{ g <- random.graph.game(10, 3/10) betweenness(g) edge.betweenness(g) } \keyword{graphs} igraph/man/graph.bipartite.Rd0000644000176000001440000000374712240234657015721 0ustar ripleyusers\name{graph.bipartite} \alias{graph.bipartite} \alias{is.bipartite} \concept{Bipartite graph} \concept{Two-mode network} \title{Create a bipartite graph} \description{A bipartite graph has two kinds of vertices and connections are only allowed between different kinds. } \usage{ graph.bipartite(types, edges, directed=FALSE) is.bipartite(graph) } \arguments{ \item{types}{A vector giving the vertex types. It will be coerced into boolean. The length of the vector gives the number of vertices in the graph.} \item{edges}{A vector giving the edges of the graph, the same way as for the regular \code{\link{graph}} function. It is checked that the edges indeed connect vertices of different kind, accoding to the supplied \code{types} vector.} \item{directed}{Whether to create a directed graph, boolean constant. Note that by default undirected graphs are created, as this is more common for bipartite graphs.} \item{graph}{The input graph.} } \details{ Bipartite graphs have a \code{type} vertex attribute in igraph, this is boolean and \code{FALSE} for the vertices of the first kind and \code{TRUE} for vertices of the second kind. \code{graph.bipartite} basically does three things. First it checks tha \code{edges} vector against the vertex \code{types}. Then it creates a graph using the \code{edges} vector and finally it adds the \code{types} vector as a vertex attribute called \code{type}. \code{is.bipartite} checks whether the graph is bipartite or not. It just checks whether the graph has a vertex attribute called \code{type}. } \value{ \code{graph.bipartite} returns a bipartite igraph graph. In other words, an igraph graph that has a vertex attribute named \code{type}. \code{is.bipartite} returns a logical scalar. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph}} to create one-mode networks} \examples{ g <- graph.bipartite( rep(0:1,length=10), c(1:10)) print(g, v=TRUE) } \keyword{graphs} igraph/man/all.st.mincuts.Rd0000644000176000001440000000447312240234657015511 0ustar ripleyusers\name{stMincuts} \alias{stMincuts} \concept{Edge cuts} \concept{(s,t)-cuts} \concept{Minimum cuts} \concept{Minimum (s,t)-cuts} \title{List all minimum \eqn{(s,t)}-cuts of a graph} \description{ Listing all minimum \eqn{(s,t)}-cuts of a directed graph, for given \eqn{s} and \eqn{t}. } \usage{ stMincuts(graph, source, target, capacity = NULL) } \arguments{ \item{graph}{The input graph. It must be directed.} \item{source}{The id of the source vertex.} \item{target}{The id of the target vertex.} \item{capacity}{Numeric vector giving the edge capacities. If this is \code{NULL} and the graph has a \code{weight} edge attribute, then this attribute defines the edge capacities. For forcing unit edge capacities, even for graphs that have a \code{weight} edge attribute, supply \code{NA} here.} } \details{ Given a \eqn{G} directed graph and two, different and non-ajacent vertices, \eqn{s} and \eqn{t}, an \eqn{(s,t)}-cut is a set of edges, such that after removing these edges from \eqn{G} there is no directed path from \eqn{s} to \eqn{t}. The size of an \eqn{(s,t)}-cut is defined as the sum of the capacities (or weights) in the cut. For unweighed (=equally weighted) graphs, this is simply the number of edges. An \eqn{(s,t)}-cut is minimum if it is of the smallest possible size. } \value{ A list with entries: \item{value}{Numeric scalar, the size of the minimum cut(s).} \item{cuts}{A list of numeric vectors containing edge ids. Each vector is a minimum \eqn{(s,t)}-cut.} \item{partition1s}{A list of numeric vectors containing vertex ids, they correspond to the edge cuts. Each vertex set is a generator of the corresponding cut, i.e. in the graph \eqn{G=(V,E)}, the vertex set \eqn{X} and its complementer \eqn{V-X}, generates the cut that contains exactly the edges that go from \eqn{X} to \eqn{V-X}.} } \references{ JS Provan and DR Shier: A Paradigm for listing (s,t)-cuts in graphs, \emph{Algorithmica} 15, 351--372, 1996. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{stCuts}}, \code{\link{minimum.size.separators}} } \examples{ # A difficult graph, from the Provan-Shier paper g <- graph.formula(s --+ a:b, a:b --+ t, a --+ 1:2:3:4:5, 1:2:3:4:5 --+ b) stMincuts(g, source="s", target="t") } \keyword{graphs} igraph/man/kleinberg.Rd0000644000176000001440000000450712240234657014573 0ustar ripleyusers\name{kleinberg} \alias{authority.score} \alias{hub.score} \concept{Hub and authority score} \title{Kleinberg's centrality scores.} \description{Kleinberg's hub and authority scores.} \usage{ authority.score (graph, scale = TRUE, weights=NULL, options = igraph.arpack.default) hub.score (graph, scale = TRUE, weights=NULL, options = igraph.arpack.default) } \arguments{ \item{graph}{The input graph.} \item{scale}{Logical scalar, whether to scale the result to have a maximum score of one. If no scaling is used then the result vector has unit length in the Euclidean norm.} \item{weights}{Optional positive weight vector for calculating weighted scores. If the graph has a \code{weight} edge attribute, then this is used by default.} \item{options}{A named list, to override some ARPACK options. See \code{\link{arpack}} for details.} } \details{ The authority scores of the vertices are defined as the principal eigenvector of \eqn{A^T A}{t(A)*A}, where \eqn{A} is the adjacency matrix of the graph. The hub scores of the vertices are defined as the principal eigenvector of \eqn{A A^T}{A*t(A)}, where \eqn{A} is the adjacency matrix of the graph. Obviously, for undirected matrices the adjacency matrix is symmetric and the two scores are the same. } \value{ A named list with members: \item{vector}{The authority/hub scores of the vertices.} \item{value}{The corresponding eigenvalue of the calculated principal eigenvector.} \item{options}{Some information about the ARPACK computation, it has the same members as the \code{options} member returned by \code{\link{arpack}}, see that for documentation.} } \references{ J. Kleinberg. Authoritative sources in a hyperlinked environment. \emph{Proc. 9th ACM-SIAM Symposium on Discrete Algorithms}, 1998. Extended version in \emph{Journal of the ACM} 46(1999). Also appears as IBM Research Report RJ 10076, May 1997. } \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{\code{\link{evcent}} for eigenvector centrality, \code{\link{page.rank}} for the Page Rank scores. \code{\link{arpack}} for the underlining machinery of the computation. } \examples{ ## An in-star g <- graph.star(10) hub.score(g)$vector authority.score(g)$vector ## A ring g2 <- graph.ring(10) hub.score(g2)$vector authority.score(g2)$vector } \keyword{graphs} igraph/man/is.named.Rd0000644000176000001440000000206312240234657014322 0ustar ripleyusers\name{is.named} \alias{is.named} \concept{Named graphs} \title{Named graphs} \description{An igraph graph is named, if there is a symbolic name associated with its vertices.} \usage{ is.named(graph) } \arguments{ \item{graph}{The input graph.} } \details{ In igraph vertices can always be identified and specified via their numeric vertex ids. This is, however, not always convenient, and in many cases there exist symbolic ids that correspond to the vertices. To allow this more flexible identification of vertices, one can assign a vertex attribute called \sQuote{name} to an igraph graph. After doing this, the symbolic vertex names can be used in all igraph functions, instead of the numeric ids. Note that the uniqueness of vertex names are currently not enforced in igraph, you have to check that for yourself, when assigning the vertex names. } \value{A logical scalar.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \examples{ g <- graph.ring(10) is.named(g) V(g)$name <- letters[1:10] is.named(g) neighbors(g, "a") } \keyword{graphs} igraph/man/unfold.tree.Rd0000644000176000001440000000317212240234657015053 0ustar ripleyusers\name{unfold.tree} \alias{unfold.tree} \concept{Tree} \concept{Forest} \concept{Breadth-first search} \title{Convert a general graph into a forest} \description{ Perform a breadth-first search on a graph and convert it into a tree or forest by replicating vertices that were found more than once. } \usage{ unfold.tree(graph, mode = c("all", "out", "in", "total"), roots) } \arguments{ \item{graph}{The input graph, it can be either directed or undirected.} \item{mode}{Character string, defined the types of the paths used for the breadth-first search. \dQuote{out} follows the outgoing, \dQuote{in} the incoming edges, \dQuote{all} and \dQuote{total} both of them. This argument is ignored for undirected graphs.} \item{roots}{A vector giving the vertices from which the breadth-first search is performed. Typically it contains one vertex per component.} } \details{ A forest is a graph, whose components are trees. The \code{roots} vector can be calculated by simply doing a topological sort in all components of the graph, see the examples below. } \value{ A list with two components: \item{tree}{The result, an \code{igraph} object, a tree or a forest.} \item{vertex_index}{A numeric vector, it gives a mapping from the vertices of the new graph to the vertices of the old graph.} } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.tree(10) %du% graph.tree(10) V(g)$id <- seq_len(vcount(g))-1 roots <- sapply(decompose.graph(g), function(x) { V(x)$id[ topological.sort(x)[1]+1 ] }) tree <- unfold.tree(g, roots=roots) } \keyword{graphs} igraph/man/graph.data.frame.Rd0000644000176000001440000001171112263024035015716 0ustar ripleyusers\name{graph.data.frame} \alias{graph.data.frame} \alias{get.data.frame} \concept{Data frame} \title{Creating igraph graphs from data frames or vice-versa} \description{ This function creates an igraph graph from one or two data frames containing the (symbolic) edge list and edge/vertex attributes. } \usage{ graph.data.frame(d, directed=TRUE, vertices=NULL) get.data.frame(x, what=c("edges", "vertices", "both")) } \arguments{ \item{d}{A data frame containing a symbolic edge list in the first two columns. Additional columns are considered as edge attributes. Since version 0.7 this argument is coerced to a data frame with \code{as.data.frame}.} \item{directed}{Logical scalar, whether or not to create a directed graph.} \item{vertices}{A data frame with vertex metadata, or \code{NULL}. See details below. Since version 0.7 this argument is coerced to a data frame with \code{as.data.frame}, if not \code{NULL}.} \item{x}{An igraph object.} \item{what}{Character constant, whether to return info about vertices, edges, or both. The default is \sQuote{edges}.} } \details{ \code{graph.data.frame} creates igraph graphs from one or two data frames. It has two modes of operatation, depending whether the \code{vertices} argument is \code{NULL} or not. If \code{vertices} is \code{NULL}, then the first two columns of \code{d} are used as a symbolic edge list and additional columns as edge attributes. The names of the attributes are taken from the names of the columns. If \code{vertices} is not \code{NULL}, then it must be a data frame giving vertex metadata. The first column of \code{vertices} is assumed to contain symbolic vertex names, this will be added to the graphs as the \sQuote{\code{name}} vertex attribute. Other columns will be added as additional vertex attributes. If \code{vertices} is not \code{NULL} then the symbolic edge list given in \code{d} is checked to contain only vertex names listed in \code{vertices}. Typically, the data frames are exported from some speadsheat software like Excel and are imported into R via \code{\link{read.table}}, \code{\link{read.delim}} or \code{\link{read.csv}}. \code{get.data.frame} converts the igraph graph into one or more data frames, depending on the \code{what} argument. If the \code{what} argument is \code{edges} (the default), then the edges of the graph and also the edge attributes are returned. The edges will be in the first two columns, named \code{from} and \code{to}. (This also denotes edge direction for directed graphs.) For named graphs, the vertex names will be included in these columns, for other graphs, the numeric vertex ids. The edge attributes will be in the other columns. It is not a good idea to have an edge attribute named \code{from} or \code{to}, because then the column named in the data frame will not be unique. The edges are listed in the order of their numeric ids. If the \code{what} argument is \code{vertices}, then vertex attributes are returned. Vertices are listed in the order of their numeric vertex ids. If the \code{what} argument is \code{both}, then both vertex and edge data is returned, in a list with named entries \code{vertices} and \code{edges}. } \note{ For \code{graph.data.frame} \code{NA} elements in the first two columns \sQuote{d} are replaced by the string \dQuote{NA} before creating the graph. This means that all \code{NA}s will correspond to a single vertex. \code{NA} elements in the first column of \sQuote{vertices} are also replaced by the string \dQuote{NA}, but the rest of \sQuote{vertices} is not touched. In other words, vertex names (=the first column) cannot be \code{NA}, but other vertex attributes can. } \value{ An igraph graph object for \code{graph.data.frame}, and either a data frame or a list of two data frames named \code{edges} and \code{vertices} for \code{as.data.frame}. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{graph.constructors}} and \code{\link{graph.formula}} for other ways to create graphs, \code{\link{read.table}} to read in tables from files.} \examples{ ## A simple example with a couple of actors ## The typical case is that these tables are read in from files.... actors <- data.frame(name=c("Alice", "Bob", "Cecil", "David", "Esmeralda"), age=c(48,33,45,34,21), gender=c("F","M","F","M","F")) relations <- data.frame(from=c("Bob", "Cecil", "Cecil", "David", "David", "Esmeralda"), to=c("Alice", "Bob", "Alice", "Alice", "Bob", "Alice"), same.dept=c(FALSE,FALSE,TRUE,FALSE,FALSE,TRUE), friendship=c(4,5,5,2,1,1), advice=c(4,5,5,4,2,3)) g <- graph.data.frame(relations, directed=TRUE, vertices=actors) print(g, e=TRUE, v=TRUE) ## The opposite operation get.data.frame(g, what="vertices") get.data.frame(g, what="edges") } \keyword{graphs} igraph/man/graph.strength.Rd0000644000176000001440000000302212240234657015556 0ustar ripleyusers\name{graph.strength} \alias{graph.strength} \title{Strength or weighted vertex degree} \description{Summing up the edge weights of the adjacent edges for each vertex.} \usage{ graph.strength (graph, vids = V(graph), mode = c("all", "out", "in", "total"), loops = TRUE, weights = NULL) } \arguments{ \item{graph}{The input graph.} \item{vids}{The vertices for which the strength will be calculated.} \item{mode}{Character string, \dQuote{out} for out-degree, \dQuote{in} for in-degree or \dQuote{all} for the sum of the two. For undirected graphs this argument is ignored.} \item{loops}{Logical; whether the loop edges are also counted.} \item{weights}{Weight vector. If the graph has a \code{weight} edge attribute, then this is used by default. If the graph does not have a \code{weight} edge attribute and this argument is \code{NULL}, then a warning is given and \code{\link{degree}} is called.} } % \details{} \value{ A numeric vector giving the strength of the vertices. } \references{ Alain Barrat, Marc Barthelemy, Romualdo Pastor-Satorras, Alessandro Vespignani: The architecture of complex weighted networks, Proc. Natl. Acad. Sci. USA 101, 3747 (2004) } \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{\code{\link{degree}} for the unweighted version.} \examples{ g <- graph.star(10) E(g)$weight <- seq(ecount(g)) graph.strength(g) graph.strength(g, mode="out") graph.strength(g, mode="in") # No weights, a warning is given g <- graph.ring(10) graph.strength(g) } \keyword{graphs} igraph/man/simplify.Rd0000644000176000001440000000342512240234657014463 0ustar ripleyusers\name{simplify} \alias{simplify} \alias{is.simple} \concept{Simple graph} \title{Simple graphs} \description{Simple graphs are graphs which do not contain loop and multiple edges.} \usage{ simplify(graph, remove.multiple = TRUE, remove.loops = TRUE, edge.attr.comb = getIgraphOpt("edge.attr.comb")) is.simple(graph) } \arguments{ \item{graph}{The graph to work on.} \item{remove.loops}{Logical, whether the loop edges are to be removed.} \item{remove.multiple}{Logical, whether the multiple edges are to be removed.} \item{edge.attr.comb}{Specifies what to do with edge attributes, if \code{remove.multiple=TRUE}. In this case many edges might be mapped to a single one in the new graph, and their attributes are combined. Please see \code{\link{attribute.combination}} for details on this.} } \details{ A loop edge is an edge for which the two endpoints are the same vertex. Two edges are multiple edges if they have exactly the same two endpoints (for directed graphs order does matter). A graph is simple is it does not contain loop edges and multiple edges. \code{is.simple} checks whether a graph is simple. \code{simplify} removes the loop and/or multiple edges from a graph. If both \code{remove.loops} and \code{remove.multiple} are \code{TRUE} the function returns a simple graph. } \value{A new graph object with the edges deleted. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{is.loop}}, \code{\link{is.multiple}} and \code{\link{count.multiple}}, \code{\link{delete.edges}}, \code{\link{delete.vertices}}} \examples{ g <- graph( c(1,2,1,2,3,3) ) is.simple(g) is.simple(simplify(g, remove.loops=FALSE)) is.simple(simplify(g, remove.multiple=FALSE)) is.simple(simplify(g)) } \keyword{graphs} igraph/man/graph.incidence.Rd0000644000176000001440000000642212240234657015650 0ustar ripleyusers\name{graph.incidence} \alias{graph.incidence} \concept{Bipartite graph} \concept{Two-mode network} \title{Create graphs from an incidence matrix} \description{\code{graph.incidence} creates a bipartite igraph graph from an incidence matrix.} \usage{ graph.incidence(incidence, directed = FALSE, mode = c("all", "out", "in", "total"), multiple = FALSE, weighted = NULL, add.names = NULL) } \arguments{ \item{incidence}{The input incidence matrix. It can also be a sparse matrix from the \code{Matrix} package.} \item{directed}{Logical scalar, whether to create a directed graph.} \item{mode}{A character constant, defines the direction of the edges in directed graphs, ignored for undirected graphs. If \sQuote{\code{out}}, then edges go from vertices of the first kind (corresponding to rows in the incidence matrix) to vertices of the second kind (columns in the incidence matrix). If \sQuote{\code{in}}, then the opposite direction is used. If \sQuote{\code{all}} or \sQuote{\code{total}}, then mutual edges are created. } \item{multiple}{Logical scalar, specifies how to interpret the matrix elements. See details below.} \item{weighted}{This argument specifies whether to create a weighted graph from the incidence matrix. If it is \code{NULL} then an unweighted graph is created and the \code{multiple} argument is used to determine the edges of the graph. If it is a character constant then for every non-zero matrix entry an edge is created and the value of the entry is added as an edge attribute named by the \code{weighted} argument. If it is \code{TRUE} then a weighted graph is created and the name of the edge attribute will be \sQuote{\code{weight}}. } \item{add.names}{A character constant, \code{NA} or \code{NULL}. \code{graph.incidence} can add the row and column names of the incidence matrix as vertex attributes. If this argument is \code{NULL} (the default) and the incidence matrix has both row and column names, then these are added as the \sQuote{\code{name}} vertex attribute. If you want a different vertex attribute for this, then give the name of the attributes as a character string. If this argument is \code{NA}, then no vertex attributes (other than type) will be added. } } \details{ Bipartite graphs have a \sQuote{\code{type}} vertex attribute in igraph, this is boolean and \code{FALSE} for the vertices of the first kind and \code{TRUE} for vertices of the second kind. \code{graph.incidence} can operate in two modes, depending on the \code{multiple} argument. If it is \code{FALSE} then a single edge is created for every non-zero element in the incidence matrix. If \code{multiple} is \code{TRUE}, then the matrix elements are rounded up to the closest non-negative integer to get the number of edges to create between a pair of vertices. } \value{ A bipartite igraph graph. In other words, an igraph graph that has a vertex attribute \code{type}. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.bipartite}} for another way to create bipartite graphs} \examples{ inc <- matrix(sample(0:1, 15, repl=TRUE), 3, 5) colnames(inc) <- letters[1:5] rownames(inc) <- LETTERS[1:3] graph.incidence(inc) } \keyword{graphs} igraph/man/is.graph.Rd0000644000176000001440000000074712240234657014346 0ustar ripleyusers\name{is.igraph} \alias{is.igraph} \title{Is this object a graph?} \description{\code{is.graph} makes its decision based on the class attribute of the object.} \usage{ is.igraph(graph) } \arguments{ \item{graph}{An R object.} } % \details{} \value{ A logical constant, \code{TRUE} if argument \code{graph} is a graph object. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.ring(10) is.igraph(g) is.igraph(numeric(10)) } \keyword{graphs} igraph/man/neighborhood.Rd0000644000176000001440000000561712325365704015305 0ustar ripleyusers\name{neighborhood} \alias{neighborhood} \alias{neighborhood.size} \alias{graph.neighborhood} \alias{connect.neighborhood} \title{Neighborhood of graph vertices} \description{These functions find the vertices not farther than a given limit from another fixed vertex, these are called the neighborhood of the vertex.} \usage{ neighborhood.size(graph, order, nodes=V(graph), mode=c("all", "out", "in")) neighborhood(graph, order, nodes=V(graph), mode=c("all", "out", "in")) graph.neighborhood(graph, order, nodes=V(graph), mode=c("all", "out", "in")) connect.neighborhood(graph, order, mode=c("all", "out", "in", "total")) } \arguments{ \item{graph}{The input graph.} \item{order}{Integer giving the order of the neighborhood.} \item{nodes}{The vertices for which the calculation is performed.} \item{mode}{Character constatnt, it specifies how to use the direction of the edges if a directed graph is analyzed. For \sQuote{out} only the outgoing edges are followed, so all vertices reachable from the source vertex in at most \code{order} steps are counted. For \sQuote{"in"} all vertices from which the source vertex is reachable in at most \code{order} steps are counted. \sQuote{"all"} ignores the direction of the edges. This argument is ignored for undirected graphs.} } \details{ The neighborhood of a given order \code{o} of a vertex \code{v} includes all vertices which are closer to \code{v} than the order. Ie. order 0 is always \code{v} itself, order 1 is \code{v} plus its immediate neighbors, order 2 is order 1 plus the immediate neighbors of the vertices in order 1, etc. \code{neighborhood.size} calculates the size of the neighborhoods for the given vertices with the given order. \code{neighborhood} calculates the neighborhoods of the given vertices with the given order parameter. \code{graph.neighborhood} is creates (sub)graphs from all neighborhoods of the given vertices with the given order parameter. This function preserves the vertex, edge and graph attributes. \code{connect.neighborhood} creates a new graph by connecting each vertex to all other vertices in its neighborhood. } \value{ \code{neighborhood.size} returns with an integer vector. \code{neighborhood} returns with a list of integer vectors. \code{graph.neighborhood} returns with a list of graphs. \code{connect.neighborhood} returns with a new graph object. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}, the first version was done by Vincent Matossian} \examples{ g <- graph.ring(10) neighborhood.size(g, 0, 1:3) neighborhood.size(g, 1, 1:3) neighborhood.size(g, 2, 1:3) neighborhood(g, 0, 1:3) neighborhood(g, 1, 1:3) neighborhood(g, 2, 1:3) # attributes are preserved V(g)$name <- c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j") graph.neighborhood(g, 2, 1:3) # connecting to the neighborhood g <- graph.ring(10) g <- connect.neighborhood(g, 2) } \keyword{graphs} igraph/man/traits.Rd0000644000176000001440000000436212240234657014136 0ustar ripleyusers\name{traits} \alias{callaway.traits.game} \alias{establishment.game} \title{Graph generation based on different vertex types} \description{These functions implement evolving network models based on different vertex types. } \usage{ callaway.traits.game (nodes, types, edge.per.step = 1, type.dist = rep(1, types), pref.matrix = matrix(1, types, types), directed = FALSE) establishment.game(nodes, types, k = 1, type.dist = rep(1, types), pref.matrix = matrix(1, types, types), directed = FALSE) } \arguments{ \item{nodes}{The number of vertices in the graph.} \item{types}{The number of different vertex types.} \item{edge.per.step}{The number of edges to add to the graph per time step.} \item{type.dist}{The distribution of the vertex types. This is assumed to be stationary in time.} \item{pref.matrix}{A matrix giving the preferences of the given vertex types. These should be probabilities, ie. numbers between zero and one.} \item{directed}{Logical constant, whether to generate directed graphs.} \item{k}{The number of trials per time step, see details below.} } \details{ For \code{callaway.traits.game} the simulation goes like this: in each discrete time step a new vertex is added to the graph. The type of this vertex is generated based on \code{type.dist}. Then two vertices are selected uniformly randomly from the graph. The probability that they will be connected depends on the types of these vertices and is taken from \code{pref.matrix}. Then another two vertices are selected and this is repeated \code{edges.per.step} times in each time step. For \code{establishment.game} the simulation goes like this: a single vertex is added at each time step. This new vertex tries to connect to \code{k} vertices in the graph. The probability that such a connection is realized depends on the types of the vertices involved and is taken from \code{pref.matrix}. } \value{ A new graph object. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ # two types of vertices, they like only themselves g1 <- callaway.traits.game(1000, 2, pref.matrix=matrix( c(1,0,0,1), nc=2)) g2 <- establishment.game(1000, 2, k=2, pref.matrix=matrix( c(1,0,0,1), nc=2)) } \keyword{graphs} igraph/man/igraphdemo.Rd0000644000176000001440000000173412240234657014747 0ustar ripleyusers\name{igraphdemo} \alias{igraphdemo} \title{Run igraph demos, step by step} \description{Run one of the accompanying igraph demos, somewhat interactively, using a Tk window. } \usage{ igraphdemo(which) } \arguments{ \item{which}{If not given, then the names of the available demos are listed. Otherwise it should be either a filename or the name of an igraph demo.} } \details{ This function provides a somewhat nicer interface to igraph demos that come with the package, than the standard \code{\link{demo}} function. Igraph demos are divided into chunks and \code{igraphdemo} runs them chunk by chunk, with the possibility of inspecting the workspace between two chunks. The \code{tcltk} package is needed for \code{igraphdemo}. } \value{Returns \code{NULL}, invisibly.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{demo}}} \examples{ igraphdemo() if (interactive()) { igraphdemo("centrality") } } \keyword{graphs} igraph/man/triad.census.Rd0000644000176000001440000000364112240234657015231 0ustar ripleyusers\name{triad.census} \alias{triad.census} \concept{Triad census} \title{Triad census, subgraphs with three vertices} \description{This function counts the different subgraphs of three vertices in a graph.} \usage{ triad.census(graph) } \arguments{ \item{graph}{The input graph, it should be directed. An undirected graph results a warning, and undefined results.} } \details{ Triad census was defined by David and Leinhardt (see References below). Every triple of vertices (A, B, C) are classified into the 16 possible states: \describe{ \item{003}{A,B,C, the empty graph.} \item{012}{A->B, C, the graph with a single directed edge.} \item{102}{A<->B, C, the graph with a mutual connection between two vertices.} \item{021D}{A<-B->C, the out-star.} \item{021U}{A->B<-C, the in-star.} \item{021C}{A->B->C, directed line.} \item{111D}{A<->B<-C.} \item{111U}{A<->B->C.} \item{030T}{A->B<-C, A->C.} \item{030C}{A<-B<-C, A->C.} \item{201}{A<->B<->C.} \item{120D}{A<-B->C, A<->C.} \item{120U}{A->B<-C, A<->C.} \item{120C}{A->B->C, A<->C.} \item{210}{A->B<->C, A<->C.} \item{300}{A<->B<->C, A<->C, the complete graph.} } This functions uses the RANDESU motif finder algorithm to find and count the subgraphs, see \code{\link{graph.motifs}}. } \value{A numeric vector, the subgraph counts, in the order given in the above description.} \references{ See also Davis, J.A. and Leinhardt, S. (1972). The Structure of Positive Interpersonal Relations in Small Groups. In J. Berger (Ed.), Sociological Theories in Progress, Volume 2, 218-251. Boston: Houghton Mifflin. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{dyad.census}} for classifying binary relationships, \code{\link{graph.motifs}} for the underlying implementation.} \examples{ g <- erdos.renyi.game(15, 45, type="gnm", dir=TRUE) triad.census(g) } \keyword{graphs} igraph/man/dyad.census.Rd0000644000176000001440000000220212240234657015037 0ustar ripleyusers\name{dyad.census} \alias{dyad.census} \concept{Dyad census} \title{Dyad census of a graph} \description{Classify dyads in a directed graphs. The relationship between each pair of vertices is measured. It can be in three states: mutual, asymmetric or non-existent.} \usage{ dyad.census(graph) } \arguments{ \item{graph}{The input graph. A warning is given if it is not directed.} } %\details{} \value{A named numeric vector with three elements: \item{mut}{The number of pairs with mutual connections.} \item{asym}{The number of pairs with non-mutual connections.} \item{null}{The number of pairs with no connection between them.} } \references{ Holland, P.W. and Leinhardt, S. A Method for Detecting Structure in Sociometric Data. \emph{American Journal of Sociology}, 76, 492--513. 1970. Wasserman, S., and Faust, K. \emph{Social Network Analysis: Methods and Applications.} Cambridge: Cambridge University Press. 1994. } \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{ \code{\link{triad.census}} for the same classification, but with triples. } \examples{ g <- ba.game(100) dyad.census(g) } \keyword{graphs} igraph/man/graph.kcores.Rd0000644000176000001440000000272412240234657015216 0ustar ripleyusers\name{graph.coreness} \alias{graph.coreness} \concept{K-core} \title{K-core decomposition of graphs} \description{The k-core of graph is a maximal subgraph in which each vertex has at least degree k. The coreness of a vertex is k if it belongs to the k-core but not to the (k+1)-core.} \usage{ graph.coreness(graph, mode=c("all", "out", "in")) } \arguments{ \item{graph}{The input graph, it can be directed or undirected} \item{mode}{The type of the core in directed graphs. Character constant, possible values: \code{in}: in-cores are computed, \code{out}: out-cores are computed, \code{all}: the corresponding undirected graph is considered. This argument is ignored for undirected graphs.} } \details{ The k-core of a graph is the maximal subgraph in which every vertex has at least degree k. The cores of a graph form layers: the (k+1)-core is always a subgraph of the k-core. This function calculates the coreness for each vertex. } \value{ Numeric vector of integer numbers giving the coreness of each vertex. } \references{Vladimir Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores Decomposition of Networks, 2002 Seidman S. B. (1983) Network structure and minimum degree, \emph{Social Networks}, 5, 269--287. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{degree}}} \examples{ g <- graph.ring(10) g <- add.edges(g, c(1,2, 2,3, 1,3)) graph.coreness(g) % small core triangle in a ring } \keyword{graphs}igraph/man/eccentricity.Rd0000644000176000001440000000341512240234657015313 0ustar ripleyusers\name{eccentricity} \alias{eccentricity} \alias{radius} \concept{Eccentricity} \concept{Radius} \title{Eccentricity and radius} \description{The eccentricity of a vertex is its shortest path distance from the farthest other node in the graph. The smallest eccentricity in a graph is called its radius} \usage{ eccentricity(graph, vids=V(graph), mode=c("all", "out", "in", "total")) radius(graph, mode=c("all", "out", "in", "total")) } \arguments{ \item{graph}{The input graph, it can be directed or undirected.} \item{vids}{The vertices for which the eccentricity is calculated.} \item{mode}{Character constant, gives whether the shortest paths to or from the given vertices should be calculated for directed graphs. If \code{out} then the shortest paths \emph{from} the vertex, if \code{in} then \emph{to} it will be considered. If \code{all}, the default, then the corresponding undirected graph will be used, edge directions will be ignored. This argument is ignored for undirected graphs.} } \details{ The eccentricity of a vertex is calculated by measuring the shortest distance from (or to) the vertex, to (or from) all vertices in the graph, and taking the maximum. This implementation ignores vertex pairs that are in different components. Isolate vertices have eccentricity zero. } \value{ \code{eccentricity} returns a numeric vector, containing the eccentricity score of each given vertex. \code{radius} returns a numeric scalar. } \references{ Harary, F. Graph Theory. Reading, MA: Addison-Wesley, p. 35, 1994. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{shortest.paths}} for general shortest path calculations.} \examples{ g <- graph.star(10, mode="undirected") eccentricity(g) radius(g) } \keyword{graphs} igraph/man/dominator.tree.Rd0000644000176000001440000000552112240234657015560 0ustar ripleyusers\name{dominator.tree} \alias{dominator.tree} \concept{Domintor tree} \title{Dominator tree} \description{Dominator tree of a directed graph. } \usage{ dominator.tree (graph, root, mode = c("out", "in")) } \arguments{ \item{graph}{A directed graph. If it is not a flowgraph, and it contains some vertices not reachable from the root vertex, then these vertices will be collected and returned as part of the result.} \item{root}{The id of the root (or source) vertex, this will be the root of the tree.} \item{mode}{Constant, must be \sQuote{\code{in}} or \sQuote{\code{out}}. If it is \sQuote{\code{in}}, then all directions are considered as opposite to the original one in the input graph.} } \details{ A flowgraph is a directed graph with a distinguished start (or root) vertex \eqn{r}, such that for any vertex \eqn{v}, there is a path from \eqn{r} to \eqn{v}. A vertex \eqn{v} dominates another vertex \eqn{w} (not equal to \eqn{v}), if every path from \eqn{r} to \eqn{w} contains \eqn{v}. Vertex \eqn{v} is the immediate dominator or \eqn{w}, \eqn{v=\textrm{idom}(w)}{v=idom(w)}, if \eqn{v} dominates \eqn{w} and every other dominator of \eqn{w} dominates \eqn{v}. The edges \eqn{{(\textrm{idom}(w), w)| w \ne r}}{{(idom(w),w)| w is not r}} form a directed tree, rooted at \eqn{r}, called the dominator tree of the graph. Vertex \eqn{v} dominates vertex \eqn{w} if and only if \eqn{v} is an ancestor of \eqn{w} in the dominator tree. This function implements the Lengauer-Tarjan algorithm to construct the dominator tree of a directed graph. For details see the reference below. } \value{ A list with components: \item{dom}{ A numeric vector giving the immediate dominators for each vertex. For vertices that are unreachable from the root, it contains \code{NaN}. For the root vertex itself it contains minus one. } \item{domtree}{ A graph object, the dominator tree. Its vertex ids are the as the vertex ids of the input graph. Isolate vertices are the ones that are unreachable from the root. } \item{leftout}{ A numeric vector containing the vertex ids that are unreachable from the root. } } \references{ Thomas Lengauer, Robert Endre Tarjan: A fast algorithm for finding dominators in a flowgraph, \emph{ACM Transactions on Programming Languages and Systems (TOPLAS)} I/1, 121--141, 1979. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ ## The example from the paper g <- graph.formula(R-+A:B:C, A-+D, B-+A:D:E, C-+F:G, D-+L, E-+H, F-+I, G-+I:J, H-+E:K, I-+K, J-+I, K-+I:R, L-+H) dtree <- dominator.tree(g, root="R") layout <- layout.reingold.tilford(dtree$domtree, root="R") layout[,2] <- -layout[,2] if (interactive()) { plot(dtree$domtree, layout=layout, vertex.label=V(dtree$domtree)$name) } } \keyword{graphs} igraph/man/scg.Rd0000644000176000001440000002262112263023733013376 0ustar ripleyusers\name{scg} \alias{scg} \concept{Spectral coarse graining} \title{All-in-one Function for the SCG of Matrices and Graphs} \description{ This function handles all the steps involved in the Spectral Coarse Graining (SCG) of some matrices and graphs as described in the reference below. } \usage{ scg(X, ev, nt, groups = NULL, mtype = c("symmetric", "laplacian", "stochastic"), algo = c("optimum", "interv_km", "interv", "exact_scg"), norm = c("row", "col"), direction = c("default", "left", "right"), evec = NULL, p = NULL, use.arpack = FALSE, maxiter = 300, sparse = getIgraphOpt("sparsematrices"), output = c("default", "matrix", "graph"), semproj = FALSE, epairs = FALSE, stat.prob = FALSE) } \arguments{ \item{X}{The input graph or square matrix. Can be of class \code{igraph}, \code{matrix} or \code{Matrix}.} \item{ev}{A vector of positive integers giving the indexes of the eigenpairs to be preserved. For real eigenpairs, 1 designates the eigenvalue with largest algebraic value, 2 the one with second largest algebraic value, etc. In the complex case, it is the magnitude that matters.} \item{nt}{A vector of positive integers of length one or equal to \code{length(ev)}. When \code{algo} = \dQuote{optimum}, \code{nt} contains the number of groups used to partition each eigenvector separately. When \code{algo} is equal to \dQuote{interv\_km} or \dQuote{interv}, \code{nt} contains the number of intervals used to partition each eigenvector. The same partition size or number of intervals is used for each eigenvector if \code{nt} is a single integer. When \code{algo} = \dQuote{exact\_cg} this parameter is ignored.} \item{groups}{A vector of \code{nrow(X)} or \code{vcount(X)} integers labeling each group vertex in the partition. If this parameter is supplied most part of the function is bypassed.} \item{mtype}{Character scalar. The type of semi-projector to be used for the SCG. For now \dQuote{symmetric}, \dQuote{laplacian} and \dQuote{stochastic} are available.} \item{algo}{Character scalar. The algorithm used to solve the SCG problem. Possible values are \dQuote{optimum}, \dQuote{interv\_km}, \dQuote{interv} and \dQuote{exact\_scg}.} \item{norm}{Character scalar. Either \dQuote{row} or \dQuote{col}. If set to \dQuote{row} the rows of the Laplacian matrix sum up to zero and the rows of the stochastic matrix sum up to one; otherwise it is the columns.} \item{direction}{Character scalar. When set to \dQuote{right}, resp. \dQuote{left}, the parameters \code{ev} and \code{evec} refer to right, resp. left eigenvectors. When passed \dQuote{default} it is the SCG described in the reference below that is applied (common usage). This argument is currently not implemented, and right eigenvectors are always used.} \item{evec}{A numeric matrix of (eigen)vectors to be preserved by the coarse graining (the vectors are to be stored column-wise in \code{evec}). If supplied, the eigenvectors should correspond to the indexes in \code{ev} as no cross-check will be done.} \item{p}{A probability vector of length \code{nrow(X)} (or \code{vcount(X)}). \code{p} is the stationary probability distribution of a Markov chain when \code{mtype} = \dQuote{stochastic}. This parameter is ignored in all other cases.} \item{use.arpack}{Logical scalar. When set to \code{TRUE} uses the function \code{\link{arpack}} to compute eigenpairs. This parameter should be set to \code{TRUE} if one deals with large (over a few thousands) AND sparse graphs or matrices. This argument is not implemented currently and LAPACK is used for solving the eigenproblems. } \item{maxiter}{A positive integer giving the maximum number of iterations for the k-means algorithm when \code{algo} = \dQuote{interv\_km}. This parameter is ignored in all other cases.} \item{sparse}{Logical scalar. Whether to return sparse matrices in the result, if matrices are requested.} \item{output}{Character scalar. Set this parameter to \dQuote{default} to retrieve a coarse-grained object of the same class as \code{X}.} \item{semproj}{Logical scalar. Set this parameter to \code{TRUE} to retrieve the semi-projectors of the SCG.} \item{epairs}{Logical scalar. Set this to \code{TRUE} to collect the eigenpairs computed by \code{scg}.} \item{stat.prob}{Logical scalar. This is to collect the stationary probability \code{p} when dealing with stochastic matrices.} } \details{ Please see \link{SCG} for an introduction. In the following \eqn{V} is the matrix of eigenvectors for which the SCG is solved. \eqn{V} is calculated from \code{X}, if it is not given in the \code{evec} argument. The algorithm \dQuote{optimum} solves exactly the SCG problem for each eigenvector in \code{V}. The running time of this algorithm is \eqn{O(\max nt \cdot m^2)}{O(max(nt) m^2)} for the symmetric and laplacian matrix problems (i.e. when \code{mtype} is \dQuote{symmetric} or \dQuote{laplacian}. It is \eqn{O(m^3)} for the stochastic problem. Here \eqn{m} is the number of rows in \code{V}. In all three cases, the memory usage is \eqn{O(m^2)}. The algorithms \dQuote{interv} and \dQuote{interv\_km} solve approximately the SCG problem by performing a (for now) constant binning of the components of the eigenvectors, that is \code{nt[i]} constant-size bins are used to partition \code{V[,i]}. When \code{algo} = \dQuote{interv\_km}, the (Lloyd) k-means algorithm is run on each partition obtained by \dQuote{interv} to improve accuracy. Once a minimizing partition (either exact or approximate) has been found for each eigenvector, the final grouping is worked out as follows: two vertices are grouped together in the final partition if they are grouped together in each minimizing partition. In general the size of the final partition is not known in advance when \code{ncol(V)}>1. Finally, the algorithm \dQuote{exact\_scg} groups the vertices with equal components in each eigenvector. The last three algorithms essentially have linear running time and memory load. } \value{ \item{Xt}{The coarse-grained graph, or matrix, possibly a sparse matrix.} \item{groups}{A vector of \code{nrow(X)} or \code{vcount(X)} integers giving the group label of each object (vertex) in the partition.} \item{L}{The semi-projector \eqn{L} if \code{semproj = TRUE}.} \item{R}{The semi-projector \eqn{R} if \code{semproj = TRUE}.} \item{values}{The computed eigenvalues if \code{epairs = TRUE}.} \item{vectors}{The computed or supplied eigenvectors if \code{epairs = TRUE}.} \item{p}{The stationary probability vector if \code{mtype = stochastic} and \code{stat.prob = TRUE}. For other matrix types this is missing.} } \references{ D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, Shrinking Matrices while Preserving their Eigenpairs with Application to the Spectral Coarse Graining of Graphs. Submitted to \emph{SIAM Journal on Matrix Analysis and Applications}, 2008. \url{http://people.epfl.ch/david.morton} } \author{David Morton de Lachapelle, \url{http://people.epfl.ch/david.morton}.} \seealso{\link{SCG} for an introduction. \code{\link{scgNormEps}}, \code{\link{scgGrouping}} and \code{\link{scgSemiProjectors}}.} \examples{ ## We are not running these examples any more, because they ## take a long time (~20 seconds) to run and this is against the CRAN ## repository policy. Copy and paste them by hand to your R prompt if ## you want to run them. \dontrun{ # SCG of a toy network g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6, 11)) cg <- scg(g, 1, 3, algo="exact_scg") #plot the result layout <- layout.kamada.kawai(g) nt <- vcount(cg$Xt) col <- rainbow(nt) vsize <- table(cg$groups) ewidth <- round(E(cg$Xt)$weight,2) op <- par(mfrow=c(1,2)) plot(g, vertex.color = col[cg$groups], vertex.size = 20, vertex.label = NA, layout = layout) plot(cg$Xt, edge.width = ewidth, edge.label = ewidth, vertex.color = col, vertex.size = 20*vsize/max(vsize), vertex.label=NA, layout = layout.kamada.kawai) par(op) ## SCG of real-world network library(igraphdata) data(immuno) summary(immuno) n <- vcount(immuno) interv <- c(100,100,50,25,12,6,3,2,2) cg <- scg(immuno, ev= n-(1:9), nt=interv, mtype="laplacian", algo="interv", epairs=TRUE) ## are the eigenvalues well-preserved? gt <- cg$Xt nt <- vcount(gt) Lt <- graph.laplacian(gt) evalt <- eigen(Lt, only.values=TRUE)$values[nt-(1:9)] res <- cbind(interv, cg$values, evalt) res <- round(res,5) colnames(res) <- c("interv","lambda_i","lambda_tilde_i") rownames(res) <- c("N-1","N-2","N-3","N-4","N-5","N-6","N-7","N-8","N-9") print(res) ## use SCG to get the communities com <- scg(graph.laplacian(immuno), ev=n-c(1,2), nt=2)$groups col <- rainbow(max(com)) layout <- layout.auto(immuno) plot(immuno, layout=layout, vertex.size=3, vertex.color=col[com], vertex.label=NA) ## display the coarse-grained graph gt <- simplify(as.undirected(gt)) layout.cg <- layout.kamada.kawai(gt) com.cg <- scg(graph.laplacian(gt), nt-c(1,2), 2)$groups vsize <- sqrt(as.vector(table(cg$groups))) op <- par(mfrow=c(1,2)) plot(immuno, layout=layout, vertex.size=3, vertex.color=col[com], vertex.label=NA) plot(gt, layout=layout.cg, vertex.size=15*vsize/max(vsize), vertex.color=col[com.cg],vertex.label=NA) par(op) } } \keyword{graphs} igraph/man/conversion.Rd0000644000176000001440000000641712240234657015020 0ustar ripleyusers\name{conversion} \alias{get.adjacency} \alias{get.edgelist} \concept{Edge list} \concept{Adjacency list} \title{Convert a graph to an adjacency matrix or an edge list} \description{Sometimes it is useful to have a standard representation of a graph, like an adjacency matrix or an edge list.} \usage{ get.adjacency(graph, type=c("both", "upper", "lower"), attr=NULL, edges=FALSE, names=TRUE, sparse=getIgraphOpt("sparsematrices")) get.edgelist(graph, names=TRUE) } \arguments{ \item{graph}{The graph to convert.} \item{type}{Gives how to create the adjacency matrix for undirected graphs. It is ignored for directed graphs. Possible values: \code{upper}: the upper right triangle of the matrix is used, \code{lower}: the lower left triangle of the matrix is used. \code{both}: the whole matrix is used, a symmetric matrix is returned.} \item{attr}{Either \code{NULL} or a character string giving an edge attribute name. If \code{NULL} a traditional adjacency matrix is returned. If not \code{NULL} then the values of the given edge attribute are included in the adjacency matrix. If the graph has multiple edges, the edge attribute of an arbitrarily chosen edge (for the multiple edges) is included. This argument is ignored if \code{edges} is \code{TRUE}. Note that this works only for certain attribute types. If the \code{sparse} argumen is \code{TRUE}, then the attribute must be either logical or numeric. If the \code{sparse} argument is \code{FALSE}, then character is also allowed. The reason for the difference is that the \code{Matrix} package does not support character sparse matrices yet. } \item{edges}{Logical scalar, whether to return the edge ids in the matrix. For non-existant edges zero is returned.} \item{names}{Logical constant. For \code{graph.adjacenct} it gives whether to assign row and column names to the matrix. These are only assigned if the \code{name} vertex attribute is present in the graph. for \code{get.edgelist} it gives whether to return a character matrix containing vertex names (ie. the \code{name} vertex attribute) if they exist or numeric vertex ids. } \item{sparse}{Logical scalar, whether to create a sparse matrix. The \sQuote{\code{Matrix}} package must be installed for creating sparse matrices.} } \details{ \code{get.adjacency} returns the adjacency matrix of a graph, a regular \R matrix if \code{sparse} is \code{FALSE}, or a sparse matrix, as defined in the \sQuote{\code{Matrix}} package, if \code{sparse} if \code{TRUE}. \code{get.edgelist} returns the list of edges in a graph. } \value{ A \code{vcount(graph)} by \code{vcount(graph)} (usually) numeric matrix for \code{get.adjacency}. (This can be huge!) Note that a non-numeric matrix might be returned if \code{attr} is a non-numeric edge attribute. A \code{ecount(graph)} by 2 numeric matrix for \code{get.edgelist}. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.adjacency}}, \code{\link{read.graph}}} \examples{ g <- erdos.renyi.game(10, 2/10) get.edgelist(g) get.adjacency(g) V(g)$name <- letters[1:vcount(g)] get.adjacency(g) E(g)$weight <- runif(ecount(g)) get.adjacency(g, attr="weight") } \keyword{graphs} igraph/man/vertex.connectivity.Rd0000644000176000001440000000721712240234657016664 0ustar ripleyusers\name{vertex.connectivity} \alias{vertex.connectivity} \alias{vertex.disjoint.paths} \alias{graph.cohesion} \concept{Vertex connectivity} \concept{Vertex-disjoint paths} \concept{Graph cohesion} \title{Vertex connectivity.} \description{The vertex connectivity of a graph or two vertices, this is recently also called group cohesion.} \usage{ vertex.connectivity(graph, source=NULL, target=NULL, checks=TRUE) vertex.disjoint.paths(graph, source, target) graph.cohesion(graph, checks=TRUE) } \arguments{ \item{graph}{The input graph.} \item{source}{The id of the source vertex, for \code{vertex.connectivity} it can be \code{NULL}, see details below.} \item{target}{The id of the target vertex, for \code{vertex.connectivity} it can be \code{NULL}, see details below.} \item{checks}{Logical constant. Whether to check that the graph is connected and also the degree of the vertices. If the graph is not (strongly) connected then the connectivity is obviously zero. Otherwise if the minimum degree is one then the vertex connectivity is also one. It is a good idea to perform these checks, as they can be done quickly compared to the connectivity calculation itself. They were suggested by Peter McMahan, thanks Peter.} } \details{ The vertex connectivity of two vertices (\code{source} and \code{target}) in a directed graph is the minimum number of vertices needed to remove from the graph to eliminate all (directed) paths from \code{source} to \code{target}. \code{vertex.connectivity} calculates this quantity if both the \code{source} and \code{target} arguments are given and they're not \code{NULL}. The vertex connectivity of a graph is the minimum vertex connectivity of all (ordered) pairs of vertices in the graph. In other words this is the minimum number of vertices needed to remove to make the graph not strongly connected. (If the graph is not strongly connected then this is zero.) \code{vertex.connectivity} calculates this quantitty if neither the \code{source} nor \code{target} arguments are given. (Ie. they are both \code{NULL}.) A set of vertex disjoint directed paths from \code{source} to \code{vertex} is a set of directed paths between them whose vertices do not contain common vertices (apart from \code{source} and \code{target}). The maximum number of vertex disjoint paths between two vertices is the same as their vertex connectivity in most cases (if the two vertices are not connected by an edge). The cohesion of a graph (as defined by White and Harary, see references), is the vertex connectivity of the graph. This is calculated by \code{graph.cohesion}. These three functions essentially calculate the same measure(s), more precisely \code{vertex.connectivity} is the most general, the other two are included only for the ease of using more descriptive function names. } \value{ A scalar real value. } \references{White, Douglas R and Frank Harary 2001. The Cohesiveness of Blocks In Social Networks: Node Connectivity and Conditional Density. \emph{Sociological Methodology} 31 (1) : 305-359.} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.maxflow}}, \code{\link{edge.connectivity}}, \code{\link{edge.disjoint.paths}}, \code{\link{graph.adhesion}}} \examples{ g <- barabasi.game(100, m=1) g <- delete.edges(g, E(g)[ 100 \%--\% 1 ]) g2 <- barabasi.game(100, m=5) g2 <- delete.edges(g2, E(g2)[ 100 \%--\% 1]) vertex.connectivity(g, 100, 1) vertex.connectivity(g2, 100, 1) vertex.disjoint.paths(g2, 100, 1) g <- erdos.renyi.game(50, 5/50) g <- as.directed(g) g <- induced.subgraph(g, subcomponent(g, 1)) graph.cohesion(g) } \keyword{graphs} igraph/man/graph.union.Rd0000644000176000001440000000376112251656216015063 0ustar ripleyusers\name{graph.union} \alias{graph.union} \alias{\%u\%} \concept{Graph operators} \title{Union of graphs} \description{The union of two or more graphs are created. The graphs may have identical or overlapping vertex sets.} \usage{ graph.union(\dots, byname = "auto") } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} \item{byname}{A logical scalar, or the character scalar \code{auto}. Whether to perform the operation based on symbolic vertex names. If it is \code{auto}, that means \code{TRUE} if all graphs are named and \code{FALSE} otherwise. A warning is generated if \code{auto} and some (but not all) graphs are named.} } \details{ \code{graph.union} creates the union of two or more graphs. Edges which are included in at least one graph will be part of the new graph. This function can be also used via the \%u\% operator. If the \code{byname} argument is \code{TRUE} (or \code{auto} and all graphs are named), then the operation is performed on symbolic vertex names instead of the internal numeric vertex ids. \code{graph.union} keeps the attributes of all graphs. All graph, vertex and edge attributes are copied to the result. If an attribute is present in multiple graphs and would result a name clash, then this attribute is renamed by adding suffixes: _1, _2, etc. The \code{name} vertex attribute is treated specially if the operation is performed based on symbolic vertex names. In this case \code{name} must be present in all graphs, and it is not renamed in the result graph. An error is generated if some input graphs are directed and others are undirected. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ ## Union of two social networks with overlapping sets of actors net1 <- graph.formula(D-A:B:F:G, A-C-F-A, B-E-G-B, A-B, F-G, H-F:G, H-I-J) net2 <- graph.formula(D-A:F:Y, B-A-X-F-H-Z, F-Y) str(net1 \%u\% net2) } \keyword{graphs} igraph/man/igraph.console.Rd0000644000176000001440000000164312271617616015546 0ustar ripleyusers\name{igraph console} \alias{igraph.console} \concept{The igraph console} \title{The igraph console} \description{The igraph console is a GUI windows that shows what the currently running igraph function is doing.} \usage{ igraph.console() } %\arguments{} \details{ The console can be started by calling the \code{igraph.console} function. Then it stays open, until the user closes it. Another way to start it to set the \code{verbose} igraph option to \dQuote{tkconsole} via \code{igraph.options}. Then the console (re)opens each time an igraph function supporting it starts; to close it, set the \code{verbose} option to another value. The console is written in Tcl/Tk and required the \code{tcltk} package. } \value{\code{NULL}, invisibly.} % \references \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{igraph.options}} and the \code{verbose} option.} %\examples{} \keyword{graphs} igraph/man/degree.sequence.game.Rd0000644000176000001440000000744612240234657016610 0ustar ripleyusers\name{degree.sequence.game} \alias{degree.sequence.game} \concept{Degree sequence} \concept{Configuration model} \title{Generate random graphs with a given degree sequence} \description{It is often useful to create a graph with given vertex degrees. This is exactly what \code{degree.sequence.game} does.} \usage{ degree.sequence.game(out.deg, in.deg = NULL, method = c("simple", "vl", "simple.no.multiple"), \dots) } \arguments{ \item{out.deg}{Numeric vector, the sequence of degrees (for undirected graphs) or out-degrees (for directed graphs). For undirected graphs its sum should be even. For directed graphs its sum should be the same as the sum of \code{in.deg}.} \item{in.deg}{For directed graph, the in-degree sequence. By default this is \code{NULL} and an undirected graph is created.} \item{method}{Character, the method for generating the graph. Right now the \dQuote{simple}, \dQuote{simple.no.multiple} and \dQuote{vl} methods are implemented.} \item{\dots}{Additional arguments, these are used as graph attributes.} } \details{The \dQuote{simple} method connects the out-stubs of the edges (undirected graphs) or the out-stubs and in-stubs (directed graphs) together. This way loop edges and also multiple edges may be generated. This method is not adequate if one needs to generate simple graphs with a given degree sequence. The multiple and loop edges can be deleted, but then the degree sequence is distorted and there is nothing to ensure that the graphs are sampled uniformly. The \dQuote{simple.no.multiple} method is similar to \dQuote{simple}, but tries to avoid multiple and loop edges and restarts the generation from scratch if it gets stuck. It is not guaranteed to sample uniformly from the space of all possible graphs with the given sequence, but it is relatively fast and it will eventually succeed if the provided degree sequence is graphical, but there is no upper bound on the number of iterations. The \dQuote{vl} method is a more sophisticated generator. The algorithm and the implementation was done by Fabien Viger and Matthieu Latapy. This generator always generates undirected, connected simple graphs, it is an error to pass the \code{in.deg} argument to it. The algorithm relies on first creating an initial (possibly unconnected) simple undirected graph with the given degree sequence (if this is possible at all). Then some rewiring is done to make the graph connected. Finally a Monte-Carlo algorithm is used to randomize the graph. The \dQuote{vl} samples from the undirected, connected simple graphs unformly. See \url{http://www-rp.lip6.fr/~latapy/FV/generation.html} for details. } \value{The new graph object.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{erdos.renyi.game}}, \code{\link{barabasi.game}}, \code{\link{simplify}} to get rid of the multiple and/or loops edges.} \examples{ ## The simple generator g <- degree.sequence.game(rep(2,100)) degree(g) is.simple(g) # sometimes TRUE, but can be FALSE g2 <- degree.sequence.game(1:10, 10:1) degree(g2, mode="out") degree(g2, mode="in") ## The vl generator g3 <- degree.sequence.game(rep(2,100), method="vl") degree(g3) is.simple(g3) # always TRUE ## Exponential degree distribution ## Note, that we correct the degree sequence if its sum is odd degs <- sample(1:100, 100, replace=TRUE, prob=exp(-0.5*(1:100))) if (sum(degs) \%\% 2 != 0) { degs[1] <- degs[1] + 1 } g4 <- degree.sequence.game(degs, method="vl") all(degree(g4) == degs) ## Power-law degree distribution ## Note, that we correct the degree sequence if its sum is odd degs <- sample(1:100, 100, replace=TRUE, prob=(1:100)^-2) if (sum(degs) \%\% 2 != 0) { degs[1] <- degs[1] + 1 } g5 <- degree.sequence.game(degs, method="vl") all(degree(g5) == degs) } \keyword{graphs} igraph/man/graph.motifs.Rd0000644000176000001440000000423612240234657015231 0ustar ripleyusers\name{graph-motifs} \alias{graph.motifs} \alias{graph.motifs.no} \alias{graph.motifs.est} \concept{Graph motif} \title{Graph motifs} \description{Graph motifs are small connected subgraphs with a well-defined structure. These functions search a graph for various motifs.} \usage{ graph.motifs(graph, size = 3, cut.prob = rep(0, size)) graph.motifs.no(graph, size = 3, cut.prob = rep(0, size)) graph.motifs.est(graph, size = 3, cut.prob = rep(0, size), sample.size = vcount(graph)/10, sample = NULL) } \arguments{ \item{graph}{Graph object, the input graph.} \item{size}{The size of the motif, currently 3 and 4 are supported only.} \item{cut.prob}{Numeric vector giving the probabilities that the search graph is cut at a certain level. Its length should be the same as the size of the motif (the \code{size} argument). By default no cuts are made.} \item{sample.size}{The number of vertices to use as a starting point for finding motifs. Only used if the \code{sample} argument is \code{NULL}.} \item{sample}{If not \code{NULL} then it specifies the vertices to use as a starting point for finding motifs.} } \details{ \code{graph.motifs} searches a graph for motifs of a given size and returns a numeric vector containing the number of different motifs. The order of the motifs is defined by their isomorphism class, see \code{\link{graph.isoclass}}. \code{graph.motifs.no} calculates the total number of motifs of a given size in graph. \code{graph.motifs.est} estimates the total number of motifs of a given size in a graph based on a sample. } \value{ \code{graph.motifs} returns a numeric vector, the number of occurences of each motif in the graph. The motifs are ordered by their isomorphism classes. Note that for unconnected subgraphs, which are not considered to be motifs, the result will be \code{NA}. \code{graph.motifs.no} and \code{graph.motifs.est} return a numeric constant. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.isoclass}}} \examples{ g <- barabasi.game(100) graph.motifs(g, 3) graph.motifs.no(g, 3) graph.motifs.est(g, 3) } \keyword{graphs} igraph/man/minimum.spanning.tree.Rd0000644000176000001440000000356612240234657017062 0ustar ripleyusers\name{minimum.spanning.tree} \alias{minimum.spanning.tree} \concept{Minimum spanning tree} \title{Minimum spanning tree} \description{A subgraph of a connected graph is a \emph{minimum spanning tree} if it is tree, and the sum of its edge weights are the minimal among all tree subgraphs of the graph. A minimum spanning forest of a graph is the graph consisting of the minimum spanning trees of its components.} \usage{ minimum.spanning.tree(graph, weights=NULL, algorithm=NULL, \dots) } \arguments{ \item{graph}{The graph object to analyze.} \item{weights}{Numeric algorithm giving the weights of the edges in the graph. The order is determined by the edge ids. This is ignored if the \code{unweighted} algorithm is chosen } \item{algorithm}{The algorithm to use for calculation. \code{unweighted} can be used for unwieghted graphs, and \code{prim} runs Prim's algorithm for weighted graphs. If this is \code{NULL} then igraph tries to select the algorithm automatically: if the graph has an edge attribute called \code{weight} of the \code{weights} argument is not \code{NULL} then Prim's algorithm is chosen, otherwise the unwweighted algorithm is performed. } \item{\dots}{Additional arguments, unused.} } \details{ If the graph is unconnected a minimum spanning forest is returned. } \value{ A graph object with the minimum spanning forest. (To check that it is a tree check that the number of its edges is \code{vcount(graph)-1}.) The edge and vertex attributes of the original graph are preserved in the result. } \references{ Prim, R.C. 1957. Shortest connection networks and some generalizations \emph{Bell System Technical Journal}, 37 1389--1401.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{clusters}}} \examples{ g <- erdos.renyi.game(100, 3/100) mst <- minimum.spanning.tree(g) } \keyword{graphs} igraph/man/write.graph.Rd0000644000176000001440000002125112251656216015057 0ustar ripleyusers\name{write.graph} \alias{write.graph} \title{Writing the graph to a file in some format} \description{\code{write.graph} is a general function for exporting graphs to foreign file formats, however not many formats are implemented right now.} \usage{ write.graph(graph, file, format=c("edgelist", "pajek", "ncol", "lgl", "graphml", "dimacs", "gml", "dot", "leda"), \dots) } \arguments{ \item{graph}{The graph to export.} \item{file}{A connection or a string giving the file name to write the graph to.} \item{format}{Character string giving the file format. Right now \code{pajek}, \code{graphml}, \code{dot}, \code{gml}, \code{edgelist}, \code{lgl}, \code{ncol} and \code{dimacs} are implemented. As of igraph 0.4 this argument is case insensitive. } \item{\dots}{Other, format specific arguments, see below.} } \section{Edge list format}{ The \code{edgelist} format is a simple text file, with one edge in a line, the two vertex ids separated by a space character. The file is sorted by the first and the second column. This format has no additional arguments. } \section{Pajek format}{ The Pajek format is a text file, see \code{\link{read.graph}} for details. Appropriate vertex and edge attributes are also written to the file. This format has no additional arguments. From version 0.6.1 igraph handles bipartite graphs when writing to Pajek files. As Pajek is less flexible for bipartite graphs (the numeric ids of the vertices must be sorted according to vertex type), igraph might need to reorder the vertices when writing a bipartite Pajek file. This effectively means that numeric vertex ids usually change when a bipartite graph is written to a Pajek file, and then read back into igraph. } \section{GraphML format}{ The GraphML format is a flexible XML based format. See \code{\link{read.graph}} for GraphML details. Vertex and edge attributes are also written to the file. Additional argument: \describe{ \item{prefixAttr}{Logical scalar, whether you want to add a prefix to the graph, vertex and edge attribute names, to ensure their uniqueness. Defaults to \code{TRUE}.} } } \section{Dot format}{ The dot format is used by the popular GraphViz program. Vertex and edge attributes are written to the file. There are no additional arguments for this format. } \section{LGL format}{ The \code{lgl} format is also a simple text file, this is the format expected by the 'Large Graph Layout' layout generator software. See \link{read.graph} for details. Additional arguments: \describe{ \item{names}{If you want to write symbolic vertex names instead of vertex ids, supply the name of the vertex attribute containing the symbolic names here. By default the \sQuote{name} attribute is used if there is one. Supply \code{NULL} if you want to use numeric vertex ids even if there is a \sQuote{name} vertex attribute.} \item{weights}{If you want to write edge weights to the file, supply the name of the edge attribute here. By defaults the vertex attribute \sQuote{weights} are used if they are installed. Supply \code{NULL} here if you want to omit the weights.} \item{isolates}{Logical, if \code{TRUE} the isolate vertices are also written to the file, they are omitted by default.} } } \section{NCOL format}{ The \code{ncol} format is also used by LGL, it is a text file, see \link{read.graph} for details. Additional arguments: \describe{ \item{names}{If you want to write symbolic vertex names instead of vertex ids, supply the name of the vertex attribute containing the symbolic names here. By default the \sQuote{name} attribute is used if there is one. Supply \code{NULL} if you want to use numeric vertex ids even if there is a \sQuote{name} vertex attribute.} \item{weights}{If you want to write edge weights to the file, supply the name of the edge attribute here. By defaults the vertex attribute \sQuote{weights} are used if they are installed. Supply \code{NULL} here if you want to omit the weights.} } } \section{Dimacs format}{ The \code{dimacs} file format, more specifically the version for network flow problems, see the files at \url{ftp://dimacs.rutgers.edu/pub/netflow/general-info/} This is a line-oriented text file (ASCII) format. The first character of each line defines the type of the line. If the first character is \code{c} the line is a comment line and it is ignored. There is one problem line (\code{p} in the file, it must appear before any node and arc descriptor lines. The problem line has three fields separated by spaces: the problem type (\code{min}, \code{max} or \code{asn}), the number of vertices and number of edges in the graph. Exactly two node identification lines are expected (\code{n}), one for the source, one for the target vertex. These have two fields: the id of the vertex and the type of the vertex, either \code{s} (=source) or \code{t} (=target). Arc lines start with \code{a} and have three fields: the source vertex, the target vertex and the edge capacity. Vertex ids are numbered from 1. Additional arguments: \describe{ \item{source}{The id of the source vertex, if \code{NULL} (the default) then it is taken from the \code{source} graph attribute.} \item{target}{The id of the target vertex, if \code{NULL} (the default) then it is taken from the \code{target} graph attribute.} \item{capacity}{A numeric vector giving the edge capacities. If \code{NULL} (the default) then it is taken from the \code{capacity} edge attribute.} } } \section{GML file format}{ GML is a quite general textual format, see \url{http://www.infosun.fim.uni-passau.de/Graphlet/GML/} for details. The graph, vertex and edges attributes are written to the file as well, if they are numeric of string. As igraph is more forgiving about attribute names, it might be neccessary to simplify the them before writing to the GML file. This way we'll have a syntactically correct GML file. The following simple procedure is performed on each attribute name: first the alphanumeric characters are extracted, the others are ignored. Then if the first character is not a letter then the attribute name is prefixed with igraph. Note that this might result identical names for two attributes, igraph does not check this. The \dQuote{id} vertex attribute is treated specially. If the \code{id} argument is not \code{NULL} then it should be a numeric vector with the vertex ids and the \dQuote{id} vertex attribute is ignored (if there is one). If \code{id} is 0 and there is a numeric \code{id} vertex attribute that is used instead. If ids are not specified in either way then the regular igraph vertex ids are used. Note that whichever way vertex ids are specified, their uniqueness is not checked. If the graph has edge attributes named \dQuote{source} or \dQuote{target} they're silently ignored. GML uses these attributes to specify the edges, so we cannot write them to the file. Rename them before calling this function if you want to preserve them. Additional arguments: \describe{ \item{id}{\code{NULL} or a numeric vector giving the vertex ids. See details above.} \item{creator}{A character scalar to be added to the \dQuote{Creator} line in the GML file. If this is \code{NULL} (the default) then the current date and time is added.} } } \section{LEDA file format}{ LEDA is a library for efficient data types and algorithms. The support for the LEDA format is very basic at the moment; igraph writes only the LEDA graph section which supports one selected vertex and edge attribute and no layout information or visual attributes. See \url{http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html} for the details of this format. Additional arguments: \describe{ \item{vertex.attr}{The name of the vertex attribute whose values are to be stored in the output or \code{NULL} if no vertex attribute has to be stored.} \item{edge.attr}{The name of the edge attribute whose values are to be stored in the output or \code{NULL} if no edge attribute has to be stored.} } } \value{A NULL, invisibly.} \references{Adai AT, Date SV, Wieland S, Marcotte EM. LGL: creating a map of protein function with an algorithm for visualizing very large biological networks. \emph{J Mol Biol.} 2004 Jun 25;340(1):179-90.} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{read.graph}} } \examples{ g <- graph.ring(10) \dontrun{write.graph(g, "/tmp/g.txt", "edgelist")} } \keyword{graphs} igraph/man/community.edge.betweenness.Rd0000644000176000001440000001045412240234657020077 0ustar ripleyusers\name{edge.betweenness.community} \alias{edge.betweenness.community} \alias{edge.betweenness.community.merges} \concept{Edge betweenness} \concept{Community structure} \title{Community structure detection based on edge betweenness} \description{Many networks consist of modules which are densely connected themselves but sparsely connected to other modules.} \usage{ edge.betweenness.community (graph, weights = E(graph)$weight, directed = TRUE, edge.betweenness = TRUE, merges = TRUE, bridges = TRUE, modularity = TRUE, membership = TRUE) } \arguments{ \item{graph}{The graph to analyze.} \item{weights}{The edge weights. Supply \code{NULL} to omit edge weights. By default the \sQuote{\code{weight}} edge attribute is used, if it is present.} \item{directed}{Logical constant, whether to calculate directed edge betweenness for directed graphs. It is ignored for undirected graphs.} \item{edge.betweenness}{Logical constant, whether to return the edge betweenness of the edges at the time of their removal.} \item{merges}{Logical constant, whether to return the merge matrix representing the hierarchical community structure of the network. This argument is called \code{merges}, even if the community structure algorithm itself is divisive and not agglomerative: it builds the tree from top to bottom. There is one line for each merge (i.e. split) in matrix, the first line is the first merge (last split). The communities are identified by integer number starting from one. Community ids smaller than or equal to \eqn{N}, the number of vertices in the graph, belong to singleton communities, ie. individual vertices. Before the first merge we have \eqn{N} communities numbered from one to \eqn{N}. The first merge, the first line of the matrix creates community \eqn{N+1}, the second merge creates community \eqn{N+2}, etc. } \item{bridges}{Logical constant, whether to return a list the edge removals which actually splitted a component of the graph.} \item{modularity}{Logical constant, whether to calculate the maximum modularity score, considering all possibly community structures along the edge-betweenness based edge removals.} \item{membership}{Logical constant, whether to calculate the membership vector corresponding to the highest possible modularity score.} } \details{ The edge betweenness score of an edge measures the number of shortest paths through it, see \code{\link{edge.betweenness}} for details. The idea of the edge betweenness based community structure detection is that it is likely that edges connecting separate modules have high edge betweenness as all the shortest paths from one module to another must traverse through them. So if we gradually remove the edge with the highest edge betweenness score we will get a hierarchical map, a rooted tree, called a dendrogram of the graph. The leafs of the tree are the individual vertices and the root of the tree represents the whole graph. \code{edge.betweenness.community} performs this algorithm by calculating the edge betweenness of the graph, removing the edge with the highest edge betweenness score, then recalculating edge betweenness of the edges and again removing the one with the highest score, etc. \code{edge.betweeness.community} returns various information collected throught the run of the algorithm. See the return value down here. } \value{ \code{edge.betweenness.community} returns a \code{\link{communities}} object, please see the \code{\link{communities}} manual page for details. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{edge.betweenness}} for the definition and calculation of the edge betweenness, \code{\link{walktrap.community}}, \code{\link{fastgreedy.community}}, \code{\link{leading.eigenvector.community}} for other community detection methods. See \code{\link{communities}} for extracting the results of the community detection. } \references{M Newman and M Girvan: Finding and evaluating community structure in networks, \emph{Physical Review E} 69, 026113 (2004) } \examples{ g <- barabasi.game(100,m=2) eb <- edge.betweenness.community(g) g <- graph.full(10) \%du\% graph.full(10) g <- add.edges(g, c(1,11)) eb <- edge.betweenness.community(g) eb } \keyword{graphs} igraph/man/is.mutual.Rd0000644000176000001440000000226212240234657014546 0ustar ripleyusers\name{is.mutual} \alias{is.mutual} \concept{Mutual edges} \concept{Reciprocity} \title{Find mutual edges in a directed graph} \description{This function checks the reciproc pair of the supplied edges.} \usage{ is.mutual(graph, es = E(graph)) } \arguments{ \item{graph}{The input graph.} \item{es}{Edge sequence, the edges that will be probed. By default is includes all edges in the order of their ids.} } \details{ In a directed graph an (A,B) edge is mutual if the graph also includes a (B,A) directed edge. Note that multi-graphs are not handled properly, i.e. if the graph contains two copies of (A,B) and one copy of (B,A), then these three edges are considered to be mutual. Undirected graphs contain only mutual edges by definition. } \value{ A logical vector of the same length as the number of edges supplied. } %\references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{reciprocity}}, \code{\link{dyad.census}} if you just want some statistics about mutual edges.} \examples{ g <- erdos.renyi.game(10,50,type="gnm",directed=TRUE) reciprocity(g) dyad.census(g) is.mutual(g) sum(is.mutual(g))/2 == dyad.census(g)$mut } \keyword{graphs} igraph/man/graph.full.bipartite.Rd0000644000176000001440000000302212240234657016644 0ustar ripleyusers\name{graph.full.bipartite} \alias{graph.full.bipartite} \concept{Bipartite graph} \concept{Two-mode network} \title{Create a full bipartite graph} \description{Bipartite graphs are also called two-mode by some. This function creates a bipartite graph in which every possible edge is present.} \usage{ graph.full.bipartite (n1, n2, directed = FALSE, mode = c("all", "out", "in")) } \arguments{ \item{n1}{The number of vertices of the first kind.} \item{n2}{The number of vertices of the second kind.} \item{directed}{Logical scalar, whether the graphs is directed.} \item{mode}{Scalar giving the kind of edges to create for directed graphs. If this is \sQuote{\code{out}} then all vertices of the first kind are connected to the others; \sQuote{\code{in}} specifies the opposite direction; \sQuote{\code{all}} creates mutual edges. This argument is ignored for undirected graphs.x} } \details{ Bipartite graphs have a \sQuote{\code{type}} vertex attribute in igraph, this is boolean and \code{FALSE} for the vertices of the first kind and \code{TRUE} for vertices of the second kind. } \value{ An igraph graph, with the \sQuote{\code{type}} vertex attribute set. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph.full}} for creating one-mode full graphs} \examples{ g <- graph.full.bipartite(2, 3) g2 <- graph.full.bipartite(2, 3, dir=TRUE) g3 <- graph.full.bipartite(2, 3, dir=TRUE, mode="in") g4 <- graph.full.bipartite(2, 3, dir=TRUE, mode="all") } \keyword{graphs} igraph/man/layout.star.Rd0000644000176000001440000000232212240234657015107 0ustar ripleyusers\name{layout.star} \alias{layout.star} \concept{Graph layout} \title{Generate coordinates to place the vertices of a graph in a star-shape} \description{ A simple layout generator, that places one vertex in the center of a circle and the rest of the vertices equidistantly on the perimeter. } \usage{ layout.star(graph, center = V(graph)[1], order = NULL) } \arguments{ \item{graph}{The graph to layout.} \item{center}{The id of the vertex to put in the center. By default it is the first vertex.} \item{order}{Numeric vector, the order of the vertices along the perimeter. The default ordering is given by the vertex ids.} } \details{ It is possible to choose the vertex that will be in the center, and the order of the vertices can be also given. } \value{A matrix with two columns and as many rows as the number of vertices in the input graph.} %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{layout}} and \code{\link{layout.drl}} for other layout algorithms, \code{\link{plot.igraph}} and \code{\link{tkplot}} on how to plot graphs and \code{\link{graph.star}} on how to create ring graphs.} \examples{ g <- graph.star(10) layout.star(g) } \keyword{graphs} igraph/man/graph.kautz.Rd0000644000176000001440000000222612240234657015063 0ustar ripleyusers\name{graph.kautz} \alias{graph.kautz} \concept{Kautz graph} \title{Kautz graphs} \description{Kautz graphs are labeled graphs representing the overlap of strings. } \usage{ graph.kautz(m,n) } \arguments{ \item{m}{Integer scalar, the size of the alphabet. See details below.} \item{n}{Integer scalar, the length of the labels. See details below.} } \details{ A Kautz graph is a labeled graph, vertices are labeled by strings of length \code{n+1} above an alphabet with \code{m+1} letters, with the restriction that every two consecutive letters in the string must be different. There is a directed edge from a vertex \code{v} to another vertex \code{w} if it is possible to transform the string of \code{v} into the string of \code{w} by removing the first letter and appending a letter to it. Kautz graphs have some interesting properties, see eg. Wikipedia for details. } \value{A graph object.} \author{Gabor Csardi , the first version in R was written by Vincent Matossian.} \seealso{\code{\link{graph.de.bruijn}}, \code{\link{line.graph}}} \examples{ line.graph(graph.kautz(2,1)) graph.kautz(2,2) } \keyword{graphs} igraph/man/tkplot.Rd0000644000176000001440000001420612263023733014137 0ustar ripleyusers\name{tkplot} \alias{tkplot} \alias{tkplot.close} \alias{tkplot.off} \alias{tkplot.fit.to.screen} \alias{tkplot.reshape} \alias{tkplot.export.postscript} \alias{tkplot.canvas} \alias{tkplot.getcoords} \alias{tkplot.setcoords} \alias{tkplot.center} \alias{tkplot.rotate} \concept{Visualization} \title{Interactive plotting of graphs} \description{\code{tkplot} and its companion functions serve as an interactive graph drawing facility. Not all parameters of the plot can be changed interactively right now though, eg. the colors of vertices, edges, and also others have to be pre-defined.} \usage{ tkplot(graph, canvas.width=450, canvas.height=450, \dots) tkplot.close(tkp.id, window.close = TRUE) tkplot.off() tkplot.fit.to.screen(tkp.id, width = NULL, height = NULL) tkplot.reshape(tkp.id, newlayout, \dots) tkplot.export.postscript(tkp.id) tkplot.canvas(tkp.id) tkplot.getcoords(tkp.id, norm = FALSE) tkplot.setcoords(tkp.id, coords) tkplot.center(tkp.id) tkplot.rotate(tkp.id, degree = NULL, rad = NULL) } \arguments{ \item{graph}{The \code{graph} to plot.} \item{canvas.width,canvas.height}{The size of the tkplot drawing area.} \item{tkp.id}{The id of the tkplot window to close/reshape/etc.} \item{window.close}{Leave this on the default value.} \item{width}{The width of the rectangle for generating new coordinates.} \item{height}{The height of the rectangle for generating new coordinates.} \item{newlayout}{The new layout, see the \code{layout} parameter of tkplot.} \item{norm}{Logical, should we norm the coordinates.} \item{coords}{Two-column numeric matrix, the new coordinates of the vertices, in absolute coordinates.} \item{degree}{The degree to rotate the plot.} \item{rad}{The degree to rotate the plot, in radian.} \item{\dots}{Additional plotting parameters. See \link{igraph.plotting} for the complete list.} } \details{ \code{tkplot} is an interactive graph drawing facility. It is not very well developed at this stage, but it should be still useful. It's handling should be quite straightforward most of the time, here are some remarks and hints. There are different popup menus, activated by the right mouse button, for vertices and edges. Both operate on the current selection if the vertex/edge under the cursor is part of the selection and operate on the vertex/edge under the cursor if it is not. One selection can be active at a time, either a vertex or an edge selection. A vertex/edge can be added to a selection by holding the \code{control} key while clicking on it with the left mouse button. Doing this again deselect the vertex/edge. Selections can be made also from the \code{Select} menu. The `Select some vertices' dialog allows to give an expression for the vertices to be selected: this can be a list of numeric R expessions separated by commas, like `\code{1,2:10,12,14,15}' for example. Similarly in the `Select some edges' dialog two such lists can be given and all edges connecting a vertex in the first list to one in the second list will be selected. In the color dialog a color name like 'orange' or RGB notation can also be used. The \code{tkplot} command creates a new Tk window with the graphical representation of \code{graph}. The command returns an integer number, the tkplot id. The other commands utilize this id to be able to query or manipulate the plot. \code{tkplot.close} closes the Tk plot with id \code{tkp.id}. \code{tkplot.off} closes all Tk plots. \code{tkplot.fit.to.screen} fits the plot to the given rectange (\code{width} and \code{height}), if some of these are \code{NULL} the actual phisical width od height of the plot window is used. \code{tkplot.reshape} applies a new layout to the plot, its optional parameters will be collected to a list analogous to \code{layout.par}. \code{tkplot.export.postscript} creates a dialog window for saving the plot in postscript format. \code{tkplot.canvas} returns the Tk canvas object that belongs to a graph plot. The canvas can be directly manipulated then, eg. labels can be added, it could be saved to a file programatically, etc. See an example below. \code{tkplot.getcoords} returns the coordinates of the vertices in a matrix. Each row corresponds to one vertex. \code{tkplot.setcoords} sets the coordinates of the vertices. A two-column matrix specifies the new positions, with each row corresponding to a single vertex. \code{tkplot.center} shifts the figure to the center of its plot window. \code{tkplot.rotate} rotates the figure, its parameter can be given either in degrees or in radians. } \value{ \code{tkplot} returns an integer, the id of the plot, this can be used to manipulate it from the command line. \code{tkplot.canvas} retuns \code{tkwin} object, the Tk canvas. \code{tkplot.getcoords} returns a matrix with the coordinates. \code{tkplot.close}, \code{tkplot.off}, \code{tkplot.fit.to.screen}, \code{tkplot.reshape}, \code{tkplot.export.postscript}, \code{tkplot.center} and \code{tkplot.rotate} return \code{NULL} invisibly. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{plot.igraph}}, \code{\link{layout}}} \examples{ g <- graph.ring(10) \dontrun{tkplot(g)} \dontrun{ ## Saving a tkplot() to a file programatically g <- graph.star(10, center=10) \%u\% graph.ring(9, directed=TRUE) E(g)$width <- sample(1:10, ecount(g), replace=TRUE) lay <- layout.auto(g) id <- tkplot(g, layout=lay) canvas <- tkplot.canvas(id) tkpostscript(canvas, file="/tmp/output.eps") tkplot.close(id) } \dontrun{ ## Setting the coordinates and adding a title label g <- graph.ring(10) id <- tkplot(graph.ring(10), canvas.width=450, canvas.height=500) canvas <- tkplot.canvas(id) padding <- 20 coords <- layout.norm(layout.circle(g), 0+padding, 450-padding, 50+padding, 500-padding) tkplot.setcoords(id, coords) width <- as.numeric(tkcget(canvas, "-width")) height <- as.numeric(tkcget(canvas, "-height")) tkcreate(canvas, "text", width/2, 25, text="My title", justify="center", font=tkfont.create(family="helvetica" ,size=20,weight="bold")) } } \keyword{graphs} igraph/man/rglplot.Rd0000644000176000001440000000201512240234657014304 0ustar ripleyusers\name{rglplot} \alias{rglplot} \alias{rglplot.igraph} \concept{Visualization} \title{3D plotting of graphs with OpenGL} \description{Using the \code{rgl} package, \code{rglplot} plots a graph in 3D. The plot can be zoomed, rotated, shifted, etc. but the coordinates of the vertices is fixed.} \usage{ rglplot(x, \dots) } \arguments{ \item{x}{The graph to plot.} \item{\dots}{Additional arguments, see \code{\link{igraph.plotting}} for the details} } \details{ Note that \code{rglplot} is considered to be highly experimental. It is not very useful either. See \code{\link{igraph.plotting}} for the possible arguments. } \value{\code{NULL}, invisibly.} % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{igraph.plotting}}, \code{\link{plot.igraph}} for the 2D version, \code{\link{tkplot}} for interactive graph drawing in 2D.} \examples{ \dontrun{ g <- graph.lattice( c(5,5,5) ) coords <- layout.fruchterman.reingold(g, dim=3) rglplot(g, layout=coords) } } \keyword{graphs} igraph/man/degree.Rd0000644000176000001440000000344612240234657014065 0ustar ripleyusers\name{degree} \alias{degree} \concept{Vertex degree} \concept{Degree distribution} \alias{degree.distribution} \title{Degree and degree distribution of the vertices} \description{The degree of a vertex is its most basic structural property, the number of its adjacent edges.} \usage{ degree(graph, v=V(graph), mode = c("all", "out", "in", "total"), loops = TRUE, normalized = FALSE) degree.distribution(graph, cumulative = FALSE, \dots) } \arguments{ \item{graph}{The graph to analyze.} \item{v}{The ids of vertices of which the degree will be calculated.} \item{mode}{Character string, \dQuote{out} for out-degree, \dQuote{in} for in-degree or \dQuote{total} for the sum of the two. For undirected graphs this argument is ignored. \dQuote{all} is a synonym of \dQuote{total}.} \item{loops}{Logical; whether the loop edges are also counted.} \item{normalized}{Logical scalar, whether to normalize the degree. If \code{TRUE} then the result is divided by \eqn{n-1}, where \eqn{n} is the number of vertices in the graph.} \item{cumulative}{Logical; whether the cumulative degree distribution is to be calculated.} \item{\dots}{Additional arguments to pass to \code{degree}, eg. \code{mode} is useful but also \code{v} and \code{loops} make sense.} } % \details{} \value{ For \code{degree} a numeric vector of the same length as argument \code{v}. For \code{degree.distribution} a numeric vector of the same length as the maximum degree plus one. The first element is the relative frequency zero degree vertices, the second vertices with degree one, etc. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ g <- graph.ring(10) degree(g) g2 <- erdos.renyi.game(1000, 10/1000) degree.distribution(g2) } \keyword{graphs} igraph/man/stochasticMatrix.Rd0000644000176000001440000000302112251656216016151 0ustar ripleyusers\name{get.stochastic} \alias{get.stochastic} \title{Stochastic matrix of a graph} \description{Retrieves the stochastic matrix of a graph of class \code{igraph}. } \usage{ get.stochastic(graph, column.wise = FALSE, sparse = getIgraphOpt("sparsematrices")) } \arguments{ \item{graph}{The input graph. Must be of class \code{igraph}.} \item{column.wise}{If \code{FALSE}, then the rows of the stochastic matrix sum up to one; otherwise it is the columns.} \item{sparse}{Logical scalar, whether to return a sparse matrix. The \code{Matrix} package is needed for sparse matrices.} } \details{ Let \eqn{M} be an \eqn{n \times n}{n x n} adjacency matrix with real non-negative entries. Let us define \eqn{D = \textrm{diag}(\sum_{i}M_{1i}, \dots, \sum_{i}M_{ni})}{% D=diag( sum(M[1,i], i), ..., sum(M[n,i], i) )} The (row) stochastic matrix is defined as \deqn{W = D^{-1}M,}{W = inv(D) M,} where it is assumed that \eqn{D} is non-singular. Column stochastic matrices are defined in a symmetric way. } \value{ A regular \R matrix or a matrix of class \code{Matrix} if a \code{sparse} argument was \code{TRUE}. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{get.adjacency}}} \examples{ library(Matrix) ## g is a large sparse graph g <- barabasi.game(n = 10^5, power = 2, directed = FALSE) W <- get.stochastic(g, sparse=TRUE) ## a dense matrix here would probably not fit in the memory class(W) ## may not be exactly 1, due to numerical errors max(abs(rowSums(W))-1) } \keyword{graphs} igraph/man/bipartite.projection.Rd0000644000176000001440000000665412325262301016763 0ustar ripleyusers\name{bipartite.projection} \alias{bipartite.projection} \alias{bipartite.projection.size} \concept{Bipartite graph} \concept{Two-mode network} \title{Project a bipartite graph} \description{A bipartite graph is projected into two one-mode networks} \usage{ bipartite.projection.size(graph, types = NULL) bipartite.projection (graph, types = NULL, multiplicity = TRUE, probe1 = NULL, which=c("both", "true", "false"), remove.type = TRUE) } \arguments{ \item{graph}{The input graph. It can be directed, but edge directions are ignored during the computation.} \item{types}{An optional vertex type vector to use instead of the \sQuote{\code{type}} vertex attribute. You must supply this argument if the graph has no \sQuote{\code{type}} vertex attribute.} \item{multiplicity}{If \code{TRUE}, then igraph keeps the multiplicity of the edges as an edge attribute. E.g. if there is an A-C-B and also an A-D-B triple in the bipartite graph (but no more X, such that A-X-B is also in the graph), then the multiplicity of the A-B edge in the projection will be 2.} \item{probe1}{This argument can be used to specify the order of the projections in the resulting list. If given, then it is considered as a vertex id (or a symbolic vertex name); the projection containing this vertex will be the first one in the result list. This argument is ignored if only one projection is requested in argument \code{which}.} \item{which}{A character scalar to specify which projection(s) to calculate. The default is to calculate both.} \item{remove.type}{Logical scalar, whether to remove the \code{type} vertex attribute from the projections. This makes sense because these graphs are not bipartite any more. However if you want to combine them with each other (or other bipartite graphs), then it is worth keeping this attribute. By default it will be removed.} } \details{ Bipartite graphs have a \code{type} vertex attribute in igraph, this is boolean and \code{FALSE} for the vertices of the first kind and \code{TRUE} for vertices of the second kind. \code{bipartite.projection.size} calculates the number of vertices and edges in the two projections of the bipartite graphs, without calculating the projections themselves. This is useful to check how much memory the projections would need if you have a large bipartite graph. \code{bipartite.projections} calculates the actual projections. You can use the \code{probe1} argument to specify the order of the projections in the result. By default vertex type \code{FALSE} is the first and \code{TRUE} is the second. \code{bipartite.projections} keeps vertex attributes. } \value{ A list of two undirected graphs. See details above. } % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} % \seealso{} \examples{ ## Projection of a full bipartite graph is a full graph g <- graph.full.bipartite(10,5) proj <- bipartite.projection(g) graph.isomorphic(proj[[1]], graph.full(10)) graph.isomorphic(proj[[2]], graph.full(5)) ## The projection keeps the vertex attributes M <- matrix(0, nr=5, nc=3) rownames(M) <- c("Alice", "Bob", "Cecil", "Dan", "Ethel") colnames(M) <- c("Party", "Skiing", "Badminton") M[] <- sample(0:1, length(M), replace=TRUE) M g2 <- graph.incidence(M) g2$name <- "Event network" proj2 <- bipartite.projection(g2) print(proj2[[1]], g=TRUE, e=TRUE) print(proj2[[2]], g=TRUE, e=TRUE) } \keyword{graphs} igraph/man/graph.formula.Rd0000644000176000001440000000773012252321507015371 0ustar ripleyusers\name{graph.formula} \alias{graph.formula} \title{Creating (small) graphs via a simple interface} \description{ This function is useful if you want to create a small (named) graph quickly, it works for both directed and undirected graphs. } \usage{ graph.formula(..., simplify = TRUE) } \arguments{ \item{...}{The formulae giving the structure of the graph, see details below.} \item{simplify}{Logical scalar, whether to call \code{\link{simplify}} on the created graph. By default the graph is simplified, loop and multiple edges are removed.} } \details{ \code{graph.formula} is very handy for creating small graphs quickly. You need to supply one or more R expressions giving the structure of the graph. The expressions consist of vertex names and edge operators. An edge operator is a sequence of \sQuote{\code{-}} and \sQuote{\code{+}} characters, the former is for the edges and the latter is used for arrow heads. The edges can be arbitrarily long, ie. you may use as many \sQuote{\code{-}} characters to \dQuote{draw} them as you like. If all edge operators consist of only \sQuote{\code{-}} characters then the graph will be undirected, whereas a single \sQuote{\code{+}} character implies a directed graph. Let us see some simple examples. Without arguments the function creates an empty graph: \preformatted{ graph.formula() } A simple undirected graph with two vertices called \sQuote{A} and \sQuote{B} and one edge only: \preformatted{ graph.formula(A-B) } Remember that the length of the edges does not matter, so we could have written the following, this creates the same graph: \preformatted{ graph.formula( A-----B ) } If you have many disconnected components in the graph, separate them with commas. You can also give isolate vertices. \preformatted{ graph.formula( A--B, C--D, E--F, G--H, I, J, K ) } The \sQuote{\code{:}} operator can be used to define vertex sets. If an edge operator connects two vertex sets then every vertex from the first set will be connected to every vertex in the second set. The following form creates a full graph, including loop edges: \preformatted{ graph.formula( A:B:C:D -- A:B:C:D ) } In directed graphs, edges will be created only if the edge operator includes a arrow head (\sQuote{+}) \emph{at the end} of the edge: \preformatted{ graph.formula( A -+ B -+ C ) graph.formula( A +- B -+ C ) graph.formula( A +- B -- C ) } Thus in the third example no edge is created between vertices \code{B} and \code{C}. Mutual edges can be also created with a simple edge operator: \preformatted{ graph.formula( A +-+ B +---+ C ++ D + E) } Note again that the length of the edge operators is arbitrary, \sQuote{\code{+}}, \sQuote{\code{++}} and \sQuote{\code{+-----+}} have exactly the same meaning. If the vertex names include spaces or other special characters then you need to quote them: \preformatted{ graph.formula( "this is" +- "a silly" -+ "graph here" ) } You can include any character in the vertex names this way, even \sQuote{+} and \sQuote{-} characters. See more examples below. } \value{ A new graph object. } % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{graph}} for more general graph creation methods.} \examples{ # A simple undirected graph g <- graph.formula( Alice-Bob-Cecil-Alice, Daniel-Cecil-Eugene, Cecil-Gordon ) g # Another undirected graph, ":" notation g2 <- graph.formula( Alice-Bob:Cecil:Daniel, Cecil:Daniel-Eugene:Gordon ) g2 # A directed graph g3 <- graph.formula( Alice +-+ Bob --+ Cecil +-- Daniel, Eugene --+ Gordon:Helen ) g3 # A graph with isolate vertices g4 <- graph.formula( Alice -- Bob -- Daniel, Cecil:Gordon, Helen ) g4 V(g4)$name # "Arrows" can be arbitrarily long g5 <- graph.formula( Alice +---------+ Bob ) g5 # Special vertex names g6 <- graph.formula( "+" -- "-", "*" -- "/", "\%\%" -- "\%/\%" ) g6 } \keyword{graphs} igraph/man/graph.density.Rd0000644000176000001440000000275312240234657015411 0ustar ripleyusers\name{graph.density} \alias{graph.density} \concept{Graph density} \title{Graph density} \description{The density of a graph is the ratio of the number of edges and the number of possible edges.} \usage{ graph.density(graph, loops=FALSE) } \arguments{ \item{graph}{The input graph.} \item{loops}{Logical constant, whether to allow loop edges in the graph. If this is TRUE then self loops are considered to be possible. If this is FALSE then we assume that the graph does not contain any loop edges and that loop edges are not meaningful.} } \details{ Note that this function may return strange results for graph with multiple edges, density is ill-defined for graphs with multiple edges. } \value{ A real constant. This function returns \code{NaN} (=0.0/0.0) for an empty graph with zero vertices. } \references{ Wasserman, S., and Faust, K. (1994). Social Network Analysis: Methods and Applications. Cambridge: Cambridge University Press. } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{vcount}}, \code{\link{ecount}}, \code{\link{simplify}} to get rid of the multiple and/or loop edges.} \examples{ g1 <- graph.empty(n=10) g2 <- graph.full(n=10) g3 <- erdos.renyi.game(n=10, 0.4) # loop edges g <- graph( c(1,2, 2,2, 2,3) ) graph.density(g, loops=FALSE) # this is wrong!!! graph.density(g, loops=TRUE) # this is right!!! graph.density(simplify(g), loops=FALSE) # this is also right, but different } \keyword{graphs} igraph/man/static.power.law.game.Rd0000644000176000001440000000665512240234657016753 0ustar ripleyusers\name{static.power.law.game} \alias{static.power.law.game} \concept{Random graph model} \title{Scale-free random graphs, from vertex fitness scores} \description{ This function generates a non-growing random graph with expected power-law degree distributions. } \usage{ static.power.law.game (no.of.nodes, no.of.edges, exponent.out, exponent.in = -1, loops = FALSE, multiple = FALSE, finite.size.correction = TRUE) } \arguments{ \item{no.of.nodes}{The number of vertices in the generated graph.} \item{no.of.edges}{The number of edges in the generated graph.} \item{exponent.out}{Numeric scalar, the power law exponent of the degree distribution. For directed graphs, this specifies the exponent of the out-degree distribution. It must be greater than or equal to 2. If you pass \code{Inf} here, you will get back an Erdos-Renyi random network.} \item{exponent.in}{Numeric scalar. If negative, the generated graph will be undirected. If greater than or equal to 2, this argument specifies the exponent of the in-degree distribution. If non-negative but less than 2, an error will be generated.} \item{loops}{Logical scalar, whether to allow loop edges in the generated graph.} \item{multiple}{Logical scalar, whether to allow multiple edges in the generated graph.} \item{finite.size.correction}{Logical scalar, whether to use the proposed finite size correction of Cho et al., see references below.} } \details{ This game generates a directed or undirected random graph where the degrees of vertices follow power-law distributions with prescribed exponents. For directed graphs, the exponents of the in- and out-degree distributions may be specified separately. The game simply uses \code{\link{static.fitness.game}} with appropriately constructed fitness vectors. In particular, the fitness of vertex \eqn{i} is \eqn{i^{-alpha}}{i^(-alpha)}, where \eqn{alpha = 1/(gamma-1)} and gamma is the exponent given in the arguments. To remove correlations between in- and out-degrees in case of directed graphs, the in-fitness vector will be shuffled after it has been set up and before \code{\link{static.fitness.game}} is called. Note that significant finite size effects may be observed for exponents smaller than 3 in the original formulation of the game. This function provides an argument that lets you remove the finite size effects by assuming that the fitness of vertex \eqn{i} is \eqn{(i+i_0-1)^{-alpha}}{(i+i0-1)^(-alpha)} where \eqn{i_0}{i0} is a constant chosen appropriately to ensure that the maximum degree is less than the square root of the number of edges times the average degree; see the paper of Chung and Lu, and Cho et al for more details. } \value{An igraph graph, directed or undirected.} \references{ Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. \emph{Phys Rev Lett} 87(27):278701, 2001. Chung F and Lu L: Connected components in a random graph with given degree sequences. \emph{Annals of Combinatorics} 6, 125-145, 2002. Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. \emph{Phys Rev Lett} 103:135702, 2009. } \author{Tamas Nepusz \email{ntamas@gmail.com}} \examples{ g <- static.power.law.game(10000, 30000, 2.2, 2.3) \dontrun{plot(degree.distribution(g, cumulative=TRUE, mode="out"), log="xy")} } \keyword{graphs} igraph/man/is.degree.sequence.Rd0000644000176000001440000000402212240234657016275 0ustar ripleyusers\name{is.degree.sequence} \alias{is.degree.sequence} \alias{is.graphical.degree.sequence} \concept{Degree sequence} \title{Degree sequences of graphs} \description{ These functions decide whether a sequence (or two, if the graph is directed) of integers can be realized as vertex degrees by a graph or simple graph. } \usage{ is.degree.sequence (out.deg, in.deg = NULL) is.graphical.degree.sequence (out.deg, in.deg = NULL) } \arguments{ \item{out.deg}{Integer vector, the degree sequence for undirected graphs, or the out-degree sequence for directed graphs.} \item{in.deg}{\code{NULL} or an integer vector. For undireted graphs, it should be \code{NULL}. For directed graphs it specifies the in-degrees.} } \details{ \code{is.degree.sequence} checks whether the given vertex degrees (in- and out-degrees for directed graphs) can be realized by a graph. Note that the graph does not have to be simple, it may contain loop and multiple edges. For undirected graphs, it also checks whether the sum of degrees is even. For directed graphs, the function checks whether the lengths of the two degree vectors are equal and whether their sums are also equal. These are known sufficient and necessary conditions for a degree sequence to be valid. \code{is.graphial.degree.sequence} determines whether the given vertex degrees (in- and out-degrees for directed graphs) can be reliazed in a simple graph, i.e. a graph without multiple or loop edges. } \value{ A logical scalar. } \references{ Hakimi SL: On the realizability of a set of integers as degrees of the vertices of a simple graph. \emph{J SIAM Appl Math} 10:496-506, 1962. PL Erdos, I Miklos and Z Toroczkai: A simple Havel-Hakimi type algorithm to realize graphical degree sequences of directed graphs. \emph{The Electronic Journal of Combinatorics} 17(1):R66, 2010. } \author{ Tamas Nepusz \email{ntamas@gmail.com} } \examples{ g <- erdos.renyi.game(100, 2/100) is.degree.sequence(degree(g)) is.graphical.degree.sequence(degree(g)) } \keyword{graphs} igraph/man/read.graph.Rd0000644000176000001440000003573612263023733014650 0ustar ripleyusers\name{read.graph} \alias{read.graph} \alias{LGL} \alias{Pajek} \alias{GraphML} \alias{GML} \alias{DL} \alias{UCINET} \title{Reading foreign file formats} \description{The \code{read.graph} function is able to read graphs in various representations from a file, or from a http connection. Currently some simple formats are supported.} \usage{ read.graph(file, format = c("edgelist", "pajek", "ncol", "lgl", "graphml", "dimacs", "graphdb", "gml", "dl"), \dots) } \arguments{ \item{file}{The connection to read from. This can be a local file, or a \code{http} or \code{ftp} connection. It can also be a character string with the file name or URI.} \item{format}{Character constant giving the file format. Right now \code{edgelist}, \code{pajek}, \code{graphml}, \code{gml}, \code{ncol}, \code{lgl}, \code{dimacs} and \code{graphdb} are supported, the default is \code{edgelist}. As of igraph 0.4 this argument is case insensitive. } \item{\dots}{Additional arguments, see below.} } \details{ The \code{read.graph} function may have additional arguments depending on the file format (the \code{format} argument). See the details separately for each file format, below. } \section{Edge list format}{ This format is a simple text file with numeric vertex ids defining the edges. There is no need to have newline characters between the edges, a simple space will also do. Additional arguments: \describe{ \item{n}{The number of vertices in the graph. If it is smaller than or equal to the largest integer in the file, then it is ignored; so it is safe to set it to zero (the default).} \item{directed}{Logical scalar, whether to create a directed graph. The default value is \code{TRUE}.} } } \section{Pajek format}{ Pajek it a popular network analysis program for Windows. (See the Pajek homepage at \url{http://vlado.fmf.uni-lj.si/pub/networks/pajek/}.) It has a quite flexible but not very well documented file format, see the Pajek manual on the Pajek homepage for some information about the file format. \code{igraph} implements only a subset of the Pajek format: \itemize{ \item Only .net files are supported, Pajek project files (which can contain many graph and also other type of data) are not. Poject files might be supported in a forthcoming igraph release if they turned out to be needed. \item Time events networks are not supported. \item Hypergraphs (graphs with non-binary edges) are not supported as igraph cannot handle them. \item Graphs containing both directed and undirected edges are not supported as igraph cannot represent them. \item Graph with multiple edge sets are not supported. } From version 0.6.1 igraph supports reading bipartite (two-mode) graphs from Pajek files and adds the \code{type} vertex attribute. A warning is given if invalid edges (edges connecting vertices of the same type) are present in the file. Vertex and edge attributes defined in the Pajek file will be also read and assigned to the graph object to be created. These are mainly parameters for graph visualization, but not exclusively, eg. the file might contain edge weights as well. The following vertex attributes might be added: \tabular{ll}{ igraph name \tab description, Pajek attribute \cr id \tab Vertex id \cr x, y, z \tab The \sQuote{x}, \sQuote{y} and \sQuote{z} coordinate of the vertex \cr vertexsize \tab The size of the vertex when plotted (\code{size} in Pajek). \cr shape \tab The shape of the vertex when plotted. \cr color \tab Vertex color (\code{ic} in Pajek) if given with symbolic name \cr framecolor \tab Border color (\code{bc} in Pajek) if given with symbolic name \cr labelcolor \tab Label color (\code{lc} in Pajek) if given with symbolic name \cr xfact, yfact \tab The \code{x_fact} and \code{y_fact} Pajek attributes. \cr labeldist \tab The distance of the label from the vertex. (\code{lr} in Pajek.) \cr labeldegree, \tab \cr labeldegree2 \tab The \code{la} and \code{lphi} Pajek attributes \cr framewidth \tab The width of the border (\code{bw} in Pajek). \cr fontsize \tab Size of the label font (\code{fos} in Pajek.) \cr rotation \tab The rotation of the vertex (\code{phi} in Pajek). \cr radius \tab Radius, for some vertex shapes (\code{r} in Pajek). \cr diamondratio \tab For the diamond shape (\code{q} in Pajek). \cr type \tab vertex types in bipartite (two-mode) graphs. \cr } These igraph attributes are only created if there is at least one vertex in the Pajek file which has the corresponding associated information. Eg. if there are vertex coordinates for at least one vertex then the \sQuote{x}, \sQuote{y} and possibly also \sQuote{z} vertex attributes will be created. For those vertices for which the attribute is not defined, \code{NaN} is assigned. The following edge attributes might be added: \tabular{ll}{ igraph name \tab description, Pajek attribute \cr weight \tab Edge weights. \cr label \tab \code{l} in Pajek. \cr color \tab Edge color, if the color is given with a symbolic name, \code{c} in Pajek. \cr color-red, \tab \cr color-green, \tab \cr color-blue \tab Edge color if it was given in RGB notation, \code{c} in Pajek. \cr edgewidth \tab \code{w} in Pajek. \cr arrowsize \tab \code{s} in Pajek. \cr hook1, hook2 \tab \code{h1} and \code{h2} in Pajek. \cr angle1, angle2 \tab \code{a1} and \code{a2} in Pajek, Bezier curve parameters. \cr velocity1, \tab \cr velocity2 \tab \code{k1} and \code{k2} in Pajek, Bezier curve parameter. \cr arrowpos \tab \code{ap} in Pajek. \cr labelpos \tab \code{lp} in Pajek. \cr labelangle, \tab \cr labelangle2 \tab \code{lr} and \code{lphi} in Pajek. \cr labeldegree \tab \code{la} in Pajek. \cr fontsize \tab \code{fos} in Pajek. \cr arrowtype \tab \code{a} in Pajek. \cr linepattern \tab \code{p} in Pajek. \cr labelcolor \tab \code{lc} in Pajek. \cr } There are no additional arguments for this format. } \section{GraphML file format}{ GraphML is an XML-based file format (an XML application in the XML terminology) to describe graphs. It is a modern format, and can store graphs with an extensible set of vertex and edge attributes, and generalized graphs which igraph cannot handle. Thus igraph supports only a subset of the GraphML language: \itemize{ \item Hypergraphs are not supported. \item Nested graphs are not supported. \item Mixed graphs, ie. graphs with both directed and undirected edges are not supported. read.graph() sets the graph directed if this is the default in the GraphML file, even if all the edges are in fact undirected. } See the GraphML homepage at \url{http://graphml.graphdrawing.org} for more information about the GraphML format. Additional arguments: \describe{ \item{index}{If the GraphML file contains more than one graphs, this argument can be used to select the graph to read. By default the first graph is read (index 0).} } } \section{GML file format}{ GML is a simple textual format, see \url{http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html} for details. Although all syntactically correct GML can be parsed, we implement only a subset of this format, some attributes might be ignored. Here is a list of all the differences: \itemize{ \item Only \code{node} and \code{edge} attributes are used, and only if they have a simple type: integer, real or string. So if an attribute is an array or a record, then it is ignored. This is also true if only some values of the attribute are complex. \item Top level attributes except for \code{Version} and the first \code{graph} attribute are completely ignored. \item Graph attributes except for \code{node} and \code{edge} are completely ignored. \item There is no maximum line length. \item There is no maximum keyword length. \item Character entities in strings are not interpreted. \item We allow \code{inf} (infinity) and \code{nan} (not a number) as a real number. This is case insensitive, so \code{nan}, \code{NaN} and \code{NAN} are equal. } Please contact us if you cannot live with these limitations of the GML parser. There are not additional argument for this format. } \section{DL file format}{ The DL format is a simple textual file format used by the UCINET software. See \url{http://www.analytictech.com/networks/dataentry.htm} for examples. All formats mentioned here is supported by igraph. Note the specification does not mention whether the format is case sensitive or not. For igraph DL files are case sensitive, i.e. \sQuote{Larry} and \sQuote{larry} are not the same. Additional arguments: \describe{ \item{directed}{Logical scalar, whether to create a directed graph. The default is to make the graph directed.} } } \section{NCOL format}{ This format is used by the Large Graph Layout program (\url{http://bioinformatics.icmb.utexas.edu/lgl}), and it is simply a symbolic weighted edge list. It is a simple text file with one edge per line. An edge is defined by two symbolic vertex names separated by whitespace. (The symbolic vertex names themselves cannot contain whitespace.) They might followed by an optional number, this will be the weight of the edge; the number can be negative and can be in scientific notation. If there is no weight specified to an edge it is assumed to be zero. The resulting graph is always undirected. LGL cannot deal with files which contain multiple or loop edges, this is however not checked here, as igraph is happy with these. Additional arguments: \describe{ \item{names}{Logical constant, whether to add the symbolic names as vertex attributes to the graph. If TRUE the name of the vertex attribute will be \sQuote{name}.} \item{weights}{Character scalar, specifies whether edge weights should be added to the graph. Possible values are and their meaning are: \sQuote{no}, edge weights will not be added; \sQuote{yes}, edge weights will be added, if they are not present in the file, then all edges get zero weight; \sQuote{auto}, edge weights will added if they are present in the file, otherwise not. The default is \sQuote{auto}. } \item{directed}{Logical constant, whether to create a directed graph. The default is undirected.} } } \section{LGL file format}{ The \code{lgl} format is used by the Large Graph Layout visualization software (\url{http://bioinformatics.icmb.utexas.edu/lgl}), it can describe undirected optionally weighted graphs. From the LGL manual: \dQuote{The second format is the LGL file format (.lgl file suffix). This is yet another graph file format that tries to be as stingy as possible with space, yet keeping the edge file in a human readable (not binary) format. The format itself is like the following: \preformatted{ # vertex1name vertex2name [optionalWeight] vertex3name [optionalWeight] } Here, the first vertex of an edge is preceded with a pound sign '\#'. Then each vertex that shares an edge with that vertex is listed one per line on subsequent lines.} LGL cannot handle loop and multiple edges or directed graphs, but in igraph it is not an error to have multiple and loop edges. Additional arguments: \describe{ \item{names}{Logical constant, whether to add the symbolic names as vertex attributes to the graph. If TRUE the name of the vertex attribute will be \sQuote{name}.} \item{weights}{Character scalar, specifies whether edge weights should be added to the graph. Possible values are and their meaning are: \sQuote{no}, edge weights will not be added; \sQuote{yes}, edge weights will be added, if they are not present in the file, then all edges get zero weight; \sQuote{auto}, edge weights will added if they are present in the file, otherwise not. The default is \sQuote{auto}. } } } \section{DIMACS file format}{ The DIMACS file format, more specifically the version for network flow problems, see the files at \url{ftp://dimacs.rutgers.edu/pub/netflow/general-info/} This is a line-oriented text file (ASCII) format. The first character of each line defines the type of the line. If the first character is \code{c} the line is a comment line and it is ignored. There is one problem line (\code{p}) in the file, it must appear before any node and arc descriptor lines. The problem line has three fields separated by spaces: the problem type (\code{min}, \code{max} or \code{asn}), the number of vertices and number of edges in the graph. Exactly two node identification lines are expected (\code{n}), one for the source, one for the target vertex. These have two fields: the id of the vertex and the type of the vertex, either \code{s} (=source) or \code{t} (=target). Arc lines start with \code{a} and have three fields: the source vertex, the target vertex and the edge capacity. Vertex ids are numbered from 1. The source vertex is assigned to the \code{source}, the target vertex to the \code{target} graph attribute. The edge capacities are assigned to the \code{capacity} edge attribute. Additional arguments: \describe{ \item{directed}{Logical scalar, whether to create a directed graph. By default a directed graph is created. } } } \section{GraphDB format}{ This is a binary format, used in the graph database for isomorphism testing. From the (now defunct) graph database homepage: \emph{The graphs are stored in a compact binary format, one graph per file. The file is composed of 16 bit words, which are represented using the so-called little-endian convention, i.e. the least significant byte of the word is stored first.} \emph{Then, for each node, the file contains the list of edges coming out of the node itself. The list is represented by a word encoding its length, followed by a word for each edge, representing the destination node of the edge. Node numeration is 0-based, so the first node of the graph has index 0.} A copy of the graph database homepage can be found here: \url{http://web.archive.org/web/20090215182331/http://amalfi.dis.unina.it/graph/db/doc/graphdbat.html}. See also \code{\link{graph.graphdb}}. Only unlabelled graphs are implemented. Additional attributes: \describe{ \item{directed}{Logical scalar. Whether to create a directed graph.} } } \value{A graph object.} % \references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{write.graph}} } % \examples{} \keyword{graphs} igraph/man/multilevel.community.Rd0000644000176000001440000000525612251656216017041 0ustar ripleyusers\name{multilevel.community} \alias{multilevel.community} \concept{Community structure} \concept{Multilevel community detection} \title{Finding community structure by multi-level optimization of modularity} \description{This function implements the multi-level modularity optimization algorithm for finding community structure, see references below. It is based on the modularity measure and a hierarchial approach. } \usage{ multilevel.community (graph, weights = NULL) } \arguments{ \item{graph}{The input graph.} \item{weights}{Optional positive weight vector. If the graph has a \code{weight} edge attribute, then this is used by default. Supply \code{NA} here if the graph has a \code{weight} edge attribute, but you want to ignore it.} } \details{ This function implements the multi-level modularity optimization algorithm for finding community structure, see VD Blondel, J-L Guillaume, R Lambiotte and E Lefebvre: Fast unfolding of community hierarchies in large networks, \url{http://arxiv.org/abs/arXiv:0803.0476} for the details. It is based on the modularity measure and a hierarchial approach. Initially, each vertex is assigned to a community on its own. In every step, vertices are re-assigned to communities in a local, greedy way: each vertex is moved to the community with which it achieves the highest contribution to modularity. When no vertices can be reassigned, each community is considered a vertex on its own, and the process starts again with the merged communities. The process stops when there is only a single vertex left or when the modularity cannot be increased any more in a step. This function was contributed by Tom Gregorovic. } \value{ \code{multilevel.community} returns a \code{\link{communities}} object, please see the \code{\link{communities}} manual page for details. } \references{ Vincent D. Blondel, Jean-Loup Guillaume, Renaud Lambiotte, Etienne Lefebvre: Fast unfolding of communities in large networks. J. Stat. Mech. (2008) P10008 } \author{Tom Gregorovic, Tamas Nepusz \email{ntamas@gmail.com}} \seealso{ See \code{\link{communities}} for extracting the membership, modularity scores, etc. from the results. Other community detection algorithms: \code{\link{walktrap.community}}, \code{\link{spinglass.community}}, \code{\link{leading.eigenvector.community}}, \code{\link{edge.betweenness.community}}, \code{\link{fastgreedy.community}}, \code{\link{label.propagation.community}} } \examples{ # This is so simple that we will have only one level g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6, 11)) multilevel.community(g) } \keyword{graphs}igraph/man/community.structure.Rd0000644000176000001440000000303612325365704016712 0ustar ripleyusers\name{community.to.membership} \alias{community.to.membership} \concept{Community structure} \title{Common functions supporting community detection algorithms} \description{ \code{community.to.membership} takes a merge matrix, a typical result of community structure detection algorithms and creates a membership vector by performing a given number of merges in the merge matrix. } \usage{ community.to.membership(graph, merges, steps, membership=TRUE, csize=TRUE) } \arguments{ \item{graph}{The graph to which the merge matrix belongs.} \item{merges}{The merge matrix, see e.g. \code{\link{walktrap.community}} for the exact format.} \item{steps}{The number of steps, ie. merges to be performed.} \item{membership}{Logical scalar, whether to include the membership vector in the result.} \item{csize}{Logical scalar, whether to include the sizes of the communities in the result.} } %\details{} \value{ A named list with two members: \item{membership}{The membership vector.} \item{csize}{A numeric vector giving the sizes of the communities.} } %\references{} \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{ \code{\link{walktrap.community}}, \code{\link{edge.betweenness.community}}, \code{\link{fastgreedy.community}}, \code{\link{spinglass.community}} for various community detection methods. } \examples{ g <- graph.full(5) \%du\% graph.full(5) \%du\% graph.full(5) g <- add.edges(g, c(1,6, 1,11, 6, 11)) wtc <- walktrap.community(g) community.to.membership(g, wtc$merges, steps=12) } \keyword{graphs} igraph/man/girth.Rd0000644000176000001440000000310412240234657013736 0ustar ripleyusers\name{girth} \alias{girth} \concept{Girth} \title{Girth of a graph} \description{The girth of a graph is the length of the shortest circle in it.} \usage{ girth(graph, circle=TRUE) } \arguments{ \item{graph}{The input graph. It may be directed, but the algorithm searches for undirected circles anyway.} \item{circle}{Logical scalar, whether to return the shortest circle itself.} } \details{ The current implementation works for undirected graphs only, directed graphs are treated as undirected graphs. Loop edges and multiple edges are ignored. If the graph is a forest (ie. acyclic), then zero is returned. This implementation is based on Alon Itai and Michael Rodeh: Finding a minimum circuit in a graph \emph{Proceedings of the ninth annual ACM symposium on Theory of computing}, 1-10, 1977. The first implementation of this function was done by Keith Briggs, thanks Keith. } \value{ A named list with two components: \item{girth}{Integer constant, the girth of the graph, or 0 if the graph is acyclic.} \item{circle}{Numeric vector with the vertex ids in the shortest circle.} } \references{ Alon Itai and Michael Rodeh: Finding a minimum circuit in a graph \emph{Proceedings of the ninth annual ACM symposium on Theory of computing}, 1-10, 1977 } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \examples{ # No circle in a tree g <- graph.tree(1000, 3) girth(g) # The worst case running time is for a ring g <- graph.ring(100) girth(g) # What about a random graph? g <- erdos.renyi.game(1000, 1/1000) girth(g) } \keyword{graphs} igraph/man/plot.sir.Rd0000644000176000001440000000454112266265431014403 0ustar ripleyusers\name{plot.sir} \alias{plot.sir} \concept{SIR model} \title{Plotting the results on multiple SIR model runs} \description{ This function can conveniently plot the results of multiple SIR model simulations. } \usage{ \S3method{plot}{sir}(x, comp = c("NI", "NS", "NR"), median = TRUE, quantiles = c(0.1, 0.9), color = NULL, median_color = NULL, quantile_color = NULL, lwd.median = 2, lwd.quantile = 2, lty.quantile = 3, xlim = NULL, ylim = NULL, xlab = "Time", ylab = NULL, \dots) } \arguments{ \item{x}{The output of the SIR simulation, coming from the \code{\link{sir}} function.} \item{comp}{Character scalar, which component to plot. Either \sQuote{NI} (infected, default), \sQuote{NS} (susceptible) or \sQuote{NR} (recovered). } \item{median}{Logical scalar, whether to plot the (binned) median.} \item{quantiles}{A vector of (binned) quantiles to plot.} \item{color}{Color of the individual simulation curves.} \item{median_color}{Color of the median curve.} \item{quantile_color}{Color(s) of the quantile curves. (It is recycled if needed and non-needed entries are ignored if too long.)} \item{lwd.median}{Line width of the median.} \item{lwd.quantile}{Line width of the quantile curves.} \item{lty.quantile}{Line type of the quantile curves.} \item{xlim}{The x limits, a two-element numeric vector. If \code{NULL}, then it is calculated from the data.} \item{ylim}{The y limits, a two-element numeric vector. If \code{NULL}, then it is calculated from the data.} \item{xlab}{The x label.} \item{ylab}{The y label. If \code{NULL} then it is automatically added based on the \code{comp} argument.} \item{\dots}{Additional arguments are passed to \code{plot}, that is run before any of the curves are added, to create the figure.} } \details{ The number of susceptible/infected/recovered individuals is plotted over time, for multiple simulations. } \value{Nothing.} \references{ Bailey, Norman T. J. (1975). The mathematical theory of infectious diseases and its applications (2nd ed.). London: Griffin. } \author{ Eric Kolaczyk (\url{http://math.bu.edu/people/kolaczyk/}) and Gabor Csardi \email{csardi.gabor@gmail.com}. } \seealso{\code{\link{sir}} for running the actual simulation.} \examples{ g <- erdos.renyi.game(100, 100, type="gnm") sm <- sir(g, beta=5, gamma=1) plot(sm) } \keyword{graphs} igraph/man/semiProjectors.Rd0000644000176000001440000000727212251656216015644 0ustar ripleyusers\name{scgSemiProjectors} \alias{scgSemiProjectors} \title{Semi-Projectors} \description{ A function to compute the \eqn{L} and \eqn{R} semi-projectors for a given partition of the vertices. } \usage{ scgSemiProjectors(groups, mtype = c("symmetric", "laplacian", "stochastic"), p = NULL, norm = c("row", "col"), sparse = getIgraphOpt("sparsematrices")) } \arguments{ \item{groups}{A vector of \code{nrow(X)} or \code{vcount(X)} integers giving the group label of every vertex in the partition.} \item{mtype}{The type of semi-projectors. For now \dQuote{symmetric}, \dQuote{laplacian} and \dQuote{stochastic} are available.} \item{p}{A probability vector of length \code{length(gr)}. \code{p} is the stationary probability distribution of a Markov chain when \code{mtype} = \dQuote{stochastic}. This parameter is ignored in all other cases.} \item{norm}{Either \dQuote{row} or \dQuote{col}. If set to \dQuote{row} the rows of the Laplacian matrix sum up to zero and the rows of the stochastic sum up to one; otherwise it is the columns.} \item{sparse}{Logical scalar, whether to return sparse matrices.} } \details{ The three types of semi-projectors are defined as follows. Let \eqn{\gamma(j)}{gamma(j)} label the group of vertex \eqn{j} in a partition of all the vertices. The symmetric semi-projectors are defined as \deqn{L_{\alpha j}=R_{\alpha j}= \frac{1}{\sqrt{|\alpha|}}\delta_{\alpha\gamma(j)},}{% L[alpha,j] = R[alpha,j] = 1/sqrt(|alpha|) delta[alpha,gamma(j)],} the (row) Laplacian semi-projectors as \deqn{L_{\alpha j}=\frac{1}{|\alpha|}\delta_{\alpha\gamma(j)}\,\,\,\, \textrm{and}\,\,\,\, R_{\alpha j}=\delta_{\alpha\gamma(j)},}{% L[alpha,j] = 1/|alpha| delta[alpha,gamma(j)] and R[alpha,j] = delta[alpha,gamma(j)],} and the (row) stochastic semi-projectors as \deqn{L_{\alpha j}=\frac{p_{1}(j)}{\sum_{k\in\gamma(j)}p_{1}(k)}\,\,\,\, \textrm{and}\,\,\,\, R_{\alpha j}=\delta_{\alpha\gamma(j)\delta_{\alpha\gamma(j)}},}{% L[alpha,j] = p[1][j] / sum(p[1][k]; k in gamma(j)) delta[alpha,gamma(j)] and R[alpha,j] = delta[alpha,gamma(j)],} where \eqn{p_1}{p[1]} is the (left) eigenvector associated with the one-eigenvalue of the stochastic matrix. \eqn{L} and \eqn{R} are defined in a symmetric way when \code{norm = col}. All these semi-projectors verify various properties described in the reference. } \value{ \item{L}{The semi-projector \eqn{L}.} \item{R}{The semi-projector \eqn{R}.} } \references{ D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, Shrinking Matrices while Preserving their Eigenpairs with Application to the Spectral Coarse Graining of Graphs. Submitted to \emph{SIAM Journal on Matrix Analysis and Applications}, 2008. \url{http://people.epfl.ch/david.morton} } \author{David Morton de Lachapelle, \url{http://people.epfl.ch/david.morton}.} \seealso{\link{SCG} for a detailed introduction. \code{\link{scg}}, \code{\link{scgNormEps}}, \code{\link{scgGrouping}}} \examples{ library(Matrix) # compute the semi-projectors and projector for the partition # provided by a community detection method g <- barabasi.game(20, m=1.5) eb <- edge.betweenness.community(g) memb <- membership(eb) lr <- scgSemiProjectors(memb) #In the symmetric case L = R tcrossprod(lr$R) # same as lr$R %*% t(lr$R) P <- crossprod(lr$R) # same as t(lr$R) %*% lr$R #P is an orthogonal projector isSymmetric(P) sum( (P \%*\% P-P)^2 ) ## use L and R to coarse-grain the graph Laplacian lr <- scgSemiProjectors(memb, mtype="laplacian") L <- graph.laplacian(g) Lt <- lr$L \%*\% L \%*\% t(lr$R) ## or better lr$L \%*\% tcrossprod(L,lr$R) rowSums(Lt) } \keyword{array} \keyword{graphs} igraph/man/graph.graphdb.Rd0000644000176000001440000000674512251656216015347 0ustar ripleyusers\name{graph.graphdb} \alias{graph.graphdb} \concept{Graph database} \title{Load a graph from the graph database for testing graph isomorphism.} \description{This function downloads a graph from a database created for the evaluation of graph isomorphism testing algothitms.} \usage{ graph.graphdb (url = NULL, prefix = "iso", type = "r001", nodes = NULL, pair = "A", which = 0, base = "http://cneurocvs.rmki.kfki.hu/graphdb/gzip", compressed = TRUE, directed = TRUE) } \arguments{ \item{url}{If not \code{NULL} it is a complete URL with the file to import.} \item{prefix}{Gives the prefix. See details below. Possible values: \code{iso}, \code{i2}, \code{si4}, \code{si6}, \code{mcs10}, \code{mcs30}, \code{mcs50}, \code{mcs70}, \code{mcs90}. } \item{type}{Gives the graph type identifier. See details below. Possible values: \code{r001}, \code{r005}, \code{r01}, \code{r02}, \code{m2D}, \code{m2Dr2}, \code{m2Dr4}, \code{m2Dr6} \code{m3D}, \code{m3Dr2}, \code{m3Dr4}, \code{m3Dr6}, \code{m4D}, \code{m4Dr2}, \code{m4Dr4}, \code{m4Dr6}, \code{b03}, \code{b03m}, \code{b06}, \code{b06m}, \code{b09}, \code{b09m}. } \item{nodes}{The number of vertices in the graph.} \item{pair}{Specifies which graph of the pair to read. Possible values: \code{A} and \code{B}.} \item{which}{Gives the number of the graph to read. For every graph type there are a number of actual graphs in the database. This argument specifies which one to read.} \item{base}{The base address of the database. See details below.} \item{compressed}{Logical constant, if TRUE than the file is expected to be compressed by gzip. If \code{url} is \code{NULL} then a \sQuote{\code{.gz}} suffix is added to the filename.} \item{directed}{Logical constant, whether to create a directed graph.} } \details{ \code{graph.graphdb} reads a graph from the graph database from an FTP or HTTP server or from a local copy. It has two modes of operation: If the \code{url} argument is specified then it should the complete path to a local or remote graph database file. In this case we simply call \code{\link{read.graph}} with the proper arguments to read the file. If \code{url} is \code{NULL}, and this is the default, then the filename is assembled from the \code{base}, \code{prefix}, \code{type}, \code{nodes}, \code{pair} and \code{which} arguments. Unfortunately the original graph database homepage is now defunct, but see its old version at \url{http://web.archive.org/web/20090215182331/http://amalfi.dis.unina.it/graph/db/doc/graphdbat.html} for the actual format of a graph database file and other information. } \value{ A new graph object. } \references{M. De Santo, P. Foggia, C. Sansone, M. Vento: A large database of graphs and its use for benchmarking graph isomorphism algorithms, \emph{Pattern Recognition Letters}, Volume 24, Issue 8 (May 2003) } \author{Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{read.graph}}, \code{\link{graph.isomorphic.vf2}}} \examples{ \dontrun{ g <- graph.graphdb(prefix="iso", type="r001", nodes=20, pair="A", which=10, compressed=TRUE) g2 <- graph.graphdb(prefix="iso", type="r001", nodes=20, pair="B", which=10, compressed=TRUE) graph.isomorphic.vf2(g, g2) % should be TRUE g3 <- graph.graphdb(url=paste(sep="/", "http://cneurocvs.rmki.kfki.hu", "graphdb/gzip/iso/bvg/b06m", "iso_b06m_m200.A09.gz")) }} \keyword{graphs} igraph/man/graph.de.bruijn.Rd0000644000176000001440000000254712240234657015613 0ustar ripleyusers\name{graph.de.bruijn} \alias{graph.de.bruijn} \concept{De Bruijn graph} \title{De Bruijn graphs.} \description{De Bruijn graphs are labeled graphs representing the overlap of strings. } \usage{ graph.de.bruijn(m,n) } \arguments{ \item{m}{Integer scalar, the size of the alphabet. See details below.} \item{n}{Integer scalar, the length of the labels. See details below.} } \details{ A de Bruijn graph represents relationships between strings. An alphabet of \code{m} letters are used and strings of length \code{n} are considered. A vertex corresponds to every possible string and there is a directed edge from vertex \code{v} to vertex \code{w} if the string of \code{v} can be transformed into the string of \code{w} by removing its first letter and appending a letter to it. Please note that the graph will have \code{m} to the power \code{n} vertices and even more edges, so probably you don't want to supply too big numbers for \code{m} and \code{n}. De Bruijn graphs have some interesting properties, please see another source, eg. Wikipedia for details. } \value{A graph object.} \author{Gabor Csardi } \seealso{\code{\link{graph.kautz}}, \code{\link{line.graph}}} \examples{ # de Bruijn graphs can be created recursively by line graphs as well g <- graph.de.bruijn(2,1) graph.de.bruijn(2,2) line.graph(g) } \keyword{graphs} igraph/man/tkigraph.Rd0000644000176000001440000000111212240234657014427 0ustar ripleyusers\name{tkigraph} \alias{tkigraph} \concept{GUI} \title{Experimental basic igraph GUI} \description{This functions starts an experimental GUI to some igraph functions. The GUI was written in Tcl/Tk, so it is cross platform.} \usage{ tkigraph() } %\arguments{} \details{ \code{tkigraph} has its own online help system, please see that for the details about how to use it. } \value{Returns \code{NULL}, invisibly.} % \references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com} } \seealso{\code{\link{tkplot}} for interactive plotting of graphs.} % \examples{} \keyword{graphs} igraph/man/get.adjlist.Rd0000644000176000001440000000241412240234657015034 0ustar ripleyusers\name{get.adjlist} \alias{get.adjlist} \alias{get.adjedgelist} \concept{Adjacency list} \title{Adjacency lists} \description{Create adjacency lists from a graph, either for adjacent edges or for neighboring vertices } \usage{ get.adjlist(graph, mode = c("all", "out", "in", "total")) get.adjedgelist(graph, mode = c("all", "out", "in", "total")) } \arguments{ \item{graph}{The input graph.} \item{mode}{Character scalar, it gives what kind of adjacent edges/vertices to include in the lists. \sQuote{\code{out}} is for outgoing edges/vertices, \sQuote{\code{in}} is for incoming edges/vertices, \sQuote{\code{all}} is for both. This argument is ignored for undirected graphs.} } \details{ \code{get.adjlist} returns a list of numeric vectors, which include the ids of neighbor vertices (according to the \code{mode} argument) of all vertices. \code{get.adjedgelist} returns a list of numeric vectors, which include the ids of adjacent edgs (according to the \code{mode} argument) of all vertices. } \value{ A list of numeric vectors. } %\references{} \author{ Gabor Csardi \email{csardi.gabor@gmail.com}} \seealso{\code{\link{get.edgelist}}, \code{\link{get.adjacency}}} \examples{ g <- graph.ring(10) get.adjlist(g) get.adjedgelist(g) } \keyword{graphs} igraph/configure.win0000644000176000001440000000000012240234657014247 0ustar ripleyusers